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