xref: /openbmc/u-boot/common/lcd.c (revision 9649c5343fb1e105109f8aac499134dd4bd723ca)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
28655b6f8Swdenk /*
3c8d2febcSNikita Kiryanov  * Common LCD routines
48655b6f8Swdenk  *
58655b6f8Swdenk  * (C) Copyright 2001-2002
68655b6f8Swdenk  * Wolfgang Denk, DENX Software Engineering -- wd@denx.de
78655b6f8Swdenk  */
88655b6f8Swdenk 
98655b6f8Swdenk /* #define DEBUG */
108655b6f8Swdenk #include <config.h>
118655b6f8Swdenk #include <common.h>
128655b6f8Swdenk #include <command.h>
13c0880485SNikita Kiryanov #include <env_callback.h>
148655b6f8Swdenk #include <linux/types.h>
1552cb4d4fSJean-Christophe PLAGNIOL-VILLARD #include <stdio_dev.h>
168655b6f8Swdenk #include <lcd.h>
170eb25b61SJoe Hershberger #include <mapmem.h>
188b0bfc68Swdenk #include <watchdog.h>
19dca2a1c1SPrzemyslaw Marczak #include <asm/unaligned.h>
20dd4425e8SRobert Winkler #include <splash.h>
217d95f2a3SSimon Glass #include <asm/io.h>
227d95f2a3SSimon Glass #include <asm/unaligned.h>
23c8d2febcSNikita Kiryanov #include <video_font.h>
24dd4425e8SRobert Winkler 
2588804d19Swdenk #ifdef CONFIG_LCD_LOGO
26c8d2febcSNikita Kiryanov #include <bmp_logo.h>
27c270730fSChe-Liang Chiou #include <bmp_logo_data.h>
28acb13868SAlessandro Rubini #if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16)
2988804d19Swdenk #error Default Color Map overlaps with Logo Color Map
3088804d19Swdenk #endif
3188804d19Swdenk #endif
328655b6f8Swdenk 
33676d319eSSimon Glass #ifndef CONFIG_LCD_ALIGNMENT
34676d319eSSimon Glass #define CONFIG_LCD_ALIGNMENT PAGE_SIZE
35676d319eSSimon Glass #endif
36676d319eSSimon Glass 
37a7de2953SNikita Kiryanov #if (LCD_BPP != LCD_COLOR8) && (LCD_BPP != LCD_COLOR16) && \
38a7de2953SNikita Kiryanov 	(LCD_BPP != LCD_COLOR32)
39a5796c51SJeroen Hofstee #error Unsupported LCD BPP.
40a5796c51SJeroen Hofstee #endif
41a5796c51SJeroen Hofstee 
42d87080b7SWolfgang Denk DECLARE_GLOBAL_DATA_PTR;
438655b6f8Swdenk 
448655b6f8Swdenk static int lcd_init(void *lcdbase);
457bf71d1fSNikita Kiryanov static void lcd_logo(void);
468655b6f8Swdenk static void lcd_setfgcolor(int color);
478655b6f8Swdenk static void lcd_setbgcolor(int color);
488655b6f8Swdenk 
4946d1d5ddSWolfgang Denk static int lcd_color_fg;
5046d1d5ddSWolfgang Denk static int lcd_color_bg;
51f1d205a1SJeroen Hofstee int lcd_line_length;
528655b6f8Swdenk char lcd_is_enabled = 0;
5300a0ca59SJeroen Hofstee static void *lcd_base;			/* Start of framebuffer memory	*/
549a8efc46SSimon Glass static char lcd_flush_dcache;	/* 1 to flush dcache after each lcd update */
559a8efc46SSimon Glass 
569a8efc46SSimon Glass /* Flush LCD activity to the caches */
lcd_sync(void)579a8efc46SSimon Glass void lcd_sync(void)
589a8efc46SSimon Glass {
599a8efc46SSimon Glass 	/*
609a8efc46SSimon Glass 	 * flush_dcache_range() is declared in common.h but it seems that some
619a8efc46SSimon Glass 	 * architectures do not actually implement it. Is there a way to find
629a8efc46SSimon Glass 	 * out whether it exists? For now, ARM is safe.
639a8efc46SSimon Glass 	 */
649a8efc46SSimon Glass #if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF)
659a8efc46SSimon Glass 	int line_length;
669a8efc46SSimon Glass 
679a8efc46SSimon Glass 	if (lcd_flush_dcache)
68f8f58fbbSAlexander Graf 		flush_dcache_range((ulong)lcd_base,
69f8f58fbbSAlexander Graf 			(ulong)(lcd_base + lcd_get_size(&line_length)));
709a8efc46SSimon Glass #endif
719a8efc46SSimon Glass }
729a8efc46SSimon Glass 
lcd_set_flush_dcache(int flush)739a8efc46SSimon Glass void lcd_set_flush_dcache(int flush)
749a8efc46SSimon Glass {
759a8efc46SSimon Glass 	lcd_flush_dcache = (flush != 0);
769a8efc46SSimon Glass }
779a8efc46SSimon Glass 
lcd_stub_putc(struct stdio_dev * dev,const char c)78709ea543SSimon Glass static void lcd_stub_putc(struct stdio_dev *dev, const char c)
79709ea543SSimon Glass {
80709ea543SSimon Glass 	lcd_putc(c);
81709ea543SSimon Glass }
82709ea543SSimon Glass 
lcd_stub_puts(struct stdio_dev * dev,const char * s)83709ea543SSimon Glass static void lcd_stub_puts(struct stdio_dev *dev, const char *s)
84709ea543SSimon Glass {
85709ea543SSimon Glass 	lcd_puts(s);
86709ea543SSimon Glass }
87709ea543SSimon Glass 
88c8d2febcSNikita Kiryanov /* Small utility to check that you got the colours right */
898655b6f8Swdenk #ifdef LCD_TEST_PATTERN
908655b6f8Swdenk 
91e32951b5SAndreas Neubacher #if LCD_BPP == LCD_COLOR8
928655b6f8Swdenk #define	N_BLK_VERT	2
938655b6f8Swdenk #define	N_BLK_HOR	3
948655b6f8Swdenk 
958655b6f8Swdenk static int test_colors[N_BLK_HOR * N_BLK_VERT] = {
968655b6f8Swdenk 	CONSOLE_COLOR_RED,	CONSOLE_COLOR_GREEN,	CONSOLE_COLOR_YELLOW,
978655b6f8Swdenk 	CONSOLE_COLOR_BLUE,	CONSOLE_COLOR_MAGENTA,	CONSOLE_COLOR_CYAN,
98e32951b5SAndreas Neubacher }; /*LCD_BPP == LCD_COLOR8 */
99e32951b5SAndreas Neubacher 
100e32951b5SAndreas Neubacher #elif LCD_BPP == LCD_COLOR16
101e32951b5SAndreas Neubacher #define	N_BLK_VERT	2
102e32951b5SAndreas Neubacher #define	N_BLK_HOR	4
103e32951b5SAndreas Neubacher 
104e32951b5SAndreas Neubacher static int test_colors[N_BLK_HOR * N_BLK_VERT] = {
105e32951b5SAndreas Neubacher 	CONSOLE_COLOR_RED,	CONSOLE_COLOR_GREEN,	CONSOLE_COLOR_YELLOW,	CONSOLE_COLOR_BLUE,
106e32951b5SAndreas Neubacher 	CONSOLE_COLOR_MAGENTA,	CONSOLE_COLOR_CYAN,	CONSOLE_COLOR_GREY,	CONSOLE_COLOR_WHITE,
1078655b6f8Swdenk };
108e32951b5SAndreas Neubacher #endif /*LCD_BPP == LCD_COLOR16 */
1098655b6f8Swdenk 
test_pattern(void)1108655b6f8Swdenk static void test_pattern(void)
1118655b6f8Swdenk {
1128655b6f8Swdenk 	ushort v_max  = panel_info.vl_row;
1138655b6f8Swdenk 	ushort h_max  = panel_info.vl_col;
1148655b6f8Swdenk 	ushort v_step = (v_max + N_BLK_VERT - 1) / N_BLK_VERT;
1158655b6f8Swdenk 	ushort h_step = (h_max + N_BLK_HOR  - 1) / N_BLK_HOR;
1168655b6f8Swdenk 	ushort v, h;
117e32951b5SAndreas Neubacher #if LCD_BPP == LCD_COLOR8
1188655b6f8Swdenk 	uchar *pix = (uchar *)lcd_base;
119e32951b5SAndreas Neubacher #elif LCD_BPP == LCD_COLOR16
120e32951b5SAndreas Neubacher 	ushort *pix = (ushort *)lcd_base;
121e32951b5SAndreas Neubacher #endif
1228655b6f8Swdenk 
1238655b6f8Swdenk 	printf("[LCD] Test Pattern: %d x %d [%d x %d]\n",
1248655b6f8Swdenk 		h_max, v_max, h_step, v_step);
1258655b6f8Swdenk 
1268655b6f8Swdenk 	for (v = 0; v < v_max; ++v) {
1278655b6f8Swdenk 		uchar iy = v / v_step;
1288655b6f8Swdenk 		for (h = 0; h < h_max; ++h) {
1296b035141SJeroen Hofstee 			uchar ix = N_BLK_HOR * iy + h / h_step;
1308655b6f8Swdenk 			*pix++ = test_colors[ix];
1318655b6f8Swdenk 		}
1328655b6f8Swdenk 	}
1338655b6f8Swdenk }
1348655b6f8Swdenk #endif /* LCD_TEST_PATTERN */
1358655b6f8Swdenk 
136cefa4717SAnatolij Gustschin /*
137cefa4717SAnatolij Gustschin  * With most lcd drivers the line length is set up
138cefa4717SAnatolij Gustschin  * by calculating it from panel_info parameters. Some
139cefa4717SAnatolij Gustschin  * drivers need to calculate the line length differently,
140cefa4717SAnatolij Gustschin  * so make the function weak to allow overriding it.
141cefa4717SAnatolij Gustschin  */
lcd_get_size(int * line_length)142cefa4717SAnatolij Gustschin __weak int lcd_get_size(int *line_length)
143676d319eSSimon Glass {
144676d319eSSimon Glass 	*line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
145676d319eSSimon Glass 	return *line_length * panel_info.vl_row;
146676d319eSSimon Glass }
147676d319eSSimon Glass 
drv_lcd_init(void)1488655b6f8Swdenk int drv_lcd_init(void)
1498655b6f8Swdenk {
15052cb4d4fSJean-Christophe PLAGNIOL-VILLARD 	struct stdio_dev lcddev;
1518655b6f8Swdenk 	int rc;
1528655b6f8Swdenk 
1537d95f2a3SSimon Glass 	lcd_base = map_sysmem(gd->fb_base, 0);
1548655b6f8Swdenk 
155c8d2febcSNikita Kiryanov 	lcd_init(lcd_base);
1568655b6f8Swdenk 
1578655b6f8Swdenk 	/* Device initialization */
1588655b6f8Swdenk 	memset(&lcddev, 0, sizeof(lcddev));
1598655b6f8Swdenk 
1608655b6f8Swdenk 	strcpy(lcddev.name, "lcd");
1618655b6f8Swdenk 	lcddev.ext   = 0;			/* No extensions */
1628655b6f8Swdenk 	lcddev.flags = DEV_FLAGS_OUTPUT;	/* Output only */
163709ea543SSimon Glass 	lcddev.putc  = lcd_stub_putc;		/* 'putc' function */
164709ea543SSimon Glass 	lcddev.puts  = lcd_stub_puts;		/* 'puts' function */
1658655b6f8Swdenk 
16652cb4d4fSJean-Christophe PLAGNIOL-VILLARD 	rc = stdio_register(&lcddev);
1678655b6f8Swdenk 
1688655b6f8Swdenk 	return (rc == 0) ? 1 : rc;
1698655b6f8Swdenk }
1708655b6f8Swdenk 
lcd_clear(void)17102110903SChe-Liang Chiou void lcd_clear(void)
1728655b6f8Swdenk {
1734d03634eSNikita Kiryanov 	int bg_color;
1747bf71d1fSNikita Kiryanov 	char *s;
1757bf71d1fSNikita Kiryanov 	ulong addr;
1767bf71d1fSNikita Kiryanov 	static int do_splash = 1;
177f4469f50SNikita Kiryanov #if LCD_BPP == LCD_COLOR8
1788655b6f8Swdenk 	/* Setting the palette */
1798655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_BLACK, 0, 0, 0);
1808655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_RED, 0xFF, 0, 0);
1818655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_GREEN, 0, 0xFF, 0);
1828655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0);
1838655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_BLUE, 0, 0, 0xFF);
1848655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF);
1858655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF);
1868655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA);
1878655b6f8Swdenk 	lcd_setcolreg(CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF);
1888655b6f8Swdenk #endif
1898655b6f8Swdenk 
1906d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifndef CONFIG_SYS_WHITE_ON_BLACK
1918655b6f8Swdenk 	lcd_setfgcolor(CONSOLE_COLOR_BLACK);
1928655b6f8Swdenk 	lcd_setbgcolor(CONSOLE_COLOR_WHITE);
1934d03634eSNikita Kiryanov 	bg_color = CONSOLE_COLOR_WHITE;
1948655b6f8Swdenk #else
1958655b6f8Swdenk 	lcd_setfgcolor(CONSOLE_COLOR_WHITE);
1968655b6f8Swdenk 	lcd_setbgcolor(CONSOLE_COLOR_BLACK);
1974d03634eSNikita Kiryanov 	bg_color = CONSOLE_COLOR_BLACK;
1986d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif	/* CONFIG_SYS_WHITE_ON_BLACK */
1998655b6f8Swdenk 
2008655b6f8Swdenk #ifdef	LCD_TEST_PATTERN
2018655b6f8Swdenk 	test_pattern();
2028655b6f8Swdenk #else
2038655b6f8Swdenk 	/* set framebuffer to background color */
20457d76a89SHannes Petermaier #if (LCD_BPP != LCD_COLOR32)
2054d03634eSNikita Kiryanov 	memset((char *)lcd_base, bg_color, lcd_line_length * panel_info.vl_row);
20657d76a89SHannes Petermaier #else
20757d76a89SHannes Petermaier 	u32 *ppix = lcd_base;
20857d76a89SHannes Petermaier 	u32 i;
20957d76a89SHannes Petermaier 	for (i = 0;
21057d76a89SHannes Petermaier 	   i < (lcd_line_length * panel_info.vl_row)/NBYTES(panel_info.vl_bpix);
21157d76a89SHannes Petermaier 	   i++) {
2124d03634eSNikita Kiryanov 		*ppix++ = bg_color;
21357d76a89SHannes Petermaier 	}
21457d76a89SHannes Petermaier #endif
2158655b6f8Swdenk #endif
216604c7d4aSHannes Petermaier 	/* setup text-console */
217604c7d4aSHannes Petermaier 	debug("[LCD] setting up console...\n");
218604c7d4aSHannes Petermaier 	lcd_init_console(lcd_base,
219604c7d4aSHannes Petermaier 			 panel_info.vl_col,
220604c7d4aSHannes Petermaier 			 panel_info.vl_row,
221604c7d4aSHannes Petermaier 			 panel_info.vl_rot);
2228655b6f8Swdenk 	/* Paint the logo and retrieve LCD base address */
2238655b6f8Swdenk 	debug("[LCD] Drawing the logo...\n");
2247bf71d1fSNikita Kiryanov 	if (do_splash) {
22500caae6dSSimon Glass 		s = env_get("splashimage");
2267bf71d1fSNikita Kiryanov 		if (s) {
2277bf71d1fSNikita Kiryanov 			do_splash = 0;
2287bf71d1fSNikita Kiryanov 			addr = simple_strtoul(s, NULL, 16);
2297bf71d1fSNikita Kiryanov 			if (lcd_splash(addr) == 0) {
2307bf71d1fSNikita Kiryanov 				lcd_sync();
2317bf71d1fSNikita Kiryanov 				return;
2327bf71d1fSNikita Kiryanov 			}
2337bf71d1fSNikita Kiryanov 		}
2347bf71d1fSNikita Kiryanov 	}
2357bf71d1fSNikita Kiryanov 
2367bf71d1fSNikita Kiryanov 	lcd_logo();
2377bf71d1fSNikita Kiryanov #if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
2387bf71d1fSNikita Kiryanov 	addr = (ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length;
2393b96b909SMarcel Ziswiler 	lcd_init_console((void *)addr, panel_info.vl_col,
2403b96b909SMarcel Ziswiler 			 panel_info.vl_row, panel_info.vl_rot);
2417bf71d1fSNikita Kiryanov #endif
2429a8efc46SSimon Glass 	lcd_sync();
2439a8efc46SSimon Glass }
2449a8efc46SSimon Glass 
lcd_init(void * lcdbase)2458655b6f8Swdenk static int lcd_init(void *lcdbase)
2468655b6f8Swdenk {
2478655b6f8Swdenk 	debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);
2488655b6f8Swdenk 	lcd_ctrl_init(lcdbase);
2491d3dea12SAnatolij Gustschin 
2501d3dea12SAnatolij Gustschin 	/*
2519316e144SStephen Warren 	 * lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi) ignores
2521d3dea12SAnatolij Gustschin 	 * the 'lcdbase' argument and uses custom lcd base address
2531d3dea12SAnatolij Gustschin 	 * by setting up gd->fb_base. Check for this condition and fixup
2541d3dea12SAnatolij Gustschin 	 * 'lcd_base' address.
2551d3dea12SAnatolij Gustschin 	 */
2567d95f2a3SSimon Glass 	if (map_to_sysmem(lcdbase) != gd->fb_base)
2577d95f2a3SSimon Glass 		lcd_base = map_sysmem(gd->fb_base, 0);
2581d3dea12SAnatolij Gustschin 
2591d3dea12SAnatolij Gustschin 	debug("[LCD] Using LCD frambuffer at %p\n", lcd_base);
2601d3dea12SAnatolij Gustschin 
2616d330719SStephen Warren 	lcd_get_size(&lcd_line_length);
2626f93d2b8SHaavard Skinnemoen 	lcd_is_enabled = 1;
26302110903SChe-Liang Chiou 	lcd_clear();
2648655b6f8Swdenk 	lcd_enable();
2658655b6f8Swdenk 
2668655b6f8Swdenk 	/* Initialize the console */
267140beb94SNikita Kiryanov 	lcd_set_col(0);
26888804d19Swdenk #ifdef CONFIG_LCD_INFO_BELOW_LOGO
269140beb94SNikita Kiryanov 	lcd_set_row(7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT);
2708655b6f8Swdenk #else
271140beb94SNikita Kiryanov 	lcd_set_row(1);	/* leave 1 blank line below logo */
2728655b6f8Swdenk #endif
2738655b6f8Swdenk 
2748655b6f8Swdenk 	return 0;
2758655b6f8Swdenk }
2768655b6f8Swdenk 
2778655b6f8Swdenk /*
2788655b6f8Swdenk  * This is called early in the system initialization to grab memory
2798655b6f8Swdenk  * for the LCD controller.
2808655b6f8Swdenk  * Returns new address for monitor, after reserving LCD buffer memory
2818655b6f8Swdenk  *
2828655b6f8Swdenk  * Note that this is running from ROM, so no write access to global data.
2838655b6f8Swdenk  */
lcd_setmem(ulong addr)2848655b6f8Swdenk ulong lcd_setmem(ulong addr)
2858655b6f8Swdenk {
2868655b6f8Swdenk 	ulong size;
287676d319eSSimon Glass 	int line_length;
2888655b6f8Swdenk 
2898f47d917SNikita Kiryanov 	debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col,
2908f47d917SNikita Kiryanov 		panel_info.vl_row, NBITS(panel_info.vl_bpix));
2918655b6f8Swdenk 
292676d319eSSimon Glass 	size = lcd_get_size(&line_length);
2938655b6f8Swdenk 
294676d319eSSimon Glass 	/* Round up to nearest full page, or MMU section if defined */
295676d319eSSimon Glass 	size = ALIGN(size, CONFIG_LCD_ALIGNMENT);
296676d319eSSimon Glass 	addr = ALIGN(addr - CONFIG_LCD_ALIGNMENT + 1, CONFIG_LCD_ALIGNMENT);
2978655b6f8Swdenk 
2988655b6f8Swdenk 	/* Allocate pages for the frame buffer. */
2998655b6f8Swdenk 	addr -= size;
3008655b6f8Swdenk 
3016b035141SJeroen Hofstee 	debug("Reserving %ldk for LCD Framebuffer at: %08lx\n",
3026b035141SJeroen Hofstee 	      size >> 10, addr);
3038655b6f8Swdenk 
3048f47d917SNikita Kiryanov 	return addr;
3058655b6f8Swdenk }
3068655b6f8Swdenk 
lcd_setfgcolor(int color)3078655b6f8Swdenk static void lcd_setfgcolor(int color)
3088655b6f8Swdenk {
30939cf4804SStelian Pop 	lcd_color_fg = color;
3108655b6f8Swdenk }
3118655b6f8Swdenk 
lcd_getfgcolor(void)3124d03634eSNikita Kiryanov int lcd_getfgcolor(void)
3134d03634eSNikita Kiryanov {
3144d03634eSNikita Kiryanov 	return lcd_color_fg;
3154d03634eSNikita Kiryanov }
3164d03634eSNikita Kiryanov 
lcd_setbgcolor(int color)3178655b6f8Swdenk static void lcd_setbgcolor(int color)
3188655b6f8Swdenk {
31939cf4804SStelian Pop 	lcd_color_bg = color;
3208655b6f8Swdenk }
3218655b6f8Swdenk 
lcd_getbgcolor(void)3224d03634eSNikita Kiryanov int lcd_getbgcolor(void)
3234d03634eSNikita Kiryanov {
3244d03634eSNikita Kiryanov 	return lcd_color_bg;
3254d03634eSNikita Kiryanov }
3264d03634eSNikita Kiryanov 
3278655b6f8Swdenk #ifdef CONFIG_LCD_LOGO
lcd_logo_set_cmap(void)328a02e9481SNikita Kiryanov __weak void lcd_logo_set_cmap(void)
329a02e9481SNikita Kiryanov {
3302306457cSNikita Kiryanov 	int i;
3312306457cSNikita Kiryanov 	ushort *cmap = configuration_get_cmap();
3322306457cSNikita Kiryanov 
3332306457cSNikita Kiryanov 	for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i)
3342306457cSNikita Kiryanov 		*cmap++ = bmp_logo_palette[i];
335a02e9481SNikita Kiryanov }
336a02e9481SNikita Kiryanov 
lcd_logo_plot(int x,int y)337bf21a5deSNikita Kiryanov void lcd_logo_plot(int x, int y)
3388655b6f8Swdenk {
3398655b6f8Swdenk 	ushort i, j;
340c8d2febcSNikita Kiryanov 	uchar *bmap = &bmp_logo_bitmap[0];
341317461c1SAndre Renaud 	unsigned bpix = NBITS(panel_info.vl_bpix);
342c8d2febcSNikita Kiryanov 	uchar *fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8);
343c8d2febcSNikita Kiryanov 	ushort *fb16;
3448655b6f8Swdenk 
3452306457cSNikita Kiryanov 	debug("Logo: width %d  height %d  colors %d\n",
3462306457cSNikita Kiryanov 	      BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS);
3478655b6f8Swdenk 
348317461c1SAndre Renaud 	if (bpix < 12) {
3498655b6f8Swdenk 		WATCHDOG_RESET();
350a02e9481SNikita Kiryanov 		lcd_logo_set_cmap();
3518655b6f8Swdenk 		WATCHDOG_RESET();
3528655b6f8Swdenk 
3538655b6f8Swdenk 		for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
3548655b6f8Swdenk 			memcpy(fb, bmap, BMP_LOGO_WIDTH);
3558655b6f8Swdenk 			bmap += BMP_LOGO_WIDTH;
3568655b6f8Swdenk 			fb += panel_info.vl_col;
3578655b6f8Swdenk 		}
3588655b6f8Swdenk 	}
3598655b6f8Swdenk 	else { /* true color mode */
360acb13868SAlessandro Rubini 		u16 col16;
361317461c1SAndre Renaud 		fb16 = (ushort *)fb;
3628655b6f8Swdenk 		for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
3638655b6f8Swdenk 			for (j = 0; j < BMP_LOGO_WIDTH; j++) {
364acb13868SAlessandro Rubini 				col16 = bmp_logo_palette[(bmap[j]-16)];
365acb13868SAlessandro Rubini 				fb16[j] =
366acb13868SAlessandro Rubini 					((col16 & 0x000F) << 1) |
367acb13868SAlessandro Rubini 					((col16 & 0x00F0) << 3) |
368acb13868SAlessandro Rubini 					((col16 & 0x0F00) << 4);
3698655b6f8Swdenk 				}
3708655b6f8Swdenk 			bmap += BMP_LOGO_WIDTH;
3718655b6f8Swdenk 			fb16 += panel_info.vl_col;
3728655b6f8Swdenk 		}
3738655b6f8Swdenk 	}
3748655b6f8Swdenk 
3758655b6f8Swdenk 	WATCHDOG_RESET();
3769a8efc46SSimon Glass 	lcd_sync();
3778655b6f8Swdenk }
3782b5cb3d3SAnatolij Gustschin #else
lcd_logo_plot(int x,int y)379bf21a5deSNikita Kiryanov static inline void lcd_logo_plot(int x, int y) {}
3808655b6f8Swdenk #endif /* CONFIG_LCD_LOGO */
3818655b6f8Swdenk 
382c3517f91SJon Loeliger #if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN)
3831ca298ceSMatthias Weisser #ifdef CONFIG_SPLASH_SCREEN_ALIGN
3847c7e280aSNikita Kiryanov 
splash_align_axis(int * axis,unsigned long panel_size,unsigned long picture_size)3857c7e280aSNikita Kiryanov static void splash_align_axis(int *axis, unsigned long panel_size,
3867c7e280aSNikita Kiryanov 					unsigned long picture_size)
3877c7e280aSNikita Kiryanov {
3887c7e280aSNikita Kiryanov 	unsigned long panel_picture_delta = panel_size - picture_size;
3897c7e280aSNikita Kiryanov 	unsigned long axis_alignment;
3907c7e280aSNikita Kiryanov 
3917c7e280aSNikita Kiryanov 	if (*axis == BMP_ALIGN_CENTER)
3927c7e280aSNikita Kiryanov 		axis_alignment = panel_picture_delta / 2;
3937c7e280aSNikita Kiryanov 	else if (*axis < 0)
3947c7e280aSNikita Kiryanov 		axis_alignment = panel_picture_delta + *axis + 1;
3957c7e280aSNikita Kiryanov 	else
3967c7e280aSNikita Kiryanov 		return;
3977c7e280aSNikita Kiryanov 
398b4141195SMasahiro Yamada 	*axis = max(0, (int)axis_alignment);
3997c7e280aSNikita Kiryanov }
4001ca298ceSMatthias Weisser #endif
4011ca298ceSMatthias Weisser 
40245d7f525STom Wai-Hong Tam #ifdef CONFIG_LCD_BMP_RLE8
40345d7f525STom Wai-Hong Tam #define BMP_RLE8_ESCAPE		0
40445d7f525STom Wai-Hong Tam #define BMP_RLE8_EOL		0
40545d7f525STom Wai-Hong Tam #define BMP_RLE8_EOBMP		1
40645d7f525STom Wai-Hong Tam #define BMP_RLE8_DELTA		2
40745d7f525STom Wai-Hong Tam 
draw_unencoded_bitmap(ushort ** fbp,uchar * bmap,ushort * cmap,int cnt)40845d7f525STom Wai-Hong Tam static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
40945d7f525STom Wai-Hong Tam 				  int cnt)
41045d7f525STom Wai-Hong Tam {
41145d7f525STom Wai-Hong Tam 	while (cnt > 0) {
41245d7f525STom Wai-Hong Tam 		*(*fbp)++ = cmap[*bmap++];
41345d7f525STom Wai-Hong Tam 		cnt--;
41445d7f525STom Wai-Hong Tam 	}
41545d7f525STom Wai-Hong Tam }
41645d7f525STom Wai-Hong Tam 
draw_encoded_bitmap(ushort ** fbp,ushort c,int cnt)41745d7f525STom Wai-Hong Tam static void draw_encoded_bitmap(ushort **fbp, ushort c, int cnt)
41845d7f525STom Wai-Hong Tam {
41945d7f525STom Wai-Hong Tam 	ushort *fb = *fbp;
42045d7f525STom Wai-Hong Tam 	int cnt_8copy = cnt >> 3;
42145d7f525STom Wai-Hong Tam 
42245d7f525STom Wai-Hong Tam 	cnt -= cnt_8copy << 3;
42345d7f525STom Wai-Hong Tam 	while (cnt_8copy > 0) {
42445d7f525STom Wai-Hong Tam 		*fb++ = c;
42545d7f525STom Wai-Hong Tam 		*fb++ = c;
42645d7f525STom Wai-Hong Tam 		*fb++ = c;
42745d7f525STom Wai-Hong Tam 		*fb++ = c;
42845d7f525STom Wai-Hong Tam 		*fb++ = c;
42945d7f525STom Wai-Hong Tam 		*fb++ = c;
43045d7f525STom Wai-Hong Tam 		*fb++ = c;
43145d7f525STom Wai-Hong Tam 		*fb++ = c;
43245d7f525STom Wai-Hong Tam 		cnt_8copy--;
43345d7f525STom Wai-Hong Tam 	}
43445d7f525STom Wai-Hong Tam 	while (cnt > 0) {
43545d7f525STom Wai-Hong Tam 		*fb++ = c;
43645d7f525STom Wai-Hong Tam 		cnt--;
43745d7f525STom Wai-Hong Tam 	}
4386b035141SJeroen Hofstee 	*fbp = fb;
43945d7f525STom Wai-Hong Tam }
44045d7f525STom Wai-Hong Tam 
44145d7f525STom Wai-Hong Tam /*
4426b035141SJeroen Hofstee  * Do not call this function directly, must be called from lcd_display_bitmap.
44345d7f525STom Wai-Hong Tam  */
lcd_display_rle8_bitmap(struct bmp_image * bmp,ushort * cmap,uchar * fb,int x_off,int y_off)4441c3dbe56SSimon Glass static void lcd_display_rle8_bitmap(struct bmp_image *bmp, ushort *cmap,
4451c3dbe56SSimon Glass 				    uchar *fb, int x_off, int y_off)
44645d7f525STom Wai-Hong Tam {
44745d7f525STom Wai-Hong Tam 	uchar *bmap;
44845d7f525STom Wai-Hong Tam 	ulong width, height;
44945d7f525STom Wai-Hong Tam 	ulong cnt, runlen;
45045d7f525STom Wai-Hong Tam 	int x, y;
45145d7f525STom Wai-Hong Tam 	int decode = 1;
45245d7f525STom Wai-Hong Tam 
453dca2a1c1SPrzemyslaw Marczak 	width = get_unaligned_le32(&bmp->header.width);
454dca2a1c1SPrzemyslaw Marczak 	height = get_unaligned_le32(&bmp->header.height);
455dca2a1c1SPrzemyslaw Marczak 	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
45645d7f525STom Wai-Hong Tam 
45745d7f525STom Wai-Hong Tam 	x = 0;
45845d7f525STom Wai-Hong Tam 	y = height - 1;
45945d7f525STom Wai-Hong Tam 
46045d7f525STom Wai-Hong Tam 	while (decode) {
46145d7f525STom Wai-Hong Tam 		if (bmap[0] == BMP_RLE8_ESCAPE) {
46245d7f525STom Wai-Hong Tam 			switch (bmap[1]) {
46345d7f525STom Wai-Hong Tam 			case BMP_RLE8_EOL:
46445d7f525STom Wai-Hong Tam 				/* end of line */
46545d7f525STom Wai-Hong Tam 				bmap += 2;
46645d7f525STom Wai-Hong Tam 				x = 0;
46745d7f525STom Wai-Hong Tam 				y--;
46845d7f525STom Wai-Hong Tam 				/* 16bpix, 2-byte per pixel, width should *2 */
46945d7f525STom Wai-Hong Tam 				fb -= (width * 2 + lcd_line_length);
47045d7f525STom Wai-Hong Tam 				break;
47145d7f525STom Wai-Hong Tam 			case BMP_RLE8_EOBMP:
47245d7f525STom Wai-Hong Tam 				/* end of bitmap */
47345d7f525STom Wai-Hong Tam 				decode = 0;
47445d7f525STom Wai-Hong Tam 				break;
47545d7f525STom Wai-Hong Tam 			case BMP_RLE8_DELTA:
47645d7f525STom Wai-Hong Tam 				/* delta run */
47745d7f525STom Wai-Hong Tam 				x += bmap[2];
47845d7f525STom Wai-Hong Tam 				y -= bmap[3];
47945d7f525STom Wai-Hong Tam 				/* 16bpix, 2-byte per pixel, x should *2 */
48045d7f525STom Wai-Hong Tam 				fb = (uchar *) (lcd_base + (y + y_off - 1)
48145d7f525STom Wai-Hong Tam 					* lcd_line_length + (x + x_off) * 2);
48245d7f525STom Wai-Hong Tam 				bmap += 4;
48345d7f525STom Wai-Hong Tam 				break;
48445d7f525STom Wai-Hong Tam 			default:
48545d7f525STom Wai-Hong Tam 				/* unencoded run */
48645d7f525STom Wai-Hong Tam 				runlen = bmap[1];
48745d7f525STom Wai-Hong Tam 				bmap += 2;
48845d7f525STom Wai-Hong Tam 				if (y < height) {
48945d7f525STom Wai-Hong Tam 					if (x < width) {
49045d7f525STom Wai-Hong Tam 						if (x + runlen > width)
49145d7f525STom Wai-Hong Tam 							cnt = width - x;
49245d7f525STom Wai-Hong Tam 						else
49345d7f525STom Wai-Hong Tam 							cnt = runlen;
49445d7f525STom Wai-Hong Tam 						draw_unencoded_bitmap(
49545d7f525STom Wai-Hong Tam 							(ushort **)&fb,
49645d7f525STom Wai-Hong Tam 							bmap, cmap, cnt);
49745d7f525STom Wai-Hong Tam 					}
49845d7f525STom Wai-Hong Tam 					x += runlen;
49945d7f525STom Wai-Hong Tam 				}
50045d7f525STom Wai-Hong Tam 				bmap += runlen;
50145d7f525STom Wai-Hong Tam 				if (runlen & 1)
50245d7f525STom Wai-Hong Tam 					bmap++;
50345d7f525STom Wai-Hong Tam 			}
50445d7f525STom Wai-Hong Tam 		} else {
50545d7f525STom Wai-Hong Tam 			/* encoded run */
50645d7f525STom Wai-Hong Tam 			if (y < height) {
50745d7f525STom Wai-Hong Tam 				runlen = bmap[0];
50845d7f525STom Wai-Hong Tam 				if (x < width) {
50945d7f525STom Wai-Hong Tam 					/* aggregate the same code */
51045d7f525STom Wai-Hong Tam 					while (bmap[0] == 0xff &&
51145d7f525STom Wai-Hong Tam 					       bmap[2] != BMP_RLE8_ESCAPE &&
51245d7f525STom Wai-Hong Tam 					       bmap[1] == bmap[3]) {
51345d7f525STom Wai-Hong Tam 						runlen += bmap[2];
51445d7f525STom Wai-Hong Tam 						bmap += 2;
51545d7f525STom Wai-Hong Tam 					}
51645d7f525STom Wai-Hong Tam 					if (x + runlen > width)
51745d7f525STom Wai-Hong Tam 						cnt = width - x;
51845d7f525STom Wai-Hong Tam 					else
51945d7f525STom Wai-Hong Tam 						cnt = runlen;
52045d7f525STom Wai-Hong Tam 					draw_encoded_bitmap((ushort **)&fb,
52145d7f525STom Wai-Hong Tam 						cmap[bmap[1]], cnt);
52245d7f525STom Wai-Hong Tam 				}
52345d7f525STom Wai-Hong Tam 				x += runlen;
52445d7f525STom Wai-Hong Tam 			}
52545d7f525STom Wai-Hong Tam 			bmap += 2;
52645d7f525STom Wai-Hong Tam 		}
52745d7f525STom Wai-Hong Tam 	}
52845d7f525STom Wai-Hong Tam }
52945d7f525STom Wai-Hong Tam #endif
53045d7f525STom Wai-Hong Tam 
fb_put_byte(uchar ** fb,uchar ** from)53127fad01bSNikita Kiryanov __weak void fb_put_byte(uchar **fb, uchar **from)
53227fad01bSNikita Kiryanov {
53327fad01bSNikita Kiryanov 	*(*fb)++ = *(*from)++;
53427fad01bSNikita Kiryanov }
535bfdcc65eSNikita Kiryanov 
536bfdcc65eSNikita Kiryanov #if defined(CONFIG_BMP_16BPP)
fb_put_word(uchar ** fb,uchar ** from)537b3d12e9bSNikita Kiryanov __weak void fb_put_word(uchar **fb, uchar **from)
538bfdcc65eSNikita Kiryanov {
539bfdcc65eSNikita Kiryanov 	*(*fb)++ = *(*from)++;
540bfdcc65eSNikita Kiryanov 	*(*fb)++ = *(*from)++;
541bfdcc65eSNikita Kiryanov }
542bfdcc65eSNikita Kiryanov #endif /* CONFIG_BMP_16BPP */
543bfdcc65eSNikita Kiryanov 
lcd_set_cmap(struct bmp_image * bmp,unsigned colors)5441c3dbe56SSimon Glass __weak void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
5450b29a896SNikita Kiryanov {
5460b29a896SNikita Kiryanov 	int i;
5471c3dbe56SSimon Glass 	struct bmp_color_table_entry cte;
5480b29a896SNikita Kiryanov 	ushort *cmap = configuration_get_cmap();
5490b29a896SNikita Kiryanov 
5500b29a896SNikita Kiryanov 	for (i = 0; i < colors; ++i) {
5510b29a896SNikita Kiryanov 		cte = bmp->color_table[i];
5520b29a896SNikita Kiryanov 		*cmap = (((cte.red)   << 8) & 0xf800) |
5530b29a896SNikita Kiryanov 			(((cte.green) << 3) & 0x07e0) |
5540b29a896SNikita Kiryanov 			(((cte.blue)  >> 3) & 0x001f);
5550b29a896SNikita Kiryanov 		cmap++;
5560b29a896SNikita Kiryanov 	}
5570b29a896SNikita Kiryanov }
5580b29a896SNikita Kiryanov 
lcd_display_bitmap(ulong bmp_image,int x,int y)5598655b6f8Swdenk int lcd_display_bitmap(ulong bmp_image, int x, int y)
5608655b6f8Swdenk {
56100cc5595SAnatolij Gustschin 	ushort *cmap_base = NULL;
5628655b6f8Swdenk 	ushort i, j;
5638655b6f8Swdenk 	uchar *fb;
5641c3dbe56SSimon Glass 	struct bmp_image *bmp = (struct bmp_image *)map_sysmem(bmp_image, 0);
5658655b6f8Swdenk 	uchar *bmap;
566fecac46cSTom Wai-Hong Tam 	ushort padded_width;
567b245e65eSGuennadi Liakhovetski 	unsigned long width, height, byte_width;
568e8143e72SWolfgang Denk 	unsigned long pwidth = panel_info.vl_col;
569b245e65eSGuennadi Liakhovetski 	unsigned colors, bpix, bmp_bpix;
5708d379f17SSimon Glass 	int hdr_size;
571021414a3Sxypron.glpk@gmx.de 	struct bmp_color_table_entry *palette;
5728655b6f8Swdenk 
5736b035141SJeroen Hofstee 	if (!bmp || !(bmp->header.signature[0] == 'B' &&
5746b035141SJeroen Hofstee 		bmp->header.signature[1] == 'M')) {
5758655b6f8Swdenk 		printf("Error: no valid bmp image at %lx\n", bmp_image);
5768f47d917SNikita Kiryanov 
5778655b6f8Swdenk 		return 1;
5788655b6f8Swdenk 	}
5798655b6f8Swdenk 
580021414a3Sxypron.glpk@gmx.de 	palette = bmp->color_table;
581dca2a1c1SPrzemyslaw Marczak 	width = get_unaligned_le32(&bmp->header.width);
582dca2a1c1SPrzemyslaw Marczak 	height = get_unaligned_le32(&bmp->header.height);
583dca2a1c1SPrzemyslaw Marczak 	bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
5848d379f17SSimon Glass 	hdr_size = get_unaligned_le16(&bmp->header.size);
5858d379f17SSimon Glass 	debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
586dca2a1c1SPrzemyslaw Marczak 
587b245e65eSGuennadi Liakhovetski 	colors = 1 << bmp_bpix;
5888655b6f8Swdenk 
5898655b6f8Swdenk 	bpix = NBITS(panel_info.vl_bpix);
5908655b6f8Swdenk 
5916b035141SJeroen Hofstee 	if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
592b245e65eSGuennadi Liakhovetski 		printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
593b245e65eSGuennadi Liakhovetski 			bpix, bmp_bpix);
5948f47d917SNikita Kiryanov 
5958655b6f8Swdenk 		return 1;
5968655b6f8Swdenk 	}
5978655b6f8Swdenk 
598a305fb15SHannes Petermaier 	/*
599a305fb15SHannes Petermaier 	 * We support displaying 8bpp BMPs on 16bpp LCDs
600a305fb15SHannes Petermaier 	 * and displaying 24bpp BMPs on 32bpp LCDs
601a305fb15SHannes Petermaier 	 * */
602a305fb15SHannes Petermaier 	if (bpix != bmp_bpix &&
603a305fb15SHannes Petermaier 	    !(bmp_bpix == 8 && bpix == 16) &&
604a305fb15SHannes Petermaier 	    !(bmp_bpix == 24 && bpix == 32)) {
6058655b6f8Swdenk 		printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
606dca2a1c1SPrzemyslaw Marczak 			bpix, get_unaligned_le16(&bmp->header.bit_count));
6078655b6f8Swdenk 		return 1;
6088655b6f8Swdenk 	}
6098655b6f8Swdenk 
6108d379f17SSimon Glass 	debug("Display-bmp: %d x %d  with %d colors, display %d\n",
6118d379f17SSimon Glass 	      (int)width, (int)height, (int)colors, 1 << bpix);
6128655b6f8Swdenk 
6130b29a896SNikita Kiryanov 	if (bmp_bpix == 8)
6140b29a896SNikita Kiryanov 		lcd_set_cmap(bmp, colors);
615e8143e72SWolfgang Denk 
6166b035141SJeroen Hofstee 	padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
6171ca298ceSMatthias Weisser 
6181ca298ceSMatthias Weisser #ifdef CONFIG_SPLASH_SCREEN_ALIGN
6197c7e280aSNikita Kiryanov 	splash_align_axis(&x, pwidth, width);
6207c7e280aSNikita Kiryanov 	splash_align_axis(&y, panel_info.vl_row, height);
6211ca298ceSMatthias Weisser #endif /* CONFIG_SPLASH_SCREEN_ALIGN */
6221ca298ceSMatthias Weisser 
623e8143e72SWolfgang Denk 	if ((x + width) > pwidth)
624e8143e72SWolfgang Denk 		width = pwidth - x;
6258655b6f8Swdenk 	if ((y + height) > panel_info.vl_row)
6268655b6f8Swdenk 		height = panel_info.vl_row - y;
6278655b6f8Swdenk 
628dca2a1c1SPrzemyslaw Marczak 	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
6298655b6f8Swdenk 	fb   = (uchar *)(lcd_base +
6308d46d5b1SLiu Ying 		(y + height - 1) * lcd_line_length + x * bpix / 8);
631a303dfb0SMark Jackson 
632b245e65eSGuennadi Liakhovetski 	switch (bmp_bpix) {
633c8d2febcSNikita Kiryanov 	case 1:
6340156444cSSimon Glass 	case 8: {
6350b29a896SNikita Kiryanov 		cmap_base = configuration_get_cmap();
63645d7f525STom Wai-Hong Tam #ifdef CONFIG_LCD_BMP_RLE8
637dca2a1c1SPrzemyslaw Marczak 		u32 compression = get_unaligned_le32(&bmp->header.compression);
6388d379f17SSimon Glass 		debug("compressed %d %d\n", compression, BMP_BI_RLE8);
639dca2a1c1SPrzemyslaw Marczak 		if (compression == BMP_BI_RLE8) {
64045d7f525STom Wai-Hong Tam 			if (bpix != 16) {
64145d7f525STom Wai-Hong Tam 				/* TODO implement render code for bpix != 16 */
64245d7f525STom Wai-Hong Tam 				printf("Error: only support 16 bpix");
64345d7f525STom Wai-Hong Tam 				return 1;
64445d7f525STom Wai-Hong Tam 			}
64545d7f525STom Wai-Hong Tam 			lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y);
64645d7f525STom Wai-Hong Tam 			break;
64745d7f525STom Wai-Hong Tam 		}
64845d7f525STom Wai-Hong Tam #endif
64945d7f525STom Wai-Hong Tam 
650b245e65eSGuennadi Liakhovetski 		if (bpix != 16)
651b245e65eSGuennadi Liakhovetski 			byte_width = width;
652b245e65eSGuennadi Liakhovetski 		else
653b245e65eSGuennadi Liakhovetski 			byte_width = width * 2;
654b245e65eSGuennadi Liakhovetski 
6558655b6f8Swdenk 		for (i = 0; i < height; ++i) {
65651152c17Swdenk 			WATCHDOG_RESET();
657b245e65eSGuennadi Liakhovetski 			for (j = 0; j < width; j++) {
658b245e65eSGuennadi Liakhovetski 				if (bpix != 16) {
65927fad01bSNikita Kiryanov 					fb_put_byte(&fb, &bmap);
660b245e65eSGuennadi Liakhovetski 				} else {
6618d379f17SSimon Glass 					struct bmp_color_table_entry *entry;
6628d379f17SSimon Glass 					uint val;
6638d379f17SSimon Glass 
6648d379f17SSimon Glass 					if (cmap_base) {
6658d379f17SSimon Glass 						val = cmap_base[*bmap];
6668d379f17SSimon Glass 					} else {
6678d379f17SSimon Glass 						entry = &palette[*bmap];
6688d379f17SSimon Glass 						val = entry->blue >> 3 |
6698d379f17SSimon Glass 							entry->green >> 2 << 5 |
6708d379f17SSimon Glass 							entry->red >> 3 << 11;
6718d379f17SSimon Glass 					}
6728d379f17SSimon Glass 					*(uint16_t *)fb = val;
6738d379f17SSimon Glass 					bmap++;
674b245e65eSGuennadi Liakhovetski 					fb += sizeof(uint16_t) / sizeof(*fb);
675b245e65eSGuennadi Liakhovetski 				}
676b245e65eSGuennadi Liakhovetski 			}
677fecac46cSTom Wai-Hong Tam 			bmap += (padded_width - width);
6786b035141SJeroen Hofstee 			fb -= byte_width + lcd_line_length;
6798655b6f8Swdenk 		}
680a303dfb0SMark Jackson 		break;
6810156444cSSimon Glass 	}
682a303dfb0SMark Jackson #if defined(CONFIG_BMP_16BPP)
683a303dfb0SMark Jackson 	case 16:
684a303dfb0SMark Jackson 		for (i = 0; i < height; ++i) {
685a303dfb0SMark Jackson 			WATCHDOG_RESET();
686bfdcc65eSNikita Kiryanov 			for (j = 0; j < width; j++)
687bfdcc65eSNikita Kiryanov 				fb_put_word(&fb, &bmap);
688bfdcc65eSNikita Kiryanov 
689fecac46cSTom Wai-Hong Tam 			bmap += (padded_width - width) * 2;
6906b035141SJeroen Hofstee 			fb -= width * 2 + lcd_line_length;
691a303dfb0SMark Jackson 		}
692a303dfb0SMark Jackson 		break;
693a303dfb0SMark Jackson #endif /* CONFIG_BMP_16BPP */
69410ba6b33SPhilipp Tomsich #if defined(CONFIG_BMP_24BPP)
695a305fb15SHannes Petermaier 	case 24:
696a305fb15SHannes Petermaier 		for (i = 0; i < height; ++i) {
697a305fb15SHannes Petermaier 			for (j = 0; j < width; j++) {
698a305fb15SHannes Petermaier 				*(fb++) = *(bmap++);
699a305fb15SHannes Petermaier 				*(fb++) = *(bmap++);
700a305fb15SHannes Petermaier 				*(fb++) = *(bmap++);
701a305fb15SHannes Petermaier 				*(fb++) = 0;
702a305fb15SHannes Petermaier 			}
703a305fb15SHannes Petermaier 			fb -= lcd_line_length + width * (bpix / 8);
704a305fb15SHannes Petermaier 		}
705a305fb15SHannes Petermaier 		break;
70610ba6b33SPhilipp Tomsich #endif /* CONFIG_BMP_24BPP */
707fb6a9aabSDonghwa Lee #if defined(CONFIG_BMP_32BPP)
708fb6a9aabSDonghwa Lee 	case 32:
709fb6a9aabSDonghwa Lee 		for (i = 0; i < height; ++i) {
710fb6a9aabSDonghwa Lee 			for (j = 0; j < width; j++) {
711fb6a9aabSDonghwa Lee 				*(fb++) = *(bmap++);
712fb6a9aabSDonghwa Lee 				*(fb++) = *(bmap++);
713fb6a9aabSDonghwa Lee 				*(fb++) = *(bmap++);
714fb6a9aabSDonghwa Lee 				*(fb++) = *(bmap++);
715fb6a9aabSDonghwa Lee 			}
7166b035141SJeroen Hofstee 			fb -= lcd_line_length + width * (bpix / 8);
717fb6a9aabSDonghwa Lee 		}
718fb6a9aabSDonghwa Lee 		break;
719fb6a9aabSDonghwa Lee #endif /* CONFIG_BMP_32BPP */
720a303dfb0SMark Jackson 	default:
721a303dfb0SMark Jackson 		break;
722a303dfb0SMark Jackson 	};
7238655b6f8Swdenk 
7249a8efc46SSimon Glass 	lcd_sync();
7258f47d917SNikita Kiryanov 	return 0;
7268655b6f8Swdenk }
727c3517f91SJon Loeliger #endif
7288655b6f8Swdenk 
lcd_logo(void)7297bf71d1fSNikita Kiryanov static void lcd_logo(void)
7308655b6f8Swdenk {
731bf21a5deSNikita Kiryanov 	lcd_logo_plot(0, 0);
7328655b6f8Swdenk 
73388804d19Swdenk #ifdef CONFIG_LCD_INFO
734140beb94SNikita Kiryanov 	lcd_set_col(LCD_INFO_X / VIDEO_FONT_WIDTH);
735140beb94SNikita Kiryanov 	lcd_set_row(LCD_INFO_Y / VIDEO_FONT_HEIGHT);
7366b59e03eSHaavard Skinnemoen 	lcd_show_board_info();
73788804d19Swdenk #endif /* CONFIG_LCD_INFO */
7388655b6f8Swdenk }
7398655b6f8Swdenk 
740c0880485SNikita Kiryanov #ifdef CONFIG_SPLASHIMAGE_GUARD
on_splashimage(const char * name,const char * value,enum env_op op,int flags)741c0880485SNikita Kiryanov static int on_splashimage(const char *name, const char *value, enum env_op op,
742c0880485SNikita Kiryanov 	int flags)
743c0880485SNikita Kiryanov {
744c0880485SNikita Kiryanov 	ulong addr;
745c0880485SNikita Kiryanov 	int aligned;
746c0880485SNikita Kiryanov 
747c0880485SNikita Kiryanov 	if (op == env_op_delete)
748c0880485SNikita Kiryanov 		return 0;
749c0880485SNikita Kiryanov 
750c0880485SNikita Kiryanov 	addr = simple_strtoul(value, NULL, 16);
751c0880485SNikita Kiryanov 	/* See README.displaying-bmps */
752c0880485SNikita Kiryanov 	aligned = (addr % 4 == 2);
753c0880485SNikita Kiryanov 	if (!aligned) {
754c0880485SNikita Kiryanov 		printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n");
755c0880485SNikita Kiryanov 		return -1;
756c0880485SNikita Kiryanov 	}
757c0880485SNikita Kiryanov 
758c0880485SNikita Kiryanov 	return 0;
759c0880485SNikita Kiryanov }
760c0880485SNikita Kiryanov 
761c0880485SNikita Kiryanov U_BOOT_ENV_CALLBACK(splashimage, on_splashimage);
762c0880485SNikita Kiryanov #endif
763c0880485SNikita Kiryanov 
lcd_get_pixel_width(void)764395166cfSVadim Bendebury int lcd_get_pixel_width(void)
765395166cfSVadim Bendebury {
766395166cfSVadim Bendebury 	return panel_info.vl_col;
767395166cfSVadim Bendebury }
768395166cfSVadim Bendebury 
lcd_get_pixel_height(void)769395166cfSVadim Bendebury int lcd_get_pixel_height(void)
770395166cfSVadim Bendebury {
771395166cfSVadim Bendebury 	return panel_info.vl_row;
772395166cfSVadim Bendebury }
773