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