Chinaunix首页 | 论坛 | 博客
  • 博客访问: 57076
  • 博文数量: 9
  • 博客积分: 227
  • 博客等级: 二等列兵
  • 技术积分: 110
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-19 02:11
文章分类
文章存档

2012年(9)

分类: LINUX

2012-04-22 11:35:55

③、帧缓冲设备驱动对底层硬件操作的函数接口实现(即:my2440fb_ops的实现)

/*Framebuffer底层硬件操作各接口函数*/
static struct fb_ops my2440fb_ops =
{
.owner = THIS_MODULE,
.fb_check_var = my2440fb_check_var,/*步中已实现*/
.fb_set_par = my2440fb_set_par,/*设置fb_info中的参数,主要是LCD的显示模式*/
.fb_blank = my2440fb_blank,/*显示空白(即:LCD开关控制)*/
.fb_setcolreg = my2440fb_setcolreg,/*设置颜色表*/
/*以下三个函数是可选的,主要是提供fb_console的支持,在内核中已经实现,这里直接调用即可*/
.fb_fillrect = cfb_fillrect,/*定义在drivers/video/cfbfillrect.c*/
.fb_copyarea = cfb_copyarea,/*定义在drivers/video/cfbcopyarea.c*/
.fb_imageblit = cfb_imageblit,/*定义在drivers/video/cfbimgblt.c*/
};

/*设置fb_info中的参数,这里根据用户设置的可变参数var调整固定参数fix*/
static int my2440fb_set_par(struct fb_info *fbinfo)
{
/*获得fb_info中的可变参数*/
struct fb_var_screeninfo *var = &fbinfo->var;

/*判断可变参数中的色位模式,根据色位模式来设置色彩模式*/
switch (var->bits_per_pixel)
{
case 32:
case 16:
case 12:/*12BPP时,设置为真彩色(分成红、绿、蓝三基色)*/
fbinfo
->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 1:/*1BPP时,设置为黑白色(分黑、白两种色,FB_VISUAL_MONO01代表黑,FB_VISUAL_MONO10代表白)*/
fbinfo
->fix.visual = FB_VISUAL_MONO01;
break;
default:/*默认设置为伪彩色,采用索引颜色显示*/
fbinfo
->fix.visual = FB_VISUAL_PSEUDOCOLOR;
break;
}

/*设置fb_info中固定参数中一行的字节数,公式:1行字节数=(1行像素个数*每像素位数BPP)/8 */
fbinfo
->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;

/*修改以上参数后,重新激活fb_info中的参数配置(即:使修改后的参数在硬件上生效)*/
my2440fb_activate_var
(fbinfo);

return 0;
}

/*重新激活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控制寄存器15,参考数据手册*/
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配置寄存器234*/
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配置寄存器234, 参考数据手册*/
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 mode5种模式,定义在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显示开关(空白)的硬件接口函数等。

 

阅读(2992) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~