uboot/drivers/video/exynos/exynos_fb.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 <display.h>
  13#include <div64.h>
  14#include <dm.h>
  15#include <fdtdec.h>
  16#include <libfdt.h>
  17#include <panel.h>
  18#include <video.h>
  19#include <video_bridge.h>
  20#include <asm/io.h>
  21#include <asm/arch/cpu.h>
  22#include <asm/arch/clock.h>
  23#include <asm/arch/clk.h>
  24#include <asm/arch/mipi_dsim.h>
  25#include <asm/arch/dp_info.h>
  26#include <asm/arch/fb.h>
  27#include <asm/arch/pinmux.h>
  28#include <asm/arch/system.h>
  29#include <asm/gpio.h>
  30#include <linux/errno.h>
  31
  32DECLARE_GLOBAL_DATA_PTR;
  33
  34enum {
  35        FIMD_RGB_INTERFACE = 1,
  36        FIMD_CPU_INTERFACE = 2,
  37};
  38
  39enum exynos_fb_rgb_mode_t {
  40        MODE_RGB_P = 0,
  41        MODE_BGR_P = 1,
  42        MODE_RGB_S = 2,
  43        MODE_BGR_S = 3,
  44};
  45
  46struct exynos_fb_priv {
  47        ushort vl_col;          /* Number of columns (i.e. 640) */
  48        ushort vl_row;          /* Number of rows (i.e. 480) */
  49        ushort vl_rot;          /* Rotation of Display (0, 1, 2, 3) */
  50        ushort vl_width;        /* Width of display area in millimeters */
  51        ushort vl_height;       /* Height of display area in millimeters */
  52
  53        /* LCD configuration register */
  54        u_char vl_freq;         /* Frequency */
  55        u_char vl_clkp;         /* Clock polarity */
  56        u_char vl_oep;          /* Output Enable polarity */
  57        u_char vl_hsp;          /* Horizontal Sync polarity */
  58        u_char vl_vsp;          /* Vertical Sync polarity */
  59        u_char vl_dp;           /* Data polarity */
  60        u_char vl_bpix;         /* Bits per pixel */
  61
  62        /* Horizontal control register. Timing from data sheet */
  63        u_char vl_hspw;         /* Horz sync pulse width */
  64        u_char vl_hfpd;         /* Wait before of line */
  65        u_char vl_hbpd;         /* Wait end of line */
  66
  67        /* Vertical control register. */
  68        u_char  vl_vspw;        /* Vertical sync pulse width */
  69        u_char  vl_vfpd;        /* Wait before of frame */
  70        u_char  vl_vbpd;        /* Wait end of frame */
  71        u_char  vl_cmd_allow_len; /* Wait end of frame */
  72
  73        unsigned int win_id;
  74        unsigned int init_delay;
  75        unsigned int power_on_delay;
  76        unsigned int reset_delay;
  77        unsigned int interface_mode;
  78        unsigned int mipi_enabled;
  79        unsigned int dp_enabled;
  80        unsigned int cs_setup;
  81        unsigned int wr_setup;
  82        unsigned int wr_act;
  83        unsigned int wr_hold;
  84        unsigned int logo_on;
  85        unsigned int logo_width;
  86        unsigned int logo_height;
  87        int logo_x_offset;
  88        int logo_y_offset;
  89        unsigned long logo_addr;
  90        unsigned int rgb_mode;
  91        unsigned int resolution;
  92
  93        /* parent clock name(MPLL, EPLL or VPLL) */
  94        unsigned int pclk_name;
  95        /* ratio value for source clock from parent clock. */
  96        unsigned int sclk_div;
  97
  98        unsigned int dual_lcd_enabled;
  99        struct exynos_fb *reg;
 100        struct exynos_platform_mipi_dsim *dsim_platform_data_dt;
 101};
 102
 103static void exynos_fimd_set_dualrgb(struct exynos_fb_priv *priv, bool enabled)
 104{
 105        struct exynos_fb *reg = priv->reg;
 106        unsigned int cfg = 0;
 107
 108        if (enabled) {
 109                cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
 110                        EXYNOS_DUALRGB_VDEN_EN_ENABLE;
 111
 112                /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
 113                cfg |= EXYNOS_DUALRGB_SUB_CNT(priv->vl_col / 2) |
 114                        EXYNOS_DUALRGB_MAIN_CNT(0);
 115        }
 116
 117        writel(cfg, &reg->dualrgb);
 118}
 119
 120static void exynos_fimd_set_dp_clkcon(struct exynos_fb_priv *priv,
 121                                      unsigned int enabled)
 122{
 123        struct exynos_fb *reg = priv->reg;
 124        unsigned int cfg = 0;
 125
 126        if (enabled)
 127                cfg = EXYNOS_DP_CLK_ENABLE;
 128
 129        writel(cfg, &reg->dp_mie_clkcon);
 130}
 131
 132static void exynos_fimd_set_par(struct exynos_fb_priv *priv,
 133                                unsigned int win_id)
 134{
 135        struct exynos_fb *reg = priv->reg;
 136        unsigned int cfg = 0;
 137
 138        /* set window control */
 139        cfg = readl((unsigned int)&reg->wincon0 +
 140                        EXYNOS_WINCON(win_id));
 141
 142        cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
 143                EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
 144                EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
 145                EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
 146
 147        /* DATAPATH is DMA */
 148        cfg |= EXYNOS_WINCON_DATAPATH_DMA;
 149
 150        cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
 151
 152        /* dma burst is 16 */
 153        cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
 154
 155        switch (priv->vl_bpix) {
 156        case 4:
 157                cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
 158                break;
 159        default:
 160                cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888;
 161                break;
 162        }
 163
 164        writel(cfg, (unsigned int)&reg->wincon0 +
 165                        EXYNOS_WINCON(win_id));
 166
 167        /* set window position to x=0, y=0*/
 168        cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
 169        writel(cfg, (unsigned int)&reg->vidosd0a +
 170                        EXYNOS_VIDOSD(win_id));
 171
 172        cfg = EXYNOS_VIDOSD_RIGHT_X(priv->vl_col - 1) |
 173                EXYNOS_VIDOSD_BOTTOM_Y(priv->vl_row - 1) |
 174                EXYNOS_VIDOSD_RIGHT_X_E(1) |
 175                EXYNOS_VIDOSD_BOTTOM_Y_E(0);
 176
 177        writel(cfg, (unsigned int)&reg->vidosd0b +
 178                        EXYNOS_VIDOSD(win_id));
 179
 180        /* set window size for window0*/
 181        cfg = EXYNOS_VIDOSD_SIZE(priv->vl_col * priv->vl_row);
 182        writel(cfg, (unsigned int)&reg->vidosd0c +
 183                        EXYNOS_VIDOSD(win_id));
 184}
 185
 186static void exynos_fimd_set_buffer_address(struct exynos_fb_priv *priv,
 187                                           unsigned int win_id,
 188                                           ulong lcd_base_addr)
 189{
 190        struct exynos_fb *reg = priv->reg;
 191        unsigned long start_addr, end_addr;
 192
 193        start_addr = lcd_base_addr;
 194        end_addr = start_addr + ((priv->vl_col * (VNBITS(priv->vl_bpix) / 8)) *
 195                                priv->vl_row);
 196
 197        writel(start_addr, (unsigned int)&reg->vidw00add0b0 +
 198                        EXYNOS_BUFFER_OFFSET(win_id));
 199        writel(end_addr, (unsigned int)&reg->vidw00add1b0 +
 200                        EXYNOS_BUFFER_OFFSET(win_id));
 201}
 202
 203static void exynos_fimd_set_clock(struct exynos_fb_priv *priv)
 204{
 205        struct exynos_fb *reg = priv->reg;
 206        unsigned int cfg = 0, div = 0, remainder, remainder_div;
 207        unsigned long pixel_clock;
 208        unsigned long long src_clock;
 209
 210        if (priv->dual_lcd_enabled) {
 211                pixel_clock = priv->vl_freq *
 212                                (priv->vl_hspw + priv->vl_hfpd +
 213                                 priv->vl_hbpd + priv->vl_col / 2) *
 214                                (priv->vl_vspw + priv->vl_vfpd +
 215                                 priv->vl_vbpd + priv->vl_row);
 216        } else if (priv->interface_mode == FIMD_CPU_INTERFACE) {
 217                pixel_clock = priv->vl_freq *
 218                                priv->vl_width * priv->vl_height *
 219                                (priv->cs_setup + priv->wr_setup +
 220                                 priv->wr_act + priv->wr_hold + 1);
 221        } else {
 222                pixel_clock = priv->vl_freq *
 223                                (priv->vl_hspw + priv->vl_hfpd +
 224                                 priv->vl_hbpd + priv->vl_col) *
 225                                (priv->vl_vspw + priv->vl_vfpd +
 226                                 priv->vl_vbpd + priv->vl_row);
 227        }
 228
 229        cfg = readl(&reg->vidcon0);
 230        cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
 231                EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
 232                EXYNOS_VIDCON0_CLKDIR_MASK);
 233        cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
 234                EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
 235
 236        src_clock = (unsigned long long) get_lcd_clk();
 237
 238        /* get quotient and remainder. */
 239        remainder = do_div(src_clock, pixel_clock);
 240        div = src_clock;
 241
 242        remainder *= 10;
 243        remainder_div = remainder / pixel_clock;
 244
 245        /* round about one places of decimals. */
 246        if (remainder_div >= 5)
 247                div++;
 248
 249        /* in case of dual lcd mode. */
 250        if (priv->dual_lcd_enabled)
 251                div--;
 252
 253        cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
 254        writel(cfg, &reg->vidcon0);
 255}
 256
 257void exynos_set_trigger(struct exynos_fb_priv *priv)
 258{
 259        struct exynos_fb *reg = priv->reg;
 260        unsigned int cfg = 0;
 261
 262        cfg = readl(&reg->trigcon);
 263
 264        cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
 265
 266        writel(cfg, &reg->trigcon);
 267}
 268
 269int exynos_is_i80_frame_done(struct exynos_fb_priv *priv)
 270{
 271        struct exynos_fb *reg = priv->reg;
 272        unsigned int cfg = 0;
 273        int status;
 274
 275        cfg = readl(&reg->trigcon);
 276
 277        /* frame done func is valid only when TRIMODE[0] is set to 1. */
 278        status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
 279                        EXYNOS_I80STATUS_TRIG_DONE;
 280
 281        return status;
 282}
 283
 284static void exynos_fimd_lcd_on(struct exynos_fb_priv *priv)
 285{
 286        struct exynos_fb *reg = priv->reg;
 287        unsigned int cfg = 0;
 288
 289        /* display on */
 290        cfg = readl(&reg->vidcon0);
 291        cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
 292        writel(cfg, &reg->vidcon0);
 293}
 294
 295static void exynos_fimd_window_on(struct exynos_fb_priv *priv,
 296                                  unsigned int win_id)
 297{
 298        struct exynos_fb *reg = priv->reg;
 299        unsigned int cfg = 0;
 300
 301        /* enable window */
 302        cfg = readl((unsigned int)&reg->wincon0 +
 303                        EXYNOS_WINCON(win_id));
 304        cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
 305        writel(cfg, (unsigned int)&reg->wincon0 +
 306                        EXYNOS_WINCON(win_id));
 307
 308        cfg = readl(&reg->winshmap);
 309        cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
 310        writel(cfg, &reg->winshmap);
 311}
 312
 313void exynos_fimd_lcd_off(struct exynos_fb_priv *priv)
 314{
 315        struct exynos_fb *reg = priv->reg;
 316        unsigned int cfg = 0;
 317
 318        cfg = readl(&reg->vidcon0);
 319        cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
 320        writel(cfg, &reg->vidcon0);
 321}
 322
 323void exynos_fimd_window_off(struct exynos_fb_priv *priv, unsigned int win_id)
 324{
 325        struct exynos_fb *reg = priv->reg;
 326        unsigned int cfg = 0;
 327
 328        cfg = readl((unsigned int)&reg->wincon0 +
 329                        EXYNOS_WINCON(win_id));
 330        cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
 331        writel(cfg, (unsigned int)&reg->wincon0 +
 332                        EXYNOS_WINCON(win_id));
 333
 334        cfg = readl(&reg->winshmap);
 335        cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
 336        writel(cfg, &reg->winshmap);
 337}
 338
 339/*
 340* The reset value for FIMD SYSMMU register MMU_CTRL is 3
 341* on Exynos5420 and newer versions.
 342* This means FIMD SYSMMU is on by default on Exynos5420
 343* and newer versions.
 344* Since in u-boot we don't use SYSMMU, we should disable
 345* those FIMD SYSMMU.
 346* Note that there are 2 SYSMMU for FIMD: m0 and m1.
 347* m0 handles windows 0 and 4, and m1 handles windows 1, 2 and 3.
 348* We disable both of them here.
 349*/
 350void exynos_fimd_disable_sysmmu(void)
 351{
 352        u32 *sysmmufimd;
 353        unsigned int node;
 354        int node_list[2];
 355        int count;
 356        int i;
 357
 358        count = fdtdec_find_aliases_for_id(gd->fdt_blob, "fimd",
 359                                COMPAT_SAMSUNG_EXYNOS_SYSMMU, node_list, 2);
 360        for (i = 0; i < count; i++) {
 361                node = node_list[i];
 362                if (node <= 0) {
 363                        debug("Can't get device node for fimd sysmmu\n");
 364                        return;
 365                }
 366
 367                sysmmufimd = (u32 *)fdtdec_get_addr(gd->fdt_blob, node, "reg");
 368                if (!sysmmufimd) {
 369                        debug("Can't get base address for sysmmu fimdm0");
 370                        return;
 371                }
 372
 373                writel(0x0, sysmmufimd);
 374        }
 375}
 376
 377void exynos_fimd_lcd_init(struct udevice *dev)
 378{
 379        struct exynos_fb_priv *priv = dev_get_priv(dev);
 380        struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 381        struct exynos_fb *reg = priv->reg;
 382        unsigned int cfg = 0, rgb_mode;
 383        unsigned int offset;
 384        unsigned int node;
 385
 386        node = dev_of_offset(dev);
 387        if (fdtdec_get_bool(gd->fdt_blob, node, "samsung,disable-sysmmu"))
 388                exynos_fimd_disable_sysmmu();
 389
 390        offset = exynos_fimd_get_base_offset();
 391
 392        rgb_mode = priv->rgb_mode;
 393
 394        if (priv->interface_mode == FIMD_RGB_INTERFACE) {
 395                cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
 396                writel(cfg, &reg->vidcon0);
 397
 398                cfg = readl(&reg->vidcon2);
 399                cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
 400                        EXYNOS_VIDCON2_TVFORMATSEL_MASK |
 401                        EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
 402                cfg |= EXYNOS_VIDCON2_WB_DISABLE;
 403                writel(cfg, &reg->vidcon2);
 404
 405                /* set polarity */
 406                cfg = 0;
 407                if (!priv->vl_clkp)
 408                        cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
 409                if (!priv->vl_hsp)
 410                        cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
 411                if (!priv->vl_vsp)
 412                        cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
 413                if (!priv->vl_dp)
 414                        cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
 415
 416                writel(cfg, (unsigned int)&reg->vidcon1 + offset);
 417
 418                /* set timing */
 419                cfg = EXYNOS_VIDTCON0_VFPD(priv->vl_vfpd - 1);
 420                cfg |= EXYNOS_VIDTCON0_VBPD(priv->vl_vbpd - 1);
 421                cfg |= EXYNOS_VIDTCON0_VSPW(priv->vl_vspw - 1);
 422                writel(cfg, (unsigned int)&reg->vidtcon0 + offset);
 423
 424                cfg = EXYNOS_VIDTCON1_HFPD(priv->vl_hfpd - 1);
 425                cfg |= EXYNOS_VIDTCON1_HBPD(priv->vl_hbpd - 1);
 426                cfg |= EXYNOS_VIDTCON1_HSPW(priv->vl_hspw - 1);
 427
 428                writel(cfg, (unsigned int)&reg->vidtcon1 + offset);
 429
 430                /* set lcd size */
 431                cfg = EXYNOS_VIDTCON2_HOZVAL(priv->vl_col - 1) |
 432                        EXYNOS_VIDTCON2_LINEVAL(priv->vl_row - 1) |
 433                        EXYNOS_VIDTCON2_HOZVAL_E(priv->vl_col - 1) |
 434                        EXYNOS_VIDTCON2_LINEVAL_E(priv->vl_row - 1);
 435
 436                writel(cfg, (unsigned int)&reg->vidtcon2 + offset);
 437        }
 438
 439        /* set display mode */
 440        cfg = readl(&reg->vidcon0);
 441        cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
 442        cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
 443        writel(cfg, &reg->vidcon0);
 444
 445        /* set par */
 446        exynos_fimd_set_par(priv, priv->win_id);
 447
 448        /* set memory address */
 449        exynos_fimd_set_buffer_address(priv, priv->win_id, plat->base);
 450
 451        /* set buffer size */
 452        cfg = EXYNOS_VIDADDR_PAGEWIDTH(priv->vl_col *
 453                        VNBITS(priv->vl_bpix) / 8) |
 454                EXYNOS_VIDADDR_PAGEWIDTH_E(priv->vl_col *
 455                        VNBITS(priv->vl_bpix) / 8) |
 456                EXYNOS_VIDADDR_OFFSIZE(0) |
 457                EXYNOS_VIDADDR_OFFSIZE_E(0);
 458
 459        writel(cfg, (unsigned int)&reg->vidw00add2 +
 460                                        EXYNOS_BUFFER_SIZE(priv->win_id));
 461
 462        /* set clock */
 463        exynos_fimd_set_clock(priv);
 464
 465        /* set rgb mode to dual lcd. */
 466        exynos_fimd_set_dualrgb(priv, priv->dual_lcd_enabled);
 467
 468        /* display on */
 469        exynos_fimd_lcd_on(priv);
 470
 471        /* window on */
 472        exynos_fimd_window_on(priv, priv->win_id);
 473
 474        exynos_fimd_set_dp_clkcon(priv, priv->dp_enabled);
 475}
 476
 477unsigned long exynos_fimd_calc_fbsize(struct exynos_fb_priv *priv)
 478{
 479        return priv->vl_col * priv->vl_row * (VNBITS(priv->vl_bpix) / 8);
 480}
 481
 482int exynos_fb_ofdata_to_platdata(struct udevice *dev)
 483{
 484        struct exynos_fb_priv *priv = dev_get_priv(dev);
 485        unsigned int node = dev_of_offset(dev);
 486        const void *blob = gd->fdt_blob;
 487        fdt_addr_t addr;
 488
 489        addr = devfdt_get_addr(dev);
 490        if (addr == FDT_ADDR_T_NONE) {
 491                debug("Can't get the FIMD base address\n");
 492                return -EINVAL;
 493        }
 494        priv->reg = (struct exynos_fb *)addr;
 495
 496        priv->vl_col = fdtdec_get_int(blob, node, "samsung,vl-col", 0);
 497        if (priv->vl_col == 0) {
 498                debug("Can't get XRES\n");
 499                return -ENXIO;
 500        }
 501
 502        priv->vl_row = fdtdec_get_int(blob, node, "samsung,vl-row", 0);
 503        if (priv->vl_row == 0) {
 504                debug("Can't get YRES\n");
 505                return -ENXIO;
 506        }
 507
 508        priv->vl_width = fdtdec_get_int(blob, node,
 509                                                "samsung,vl-width", 0);
 510
 511        priv->vl_height = fdtdec_get_int(blob, node,
 512                                                "samsung,vl-height", 0);
 513
 514        priv->vl_freq = fdtdec_get_int(blob, node, "samsung,vl-freq", 0);
 515        if (priv->vl_freq == 0) {
 516                debug("Can't get refresh rate\n");
 517                return -ENXIO;
 518        }
 519
 520        if (fdtdec_get_bool(blob, node, "samsung,vl-clkp"))
 521                priv->vl_clkp = VIDEO_ACTIVE_LOW;
 522
 523        if (fdtdec_get_bool(blob, node, "samsung,vl-oep"))
 524                priv->vl_oep = VIDEO_ACTIVE_LOW;
 525
 526        if (fdtdec_get_bool(blob, node, "samsung,vl-hsp"))
 527                priv->vl_hsp = VIDEO_ACTIVE_LOW;
 528
 529        if (fdtdec_get_bool(blob, node, "samsung,vl-vsp"))
 530                priv->vl_vsp = VIDEO_ACTIVE_LOW;
 531
 532        if (fdtdec_get_bool(blob, node, "samsung,vl-dp"))
 533                priv->vl_dp = VIDEO_ACTIVE_LOW;
 534
 535        priv->vl_bpix = fdtdec_get_int(blob, node, "samsung,vl-bpix", 0);
 536        if (priv->vl_bpix == 0) {
 537                debug("Can't get bits per pixel\n");
 538                return -ENXIO;
 539        }
 540
 541        priv->vl_hspw = fdtdec_get_int(blob, node, "samsung,vl-hspw", 0);
 542        if (priv->vl_hspw == 0) {
 543                debug("Can't get hsync width\n");
 544                return -ENXIO;
 545        }
 546
 547        priv->vl_hfpd = fdtdec_get_int(blob, node, "samsung,vl-hfpd", 0);
 548        if (priv->vl_hfpd == 0) {
 549                debug("Can't get right margin\n");
 550                return -ENXIO;
 551        }
 552
 553        priv->vl_hbpd = (u_char)fdtdec_get_int(blob, node,
 554                                                        "samsung,vl-hbpd", 0);
 555        if (priv->vl_hbpd == 0) {
 556                debug("Can't get left margin\n");
 557                return -ENXIO;
 558        }
 559
 560        priv->vl_vspw = (u_char)fdtdec_get_int(blob, node,
 561                                                        "samsung,vl-vspw", 0);
 562        if (priv->vl_vspw == 0) {
 563                debug("Can't get vsync width\n");
 564                return -ENXIO;
 565        }
 566
 567        priv->vl_vfpd = fdtdec_get_int(blob, node,
 568                                                        "samsung,vl-vfpd", 0);
 569        if (priv->vl_vfpd == 0) {
 570                debug("Can't get lower margin\n");
 571                return -ENXIO;
 572        }
 573
 574        priv->vl_vbpd = fdtdec_get_int(blob, node, "samsung,vl-vbpd", 0);
 575        if (priv->vl_vbpd == 0) {
 576                debug("Can't get upper margin\n");
 577                return -ENXIO;
 578        }
 579
 580        priv->vl_cmd_allow_len = fdtdec_get_int(blob, node,
 581                                                "samsung,vl-cmd-allow-len", 0);
 582
 583        priv->win_id = fdtdec_get_int(blob, node, "samsung,winid", 0);
 584        priv->init_delay = fdtdec_get_int(blob, node,
 585                                                "samsung,init-delay", 0);
 586        priv->power_on_delay = fdtdec_get_int(blob, node,
 587                                                "samsung,power-on-delay", 0);
 588        priv->reset_delay = fdtdec_get_int(blob, node,
 589                                                "samsung,reset-delay", 0);
 590        priv->interface_mode = fdtdec_get_int(blob, node,
 591                                                "samsung,interface-mode", 0);
 592        priv->mipi_enabled = fdtdec_get_int(blob, node,
 593                                                "samsung,mipi-enabled", 0);
 594        priv->dp_enabled = fdtdec_get_int(blob, node,
 595                                                "samsung,dp-enabled", 0);
 596        priv->cs_setup = fdtdec_get_int(blob, node,
 597                                                "samsung,cs-setup", 0);
 598        priv->wr_setup = fdtdec_get_int(blob, node,
 599                                                "samsung,wr-setup", 0);
 600        priv->wr_act = fdtdec_get_int(blob, node, "samsung,wr-act", 0);
 601        priv->wr_hold = fdtdec_get_int(blob, node, "samsung,wr-hold", 0);
 602
 603        priv->logo_on = fdtdec_get_int(blob, node, "samsung,logo-on", 0);
 604        if (priv->logo_on) {
 605                priv->logo_width = fdtdec_get_int(blob, node,
 606                                                "samsung,logo-width", 0);
 607                priv->logo_height = fdtdec_get_int(blob, node,
 608                                                "samsung,logo-height", 0);
 609                priv->logo_addr = fdtdec_get_int(blob, node,
 610                                                "samsung,logo-addr", 0);
 611        }
 612
 613        priv->rgb_mode = fdtdec_get_int(blob, node,
 614                                                "samsung,rgb-mode", 0);
 615        priv->pclk_name = fdtdec_get_int(blob, node,
 616                                                "samsung,pclk-name", 0);
 617        priv->sclk_div = fdtdec_get_int(blob, node,
 618                                                "samsung,sclk-div", 0);
 619        priv->dual_lcd_enabled = fdtdec_get_int(blob, node,
 620                                                "samsung,dual-lcd-enabled", 0);
 621
 622        return 0;
 623}
 624
 625static int exynos_fb_probe(struct udevice *dev)
 626{
 627        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 628        struct exynos_fb_priv *priv = dev_get_priv(dev);
 629        struct udevice *panel, *bridge;
 630        struct udevice *dp;
 631        int ret;
 632
 633        debug("%s: start\n", __func__);
 634        set_system_display_ctrl();
 635        set_lcd_clk();
 636
 637#ifdef CONFIG_EXYNOS_MIPI_DSIM
 638        exynos_init_dsim_platform_data(&panel_info);
 639#endif
 640        exynos_fimd_lcd_init(dev);
 641
 642        ret = uclass_first_device(UCLASS_PANEL, &panel);
 643        if (ret) {
 644                printf("LCD panel failed to probe\n");
 645                return ret;
 646        }
 647        if (!panel) {
 648                printf("LCD panel not found\n");
 649                return -ENODEV;
 650        }
 651
 652        ret = uclass_first_device(UCLASS_DISPLAY, &dp);
 653        if (ret) {
 654                debug("%s: Display device error %d\n", __func__, ret);
 655                return ret;
 656        }
 657        if (!dev) {
 658                debug("%s: Display device missing\n", __func__);
 659                return -ENODEV;
 660        }
 661        ret = display_enable(dp, 18, NULL);
 662        if (ret) {
 663                debug("%s: Display enable error %d\n", __func__, ret);
 664                return ret;
 665        }
 666
 667        /* backlight / pwm */
 668        ret = panel_enable_backlight(panel);
 669        if (ret) {
 670                debug("%s: backlight error: %d\n", __func__, ret);
 671                return ret;
 672        }
 673
 674        ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge);
 675        if (!ret)
 676                ret = video_bridge_set_backlight(bridge, 80);
 677        if (ret) {
 678                debug("%s: No video bridge, or no backlight on bridge\n",
 679                      __func__);
 680                exynos_pinmux_config(PERIPH_ID_PWM0, 0);
 681        }
 682
 683        uc_priv->xsize = priv->vl_col;
 684        uc_priv->ysize = priv->vl_row;
 685        uc_priv->bpix = priv->vl_bpix;
 686
 687        /* Enable flushing after LCD writes if requested */
 688        video_set_flush_dcache(dev, true);
 689
 690        return 0;
 691}
 692
 693static int exynos_fb_bind(struct udevice *dev)
 694{
 695        struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 696
 697        /* This is the maximum panel size we expect to see */
 698        plat->size = 1920 * 1080 * 2;
 699
 700        return 0;
 701}
 702
 703static const struct video_ops exynos_fb_ops = {
 704};
 705
 706static const struct udevice_id exynos_fb_ids[] = {
 707        { .compatible = "samsung,exynos-fimd" },
 708        { }
 709};
 710
 711U_BOOT_DRIVER(exynos_fb) = {
 712        .name   = "exynos_fb",
 713        .id     = UCLASS_VIDEO,
 714        .of_match = exynos_fb_ids,
 715        .ops    = &exynos_fb_ops,
 716        .bind   = exynos_fb_bind,
 717        .probe  = exynos_fb_probe,
 718        .ofdata_to_platdata     = exynos_fb_ofdata_to_platdata,
 719        .priv_auto_alloc_size   = sizeof(struct exynos_fb_priv),
 720};
 721