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