Linux-2.6.38的LCD驱动分析(三)
参考自:http://blog.chinaunix.net/uid-11114210-id-2907026.html
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe,
.remove = s3cfb_remove,
.suspend = s3cfb_suspend,
.resume = s3cfb_resume,
.driver = {
{
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;
}
{
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); 得到中断线,以便释放
for (index = 0; index < S3CFB_NUM; index++) {
s3cfb_unmap_video_memory((s3cfb_info_t *) &s3cfb_info[index]);释放缓冲区
unregister_framebuffer(&info[index].fb);向内核注销该帧缓冲
return 0;
}
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;
}
{
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;
}