Newer
Older
/*
* Common LCD routines for supported CPUs
*
* (C) Copyright 2001-2002
* Wolfgang Denk, DENX Software Engineering -- wd@denx.de
*
* SPDX-License-Identifier: GPL-2.0+
*/
/************************************************************************/
/* ** HEADER FILES */
/************************************************************************/
/* #define DEBUG */
#include <config.h>
#include <common.h>
#include <command.h>
#include <stdarg.h>
#include <search.h>
#include <env_callback.h>
#include <linux/types.h>
#include <stdio_dev.h>
#if defined(CONFIG_POST)
#include <post.h>
#endif
#include <lcd.h>
#include <splash.h>
#if defined(CONFIG_CPU_PXA25X) || defined(CONFIG_CPU_PXA27X) || \
defined(CONFIG_CPU_MONAHANS)
#define CONFIG_CPU_PXA
#include <asm/byteorder.h>
#endif
#if defined(CONFIG_MPC823)
#include <lcdvideo.h>
#endif
#if defined(CONFIG_ATMEL_LCD)
#include <atmel_lcdc.h>
#endif
#if defined(CONFIG_LCD_DT_SIMPLEFB)
#include <libfdt.h>
#endif
/************************************************************************/
/* ** FONT DATA */
/************************************************************************/
#include <video_font.h> /* Get font data, width and height */
/************************************************************************/
/* ** LOGO DATA */
/************************************************************************/
#ifdef CONFIG_LCD_LOGO
# include <bmp_logo.h> /* Get logo data, width and height */
# 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
#endif
#ifndef CONFIG_LCD_ALIGNMENT
#define CONFIG_LCD_ALIGNMENT PAGE_SIZE
#endif
/* By default we scroll by a single line */
#ifndef CONFIG_CONSOLE_SCROLL_LINES
#define CONFIG_CONSOLE_SCROLL_LINES 1
#endif
/************************************************************************/
/* ** CONSOLE DEFINITIONS & FUNCTIONS */
/************************************************************************/
#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
# define CONSOLE_ROWS ((panel_info.vl_row-BMP_LOGO_HEIGHT) \
/ VIDEO_FONT_HEIGHT)
#else
# define CONSOLE_ROWS (panel_info.vl_row / VIDEO_FONT_HEIGHT)
#endif
#define CONSOLE_COLS (panel_info.vl_col / VIDEO_FONT_WIDTH)
#define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * lcd_line_length)
#define CONSOLE_ROW_FIRST lcd_console_address
#define CONSOLE_ROW_SECOND (lcd_console_address + CONSOLE_ROW_SIZE)
#define CONSOLE_ROW_LAST (lcd_console_address + CONSOLE_SIZE \
- CONSOLE_ROW_SIZE)
#define CONSOLE_SIZE (CONSOLE_ROW_SIZE * CONSOLE_ROWS)
#define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE)
#if LCD_BPP == LCD_MONOCHROME
# define COLOR_MASK(c) ((c) | (c) << 1 | (c) << 2 | (c) << 3 | \
(c) << 4 | (c) << 5 | (c) << 6 | (c) << 7)
#elif (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16)
# define COLOR_MASK(c) (c)
#else
# error Unsupported LCD BPP.
#endif
DECLARE_GLOBAL_DATA_PTR;
static void lcd_drawchars(ushort x, ushort y, uchar *str, int count);
static inline void lcd_puts_xy(ushort x, ushort y, uchar *s);
static inline void lcd_putc_xy(ushort x, ushort y, uchar c);
static int lcd_init(void *lcdbase);
static int lcd_getbgcolor(void);
static void lcd_setfgcolor(int color);
static void lcd_setbgcolor(int color);
static int lcd_color_fg;
static int lcd_color_bg;
char lcd_is_enabled = 0;
static short console_col;
static short console_row;
static void *lcd_console_address;
static void *lcd_base; /* Start of framebuffer memory */
static char lcd_flush_dcache; /* 1 to flush dcache after each lcd update */
/************************************************************************/
/* 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);
}
/*----------------------------------------------------------------------*/
static void console_scrollup(void)
const int rows = CONFIG_CONSOLE_SCROLL_LINES;
/* Copy up rows ignoring those that will be overwritten */
memcpy(CONSOLE_ROW_FIRST,
lcd_console_address + CONSOLE_ROW_SIZE * rows,
CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows);
/* Clear the last rows */
memset(lcd_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows,
COLOR_MASK(lcd_color_bg),
console_row -= rows;
}
/*----------------------------------------------------------------------*/
static inline void console_back(void)
{
if (--console_col < 0) {
console_col = CONSOLE_COLS-1 ;
console_row = 0;
}
lcd_putc_xy(console_col * VIDEO_FONT_WIDTH,
console_row * VIDEO_FONT_HEIGHT, ' ');
}
/*----------------------------------------------------------------------*/
static inline void console_newline(void)
{
console_col = 0;
/* Check if we need to scroll the terminal */
if (++console_row >= CONSOLE_ROWS)
/*----------------------------------------------------------------------*/
{
if (!lcd_is_enabled) {
serial_putc(c);
return;
}
switch (c) {
case '\r':
console_col = 0;
return;
case '\n':
console_newline();
case '\t': /* Tab (8 chars alignment) */
console_col += 8;
console_col &= ~7;
if (console_col >= CONSOLE_COLS)
console_newline();
return;
case '\b':
console_back();
return;
default:
lcd_putc_xy(console_col * VIDEO_FONT_WIDTH,
console_row * VIDEO_FONT_HEIGHT, c);
if (++console_col >= CONSOLE_COLS)
console_newline();
}
}
/*----------------------------------------------------------------------*/
{
if (!lcd_is_enabled) {
return;
}
/*----------------------------------------------------------------------*/
void lcd_printf(const char *fmt, ...)
{
va_list args;
char buf[CONFIG_SYS_PBSIZE];
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
lcd_puts(buf);
}
/************************************************************************/
/* ** Low-Level Graphics Routines */
/************************************************************************/
static void lcd_drawchars(ushort x, ushort y, uchar *str, int count)
{
uchar *dest;
#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
y += BMP_LOGO_HEIGHT;
#endif
#if LCD_BPP == LCD_MONOCHROME
ushort off = x * (1 << LCD_BPP) % 8;
#endif
dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_BPP) / 8);
for (row = 0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) {
uchar *s = str;
int i;
#if LCD_BPP == LCD_COLOR16
ushort *d = (ushort *)dest;
#else
uchar *d = dest;
#endif
#if LCD_BPP == LCD_MONOCHROME
uchar rest = *d & -(1 << (8 - off));
uchar sym;
#endif
uchar c, bits;
c = *s++;
bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
#if LCD_BPP == LCD_MONOCHROME
sym = (COLOR_MASK(lcd_color_fg) & bits) |
(COLOR_MASK(lcd_color_bg) & ~bits);
*d++ = rest | (sym >> off);
rest = sym << (8-off);
#elif LCD_BPP == LCD_COLOR8
*d++ = (bits & 0x80) ?
lcd_color_fg : lcd_color_bg;
bits <<= 1;
}
#elif LCD_BPP == LCD_COLOR16
*d++ = (bits & 0x80) ?
lcd_color_fg : lcd_color_bg;
bits <<= 1;
}
#endif
}
#if LCD_BPP == LCD_MONOCHROME
*d = rest | (*d & ((1 << (8 - off)) - 1));
#endif
}
}
/*----------------------------------------------------------------------*/
static inline void lcd_puts_xy(ushort x, ushort y, uchar *s)
lcd_drawchars(x, y, s, strlen((char *)s));
}
/*----------------------------------------------------------------------*/
static inline void lcd_putc_xy(ushort x, ushort y, uchar c)
lcd_drawchars(x, y, &c, 1);
}
/************************************************************************/
/** Small utility to check that you got the colours right */
/************************************************************************/
#ifdef LCD_TEST_PATTERN
#define N_BLK_VERT 2
#define N_BLK_HOR 3
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,
};
{
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;
uchar *pix = (uchar *)lcd_base;
printf("[LCD] Test Pattern: %d x %d [%d x %d]\n",
h_max, v_max, h_step, v_step);
/* WARNING: Code silently assumes 8bit/pixel */
uchar iy = v / v_step;
uchar ix = N_BLK_HOR * iy + h / h_step;
*pix++ = test_colors[ix];
}
}
}
#endif /* LCD_TEST_PATTERN */
/************************************************************************/
/* ** GENERIC Initialization Routines */
/************************************************************************/
/*
* 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)
{
*line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
return *line_length * panel_info.vl_row;
}
struct stdio_dev lcddev;
lcd_init(lcd_base); /* LCD initialization */
/* Device initialization */
memset(&lcddev, 0, sizeof(lcddev));
lcddev.ext = 0; /* No extensions */
lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */
lcddev.putc = lcd_putc; /* 'putc' function */
lcddev.puts = lcd_puts; /* 'puts' function */
return (rc == 0) ? 1 : rc;
}
/*----------------------------------------------------------------------*/
{
#if LCD_BPP == LCD_MONOCHROME
/* Setting the palette */
lcd_initcolregs();
#elif LCD_BPP == LCD_COLOR8
/* Setting the palette */
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);
#ifndef CONFIG_SYS_WHITE_ON_BLACK
lcd_setfgcolor(CONSOLE_COLOR_BLACK);
lcd_setbgcolor(CONSOLE_COLOR_WHITE);
lcd_setfgcolor(CONSOLE_COLOR_WHITE);
lcd_setbgcolor(CONSOLE_COLOR_BLACK);
#endif /* CONFIG_SYS_WHITE_ON_BLACK */
#ifdef LCD_TEST_PATTERN
test_pattern();
#else
/* set framebuffer to background color */
COLOR_MASK(lcd_getbgcolor()),
lcd_line_length * panel_info.vl_row);
#endif
/* Paint the logo and retrieve LCD base address */
debug("[LCD] Drawing the logo...\n");
console_col = 0;
console_row = 0;
lcd_sync();
}
static int do_lcd_clear(cmd_tbl_t *cmdtp, int flag, int argc,
char *const argv[])
{
lcd_clear();
return 0;
}
U_BOOT_CMD(
);
/*----------------------------------------------------------------------*/
static int lcd_init(void *lcdbase)
{
/* Initialize the lcd controller */
debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);
/*
* lcd_ctrl_init() of some drivers (i.e. bcm2835 on rpi_b) ignores
* the 'lcdbase' argument and uses custom lcd base address
* by setting up gd->fb_base. Check for this condition and fixup
* 'lcd_base' address.
*/
if ((unsigned long)lcdbase != gd->fb_base)
lcd_base = (void *)gd->fb_base;
debug("[LCD] Using LCD frambuffer at %p\n", lcd_base);
lcd_get_size(&lcd_line_length);
lcd_is_enabled = 1;
/* Initialize the console */
console_col = 0;
#ifdef CONFIG_LCD_INFO_BELOW_LOGO
console_row = 7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT;
#else
console_row = 1; /* leave 1 blank line below logo */
#endif
return 0;
}
/************************************************************************/
/* ** ROM capable initialization part - needed to reserve FB memory */
/************************************************************************/
/*
* 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.
*/
{
ulong size;
int line_length;
debug("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col,
panel_info.vl_row, NBITS(panel_info.vl_bpix));
size = lcd_get_size(&line_length);
/* 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);
/* Allocate pages for the frame buffer. */
addr -= size;
debug("Reserving %ldk for LCD Framebuffer at: %08lx\n",
size >> 10, addr);
}
/*----------------------------------------------------------------------*/
static void lcd_setfgcolor(int color)
}
/*----------------------------------------------------------------------*/
static void lcd_setbgcolor(int color)
}
/*----------------------------------------------------------------------*/
{
return lcd_color_fg;
}
/*----------------------------------------------------------------------*/
static int lcd_getbgcolor(void)
{
return lcd_color_bg;
}
/************************************************************************/
/* ** Chipset depending Bitmap / Logo stuff... */
/************************************************************************/
static inline ushort *configuration_get_cmap(void)
{
#if defined CONFIG_CPU_PXA
struct pxafb_info *fbi = &panel_info.pxa;
return (ushort *)fbi->palette;
#elif defined(CONFIG_MPC823)
immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
cpm8xx_t *cp = &(immr->im_cpm);
return (ushort *)&(cp->lcd_cmap[255 * sizeof(ushort)]);
#elif defined(CONFIG_ATMEL_LCD)
return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0));
#elif !defined(CONFIG_ATMEL_HLCD) && !defined(CONFIG_EXYNOS_FB)
return panel_info.cmap;
return bmp_logo_palette;
#else
return NULL;
#endif
#ifdef CONFIG_LCD_LOGO
uint *cmap = (uint *)bmp_logo_palette;
ushort *cmap = (ushort *)bmp_logo_palette;
ushort i, j;
uchar *bmap;
uchar *fb;
ushort *fb16;
#if defined(CONFIG_MPC823)
immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
cpm8xx_t *cp = &(immr->im_cpm);
unsigned bpix = NBITS(panel_info.vl_bpix);
debug("Logo: width %d height %d colors %d cmap %d\n",
BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS,
bmap = &bmp_logo_bitmap[0];
fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8);
/* Leave room for default color map
* default case: generic system with no cmap (most likely 16bpp)
* cmap was set to the source palette, so no change is done.
* This avoids even more ifdefs in the next stanza
*/
#if defined(CONFIG_MPC823)
cmap = (ushort *) &(cp->lcd_cmap[BMP_LOGO_OFFSET * sizeof(ushort)]);
cmap = (uint *)configuration_get_cmap();
#endif
WATCHDOG_RESET();
/* Set color map */
for (i = 0; i < ARRAY_SIZE(bmp_logo_palette); ++i) {
ushort colreg = bmp_logo_palette[i];
#ifdef CONFIG_ATMEL_LCD
uint lut_entry;
#ifdef CONFIG_ATMEL_LCD_BGR555
lut_entry = ((colreg & 0x000F) << 11) |
((colreg & 0x00F0) << 2) |
((colreg & 0x0F00) >> 7);
#else /* CONFIG_ATMEL_LCD_RGB565 */
lut_entry = ((colreg & 0x000F) << 1) |
((colreg & 0x00F0) << 3) |
((colreg & 0x0F00) << 4);
#endif
*(cmap + BMP_LOGO_OFFSET) = lut_entry;
cmap++;
#else /* !CONFIG_ATMEL_LCD */
#ifdef CONFIG_SYS_INVERT_COLORS
*cmap++ = 0xffff - colreg;
#else
*cmap++ = colreg;
#endif
}
WATCHDOG_RESET();
for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
memcpy(fb, bmap, BMP_LOGO_WIDTH);
bmap += BMP_LOGO_WIDTH;
}
}
else { /* true color mode */
for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
for (j = 0; j < BMP_LOGO_WIDTH; j++) {
col16 = bmp_logo_palette[(bmap[j]-16)];
fb16[j] =
((col16 & 0x000F) << 1) |
((col16 & 0x00F0) << 3) |
((col16 & 0x0F00) << 4);
}
bmap += BMP_LOGO_WIDTH;
fb16 += panel_info.vl_col;
}
}
WATCHDOG_RESET();
#else
static inline void bitmap_plot(int x, int y) {}
#endif /* CONFIG_LCD_LOGO */
/*----------------------------------------------------------------------*/
#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN)
/*
* Display the BMP file located at address bmp_image.
* Only uncompressed.
*/
#ifdef CONFIG_SPLASH_SCREEN_ALIGN
#define BMP_ALIGN_CENTER 0x7FFF
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;
*axis = max(0, axis_alignment);
}
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
#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--;
}
* Do not call this function directly, must be called from lcd_display_bitmap.
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
*/
static void lcd_display_rle8_bitmap(bmp_image_t *bmp, ushort *cmap, uchar *fb,
int x_off, int y_off)
{
uchar *bmap;
ulong width, height;
ulong cnt, runlen;
int x, y;
int decode = 1;
width = le32_to_cpu(bmp->header.width);
height = le32_to_cpu(bmp->header.height);
bmap = (uchar *)bmp + le32_to_cpu(bmp->header.data_offset);
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
#if defined(CONFIG_MPC823) || defined(CONFIG_MCC200)
#define FB_PUT_BYTE(fb, from) *(fb)++ = (255 - *(from)++)
#else
#define FB_PUT_BYTE(fb, from) *(fb)++ = *(from)++
#endif
#if defined(CONFIG_BMP_16BPP)
#if defined(CONFIG_ATMEL_LCD_BGR555)
static inline void fb_put_word(uchar **fb, uchar **from)
{
*(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03);
*(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2);
*from += 2;
}
#else
static inline void fb_put_word(uchar **fb, uchar **from)
{
*(*fb)++ = *(*from)++;
*(*fb)++ = *(*from)++;
}
#endif
#endif /* CONFIG_BMP_16BPP */
int lcd_display_bitmap(ulong bmp_image, int x, int y)
{
#if !defined(CONFIG_MCC200)
ushort *cmap = NULL;
#endif
ushort *cmap_base = NULL;
ushort i, j;
uchar *fb;
bmp_image_t *bmp=(bmp_image_t *)bmp_image;
uchar *bmap;
ushort padded_width;
unsigned long width, height, byte_width;
unsigned long pwidth = panel_info.vl_col;
unsigned colors, bpix, bmp_bpix;
if (!bmp || !(bmp->header.signature[0] == 'B' &&
bmp->header.signature[1] == 'M')) {
printf("Error: no valid bmp image at %lx\n", bmp_image);
width = le32_to_cpu(bmp->header.width);
height = le32_to_cpu(bmp->header.height);
bmp_bpix = le16_to_cpu(bmp->header.bit_count);
colors = 1 << bmp_bpix;
bpix = NBITS(panel_info.vl_bpix);
if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
bpix, bmp_bpix);
return 1;
}
/* We support displaying 8bpp BMPs on 16bpp LCDs */
if (bpix != bmp_bpix && !(bmp_bpix == 8 && bpix == 16)) {
printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
bpix,
le16_to_cpu(bmp->header.bit_count));
return 1;
}
debug("Display-bmp: %d x %d with %d colors\n",
(int)width, (int)height, (int)colors);
#if !defined(CONFIG_MCC200)
/* MCC200 LCD doesn't need CMAP, supports 1bpp b&w only */
/* Set color map */
bmp_color_table_entry_t cte = bmp->color_table[i];
#if !defined(CONFIG_ATMEL_LCD)
ushort colreg =
( ((cte.red) << 8) & 0xf800) |
( ((cte.green) << 3) & 0x07e0) |
( ((cte.blue) >> 3) & 0x001f) ;
#ifdef CONFIG_SYS_INVERT_COLORS
#if defined(CONFIG_MPC823)
#endif
#else /* CONFIG_ATMEL_LCD */
lcd_setcolreg(i, cte.red, cte.green, cte.blue);
#endif
}
}
/*
* BMP format for Monochrome assumes that the state of a
* pixel is described on a per Bit basis, not per Byte.
* So, in case of Monochrome BMP we should align widths
* on a byte boundary and convert them from Bit to Byte
* units.
* Probably, PXA250 and MPC823 process 1bpp BMP images in
* their own ways, so make the converting to be MCC200
* specific.
*/
#if defined(CONFIG_MCC200)
width = ((width + 7) & ~7) >> 3;
x = ((x + 7) & ~7) >> 3;
pwidth= ((pwidth + 7) & ~7) >> 3;
}
#endif
padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
#ifdef CONFIG_SPLASH_SCREEN_ALIGN
splash_align_axis(&x, pwidth, width);
splash_align_axis(&y, panel_info.vl_row, height);
#endif /* CONFIG_SPLASH_SCREEN_ALIGN */
if ((y + height) > panel_info.vl_row)
height = panel_info.vl_row - y;
bmap = (uchar *) bmp + le32_to_cpu(bmp->header.data_offset);
fb = (uchar *) (lcd_base +
(y + height - 1) * lcd_line_length + x * bpix / 8);
#ifdef CONFIG_LCD_BMP_RLE8
if (le32_to_cpu(bmp->header.compression) == BMP_BI_RLE8) {
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
if (bpix != 16)
byte_width = width;
else
byte_width = width * 2;
for (i = 0; i < height; ++i) {
WATCHDOG_RESET();
for (j = 0; j < width; j++) {
if (bpix != 16) {
} else {
*(uint16_t *)fb = cmap_base[*(bmap++)];
fb += sizeof(uint16_t) / sizeof(*fb);
}
}
bmap += (padded_width - width);
fb -= byte_width + lcd_line_length;
}
break;
#if defined(CONFIG_BMP_16BPP)
case 16:
for (i = 0; i < height; ++i) {
WATCHDOG_RESET();
for (j = 0; j < width; j++)
fb_put_word(&fb, &bmap);
bmap += (padded_width - width) * 2;
fb -= width * 2 + lcd_line_length;
}
break;
#endif /* CONFIG_BMP_16BPP */
#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++);
}
fb -= lcd_line_length + width * (bpix / 8);
}
break;
#endif /* CONFIG_BMP_32BPP */
lcd_sync();
{
#ifdef CONFIG_SPLASH_SCREEN
char *s;
ulong addr;
static int do_splash = 1;
if (do_splash && (s = getenv("splashimage")) != NULL) {
do_splash = 0;
if (splash_screen_prepare())
return (void *)lcd_base;
addr = simple_strtoul (s, NULL, 16);
splash_get_pos(&x, &y);
}
#endif /* CONFIG_SPLASH_SCREEN */
#ifdef CONFIG_LCD_INFO
console_col = LCD_INFO_X / VIDEO_FONT_WIDTH;
console_row = LCD_INFO_Y / VIDEO_FONT_HEIGHT;
lcd_show_board_info();
#endif /* CONFIG_LCD_INFO */
#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
return (void *)((ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length);
#endif /* CONFIG_LCD_LOGO && !defined(CONFIG_LCD_INFO_BELOW_LOGO) */
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
#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
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
void lcd_position_cursor(unsigned col, unsigned row)
{
console_col = min(col, CONSOLE_COLS - 1);
console_row = min(row, CONSOLE_ROWS - 1);
}
int lcd_get_pixel_width(void)
{
return panel_info.vl_col;
}
int lcd_get_pixel_height(void)
{
return panel_info.vl_row;
}
int lcd_get_screen_rows(void)
{
return CONSOLE_ROWS;
}
int lcd_get_screen_columns(void)
{
return CONSOLE_COLS;
}
#if defined(CONFIG_LCD_DT_SIMPLEFB)
static int lcd_dt_simplefb_configure_node(void *blob, int off)
{
u32 stride;
fdt32_t cells[2];
int ret;
static const char format[] =
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
#if LCD_BPP == LCD_COLOR16
"r5g6b5";
#else
"";
#endif
if (!format[0])
return -1;
stride = panel_info.vl_col * 2;
cells[0] = cpu_to_fdt32(gd->fb_base);
cells[1] = cpu_to_fdt32(stride * panel_info.vl_row);
ret = fdt_setprop(blob, off, "reg", cells, sizeof(cells[0]) * 2);
if (ret < 0)
return -1;
cells[0] = cpu_to_fdt32(panel_info.vl_col);
ret = fdt_setprop(blob, off, "width", cells, sizeof(cells[0]));
if (ret < 0)
return -1;
cells[0] = cpu_to_fdt32(panel_info.vl_row);
ret = fdt_setprop(blob, off, "height", cells, sizeof(cells[0]));
if (ret < 0)
return -1;
cells[0] = cpu_to_fdt32(stride);
ret = fdt_setprop(blob, off, "stride", cells, sizeof(cells[0]));
if (ret < 0)
return -1;
ret = fdt_setprop(blob, off, "format", format, strlen(format) + 1);
if (ret < 0)
return -1;
ret = fdt_delprop(blob, off, "status");
if (ret < 0)
return -1;
return 0;
}
int lcd_dt_simplefb_add_node(void *blob)
{
static const char compat[] = "simple-framebuffer";
static const char disabled[] = "disabled";
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
int off, ret;
off = fdt_add_subnode(blob, 0, "framebuffer");
if (off < 0)
return -1;
ret = fdt_setprop(blob, off, "status", disabled, sizeof(disabled));
if (ret < 0)
return -1;
ret = fdt_setprop(blob, off, "compatible", compat, sizeof(compat));
if (ret < 0)
return -1;
return lcd_dt_simplefb_configure_node(blob, off);
}
int lcd_dt_simplefb_enable_existing_node(void *blob)
{
int off;
off = fdt_node_offset_by_compatible(blob, -1, "simple-framebuffer");
if (off < 0)
return -1;
return lcd_dt_simplefb_configure_node(blob, off);
}
#endif