您的位置:首页 > 运维架构 > Linux

Linux-2.6.38的LCD驱动分析(三)

2013-12-24 10:35 357 查看
三、解剖s3cfb_driver变量

s3cfb_driver变量有什么作用呢?在前面的2.2节提到了它的定义,从它的原型可以看出s3cfb_driver是个platform_driver类型的变量,前面的几个小节提到了从platform_driver的名字可以看出它应该是platform_device的驱动类型。为了方便阅读,这里再贴一次s3cfb_driver的定义:

static struct platform_driver s3cfb_driver = {

.probe = s3cfb_probe,

.remove = s3cfb_remove,

.suspend = s3cfb_suspend,

.resume = s3cfb_resume,

.driver = {

.name = "s3c-fb",

.owner = THIS_MODULE,

},

};

从定义可以看出,该platform_device的驱动函数有s3cfb_probe,s3cfb_remove,s3cfb_suspend和s3cfb_suspend。.resource成员前面的章节有说明,.driver成员的值相信不用再说明了吧,再明白不过了。前面的章节,s3cfb_probe被比较详细的介绍,这节中的主要任务就是解释其他的几个函数。在解释他们之前,s3cfb_probe里面在该函数结尾的时候调用了几个函数没有说到,所以在这里补上。



3.1 s3cfb_probe余党

在s3cfb_probe中最好调用了s3cfb_init_registers和s3cfb_check_var函数,这里应该将他们交代清楚。很显然,s3cfb_init_registers是初始化相关寄存器。那么后者呢?这里先把s3cfb_init_registers搞定再说。在文件drivers\video\samsung\s3cfb_fimd4x.c中,s3cfb_init_registers的定义与实现如下,先根据它的指向流程,一步一步解释:

int s3cfb_init_registers(s3cfb_info_t *fbi)

{

struct clk *lcd_clock;

struct fb_var_screeninfo *var = &fbi->fb.var;

unsigned long flags = 0, page_width = 0, offset = 0;

unsigned long video_phy_temp_f1 = fbi->screen_dma_f1;

unsigned long video_phy_temp_f2 = fbi->screen_dma_f2;

int win_num = fbi->win_id;

/* Initialise LCD with values from hare */

local_irq_save(flags);

/* 关闭中断,在关闭中断前,中断的当前状态被保存在flags中,对于关闭中断的函数,linux内核有很多种,可以查阅相关的资料。*/

page_width = var->xres * s3cfb_fimd.bytes_per_pixel;

offset = (var->xres_virtual - var->xres) * s3cfb_fimd.bytes_per_pixel;

if (win_num == 0) {

s3cfb_fimd.vidcon0 = s3cfb_fimd.vidcon0 & ~(S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);

writel(s3cfb_fimd.vidcon0, S3C_VIDCON0);

lcd_clock = clk_get(NULL, "lcd");

s3cfb_fimd.vidcon0 |= S3C_VIDCON0_CLKVAL_F((int) (s3cfb_fimd.pixclock));

#if defined(CONFIG_FB_S3C_EXT_VIRTUAL_SCREEN)

offset = 0;

s3cfb_fimd.vidw00add0b0 = video_phy_temp_f1;

s3cfb_fimd.vidw00add0b1 = video_phy_temp_f2;

s3cfb_fimd.vidw00add1b0 = S3C_VIDWxxADD1_VBASEL_F((unsigned long) video_phy_temp_f1 + (page_width + offset) * (var->yres));

s3cfb_fimd.vidw00add1b1 = S3C_VIDWxxADD1_VBASEL_F((unsigned long) video_phy_temp_f2 + (page_width + offset) * (var->yres));

#endif

}

writel(video_phy_temp_f1, S3C_VIDW00ADD0B0 + (0x08 * win_num));

writel(S3C_VIDWxxADD1_VBASEL_F((unsigned long) video_phy_temp_f1 + (page_width + offset) * (var->yres)), S3C_VIDW00ADD1B0 + (0x08 * win_num));

writel(S3C_VIDWxxADD2_OFFSIZE_F(offset) | (S3C_VIDWxxADD2_PAGEWIDTH_F(page_width)), S3C_VIDW00ADD2 + (0x04 * win_num));

if (win_num < 2) {

writel(video_phy_temp_f2, S3C_VIDW00ADD0B1 + (0x08 * win_num));

writel(S3C_VIDWxxADD1_VBASEL_F((unsigned long) video_phy_temp_f2 + (page_width + offset) * (var->yres)), S3C_VIDW00ADD1B1 + (0x08 * win_num));

}

下面的几个writel函数开始初始化LCD控制寄存器

switch (win_num) {

case 0://window0

writel(s3cfb_fimd.wincon0, S3C_WINCON0);

writel(s3cfb_fimd.vidcon0, S3C_VIDCON0);

writel(s3cfb_fimd.vidcon1, S3C_VIDCON1);

writel(s3cfb_fimd.vidtcon0, S3C_VIDTCON0);

writel(s3cfb_fimd.vidtcon1, S3C_VIDTCON1);

writel(s3cfb_fimd.vidtcon2, S3C_VIDTCON2);

writel(s3cfb_fimd.dithmode, S3C_DITHMODE);

writel(s3cfb_fimd.vidintcon0, S3C_VIDINTCON0);

writel(s3cfb_fimd.vidintcon1, S3C_VIDINTCON1);

writel(s3cfb_fimd.vidosd0a, S3C_VIDOSD0A);

writel(s3cfb_fimd.vidosd0b, S3C_VIDOSD0B);

writel(s3cfb_fimd.vidosd0c, S3C_VIDOSD0C);

writel(s3cfb_fimd.wpalcon, S3C_WPALCON);

s3cfb_onoff_win(fbi, ON);

break;

case 1://window1

writel(s3cfb_fimd.wincon1, S3C_WINCON1);

writel(s3cfb_fimd.vidosd1a, S3C_VIDOSD1A);

writel(s3cfb_fimd.vidosd1b, S3C_VIDOSD1B);

writel(s3cfb_fimd.vidosd1c, S3C_VIDOSD1C);

writel(s3cfb_fimd.vidosd1d, S3C_VIDOSD1D);

writel(s3cfb_fimd.wpalcon, S3C_WPALCON);

s3cfb_onoff_win(fbi, OFF);

break;

case 2://window2

writel(s3cfb_fimd.wincon2, S3C_WINCON2);

writel(s3cfb_fimd.vidosd2a, S3C_VIDOSD2A);

writel(s3cfb_fimd.vidosd2b, S3C_VIDOSD2B);

writel(s3cfb_fimd.vidosd2c, S3C_VIDOSD2C);

writel(s3cfb_fimd.vidosd2d, S3C_VIDOSD2D);

writel(s3cfb_fimd.wpalcon, S3C_WPALCON);

s3cfb_onoff_win(fbi, OFF);

break;

case 3://window3

writel(s3cfb_fimd.wincon3, S3C_WINCON3);

writel(s3cfb_fimd.vidosd3a, S3C_VIDOSD3A);

writel(s3cfb_fimd.vidosd3b, S3C_VIDOSD3B);

writel(s3cfb_fimd.vidosd3c, S3C_VIDOSD3C);

writel(s3cfb_fimd.wpalcon, S3C_WPALCON);

s3cfb_onoff_win(fbi, OFF);

break;

case 4://window4

writel(s3cfb_fimd.wincon4, S3C_WINCON4);

writel(s3cfb_fimd.vidosd4a, S3C_VIDOSD4A);

writel(s3cfb_fimd.vidosd4b, S3C_VIDOSD4B);

writel(s3cfb_fimd.vidosd4c, S3C_VIDOSD4C);

writel(s3cfb_fimd.wpalcon, S3C_WPALCON);

s3cfb_onoff_win(fbi, OFF);

break;

}

local_irq_restore(flags);

//使能中断,并恢复以前的状态

return 0;

}



OK,s3cfb_init_registers就简单介绍到这里。下面看看s3cfb_check_var函数要干些什么事,要说到这个函数,还得提到fb_var_screeninfo结构类型,与它对应的是fb_fix_screeninfo结构类型。这两个类型分别代表了显示屏的属性信息,这些信息可以分为可变属性信息(如:颜色深度,分辨率等)和不可变的信息(如帧缓冲的其实地址)。既然fb_var_screeninfo表示了可变的属下信息,那么这些可变信息就应该有一定范围,否则显示就会出问题,所以s3cfb_check_var函数的功能就是要在LCD的帧缓冲驱动开始运行之前将这些值初始到合法的范围内。知道了s3cfb_check_var要做什么,再去阅读s3cfb_check_var函数的代码就没什么问题了。



3.2 s3cfb_remove

从这里开始将解释s3cfb_driver中的其他几个函数。那么就从s3cfb_remove开刀吧!顾名思义该函数就该知道,它要将这个platform设备从系统中移除,可以推测它应该释放掉所有的资源,包括内存空间,中断线等等。还是按照惯例,在它的实现代码中一步步的解释。\

static int s3cfb_remove(struct platform_device *pdev)

{

struct fb_info *fbinfo = platform_get_drvdata(pdev);该函数从platform_device中,获取fb_info信息

s3cfb_info_t *info = fbinfo->par;得到私有数据

int index = 0, irq;

s3cfb_stop_lcd();停止LCD控制器

msleep(1);休息以下,等待LCD停止

停止时钟
if (info->clk) {

clk_disable(info->clk);

clk_put(info->clk);

info->clk = NULL;

}

irq = platform_get_irq(pdev, 0);得到中断线,以便释放

release_resource(info->mem);释放内存空间

for (index = 0; index < S3CFB_NUM; index++) {

s3cfb_unmap_video_memory((s3cfb_info_t *) &s3cfb_info[index]);释放缓冲区

free_irq(irq, &s3cfb_info[index]);释放该中断

unregister_framebuffer(&info[index].fb);向内核注销该帧缓冲

}

return 0;

}

3.3 s3cfb_suspend与s3cfb_resume

在实际的设备,常常可以看到LCD在不需要的时候进入休眠状态,当需要使用的时候又开始工作,比如手机,在不需要的时候LCD就熄灭,当需要使用的时候LCD又被点亮。从实际中可以看出这对函数非常重要。虽然他们很重要,但不一定很复杂,下面看看它们是怎么样实现的。



int s3cfb_suspend(struct platform_device *dev, pm_message_t state)

{

struct fb_info *fbinfo = platform_get_drvdata(dev);//获取帧缓冲设备数据结构

s3cfb_info_t *info = fbinfo->par;

s3cfb_stop_lcd();停止LCD

s3c6410_pm_do_save(s3c_lcd_save, ARRAY_SIZE(s3c_lcd_save));保存当前LCD寄存器等的信息

/* sleep before disabling the clock, we need to ensure

* the LCD DMA engine is not going to get back on the bus

* before the clock goes off again (bjd) */

msleep(1);//等待一下,因为LCD停止需要一点时间

clk_disable(info->clk);//关闭LCD的时钟

return 0;

}

下面来看看s3cfb_resume

int s3cfb_resume(struct platform_device *dev)

{

struct fb_info *fbinfo = platform_get_drvdata(dev);

s3cfb_info_t *info = fbinfo->par;

clk_enable(info->clk);

s3c6410_pm_do_restore(s3c_lcd_save, ARRAY_SIZE(s3c_lcd_save));恢复LCD寄存器值

s3cfb_set_gpio();从新设置LCD GPIO

s3cfb_start_lcd();使能LCD

return 0;

}



OK,到现在为止,对于platform device的相关驱动就over了。不过精彩的还在后头哦!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: