387 lines
12 KiB
C
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);
|
|
|