1 /* 2 * Common LCD routines for supported CPUs 3 * 4 * (C) Copyright 2001-2002 5 * Wolfgang Denk, DENX Software Engineering -- wd@denx.de 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 /************************************************************************/ 11 /* ** HEADER FILES */ 12 /************************************************************************/ 13 14 /* #define DEBUG */ 15 16 #include <config.h> 17 #include <common.h> 18 #include <command.h> 19 #include <stdarg.h> 20 #include <search.h> 21 #include <env_callback.h> 22 #include <linux/types.h> 23 #include <stdio_dev.h> 24 #if defined(CONFIG_POST) 25 #include <post.h> 26 #endif 27 #include <lcd.h> 28 #include <watchdog.h> 29 #include <asm/unaligned.h> 30 #include <splash.h> 31 #include <asm/io.h> 32 #include <asm/unaligned.h> 33 #include <fdt_support.h> 34 35 #if defined(CONFIG_CPU_PXA25X) || defined(CONFIG_CPU_PXA27X) || \ 36 defined(CONFIG_CPU_MONAHANS) 37 #include <asm/byteorder.h> 38 #endif 39 40 #if defined(CONFIG_MPC823) 41 #include <lcdvideo.h> 42 #endif 43 44 #if defined(CONFIG_ATMEL_LCD) 45 #include <atmel_lcdc.h> 46 #endif 47 48 #if defined(CONFIG_LCD_DT_SIMPLEFB) 49 #include <libfdt.h> 50 #endif 51 52 /************************************************************************/ 53 /* ** FONT DATA */ 54 /************************************************************************/ 55 #include <video_font.h> /* Get font data, width and height */ 56 57 /************************************************************************/ 58 /* ** LOGO DATA */ 59 /************************************************************************/ 60 #ifdef CONFIG_LCD_LOGO 61 # include <bmp_logo.h> /* Get logo data, width and height */ 62 # include <bmp_logo_data.h> 63 # if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16) 64 # error Default Color Map overlaps with Logo Color Map 65 # endif 66 #endif 67 68 #ifdef CONFIG_SANDBOX 69 #include <asm/sdl.h> 70 #endif 71 72 #ifndef CONFIG_LCD_ALIGNMENT 73 #define CONFIG_LCD_ALIGNMENT PAGE_SIZE 74 #endif 75 76 /* By default we scroll by a single line */ 77 #ifndef CONFIG_CONSOLE_SCROLL_LINES 78 #define CONFIG_CONSOLE_SCROLL_LINES 1 79 #endif 80 81 /************************************************************************/ 82 /* ** CONSOLE DEFINITIONS & FUNCTIONS */ 83 /************************************************************************/ 84 #define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * lcd_line_length) 85 #define CONSOLE_ROW_FIRST lcd_console_address 86 #define CONSOLE_ROW_SECOND (lcd_console_address + CONSOLE_ROW_SIZE) 87 #define CONSOLE_ROW_LAST (lcd_console_address + CONSOLE_SIZE \ 88 - CONSOLE_ROW_SIZE) 89 #define CONSOLE_SIZE (CONSOLE_ROW_SIZE * console_rows) 90 #define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE) 91 92 #if (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16) || \ 93 (LCD_BPP == LCD_COLOR32) 94 # define COLOR_MASK(c) (c) 95 #else 96 # error Unsupported LCD BPP. 97 #endif 98 99 DECLARE_GLOBAL_DATA_PTR; 100 101 static void lcd_drawchars(ushort x, ushort y, uchar *str, int count); 102 static inline void lcd_putc_xy(ushort x, ushort y, uchar c); 103 104 static int lcd_init(void *lcdbase); 105 106 static void *lcd_logo(void); 107 108 static void lcd_setfgcolor(int color); 109 static void lcd_setbgcolor(int color); 110 111 static int lcd_color_fg; 112 static int lcd_color_bg; 113 int lcd_line_length; 114 115 char lcd_is_enabled = 0; 116 117 static short console_curr_col; 118 static short console_curr_row; 119 static short console_cols; 120 static short console_rows; 121 122 static void *lcd_console_address; 123 static void *lcd_base; /* Start of framebuffer memory */ 124 125 static char lcd_flush_dcache; /* 1 to flush dcache after each lcd update */ 126 127 /************************************************************************/ 128 129 /* Flush LCD activity to the caches */ 130 void lcd_sync(void) 131 { 132 /* 133 * flush_dcache_range() is declared in common.h but it seems that some 134 * architectures do not actually implement it. Is there a way to find 135 * out whether it exists? For now, ARM is safe. 136 */ 137 #if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF) 138 int line_length; 139 140 if (lcd_flush_dcache) 141 flush_dcache_range((u32)lcd_base, 142 (u32)(lcd_base + lcd_get_size(&line_length))); 143 #elif defined(CONFIG_SANDBOX) && defined(CONFIG_VIDEO_SANDBOX_SDL) 144 static ulong last_sync; 145 146 if (get_timer(last_sync) > 10) { 147 sandbox_sdl_sync(lcd_base); 148 last_sync = get_timer(0); 149 } 150 #endif 151 } 152 153 void lcd_set_flush_dcache(int flush) 154 { 155 lcd_flush_dcache = (flush != 0); 156 } 157 158 void lcd_init_console(void *address, int rows, int cols) 159 { 160 console_curr_col = 0; 161 console_curr_row = 0; 162 console_cols = cols; 163 console_rows = rows; 164 lcd_console_address = address; 165 } 166 167 void lcd_set_col(short col) 168 { 169 console_curr_col = col; 170 } 171 172 void lcd_set_row(short row) 173 { 174 console_curr_row = row; 175 } 176 177 /*----------------------------------------------------------------------*/ 178 179 static void console_scrollup(void) 180 { 181 const int rows = CONFIG_CONSOLE_SCROLL_LINES; 182 183 /* Copy up rows ignoring those that will be overwritten */ 184 memcpy(CONSOLE_ROW_FIRST, 185 lcd_console_address + CONSOLE_ROW_SIZE * rows, 186 CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows); 187 188 /* Clear the last rows */ 189 #if (LCD_BPP != LCD_COLOR32) 190 memset(lcd_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows, 191 COLOR_MASK(lcd_color_bg), 192 CONSOLE_ROW_SIZE * rows); 193 #else 194 u32 *ppix = lcd_console_address + 195 CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows; 196 u32 i; 197 for (i = 0; 198 i < (CONSOLE_ROW_SIZE * rows) / NBYTES(panel_info.vl_bpix); 199 i++) { 200 *ppix++ = COLOR_MASK(lcd_color_bg); 201 } 202 #endif 203 lcd_sync(); 204 console_curr_row -= rows; 205 } 206 207 /*----------------------------------------------------------------------*/ 208 209 static inline void console_back(void) 210 { 211 if (--console_curr_col < 0) { 212 console_curr_col = console_cols - 1; 213 if (--console_curr_row < 0) 214 console_curr_row = 0; 215 } 216 217 lcd_putc_xy(console_curr_col * VIDEO_FONT_WIDTH, 218 console_curr_row * VIDEO_FONT_HEIGHT, ' '); 219 } 220 221 /*----------------------------------------------------------------------*/ 222 223 static inline void console_newline(void) 224 { 225 console_curr_col = 0; 226 227 /* Check if we need to scroll the terminal */ 228 if (++console_curr_row >= console_rows) 229 console_scrollup(); 230 else 231 lcd_sync(); 232 } 233 234 /*----------------------------------------------------------------------*/ 235 236 static void lcd_stub_putc(struct stdio_dev *dev, const char c) 237 { 238 lcd_putc(c); 239 } 240 241 void lcd_putc(const char c) 242 { 243 if (!lcd_is_enabled) { 244 serial_putc(c); 245 246 return; 247 } 248 249 switch (c) { 250 case '\r': 251 console_curr_col = 0; 252 253 return; 254 case '\n': 255 console_newline(); 256 257 return; 258 case '\t': /* Tab (8 chars alignment) */ 259 console_curr_col += 8; 260 console_curr_col &= ~7; 261 262 if (console_curr_col >= console_cols) 263 console_newline(); 264 265 return; 266 case '\b': 267 console_back(); 268 269 return; 270 default: 271 lcd_putc_xy(console_curr_col * VIDEO_FONT_WIDTH, 272 console_curr_row * VIDEO_FONT_HEIGHT, c); 273 if (++console_curr_col >= console_cols) 274 console_newline(); 275 } 276 } 277 278 /*----------------------------------------------------------------------*/ 279 280 static void lcd_stub_puts(struct stdio_dev *dev, const char *s) 281 { 282 lcd_puts(s); 283 } 284 285 void lcd_puts(const char *s) 286 { 287 if (!lcd_is_enabled) { 288 serial_puts(s); 289 290 return; 291 } 292 293 while (*s) 294 lcd_putc(*s++); 295 296 lcd_sync(); 297 } 298 299 /*----------------------------------------------------------------------*/ 300 301 void lcd_printf(const char *fmt, ...) 302 { 303 va_list args; 304 char buf[CONFIG_SYS_PBSIZE]; 305 306 va_start(args, fmt); 307 vsprintf(buf, fmt, args); 308 va_end(args); 309 310 lcd_puts(buf); 311 } 312 313 /************************************************************************/ 314 /* ** Low-Level Graphics Routines */ 315 /************************************************************************/ 316 317 static void lcd_drawchars(ushort x, ushort y, uchar *str, int count) 318 { 319 uchar *dest; 320 ushort row; 321 322 #if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) 323 y += BMP_LOGO_HEIGHT; 324 #endif 325 326 dest = (uchar *)(lcd_base + y * lcd_line_length + x * NBITS(LCD_BPP)/8); 327 328 for (row = 0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) { 329 uchar *s = str; 330 int i; 331 #if LCD_BPP == LCD_COLOR16 332 ushort *d = (ushort *)dest; 333 #elif LCD_BPP == LCD_COLOR32 334 u32 *d = (u32 *)dest; 335 #else 336 uchar *d = dest; 337 #endif 338 339 for (i = 0; i < count; ++i) { 340 uchar c, bits; 341 342 c = *s++; 343 bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row]; 344 345 for (c = 0; c < 8; ++c) { 346 *d++ = (bits & 0x80) ? 347 lcd_color_fg : lcd_color_bg; 348 bits <<= 1; 349 } 350 } 351 } 352 } 353 354 static inline void lcd_putc_xy(ushort x, ushort y, uchar c) 355 { 356 lcd_drawchars(x, y, &c, 1); 357 } 358 359 /************************************************************************/ 360 /** Small utility to check that you got the colours right */ 361 /************************************************************************/ 362 #ifdef LCD_TEST_PATTERN 363 364 #define N_BLK_VERT 2 365 #define N_BLK_HOR 3 366 367 static int test_colors[N_BLK_HOR * N_BLK_VERT] = { 368 CONSOLE_COLOR_RED, CONSOLE_COLOR_GREEN, CONSOLE_COLOR_YELLOW, 369 CONSOLE_COLOR_BLUE, CONSOLE_COLOR_MAGENTA, CONSOLE_COLOR_CYAN, 370 }; 371 372 static void test_pattern(void) 373 { 374 ushort v_max = panel_info.vl_row; 375 ushort h_max = panel_info.vl_col; 376 ushort v_step = (v_max + N_BLK_VERT - 1) / N_BLK_VERT; 377 ushort h_step = (h_max + N_BLK_HOR - 1) / N_BLK_HOR; 378 ushort v, h; 379 uchar *pix = (uchar *)lcd_base; 380 381 printf("[LCD] Test Pattern: %d x %d [%d x %d]\n", 382 h_max, v_max, h_step, v_step); 383 384 /* WARNING: Code silently assumes 8bit/pixel */ 385 for (v = 0; v < v_max; ++v) { 386 uchar iy = v / v_step; 387 for (h = 0; h < h_max; ++h) { 388 uchar ix = N_BLK_HOR * iy + h / h_step; 389 *pix++ = test_colors[ix]; 390 } 391 } 392 } 393 #endif /* LCD_TEST_PATTERN */ 394 395 396 /************************************************************************/ 397 /* ** GENERIC Initialization Routines */ 398 /************************************************************************/ 399 /* 400 * With most lcd drivers the line length is set up 401 * by calculating it from panel_info parameters. Some 402 * drivers need to calculate the line length differently, 403 * so make the function weak to allow overriding it. 404 */ 405 __weak int lcd_get_size(int *line_length) 406 { 407 *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; 408 return *line_length * panel_info.vl_row; 409 } 410 411 int drv_lcd_init(void) 412 { 413 struct stdio_dev lcddev; 414 int rc; 415 416 lcd_base = map_sysmem(gd->fb_base, 0); 417 418 lcd_init(lcd_base); /* LCD initialization */ 419 420 /* Device initialization */ 421 memset(&lcddev, 0, sizeof(lcddev)); 422 423 strcpy(lcddev.name, "lcd"); 424 lcddev.ext = 0; /* No extensions */ 425 lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */ 426 lcddev.putc = lcd_stub_putc; /* 'putc' function */ 427 lcddev.puts = lcd_stub_puts; /* 'puts' function */ 428 429 rc = stdio_register(&lcddev); 430 431 return (rc == 0) ? 1 : rc; 432 } 433 434 /*----------------------------------------------------------------------*/ 435 void lcd_clear(void) 436 { 437 short console_rows, console_cols; 438 #if LCD_BPP == LCD_COLOR8 439 /* Setting the palette */ 440 lcd_setcolreg(CONSOLE_COLOR_BLACK, 0, 0, 0); 441 lcd_setcolreg(CONSOLE_COLOR_RED, 0xFF, 0, 0); 442 lcd_setcolreg(CONSOLE_COLOR_GREEN, 0, 0xFF, 0); 443 lcd_setcolreg(CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0); 444 lcd_setcolreg(CONSOLE_COLOR_BLUE, 0, 0, 0xFF); 445 lcd_setcolreg(CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF); 446 lcd_setcolreg(CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF); 447 lcd_setcolreg(CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA); 448 lcd_setcolreg(CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF); 449 #endif 450 451 #ifndef CONFIG_SYS_WHITE_ON_BLACK 452 lcd_setfgcolor(CONSOLE_COLOR_BLACK); 453 lcd_setbgcolor(CONSOLE_COLOR_WHITE); 454 #else 455 lcd_setfgcolor(CONSOLE_COLOR_WHITE); 456 lcd_setbgcolor(CONSOLE_COLOR_BLACK); 457 #endif /* CONFIG_SYS_WHITE_ON_BLACK */ 458 459 #ifdef LCD_TEST_PATTERN 460 test_pattern(); 461 #else 462 /* set framebuffer to background color */ 463 #if (LCD_BPP != LCD_COLOR32) 464 memset((char *)lcd_base, 465 COLOR_MASK(lcd_color_bg), 466 lcd_line_length * panel_info.vl_row); 467 #else 468 u32 *ppix = lcd_base; 469 u32 i; 470 for (i = 0; 471 i < (lcd_line_length * panel_info.vl_row)/NBYTES(panel_info.vl_bpix); 472 i++) { 473 *ppix++ = COLOR_MASK(lcd_color_bg); 474 } 475 #endif 476 #endif 477 /* Paint the logo and retrieve LCD base address */ 478 debug("[LCD] Drawing the logo...\n"); 479 #if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) 480 console_rows = (panel_info.vl_row - BMP_LOGO_HEIGHT); 481 console_rows /= VIDEO_FONT_HEIGHT; 482 #else 483 console_rows = panel_info.vl_row / VIDEO_FONT_HEIGHT; 484 #endif 485 console_cols = panel_info.vl_col / VIDEO_FONT_WIDTH; 486 lcd_init_console(lcd_logo(), console_rows, console_cols); 487 lcd_sync(); 488 } 489 490 static int do_lcd_clear(cmd_tbl_t *cmdtp, int flag, int argc, 491 char *const argv[]) 492 { 493 lcd_clear(); 494 return 0; 495 } 496 497 U_BOOT_CMD( 498 cls, 1, 1, do_lcd_clear, 499 "clear screen", 500 "" 501 ); 502 503 /*----------------------------------------------------------------------*/ 504 505 static int lcd_init(void *lcdbase) 506 { 507 /* Initialize the lcd controller */ 508 debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase); 509 510 lcd_ctrl_init(lcdbase); 511 512 /* 513 * lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi) ignores 514 * the 'lcdbase' argument and uses custom lcd base address 515 * by setting up gd->fb_base. Check for this condition and fixup 516 * 'lcd_base' address. 517 */ 518 if (map_to_sysmem(lcdbase) != gd->fb_base) 519 lcd_base = map_sysmem(gd->fb_base, 0); 520 521 debug("[LCD] Using LCD frambuffer at %p\n", lcd_base); 522 523 lcd_get_size(&lcd_line_length); 524 lcd_is_enabled = 1; 525 lcd_clear(); 526 lcd_enable(); 527 528 /* Initialize the console */ 529 lcd_set_col(0); 530 #ifdef CONFIG_LCD_INFO_BELOW_LOGO 531 lcd_set_row(7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT); 532 #else 533 lcd_set_row(1); /* leave 1 blank line below logo */ 534 #endif 535 536 return 0; 537 } 538 539 540 /************************************************************************/ 541 /* ** ROM capable initialization part - needed to reserve FB memory */ 542 /************************************************************************/ 543 /* 544 * This is called early in the system initialization to grab memory 545 * for the LCD controller. 546 * Returns new address for monitor, after reserving LCD buffer memory 547 * 548 * Note that this is running from ROM, so no write access to global data. 549 */ 550 ulong lcd_setmem(ulong addr) 551 { 552 ulong size; 553 int line_length; 554 555 debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col, 556 panel_info.vl_row, NBITS(panel_info.vl_bpix)); 557 558 size = lcd_get_size(&line_length); 559 560 /* Round up to nearest full page, or MMU section if defined */ 561 size = ALIGN(size, CONFIG_LCD_ALIGNMENT); 562 addr = ALIGN(addr - CONFIG_LCD_ALIGNMENT + 1, CONFIG_LCD_ALIGNMENT); 563 564 /* Allocate pages for the frame buffer. */ 565 addr -= size; 566 567 debug("Reserving %ldk for LCD Framebuffer at: %08lx\n", 568 size >> 10, addr); 569 570 return addr; 571 } 572 573 /*----------------------------------------------------------------------*/ 574 575 static void lcd_setfgcolor(int color) 576 { 577 lcd_color_fg = color; 578 } 579 580 /*----------------------------------------------------------------------*/ 581 582 static void lcd_setbgcolor(int color) 583 { 584 lcd_color_bg = color; 585 } 586 587 /************************************************************************/ 588 /* ** Chipset depending Bitmap / Logo stuff... */ 589 /************************************************************************/ 590 static inline ushort *configuration_get_cmap(void) 591 { 592 #if defined CONFIG_CPU_PXA 593 struct pxafb_info *fbi = &panel_info.pxa; 594 return (ushort *)fbi->palette; 595 #elif defined(CONFIG_MPC823) 596 immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 597 cpm8xx_t *cp = &(immr->im_cpm); 598 return (ushort *)&(cp->lcd_cmap[255 * sizeof(ushort)]); 599 #elif defined(CONFIG_ATMEL_LCD) 600 return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0)); 601 #elif !defined(CONFIG_ATMEL_HLCD) && !defined(CONFIG_EXYNOS_FB) 602 return panel_info.cmap; 603 #elif defined(CONFIG_LCD_LOGO) 604 return bmp_logo_palette; 605 #else 606 return NULL; 607 #endif 608 } 609 610 #ifdef CONFIG_LCD_LOGO 611 void bitmap_plot(int x, int y) 612 { 613 #ifdef CONFIG_ATMEL_LCD 614 uint *cmap = (uint *)bmp_logo_palette; 615 #else 616 ushort *cmap = (ushort *)bmp_logo_palette; 617 #endif 618 ushort i, j; 619 uchar *bmap; 620 uchar *fb; 621 ushort *fb16; 622 #if defined(CONFIG_MPC823) 623 immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; 624 cpm8xx_t *cp = &(immr->im_cpm); 625 #endif 626 unsigned bpix = NBITS(panel_info.vl_bpix); 627 628 debug("Logo: width %d height %d colors %d cmap %d\n", 629 BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS, 630 ARRAY_SIZE(bmp_logo_palette)); 631 632 bmap = &bmp_logo_bitmap[0]; 633 fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8); 634 635 if (bpix < 12) { 636 /* Leave room for default color map 637 * default case: generic system with no cmap (most likely 16bpp) 638 * cmap was set to the source palette, so no change is done. 639 * This avoids even more ifdefs in the next stanza 640 */ 641 #if defined(CONFIG_MPC823) 642 cmap = (ushort *) &(cp->lcd_cmap[BMP_LOGO_OFFSET * sizeof(ushort)]); 643 #elif defined(CONFIG_ATMEL_LCD) 644 cmap = (uint *)configuration_get_cmap(); 645 #else 646 cmap = configuration_get_cmap(); 647 #endif 648 649 WATCHDOG_RESET(); 650 651 /* Set color map */ 652 for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i) { 653 ushort colreg = bmp_logo_palette[i]; 654 #ifdef CONFIG_ATMEL_LCD 655 uint lut_entry; 656 #ifdef CONFIG_ATMEL_LCD_BGR555 657 lut_entry = ((colreg & 0x000F) << 11) | 658 ((colreg & 0x00F0) << 2) | 659 ((colreg & 0x0F00) >> 7); 660 #else /* CONFIG_ATMEL_LCD_RGB565 */ 661 lut_entry = ((colreg & 0x000F) << 1) | 662 ((colreg & 0x00F0) << 3) | 663 ((colreg & 0x0F00) << 4); 664 #endif 665 *(cmap + BMP_LOGO_OFFSET) = lut_entry; 666 cmap++; 667 #else /* !CONFIG_ATMEL_LCD */ 668 *cmap++ = colreg; 669 #endif /* CONFIG_ATMEL_LCD */ 670 } 671 672 WATCHDOG_RESET(); 673 674 for (i = 0; i < BMP_LOGO_HEIGHT; ++i) { 675 memcpy(fb, bmap, BMP_LOGO_WIDTH); 676 bmap += BMP_LOGO_WIDTH; 677 fb += panel_info.vl_col; 678 } 679 } 680 else { /* true color mode */ 681 u16 col16; 682 fb16 = (ushort *)fb; 683 for (i = 0; i < BMP_LOGO_HEIGHT; ++i) { 684 for (j = 0; j < BMP_LOGO_WIDTH; j++) { 685 col16 = bmp_logo_palette[(bmap[j]-16)]; 686 fb16[j] = 687 ((col16 & 0x000F) << 1) | 688 ((col16 & 0x00F0) << 3) | 689 ((col16 & 0x0F00) << 4); 690 } 691 bmap += BMP_LOGO_WIDTH; 692 fb16 += panel_info.vl_col; 693 } 694 } 695 696 WATCHDOG_RESET(); 697 lcd_sync(); 698 } 699 #else 700 static inline void bitmap_plot(int x, int y) {} 701 #endif /* CONFIG_LCD_LOGO */ 702 703 /*----------------------------------------------------------------------*/ 704 #if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) 705 /* 706 * Display the BMP file located at address bmp_image. 707 * Only uncompressed. 708 */ 709 710 #ifdef CONFIG_SPLASH_SCREEN_ALIGN 711 #define BMP_ALIGN_CENTER 0x7FFF 712 713 static void splash_align_axis(int *axis, unsigned long panel_size, 714 unsigned long picture_size) 715 { 716 unsigned long panel_picture_delta = panel_size - picture_size; 717 unsigned long axis_alignment; 718 719 if (*axis == BMP_ALIGN_CENTER) 720 axis_alignment = panel_picture_delta / 2; 721 else if (*axis < 0) 722 axis_alignment = panel_picture_delta + *axis + 1; 723 else 724 return; 725 726 *axis = max(0, (int)axis_alignment); 727 } 728 #endif 729 730 731 #ifdef CONFIG_LCD_BMP_RLE8 732 733 #define BMP_RLE8_ESCAPE 0 734 #define BMP_RLE8_EOL 0 735 #define BMP_RLE8_EOBMP 1 736 #define BMP_RLE8_DELTA 2 737 738 static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, 739 int cnt) 740 { 741 while (cnt > 0) { 742 *(*fbp)++ = cmap[*bmap++]; 743 cnt--; 744 } 745 } 746 747 static void draw_encoded_bitmap(ushort **fbp, ushort c, int cnt) 748 { 749 ushort *fb = *fbp; 750 int cnt_8copy = cnt >> 3; 751 752 cnt -= cnt_8copy << 3; 753 while (cnt_8copy > 0) { 754 *fb++ = c; 755 *fb++ = c; 756 *fb++ = c; 757 *fb++ = c; 758 *fb++ = c; 759 *fb++ = c; 760 *fb++ = c; 761 *fb++ = c; 762 cnt_8copy--; 763 } 764 while (cnt > 0) { 765 *fb++ = c; 766 cnt--; 767 } 768 *fbp = fb; 769 } 770 771 /* 772 * Do not call this function directly, must be called from lcd_display_bitmap. 773 */ 774 static void lcd_display_rle8_bitmap(bmp_image_t *bmp, ushort *cmap, uchar *fb, 775 int x_off, int y_off) 776 { 777 uchar *bmap; 778 ulong width, height; 779 ulong cnt, runlen; 780 int x, y; 781 int decode = 1; 782 783 width = get_unaligned_le32(&bmp->header.width); 784 height = get_unaligned_le32(&bmp->header.height); 785 bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); 786 787 x = 0; 788 y = height - 1; 789 790 while (decode) { 791 if (bmap[0] == BMP_RLE8_ESCAPE) { 792 switch (bmap[1]) { 793 case BMP_RLE8_EOL: 794 /* end of line */ 795 bmap += 2; 796 x = 0; 797 y--; 798 /* 16bpix, 2-byte per pixel, width should *2 */ 799 fb -= (width * 2 + lcd_line_length); 800 break; 801 case BMP_RLE8_EOBMP: 802 /* end of bitmap */ 803 decode = 0; 804 break; 805 case BMP_RLE8_DELTA: 806 /* delta run */ 807 x += bmap[2]; 808 y -= bmap[3]; 809 /* 16bpix, 2-byte per pixel, x should *2 */ 810 fb = (uchar *) (lcd_base + (y + y_off - 1) 811 * lcd_line_length + (x + x_off) * 2); 812 bmap += 4; 813 break; 814 default: 815 /* unencoded run */ 816 runlen = bmap[1]; 817 bmap += 2; 818 if (y < height) { 819 if (x < width) { 820 if (x + runlen > width) 821 cnt = width - x; 822 else 823 cnt = runlen; 824 draw_unencoded_bitmap( 825 (ushort **)&fb, 826 bmap, cmap, cnt); 827 } 828 x += runlen; 829 } 830 bmap += runlen; 831 if (runlen & 1) 832 bmap++; 833 } 834 } else { 835 /* encoded run */ 836 if (y < height) { 837 runlen = bmap[0]; 838 if (x < width) { 839 /* aggregate the same code */ 840 while (bmap[0] == 0xff && 841 bmap[2] != BMP_RLE8_ESCAPE && 842 bmap[1] == bmap[3]) { 843 runlen += bmap[2]; 844 bmap += 2; 845 } 846 if (x + runlen > width) 847 cnt = width - x; 848 else 849 cnt = runlen; 850 draw_encoded_bitmap((ushort **)&fb, 851 cmap[bmap[1]], cnt); 852 } 853 x += runlen; 854 } 855 bmap += 2; 856 } 857 } 858 } 859 #endif 860 861 #if defined(CONFIG_MPC823) 862 #define FB_PUT_BYTE(fb, from) *(fb)++ = (255 - *(from)++) 863 #else 864 #define FB_PUT_BYTE(fb, from) *(fb)++ = *(from)++ 865 #endif 866 867 #if defined(CONFIG_BMP_16BPP) 868 #if defined(CONFIG_ATMEL_LCD_BGR555) 869 static inline void fb_put_word(uchar **fb, uchar **from) 870 { 871 *(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03); 872 *(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2); 873 *from += 2; 874 } 875 #else 876 static inline void fb_put_word(uchar **fb, uchar **from) 877 { 878 *(*fb)++ = *(*from)++; 879 *(*fb)++ = *(*from)++; 880 } 881 #endif 882 #endif /* CONFIG_BMP_16BPP */ 883 884 int lcd_display_bitmap(ulong bmp_image, int x, int y) 885 { 886 ushort *cmap = NULL; 887 ushort *cmap_base = NULL; 888 ushort i, j; 889 uchar *fb; 890 bmp_image_t *bmp = (bmp_image_t *)map_sysmem(bmp_image, 0); 891 uchar *bmap; 892 ushort padded_width; 893 unsigned long width, height, byte_width; 894 unsigned long pwidth = panel_info.vl_col; 895 unsigned colors, bpix, bmp_bpix; 896 897 if (!bmp || !(bmp->header.signature[0] == 'B' && 898 bmp->header.signature[1] == 'M')) { 899 printf("Error: no valid bmp image at %lx\n", bmp_image); 900 901 return 1; 902 } 903 904 width = get_unaligned_le32(&bmp->header.width); 905 height = get_unaligned_le32(&bmp->header.height); 906 bmp_bpix = get_unaligned_le16(&bmp->header.bit_count); 907 908 colors = 1 << bmp_bpix; 909 910 bpix = NBITS(panel_info.vl_bpix); 911 912 if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { 913 printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", 914 bpix, bmp_bpix); 915 916 return 1; 917 } 918 919 /* 920 * We support displaying 8bpp BMPs on 16bpp LCDs 921 * and displaying 24bpp BMPs on 32bpp LCDs 922 * */ 923 if (bpix != bmp_bpix && 924 !(bmp_bpix == 8 && bpix == 16) && 925 !(bmp_bpix == 24 && bpix == 32)) { 926 printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", 927 bpix, get_unaligned_le16(&bmp->header.bit_count)); 928 return 1; 929 } 930 931 debug("Display-bmp: %d x %d with %d colors\n", 932 (int)width, (int)height, (int)colors); 933 934 if (bmp_bpix == 8) { 935 cmap = configuration_get_cmap(); 936 cmap_base = cmap; 937 938 /* Set color map */ 939 for (i = 0; i < colors; ++i) { 940 bmp_color_table_entry_t cte = bmp->color_table[i]; 941 #if !defined(CONFIG_ATMEL_LCD) 942 ushort colreg = 943 ( ((cte.red) << 8) & 0xf800) | 944 ( ((cte.green) << 3) & 0x07e0) | 945 ( ((cte.blue) >> 3) & 0x001f) ; 946 *cmap = colreg; 947 #if defined(CONFIG_MPC823) 948 cmap--; 949 #else 950 cmap++; 951 #endif 952 #else /* CONFIG_ATMEL_LCD */ 953 lcd_setcolreg(i, cte.red, cte.green, cte.blue); 954 #endif 955 } 956 } 957 958 padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width); 959 960 #ifdef CONFIG_SPLASH_SCREEN_ALIGN 961 splash_align_axis(&x, pwidth, width); 962 splash_align_axis(&y, panel_info.vl_row, height); 963 #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ 964 965 if ((x + width) > pwidth) 966 width = pwidth - x; 967 if ((y + height) > panel_info.vl_row) 968 height = panel_info.vl_row - y; 969 970 bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); 971 fb = (uchar *)(lcd_base + 972 (y + height - 1) * lcd_line_length + x * bpix / 8); 973 974 switch (bmp_bpix) { 975 case 1: /* pass through */ 976 case 8: { 977 #ifdef CONFIG_LCD_BMP_RLE8 978 u32 compression = get_unaligned_le32(&bmp->header.compression); 979 if (compression == BMP_BI_RLE8) { 980 if (bpix != 16) { 981 /* TODO implement render code for bpix != 16 */ 982 printf("Error: only support 16 bpix"); 983 return 1; 984 } 985 lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y); 986 break; 987 } 988 #endif 989 990 if (bpix != 16) 991 byte_width = width; 992 else 993 byte_width = width * 2; 994 995 for (i = 0; i < height; ++i) { 996 WATCHDOG_RESET(); 997 for (j = 0; j < width; j++) { 998 if (bpix != 16) { 999 FB_PUT_BYTE(fb, bmap); 1000 } else { 1001 *(uint16_t *)fb = cmap_base[*(bmap++)]; 1002 fb += sizeof(uint16_t) / sizeof(*fb); 1003 } 1004 } 1005 bmap += (padded_width - width); 1006 fb -= byte_width + lcd_line_length; 1007 } 1008 break; 1009 } 1010 #if defined(CONFIG_BMP_16BPP) 1011 case 16: 1012 for (i = 0; i < height; ++i) { 1013 WATCHDOG_RESET(); 1014 for (j = 0; j < width; j++) 1015 fb_put_word(&fb, &bmap); 1016 1017 bmap += (padded_width - width) * 2; 1018 fb -= width * 2 + lcd_line_length; 1019 } 1020 break; 1021 #endif /* CONFIG_BMP_16BPP */ 1022 #if defined(CONFIG_BMP_24BMP) 1023 case 24: 1024 for (i = 0; i < height; ++i) { 1025 for (j = 0; j < width; j++) { 1026 *(fb++) = *(bmap++); 1027 *(fb++) = *(bmap++); 1028 *(fb++) = *(bmap++); 1029 *(fb++) = 0; 1030 } 1031 fb -= lcd_line_length + width * (bpix / 8); 1032 } 1033 break; 1034 #endif /* CONFIG_BMP_24BMP */ 1035 #if defined(CONFIG_BMP_32BPP) 1036 case 32: 1037 for (i = 0; i < height; ++i) { 1038 for (j = 0; j < width; j++) { 1039 *(fb++) = *(bmap++); 1040 *(fb++) = *(bmap++); 1041 *(fb++) = *(bmap++); 1042 *(fb++) = *(bmap++); 1043 } 1044 fb -= lcd_line_length + width * (bpix / 8); 1045 } 1046 break; 1047 #endif /* CONFIG_BMP_32BPP */ 1048 default: 1049 break; 1050 }; 1051 1052 lcd_sync(); 1053 return 0; 1054 } 1055 #endif 1056 1057 static void *lcd_logo(void) 1058 { 1059 #ifdef CONFIG_SPLASH_SCREEN 1060 char *s; 1061 ulong addr; 1062 static int do_splash = 1; 1063 1064 if (do_splash && (s = getenv("splashimage")) != NULL) { 1065 int x = 0, y = 0; 1066 do_splash = 0; 1067 1068 if (splash_screen_prepare()) 1069 return (void *)lcd_base; 1070 1071 addr = simple_strtoul (s, NULL, 16); 1072 1073 splash_get_pos(&x, &y); 1074 1075 if (bmp_display(addr, x, y) == 0) 1076 return (void *)lcd_base; 1077 } 1078 #endif /* CONFIG_SPLASH_SCREEN */ 1079 1080 bitmap_plot(0, 0); 1081 1082 #ifdef CONFIG_LCD_INFO 1083 lcd_set_col(LCD_INFO_X / VIDEO_FONT_WIDTH); 1084 lcd_set_row(LCD_INFO_Y / VIDEO_FONT_HEIGHT); 1085 lcd_show_board_info(); 1086 #endif /* CONFIG_LCD_INFO */ 1087 1088 #if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO) 1089 return (void *)((ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length); 1090 #else 1091 return (void *)lcd_base; 1092 #endif /* CONFIG_LCD_LOGO && !defined(CONFIG_LCD_INFO_BELOW_LOGO) */ 1093 } 1094 1095 #ifdef CONFIG_SPLASHIMAGE_GUARD 1096 static int on_splashimage(const char *name, const char *value, enum env_op op, 1097 int flags) 1098 { 1099 ulong addr; 1100 int aligned; 1101 1102 if (op == env_op_delete) 1103 return 0; 1104 1105 addr = simple_strtoul(value, NULL, 16); 1106 /* See README.displaying-bmps */ 1107 aligned = (addr % 4 == 2); 1108 if (!aligned) { 1109 printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n"); 1110 return -1; 1111 } 1112 1113 return 0; 1114 } 1115 1116 U_BOOT_ENV_CALLBACK(splashimage, on_splashimage); 1117 #endif 1118 1119 void lcd_position_cursor(unsigned col, unsigned row) 1120 { 1121 console_curr_col = min_t(short, col, console_cols - 1); 1122 console_curr_row = min_t(short, row, console_rows - 1); 1123 } 1124 1125 int lcd_get_pixel_width(void) 1126 { 1127 return panel_info.vl_col; 1128 } 1129 1130 int lcd_get_pixel_height(void) 1131 { 1132 return panel_info.vl_row; 1133 } 1134 1135 int lcd_get_screen_rows(void) 1136 { 1137 return console_rows; 1138 } 1139 1140 int lcd_get_screen_columns(void) 1141 { 1142 return console_cols; 1143 } 1144 1145 #if defined(CONFIG_LCD_DT_SIMPLEFB) 1146 static int lcd_dt_simplefb_configure_node(void *blob, int off) 1147 { 1148 #if LCD_BPP == LCD_COLOR16 1149 return fdt_setup_simplefb_node(blob, off, gd->fb_base, 1150 panel_info.vl_col, panel_info.vl_row, 1151 panel_info.vl_col * 2, "r5g6b5"); 1152 #else 1153 return -1; 1154 #endif 1155 } 1156 1157 int lcd_dt_simplefb_add_node(void *blob) 1158 { 1159 static const char compat[] = "simple-framebuffer"; 1160 static const char disabled[] = "disabled"; 1161 int off, ret; 1162 1163 off = fdt_add_subnode(blob, 0, "framebuffer"); 1164 if (off < 0) 1165 return -1; 1166 1167 ret = fdt_setprop(blob, off, "status", disabled, sizeof(disabled)); 1168 if (ret < 0) 1169 return -1; 1170 1171 ret = fdt_setprop(blob, off, "compatible", compat, sizeof(compat)); 1172 if (ret < 0) 1173 return -1; 1174 1175 return lcd_dt_simplefb_configure_node(blob, off); 1176 } 1177 1178 int lcd_dt_simplefb_enable_existing_node(void *blob) 1179 { 1180 int off; 1181 1182 off = fdt_node_offset_by_compatible(blob, -1, "simple-framebuffer"); 1183 if (off < 0) 1184 return -1; 1185 1186 return lcd_dt_simplefb_configure_node(blob, off); 1187 } 1188 #endif 1189