uboot/common/lcd.c
<<
>>
Prefs
   1/*
   2 * Common LCD routines
   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/* #define DEBUG */
  11#include <config.h>
  12#include <common.h>
  13#include <command.h>
  14#include <env_callback.h>
  15#include <linux/types.h>
  16#include <stdio_dev.h>
  17#include <lcd.h>
  18#include <mapmem.h>
  19#include <watchdog.h>
  20#include <asm/unaligned.h>
  21#include <splash.h>
  22#include <asm/io.h>
  23#include <asm/unaligned.h>
  24#include <video_font.h>
  25
  26#ifdef CONFIG_LCD_LOGO
  27#include <bmp_logo.h>
  28#include <bmp_logo_data.h>
  29#if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16)
  30#error Default Color Map overlaps with Logo Color Map
  31#endif
  32#endif
  33
  34#ifndef CONFIG_LCD_ALIGNMENT
  35#define CONFIG_LCD_ALIGNMENT PAGE_SIZE
  36#endif
  37
  38#if (LCD_BPP != LCD_COLOR8) && (LCD_BPP != LCD_COLOR16) && \
  39        (LCD_BPP != LCD_COLOR32)
  40#error Unsupported LCD BPP.
  41#endif
  42
  43DECLARE_GLOBAL_DATA_PTR;
  44
  45static int lcd_init(void *lcdbase);
  46static void lcd_logo(void);
  47static void lcd_setfgcolor(int color);
  48static void lcd_setbgcolor(int color);
  49
  50static int lcd_color_fg;
  51static int lcd_color_bg;
  52int lcd_line_length;
  53char lcd_is_enabled = 0;
  54static void *lcd_base;                  /* Start of framebuffer memory  */
  55static char lcd_flush_dcache;   /* 1 to flush dcache after each lcd update */
  56
  57/* Flush LCD activity to the caches */
  58void lcd_sync(void)
  59{
  60        /*
  61         * flush_dcache_range() is declared in common.h but it seems that some
  62         * architectures do not actually implement it. Is there a way to find
  63         * out whether it exists? For now, ARM is safe.
  64         */
  65#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF)
  66        int line_length;
  67
  68        if (lcd_flush_dcache)
  69                flush_dcache_range((ulong)lcd_base,
  70                        (ulong)(lcd_base + lcd_get_size(&line_length)));
  71#endif
  72}
  73
  74void lcd_set_flush_dcache(int flush)
  75{
  76        lcd_flush_dcache = (flush != 0);
  77}
  78
  79static void lcd_stub_putc(struct stdio_dev *dev, const char c)
  80{
  81        lcd_putc(c);
  82}
  83
  84static void lcd_stub_puts(struct stdio_dev *dev, const char *s)
  85{
  86        lcd_puts(s);
  87}
  88
  89/* Small utility to check that you got the colours right */
  90#ifdef LCD_TEST_PATTERN
  91
  92#if LCD_BPP == LCD_COLOR8
  93#define N_BLK_VERT      2
  94#define N_BLK_HOR       3
  95
  96static int test_colors[N_BLK_HOR * N_BLK_VERT] = {
  97        CONSOLE_COLOR_RED,      CONSOLE_COLOR_GREEN,    CONSOLE_COLOR_YELLOW,
  98        CONSOLE_COLOR_BLUE,     CONSOLE_COLOR_MAGENTA,  CONSOLE_COLOR_CYAN,
  99}; /*LCD_BPP == LCD_COLOR8 */
 100
 101#elif LCD_BPP == LCD_COLOR16
 102#define N_BLK_VERT      2
 103#define N_BLK_HOR       4
 104
 105static int test_colors[N_BLK_HOR * N_BLK_VERT] = {
 106        CONSOLE_COLOR_RED,      CONSOLE_COLOR_GREEN,    CONSOLE_COLOR_YELLOW,   CONSOLE_COLOR_BLUE,
 107        CONSOLE_COLOR_MAGENTA,  CONSOLE_COLOR_CYAN,     CONSOLE_COLOR_GREY,     CONSOLE_COLOR_WHITE,
 108};
 109#endif /*LCD_BPP == LCD_COLOR16 */
 110
 111static void test_pattern(void)
 112{
 113        ushort v_max  = panel_info.vl_row;
 114        ushort h_max  = panel_info.vl_col;
 115        ushort v_step = (v_max + N_BLK_VERT - 1) / N_BLK_VERT;
 116        ushort h_step = (h_max + N_BLK_HOR  - 1) / N_BLK_HOR;
 117        ushort v, h;
 118#if LCD_BPP == LCD_COLOR8
 119        uchar *pix = (uchar *)lcd_base;
 120#elif LCD_BPP == LCD_COLOR16
 121        ushort *pix = (ushort *)lcd_base;
 122#endif
 123
 124        printf("[LCD] Test Pattern: %d x %d [%d x %d]\n",
 125                h_max, v_max, h_step, v_step);
 126
 127        for (v = 0; v < v_max; ++v) {
 128                uchar iy = v / v_step;
 129                for (h = 0; h < h_max; ++h) {
 130                        uchar ix = N_BLK_HOR * iy + h / h_step;
 131                        *pix++ = test_colors[ix];
 132                }
 133        }
 134}
 135#endif /* LCD_TEST_PATTERN */
 136
 137/*
 138 * With most lcd drivers the line length is set up
 139 * by calculating it from panel_info parameters. Some
 140 * drivers need to calculate the line length differently,
 141 * so make the function weak to allow overriding it.
 142 */
 143__weak int lcd_get_size(int *line_length)
 144{
 145        *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
 146        return *line_length * panel_info.vl_row;
 147}
 148
 149int drv_lcd_init(void)
 150{
 151        struct stdio_dev lcddev;
 152        int rc;
 153
 154        lcd_base = map_sysmem(gd->fb_base, 0);
 155
 156        lcd_init(lcd_base);
 157
 158        /* Device initialization */
 159        memset(&lcddev, 0, sizeof(lcddev));
 160
 161        strcpy(lcddev.name, "lcd");
 162        lcddev.ext   = 0;                       /* No extensions */
 163        lcddev.flags = DEV_FLAGS_OUTPUT;        /* Output only */
 164        lcddev.putc  = lcd_stub_putc;           /* 'putc' function */
 165        lcddev.puts  = lcd_stub_puts;           /* 'puts' function */
 166
 167        rc = stdio_register(&lcddev);
 168
 169        return (rc == 0) ? 1 : rc;
 170}
 171
 172void lcd_clear(void)
 173{
 174        int bg_color;
 175        char *s;
 176        ulong addr;
 177        static int do_splash = 1;
 178#if LCD_BPP == LCD_COLOR8
 179        /* Setting the palette */
 180        lcd_setcolreg(CONSOLE_COLOR_BLACK, 0, 0, 0);
 181        lcd_setcolreg(CONSOLE_COLOR_RED, 0xFF, 0, 0);
 182        lcd_setcolreg(CONSOLE_COLOR_GREEN, 0, 0xFF, 0);
 183        lcd_setcolreg(CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0);
 184        lcd_setcolreg(CONSOLE_COLOR_BLUE, 0, 0, 0xFF);
 185        lcd_setcolreg(CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF);
 186        lcd_setcolreg(CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF);
 187        lcd_setcolreg(CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA);
 188        lcd_setcolreg(CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF);
 189#endif
 190
 191#ifndef CONFIG_SYS_WHITE_ON_BLACK
 192        lcd_setfgcolor(CONSOLE_COLOR_BLACK);
 193        lcd_setbgcolor(CONSOLE_COLOR_WHITE);
 194        bg_color = CONSOLE_COLOR_WHITE;
 195#else
 196        lcd_setfgcolor(CONSOLE_COLOR_WHITE);
 197        lcd_setbgcolor(CONSOLE_COLOR_BLACK);
 198        bg_color = CONSOLE_COLOR_BLACK;
 199#endif  /* CONFIG_SYS_WHITE_ON_BLACK */
 200
 201#ifdef  LCD_TEST_PATTERN
 202        test_pattern();
 203#else
 204        /* set framebuffer to background color */
 205#if (LCD_BPP != LCD_COLOR32)
 206        memset((char *)lcd_base, bg_color, lcd_line_length * panel_info.vl_row);
 207#else
 208        u32 *ppix = lcd_base;
 209        u32 i;
 210        for (i = 0;
 211           i < (lcd_line_length * panel_info.vl_row)/NBYTES(panel_info.vl_bpix);
 212           i++) {
 213                *ppix++ = bg_color;
 214        }
 215#endif
 216#endif
 217        /* setup text-console */
 218        debug("[LCD] setting up console...\n");
 219        lcd_init_console(lcd_base,
 220                         panel_info.vl_col,
 221                         panel_info.vl_row,
 222                         panel_info.vl_rot);
 223        /* Paint the logo and retrieve LCD base address */
 224        debug("[LCD] Drawing the logo...\n");
 225        if (do_splash) {
 226                s = getenv("splashimage");
 227                if (s) {
 228                        do_splash = 0;
 229                        addr = simple_strtoul(s, NULL, 16);
 230                        if (lcd_splash(addr) == 0) {
 231                                lcd_sync();
 232                                return;
 233                        }
 234                }
 235        }
 236
 237        lcd_logo();
 238#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
 239        addr = (ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length;
 240        lcd_init_console((void *)addr, panel_info.vl_col,
 241                         panel_info.vl_row, panel_info.vl_rot);
 242#endif
 243        lcd_sync();
 244}
 245
 246static int do_lcd_clear(cmd_tbl_t *cmdtp, int flag, int argc,
 247                        char *const argv[])
 248{
 249        lcd_clear();
 250        return 0;
 251}
 252U_BOOT_CMD(cls, 1, 1, do_lcd_clear, "clear screen", "");
 253
 254static int lcd_init(void *lcdbase)
 255{
 256        debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);
 257        lcd_ctrl_init(lcdbase);
 258
 259        /*
 260         * lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi) ignores
 261         * the 'lcdbase' argument and uses custom lcd base address
 262         * by setting up gd->fb_base. Check for this condition and fixup
 263         * 'lcd_base' address.
 264         */
 265        if (map_to_sysmem(lcdbase) != gd->fb_base)
 266                lcd_base = map_sysmem(gd->fb_base, 0);
 267
 268        debug("[LCD] Using LCD frambuffer at %p\n", lcd_base);
 269
 270        lcd_get_size(&lcd_line_length);
 271        lcd_is_enabled = 1;
 272        lcd_clear();
 273        lcd_enable();
 274
 275        /* Initialize the console */
 276        lcd_set_col(0);
 277#ifdef CONFIG_LCD_INFO_BELOW_LOGO
 278        lcd_set_row(7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT);
 279#else
 280        lcd_set_row(1); /* leave 1 blank line below logo */
 281#endif
 282
 283        return 0;
 284}
 285
 286/*
 287 * This is called early in the system initialization to grab memory
 288 * for the LCD controller.
 289 * Returns new address for monitor, after reserving LCD buffer memory
 290 *
 291 * Note that this is running from ROM, so no write access to global data.
 292 */
 293ulong lcd_setmem(ulong addr)
 294{
 295        ulong size;
 296        int line_length;
 297
 298        debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col,
 299                panel_info.vl_row, NBITS(panel_info.vl_bpix));
 300
 301        size = lcd_get_size(&line_length);
 302
 303        /* Round up to nearest full page, or MMU section if defined */
 304        size = ALIGN(size, CONFIG_LCD_ALIGNMENT);
 305        addr = ALIGN(addr - CONFIG_LCD_ALIGNMENT + 1, CONFIG_LCD_ALIGNMENT);
 306
 307        /* Allocate pages for the frame buffer. */
 308        addr -= size;
 309
 310        debug("Reserving %ldk for LCD Framebuffer at: %08lx\n",
 311              size >> 10, addr);
 312
 313        return addr;
 314}
 315
 316static void lcd_setfgcolor(int color)
 317{
 318        lcd_color_fg = color;
 319}
 320
 321int lcd_getfgcolor(void)
 322{
 323        return lcd_color_fg;
 324}
 325
 326static void lcd_setbgcolor(int color)
 327{
 328        lcd_color_bg = color;
 329}
 330
 331int lcd_getbgcolor(void)
 332{
 333        return lcd_color_bg;
 334}
 335
 336#ifdef CONFIG_LCD_LOGO
 337__weak void lcd_logo_set_cmap(void)
 338{
 339        int i;
 340        ushort *cmap = configuration_get_cmap();
 341
 342        for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i)
 343                *cmap++ = bmp_logo_palette[i];
 344}
 345
 346void lcd_logo_plot(int x, int y)
 347{
 348        ushort i, j;
 349        uchar *bmap = &bmp_logo_bitmap[0];
 350        unsigned bpix = NBITS(panel_info.vl_bpix);
 351        uchar *fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8);
 352        ushort *fb16;
 353
 354        debug("Logo: width %d  height %d  colors %d\n",
 355              BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS);
 356
 357        if (bpix < 12) {
 358                WATCHDOG_RESET();
 359                lcd_logo_set_cmap();
 360                WATCHDOG_RESET();
 361
 362                for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
 363                        memcpy(fb, bmap, BMP_LOGO_WIDTH);
 364                        bmap += BMP_LOGO_WIDTH;
 365                        fb += panel_info.vl_col;
 366                }
 367        }
 368        else { /* true color mode */
 369                u16 col16;
 370                fb16 = (ushort *)fb;
 371                for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
 372                        for (j = 0; j < BMP_LOGO_WIDTH; j++) {
 373                                col16 = bmp_logo_palette[(bmap[j]-16)];
 374                                fb16[j] =
 375                                        ((col16 & 0x000F) << 1) |
 376                                        ((col16 & 0x00F0) << 3) |
 377                                        ((col16 & 0x0F00) << 4);
 378                                }
 379                        bmap += BMP_LOGO_WIDTH;
 380                        fb16 += panel_info.vl_col;
 381                }
 382        }
 383
 384        WATCHDOG_RESET();
 385        lcd_sync();
 386}
 387#else
 388static inline void lcd_logo_plot(int x, int y) {}
 389#endif /* CONFIG_LCD_LOGO */
 390
 391#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN)
 392#ifdef CONFIG_SPLASH_SCREEN_ALIGN
 393#define BMP_ALIGN_CENTER        0x7FFF
 394
 395static void splash_align_axis(int *axis, unsigned long panel_size,
 396                                        unsigned long picture_size)
 397{
 398        unsigned long panel_picture_delta = panel_size - picture_size;
 399        unsigned long axis_alignment;
 400
 401        if (*axis == BMP_ALIGN_CENTER)
 402                axis_alignment = panel_picture_delta / 2;
 403        else if (*axis < 0)
 404                axis_alignment = panel_picture_delta + *axis + 1;
 405        else
 406                return;
 407
 408        *axis = max(0, (int)axis_alignment);
 409}
 410#endif
 411
 412#ifdef CONFIG_LCD_BMP_RLE8
 413#define BMP_RLE8_ESCAPE         0
 414#define BMP_RLE8_EOL            0
 415#define BMP_RLE8_EOBMP          1
 416#define BMP_RLE8_DELTA          2
 417
 418static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
 419                                  int cnt)
 420{
 421        while (cnt > 0) {
 422                *(*fbp)++ = cmap[*bmap++];
 423                cnt--;
 424        }
 425}
 426
 427static void draw_encoded_bitmap(ushort **fbp, ushort c, int cnt)
 428{
 429        ushort *fb = *fbp;
 430        int cnt_8copy = cnt >> 3;
 431
 432        cnt -= cnt_8copy << 3;
 433        while (cnt_8copy > 0) {
 434                *fb++ = c;
 435                *fb++ = c;
 436                *fb++ = c;
 437                *fb++ = c;
 438                *fb++ = c;
 439                *fb++ = c;
 440                *fb++ = c;
 441                *fb++ = c;
 442                cnt_8copy--;
 443        }
 444        while (cnt > 0) {
 445                *fb++ = c;
 446                cnt--;
 447        }
 448        *fbp = fb;
 449}
 450
 451/*
 452 * Do not call this function directly, must be called from lcd_display_bitmap.
 453 */
 454static void lcd_display_rle8_bitmap(struct bmp_image *bmp, ushort *cmap,
 455                                    uchar *fb, int x_off, int y_off)
 456{
 457        uchar *bmap;
 458        ulong width, height;
 459        ulong cnt, runlen;
 460        int x, y;
 461        int decode = 1;
 462
 463        width = get_unaligned_le32(&bmp->header.width);
 464        height = get_unaligned_le32(&bmp->header.height);
 465        bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
 466
 467        x = 0;
 468        y = height - 1;
 469
 470        while (decode) {
 471                if (bmap[0] == BMP_RLE8_ESCAPE) {
 472                        switch (bmap[1]) {
 473                        case BMP_RLE8_EOL:
 474                                /* end of line */
 475                                bmap += 2;
 476                                x = 0;
 477                                y--;
 478                                /* 16bpix, 2-byte per pixel, width should *2 */
 479                                fb -= (width * 2 + lcd_line_length);
 480                                break;
 481                        case BMP_RLE8_EOBMP:
 482                                /* end of bitmap */
 483                                decode = 0;
 484                                break;
 485                        case BMP_RLE8_DELTA:
 486                                /* delta run */
 487                                x += bmap[2];
 488                                y -= bmap[3];
 489                                /* 16bpix, 2-byte per pixel, x should *2 */
 490                                fb = (uchar *) (lcd_base + (y + y_off - 1)
 491                                        * lcd_line_length + (x + x_off) * 2);
 492                                bmap += 4;
 493                                break;
 494                        default:
 495                                /* unencoded run */
 496                                runlen = bmap[1];
 497                                bmap += 2;
 498                                if (y < height) {
 499                                        if (x < width) {
 500                                                if (x + runlen > width)
 501                                                        cnt = width - x;
 502                                                else
 503                                                        cnt = runlen;
 504                                                draw_unencoded_bitmap(
 505                                                        (ushort **)&fb,
 506                                                        bmap, cmap, cnt);
 507                                        }
 508                                        x += runlen;
 509                                }
 510                                bmap += runlen;
 511                                if (runlen & 1)
 512                                        bmap++;
 513                        }
 514                } else {
 515                        /* encoded run */
 516                        if (y < height) {
 517                                runlen = bmap[0];
 518                                if (x < width) {
 519                                        /* aggregate the same code */
 520                                        while (bmap[0] == 0xff &&
 521                                               bmap[2] != BMP_RLE8_ESCAPE &&
 522                                               bmap[1] == bmap[3]) {
 523                                                runlen += bmap[2];
 524                                                bmap += 2;
 525                                        }
 526                                        if (x + runlen > width)
 527                                                cnt = width - x;
 528                                        else
 529                                                cnt = runlen;
 530                                        draw_encoded_bitmap((ushort **)&fb,
 531                                                cmap[bmap[1]], cnt);
 532                                }
 533                                x += runlen;
 534                        }
 535                        bmap += 2;
 536                }
 537        }
 538}
 539#endif
 540
 541__weak void fb_put_byte(uchar **fb, uchar **from)
 542{
 543        *(*fb)++ = *(*from)++;
 544}
 545
 546#if defined(CONFIG_BMP_16BPP)
 547__weak void fb_put_word(uchar **fb, uchar **from)
 548{
 549        *(*fb)++ = *(*from)++;
 550        *(*fb)++ = *(*from)++;
 551}
 552#endif /* CONFIG_BMP_16BPP */
 553
 554__weak void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
 555{
 556        int i;
 557        struct bmp_color_table_entry cte;
 558        ushort *cmap = configuration_get_cmap();
 559
 560        for (i = 0; i < colors; ++i) {
 561                cte = bmp->color_table[i];
 562                *cmap = (((cte.red)   << 8) & 0xf800) |
 563                        (((cte.green) << 3) & 0x07e0) |
 564                        (((cte.blue)  >> 3) & 0x001f);
 565#if defined(CONFIG_MPC823)
 566                cmap--;
 567#else
 568                cmap++;
 569#endif
 570        }
 571}
 572
 573int lcd_display_bitmap(ulong bmp_image, int x, int y)
 574{
 575        ushort *cmap_base = NULL;
 576        ushort i, j;
 577        uchar *fb;
 578        struct bmp_image *bmp = (struct bmp_image *)map_sysmem(bmp_image, 0);
 579        uchar *bmap;
 580        ushort padded_width;
 581        unsigned long width, height, byte_width;
 582        unsigned long pwidth = panel_info.vl_col;
 583        unsigned colors, bpix, bmp_bpix;
 584        int hdr_size;
 585        struct bmp_color_table_entry *palette = bmp->color_table;
 586
 587        if (!bmp || !(bmp->header.signature[0] == 'B' &&
 588                bmp->header.signature[1] == 'M')) {
 589                printf("Error: no valid bmp image at %lx\n", bmp_image);
 590
 591                return 1;
 592        }
 593
 594        width = get_unaligned_le32(&bmp->header.width);
 595        height = get_unaligned_le32(&bmp->header.height);
 596        bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
 597        hdr_size = get_unaligned_le16(&bmp->header.size);
 598        debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
 599
 600        colors = 1 << bmp_bpix;
 601
 602        bpix = NBITS(panel_info.vl_bpix);
 603
 604        if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
 605                printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 606                        bpix, bmp_bpix);
 607
 608                return 1;
 609        }
 610
 611        /*
 612         * We support displaying 8bpp BMPs on 16bpp LCDs
 613         * and displaying 24bpp BMPs on 32bpp LCDs
 614         * */
 615        if (bpix != bmp_bpix &&
 616            !(bmp_bpix == 8 && bpix == 16) &&
 617            !(bmp_bpix == 24 && bpix == 32)) {
 618                printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 619                        bpix, get_unaligned_le16(&bmp->header.bit_count));
 620                return 1;
 621        }
 622
 623        debug("Display-bmp: %d x %d  with %d colors, display %d\n",
 624              (int)width, (int)height, (int)colors, 1 << bpix);
 625
 626        if (bmp_bpix == 8)
 627                lcd_set_cmap(bmp, colors);
 628
 629        padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
 630
 631#ifdef CONFIG_SPLASH_SCREEN_ALIGN
 632        splash_align_axis(&x, pwidth, width);
 633        splash_align_axis(&y, panel_info.vl_row, height);
 634#endif /* CONFIG_SPLASH_SCREEN_ALIGN */
 635
 636        if ((x + width) > pwidth)
 637                width = pwidth - x;
 638        if ((y + height) > panel_info.vl_row)
 639                height = panel_info.vl_row - y;
 640
 641        bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
 642        fb   = (uchar *)(lcd_base +
 643                (y + height - 1) * lcd_line_length + x * bpix / 8);
 644
 645        switch (bmp_bpix) {
 646        case 1:
 647        case 8: {
 648                cmap_base = configuration_get_cmap();
 649#ifdef CONFIG_LCD_BMP_RLE8
 650                u32 compression = get_unaligned_le32(&bmp->header.compression);
 651                debug("compressed %d %d\n", compression, BMP_BI_RLE8);
 652                if (compression == BMP_BI_RLE8) {
 653                        if (bpix != 16) {
 654                                /* TODO implement render code for bpix != 16 */
 655                                printf("Error: only support 16 bpix");
 656                                return 1;
 657                        }
 658                        lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y);
 659                        break;
 660                }
 661#endif
 662
 663                if (bpix != 16)
 664                        byte_width = width;
 665                else
 666                        byte_width = width * 2;
 667
 668                for (i = 0; i < height; ++i) {
 669                        WATCHDOG_RESET();
 670                        for (j = 0; j < width; j++) {
 671                                if (bpix != 16) {
 672                                        fb_put_byte(&fb, &bmap);
 673                                } else {
 674                                        struct bmp_color_table_entry *entry;
 675                                        uint val;
 676
 677                                        if (cmap_base) {
 678                                                val = cmap_base[*bmap];
 679                                        } else {
 680                                                entry = &palette[*bmap];
 681                                                val = entry->blue >> 3 |
 682                                                        entry->green >> 2 << 5 |
 683                                                        entry->red >> 3 << 11;
 684                                        }
 685                                        *(uint16_t *)fb = val;
 686                                        bmap++;
 687                                        fb += sizeof(uint16_t) / sizeof(*fb);
 688                                }
 689                        }
 690                        bmap += (padded_width - width);
 691                        fb -= byte_width + lcd_line_length;
 692                }
 693                break;
 694        }
 695#if defined(CONFIG_BMP_16BPP)
 696        case 16:
 697                for (i = 0; i < height; ++i) {
 698                        WATCHDOG_RESET();
 699                        for (j = 0; j < width; j++)
 700                                fb_put_word(&fb, &bmap);
 701
 702                        bmap += (padded_width - width) * 2;
 703                        fb -= width * 2 + lcd_line_length;
 704                }
 705                break;
 706#endif /* CONFIG_BMP_16BPP */
 707#if defined(CONFIG_BMP_24BMP)
 708        case 24:
 709                for (i = 0; i < height; ++i) {
 710                        for (j = 0; j < width; j++) {
 711                                *(fb++) = *(bmap++);
 712                                *(fb++) = *(bmap++);
 713                                *(fb++) = *(bmap++);
 714                                *(fb++) = 0;
 715                        }
 716                        fb -= lcd_line_length + width * (bpix / 8);
 717                }
 718                break;
 719#endif /* CONFIG_BMP_24BMP */
 720#if defined(CONFIG_BMP_32BPP)
 721        case 32:
 722                for (i = 0; i < height; ++i) {
 723                        for (j = 0; j < width; j++) {
 724                                *(fb++) = *(bmap++);
 725                                *(fb++) = *(bmap++);
 726                                *(fb++) = *(bmap++);
 727                                *(fb++) = *(bmap++);
 728                        }
 729                        fb -= lcd_line_length + width * (bpix / 8);
 730                }
 731                break;
 732#endif /* CONFIG_BMP_32BPP */
 733        default:
 734                break;
 735        };
 736
 737        lcd_sync();
 738        return 0;
 739}
 740#endif
 741
 742static void lcd_logo(void)
 743{
 744        lcd_logo_plot(0, 0);
 745
 746#ifdef CONFIG_LCD_INFO
 747        lcd_set_col(LCD_INFO_X / VIDEO_FONT_WIDTH);
 748        lcd_set_row(LCD_INFO_Y / VIDEO_FONT_HEIGHT);
 749        lcd_show_board_info();
 750#endif /* CONFIG_LCD_INFO */
 751}
 752
 753#ifdef CONFIG_SPLASHIMAGE_GUARD
 754static int on_splashimage(const char *name, const char *value, enum env_op op,
 755        int flags)
 756{
 757        ulong addr;
 758        int aligned;
 759
 760        if (op == env_op_delete)
 761                return 0;
 762
 763        addr = simple_strtoul(value, NULL, 16);
 764        /* See README.displaying-bmps */
 765        aligned = (addr % 4 == 2);
 766        if (!aligned) {
 767                printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n");
 768                return -1;
 769        }
 770
 771        return 0;
 772}
 773
 774U_BOOT_ENV_CALLBACK(splashimage, on_splashimage);
 775#endif
 776
 777int lcd_get_pixel_width(void)
 778{
 779        return panel_info.vl_col;
 780}
 781
 782int lcd_get_pixel_height(void)
 783{
 784        return panel_info.vl_row;
 785}
 786