uboot/common/lcd.c
<<
>>
Prefs
   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
  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
 103DECLARE_GLOBAL_DATA_PTR;
 104
 105static void lcd_drawchars(ushort x, ushort y, uchar *str, int count);
 106static inline void lcd_puts_xy(ushort x, ushort y, uchar *s);
 107static inline void lcd_putc_xy(ushort x, ushort y, uchar  c);
 108
 109static int lcd_init(void *lcdbase);
 110
 111static void *lcd_logo(void);
 112
 113static int lcd_getbgcolor(void);
 114static void lcd_setfgcolor(int color);
 115static void lcd_setbgcolor(int color);
 116
 117static int lcd_color_fg;
 118static int lcd_color_bg;
 119int lcd_line_length;
 120
 121char lcd_is_enabled = 0;
 122
 123static short console_col;
 124static short console_row;
 125
 126static void *lcd_console_address;
 127static void *lcd_base;                  /* Start of framebuffer memory  */
 128
 129static char lcd_flush_dcache;   /* 1 to flush dcache after each lcd update */
 130
 131/************************************************************************/
 132
 133/* Flush LCD activity to the caches */
 134void 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
 150void lcd_set_flush_dcache(int flush)
 151{
 152        lcd_flush_dcache = (flush != 0);
 153}
 154
 155/*----------------------------------------------------------------------*/
 156
 157static 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
 177static 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
 191static 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
 204void 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
 243void 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
 259void 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
 275static 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
 337static 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
 344static 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
 357static 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
 362static 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
 401int 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/*----------------------------------------------------------------------*/
 425void 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
 469static 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
 476U_BOOT_CMD(
 477        cls,    1,      1,      do_lcd_clear,
 478        "clear screen",
 479        ""
 480);
 481
 482/*----------------------------------------------------------------------*/
 483
 484static 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 */
 529ulong 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
 554static void lcd_setfgcolor(int color)
 555{
 556        lcd_color_fg = color;
 557}
 558
 559/*----------------------------------------------------------------------*/
 560
 561static void lcd_setbgcolor(int color)
 562{
 563        lcd_color_bg = color;
 564}
 565
 566/*----------------------------------------------------------------------*/
 567
 568int lcd_getfgcolor(void)
 569{
 570        return lcd_color_fg;
 571}
 572
 573/*----------------------------------------------------------------------*/
 574
 575static int lcd_getbgcolor(void)
 576{
 577        return lcd_color_bg;
 578}
 579
 580/************************************************************************/
 581/* ** Chipset depending Bitmap / Logo stuff...                          */
 582/************************************************************************/
 583static 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
 604void 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
 697static 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
 710static 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
 735static 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
 744static 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 */
 771static 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 = le32_to_cpu(bmp->header.width);
 781        height = le32_to_cpu(bmp->header.height);
 782        bmap = (uchar *)bmp + le32_to_cpu(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)
 866static 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
 873static 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
 881int 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 = le32_to_cpu(bmp->header.width);
 904        height = le32_to_cpu(bmp->header.height);
 905        bmp_bpix = le16_to_cpu(bmp->header.bit_count);
 906        colors = 1 << bmp_bpix;
 907
 908        bpix = NBITS(panel_info.vl_bpix);
 909
 910        if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
 911                printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 912                        bpix, bmp_bpix);
 913
 914                return 1;
 915        }
 916
 917        /* We support displaying 8bpp BMPs on 16bpp LCDs */
 918        if (bpix != bmp_bpix && !(bmp_bpix == 8 && bpix == 16)) {
 919                printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 920                        bpix,
 921                        le16_to_cpu(bmp->header.bit_count));
 922
 923                return 1;
 924        }
 925
 926        debug("Display-bmp: %d x %d  with %d colors\n",
 927                (int)width, (int)height, (int)colors);
 928
 929#if !defined(CONFIG_MCC200)
 930        /* MCC200 LCD doesn't need CMAP, supports 1bpp b&w only */
 931        if (bmp_bpix == 8) {
 932                cmap = configuration_get_cmap();
 933                cmap_base = cmap;
 934
 935                /* Set color map */
 936                for (i = 0; i < colors; ++i) {
 937                        bmp_color_table_entry_t cte = bmp->color_table[i];
 938#if !defined(CONFIG_ATMEL_LCD)
 939                        ushort colreg =
 940                                ( ((cte.red)   << 8) & 0xf800) |
 941                                ( ((cte.green) << 3) & 0x07e0) |
 942                                ( ((cte.blue)  >> 3) & 0x001f) ;
 943#ifdef CONFIG_SYS_INVERT_COLORS
 944                        *cmap = 0xffff - colreg;
 945#else
 946                        *cmap = colreg;
 947#endif
 948#if defined(CONFIG_MPC823)
 949                        cmap--;
 950#else
 951                        cmap++;
 952#endif
 953#else /* CONFIG_ATMEL_LCD */
 954                        lcd_setcolreg(i, cte.red, cte.green, cte.blue);
 955#endif
 956                }
 957        }
 958#endif
 959
 960        /*
 961         *  BMP format for Monochrome assumes that the state of a
 962         * pixel is described on a per Bit basis, not per Byte.
 963         *  So, in case of Monochrome BMP we should align widths
 964         * on a byte boundary and convert them from Bit to Byte
 965         * units.
 966         *  Probably, PXA250 and MPC823 process 1bpp BMP images in
 967         * their own ways, so make the converting to be MCC200
 968         * specific.
 969         */
 970#if defined(CONFIG_MCC200)
 971        if (bpix == 1) {
 972                width = ((width + 7) & ~7) >> 3;
 973                x     = ((x + 7) & ~7) >> 3;
 974                pwidth= ((pwidth + 7) & ~7) >> 3;
 975        }
 976#endif
 977
 978        padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
 979
 980#ifdef CONFIG_SPLASH_SCREEN_ALIGN
 981        splash_align_axis(&x, pwidth, width);
 982        splash_align_axis(&y, panel_info.vl_row, height);
 983#endif /* CONFIG_SPLASH_SCREEN_ALIGN */
 984
 985        if ((x + width) > pwidth)
 986                width = pwidth - x;
 987        if ((y + height) > panel_info.vl_row)
 988                height = panel_info.vl_row - y;
 989
 990        bmap = (uchar *) bmp + le32_to_cpu(bmp->header.data_offset);
 991        fb   = (uchar *) (lcd_base +
 992                (y + height - 1) * lcd_line_length + x * bpix / 8);
 993
 994        switch (bmp_bpix) {
 995        case 1: /* pass through */
 996        case 8:
 997#ifdef CONFIG_LCD_BMP_RLE8
 998                if (le32_to_cpu(bmp->header.compression) == BMP_BI_RLE8) {
 999                        if (bpix != 16) {
1000                                /* TODO implement render code for bpix != 16 */
1001                                printf("Error: only support 16 bpix");
1002                                return 1;
1003                        }
1004                        lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y);
1005                        break;
1006                }
1007#endif
1008
1009                if (bpix != 16)
1010                        byte_width = width;
1011                else
1012                        byte_width = width * 2;
1013
1014                for (i = 0; i < height; ++i) {
1015                        WATCHDOG_RESET();
1016                        for (j = 0; j < width; j++) {
1017                                if (bpix != 16) {
1018                                        FB_PUT_BYTE(fb, bmap);
1019                                } else {
1020                                        *(uint16_t *)fb = cmap_base[*(bmap++)];
1021                                        fb += sizeof(uint16_t) / sizeof(*fb);
1022                                }
1023                        }
1024                        bmap += (padded_width - width);
1025                        fb -= byte_width + lcd_line_length;
1026                }
1027                break;
1028
1029#if defined(CONFIG_BMP_16BPP)
1030        case 16:
1031                for (i = 0; i < height; ++i) {
1032                        WATCHDOG_RESET();
1033                        for (j = 0; j < width; j++)
1034                                fb_put_word(&fb, &bmap);
1035
1036                        bmap += (padded_width - width) * 2;
1037                        fb -= width * 2 + lcd_line_length;
1038                }
1039                break;
1040#endif /* CONFIG_BMP_16BPP */
1041
1042#if defined(CONFIG_BMP_32BPP)
1043        case 32:
1044                for (i = 0; i < height; ++i) {
1045                        for (j = 0; j < width; j++) {
1046                                *(fb++) = *(bmap++);
1047                                *(fb++) = *(bmap++);
1048                                *(fb++) = *(bmap++);
1049                                *(fb++) = *(bmap++);
1050                        }
1051                        fb -= lcd_line_length + width * (bpix / 8);
1052                }
1053                break;
1054#endif /* CONFIG_BMP_32BPP */
1055        default:
1056                break;
1057        };
1058
1059        lcd_sync();
1060        return 0;
1061}
1062#endif
1063
1064static void *lcd_logo(void)
1065{
1066#ifdef CONFIG_SPLASH_SCREEN
1067        char *s;
1068        ulong addr;
1069        static int do_splash = 1;
1070
1071        if (do_splash && (s = getenv("splashimage")) != NULL) {
1072                int x = 0, y = 0;
1073                do_splash = 0;
1074
1075                if (splash_screen_prepare())
1076                        return (void *)lcd_base;
1077
1078                addr = simple_strtoul (s, NULL, 16);
1079
1080                splash_get_pos(&x, &y);
1081
1082                if (bmp_display(addr, x, y) == 0)
1083                        return (void *)lcd_base;
1084        }
1085#endif /* CONFIG_SPLASH_SCREEN */
1086
1087        bitmap_plot(0, 0);
1088
1089#ifdef CONFIG_LCD_INFO
1090        console_col = LCD_INFO_X / VIDEO_FONT_WIDTH;
1091        console_row = LCD_INFO_Y / VIDEO_FONT_HEIGHT;
1092        lcd_show_board_info();
1093#endif /* CONFIG_LCD_INFO */
1094
1095#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
1096        return (void *)((ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length);
1097#else
1098        return (void *)lcd_base;
1099#endif /* CONFIG_LCD_LOGO && !defined(CONFIG_LCD_INFO_BELOW_LOGO) */
1100}
1101
1102#ifdef CONFIG_SPLASHIMAGE_GUARD
1103static int on_splashimage(const char *name, const char *value, enum env_op op,
1104        int flags)
1105{
1106        ulong addr;
1107        int aligned;
1108
1109        if (op == env_op_delete)
1110                return 0;
1111
1112        addr = simple_strtoul(value, NULL, 16);
1113        /* See README.displaying-bmps */
1114        aligned = (addr % 4 == 2);
1115        if (!aligned) {
1116                printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n");
1117                return -1;
1118        }
1119
1120        return 0;
1121}
1122
1123U_BOOT_ENV_CALLBACK(splashimage, on_splashimage);
1124#endif
1125
1126void lcd_position_cursor(unsigned col, unsigned row)
1127{
1128        console_col = min(col, CONSOLE_COLS - 1);
1129        console_row = min(row, CONSOLE_ROWS - 1);
1130}
1131
1132int lcd_get_pixel_width(void)
1133{
1134        return panel_info.vl_col;
1135}
1136
1137int lcd_get_pixel_height(void)
1138{
1139        return panel_info.vl_row;
1140}
1141
1142int lcd_get_screen_rows(void)
1143{
1144        return CONSOLE_ROWS;
1145}
1146
1147int lcd_get_screen_columns(void)
1148{
1149        return CONSOLE_COLS;
1150}
1151
1152#if defined(CONFIG_LCD_DT_SIMPLEFB)
1153static int lcd_dt_simplefb_configure_node(void *blob, int off)
1154{
1155        u32 stride;
1156        fdt32_t cells[2];
1157        int ret;
1158        static const char format[] =
1159#if LCD_BPP == LCD_COLOR16
1160                "r5g6b5";
1161#else
1162                "";
1163#endif
1164
1165        if (!format[0])
1166                return -1;
1167
1168        stride = panel_info.vl_col * 2;
1169
1170        cells[0] = cpu_to_fdt32(gd->fb_base);
1171        cells[1] = cpu_to_fdt32(stride * panel_info.vl_row);
1172        ret = fdt_setprop(blob, off, "reg", cells, sizeof(cells[0]) * 2);
1173        if (ret < 0)
1174                return -1;
1175
1176        cells[0] = cpu_to_fdt32(panel_info.vl_col);
1177        ret = fdt_setprop(blob, off, "width", cells, sizeof(cells[0]));
1178        if (ret < 0)
1179                return -1;
1180
1181        cells[0] = cpu_to_fdt32(panel_info.vl_row);
1182        ret = fdt_setprop(blob, off, "height", cells, sizeof(cells[0]));
1183        if (ret < 0)
1184                return -1;
1185
1186        cells[0] = cpu_to_fdt32(stride);
1187        ret = fdt_setprop(blob, off, "stride", cells, sizeof(cells[0]));
1188        if (ret < 0)
1189                return -1;
1190
1191        ret = fdt_setprop(blob, off, "format", format, strlen(format) + 1);
1192        if (ret < 0)
1193                return -1;
1194
1195        ret = fdt_delprop(blob, off, "status");
1196        if (ret < 0)
1197                return -1;
1198
1199        return 0;
1200}
1201
1202int lcd_dt_simplefb_add_node(void *blob)
1203{
1204        static const char compat[] = "simple-framebuffer";
1205        static const char disabled[] = "disabled";
1206        int off, ret;
1207
1208        off = fdt_add_subnode(blob, 0, "framebuffer");
1209        if (off < 0)
1210                return -1;
1211
1212        ret = fdt_setprop(blob, off, "status", disabled, sizeof(disabled));
1213        if (ret < 0)
1214                return -1;
1215
1216        ret = fdt_setprop(blob, off, "compatible", compat, sizeof(compat));
1217        if (ret < 0)
1218                return -1;
1219
1220        return lcd_dt_simplefb_configure_node(blob, off);
1221}
1222
1223int lcd_dt_simplefb_enable_existing_node(void *blob)
1224{
1225        int off;
1226
1227        off = fdt_node_offset_by_compatible(blob, -1, "simple-framebuffer");
1228        if (off < 0)
1229                return -1;
1230
1231        return lcd_dt_simplefb_configure_node(blob, off);
1232}
1233#endif
1234