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