|  | /* | 
|  | * (C) Copyright 2003-2004 | 
|  | * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com | 
|  | * | 
|  | * (C) Copyright 2005 | 
|  | * Stefan Roese, DENX Software Engineering, sr@denx.de. | 
|  | * | 
|  | * SPDX-License-Identifier:	GPL-2.0+ | 
|  | */ | 
|  |  | 
|  | #include "asm/io.h" | 
|  | #include "lcd.h" | 
|  |  | 
|  |  | 
|  | extern int video_display_bitmap (ulong, int, int); | 
|  |  | 
|  |  | 
|  | int palette_index; | 
|  | int palette_value; | 
|  | int lcd_depth; | 
|  | unsigned char *glob_lcd_reg; | 
|  | unsigned char *glob_lcd_mem; | 
|  |  | 
|  | #if defined(CONFIG_SYS_LCD_ENDIAN) | 
|  | void lcd_setup(int lcd, int config) | 
|  | { | 
|  | if (lcd == 0) { | 
|  | /* | 
|  | * Set endianess and reset lcd controller 0 (small) | 
|  | */ | 
|  |  | 
|  | /* set reset to low */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) & ~CONFIG_SYS_LCD0_RST); | 
|  | udelay(10); /* wait 10us */ | 
|  | if (config == 1) { | 
|  | /* big-endian */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) | CONFIG_SYS_LCD_ENDIAN); | 
|  | } else { | 
|  | /* little-endian */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) & ~CONFIG_SYS_LCD_ENDIAN); | 
|  | } | 
|  | udelay(10); /* wait 10us */ | 
|  | /* set reset to high */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) | CONFIG_SYS_LCD0_RST); | 
|  | } else { | 
|  | /* | 
|  | * Set endianess and reset lcd controller 1 (big) | 
|  | */ | 
|  |  | 
|  | /* set reset to low */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) & ~CONFIG_SYS_LCD1_RST); | 
|  | udelay(10); /* wait 10us */ | 
|  | if (config == 1) { | 
|  | /* big-endian */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) | CONFIG_SYS_LCD_ENDIAN); | 
|  | } else { | 
|  | /* little-endian */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) & ~CONFIG_SYS_LCD_ENDIAN); | 
|  | } | 
|  | udelay(10); /* wait 10us */ | 
|  | /* set reset to high */ | 
|  | out_be32((void*)GPIO0_OR, | 
|  | in_be32((void*)GPIO0_OR) | CONFIG_SYS_LCD1_RST); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * CONFIG_SYS_LCD_ENDIAN may also be FPGA_RESET, so set inactive | 
|  | */ | 
|  | out_be32((void*)GPIO0_OR, in_be32((void*)GPIO0_OR) | CONFIG_SYS_LCD_ENDIAN); | 
|  | } | 
|  | #endif /* CONFIG_SYS_LCD_ENDIAN */ | 
|  |  | 
|  |  | 
|  | int lcd_bmp(uchar *logo_bmp) | 
|  | { | 
|  | int i; | 
|  | uchar *ptr; | 
|  | ushort *ptr2; | 
|  | ushort val; | 
|  | unsigned char *dst = NULL; | 
|  | int x, y; | 
|  | int width, height, bpp, colors, line_size; | 
|  | int header_size; | 
|  | unsigned char *bmp; | 
|  | unsigned char r, g, b; | 
|  | BITMAPINFOHEADER *bm_info; | 
|  | ulong len; | 
|  |  | 
|  | /* | 
|  | * Check for bmp mark 'BM' | 
|  | */ | 
|  | if (*(ushort *)logo_bmp != 0x424d) { | 
|  | /* | 
|  | * Decompress bmp image | 
|  | */ | 
|  | len = CONFIG_SYS_VIDEO_LOGO_MAX_SIZE; | 
|  | dst = malloc(CONFIG_SYS_VIDEO_LOGO_MAX_SIZE); | 
|  | if (dst == NULL) { | 
|  | printf("Error: malloc for gunzip failed!\n"); | 
|  | return 1; | 
|  | } | 
|  | if (gunzip(dst, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, | 
|  | (uchar *)logo_bmp, &len) != 0) { | 
|  | free(dst); | 
|  | return 1; | 
|  | } | 
|  | if (len == CONFIG_SYS_VIDEO_LOGO_MAX_SIZE) { | 
|  | printf("Image could be truncated" | 
|  | " (increase CONFIG_SYS_VIDEO_LOGO_MAX_SIZE)!\n"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Check for bmp mark 'BM' | 
|  | */ | 
|  | if (*(ushort *)dst != 0x424d) { | 
|  | printf("LCD: Unknown image format!\n"); | 
|  | free(dst); | 
|  | return 1; | 
|  | } | 
|  | } else { | 
|  | /* | 
|  | * Uncompressed BMP image, just use this pointer | 
|  | */ | 
|  | dst = (uchar *)logo_bmp; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Get image info from bmp-header | 
|  | */ | 
|  | bm_info = (BITMAPINFOHEADER *)(dst + 14); | 
|  | bpp = LOAD_SHORT(bm_info->biBitCount); | 
|  | width = LOAD_LONG(bm_info->biWidth); | 
|  | height = LOAD_LONG(bm_info->biHeight); | 
|  | switch (bpp) { | 
|  | case 1: | 
|  | colors = 1; | 
|  | line_size = width >> 3; | 
|  | break; | 
|  | case 4: | 
|  | colors = 16; | 
|  | line_size = width >> 1; | 
|  | break; | 
|  | case 8: | 
|  | colors = 256; | 
|  | line_size = width; | 
|  | break; | 
|  | case 24: | 
|  | colors = 0; | 
|  | line_size = width * 3; | 
|  | break; | 
|  | default: | 
|  | printf("LCD: Unknown bpp (%d) im image!\n", bpp); | 
|  | if ((dst != NULL) && (dst != (uchar *)logo_bmp)) | 
|  | free(dst); | 
|  | return 1; | 
|  | } | 
|  | printf(" (%d*%d, %dbpp)\n", width, height, bpp); | 
|  |  | 
|  | /* | 
|  | * Write color palette | 
|  | */ | 
|  | if ((colors <= 256) && (lcd_depth <= 8)) { | 
|  | ptr = (unsigned char *)(dst + 14 + 40); | 
|  | for (i = 0; i < colors; i++) { | 
|  | b = *ptr++; | 
|  | g = *ptr++; | 
|  | r = *ptr++; | 
|  | ptr++; | 
|  | S1D_WRITE_PALETTE(glob_lcd_reg, i, r, g, b); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write bitmap data into framebuffer | 
|  | */ | 
|  | ptr = glob_lcd_mem; | 
|  | ptr2 = (ushort *)glob_lcd_mem; | 
|  | header_size = 14 + 40 + 4*colors;          /* skip bmp header */ | 
|  | for (y = 0; y < height; y++) { | 
|  | bmp = &dst[(height-1-y)*line_size + header_size]; | 
|  | if (lcd_depth == 16) { | 
|  | if (bpp == 24) { | 
|  | for (x = 0; x < width; x++) { | 
|  | /* | 
|  | * Generate epson 16bpp fb-format | 
|  | * from 24bpp image | 
|  | */ | 
|  | b = *bmp++ >> 3; | 
|  | g = *bmp++ >> 2; | 
|  | r = *bmp++ >> 3; | 
|  | val = ((r & 0x1f) << 11) | | 
|  | ((g & 0x3f) << 5) | | 
|  | (b & 0x1f); | 
|  | *ptr2++ = val; | 
|  | } | 
|  | } else if (bpp == 8) { | 
|  | for (x = 0; x < line_size; x++) { | 
|  | /* query rgb value from palette */ | 
|  | ptr = (unsigned char *)(dst + 14 + 40); | 
|  | ptr += (*bmp++) << 2; | 
|  | b = *ptr++ >> 3; | 
|  | g = *ptr++ >> 2; | 
|  | r = *ptr++ >> 3; | 
|  | val = ((r & 0x1f) << 11) | | 
|  | ((g & 0x3f) << 5) | | 
|  | (b & 0x1f); | 
|  | *ptr2++ = val; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | for (x = 0; x < line_size; x++) | 
|  | *ptr++ = *bmp++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((dst != NULL) && (dst != (uchar *)logo_bmp)) | 
|  | free(dst); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int lcd_init(uchar *lcd_reg, uchar *lcd_mem, S1D_REGS *regs, int reg_count, | 
|  | uchar *logo_bmp, ulong len) | 
|  | { | 
|  | int i; | 
|  | ushort s1dReg; | 
|  | uchar s1dValue; | 
|  | int reg_byte_swap; | 
|  |  | 
|  | /* | 
|  | * Detect epson | 
|  | */ | 
|  | out_8(&lcd_reg[0], 0x00); | 
|  | out_8(&lcd_reg[1], 0x00); | 
|  |  | 
|  | if (in_8(&lcd_reg[0]) == 0x1c) { | 
|  | /* | 
|  | * Big epson detected | 
|  | */ | 
|  | reg_byte_swap = false; | 
|  | palette_index = 0x1e2; | 
|  | palette_value = 0x1e4; | 
|  | lcd_depth = 16; | 
|  | puts("LCD:   S1D13806"); | 
|  | } else if (in_8(&lcd_reg[1]) == 0x1c) { | 
|  | /* | 
|  | * Big epson detected (with register swap bug) | 
|  | */ | 
|  | reg_byte_swap = true; | 
|  | palette_index = 0x1e3; | 
|  | palette_value = 0x1e5; | 
|  | lcd_depth = 16; | 
|  | puts("LCD:   S1D13806S"); | 
|  | } else if (in_8(&lcd_reg[0]) == 0x18) { | 
|  | /* | 
|  | * Small epson detected (704) | 
|  | */ | 
|  | reg_byte_swap = false; | 
|  | palette_index = 0x15; | 
|  | palette_value = 0x17; | 
|  | lcd_depth = 8; | 
|  | puts("LCD:   S1D13704"); | 
|  | } else if (in_8(&lcd_reg[0x10000]) == 0x24) { | 
|  | /* | 
|  | * Small epson detected (705) | 
|  | */ | 
|  | reg_byte_swap = false; | 
|  | palette_index = 0x15; | 
|  | palette_value = 0x17; | 
|  | lcd_depth = 8; | 
|  | lcd_reg += 0x10000; /* add offset for 705 regs */ | 
|  | puts("LCD:   S1D13705"); | 
|  | } else { | 
|  | out_8(&lcd_reg[0x1a], 0x00); | 
|  | udelay(1000); | 
|  | if (in_8(&lcd_reg[1]) == 0x0c) { | 
|  | /* | 
|  | * S1D13505 detected | 
|  | */ | 
|  | reg_byte_swap = true; | 
|  | palette_index = 0x25; | 
|  | palette_value = 0x27; | 
|  | lcd_depth = 16; | 
|  |  | 
|  | puts("LCD:   S1D13505"); | 
|  | } else { | 
|  | puts("LCD:   No controller detected!\n"); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Setup lcd controller regs | 
|  | */ | 
|  | for (i = 0; i < reg_count; i++) { | 
|  | s1dReg = regs[i].Index; | 
|  | if (reg_byte_swap) { | 
|  | if ((s1dReg & 0x0001) == 0) | 
|  | s1dReg |= 0x0001; | 
|  | else | 
|  | s1dReg &= ~0x0001; | 
|  | } | 
|  | s1dValue = regs[i].Value; | 
|  | out_8(&lcd_reg[s1dReg], s1dValue); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Save reg & mem pointer for later usage (e.g. bmp command) | 
|  | */ | 
|  | glob_lcd_reg = lcd_reg; | 
|  | glob_lcd_mem = lcd_mem; | 
|  |  | 
|  | /* | 
|  | * Display bmp image | 
|  | */ | 
|  | return lcd_bmp(logo_bmp); | 
|  | } | 
|  |  | 
|  | int do_esdbmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | 
|  | { | 
|  | ulong addr; | 
|  | #ifdef CONFIG_VIDEO_SM501 | 
|  | char *str; | 
|  | #endif | 
|  | if (argc != 2) | 
|  | return cmd_usage(cmdtp); | 
|  |  | 
|  | addr = simple_strtoul(argv[1], NULL, 16); | 
|  |  | 
|  | #ifdef CONFIG_VIDEO_SM501 | 
|  | str = getenv("bd_type"); | 
|  | if ((strcmp(str, "ppc221") == 0) || (strcmp(str, "ppc231") == 0)) { | 
|  | /* | 
|  | * SM501 available, use standard bmp command | 
|  | */ | 
|  | return video_display_bitmap(addr, 0, 0); | 
|  | } else { | 
|  | /* | 
|  | * No SM501 available, use esd epson bmp command | 
|  | */ | 
|  | return lcd_bmp((uchar *)addr); | 
|  | } | 
|  | #else | 
|  | return lcd_bmp((uchar *)addr); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | U_BOOT_CMD( | 
|  | esdbmp,	2,	1,	do_esdbmp, | 
|  | "display BMP image", | 
|  | "<imageAddr> - display image" | 
|  | ); |