(若转载,请注明出处,若有错误请指正,谢谢)
(以下分析皆基于:itop4412精英板设备和代码资源)
(内核为:iTop4412_Kernel_3.0提供)
(看客需要一定的linux平台驱动基础,和lcd操作基础)
(针对lcd基本操作,我准备写一个lcd裸机编程来完善)
lcd的工作,在kernel 中有device 和driver两个描述,这也是必然。
在 分析一中我们看了device,现在我们来看看driver。
注:在驱动分析中,我们会在数据手册和原理图中查看。
首先来看看lcd驱动框架(ps 该框图来自网上)
由上图可以看出 lcd的应用层 通过 内核的fbmem接口 再调用驱动xxxfb.c的内容
而fbmem接口是内核提供的,所有驱动设计人员主要的任务就是定义一个fb_info 结构体(该结构由内核提供),然后填充结构体中的内容做好相应的初始化后,提交给内核就可以了。
//———————————-
首先驱动文件在: 在kernel/drivers/video/samsung/目录下。我现在用的设备涉及的驱动如下:
(ps:这里以itop 提供的4.3寸WXCAT43屏为参考,(主要贵的买不起的哇%>_<%) )
s3cfb_wa101s.c ——设备参数相关文件
s3cfb_fimd6x.c —— 具体平台硬件相关文件
s3cfb_ops.c ——-fb 操作集合
s3cfb_main.c ——-驱动框架
好,首先来,分析s3cfb_main.c ,当然在介绍该.c时,会把之前所有的.c都牵扯进来,毕竟驱动框架调用了其他c文件的接口。(这一节主要介绍probe函数调用的接口功能,接口具体实现在下一节中介绍)
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe, //探测设备
.remove = s3cfb_remove,//设备移除
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = s3cfb_suspend,//设备暂定
.resume = s3cfb_resume, //设备恢复
#endif
.driver = {
.name = S3CFB_NAME, //设备名字,在s3cfb.h 当中定义,与device匹配的关键,定义如下
//#define S3CFB_NAME "s3cfb"
.owner = THIS_MODULE,
#ifdef CONFIG_EXYNOS_DEV_PD
.pm = &s3cfb_pm_ops, //pm电源管理集合
#endif
},
};
static int s3cfb_register(void)
{
platform_driver_register(&s3cfb_driver); 平台设备的注册
return 0;
}
static void s3cfb_unregister(void)
{
platform_driver_unregister(&s3cfb_driver);平台设备的卸载
}
module_init(s3cfb_register);
module_exit(s3cfb_unregister);
s3cfb_probe 是重点 ,现在着重介绍:static int s3cfb_probe(struct platform_device *pdev);
probe函数的主要作用是获取device资源,获取设备资源,初始化设备,注册和使能设备等
里面涉及的结构体将在必要时介绍。下面是probe函数代码简化,我把一些出错判断和调试信息等删除了以便分析.
下面是我简单的画的一个数据结构体指向草图,希望对你有帮助。
// 现在来看看probe框架,里面函数具体介绍放在probe框架介绍之后
static int s3cfb_probe(struct platform_device *pdev)
{
struct s3c_platform_fb *pdata = NULL; //s3c平台资源结构体
struct resource *res = NULL; //资源指针
struct s3cfb_global *fbdev[2]; //lcd driver全局指针结构体指针,是分析驱动的核心指针
#ifdef CONFIG_EXYNOS_DEV_PD
/* to use the runtime PM helper functions */
pm_runtime_enable(&pdev->dev);
/* enable the power domain */
pm_runtime_get_sync(&pdev->dev);
#endif
#ifndef CONFIG_TC4_EVT
lcd_regulator = regulator_get(NULL, "vdd33_lcd");
regulator_enable(lcd_regulator); //yulu
#endif
/*----------------------------------------------*/
pm_runtime_enable ,pm_runtime_get_sysnc 是运行时电源管理(运行时PM)的支持,是在电源管理的核心(PM core)下借助于以下方式实现的。regular_get ,regular_enable 是电源管理制regulator 机制。现在android/linux为模块设备供电有两种,一种GPIO供电,另一个就是电源管理芯片。电源管理芯片可以为多设备供电,且这些设备电压电流有所同。为这些设备提供的稳压器代码模型即为regulator。
这里不做详细要求,如有兴趣请查阅相关资料
/*-----------------------------------------------*/
fbfimd = kzalloc(sizeof(struct s3cfb_fimd_desc), GFP_KERNEL);//创建fimd 设备描述
if (FIMD_MAX == 2)// 接入设备dual 判断,这里显然只有一个,这个宏在s3cfb.h 中
fbfimd->dual = 1;
else
fbfimd->dual = 0;
for (i = 0; i < FIMD_MAX; i++) {//根据要描述的fimd,进行分配空间,初始化等操作
/* global structure */ //分配全局数据区,通过上面的数据结构体可以很清晰看出来
fbfimd->fbdev[i] = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);
fbdev[i] = fbfimd->fbdev[i];
fbdev[i]->dev = &pdev->dev; //让global指向了platform 的设备,也就是
//上一节中介绍的平台设备描述,struct platform_device s3c_device_fb 里面有设备的io
//中断资源等
//这个函数是获取具体设备的参数的,跟s3cfb_wa101s.c有关
// 在这个函数中我们看到如何通过硬件拨码开关选择lcd硬件
//讲在下面介绍
s3cfb_set_lcd_info(fbdev[i]);
/* platform_data */
//通过这个函数获得上一篇中介绍的上s3c_platform_fb
//现在让我们回顾一下 在dev-fimd-s5p.c 中有 s3cfb_set_platdat 函数中
/*
struct s3c_platform_fb *npd;........
s3cfb_get_clk_name(npd->clk_name);
npd->cfg_gpio = s3cfb_cfg_gpio;
npd->backlight_on = s3cfb_backlight_on;
npd->backlight_off = s3cfb_backlight_off;
npd->lcd_on = s3cfb_lcd_on;
npd->lcd_off = s3cfb_lcd_off;
npd->clk_on = s3cfb_clk_on;
npd->clk_off = s3cfb_clk_off;
*/
//现在知道作用了吧
pdata = to_fb_plat(&pdev->dev);
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdev);//初始化io,将在下面介绍
if (pdata->clk_on)
pdata->clk_on(pdev, &fbdev[i]->clock);//初始化时钟
//将在下面介绍
/* io memory */
//从平台设备中获取io资源,申请io资源,映射io资源
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
res = request_mem_region(res->start,
res->end - res->start + 1, pdev->name);
fbdev[i]->regs = ioremap(res->start, res->end - res->start + 1);
/* irq */ 从平台驱动获取frame中断
fbdev[i]->irq = platform_get_irq(pdev, 0);
request_irq(fbdev[i]->irq, s3cfb_irq_frame, IRQF_SHARED,pdev->name, fbdev[i])
// 如有必要获取fifo中断
#ifdef CONFIG_FB_S5P_TRACE_UNDERRUN
if (request_irq(platform_get_irq(pdev, 1), s3cfb_irq_fifo,IRQF_DISABLED, pdev->name, fbdev[i]))
s3cfb_set_fifo_interrupt(fbdev[i], 1);
dev_info(fbdev[i]->dev, "fifo underrun trace\n");
#endif
/* hw setting */
/*故名思议这是对硬件的初始化,里面主要是对exynos4412的寄存器设置所有会调到 s3cfb_fimd6x.c 中的寄存器操作,将在下面详细介绍*/
s3cfb_init_global(fbdev[i]);
fbdev[i]->system_state = POWER_ON;//设置设备工作状态power on
/* alloc fb_info */
/* 这个函数在s3cfb_ops.c 文件中 -------fb 操作集合*/
/*这个函数的作用是申请struct fb_info 结构体,初始化fb_info 结构体的信息*/
/*fb_info 结构体 上与内核接口耦合的关系,我们写lcd驱动就是除了初始化相关硬件以外*/
/*就是把fb_info 结构体初始化后 注册到内核,供 fb_mem 调用,上层才能跟底层结合起来*/
/*该函数具体内容将在接下来介绍*/
s3cfb_alloc_framebuffer(fbdev[i], i);
/* register fb_info */
/*这个函数在s3cfb_ops.c 文件中 主要是向内核注册fb_info ,将在以后介绍*/
if (s3cfb_register_framebuffer(fbdev[i]))
/* enable display */
/*s3cfb_set_clock 向VIDCON0 配置相应的时钟参数*/
s3cfb_set_clock(fbdev[i]);
/* --------选择windows通道,使能wins窗口,将在接下来*/
s3cfb_enable_window(fbdev[0], pdata->default_win);
#ifdef CONFIG_FB_S5P_SOFTBUTTON_UI /* Add Menu UI */
s3cfb_enable_window(fbdev[0], 4);
#endif
/*设置窗口状态,/* screen: unblanked, hsync: on, vsync: on */
s3cfb_update_power_state(fbdev[i], pdata->default_win,
FB_BLANK_UNBLANK);
/*2.真正使能lcd模块,设置exynos的基础器,所以与硬件有关*/
/*具体函数在 s3cfb_fimd6x.c 。将在接下来介绍*/
s3cfb_display_on(fbdev[i]);
#ifdef CONFIG_FB_S5P_LCD_INIT
/* panel control */
if (pdata->backlight_on) //判断是否开启背光
pdata->backlight_on(pdev);
if (pdata->lcd_on) //判断是启动lcd
pdata->lcd_on(pdev);
#endif
/*在/sys/class/下创建一个属性文件 okprobe 函数就此结束*/
ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
return 0;
}
好了我们再来回顾了probe函数的主要作用和流程:
1. 获取平台设备 device中的资源
2. 对设备做了一下相应的初始化
3. 申请了fb_info ,根据要求进行了填充
4. 向内核提交了fb_info
5. 使能设备等
6. 创建属性文件
这是probe功能,remove是做相应的逆操作,在这里不做介绍了。
后:在上面的介绍中是只有简单介绍了probe中主要有哪些函数和一些功能,
那么在下一节中将会介绍,这些函数的具体实现和原理
未完待续。。。。。