主机:VM - redhat 9.0
开发板:FL2440,linux-2.6.12
arm-linux-gcc:3.4.1
/*
* linux/drivers/video/s3c2410fb.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-irq.h>
#include "s3c2410fb.h"
/*
* Complain if VAR is out of range.
*/
#define DEBUG_VAR 1
// 通过写入一个数据到此寄存器来清除SRCPND 寄存器的指定位。其只清除那些数据中被设置为1 的相应
// 位置的SRCPND 位。那些数据中被设置为0 的相应位置的位保持不变。
#define ClearPending(x) { \
__raw_writel((1 << (x)), S3C2410_SRCPND); \
__raw_writel((1 << (x)), S3C2410_INTPND); \
}
struct gzliu_fb_mach_info fs2410_info = {
.pixclock = 270000,
.xres = 320,
.yres = 240,
.bpp = 16,
.hsync_len = 8,
.left_margin = 5,
.right_margin = 15,
.vsync_len = 15,
.upper_margin = 3,
.lower_margin = 5,
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
.cmap_greyscale = 0,
.cmap_inverse = 0,
.cmap_static = 0,
.reg = {
.lcdcon1 = (6<<8)|(0<<7)|(3<<5)|(12<<1),
.lcdcon2 = (3<<24) | (239<<14) | (5<<6) | (15),
.lcdcon3 = (58<<19) | (319<<8) | (15),
.lcdcon4 = (13<<8) | (8),
.lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (0<<7) | (0<<6) | (1<<3) |(0<<1) | (1),
}
};
static void (*gzliu_fb_backlight_power)(int);
static void (*gzliu_fb_lcd_power)(int);
static int gzliu_fb_activate_var(struct fb_var_screeninfo *var, struct gzliu_fb_info *);
static void set_ctrlr_state(struct gzliu_fb_info *fbi, u_int state);
static inline void gzliu_fb_schedule_work(struct gzliu_fb_info *fbi, u_int state)
{
printk("@@@@@@@@@@ gzliu_fb_schedule_work() @@@@@@@@@@@@\n");
unsigned long flags;
local_irq_save(flags);
/*
* We need to handle two requests being made at the same time.
* There are two important cases:
* 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
* We must perform the unblanking, which will do our REENABLE for us.
* 2. When we are blanking, but immediately unblank before we have
* blanked. We do the "REENABLE" thing here as well, just to be sure.
*/
if (fbi->task_state == C_ENABLE && state == C_REENABLE)
state = (u_int) -1;
if (fbi->task_state == C_DISABLE && state == C_ENABLE)
state = C_REENABLE;
if (state != (u_int)-1) {
fbi->task_state = state;
schedule_work(&fbi->task);
}
local_irq_restore(flags);
}
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
{
printk("@@@@@@@@@@ chan_to_field() @@@@@@@@@@@@\n");
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int gzliu_fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
u_int trans, struct fb_info *info)
{
printk("@@@@@@@@@@ gzliu_fb_setpalettereg() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info;
u_int val, ret = 1;
if (regno < fbi->palette_size) {
if (fbi->fb.var.grayscale) {
val = ((blue >> 8) & 0x00ff);
} else {
val = ((red >> 0) & 0xf800);
val |= ((green >> 5) & 0x07e0);
val |= ((blue >> 11) & 0x001f);
}
fbi->palette_cpu[regno] = val;
ret = 0;
}
return ret;
}
static int gzliu_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int trans, struct fb_info *info)
{
printk("@@@@@@@@@@ gzliu_fb_setcolreg() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info;
unsigned int val;
int ret = 1;
/*
* If inverse mode was selected, invert all the colours
* rather than the register number. The register number
* is what you poke into the framebuffer to produce the
* colour you requested.
*/
if (fbi->cmap_inverse) {
red = 0xffff - red;
green = 0xffff - green;
blue = 0xffff - blue;
}
/*
* If greyscale is true, then we convert the RGB value
* to greyscale no matter what visual we are using.
*/
if (fbi->fb.var.grayscale)
red = green = blue = (19595 * red + 38470 * green +
7471 * blue) >> 16;
switch (fbi->fb.fix.visual) {
case FB_VISUAL_TRUECOLOR:
/*
* 12 or 16-bit True Colour. We encode the RGB value
* according to the RGB bitfield information.
*/
if (regno < 16) {
u32 *pal = fbi->fb.pseudo_palette;
val = chan_to_field(red, &fbi->fb.var.red);
val |= chan_to_field(green, &fbi->fb.var.green);
val |= chan_to_field(blue, &fbi->fb.var.blue);
pal[regno] = val;
ret = 0;
}
break;
case FB_VISUAL_STATIC_PSEUDOCOLOR:
case FB_VISUAL_PSEUDOCOLOR:
ret = gzliu_fb_setpalettereg(regno, red, green, blue, trans, info);
break;
}
return ret;
}
/*
* gzliu_fb_check_var():
* Get the video params out of 'var'. If a value doesn't fit, round it up,
* if it's too big, return -EINVAL.
*
* Round up in the following order: bits_per_pixel, xres,
* yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
* bitfields, horizontal timing, vertical timing.
*/
static int gzliu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
printk("@@@@@@@@@@ gzliu_fb_check_var() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info;
if (var->xres < MIN_XRES)
var->xres = MIN_XRES;
if (var->yres < MIN_YRES)
var->yres = MIN_YRES;
if (var->xres > fbi->max_xres)
var->xres = fbi->max_xres;
if (var->yres > fbi->max_yres)
var->yres = fbi->max_yres;
var->xres_virtual =
max(var->xres_virtual, var->xres);
var->yres_virtual =
max(var->yres_virtual, var->yres);
/*
* Setup the RGB parameters for this display.
*
* The pixel packing format is described on page 7-11 of the
* PXA2XX Developer's Manual.
*/
if ( var->bits_per_pixel == 16 ) {
var->red.offset = 11; var->red.length = 5;
var->green.offset = 5; var->green.length = 6;
var->blue.offset = 0; var->blue.length = 5;
var->transp.offset = var->transp.length = 0;
} else {
var->red.offset = var->green.offset = var->blue.offset = var->transp.offset = 0;
var->red.length = 8;
var->green.length = 8;
var->blue.length = 8;
var->transp.length = 0;
}
#ifdef CONFIG_CPU_FREQ
DPRINTK("dma period = %d ps, clock = %d kHz\n",
gzliu_fb_display_dma_period(var),
get_clk_frequency_khz(0));
#endif
return 0;
}
static inline void gzliu_fb_set_truecolor(u_int is_true_color)
{
printk("@@@@@@@@@@ gzliu_fb_set_truecolor() @@@@@@@@@@@@\n");
DPRINTK("true_color = %d\n", is_true_color);
}
/*
* gzliu_fb_set_par():
* Set the user defined part of the display for the specified console
*/
static int gzliu_fb_set_par(struct fb_info *info)
{
printk("@@@@@@@@@@ gzliu_fb_set_par() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info;
struct fb_var_screeninfo *var = &info->var;
unsigned long palette_mem_size;
DPRINTK("set_par\n");
if (var->bits_per_pixel == 16)
fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;
else if (!fbi->cmap_static)
fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
else {
/*
* Some people have weird ideas about wanting static
* pseudocolor maps. I suspect their user space
* applications are broken.
*/
fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
}
fbi->fb.fix.line_length = var->xres_virtual *
var->bits_per_pixel / 8;
if (var->bits_per_pixel == 16)
fbi->palette_size = 0;
else
fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel;
palette_mem_size = fbi->palette_size * sizeof(u16);
printk("@@@@@@@ palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
/*
* Set (any) board control register to handle new color depth
*/
gzliu_fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
if (fbi->fb.var.bits_per_pixel == 16)
fb_dealloc_cmap(&fbi->fb.cmap);
else
fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0);
gzliu_fb_activate_var(var, fbi);
return 0;
}
/*
* gzliu_fb_blank():
* Blank the display by setting all palette values to zero. Note, the
* 12 and 16 bpp modes don't really use the palette, so this will not
* blank the display in all modes.
*/
static int gzliu_fb_blank(int blank, struct fb_info *info)
{
printk("@@@@@@@@@@ gzliu_fb_blank() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info;
int i;
DPRINTK("gzliu_fb_blank: blank=%d\n", blank);
switch (blank) {
case VESA_POWERDOWN:
case VESA_VSYNC_SUSPEND:
case VESA_HSYNC_SUSPEND:
if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
for (i = 0; i < fbi->palette_size; i++);
gzliu_fb_schedule_work(fbi, C_DISABLE);
break;
case VESA_NO_BLANKING:
if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
fb_set_cmap(&fbi->fb.cmap, info);
gzliu_fb_schedule_work(fbi, C_ENABLE);
}
return 0;
}
static int soft_cursor_dummy(struct fb_info *info, struct fb_cursor *cursor)
{
printk("@@@@@@@@@@ soft_cursor_dummy() @@@@@@@@@@@@\n");
return 0;
}
static struct fb_ops gzliu_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = gzliu_fb_check_var,
.fb_set_par = gzliu_fb_set_par,
.fb_setcolreg = gzliu_fb_setcolreg,
.fb_blank = gzliu_fb_blank,
.fb_cursor = soft_cursor_dummy,
};
static inline unsigned int get_pcd(unsigned int pixclock)
{
printk("@@@@@@@@@@ get_pcd() @@@@@@@@@@@@\n");
unsigned long long pcd;
pcd = s3c2410_hclk/(((__raw_readl(S3C2410_LCDCON1)>>8)&0x3ff)*2+1);
return (unsigned int)pcd;
}
/*
* gzliu_fb_activate_var():
* Configures LCD Controller based on entries in var parameter. Settings are
* only written to the controller if changes were made.
*/
static int gzliu_fb_activate_var(struct fb_var_screeninfo *var, struct gzliu_fb_info *fbi)
{
printk("@@@@@@@@@@ gzliu_fb_activate_var() @@@@@@@@@@@@\n");
struct gzliu_fb_lcd_reg new_regs;
u_long flags;
u_int half_screen_size, yres;
unsigned long VideoPhysicalTemp = fbi->screen_dma;
DPRINTK("Configuring gzliu_ LCD\n");
DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n",
var->xres, var->hsync_len,
var->left_margin, var->right_margin);
DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n",
var->yres, var->vsync_len,
var->upper_margin, var->lower_margin);
DPRINTK("var: pixclock=%d pcd=%d\n", var->pixclock, pcd);
#if DEBUG_VAR
if (var->xres < 16 || var->xres > 1024)
printk(KERN_ERR "%s: invalid xres %d\n",
fbi->fb.fix.id, var->xres);
switch(var->bits_per_pixel) {
case 1:
case 2:
case 4:
case 8:
case 16:
break;
default:
printk(KERN_ERR "%s: invalid bit depth %d\n",
fbi->fb.fix.id, var->bits_per_pixel);
break;
}
if (var->hsync_len < 1 || var->hsync_len > 64)
printk(KERN_ERR "%s: invalid hsync_len %d\n",
fbi->fb.fix.id, var->hsync_len);
if (var->left_margin < 1 || var->left_margin > 255)
printk(KERN_ERR "%s: invalid left_margin %d\n",
fbi->fb.fix.id, var->left_margin);
if (var->right_margin < 1 || var->right_margin > 255)
printk(KERN_ERR "%s: invalid right_margin %d\n",
fbi->fb.fix.id, var->right_margin);
if (var->yres < 1 || var->yres > 1024)
printk(KERN_ERR "%s: invalid yres %d\n",
fbi->fb.fix.id, var->yres);
if (var->vsync_len < 1 || var->vsync_len > 64)
printk(KERN_ERR "%s: invalid vsync_len %d\n",
fbi->fb.fix.id, var->vsync_len);
if (var->upper_margin < 0 || var->upper_margin > 255)
printk(KERN_ERR "%s: invalid upper_margin %d\n",
fbi->fb.fix.id, var->upper_margin);
if (var->lower_margin < 0 || var->lower_margin > 255)
printk(KERN_ERR "%s: invalid lower_margin %d\n",
fbi->fb.fix.id, var->lower_margin);
#endif
/* Update shadow copy atomically */
local_irq_save(flags);
new_regs.lcdcon1 = fbi->reg.lcdcon1 & ~S3C2410_LCDCON1_ENVID;
new_regs.lcdcon2 = (fbi->reg.lcdcon2 & ~LCD2_LINEVAL_MSK)
| LCD2_LINEVAL(var->yres - 1);
/* TFT LCD only ! */
new_regs.lcdcon3 = (fbi->reg.lcdcon3 & ~LCD3_HOZVAL_MSK)
| LCD3_HOZVAL(var->xres - 1);
new_regs.lcdcon4 = fbi->reg.lcdcon4;
new_regs.lcdcon5 = fbi->reg.lcdcon5;
new_regs.lcdsaddr1 =
LCDADDR_BANK(((unsigned long)VideoPhysicalTemp >> 22))
| LCDADDR_BASEU(((unsigned long)VideoPhysicalTemp >> 1));
/* 16bpp */
new_regs.lcdsaddr2 = LCDADDR_BASEL(
((unsigned long)VideoPhysicalTemp + (var->xres * 2 * (var->yres/*-1*/)))
>> 1);
new_regs.lcdsaddr3 = LCDADDR_OFFSET(0) | (LCDADDR_PAGE(var->xres) /*>> 1*/);
yres = var->yres;
half_screen_size = var->bits_per_pixel;
half_screen_size = half_screen_size * var->xres * var->yres / 16;
fbi->reg.lcdcon1 = new_regs.lcdcon1;
fbi->reg.lcdcon2 = new_regs.lcdcon2;
fbi->reg.lcdcon3 = new_regs.lcdcon3;
fbi->reg.lcdcon4 = new_regs.lcdcon4;
fbi->reg.lcdcon5 = new_regs.lcdcon5;
fbi->reg.lcdsaddr1 = new_regs.lcdsaddr1;
fbi->reg.lcdsaddr2 = new_regs.lcdsaddr2;
fbi->reg.lcdsaddr3 = new_regs.lcdsaddr3;
__raw_writel(fbi->reg.lcdcon1&~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
__raw_writel(fbi->reg.lcdcon2, S3C2410_LCDCON2);
__raw_writel(fbi->reg.lcdcon3, S3C2410_LCDCON3);
__raw_writel(fbi->reg.lcdcon4, S3C2410_LCDCON4);
__raw_writel(fbi->reg.lcdcon5, S3C2410_LCDCON5);
__raw_writel(fbi->reg.lcdsaddr1, S3C2410_LCDSADDR1);
__raw_writel(fbi->reg.lcdsaddr2, S3C2410_LCDSADDR2);
__raw_writel(fbi->reg.lcdsaddr3, S3C2410_LCDSADDR3);
//next code should not be used in TX06D18 LCD
#if !defined (TX06D18_TFT_LCD ) //change by gongjun
#if defined(CONFIG_S3C2410_SMDK) && !defined(CONFIG_SMDK_AIJI)
LCDLPCSEL = 0x2;
#elif defined(CONFIG_S3C2410_SMDK) && defined(CONFIG_SMDK_AIJI)
LCDLPCSEL = 0x7;
#endif
#endif
__raw_writel(0, S3C2410_TPAL);
__raw_writel(fbi->reg.lcdcon1|S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
#if 1
{
printk("LCDCON1 0x%08x\n", __raw_readl(S3C2410_LCDCON1));
printk("LCDCON2 0x%08x\n", __raw_readl(S3C2410_LCDCON2));
printk("LCDCON3 0x%08x\n", __raw_readl(S3C2410_LCDCON3));
printk("LCDCON4 0x%08x\n", __raw_readl(S3C2410_LCDCON4));
printk("LCDCON5 0x%08x\n", __raw_readl(S3C2410_LCDCON5));
printk("LCDSADDR1 0x%08x\n", __raw_readl(S3C2410_LCDSADDR1));
printk("LCDSADDR2 0x%08x\n", __raw_readl(S3C2410_LCDSADDR2));
printk("LCDSADDR3 0x%08x\n", __raw_readl(S3C2410_LCDSADDR3));
}
#endif
local_irq_restore(flags);
return 0;
}
/*
* NOTE! The following functions are purely helpers for set_ctrlr_state.
* Do not call them directly; set_ctrlr_state does the correct serialisation
* to ensure that things happen in the right way 100% of time time.
* -- rmk
*/
static inline void __gzliu_fb_backlight_power(struct gzliu_fb_info *fbi, int on)
{
printk("@@@@@@@@@@ __gzliu_fb_backlight_power() @@@@@@@@@@@@\n");
printk("backlight o%s\n", on ? "n" : "ff");
if (gzliu_fb_backlight_power)
gzliu_fb_backlight_power(on);
}
static inline void __gzliu_fb_lcd_power(struct gzliu_fb_info *fbi, int on)
{
printk("@@@@@@@@@@ __gzliu_fb_lcd_power() @@@@@@@@@@@@\n");
printk("LCD power o%s\n", on ? "n" : "ff");
if (gzliu_fb_lcd_power)
gzliu_fb_lcd_power(on);
}
static void gzliu_fb_setup_gpio(struct gzliu_fb_info *fbi)
{
printk("@@@@@@@@@@ gzliu_fb_setup_gpio() @@@@@@@@@@@@\n");
DPRINTK("setup gpio\n");
// 将GPD这组GPIO的16个引脚配置为VD
__raw_writel(0xaaaaaaaa, S3C2410_GPDCON);
__raw_writel(7, S3C2410_LCDINTMSK); // 3 by gjl MASK LCD Sub Interrupt
__raw_writel(0, S3C2410_TPAL); // Disable Temp Palette
__raw_writel(0, S3C2410_LPCSEL); // 0 by gjl Disable LPC3600
__raw_writel(0, S3C2410_PRIORITY); //0x7f add by gjl
}
static void gzliu_fb_enable_controller(struct gzliu_fb_info *fbi)
{
printk("@@@@@@@@@@ gzliu_fb_enable_controller() @@@@@@@@@@@@\n");
__raw_writel(fbi->reg.lcdcon1&~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
__raw_writel(fbi->reg.lcdcon2, S3C2410_LCDCON2);
__raw_writel(fbi->reg.lcdcon3, S3C2410_LCDCON3);
__raw_writel(fbi->reg.lcdcon4, S3C2410_LCDCON4);
__raw_writel(fbi->reg.lcdcon5, S3C2410_LCDCON5);
__raw_writel(fbi->reg.lcdsaddr1, S3C2410_LCDSADDR1);
__raw_writel(fbi->reg.lcdsaddr2, S3C2410_LCDSADDR2);
__raw_writel(fbi->reg.lcdsaddr3, S3C2410_LCDSADDR3);
__raw_writel(fbi->reg.lcdcon1|S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
#if 1
printk("LCDCON1 0x%08x\n", __raw_readl(S3C2410_LCDCON1));
printk("LCDCON2 0x%08x\n", __raw_readl(S3C2410_LCDCON2));
printk("LCDCON3 0x%08x\n", __raw_readl(S3C2410_LCDCON3));
printk("LCDCON4 0x%08x\n", __raw_readl(S3C2410_LCDCON4));
printk("LCDCON5 0x%08x\n", __raw_readl(S3C2410_LCDCON5));
printk("LCDSADDR1 0x%08x\n", __raw_readl(S3C2410_LCDSADDR1));
printk("LCDSADDR2 0x%08x\n", __raw_readl(S3C2410_LCDSADDR2));
printk("LCDSADDR3 0x%08x\n", __raw_readl(S3C2410_LCDSADDR3));
#endif
}
static void gzliu_fb_disable_controller(struct gzliu_fb_info *fbi)
{
printk("@@@@@@@@@@ gzliu_fb_disable_controller() @@@@@@@@@@@@\n");
__raw_writel(fbi->reg.lcdcon1&~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
}
/*
* This function must be called from task context only, since it will
* sleep when disabling the LCD controller, or if we get two contending
* processes trying to alter state.
*/
static void set_ctrlr_state(struct gzliu_fb_info *fbi, u_int state)
{
printk("@@@@@@@@@@ set_ctrlr_state() @@@@@@@@@@@@\n");
u_int old_state;
down(&fbi->ctrlr_sem);
old_state = fbi->state;
/*
* Hack around fbcon initialisation.
*/
if (old_state == C_STARTUP && state == C_REENABLE)
state = C_ENABLE;
switch (state) {
case C_DISABLE_CLKCHANGE:
/*
* Disable controller for clock change. If the
* controller is already disabled, then do nothing.
*/
if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {
fbi->state = state;
gzliu_fb_disable_controller(fbi);
}
break;
case C_DISABLE_PM:
case C_DISABLE:
/*
* Disable controller
*/
if (old_state != C_DISABLE) {
fbi->state = state;
__gzliu_fb_backlight_power(fbi, 0);
__gzliu_fb_lcd_power(fbi, 0);
if (old_state != C_DISABLE_CLKCHANGE)
gzliu_fb_disable_controller(fbi);
}
break;
case C_ENABLE_CLKCHANGE:
/*
* Enable the controller after clock change. Only
* do this if we were disabled for the clock change.
*/
if (old_state == C_DISABLE_CLKCHANGE) {
fbi->state = C_ENABLE;
gzliu_fb_enable_controller(fbi);
}
break;
case C_REENABLE:
/*
* Re-enable the controller only if it was already
* enabled. This is so we reprogram the control
* registers.
*/
if (old_state == C_ENABLE) {
gzliu_fb_disable_controller(fbi);
gzliu_fb_setup_gpio(fbi);
gzliu_fb_enable_controller(fbi);
}
break;
case C_ENABLE_PM:
/*
* Re-enable the controller after PM. This is not
* perfect - think about the case where we were doing
* a clock change, and we suspended half-way through.
*/
if (old_state != C_DISABLE_PM)
break;
/* fall through */
case C_ENABLE:
/*
* Power up the LCD screen, enable controller, and
* turn on the backlight.
*/
if (old_state != C_ENABLE) {
fbi->state = C_ENABLE;
gzliu_fb_setup_gpio(fbi);
gzliu_fb_enable_controller(fbi);
__gzliu_fb_lcd_power(fbi, 1);
__gzliu_fb_backlight_power(fbi, 1);
}
break;
}
up(&fbi->ctrlr_sem);
}/* static void set_ctrlr_state() */
/*
* Our LCD controller task (which is called when we blank or unblank)
* via keventd.
*/
static void gzliu_fb_task(void *dummy)
{
printk("@@@@@@@@@@ gzliu_fb_task() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi = dummy;
u_int state = xchg(&fbi->task_state, -1);
set_ctrlr_state(fbi, state);
}
void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
{
printk("@@@@@@@@@@ consistent_alloc() @@@@@@@@@@@@\n");
struct page *page, *end, *free;
unsigned long order;
void *ret, *virt;
if (in_interrupt())
BUG();
/*
* // PAGE_SHIFT determines the page size //
*
* #define PAGE_SHIFT 12
* #define PAGE_SIZE (1UL << PAGE_SHIFT)
* #define PAGE_MASK (~(PAGE_SIZE-1))
*
* // to align the pointer to the (next) page boundary //
*
* #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
*/
size = PAGE_ALIGN(size);
/*
* include/asm-arm/page.h
*
* // Pure 2^n version of get_order //
* static inline int get_order(unsigned long size)
* {
* int order;
*
* size = (size-1) >> (PAGE_SHIFT-1);
* order = -1;
* do {
* size >>= 1;
* order++;
* } while (size);
* return order;
* }
*/
order = get_order(size);
page = alloc_pages(gfp, order);
if (!page)
goto no_page;
/*
* We could do with a page_to_phys and page_to_bus here.
*/
virt = page_address(page);
/*
* #define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
* #define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
*
* dma_handle -- 物理地址
* ret -- 映射后的虚拟地址
*/
*dma_handle = virt_to_bus(virt);
ret = __ioremap(virt_to_phys(virt), size, 0,0);
if (!ret)
goto no_remap;
#if 0 /* ioremap_does_flush_cache_all */
/*
* we need to ensure that there are no cachelines in use, or
* worse dirty in this area. Really, we don't need to do
* this since __ioremap does a flush_cache_all() anyway. --rmk
*/
invalidate_dcache_range(virt, virt + size);
#endif
/*
* free wasted pages. We skip the first page since we know
* that it will have count = 1 and won't require freeing.
* We also mark the pages in use as reserved so that
* remap_page_range works.
*/
page = virt_to_page(virt);
free = page + (size >> PAGE_SHIFT);
end = page + (1 << order);
for (; page < end; page++) {
set_page_count(page, 1);
if (page >= free)
__free_page(page);
else
SetPageReserved(page);
}
return ret;
no_remap:
__free_pages(page, order);
no_page:
return NULL;
}
/*
* gzliu_fb_map_video_memory():
* Allocates the DRAM memory for the frame buffer. This buffer is
* remapped into a non-cached, non-buffered, memory region to
* allow palette and pixel writes to occur without flushing the
* cache. Once this area is remapped, all virtual memory
* access to the video memory should occur at the new region.
*/
static int __init gzliu_fb_map_video_memory(struct gzliu_fb_info *fbi)
{
printk("@@@@@@@@@@ gzliu_fb_map_video_memory() @@@@@@@@@@@@\n");
u_long palette_mem_size;
/*
* We reserve one page for the palette, plus the size
* of the framebuffer.
*/
fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
//changed by gjl dma_alloc_writecombine dma_alloc_coherent
fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size,
&fbi->map_dma); //fbi->dev, fbi->map_size,&fbi->map_dma, GFP_KERNEL);
if (fbi->map_cpu) {
printk("@@@@@@ VA=0x%p, PA=0x%08x, size=0x%08x\n", fbi->map_cpu, fbi->map_dma, fbi->map_size);
/* prevent initial garbage on screen */
memset(fbi->map_cpu, 0, fbi->map_size);
fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE;
fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
/*
* FIXME: this is actually the wrong thing to place in
* smem_start. But fbdev suffers from the problem that
* it needs an API which doesn't exist (in this case,
* dma_writecombine_mmap)
*/
fbi->fb.fix.smem_start = fbi->screen_dma;
fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16;
palette_mem_size = fbi->palette_size * sizeof(u16);
printk("@@@@@@@ palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
}
return fbi->map_cpu ? 0 : -ENOMEM;
}
static struct gzliu_fb_info * __init gzliu_fb_init_fbinfo(struct device *dev)
{
printk("@@@@@@@@@@ gzliu_fb_init_fbinfo() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi;
void *addr;
struct gzliu_fb_mach_info *inf = &fs2410_info;//dev->platform_data;
/* Alloc the gzliu_fb_info and pseudo_palette in one step */
fbi = kmalloc(sizeof(struct gzliu_fb_info) + sizeof(u32) * 16, GFP_KERNEL);
if (!fbi)
return NULL;
memset(fbi, 0, sizeof(struct gzliu_fb_info));
fbi->dev = dev;
strcpy(fbi->fb.fix.id, GZLIU_NAME);
fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fbi->fb.fix.type_aux = 0;
fbi->fb.fix.xpanstep = 0;
fbi->fb.fix.ypanstep = 0;
fbi->fb.fix.ywrapstep = 0;
fbi->fb.fix.accel = FB_ACCEL_NONE; // 没有硬件加速
fbi->fb.var.nonstd = 0;
fbi->fb.var.activate = FB_ACTIVATE_NOW;
fbi->fb.var.height = -1;
fbi->fb.var.width = -1;
fbi->fb.var.accel_flags = 0;
fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
fbi->fb.fbops = &gzliu_fb_ops;
fbi->fb.flags = FBINFO_FLAG_DEFAULT;
fbi->fb.node = -1;
fbi->fb.currcon = -1;
addr = fbi;
addr = addr + sizeof(struct gzliu_fb_info);
fbi->fb.pseudo_palette = addr;
fbi->max_xres = inf->xres;
fbi->fb.var.xres = inf->xres;
fbi->fb.var.xres_virtual = inf->xres;
fbi->max_yres = inf->yres;
fbi->fb.var.yres = inf->yres;
fbi->fb.var.yres_virtual = inf->yres;
fbi->max_bpp = inf->bpp;
fbi->fb.var.bits_per_pixel = inf->bpp;
fbi->fb.var.pixclock = inf->pixclock;
fbi->fb.var.hsync_len = inf->hsync_len;
fbi->fb.var.left_margin = inf->left_margin;
fbi->fb.var.right_margin = inf->right_margin;
fbi->fb.var.vsync_len = inf->vsync_len;
fbi->fb.var.upper_margin = inf->upper_margin;
fbi->fb.var.lower_margin = inf->lower_margin;
fbi->fb.var.sync = inf->sync;
fbi->fb.var.grayscale = inf->cmap_greyscale;
fbi->cmap_inverse = inf->cmap_inverse;
fbi->cmap_static = inf->cmap_static;
fbi->reg.lcdcon1 = inf->reg.lcdcon1;
fbi->reg.lcdcon2 = inf->reg.lcdcon2;
fbi->reg.lcdcon3 = inf->reg.lcdcon3;
fbi->reg.lcdcon4 = inf->reg.lcdcon4;
fbi->reg.lcdcon5 = inf->reg.lcdcon5;
fbi->state = C_STARTUP;
fbi->task_state = (u_char)-1;
fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres *
fbi->max_bpp / 8;
init_waitqueue_head(&fbi->ctrlr_wait);
INIT_WORK(&fbi->task, gzliu_fb_task, fbi);
init_MUTEX(&fbi->ctrlr_sem);
return fbi;
}
// add by
static void s3c2410fb_irq_fifo(int irq, void *dev_id, struct pt_regs *regs)
{
printk("@@@@@@@@@@ s3c2410fb_irq_fifo() @@@@@@@@@@@@\n");
volatile register int a,b;
local_irq_disable();
__raw_writel(__raw_readl(S3C2410_LCDINTMSK)|=3, S3C2410_LCDINTMSK);
__raw_writel(1, S3C2410_LCDSRCPND);
__raw_writel(1, S3C2410_LCDINTPND);
__raw_writel(__raw_readl(S3C2410_LCDINTMSK)&=(~(1)), S3C2410_LCDINTMSK);
b=0;
for(a=0;a<2000;a++)b++;
printk("@@@@@@@@ irq: %d @@@@@@@@@\n", irq);
ClearPending(irq);
}
int __init gzliu_fb_probe(struct device *dev)
{
printk("@@@@@@@@@@ gzliu_fb_probe() @@@@@@@@@@@@\n");
struct gzliu_fb_info *fbi;
struct gzliu_fb_mach_info *inf;
unsigned long flags;
int ret;
printk(KERN_ERR "gzliu_fb_probe start!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
inf = &fs2410_info;//dev->platform_data;
ret = -ENOMEM;
fbi = NULL;
if (!inf)
goto failed;
dev_dbg(dev, "got a %dx%dx%d LCD\n",inf->xres, inf->yres, inf->bpp);
if (inf->xres == 0 || inf->yres == 0 || inf->bpp == 0) {
dev_err(dev, "Invalid resolution or bit depth\n");
ret = -EINVAL;
goto failed;
}
gzliu_fb_backlight_power = inf->gzliu_fb_backlight_power;
gzliu_fb_lcd_power = inf->gzliu_fb_lcd_power;
fbi = gzliu_fb_init_fbinfo(dev);
if (!fbi) {
dev_err(dev, "Failed to initialize framebuffer device\n");
ret = -ENOMEM;
goto failed;
}
/* Initialize video memory */
ret = gzliu_fb_map_video_memory(fbi);
if (ret) {
dev_err(dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto failed;
}
/*
* This makes sure that our colour bitfield
* descriptors are correctly initialised.
*/
gzliu_fb_check_var(&fbi->fb.var, &fbi->fb);
gzliu_fb_set_par(&fbi->fb);
/*
* include/linux/device.h
*
* static inline void dev_set_drvdata (struct device *dev, void *data)
* {
* dev->driver_data = data;
* }
*/
dev_set_drvdata(dev, fbi);
/*
* drivers/vedio/fbmem.c
*
* int register_framebuffer(struct fb_info *fb_info)
* {
* int i;
* struct fb_event event;
*
* if (num_registered_fb == FB_MAX)
* return -ENXIO;
* num_registered_fb++;
* for (i = 0 ; i < FB_MAX; i++)
* if (!registered_fb[i])
* break;
* fb_info->node = i;
*
* fb_info->class_device = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i),
* fb_info->device, "fb%d", i);
* if (IS_ERR(fb_info->class_device)) {
* // Not fatal //
* printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->class_device));
* fb_info->class_device = NULL;
* } else
* fb_init_class_device(fb_info);
*
* if (fb_info->pixmap.addr == NULL) {
* fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
* if (fb_info->pixmap.addr) {
* fb_info->pixmap.size = FBPIXMAPSIZE;
* fb_info->pixmap.buf_align = 1;
* fb_info->pixmap.scan_align = 1;
* fb_info->pixmap.access_align = 4;
* fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
* }
* }
* fb_info->pixmap.offset = 0;
*
* if (!fb_info->modelist.prev ||
* !fb_info->modelist.next ||
* list_empty(&fb_info->modelist)) {
* struct fb_videomode mode;
*
* INIT_LIST_HEAD(&fb_info->modelist);
* fb_var_to_videomode(&mode, &fb_info->var);
* fb_add_videomode(&mode, &fb_info->modelist);
* }
*
* registered_fb[i] = fb_info;
*
* devfs_mk_cdev(MKDEV(FB_MAJOR, i), S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i);
* event.info = fb_info;
* notifier_call_chain(&fb_notifier_list, FB_EVENT_FB_REGISTERED, &event);
* return 0;
* }
*
*/
ret = register_framebuffer(&fbi->fb);
if (ret < 0) {
dev_err(dev, "Failed to register framebuffer device: %d\n", ret);
goto failed;
}
printk("success to register framebuffer device: %d!!!\n", ret);
// add by
disable_irq(IRQ_LCD);
ret = request_irq(IRQ_LCD, s3c2410fb_irq_fifo, SA_INTERRUPT, "LCD", fbi);
if (ret) {
printk(KERN_ERR "s3c2440fb: request_irq failed: %d\n", ret);
goto failed;
}
enable_irq(IRQ_LCD);
/*
* Ok, now enable the LCD controller
*/
set_ctrlr_state(fbi, C_ENABLE);
printk("@@@@@@@ done probe @@@@@@@@\n");
return 0;
failed:
dev_set_drvdata(dev, NULL);
if (fbi)
kfree(fbi);
return ret;
}/* int __init gzliu_fb_probe() */
static struct device_driver gzliu_fb_driver = {
.name = "s3c2410-lcd",
.bus = &platform_bus_type,
.probe = gzliu_fb_probe,
};
int __devinit s3c2410fb_init(void)
{
printk("@@@@@@@@@@ s3c2410fb_init() @@@@@@@@@@@@\n");
int ret;
printk("@@@@@ --- s3c2410fb init --- @@@@@\n");
ret = driver_register(&gzliu_fb_driver);
if(ret)
printk("register device driver failed, return code is %d\n", ret);
__raw_writel(__raw_readl(S3C2410_LCDINTMSK)|=3, S3C2410_LCDINTMSK);
__raw_writel(1, S3C2410_LCDSRCPND);
__raw_writel(1, S3C2410_LCDINTPND);
__raw_writel(__raw_readl(S3C2410_LCDINTMSK)&=(~(1)), S3C2410_LCDINTMSK);
return ret;
}
module_init(s3c2410fb_init);
MODULE_AUTHOR("gzliu_hit@qq.com");
MODULE_DESCRIPTION("framebuffer driver for s3c2440");
MODULE_LICENSE("GPL");
/*
* linux/drivers/video/s3c2410fb.h
*/
#ifndef __GZLIU_FB_H__
#define __GZLIU_FB_H__
/* Shadows for LCD controller registers */
struct gzliu_fb_lcd_reg {
unsigned long lcdcon1;
unsigned long lcdcon2;
unsigned long lcdcon3;
unsigned long lcdcon4;
unsigned long lcdcon5;
unsigned long lcdsaddr1;
unsigned long lcdsaddr2;
unsigned long lcdsaddr3;
};
struct gzliu_fb_info {
struct fb_info fb;
struct device *dev;
u_int max_bpp;
u_int max_xres;
u_int max_yres;
/*
* These are the addresses we mapped
* the framebuffer memory region to.
*/
/* raw memory addresses */
dma_addr_t map_dma; /* physical */
u_char * map_cpu; /* virtual */
u_int map_size;
/* addresses of pieces placed in raw buffer */
u_char * screen_cpu; /* virtual address of frame buffer */
dma_addr_t screen_dma; /* physical address of frame buffer */
u16 * palette_cpu; /* virtual address of palette memory */
dma_addr_t palette_dma; /* physical address of palette memory */
u_int palette_size;
/* DMA descriptors */
dma_addr_t dmadesc_fblow_dma;
dma_addr_t dmadesc_fbhigh_dma;
dma_addr_t dmadesc_palette_dma;
u_int cmap_inverse:1,
cmap_static:1,
unused:30;
volatile u_char state;
volatile u_char task_state;
struct semaphore ctrlr_sem;
wait_queue_head_t ctrlr_wait;
struct work_struct task;
struct gzliu_fb_lcd_reg reg;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
struct notifier_block freq_policy;
#endif
};
struct gzliu_fb_mach_info {
u_long pixclock;
u_short xres;
u_short yres;
u_char bpp;
u_char hsync_len;
u_char left_margin;
u_char right_margin;
u_char vsync_len;
u_char upper_margin;
u_char lower_margin;
u_char sync;
u_int cmap_greyscale:1,
cmap_inverse:1,
cmap_static:1,
unused:29;
struct gzliu_fb_lcd_reg reg;
void (*gzliu_fb_backlight_power)(int);
void (*gzliu_fb_lcd_power)(int);
};
/*
* Debug macros
*/
//#define DEBUG 1
#if DEBUG
# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
#else
# define DPRINTK(fmt, args...)
#endif
#define TO_INF(ptr,member) container_of(ptr,struct gzliu_fb_info,member)
/*
* These are the actions for set_ctrlr_state
*/
#define C_DISABLE (0)
#define C_ENABLE (1)
#define C_DISABLE_CLKCHANGE (2)
#define C_ENABLE_CLKCHANGE (3)
#define C_REENABLE (4)
#define C_DISABLE_PM (5)
#define C_ENABLE_PM (6)
#define C_STARTUP (7)
#define GZLIU_NAME "GZLIU"
/*
* Minimum X and Y resolutions
*/
#define MIN_XRES 64
#define MIN_YRES 64
#ifndef __ASSEMBLY__
#define UData(Data) ((unsigned long) (Data))
#else
#define UData(Data) (Data)
#endif
#define Fld(Size, Shft) (((Size) << 16) + (Shft))
#define FSize(Field) ((Field) >> 16)
#define FShft(Field) ((Field) & 0x0000FFFF)
#define FMsk(Field) (((UData (1) << FSize (Field)) - 1) << FShft (Field))
#define FInsrt(Value, Field) \
(UData (Value) << FShft (Field))
#define fLCD2_LINEVAL Fld(10,14) /* TFT/STN: vertical size of LCD */
#define LCD2_LINEVAL(x) FInsrt((x), fLCD2_LINEVAL)
#define LCD2_LINEVAL_MSK FMsk(fLCD2_LINEVAL)
#define fLCD3_HOZVAL Fld(11,8) /* horizontal size of LCD */
#define LCD3_HOZVAL(x) FInsrt((x), fLCD3_HOZVAL)
#define LCD3_HOZVAL_MSK FMsk(fLCD3_HOZVAL)
#define fLCDADDR_BANK Fld(9,21) /* bank location for video buffer */
#define LCDADDR_BANK(x) FInsrt((x), fLCDADDR_BANK)
#define fLCDADDR_BASEU Fld(21,0) /* address of upper left corner */
#define LCDADDR_BASEU(x) FInsrt((x), fLCDADDR_BASEU)
#define fLCDADDR_BASEL Fld(21,0) /* address of lower right corner */
#define LCDADDR_BASEL(x) FInsrt((x), fLCDADDR_BASEL)
#define fLCDADDR_OFFSET Fld(11,11) /* Virtual screen offset size
(# of half words) */
#define LCDADDR_OFFSET(x) FInsrt((x), fLCDADDR_OFFSET)
#define fLCDADDR_PAGE Fld(11,0) /* Virtual screen page width
(# of half words) */
#define LCDADDR_PAGE(x) FInsrt((x), fLCDADDR_PAGE)
#endif /* __GZLIU_FB_H__ */