1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Porting to u-boot: 4 * 5 * (C) Copyright 2010 6 * Stefano Babic, DENX Software Engineering, sbabic@denx.de 7 * 8 * MX51 Linux framebuffer: 9 * 10 * (C) Copyright 2004-2010 Freescale Semiconductor, Inc. 11 */ 12 13#include <common.h> 14#include <log.h> 15#include <part.h> 16#include <asm/cache.h> 17#include <linux/errno.h> 18#include <asm/global_data.h> 19#include <linux/string.h> 20#include <linux/list.h> 21#include <linux/fb.h> 22#include <asm/io.h> 23#include <asm/mach-imx/video.h> 24#include <malloc.h> 25#include "../videomodes.h" 26#include "ipu.h" 27#include "mxcfb.h" 28#include "ipu_regs.h" 29#include "display.h" 30#include <panel.h> 31 32#include <dm.h> 33#include <video.h> 34 35DECLARE_GLOBAL_DATA_PTR; 36 37static int mxcfb_map_video_memory(struct fb_info *fbi); 38static int mxcfb_unmap_video_memory(struct fb_info *fbi); 39 40static struct fb_videomode const *gmode; 41static uint8_t gdisp; 42static uint32_t gpixfmt; 43 44static void fb_videomode_to_var(struct fb_var_screeninfo *var, 45 const struct fb_videomode *mode) 46{ 47 var->xres = mode->xres; 48 var->yres = mode->yres; 49 var->xres_virtual = mode->xres; 50 var->yres_virtual = mode->yres; 51 var->xoffset = 0; 52 var->yoffset = 0; 53 var->pixclock = mode->pixclock; 54 var->left_margin = mode->left_margin; 55 var->right_margin = mode->right_margin; 56 var->upper_margin = mode->upper_margin; 57 var->lower_margin = mode->lower_margin; 58 var->hsync_len = mode->hsync_len; 59 var->vsync_len = mode->vsync_len; 60 var->sync = mode->sync; 61 var->vmode = mode->vmode & FB_VMODE_MASK; 62} 63 64/* 65 * Structure containing the MXC specific framebuffer information. 66 */ 67struct mxcfb_info { 68 struct udevice *udev; 69 int blank; 70 ipu_channel_t ipu_ch; 71 int ipu_di; 72 u32 ipu_di_pix_fmt; 73 unsigned char overlay; 74 unsigned char alpha_chan_en; 75 dma_addr_t alpha_phy_addr0; 76 dma_addr_t alpha_phy_addr1; 77 void *alpha_virt_addr0; 78 void *alpha_virt_addr1; 79 uint32_t alpha_mem_len; 80 uint32_t cur_ipu_buf; 81 uint32_t cur_ipu_alpha_buf; 82 83 u32 pseudo_palette[16]; 84}; 85 86enum { 87 BOTH_ON, 88 SRC_ON, 89 TGT_ON, 90 BOTH_OFF 91}; 92 93static unsigned long default_bpp = 16; 94static unsigned char g_dp_in_use; 95static struct fb_info *mxcfb_info[3]; 96static int ext_clk_used; 97 98static uint32_t bpp_to_pixfmt(struct fb_info *fbi) 99{ 100 uint32_t pixfmt = 0; 101 102 debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel); 103 104 if (fbi->var.nonstd) 105 return fbi->var.nonstd; 106 107 switch (fbi->var.bits_per_pixel) { 108 case 24: 109 pixfmt = IPU_PIX_FMT_BGR24; 110 break; 111 case 32: 112 pixfmt = IPU_PIX_FMT_BGR32; 113 break; 114 case 16: 115 pixfmt = IPU_PIX_FMT_RGB565; 116 break; 117 } 118 return pixfmt; 119} 120 121static int setup_disp_channel1(struct fb_info *fbi) 122{ 123 ipu_channel_params_t params; 124 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; 125 126 memset(¶ms, 0, sizeof(params)); 127 params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; 128 129 debug("%s called\n", __func__); 130 /* 131 * Assuming interlaced means yuv output, below setting also 132 * valid for mem_dc_sync. FG should have the same vmode as BG. 133 */ 134 if (fbi->var.vmode & FB_VMODE_INTERLACED) { 135 params.mem_dp_bg_sync.interlaced = 1; 136 params.mem_dp_bg_sync.out_pixel_fmt = 137 IPU_PIX_FMT_YUV444; 138 } else { 139 if (mxc_fbi->ipu_di_pix_fmt) { 140 params.mem_dp_bg_sync.out_pixel_fmt = 141 mxc_fbi->ipu_di_pix_fmt; 142 } else { 143 params.mem_dp_bg_sync.out_pixel_fmt = 144 IPU_PIX_FMT_RGB666; 145 } 146 } 147 params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi); 148 if (mxc_fbi->alpha_chan_en) 149 params.mem_dp_bg_sync.alpha_chan_en = 1; 150 151 ipu_init_channel(mxc_fbi->ipu_ch, ¶ms); 152 153 return 0; 154} 155 156static int setup_disp_channel2(struct fb_info *fbi) 157{ 158 int retval = 0; 159 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; 160 161 mxc_fbi->cur_ipu_buf = 1; 162 if (mxc_fbi->alpha_chan_en) 163 mxc_fbi->cur_ipu_alpha_buf = 1; 164 165 fbi->var.xoffset = fbi->var.yoffset = 0; 166 167 debug("%s: %x %d %d %d %lx %lx\n", 168 __func__, 169 mxc_fbi->ipu_ch, 170 fbi->var.xres, 171 fbi->var.yres, 172 fbi->fix.line_length, 173 fbi->fix.smem_start, 174 fbi->fix.smem_start + 175 (fbi->fix.line_length * fbi->var.yres)); 176 177 retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, 178 bpp_to_pixfmt(fbi), 179 fbi->var.xres, fbi->var.yres, 180 fbi->fix.line_length, 181 fbi->fix.smem_start + 182 (fbi->fix.line_length * fbi->var.yres), 183 fbi->fix.smem_start, 184 0, 0); 185 if (retval) 186 printf("ipu_init_channel_buffer error %d\n", retval); 187 188 return retval; 189} 190 191/* 192 * Set framebuffer parameters and change the operating mode. 193 * 194 * @param info framebuffer information pointer 195 */ 196static int mxcfb_set_par(struct fb_info *fbi) 197{ 198 int retval = 0; 199 u32 mem_len; 200 ipu_di_signal_cfg_t sig_cfg; 201 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; 202 uint32_t out_pixel_fmt; 203 204 ipu_disable_channel(mxc_fbi->ipu_ch); 205 ipu_uninit_channel(mxc_fbi->ipu_ch); 206 207 mem_len = fbi->var.yres_virtual * fbi->fix.line_length; 208 if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { 209 if (fbi->fix.smem_start) 210 mxcfb_unmap_video_memory(fbi); 211 212 if (mxcfb_map_video_memory(fbi) < 0) 213 return -ENOMEM; 214 } 215 216 setup_disp_channel1(fbi); 217 218 memset(&sig_cfg, 0, sizeof(sig_cfg)); 219 if (fbi->var.vmode & FB_VMODE_INTERLACED) { 220 sig_cfg.interlaced = 1; 221 out_pixel_fmt = IPU_PIX_FMT_YUV444; 222 } else { 223 if (mxc_fbi->ipu_di_pix_fmt) 224 out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; 225 else 226 out_pixel_fmt = IPU_PIX_FMT_RGB666; 227 } 228 if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */ 229 sig_cfg.odd_field_first = 1; 230 if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used) 231 sig_cfg.ext_clk = 1; 232 if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) 233 sig_cfg.Hsync_pol = 1; 234 if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) 235 sig_cfg.Vsync_pol = 1; 236 if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL)) 237 sig_cfg.clk_pol = 1; 238 if (fbi->var.sync & FB_SYNC_DATA_INVERT) 239 sig_cfg.data_pol = 1; 240 if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) 241 sig_cfg.enable_pol = 1; 242 if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) 243 sig_cfg.clkidle_en = 1; 244 245 debug("pixclock = %lu Hz\n", PICOS2KHZ(fbi->var.pixclock) * 1000UL); 246 247 if (ipu_init_sync_panel(mxc_fbi->ipu_di, 248 (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, 249 fbi->var.xres, fbi->var.yres, 250 out_pixel_fmt, 251 fbi->var.left_margin, 252 fbi->var.hsync_len, 253 fbi->var.right_margin, 254 fbi->var.upper_margin, 255 fbi->var.vsync_len, 256 fbi->var.lower_margin, 257 0, sig_cfg) != 0) { 258 puts("mxcfb: Error initializing panel.\n"); 259 return -EINVAL; 260 } 261 262 retval = setup_disp_channel2(fbi); 263 if (retval) 264 return retval; 265 266 if (mxc_fbi->blank == FB_BLANK_UNBLANK) 267 ipu_enable_channel(mxc_fbi->ipu_ch); 268 269 return retval; 270} 271 272/* 273 * Check framebuffer variable parameters and adjust to valid values. 274 * 275 * @param var framebuffer variable parameters 276 * 277 * @param info framebuffer information pointer 278 */ 279static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 280{ 281 u32 vtotal; 282 u32 htotal; 283 284 if (var->xres_virtual < var->xres) 285 var->xres_virtual = var->xres; 286 if (var->yres_virtual < var->yres) 287 var->yres_virtual = var->yres; 288 289 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && 290 (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8)) 291 var->bits_per_pixel = default_bpp; 292 293 switch (var->bits_per_pixel) { 294 case 8: 295 var->red.length = 3; 296 var->red.offset = 5; 297 var->red.msb_right = 0; 298 299 var->green.length = 3; 300 var->green.offset = 2; 301 var->green.msb_right = 0; 302 303 var->blue.length = 2; 304 var->blue.offset = 0; 305 var->blue.msb_right = 0; 306 307 var->transp.length = 0; 308 var->transp.offset = 0; 309 var->transp.msb_right = 0; 310 break; 311 case 16: 312 var->red.length = 5; 313 var->red.offset = 11; 314 var->red.msb_right = 0; 315 316 var->green.length = 6; 317 var->green.offset = 5; 318 var->green.msb_right = 0; 319 320 var->blue.length = 5; 321 var->blue.offset = 0; 322 var->blue.msb_right = 0; 323 324 var->transp.length = 0; 325 var->transp.offset = 0; 326 var->transp.msb_right = 0; 327 break; 328 case 24: 329 var->red.length = 8; 330 var->red.offset = 16; 331 var->red.msb_right = 0; 332 333 var->green.length = 8; 334 var->green.offset = 8; 335 var->green.msb_right = 0; 336 337 var->blue.length = 8; 338 var->blue.offset = 0; 339 var->blue.msb_right = 0; 340 341 var->transp.length = 0; 342 var->transp.offset = 0; 343 var->transp.msb_right = 0; 344 break; 345 case 32: 346 var->red.length = 8; 347 var->red.offset = 16; 348 var->red.msb_right = 0; 349 350 var->green.length = 8; 351 var->green.offset = 8; 352 var->green.msb_right = 0; 353 354 var->blue.length = 8; 355 var->blue.offset = 0; 356 var->blue.msb_right = 0; 357 358 var->transp.length = 8; 359 var->transp.offset = 24; 360 var->transp.msb_right = 0; 361 break; 362 } 363 364 if (var->pixclock < 1000) { 365 htotal = var->xres + var->right_margin + var->hsync_len + 366 var->left_margin; 367 vtotal = var->yres + var->lower_margin + var->vsync_len + 368 var->upper_margin; 369 var->pixclock = (vtotal * htotal * 6UL) / 100UL; 370 var->pixclock = KHZ2PICOS(var->pixclock); 371 printf("pixclock set for 60Hz refresh = %u ps\n", 372 var->pixclock); 373 } 374 375 var->height = -1; 376 var->width = -1; 377 var->grayscale = 0; 378 379 return 0; 380} 381 382static int mxcfb_map_video_memory(struct fb_info *fbi) 383{ 384 struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; 385 struct video_uc_plat *plat = dev_get_uclass_plat(mxc_fbi->udev); 386 387 if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) { 388 fbi->fix.smem_len = fbi->var.yres_virtual * 389 fbi->fix.line_length; 390 } 391 fbi->fix.smem_len = roundup(fbi->fix.smem_len, ARCH_DMA_MINALIGN); 392 393 fbi->screen_base = (char *)plat->base; 394 395 fbi->fix.smem_start = (unsigned long)fbi->screen_base; 396 if (fbi->screen_base == 0) { 397 puts("Unable to allocate framebuffer memory\n"); 398 fbi->fix.smem_len = 0; 399 fbi->fix.smem_start = 0; 400 return -EBUSY; 401 } 402 403 debug("allocated fb @ paddr=0x%08X, size=%d.\n", 404 (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len); 405 406 fbi->screen_size = fbi->fix.smem_len; 407 gd->fb_base = fbi->fix.smem_start; 408 409 /* Clear the screen */ 410 memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); 411 412 return 0; 413} 414 415static int mxcfb_unmap_video_memory(struct fb_info *fbi) 416{ 417 fbi->screen_base = 0; 418 fbi->fix.smem_start = 0; 419 fbi->fix.smem_len = 0; 420 return 0; 421} 422 423/* 424 * Initializes the framebuffer information pointer. After allocating 425 * sufficient memory for the framebuffer structure, the fields are 426 * filled with custom information passed in from the configurable 427 * structures. This includes information such as bits per pixel, 428 * color maps, screen width/height and RGBA offsets. 429 * 430 * Return: Framebuffer structure initialized with our information 431 */ 432static struct fb_info *mxcfb_init_fbinfo(void) 433{ 434#define BYTES_PER_LONG 4 435#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) 436 struct fb_info *fbi; 437 struct mxcfb_info *mxcfbi; 438 char *p; 439 int size = sizeof(struct mxcfb_info) + PADDING + 440 sizeof(struct fb_info); 441 442 debug("%s: %d %d %d %d\n", 443 __func__, 444 PADDING, 445 size, 446 sizeof(struct mxcfb_info), 447 sizeof(struct fb_info)); 448 /* 449 * Allocate sufficient memory for the fb structure 450 */ 451 452 p = malloc(size); 453 if (!p) 454 return NULL; 455 456 memset(p, 0, size); 457 458 fbi = (struct fb_info *)p; 459 fbi->par = p + sizeof(struct fb_info) + PADDING; 460 461 mxcfbi = (struct mxcfb_info *)fbi->par; 462 debug("Framebuffer structures at: fbi=0x%x mxcfbi=0x%x\n", 463 (unsigned int)fbi, (unsigned int)mxcfbi); 464 465 fbi->var.activate = FB_ACTIVATE_NOW; 466 467 fbi->flags = FBINFO_FLAG_DEFAULT; 468 fbi->pseudo_palette = mxcfbi->pseudo_palette; 469 470 return fbi; 471} 472 473extern struct clk *g_ipu_clk; 474 475/* 476 * Probe routine for the framebuffer driver. It is called during the 477 * driver binding process. The following functions are performed in 478 * this routine: Framebuffer initialization, Memory allocation and 479 * mapping, Framebuffer registration, IPU initialization. 480 * 481 * Return: Appropriate error code to the kernel common code 482 */ 483static int mxcfb_probe(struct udevice *dev, u32 interface_pix_fmt, 484 uint8_t disp, struct fb_videomode const *mode) 485{ 486 struct fb_info *fbi; 487 struct mxcfb_info *mxcfbi; 488 489 /* 490 * Initialize FB structures 491 */ 492 fbi = mxcfb_init_fbinfo(); 493 if (!fbi) 494 return -ENOMEM; 495 496 mxcfbi = (struct mxcfb_info *)fbi->par; 497 498 if (!g_dp_in_use) { 499 mxcfbi->ipu_ch = MEM_BG_SYNC; 500 mxcfbi->blank = FB_BLANK_UNBLANK; 501 } else { 502 mxcfbi->ipu_ch = MEM_DC_SYNC; 503 mxcfbi->blank = FB_BLANK_POWERDOWN; 504 } 505 506 mxcfbi->ipu_di = disp; 507 mxcfbi->udev = dev; 508 509 if (!ipu_clk_enabled()) 510 clk_enable(g_ipu_clk); 511 512 ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); 513 ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); 514 515 g_dp_in_use = 1; 516 517 mxcfb_info[mxcfbi->ipu_di] = fbi; 518 519 /* Need dummy values until real panel is configured */ 520 521 mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; 522 fb_videomode_to_var(&fbi->var, mode); 523 fbi->var.bits_per_pixel = 16; 524 fbi->fix.line_length = fbi->var.xres_virtual * 525 (fbi->var.bits_per_pixel / 8); 526 fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length; 527 528 mxcfb_check_var(&fbi->var, fbi); 529 530 /* Default Y virtual size is 2x panel size */ 531 fbi->var.yres_virtual = fbi->var.yres * 2; 532 533 /* allocate fb first */ 534 if (mxcfb_map_video_memory(fbi) < 0) 535 return -ENOMEM; 536 537 mxcfb_set_par(fbi); 538 539#ifdef DEBUG 540 ipu_dump_registers(); 541#endif 542 543 return 0; 544} 545 546void ipuv3_fb_shutdown(void) 547{ 548 int i; 549 struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT; 550 551 if (!ipu_clk_enabled()) 552 return; 553 554 for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) { 555 struct fb_info *fbi = mxcfb_info[i]; 556 if (fbi) { 557 struct mxcfb_info *mxc_fbi = fbi->par; 558 ipu_disable_channel(mxc_fbi->ipu_ch); 559 ipu_uninit_channel(mxc_fbi->ipu_ch); 560 } 561 } 562 for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) { 563 __raw_writel(__raw_readl(&stat->int_stat[i]), 564 &stat->int_stat[i]); 565 } 566} 567 568int ipuv3_fb_init(struct fb_videomode const *mode, 569 uint8_t disp, 570 uint32_t pixfmt) 571{ 572 gmode = mode; 573 gdisp = disp; 574 gpixfmt = pixfmt; 575 576 return 0; 577} 578 579enum { 580 /* Maximum display size we support */ 581 LCD_MAX_WIDTH = 1920, 582 LCD_MAX_HEIGHT = 1080, 583 LCD_MAX_LOG2_BPP = VIDEO_BPP16, 584}; 585 586static int ipuv3_video_probe(struct udevice *dev) 587{ 588 struct video_uc_plat *plat = dev_get_uclass_plat(dev); 589 struct video_priv *uc_priv = dev_get_uclass_priv(dev); 590#if defined(CONFIG_DISPLAY) 591 struct udevice *disp_dev; 592#endif 593 u32 fb_start, fb_end; 594 int ret; 595 596 debug("%s() plat: base 0x%lx, size 0x%x\n", 597 __func__, plat->base, plat->size); 598 599 ret = ipu_probe(); 600 if (ret) 601 return ret; 602 603 ret = ipu_displays_init(); 604 if (ret < 0) 605 return ret; 606 607 ret = mxcfb_probe(dev, gpixfmt, gdisp, gmode); 608 if (ret < 0) 609 return ret; 610 611#if defined(CONFIG_DISPLAY) 612 ret = uclass_first_device_err(UCLASS_DISPLAY, &disp_dev); 613 if (!ret) 614 ret = display_enable(disp_dev, 16, NULL); 615 if (ret < 0) 616 return ret; 617#endif 618 if (IS_ENABLED(CONFIG_PANEL)) { 619 struct udevice *panel_dev; 620 621 ret = uclass_get_device(UCLASS_PANEL, 0, &panel_dev); 622 if (panel_dev) 623 panel_enable_backlight(panel_dev); 624 } 625 626 uc_priv->xsize = gmode->xres; 627 uc_priv->ysize = gmode->yres; 628 uc_priv->bpix = LCD_MAX_LOG2_BPP; 629 630 /* Enable dcache for the frame buffer */ 631 fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); 632 fb_end = plat->base + plat->size; 633 fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT); 634 mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, 635 DCACHE_WRITEBACK); 636 video_set_flush_dcache(dev, true); 637 gd->fb_base = fb_start; 638 639 return 0; 640} 641 642struct ipuv3_video_priv { 643 ulong regs; 644}; 645 646static int ipuv3_video_bind(struct udevice *dev) 647{ 648 struct video_uc_plat *plat = dev_get_uclass_plat(dev); 649 650 plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * 651 (1 << VIDEO_BPP32) / 8; 652 653 return 0; 654} 655 656static const struct udevice_id ipuv3_video_ids[] = { 657#ifdef CONFIG_ARCH_MX6 658 { .compatible = "fsl,imx6q-ipu" }, 659#endif 660#ifdef CONFIG_ARCH_MX5 661 { .compatible = "fsl,imx53-ipu" }, 662#endif 663 { } 664}; 665 666U_BOOT_DRIVER(fsl_imx6q_ipu) = { 667 .name = "fsl_imx6q_ipu", 668 .id = UCLASS_VIDEO, 669 .of_match = ipuv3_video_ids, 670 .bind = ipuv3_video_bind, 671 .probe = ipuv3_video_probe, 672 .priv_auto = sizeof(struct ipuv3_video_priv), 673 .flags = DM_FLAG_PRE_RELOC, 674}; 675