/* arch/arm/mach-msm/htc_fb_console.c * * By Octavian Voicu * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* * Ugly hack to get some basic console working for HTC Diamond but should * also work for Raphael and other similar phones. Will register itself * as an early console to show boot messages. This won't work unless * the LCD is already powered up and functional (from wince, boot * using Haret). We just DMA pixels to the LCD, all configuration * must already be done. * * Not exactly very clean nor optimized, but it's a one night hack * and it works :) * * If anyone makes any progress on HTC Diamond drop me a line * (see email at the top), I do wanna see Android on my HTC Diamond! * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../drivers/video/msm/mdp_hw.h" /* Defined in /arch/arm/mm/mmu.c, helps us map physical memory to virtual memory. * It should work pretty early in the boot process, that why we use it */ extern void create_mapping(struct map_desc *md); /* Defined in include/linux/fb.h. When, on write, a registered framebuffer device is detected, * we immediately unregister ourselves. */ #ifndef CONFIG_HTC_FB_CONSOLE_BOOT extern int num_registered_fb; #endif /* Green message (color = 2), the reset color to white (color = 6) */ #define HTC_FB_MSG ("\n\n" "\x1b" "2" "HTC Linux framebuffer console by druidu" "\x1b" "7" "\n\n") /* LCD resolution, can differ per device (below is based on HTCLEO) */ #define HTC_FB_LCD_WIDTH 480 #define HTC_FB_LCD_HEIGHT 800 /* Set max console size for a 4x4 font */ #define HTC_FB_CON_MAX_ROWS (HTC_FB_LCD_WIDTH / 4) #define HTC_FB_CON_MAX_COLS (HTC_FB_LCD_HEIGHT / 4) /* Device dependent settings, currently configured for HTCLEO */ #define HTC_FB_BASE 0xe2000000 /* virtual page for our fb */ #define HTC_FB_PHYS 0x03800000 #define HTC_FB_OFF 0x00039000 #define HTC_FB_SIZE 0x00400000 #define HTC_MDP_BASE 0xe3000000 #define HTC_MDP_PHYS 0xAA200000 #define HTC_MDP_SIZE 0x000F0000 /* Pack color data in 565 RGB format; r and b are 5 bits, g is 6 bits */ #define HTC_FB_RGB(r, g, b) ((((r) & 0x1f) << 11) | (((g) & 0x3f) << 5) | (((b) & 0x1f) << 0)) /* Some standard colors */ unsigned short htc_fb_colors[8] = { HTC_FB_RGB(0x00, 0x00, 0x00), /* Black */ HTC_FB_RGB(0x1f, 0x00, 0x00), /* Red */ HTC_FB_RGB(0x00, 0x15, 0x00), /* Green */ HTC_FB_RGB(0x0f, 0x15, 0x00), /* Brown */ HTC_FB_RGB(0x00, 0x00, 0x1f), /* Blue */ HTC_FB_RGB(0x1f, 0x00, 0x1f), /* Magenta */ HTC_FB_RGB(0x00, 0x3f, 0x1f), /* Cyan */ HTC_FB_RGB(0x1f, 0x3f, 0x1f) /* White */ }; /* We can use any font which has width <= 8 pixels */ const struct font_desc *htc_fb_default_font; /* Pointer to font data (255 * font_rows bytes of data) */ const unsigned char *htc_fb_font_data; /* Size of font in pixels */ unsigned int htc_fb_font_cols, htc_fb_font_rows; /* Size of console in chars */ unsigned int htc_fb_console_cols, htc_fb_console_rows; /* Current position of cursor (where next character will be written) */ unsigned int htc_fb_cur_x, htc_fb_cur_y; /* Current fg / bg colors */ unsigned char htc_fb_cur_fg, htc_fb_cur_bg; /* Buffer to hold characters and attributes */ unsigned char htc_fb_chars[HTC_FB_CON_MAX_ROWS][HTC_FB_CON_MAX_COLS]; unsigned char htc_fb_fg[HTC_FB_CON_MAX_ROWS][HTC_FB_CON_MAX_COLS]; unsigned char htc_fb_bg[HTC_FB_CON_MAX_ROWS][HTC_FB_CON_MAX_COLS]; static void htc_fb_console_write(struct console *console, const char *s, unsigned int count); /* Console data */ static struct console htc_fb_console = { .name = "htc_fb", .write = htc_fb_console_write, .flags = #ifdef CONFIG_HTC_FB_CONSOLE_BOOT CON_BOOT | #endif CON_PRINTBUFFER | CON_ENABLED, .index = -1, }; // Redefine these defines so that we can easily borrow the code, first parameter is ignored #undef mdp_writel #define mdp_writel(mdp, value, offset) writel(value, HTC_MDP_BASE + offset) #undef mdp_readl #define mdp_readl(mdp, offset) readl(HTC_MDP_BASE + offset) /* Update the framebuffer from the character buffer then start DMA */ static void htc_fb_console_update(void) { unsigned int memaddr, fbram, stride, width, height, x, y, i, j, r1, c1, r2, c2; #if 0 unsigned int dma2_cfg; #endif unsigned short ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ unsigned short *ptr; unsigned char ch; fbram = HTC_FB_PHYS + HTC_FB_OFF; memaddr = HTC_FB_BASE + HTC_FB_OFF; ptr = (unsigned short*) memaddr; for (i = 0; i < htc_fb_console_rows * htc_fb_font_rows; i++) { r1 = i / htc_fb_font_rows; r2 = i % htc_fb_font_rows; for (j = 0; j < htc_fb_console_cols * htc_fb_font_cols; j++) { c1 = j / htc_fb_font_cols; c2 = j % htc_fb_font_cols; ch = htc_fb_chars[r1][c1]; *ptr++ = htc_fb_font_data[(((int) ch) * htc_fb_font_rows) + r2] & ((1 << (htc_fb_font_cols - 1)) >> c2) ? htc_fb_colors[htc_fb_fg[r1][c1]] : htc_fb_colors[htc_fb_bg[r1][c1]]; } ptr += HTC_FB_LCD_WIDTH - htc_fb_console_cols * htc_fb_font_cols; } stride = HTC_FB_LCD_WIDTH * 2; width = HTC_FB_LCD_WIDTH; height = HTC_FB_LCD_HEIGHT; x = 0; y = 0; /* In previous versions of htc_fb_console we configured the dma_config. However, the settings for HTCLEO are still unknown. Currently leaving the dma unconfigured, which means we just assume bootloader or Windows Mobile has configured it correctly for us. */ #if 0 dma2_cfg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | DMA_PACK_PATTERN_RGB | DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; dma2_cfg |= DMA_IBUF_FORMAT_RGB565; dma2_cfg |= DMA_OUT_SEL_MDDI; dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; dma2_cfg |= DMA_DITHER_EN; /* 565 16BPP */ dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; /* 666 18BPP */ //dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; #endif /* setup size, address, and stride */ mdp_writel(NULL, (height << 16) | (width), MDP_DMA_P_SIZE); mdp_writel(NULL, fbram, MDP_DMA_P_IBUF_ADDR); mdp_writel(NULL, stride, MDP_DMA_P_IBUF_Y_STRIDE); /* set y & x offset and MDDI transaction parameters */ mdp_writel(NULL, (y << 16) | (x), MDP_DMA_P_OUT_XY); mdp_writel(NULL, ld_param, MDP_MDDI_PARAM_WR_SEL); mdp_writel(NULL, (MDDI_VDO_PACKET_DESC_RGB565 << 16) | MDDI_VDO_PACKET_PRIM, MDP_MDDI_PARAM); #if 0 //mdp_writel(NULL, dma2_cfg, MDP_DMA_P_CONFIG); #endif mdp_writel(NULL, 0, MDP_DMA_P_START); /* Wait a bit to let the transfer finish */ mdelay(16); } /* Clear screen and buffers */ static void htc_fb_console_clear(void) { /* Default white on black, clear everything */ memset((void*) (HTC_FB_BASE + HTC_FB_OFF), 0, HTC_FB_LCD_WIDTH * HTC_FB_LCD_HEIGHT * 2); memset(htc_fb_chars, 0, htc_fb_console_cols * htc_fb_console_rows); memset(htc_fb_fg, 7, htc_fb_console_cols * htc_fb_console_rows); memset(htc_fb_bg, 0, htc_fb_console_cols * htc_fb_console_rows); htc_fb_cur_x = htc_fb_cur_y = 0; htc_fb_cur_fg = 7; htc_fb_cur_bg = 0; htc_fb_console_update(); } static struct console htc_fb_console; /* Write a string to character buffer; handles word wrapping, auto-scrolling, etc * After that, calls htc_fb_console_update to send data to the LCD */ static void htc_fb_console_write(struct console *console, const char *s, unsigned int count) { unsigned int i, j, k, scroll; const char *p; #ifndef CONFIG_HTC_FB_CONSOLE_BOOT // See if a framebuffer has been registered. If so, we disable this console to prevent conflict with // other FB devices (i.e. msm_fb). if (num_registered_fb > 0) { printk(KERN_INFO "htc_fb_console: framebuffer device detected, disabling boot console\n"); console->flags = 0; return; } #endif scroll = 0; for (k = 0, p = s; k < count; k++, p++) { if (*p == '\n') { /* Jump to next line */ scroll = 1; } else if (*p == '\t') { /* Tab size 8 chars */ htc_fb_cur_x = (htc_fb_cur_x + 7) % 8; if (htc_fb_cur_x >= htc_fb_console_cols) { scroll = 1; } } else if (*p == '\x1b') { /* Escape char (ascii 27) * Some primitive way to change color: * \x1b followed by one digit to represent color (0 black ... 7 white) */ if (k < count - 1) { p++; htc_fb_cur_fg = *p - '0'; if (htc_fb_cur_fg >= 8) { htc_fb_cur_fg = 7; } } } else if (*p != '\r') { /* Ignore \r, other cars get written here */ htc_fb_chars[htc_fb_cur_y][htc_fb_cur_x] = *p; htc_fb_fg[htc_fb_cur_y][htc_fb_cur_x] = htc_fb_cur_fg; htc_fb_bg[htc_fb_cur_y][htc_fb_cur_x] = htc_fb_cur_bg; htc_fb_cur_x++; if (htc_fb_cur_x >= htc_fb_console_cols) { scroll = 1; } } if (scroll) { scroll = 0; htc_fb_cur_x = 0; htc_fb_cur_y++; if (htc_fb_cur_y == htc_fb_console_rows) { /* Scroll below last line, shift all rows up * Should have used a bigger buffer so no shift, * would actually be needed -- but hey, it's a one night hack */ htc_fb_cur_y--; for (i = 1; i < htc_fb_console_rows; i++) { for (j = 0; j < htc_fb_console_cols; j++) { htc_fb_chars[i - 1][j] = htc_fb_chars[i][j]; htc_fb_fg[i - 1][j] = htc_fb_fg[i][j]; htc_fb_bg[i - 1][j] = htc_fb_bg[i][j]; } } for (j = 0; j < htc_fb_console_cols; j++) { htc_fb_chars[htc_fb_console_rows - 1][j] = 0; htc_fb_fg[htc_fb_console_rows - 1][j] = htc_fb_cur_fg; htc_fb_bg[htc_fb_console_rows - 1][j] = htc_fb_cur_bg; } } } } htc_fb_console_update(); #ifdef CONFIG_HTC_FB_CONSOLE_DELAY /* Delay so we can see what's there, we have no keys to scroll */ mdelay(500); #endif } #if defined(CONFIG_VERY_EARLY_CONSOLE) // Make sure we don't init twice static bool htc_fb_console_init_done = false; #endif /* Init console on LCD using MDDI/MDP interface to transfer pixel data. * We can DMA to the board, as long as we give a physical address to the LCD * controller and use the coresponding virtual address to write pixels to. * The physical address I used is the one wince had for the framebuffer */ #if !defined(CONFIG_VERY_EARLY_CONSOLE) static #endif int __init htc_fb_console_init(void) { struct map_desc map, map_mdp; #if defined(CONFIG_VERY_EARLY_CONSOLE) if (htc_fb_console_init_done) { printk(KERN_INFO "htc_fb_console_init: already initialized, bailing out\n"); return 0; } htc_fb_console_init_done = true; #endif /* Map the framebuffer Windows was using, as we know the physical address */ map.pfn = __phys_to_pfn(HTC_FB_PHYS & SECTION_MASK); map.virtual = HTC_FB_BASE; map.length = ((unsigned long) HTC_FB_SIZE + ~SECTION_MASK) & SECTION_MASK; map.type = MT_MEMORY; /* Ugly hack, but we're not sure what works and what doesn't, * so better use the lowest level we have for setting the mapping */ create_mapping(&map); /* Map the MDP */ map_mdp.pfn = __phys_to_pfn(HTC_MDP_PHYS & SECTION_MASK); map_mdp.virtual = HTC_MDP_BASE; map_mdp.length = ((unsigned long) HTC_MDP_SIZE + ~SECTION_MASK) & SECTION_MASK; map_mdp.type = MT_MEMORY; create_mapping(&map_mdp); /* Init font (we support any font that has width <= 8; height doesn't matter) */ htc_fb_default_font = get_default_font(HTC_FB_LCD_WIDTH, HTC_FB_LCD_HEIGHT, 0xFF, 0xFFFFFFFF); if (!htc_fb_default_font) { printk(KERN_WARNING "Can't find a suitable font for htc_fb\n"); return -1; } htc_fb_font_data = htc_fb_default_font->data; htc_fb_font_cols = htc_fb_default_font->width; htc_fb_font_rows = htc_fb_default_font->height; htc_fb_console_cols = HTC_FB_LCD_WIDTH / htc_fb_font_cols; if (htc_fb_console_cols > HTC_FB_CON_MAX_COLS) htc_fb_console_cols = HTC_FB_CON_MAX_COLS; htc_fb_console_rows = HTC_FB_LCD_HEIGHT / htc_fb_font_rows; if (htc_fb_console_rows > HTC_FB_CON_MAX_ROWS) htc_fb_console_rows = HTC_FB_CON_MAX_ROWS; /* Clear the buffer; we could probably see the Haret output if we didn't clear * the buffer (if it used same physical address) */ htc_fb_console_clear(); /* Welcome message */ htc_fb_console_write(&htc_fb_console, HTC_FB_MSG, strlen(HTC_FB_MSG)); /* Register console */ register_console(&htc_fb_console); console_verbose(); return 0; } #if !defined(CONFIG_VERY_EARLY_CONSOLE) console_initcall(htc_fb_console_init); #endif