android_kernel_cmhtcleo/arch/arm/mach-msm/htc_fb_console.c
2010-08-27 19:11:34 +02:00

387 lines
12 KiB
C

/* arch/arm/mach-msm/htc_fb_console.c
*
* By Octavian Voicu <octavian@voicu.gmail.com>
*
* 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 <linux/console.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/font.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <mach/msm_iomap.h>
#include <mach/msm_fb.h>
#include <asm/pgtable.h>
#include <asm/mach/map.h>
#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;
}
console_initcall(htc_fb_console_init);