- UID
- 852722
|
/*重新激活fb_info中的参数配置*/
static void my2440fb_activate_var(struct fb_info *fbinfo)
{
/*获得结构体变量*/
struct my2440fb_var *fbvar = fbinfo->par;
void __iomem *regs = fbvar->lcd_base;
/*获得fb_info可变参数*/
struct fb_var_screeninfo *var = &fbinfo->var;
/*计算LCD控制寄存器1中的CLKVAL值, 根据数据手册中该寄存器的描述,计算公式如下:
* STN屏:VCLK = HCLK / (CLKVAL * 2), CLKVAL要求>= 2
* TFT屏:VCLK = HCLK / [(CLKVAL + 1) * 2], CLKVAL要求>= 0*/
int clkdiv = my2440fb_calc_pixclk(fbvar, var->pixclock) / 2;
/*获得屏幕的类型*/
int type = fbvar->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
if (type == S3C2410_LCDCON1_TFT)
{
/*根据数据手册按照TFT屏的要求配置LCD控制寄存器1-5*/
my2440fb_config_tft_lcd_regs(fbinfo, &fbvar->regs);
--clkdiv;
if (clkdiv < 0)
{
clkdiv = 0;
}
}
else
{
/*根据数据手册按照STN屏的要求配置LCD控制寄存器1-5*/
my2440fb_config_stn_lcd_regs(fbinfo, &fbvar->regs);
if (clkdiv < 2)
{
clkdiv = 2;
}
}
/*设置计算的LCD控制寄存器1中的CLKVAL值*/
fbvar->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
/*将各参数值写入LCD控制寄存器1-5中*/
writel(fbvar->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, regs + S3C2410_LCDCON1);
writel(fbvar->regs.lcdcon2, regs + S3C2410_LCDCON2);
writel(fbvar->regs.lcdcon3, regs + S3C2410_LCDCON3);
writel(fbvar->regs.lcdcon4, regs + S3C2410_LCDCON4);
writel(fbvar->regs.lcdcon5, regs + S3C2410_LCDCON5);
/*配置帧缓冲起始地址寄存器1-3*/
my2440fb_set_lcdaddr(fbinfo);
fbvar->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
writel(fbvar->regs.lcdcon1, regs + S3C2410_LCDCON1);
}
/*计算LCD控制寄存器1中的CLKVAL值*/
static unsigned int my2440fb_calc_pixclk(struct my2440fb_var *fbvar, unsigned long pixclk)
{
/*获得LCD的时钟*/
unsigned long clk = clk_get_rate(fbvar->lcd_clock);
/* 像素时钟单位是皮秒,而时钟的单位是赫兹,所以计算公式为:
* Hz -> picoseconds is / 10^-12
*/
unsigned long long div = (unsigned long long)clk * pixclk;
div >>= 12; /* div / 2^12 */
do_div(div, 625 * 625UL * 625); /* div / 5^12, do_div宏定义在asm/div64.h中*/
return div;
}
/*根据数据手册按照TFT屏的要求配置LCD控制寄存器1-5*/
static void my2440fb_config_tft_lcd_regs(const struct fb_info *fbinfo, struct s3c2410fb_hw *regs)
{
const struct my2440fb_var *fbvar = fbinfo->par;
const struct fb_var_screeninfo *var = &fbinfo->var;
/*根据色位模式设置LCD控制寄存器1和5,参考数据手册*/
switch (var->bits_per_pixel)
{
case 1:/*1BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
break;
case 2:/*2BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
break;
case 4:/*4BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
break;
case 8:/*8BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
regs->lcdcon5 |= S3C2410_LCDCON5_BSWP | S3C2410_LCDCON5_FRM565;
regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
break;
case 16:/*16BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
break;
case 32:/*32BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP | S3C2410_LCDCON5_HWSWP | S3C2410_LCDCON5_BPP24BL);
break;
default:/*无效的BPP*/
dev_err(fbvar->dev, "invalid bpp %d/n", var->bits_per_pixel);
}
/*设置LCD配置寄存器2、3、4*/
regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
S3C2410_LCDCON3_HOZVAL(var->xres - 1);
regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
}
/*根据数据手册按照STN屏的要求配置LCD控制寄存器1-5*/
static void my2440fb_config_stn_lcd_regs(const struct fb_info *fbinfo, struct s3c2410fb_hw *regs)
{
const struct my2440fb_var *fbvar = fbinfo->par;
const struct fb_var_screeninfo *var = &fbinfo->var;
int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT;
int hs = var->xres >> 2;
unsigned wdly = (var->left_margin >> 4) - 1;
unsigned wlh = (var->hsync_len >> 4) - 1;
if (type != S3C2410_LCDCON1_STN4)
{
hs >>= 1;
}
/*根据色位模式设置LCD控制寄存器1,参考数据手册*/
switch (var->bits_per_pixel)
{
case 1:/*1BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP;
break;
case 2:/*2BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY;
break;
case 4:/*4BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY;
break;
case 8:/*8BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP;
hs *= 3;
break;
case 12:/*12BPP*/
regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP;
hs *= 3;
break;
default:/*无效的BPP*/
dev_err(fbvar->dev, "invalid bpp %d/n", var->bits_per_pixel);
}
/*设置LCD配置寄存器2、3、4, 参考数据手册*/
if (wdly > 3) wdly = 3;
if (wlh > 3) wlh = 3;
regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1);
regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) |
S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) |
S3C2410_LCDCON3_HOZVAL(hs - 1);
regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh);
}
/*配置帧缓冲起始地址寄存器1-3,参考数据手册*/
static void my2440fb_set_lcdaddr(struct fb_info *fbinfo)
{
unsigned long saddr1, saddr2, saddr3;
struct my2440fb_var *fbvar = fbinfo->par;
void __iomem *regs = fbvar->lcd_base;
saddr1 = fbinfo->fix.smem_start >> 1;
saddr2 = fbinfo->fix.smem_start;
saddr2 += fbinfo->fix.line_length * fbinfo->var.yres;
saddr2 >>= 1;
saddr3 = S3C2410_OFFSIZE(0) | S3C2410_PAGEWIDTH((fbinfo->fix.line_length / 2) & 0x3ff);
writel(saddr1, regs + S3C2410_LCDSADDR1);
writel(saddr2, regs + S3C2410_LCDSADDR2);
writel(saddr3, regs + S3C2410_LCDSADDR3);
}
/*显示空白,blank mode有5种模式,定义在fb.h中,是一个枚举*/
static int my2440fb_blank(int blank_mode, struct fb_info *fbinfo)
{
struct my2440fb_var *fbvar = fbinfo->par;
void __iomem *regs = fbvar->lcd_base;
/*根据显示空白的模式来设置LCD是开启还是停止*/
if (blank_mode == FB_BLANK_POWERDOWN)
{
my2440fb_lcd_enable(fbvar, 0);/*在第②步中定义*/
}
else
{
my2440fb_lcd_enable(fbvar, 1);/*在第②步中定义*/
}
/*根据显示空白的模式来控制临时调色板寄存器*/
if (blank_mode == FB_BLANK_UNBLANK)
{
/*临时调色板寄存器无效*/
writel(0x0, regs + S3C2410_TPAL);
}
else
{
/*临时调色板寄存器有效*/
writel(S3C2410_TPAL_EN, regs + S3C2410_TPAL);
}
return 0;
}
/*设置颜色表*/
static int my2440fb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info *fbinfo)
{
unsigned int val;
struct my2440fb_var *fbvar = fbinfo->par;
void __iomem *regs = fbvar->lcd_base;
switch (fbinfo->fix.visual)
{
case FB_VISUAL_TRUECOLOR:
/*真彩色*/
if (regno < 16)
{
u32 *pal = fbinfo->pseudo_palette;
val = chan_to_field(red, &fbinfo->var.red);
val |= chan_to_field(green, &fbinfo->var.green);
val |= chan_to_field(blue, &fbinfo->var.blue);
pal[regno] = val;
}
break;
case FB_VISUAL_PSEUDOCOLOR:
/*伪彩色*/
if (regno < 256)
{
val = (red >> 0) & 0xf800;
val |= (green >> 5) & 0x07e0;
val |= (blue >> 11) & 0x001f;
writel(val, regs + S3C2410_TFTPAL(regno));
/*修改调色板*/
schedule_palette_update(fbvar, regno, val);
}
break;
default:
return 1;
}
return 0;
}
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
/*修改调色板*/
static void schedule_palette_update(struct my2440fb_var *fbvar, unsigned int regno, unsigned int val)
{
unsigned long flags;
unsigned long irqen;
/*LCD中断挂起寄存器基地址*/
void __iomem *lcd_irq_base = fbvar->lcd_base + S3C2410_LCDINTBASE;
/*在修改中断寄存器值之前先屏蔽中断,将中断状态保存到flags中*/
local_irq_save(flags);
fbvar->palette_buffer[regno] = val;
/*判断调色板是否准备就像*/
if (!fbvar->palette_ready)
{
fbvar->palette_ready = 1;
/*使能中断屏蔽寄存器*/
irqen = readl(lcd_irq_base + S3C24XX_LCDINTMSK);
irqen &= ~S3C2410_LCDINT_FRSYNC;
writel(irqen, lcd_irq_base + S3C24XX_LCDINTMSK);
}
/*恢复被屏蔽的中断*/
local_irq_restore(flags);
}
五、从整体上再描述一下FrameBuffer设备驱动实例代码的结构:
1、在第①部分代码中主要做的事情有:
a.将LCD设备注册到系统平台设备中;
b.定义LCD平台设备结构体lcd_fb_driver。
2、在第②部分代码中主要做的事情有:
a.获取和设置LCD平台设备的各种资源;
b.分配fb_info结构体空间;
c.初始化fb_info结构体中的各参数;
d.初始化LCD控制器;
e.检查fb_info中可变参数;
f.申请帧缓冲设备的显示缓冲区空间;
g.注册fb_info。
3、在第③部分代码中主要做的事情有:
a.实现对fb_info相关参数进行检查的硬件接口函数;
b.实现对LCD显示模式进行设定的硬件接口函数;
c.实现对LCD显示开关(空白)的硬件接口函数等。
void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
{
struct s3c2410fb_mach_info *npd;
npd = kmalloc(sizeof(*npd), GFP_KERNEL);
if (npd) {
memcpy(npd, pd, sizeof(*npd));
//这里就是将内核中定义的s3c2410fb_mach_info结构体数据保存到LCD平台数据中,所以在写驱动的时候就可以直接在平台数据中获取s3c2410fb_mach_info结构体的数据(即LCD各种参数信息)进行操作
s3c_device_lcd.dev.platform_data = npd;
} else {
printk(KERN_ERR "no memory for LCD platform data/n");
}
}
|
//S3C2440初始化函数
static void __init smdk2440_machine_init(void)
{
//调用该函数将上面定义的LCD硬件信息保存到平台数据中
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
|
/* LCD Controller */
//LCD控制器的资源信息
static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C24XX_PA_LCD, //控制器IO端口开始地址
.end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,//控制器IO端口结束地址
.flags = IORESOURCE_MEM,//标识为LCD控制器IO端口,在驱动中引用这个就表示引用IO端口
},
[1] = {
.start = IRQ_LCD,//LCD中断
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,//标识为LCD中断
}
};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",//作为平台设备的LCD设备名
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),//资源数量
.resource = s3c_lcd_resource,//引用上面定义的资源
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
EXPORT_SYMBOL(s3c_device_lcd);//导出定义的LCD平台设备,好在mach-smdk2440.c的smdk2440_devices[]中添加到平台设备列表中
|
struct fb_info {
int node;
int flags;
struct fb_var_screeninfo var;/*LCD可变参数结构体*/
struct fb_fix_screeninfo fix;/*LCD固定参数结构体*/
struct fb_monspecs monspecs; /*LCD显示器标准*/
struct work_struct queue; /*帧缓冲事件队列*/
struct fb_pixmap pixmap; /*图像硬件mapper*/
struct fb_pixmap sprite; /*光标硬件mapper*/
struct fb_cmap cmap; /*当前的颜色表*/
struct fb_videomode *mode; /*当前的显示模式*/
#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev;/*对应的背光设备*/
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];/*背光调整*/
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops; /*对底层硬件操作的函数指针*/
struct device *device;
struct device *dev; /*fb设备*/
int class_flag;
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /*图块Blitting*/
#endif
char __iomem *screen_base; /*虚拟基地址*/
unsigned long screen_size; /*LCD IO映射的虚拟内存大小*/
void *pseudo_palette; /*伪16色颜色表*/
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /*LCD的挂起或恢复状态*/
void *fbcon_par;
void *par;
};
| 其中,比较重要的成员有struct fb_var_screeninfo var、struct fb_fix_screeninfo fix和struct fb_ops *fbops,他们也都是结构体。下面我们一个一个的来看。
fb_var_screeninfo结构体主要记录用户可以修改的控制器的参数,比如屏幕的分辨率和每个像素的比特数等,该结构体定义如下:
而fb_fix_screeninfo结构体又主要记录用户不可以修改的控制器的参数,比如屏幕缓冲区的物理地址和长度等,该结构体的定义如下:
struct fb_fix_screeninfo {
char id[16]; /*字符串形式的标示符 */
unsigned long smem_start; /*fb缓存的开始位置 */
__u32 smem_len; /*fb缓存的长度 */
__u32 type; /*看FB_TYPE_* */
__u32 type_aux; /*分界*/
__u32 visual; /*看FB_VISUAL_* */
__u16 xpanstep; /*如果没有硬件panning就赋值为0 */
__u16 ypanstep; /*如果没有硬件panning就赋值为0 */
__u16 ywrapstep; /*如果没有硬件ywrap就赋值为0 */
__u32 line_length; /*一行的字节数 */
unsigned long mmio_start; /*内存映射IO的开始位置*/
__u32 mmio_len; /*内存映射IO的长度*/
__u32 accel;
__u16 reserved[3]; /*保留*/
};
|
struct fb_var_screeninfo {
__u32 xres; /*可见屏幕一行有多少个像素点*/
__u32 yres; /*可见屏幕一列有多少个像素点*/
__u32 xres_virtual; /*虚拟屏幕一行有多少个像素点*/
__u32 yres_virtual; /*虚拟屏幕一列有多少个像素点*/
__u32 xoffset; /*虚拟到可见屏幕之间的行偏移*/
__u32 yoffset; /*虚拟到可见屏幕之间的列偏移*/
__u32 bits_per_pixel; /*每个像素的位数即BPP*/
__u32 grayscale; /*非0时,指的是灰度*/
struct fb_bitfield red; /*fb缓存的R位域*/
struct fb_bitfield green; /*fb缓存的G位域*/
struct fb_bitfield blue; /*fb缓存的B位域*/
struct fb_bitfield transp; /*透明度*/
__u32 nonstd; /* != 0 非标准像素格式*/
__u32 activate;
__u32 height; /*高度*/
__u32 width; /*宽度*/
__u32 accel_flags;
/*定时:除了pixclock本身外,其他的都以像素时钟为单位*/
__u32 pixclock; /*像素时钟(皮秒)*/
__u32 left_margin; /*行切换,从同步到绘图之间的延迟*/
__u32 right_margin; /*行切换,从绘图到同步之间的延迟*/
__u32 upper_margin; /*帧切换,从同步到绘图之间的延迟*/
__u32 lower_margin; /*帧切换,从绘图到同步之间的延迟*/
__u32 hsync_len; /*水平同步的长度*/
__u32 vsync_len; /*垂直同步的长度*/
__u32 sync;
__u32 vmode;
__u32 rotate;
__u32 reserved[5]; /*保留*/
};
|
|
|