uboot/drivers/video/exynos_fimd.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 Samsung Electronics
   3 *
   4 * Author: InKi Dae <inki.dae@samsung.com>
   5 * Author: Donghwa Lee <dh09.lee@samsung.com>
   6 *
   7 * SPDX-License-Identifier:     GPL-2.0+
   8 */
   9
  10#include <config.h>
  11#include <common.h>
  12#include <asm/io.h>
  13#include <lcd.h>
  14#include <div64.h>
  15#include <fdtdec.h>
  16#include <libfdt.h>
  17#include <asm/arch/clk.h>
  18#include <asm/arch/clock.h>
  19#include <asm/arch/cpu.h>
  20#include "exynos_fb.h"
  21
  22DECLARE_GLOBAL_DATA_PTR;
  23
  24static unsigned long *lcd_base_addr;
  25static vidinfo_t *pvid;
  26static struct exynos_fb *fimd_ctrl;
  27
  28void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size,
  29                u_long palette_size)
  30{
  31        lcd_base_addr = (unsigned long *)screen_base;
  32}
  33
  34static void exynos_fimd_set_dualrgb(unsigned int enabled)
  35{
  36        unsigned int cfg = 0;
  37
  38        if (enabled) {
  39                cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
  40                        EXYNOS_DUALRGB_VDEN_EN_ENABLE;
  41
  42                /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
  43                cfg |= EXYNOS_DUALRGB_SUB_CNT(pvid->vl_col / 2) |
  44                        EXYNOS_DUALRGB_MAIN_CNT(0);
  45        }
  46
  47        writel(cfg, &fimd_ctrl->dualrgb);
  48}
  49
  50static void exynos_fimd_set_dp_clkcon(unsigned int enabled)
  51{
  52        unsigned int cfg = 0;
  53
  54        if (enabled)
  55                cfg = EXYNOS_DP_CLK_ENABLE;
  56
  57        writel(cfg, &fimd_ctrl->dp_mie_clkcon);
  58}
  59
  60static void exynos_fimd_set_par(unsigned int win_id)
  61{
  62        unsigned int cfg = 0;
  63
  64        /* set window control */
  65        cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
  66                        EXYNOS_WINCON(win_id));
  67
  68        cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
  69                EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
  70                EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
  71                EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
  72
  73        /* DATAPATH is DMA */
  74        cfg |= EXYNOS_WINCON_DATAPATH_DMA;
  75
  76        cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
  77
  78        /* dma burst is 16 */
  79        cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
  80
  81        switch (pvid->vl_bpix) {
  82        case 4:
  83                cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
  84                break;
  85        default:
  86                cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888;
  87                break;
  88        }
  89
  90        writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
  91                        EXYNOS_WINCON(win_id));
  92
  93        /* set window position to x=0, y=0*/
  94        cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
  95        writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a +
  96                        EXYNOS_VIDOSD(win_id));
  97
  98        cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) |
  99                EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) |
 100                EXYNOS_VIDOSD_RIGHT_X_E(1) |
 101                EXYNOS_VIDOSD_BOTTOM_Y_E(0);
 102
 103        writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b +
 104                        EXYNOS_VIDOSD(win_id));
 105
 106        /* set window size for window0*/
 107        cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row);
 108        writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c +
 109                        EXYNOS_VIDOSD(win_id));
 110}
 111
 112static void exynos_fimd_set_buffer_address(unsigned int win_id)
 113{
 114        unsigned long start_addr, end_addr;
 115
 116        start_addr = (unsigned long)lcd_base_addr;
 117        end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) *
 118                                pvid->vl_row);
 119
 120        writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 +
 121                        EXYNOS_BUFFER_OFFSET(win_id));
 122        writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 +
 123                        EXYNOS_BUFFER_OFFSET(win_id));
 124}
 125
 126static void exynos_fimd_set_clock(vidinfo_t *pvid)
 127{
 128        unsigned int cfg = 0, div = 0, remainder, remainder_div;
 129        unsigned long pixel_clock;
 130        unsigned long long src_clock;
 131
 132        if (pvid->dual_lcd_enabled) {
 133                pixel_clock = pvid->vl_freq *
 134                                (pvid->vl_hspw + pvid->vl_hfpd +
 135                                 pvid->vl_hbpd + pvid->vl_col / 2) *
 136                                (pvid->vl_vspw + pvid->vl_vfpd +
 137                                 pvid->vl_vbpd + pvid->vl_row);
 138        } else if (pvid->interface_mode == FIMD_CPU_INTERFACE) {
 139                pixel_clock = pvid->vl_freq *
 140                                pvid->vl_width * pvid->vl_height *
 141                                (pvid->cs_setup + pvid->wr_setup +
 142                                 pvid->wr_act + pvid->wr_hold + 1);
 143        } else {
 144                pixel_clock = pvid->vl_freq *
 145                                (pvid->vl_hspw + pvid->vl_hfpd +
 146                                 pvid->vl_hbpd + pvid->vl_col) *
 147                                (pvid->vl_vspw + pvid->vl_vfpd +
 148                                 pvid->vl_vbpd + pvid->vl_row);
 149        }
 150
 151        cfg = readl(&fimd_ctrl->vidcon0);
 152        cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
 153                EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
 154                EXYNOS_VIDCON0_CLKDIR_MASK);
 155        cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
 156                EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
 157
 158        src_clock = (unsigned long long) get_lcd_clk();
 159
 160        /* get quotient and remainder. */
 161        remainder = do_div(src_clock, pixel_clock);
 162        div = src_clock;
 163
 164        remainder *= 10;
 165        remainder_div = remainder / pixel_clock;
 166
 167        /* round about one places of decimals. */
 168        if (remainder_div >= 5)
 169                div++;
 170
 171        /* in case of dual lcd mode. */
 172        if (pvid->dual_lcd_enabled)
 173                div--;
 174
 175        cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
 176        writel(cfg, &fimd_ctrl->vidcon0);
 177}
 178
 179void exynos_set_trigger(void)
 180{
 181        unsigned int cfg = 0;
 182
 183        cfg = readl(&fimd_ctrl->trigcon);
 184
 185        cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
 186
 187        writel(cfg, &fimd_ctrl->trigcon);
 188}
 189
 190int exynos_is_i80_frame_done(void)
 191{
 192        unsigned int cfg = 0;
 193        int status;
 194
 195        cfg = readl(&fimd_ctrl->trigcon);
 196
 197        /* frame done func is valid only when TRIMODE[0] is set to 1. */
 198        status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
 199                        EXYNOS_I80STATUS_TRIG_DONE;
 200
 201        return status;
 202}
 203
 204static void exynos_fimd_lcd_on(void)
 205{
 206        unsigned int cfg = 0;
 207
 208        /* display on */
 209        cfg = readl(&fimd_ctrl->vidcon0);
 210        cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
 211        writel(cfg, &fimd_ctrl->vidcon0);
 212}
 213
 214static void exynos_fimd_window_on(unsigned int win_id)
 215{
 216        unsigned int cfg = 0;
 217
 218        /* enable window */
 219        cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
 220                        EXYNOS_WINCON(win_id));
 221        cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
 222        writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
 223                        EXYNOS_WINCON(win_id));
 224
 225        cfg = readl(&fimd_ctrl->winshmap);
 226        cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
 227        writel(cfg, &fimd_ctrl->winshmap);
 228}
 229
 230void exynos_fimd_lcd_off(void)
 231{
 232        unsigned int cfg = 0;
 233
 234        cfg = readl(&fimd_ctrl->vidcon0);
 235        cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
 236        writel(cfg, &fimd_ctrl->vidcon0);
 237}
 238
 239void exynos_fimd_window_off(unsigned int win_id)
 240{
 241        unsigned int cfg = 0;
 242
 243        cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
 244                        EXYNOS_WINCON(win_id));
 245        cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
 246        writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
 247                        EXYNOS_WINCON(win_id));
 248
 249        cfg = readl(&fimd_ctrl->winshmap);
 250        cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
 251        writel(cfg, &fimd_ctrl->winshmap);
 252}
 253
 254
 255void exynos_fimd_lcd_init(vidinfo_t *vid)
 256{
 257        unsigned int cfg = 0, rgb_mode;
 258        unsigned int offset;
 259#ifdef CONFIG_OF_CONTROL
 260        unsigned int node;
 261
 262        node = fdtdec_next_compatible(gd->fdt_blob,
 263                                        0, COMPAT_SAMSUNG_EXYNOS_FIMD);
 264        if (node <= 0)
 265                debug("exynos_fb: Can't get device node for fimd\n");
 266
 267        fimd_ctrl = (struct exynos_fb *)fdtdec_get_addr(gd->fdt_blob,
 268                                                                node, "reg");
 269        if (fimd_ctrl == NULL)
 270                debug("Can't get the FIMD base address\n");
 271#else
 272        fimd_ctrl = (struct exynos_fb *)samsung_get_base_fimd();
 273#endif
 274
 275        offset = exynos_fimd_get_base_offset();
 276
 277        /* store panel info to global variable */
 278        pvid = vid;
 279
 280        rgb_mode = vid->rgb_mode;
 281
 282        if (vid->interface_mode == FIMD_RGB_INTERFACE) {
 283                cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
 284                writel(cfg, &fimd_ctrl->vidcon0);
 285
 286                cfg = readl(&fimd_ctrl->vidcon2);
 287                cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
 288                        EXYNOS_VIDCON2_TVFORMATSEL_MASK |
 289                        EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
 290                cfg |= EXYNOS_VIDCON2_WB_DISABLE;
 291                writel(cfg, &fimd_ctrl->vidcon2);
 292
 293                /* set polarity */
 294                cfg = 0;
 295                if (!pvid->vl_clkp)
 296                        cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
 297                if (!pvid->vl_hsp)
 298                        cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
 299                if (!pvid->vl_vsp)
 300                        cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
 301                if (!pvid->vl_dp)
 302                        cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
 303
 304                writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset);
 305
 306                /* set timing */
 307                cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1);
 308                cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1);
 309                cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1);
 310                writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset);
 311
 312                cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1);
 313                cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1);
 314                cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1);
 315
 316                writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset);
 317
 318                /* set lcd size */
 319                cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) |
 320                        EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) |
 321                        EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) |
 322                        EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1);
 323
 324                writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset);
 325        }
 326
 327        /* set display mode */
 328        cfg = readl(&fimd_ctrl->vidcon0);
 329        cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
 330        cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
 331        writel(cfg, &fimd_ctrl->vidcon0);
 332
 333        /* set par */
 334        exynos_fimd_set_par(pvid->win_id);
 335
 336        /* set memory address */
 337        exynos_fimd_set_buffer_address(pvid->win_id);
 338
 339        /* set buffer size */
 340        cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) |
 341                EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) |
 342                EXYNOS_VIDADDR_OFFSIZE(0) |
 343                EXYNOS_VIDADDR_OFFSIZE_E(0);
 344
 345        writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 +
 346                                        EXYNOS_BUFFER_SIZE(pvid->win_id));
 347
 348        /* set clock */
 349        exynos_fimd_set_clock(pvid);
 350
 351        /* set rgb mode to dual lcd. */
 352        exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled);
 353
 354        /* display on */
 355        exynos_fimd_lcd_on();
 356
 357        /* window on */
 358        exynos_fimd_window_on(pvid->win_id);
 359
 360        exynos_fimd_set_dp_clkcon(pvid->dp_enabled);
 361}
 362
 363unsigned long exynos_fimd_calc_fbsize(void)
 364{
 365        return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8);
 366}
 367