lcd.c 17.7 KB
Newer Older
1
/*
Nikita Kiryanov's avatar
Nikita Kiryanov committed
2
 * Common LCD routines
3 4 5 6
 *
 * (C) Copyright 2001-2002
 * Wolfgang Denk, DENX Software Engineering -- wd@denx.de
 *
7
 * SPDX-License-Identifier:	GPL-2.0+
8 9 10 11 12 13
 */

/* #define DEBUG */
#include <config.h>
#include <common.h>
#include <command.h>
14
#include <env_callback.h>
15
#include <linux/types.h>
16
#include <stdio_dev.h>
17
#include <lcd.h>
18
#include <mapmem.h>
wdenk's avatar
wdenk committed
19
#include <watchdog.h>
20
#include <asm/unaligned.h>
21
#include <splash.h>
Simon Glass's avatar
Simon Glass committed
22 23
#include <asm/io.h>
#include <asm/unaligned.h>
Nikita Kiryanov's avatar
Nikita Kiryanov committed
24
#include <video_font.h>
25

wdenk's avatar
wdenk committed
26
#ifdef CONFIG_LCD_LOGO
Nikita Kiryanov's avatar
Nikita Kiryanov committed
27 28 29 30 31
#include <bmp_logo.h>
#include <bmp_logo_data.h>
#if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16)
#error Default Color Map overlaps with Logo Color Map
#endif
wdenk's avatar
wdenk committed
32
#endif
33

34 35 36 37
#ifndef CONFIG_LCD_ALIGNMENT
#define CONFIG_LCD_ALIGNMENT PAGE_SIZE
#endif

Nikita Kiryanov's avatar
Nikita Kiryanov committed
38 39
#if (LCD_BPP != LCD_COLOR8) && (LCD_BPP != LCD_COLOR16) && \
	(LCD_BPP != LCD_COLOR32)
Nikita Kiryanov's avatar
Nikita Kiryanov committed
40
#error Unsupported LCD BPP.
41 42
#endif

43
DECLARE_GLOBAL_DATA_PTR;
44

45
static int lcd_init(void *lcdbase);
46
static void lcd_logo(void);
47 48
static void lcd_setfgcolor(int color);
static void lcd_setbgcolor(int color);
49

50 51
static int lcd_color_fg;
static int lcd_color_bg;
52
int lcd_line_length;
53
char lcd_is_enabled = 0;
54
static void *lcd_base;			/* Start of framebuffer memory	*/
55
static char lcd_flush_dcache;	/* 1 to flush dcache after each lcd update */
56

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
/* Flush LCD activity to the caches */
void lcd_sync(void)
{
	/*
	 * flush_dcache_range() is declared in common.h but it seems that some
	 * architectures do not actually implement it. Is there a way to find
	 * out whether it exists? For now, ARM is safe.
	 */
#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF)
	int line_length;

	if (lcd_flush_dcache)
		flush_dcache_range((u32)lcd_base,
			(u32)(lcd_base + lcd_get_size(&line_length)));
#endif
}

void lcd_set_flush_dcache(int flush)
{
	lcd_flush_dcache = (flush != 0);
}

79 80 81 82 83 84 85 86 87 88
static void lcd_stub_putc(struct stdio_dev *dev, const char c)
{
	lcd_putc(c);
}

static void lcd_stub_puts(struct stdio_dev *dev, const char *s)
{
	lcd_puts(s);
}

Nikita Kiryanov's avatar
Nikita Kiryanov committed
89
/* Small utility to check that you got the colours right */
90 91
#ifdef LCD_TEST_PATTERN

92
#if LCD_BPP == LCD_COLOR8
93 94 95
#define	N_BLK_VERT	2
#define	N_BLK_HOR	3

96
static int test_colors[N_BLK_HOR * N_BLK_VERT] = {
97 98
	CONSOLE_COLOR_RED,	CONSOLE_COLOR_GREEN,	CONSOLE_COLOR_YELLOW,
	CONSOLE_COLOR_BLUE,	CONSOLE_COLOR_MAGENTA,	CONSOLE_COLOR_CYAN,
99 100 101 102 103 104 105 106 107
}; /*LCD_BPP == LCD_COLOR8 */

#elif LCD_BPP == LCD_COLOR16
#define	N_BLK_VERT	2
#define	N_BLK_HOR	4

static int test_colors[N_BLK_HOR * N_BLK_VERT] = {
	CONSOLE_COLOR_RED,	CONSOLE_COLOR_GREEN,	CONSOLE_COLOR_YELLOW,	CONSOLE_COLOR_BLUE,
	CONSOLE_COLOR_MAGENTA,	CONSOLE_COLOR_CYAN,	CONSOLE_COLOR_GREY,	CONSOLE_COLOR_WHITE,
108
};
109
#endif /*LCD_BPP == LCD_COLOR16 */
110

111
static void test_pattern(void)
112 113 114 115 116 117
{
	ushort v_max  = panel_info.vl_row;
	ushort h_max  = panel_info.vl_col;
	ushort v_step = (v_max + N_BLK_VERT - 1) / N_BLK_VERT;
	ushort h_step = (h_max + N_BLK_HOR  - 1) / N_BLK_HOR;
	ushort v, h;
118
#if LCD_BPP == LCD_COLOR8
119
	uchar *pix = (uchar *)lcd_base;
120 121 122
#elif LCD_BPP == LCD_COLOR16
	ushort *pix = (ushort *)lcd_base;
#endif
123

124
	printf("[LCD] Test Pattern: %d x %d [%d x %d]\n",
125 126
		h_max, v_max, h_step, v_step);

127
	for (v = 0; v < v_max; ++v) {
128
		uchar iy = v / v_step;
129
		for (h = 0; h < h_max; ++h) {
130
			uchar ix = N_BLK_HOR * iy + h / h_step;
131 132 133 134 135 136
			*pix++ = test_colors[ix];
		}
	}
}
#endif /* LCD_TEST_PATTERN */

137 138 139 140 141 142 143
/*
 * With most lcd drivers the line length is set up
 * by calculating it from panel_info parameters. Some
 * drivers need to calculate the line length differently,
 * so make the function weak to allow overriding it.
 */
__weak int lcd_get_size(int *line_length)
144 145 146 147 148
{
	*line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
	return *line_length * panel_info.vl_row;
}

149
int drv_lcd_init(void)
150
{
151
	struct stdio_dev lcddev;
152 153
	int rc;

Simon Glass's avatar
Simon Glass committed
154
	lcd_base = map_sysmem(gd->fb_base, 0);
155

Nikita Kiryanov's avatar
Nikita Kiryanov committed
156
	lcd_init(lcd_base);
157 158

	/* Device initialization */
159
	memset(&lcddev, 0, sizeof(lcddev));
160

161
	strcpy(lcddev.name, "lcd");
162 163
	lcddev.ext   = 0;			/* No extensions */
	lcddev.flags = DEV_FLAGS_OUTPUT;	/* Output only */
164 165
	lcddev.putc  = lcd_stub_putc;		/* 'putc' function */
	lcddev.puts  = lcd_stub_puts;		/* 'puts' function */
166

167
	rc = stdio_register(&lcddev);
168 169 170 171

	return (rc == 0) ? 1 : rc;
}

172
void lcd_clear(void)
173
{
174
	int bg_color;
175 176 177
	char *s;
	ulong addr;
	static int do_splash = 1;
Nikita Kiryanov's avatar
Nikita Kiryanov committed
178
#if LCD_BPP == LCD_COLOR8
179
	/* Setting the palette */
180 181 182 183 184 185 186 187 188
	lcd_setcolreg(CONSOLE_COLOR_BLACK, 0, 0, 0);
	lcd_setcolreg(CONSOLE_COLOR_RED, 0xFF, 0, 0);
	lcd_setcolreg(CONSOLE_COLOR_GREEN, 0, 0xFF, 0);
	lcd_setcolreg(CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0);
	lcd_setcolreg(CONSOLE_COLOR_BLUE, 0, 0, 0xFF);
	lcd_setcolreg(CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF);
	lcd_setcolreg(CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF);
	lcd_setcolreg(CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA);
	lcd_setcolreg(CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF);
189 190
#endif

191
#ifndef CONFIG_SYS_WHITE_ON_BLACK
192 193
	lcd_setfgcolor(CONSOLE_COLOR_BLACK);
	lcd_setbgcolor(CONSOLE_COLOR_WHITE);
194
	bg_color = CONSOLE_COLOR_WHITE;
195
#else
196 197
	lcd_setfgcolor(CONSOLE_COLOR_WHITE);
	lcd_setbgcolor(CONSOLE_COLOR_BLACK);
198
	bg_color = CONSOLE_COLOR_BLACK;
199
#endif	/* CONFIG_SYS_WHITE_ON_BLACK */
200 201 202 203 204

#ifdef	LCD_TEST_PATTERN
	test_pattern();
#else
	/* set framebuffer to background color */
205
#if (LCD_BPP != LCD_COLOR32)
206
	memset((char *)lcd_base, bg_color, lcd_line_length * panel_info.vl_row);
207 208 209 210 211 212
#else
	u32 *ppix = lcd_base;
	u32 i;
	for (i = 0;
	   i < (lcd_line_length * panel_info.vl_row)/NBYTES(panel_info.vl_bpix);
	   i++) {
213
		*ppix++ = bg_color;
214 215
	}
#endif
216
#endif
217 218 219 220 221 222
	/* setup text-console */
	debug("[LCD] setting up console...\n");
	lcd_init_console(lcd_base,
			 panel_info.vl_col,
			 panel_info.vl_row,
			 panel_info.vl_rot);
223
	/* Paint the logo and retrieve LCD base address */
224
	debug("[LCD] Drawing the logo...\n");
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	if (do_splash) {
		s = getenv("splashimage");
		if (s) {
			do_splash = 0;
			addr = simple_strtoul(s, NULL, 16);
			if (lcd_splash(addr) == 0) {
				lcd_sync();
				return;
			}
		}
	}

	lcd_logo();
#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
	addr = (ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length;
240 241
	lcd_init_console((void *)addr, panel_info.vl_col,
			 panel_info.vl_row, panel_info.vl_rot);
242
#endif
243 244 245 246 247 248 249 250
	lcd_sync();
}

static int do_lcd_clear(cmd_tbl_t *cmdtp, int flag, int argc,
			char *const argv[])
{
	lcd_clear();
	return 0;
251
}
Nikita Kiryanov's avatar
Nikita Kiryanov committed
252
U_BOOT_CMD(cls,	1, 1, do_lcd_clear, "clear screen", "");
253

254
static int lcd_init(void *lcdbase)
255
{
256 257
	debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);
	lcd_ctrl_init(lcdbase);
258 259

	/*
Stephen Warren's avatar
Stephen Warren committed
260
	 * lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi) ignores
261 262 263 264
	 * the 'lcdbase' argument and uses custom lcd base address
	 * by setting up gd->fb_base. Check for this condition and fixup
	 * 'lcd_base' address.
	 */
Simon Glass's avatar
Simon Glass committed
265 266
	if (map_to_sysmem(lcdbase) != gd->fb_base)
		lcd_base = map_sysmem(gd->fb_base, 0);
267 268 269

	debug("[LCD] Using LCD frambuffer at %p\n", lcd_base);

270
	lcd_get_size(&lcd_line_length);
271
	lcd_is_enabled = 1;
272
	lcd_clear();
273
	lcd_enable();
274 275

	/* Initialize the console */
Nikita Kiryanov's avatar
Nikita Kiryanov committed
276
	lcd_set_col(0);
wdenk's avatar
wdenk committed
277
#ifdef CONFIG_LCD_INFO_BELOW_LOGO
Nikita Kiryanov's avatar
Nikita Kiryanov committed
278
	lcd_set_row(7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT);
279
#else
Nikita Kiryanov's avatar
Nikita Kiryanov committed
280
	lcd_set_row(1);	/* leave 1 blank line below logo */
281 282 283 284 285 286 287 288 289 290 291 292
#endif

	return 0;
}

/*
 * This is called early in the system initialization to grab memory
 * for the LCD controller.
 * Returns new address for monitor, after reserving LCD buffer memory
 *
 * Note that this is running from ROM, so no write access to global data.
 */
293
ulong lcd_setmem(ulong addr)
294 295
{
	ulong size;
296
	int line_length;
297

298 299
	debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col,
		panel_info.vl_row, NBITS(panel_info.vl_bpix));
300

301
	size = lcd_get_size(&line_length);
302

303 304 305
	/* Round up to nearest full page, or MMU section if defined */
	size = ALIGN(size, CONFIG_LCD_ALIGNMENT);
	addr = ALIGN(addr - CONFIG_LCD_ALIGNMENT + 1, CONFIG_LCD_ALIGNMENT);
306 307 308 309

	/* Allocate pages for the frame buffer. */
	addr -= size;

310 311
	debug("Reserving %ldk for LCD Framebuffer at: %08lx\n",
	      size >> 10, addr);
312

313
	return addr;
314 315
}

316
static void lcd_setfgcolor(int color)
317
{
Stelian Pop's avatar
Stelian Pop committed
318
	lcd_color_fg = color;
319 320
}

321 322 323 324 325
int lcd_getfgcolor(void)
{
	return lcd_color_fg;
}

326
static void lcd_setbgcolor(int color)
327
{
Stelian Pop's avatar
Stelian Pop committed
328
	lcd_color_bg = color;
329 330
}

331 332 333 334 335
int lcd_getbgcolor(void)
{
	return lcd_color_bg;
}

336
#ifdef CONFIG_LCD_LOGO
337 338
__weak void lcd_logo_set_cmap(void)
{
339 340 341 342 343
	int i;
	ushort *cmap = configuration_get_cmap();

	for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i)
		*cmap++ = bmp_logo_palette[i];
344 345
}

346
void lcd_logo_plot(int x, int y)
347 348
{
	ushort i, j;
Nikita Kiryanov's avatar
Nikita Kiryanov committed
349
	uchar *bmap = &bmp_logo_bitmap[0];
350
	unsigned bpix = NBITS(panel_info.vl_bpix);
Nikita Kiryanov's avatar
Nikita Kiryanov committed
351 352
	uchar *fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8);
	ushort *fb16;
353

354 355
	debug("Logo: width %d  height %d  colors %d\n",
	      BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS);
356

357
	if (bpix < 12) {
358
		WATCHDOG_RESET();
359
		lcd_logo_set_cmap();
360 361
		WATCHDOG_RESET();

362 363
		for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
			memcpy(fb, bmap, BMP_LOGO_WIDTH);
364
			bmap += BMP_LOGO_WIDTH;
365
			fb += panel_info.vl_col;
366 367 368
		}
	}
	else { /* true color mode */
Alessandro Rubini's avatar
Alessandro Rubini committed
369
		u16 col16;
370
		fb16 = (ushort *)fb;
371 372
		for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
			for (j = 0; j < BMP_LOGO_WIDTH; j++) {
Alessandro Rubini's avatar
Alessandro Rubini committed
373 374 375 376 377
				col16 = bmp_logo_palette[(bmap[j]-16)];
				fb16[j] =
					((col16 & 0x000F) << 1) |
					((col16 & 0x00F0) << 3) |
					((col16 & 0x0F00) << 4);
378 379 380 381 382 383 384
				}
			bmap += BMP_LOGO_WIDTH;
			fb16 += panel_info.vl_col;
		}
	}

	WATCHDOG_RESET();
385
	lcd_sync();
386
}
387
#else
388
static inline void lcd_logo_plot(int x, int y) {}
389 390
#endif /* CONFIG_LCD_LOGO */

391
#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN)
392 393
#ifdef CONFIG_SPLASH_SCREEN_ALIGN
#define BMP_ALIGN_CENTER	0x7FFF
394 395 396 397 398 399 400 401 402 403 404 405 406 407

static void splash_align_axis(int *axis, unsigned long panel_size,
					unsigned long picture_size)
{
	unsigned long panel_picture_delta = panel_size - picture_size;
	unsigned long axis_alignment;

	if (*axis == BMP_ALIGN_CENTER)
		axis_alignment = panel_picture_delta / 2;
	else if (*axis < 0)
		axis_alignment = panel_picture_delta + *axis + 1;
	else
		return;

408
	*axis = max(0, (int)axis_alignment);
409
}
410 411
#endif

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
#ifdef CONFIG_LCD_BMP_RLE8
#define BMP_RLE8_ESCAPE		0
#define BMP_RLE8_EOL		0
#define BMP_RLE8_EOBMP		1
#define BMP_RLE8_DELTA		2

static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
				  int cnt)
{
	while (cnt > 0) {
		*(*fbp)++ = cmap[*bmap++];
		cnt--;
	}
}

static void draw_encoded_bitmap(ushort **fbp, ushort c, int cnt)
{
	ushort *fb = *fbp;
	int cnt_8copy = cnt >> 3;

	cnt -= cnt_8copy << 3;
	while (cnt_8copy > 0) {
		*fb++ = c;
		*fb++ = c;
		*fb++ = c;
		*fb++ = c;
		*fb++ = c;
		*fb++ = c;
		*fb++ = c;
		*fb++ = c;
		cnt_8copy--;
	}
	while (cnt > 0) {
		*fb++ = c;
		cnt--;
	}
448
	*fbp = fb;
449 450 451
}

/*
452
 * Do not call this function directly, must be called from lcd_display_bitmap.
453
 */
454 455
static void lcd_display_rle8_bitmap(struct bmp_image *bmp, ushort *cmap,
				    uchar *fb, int x_off, int y_off)
456 457 458 459 460 461 462
{
	uchar *bmap;
	ulong width, height;
	ulong cnt, runlen;
	int x, y;
	int decode = 1;

463 464 465
	width = get_unaligned_le32(&bmp->header.width);
	height = get_unaligned_le32(&bmp->header.height);
	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540

	x = 0;
	y = height - 1;

	while (decode) {
		if (bmap[0] == BMP_RLE8_ESCAPE) {
			switch (bmap[1]) {
			case BMP_RLE8_EOL:
				/* end of line */
				bmap += 2;
				x = 0;
				y--;
				/* 16bpix, 2-byte per pixel, width should *2 */
				fb -= (width * 2 + lcd_line_length);
				break;
			case BMP_RLE8_EOBMP:
				/* end of bitmap */
				decode = 0;
				break;
			case BMP_RLE8_DELTA:
				/* delta run */
				x += bmap[2];
				y -= bmap[3];
				/* 16bpix, 2-byte per pixel, x should *2 */
				fb = (uchar *) (lcd_base + (y + y_off - 1)
					* lcd_line_length + (x + x_off) * 2);
				bmap += 4;
				break;
			default:
				/* unencoded run */
				runlen = bmap[1];
				bmap += 2;
				if (y < height) {
					if (x < width) {
						if (x + runlen > width)
							cnt = width - x;
						else
							cnt = runlen;
						draw_unencoded_bitmap(
							(ushort **)&fb,
							bmap, cmap, cnt);
					}
					x += runlen;
				}
				bmap += runlen;
				if (runlen & 1)
					bmap++;
			}
		} else {
			/* encoded run */
			if (y < height) {
				runlen = bmap[0];
				if (x < width) {
					/* aggregate the same code */
					while (bmap[0] == 0xff &&
					       bmap[2] != BMP_RLE8_ESCAPE &&
					       bmap[1] == bmap[3]) {
						runlen += bmap[2];
						bmap += 2;
					}
					if (x + runlen > width)
						cnt = width - x;
					else
						cnt = runlen;
					draw_encoded_bitmap((ushort **)&fb,
						cmap[bmap[1]], cnt);
				}
				x += runlen;
			}
			bmap += 2;
		}
	}
}
#endif

541 542 543 544
__weak void fb_put_byte(uchar **fb, uchar **from)
{
	*(*fb)++ = *(*from)++;
}
545 546

#if defined(CONFIG_BMP_16BPP)
547
__weak void fb_put_word(uchar **fb, uchar **from)
548 549 550 551 552 553
{
	*(*fb)++ = *(*from)++;
	*(*fb)++ = *(*from)++;
}
#endif /* CONFIG_BMP_16BPP */

554
__weak void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
Nikita Kiryanov's avatar
Nikita Kiryanov committed
555 556
{
	int i;
557
	struct bmp_color_table_entry cte;
Nikita Kiryanov's avatar
Nikita Kiryanov committed
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
	ushort *cmap = configuration_get_cmap();

	for (i = 0; i < colors; ++i) {
		cte = bmp->color_table[i];
		*cmap = (((cte.red)   << 8) & 0xf800) |
			(((cte.green) << 3) & 0x07e0) |
			(((cte.blue)  >> 3) & 0x001f);
#if defined(CONFIG_MPC823)
		cmap--;
#else
		cmap++;
#endif
	}
}

573 574
int lcd_display_bitmap(ulong bmp_image, int x, int y)
{
575
	ushort *cmap_base = NULL;
576 577
	ushort i, j;
	uchar *fb;
578
	struct bmp_image *bmp = (struct bmp_image *)map_sysmem(bmp_image, 0);
579
	uchar *bmap;
580
	ushort padded_width;
581
	unsigned long width, height, byte_width;
582
	unsigned long pwidth = panel_info.vl_col;
583
	unsigned colors, bpix, bmp_bpix;
584 585
	int hdr_size;
	struct bmp_color_table_entry *palette = bmp->color_table;
586

587 588
	if (!bmp || !(bmp->header.signature[0] == 'B' &&
		bmp->header.signature[1] == 'M')) {
589 590
		printf("Error: no valid bmp image at %lx\n", bmp_image);

591
		return 1;
592
	}
593

594 595 596
	width = get_unaligned_le32(&bmp->header.width);
	height = get_unaligned_le32(&bmp->header.height);
	bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
597 598
	hdr_size = get_unaligned_le16(&bmp->header.size);
	debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
599

600
	colors = 1 << bmp_bpix;
601 602 603

	bpix = NBITS(panel_info.vl_bpix);

604
	if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
605 606
		printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
			bpix, bmp_bpix);
607

608 609 610
		return 1;
	}

611 612 613 614 615 616 617
	/*
	 * We support displaying 8bpp BMPs on 16bpp LCDs
	 * and displaying 24bpp BMPs on 32bpp LCDs
	 * */
	if (bpix != bmp_bpix &&
	    !(bmp_bpix == 8 && bpix == 16) &&
	    !(bmp_bpix == 24 && bpix == 32)) {
618
		printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
619
			bpix, get_unaligned_le16(&bmp->header.bit_count));
620 621 622
		return 1;
	}

623 624
	debug("Display-bmp: %d x %d  with %d colors, display %d\n",
	      (int)width, (int)height, (int)colors, 1 << bpix);
625

Nikita Kiryanov's avatar
Nikita Kiryanov committed
626 627
	if (bmp_bpix == 8)
		lcd_set_cmap(bmp, colors);
628

629
	padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
630 631

#ifdef CONFIG_SPLASH_SCREEN_ALIGN
632 633
	splash_align_axis(&x, pwidth, width);
	splash_align_axis(&y, panel_info.vl_row, height);
634 635
#endif /* CONFIG_SPLASH_SCREEN_ALIGN */

636
	if ((x + width) > pwidth)
637
		width = pwidth - x;
638
	if ((y + height) > panel_info.vl_row)
639 640
		height = panel_info.vl_row - y;

641 642
	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
	fb   = (uchar *)(lcd_base +
643
		(y + height - 1) * lcd_line_length + x * bpix / 8);
Mark Jackson's avatar
Mark Jackson committed
644

645
	switch (bmp_bpix) {
Nikita Kiryanov's avatar
Nikita Kiryanov committed
646
	case 1:
647
	case 8: {
Nikita Kiryanov's avatar
Nikita Kiryanov committed
648
		cmap_base = configuration_get_cmap();
649
#ifdef CONFIG_LCD_BMP_RLE8
650
		u32 compression = get_unaligned_le32(&bmp->header.compression);
651
		debug("compressed %d %d\n", compression, BMP_BI_RLE8);
652
		if (compression == BMP_BI_RLE8) {
653 654 655 656 657 658 659 660 661 662
			if (bpix != 16) {
				/* TODO implement render code for bpix != 16 */
				printf("Error: only support 16 bpix");
				return 1;
			}
			lcd_display_rle8_bitmap(bmp, cmap_base, fb, x, y);
			break;
		}
#endif

663 664 665 666 667
		if (bpix != 16)
			byte_width = width;
		else
			byte_width = width * 2;

Mark Jackson's avatar
Mark Jackson committed
668 669
		for (i = 0; i < height; ++i) {
			WATCHDOG_RESET();
670 671
			for (j = 0; j < width; j++) {
				if (bpix != 16) {
672
					fb_put_byte(&fb, &bmap);
673
				} else {
674 675 676 677 678 679 680 681 682 683 684 685 686
					struct bmp_color_table_entry *entry;
					uint val;

					if (cmap_base) {
						val = cmap_base[*bmap];
					} else {
						entry = &palette[*bmap];
						val = entry->blue >> 3 |
							entry->green >> 2 << 5 |
							entry->red >> 3 << 11;
					}
					*(uint16_t *)fb = val;
					bmap++;
687 688 689
					fb += sizeof(uint16_t) / sizeof(*fb);
				}
			}
690
			bmap += (padded_width - width);
691
			fb -= byte_width + lcd_line_length;
Mark Jackson's avatar
Mark Jackson committed
692 693
		}
		break;
694
	}
Mark Jackson's avatar
Mark Jackson committed
695 696 697 698
#if defined(CONFIG_BMP_16BPP)
	case 16:
		for (i = 0; i < height; ++i) {
			WATCHDOG_RESET();
699 700 701
			for (j = 0; j < width; j++)
				fb_put_word(&fb, &bmap);

702
			bmap += (padded_width - width) * 2;
703
			fb -= width * 2 + lcd_line_length;
Mark Jackson's avatar
Mark Jackson committed
704 705 706
		}
		break;
#endif /* CONFIG_BMP_16BPP */
707 708 709 710 711 712 713 714 715 716 717 718 719
#if defined(CONFIG_BMP_24BMP)
	case 24:
		for (i = 0; i < height; ++i) {
			for (j = 0; j < width; j++) {
				*(fb++) = *(bmap++);
				*(fb++) = *(bmap++);
				*(fb++) = *(bmap++);
				*(fb++) = 0;
			}
			fb -= lcd_line_length + width * (bpix / 8);
		}
		break;
#endif /* CONFIG_BMP_24BMP */
720 721 722 723 724 725 726 727 728
#if defined(CONFIG_BMP_32BPP)
	case 32:
		for (i = 0; i < height; ++i) {
			for (j = 0; j < width; j++) {
				*(fb++) = *(bmap++);
				*(fb++) = *(bmap++);
				*(fb++) = *(bmap++);
				*(fb++) = *(bmap++);
			}
729
			fb -= lcd_line_length + width * (bpix / 8);
730 731 732
		}
		break;
#endif /* CONFIG_BMP_32BPP */
Mark Jackson's avatar
Mark Jackson committed
733 734 735
	default:
		break;
	};
736

737
	lcd_sync();
738
	return 0;
739
}
740
#endif
741

742
static void lcd_logo(void)
743
{
744
	lcd_logo_plot(0, 0);
745

746
#ifdef CONFIG_LCD_INFO
Nikita Kiryanov's avatar
Nikita Kiryanov committed
747 748
	lcd_set_col(LCD_INFO_X / VIDEO_FONT_WIDTH);
	lcd_set_row(LCD_INFO_Y / VIDEO_FONT_HEIGHT);
749 750
	lcd_show_board_info();
#endif /* CONFIG_LCD_INFO */
751 752
}

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
#ifdef CONFIG_SPLASHIMAGE_GUARD
static int on_splashimage(const char *name, const char *value, enum env_op op,
	int flags)
{
	ulong addr;
	int aligned;

	if (op == env_op_delete)
		return 0;

	addr = simple_strtoul(value, NULL, 16);
	/* See README.displaying-bmps */
	aligned = (addr % 4 == 2);
	if (!aligned) {
		printf("Invalid splashimage value. Value must be 16 bit aligned, but not 32 bit aligned\n");
		return -1;
	}

	return 0;
}

U_BOOT_ENV_CALLBACK(splashimage, on_splashimage);
#endif

777 778 779 780 781 782 783 784 785
int lcd_get_pixel_width(void)
{
	return panel_info.vl_col;
}

int lcd_get_pixel_height(void)
{
	return panel_info.vl_row;
}