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