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