LCD驱动源码分析(s3cfb.c)
Posted 正在起飞的蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCD驱动源码分析(s3cfb.c)相关的知识,希望对你有一定的参考价值。
1、驱动源码分析大致思路
(1)分析LCD驱动首先需要分析内核的帧缓冲子系统,因为LCD驱动就是按照帧缓冲子系统提供的注册接口来注册的;
(2)内核帧缓冲子系统参考博客:《Linux 帧缓冲子系统详解:LCD介绍、framebuffer驱动框架、LCD驱动源码分析》;
(3)先分析LCD驱动的加载和卸载;
(4)梳理清楚应用是如果调用到LCD驱动的;
(5)LCD驱动的源码大致分为两部分:一部分是Soc的LCD控制器,一方面是LCD本身,两者都是去查阅数据手册;
(6)LCD和Soc是通过gpio口连接的,分析LCD源码还涉及内核的gpio子系统,参考博客:《2.6.35内核的gpio子系统详解》;
2、应用层、帧缓冲子系统、LCD驱动三者的联系
(1)应用层通过操作设备节点"/dev/fbn"来操作LCD屏幕;
(2)应用层操作设备节点,首先是调用到帧缓冲子系统的struct file_operations操作方法;
(3)设备节点里包含主次设备号,帧缓冲系统先根据主次设备号找到对应注册过的驱动;
(4)最后调用驱动的struct fb_ops结构体里的方法;
补充:《应用程序操作LCD源码分析》;
3、LCD驱动的加载
static struct platform_driver s3cfb_driver =
.probe = s3cfb_probe,
.remove = __devexit_p(s3cfb_remove),
.driver =
.name = S3CFB_NAME,
.owner = THIS_MODULE,
,
;
static int __init s3cfb_register(void)
//向platform平台总线注册驱动
platform_driver_register(&s3cfb_driver);
return 0;
static void __exit s3cfb_unregister(void)
//向platform平台总线卸载驱动
platform_driver_unregister(&s3cfb_driver);
module_init(s3cfb_register);
module_exit(s3cfb_unregister);
(1)LCD驱动是利用平台总线进行注册的,在内核启动时也会注册相应的LCD的平台设备,当驱动和设备匹配就会执行驱动的probe函数;
(2)平台总线相关知识参考博客:《内核platform总线详解:定义、注册、匹配、使用示例》;
4、LCD驱动的prob函数分析
static int __devinit s3cfb_probe(struct platform_device *pdev)
struct s3c_platform_fb *pdata;
struct s3cfb_global *fbdev;
struct resource *res;
int i, j, ret = 0;
//分配s3cfb_global结构体大小的内存
fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);
if (!fbdev)
dev_err(&pdev->dev, "failed to allocate for "
"global fb structure\\n");
ret = -ENOMEM;
goto err_global;
//保存设备结构体
fbdev->dev = &pdev->dev;
//电流整流:和电源管理有关,实现低功耗
fbdev->regulator = regulator_get(&pdev->dev, "pd");
if (!fbdev->regulator)
dev_err(fbdev->dev, "failed to get regulator\\n");
ret = -EINVAL;
goto err_regulator;
ret = regulator_enable(fbdev->regulator);
if (ret < 0)
dev_err(fbdev->dev, "failed to enable regulator\\n");
ret = -EINVAL;
goto err_regulator;
//获取platform设备传过来的私有数据
pdata = to_fb_plat(&pdev->dev);
if (!pdata)
dev_err(fbdev->dev, "failed to get platform data\\n");
ret = -EINVAL;
goto err_pdata;
fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;
//设置LCD相关的引脚
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdev);
//打开LCD的时钟
if (pdata->clk_on)
pdata->clk_on(pdev, &fbdev->clock);
//获取设备IORESOURCE_MEM类的资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
dev_err(fbdev->dev, "failed to get io memory region\\n");
ret = -EINVAL;
goto err_io;
/***************动态映射*****************/
//向内核申请地址资源
res = request_mem_region(res->start,
res->end - res->start + 1, pdev->name);
if (!res)
dev_err(fbdev->dev, "failed to request io memory region\\n");
ret = -EINVAL;
goto err_io;
//IO地址动态映射
fbdev->regs = ioremap(res->start, res->end - res->start + 1);
if (!fbdev->regs)
dev_err(fbdev->dev, "failed to remap io region\\n");
ret = -EINVAL;
goto err_mem;
//开启相关中断,是设置的LCD控制器的寄存器
s3cfb_set_vsync_interrupt(fbdev, 1);
s3cfb_set_global_interrupt(fbdev, 1);
//根据设备传过来的数据,设置LCD控制器
s3cfb_init_global(fbdev);
//构建struct fb_info结构体
if (s3cfb_alloc_framebuffer(fbdev))
ret = -ENOMEM;
goto err_alloc;
//将struct fb_info结构体注册到帧缓冲子系统中
if (s3cfb_register_framebuffer(fbdev))
ret = -EINVAL;
goto err_register;
//设置LCD控制器的时钟
s3cfb_set_clock(fbdev);
//打开默认的显示屏
s3cfb_set_window(fbdev, pdata->default_win, 1);
//打开屏幕的显示功能,到现在才允许视频输出到屏幕
s3cfb_display_on(fbdev);
//申请中断号
fbdev->irq = platform_get_irq(pdev, 0);
if (request_irq(fbdev->irq, s3cfb_irq_frame, IRQF_SHARED,
pdev->name, fbdev))
dev_err(fbdev->dev, "request_irq failed\\n");
ret = -EINVAL;
goto err_irq;
#ifdef CONFIG_FB_S3C_LCD_INIT
if (pdata->backlight_on)
pdata->backlight_on(pdev);
if (!bootloaderfb && pdata->reset_lcd)
pdata->reset_lcd(pdev);
#endif
//和电源管理有关
#ifdef CONFIG_HAS_EARLYSUSPEND
fbdev->early_suspend.suspend = s3cfb_early_suspend;
fbdev->early_suspend.resume = s3cfb_late_resume;
fbdev->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
register_early_suspend(&fbdev->early_suspend);
#endif
//给设备创建属性文件
ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
if (ret < 0)
dev_err(fbdev->dev, "failed to add sysfs entries\\n");
dev_info(fbdev->dev, "registered successfully\\n");
//这是显示内核开机的logo
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR))
printk("Start display and show logo\\n");
/* Start display and show logo on boot */
fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]);
fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR);
#endif
mdelay(100);
//点亮屏幕的背光
if (pdata->backlight_on)
pdata->backlight_on(pdev);
return 0;
err_irq:
s3cfb_display_off(fbdev);
s3cfb_set_window(fbdev, pdata->default_win, 0);
for (i = pdata->default_win;
i < pdata->nr_wins + pdata->default_win; i++)
j = i % pdata->nr_wins;
unregister_framebuffer(fbdev->fb[j]);
err_register:
for (i = 0; i < pdata->nr_wins; i++)
if (i == pdata->default_win)
s3cfb_unmap_default_video_memory(fbdev->fb[i]);
framebuffer_release(fbdev->fb[i]);
kfree(fbdev->fb);
err_alloc:
iounmap(fbdev->regs);
err_mem:
release_mem_region(res->start,
res->end - res->start + 1);
err_io:
pdata->clk_off(pdev, &fbdev->clock);
err_pdata:
regulator_disable(fbdev->regulator);
err_regulator:
kfree(fbdev);
err_global:
return ret;
(1)解析出平台设备传过来的数据;
(2)设置LCD相关的gpio引脚,设置好时钟;
(3)对LCD相关的寄存器地址进行动态映射;
(4)根据平台设备传过来的数据,设置Soc的LCD控制器相关寄存器;
(5)构建struct fb_info结构体,最重要的是struct fb_ops结构体,这是LCD屏幕的具体硬件操作方法,然后注册到帧缓冲子系统中;
(6)打开默认的显示屏。一个LCD可以对应对个虚拟屏幕;
(7)给设备创建属性文件,设备属于"graphics"类,在"/sys/class/graphics"目录下可以看到;
(8)显示内核的启动logo,打开屏幕的背光,真正显示出启动logo;
以上是关于LCD驱动源码分析(s3cfb.c)的主要内容,如果未能解决你的问题,请参考以下文章
《Linux驱动》iTop4412开发板LCD驱动 详细分析
(VKL系列)超低功耗LCD液晶显示驱动IC-VKL076 SSOP28,19*4 76点阵,超低工作电流约7.5微安,适用水电表/温湿度计/温控器/传感器等,FAE技术支持