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 31 DECLARE_GLOBAL_DATA_PTR; 32 33 enum { 34 FIMD_RGB_INTERFACE = 1, 35 FIMD_CPU_INTERFACE = 2, 36 }; 37 38 enum 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 45 struct 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 102 static 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, ®->dualrgb); 117 } 118 119 static 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, ®->dp_mie_clkcon); 129 } 130 131 static 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)®->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)®->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)®->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)®->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)®->vidosd0c + 182 EXYNOS_VIDOSD(win_id)); 183 } 184 185 static 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)®->vidw00add0b0 + 197 EXYNOS_BUFFER_OFFSET(win_id)); 198 writel(end_addr, (unsigned int)®->vidw00add1b0 + 199 EXYNOS_BUFFER_OFFSET(win_id)); 200 } 201 202 static 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(®->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, ®->vidcon0); 254 } 255 256 void 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(®->trigcon); 262 263 cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); 264 265 writel(cfg, ®->trigcon); 266 } 267 268 int 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(®->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 283 static 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(®->vidcon0); 290 cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); 291 writel(cfg, ®->vidcon0); 292 } 293 294 static 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)®->wincon0 + 302 EXYNOS_WINCON(win_id)); 303 cfg |= EXYNOS_WINCON_ENWIN_ENABLE; 304 writel(cfg, (unsigned int)®->wincon0 + 305 EXYNOS_WINCON(win_id)); 306 307 cfg = readl(®->winshmap); 308 cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); 309 writel(cfg, ®->winshmap); 310 } 311 312 void 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(®->vidcon0); 318 cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); 319 writel(cfg, ®->vidcon0); 320 } 321 322 void 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)®->wincon0 + 328 EXYNOS_WINCON(win_id)); 329 cfg &= EXYNOS_WINCON_ENWIN_DISABLE; 330 writel(cfg, (unsigned int)®->wincon0 + 331 EXYNOS_WINCON(win_id)); 332 333 cfg = readl(®->winshmap); 334 cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); 335 writel(cfg, ®->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 */ 349 void 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 376 void 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, ®->vidcon0); 396 397 cfg = readl(®->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, ®->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)®->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)®->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)®->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)®->vidtcon2 + offset); 436 } 437 438 /* set display mode */ 439 cfg = readl(®->vidcon0); 440 cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; 441 cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); 442 writel(cfg, ®->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)®->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 476 unsigned 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 481 int 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 624 static 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 692 static 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 702 static const struct video_ops exynos_fb_ops = { 703 }; 704 705 static const struct udevice_id exynos_fb_ids[] = { 706 { .compatible = "samsung,exynos-fimd" }, 707 { } 708 }; 709 710 U_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