LCD 相关缩写

MDP: Mobile display processor:移动显示处理器
DSI: Display Serial Interface  : 显示器串行接口
MDSS : Multimedia Display sub system  : 多媒体显示子系统
DCS (DisplayCommandSet):DCS是一个标准化的命令集,用于命令模式的显示模组

一 、LCD硬件结构框架:
一般情况下,固定电话、对讲机、带实体按键的手机(老人机,户外手机)等等都会使用RGB接口的LCD,而对屏幕要求较高的设备比如目前流行的不带实体按键智能手机,平板电脑,广告机等都会使用HDMI、MIPI、LVDS等接口的LCD屏。

LCD硬件结构框架如下图:

DCS架构层 dcs框架_嵌入式硬件

 
二 、不同的硬件结构LCD的驱动框架简述
由上图可知,不同接口的LCD有不同的硬件结构,自然而然就有不同的驱动框架:
RGB接口LCD驱动框架:分为两部分
1 核心层驱动
2 LCD控制器驱动

这两部分 核心层驱动已经有内核统一提供了,我们需要完成的是 LCD控制器驱动,大致思路通过platform总线挂在LCD控制器,在
prob中完成 fb_info的创建,初始化,注册等工作。

LVDS、HDMI(DSI)、MIPI(DSI)等接口的LCD驱动框架:分为两部分
1 核心驱动
2 LVDS、HDMI、MIPI接口控制器驱动

三 、RGB接口LCD驱动简述
3.1 硬件线路框架
本文驱动是基于高通平台msm8909,msm8909LCD接口是MIPI接口,而我们的户外手机对屏幕要求不高,有实体按键,所以用RGB接口的LCD即可,所以CPU与LCD之间需要一个 MIPI转RGB的转换芯片,本文使用ICN6211转换芯片,框图如下:

DCS架构层 dcs框架_驱动开发_02

 

3.2 软件驱动框架
在Linux设备中,LCD是一个很重要的外设,LCD显示采用了帧缓冲(framebuffer)技术,所以LCD驱动也叫Framebuffer驱动,所以LCD驱动框架就是围绕 帧缓冲 展开工作,下面解释一下什么是帧缓冲,帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象出来,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关系物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备。

帧缓冲设备最关键的一个数据结构体是fb_info结构,。包括了关于帧缓冲设备属性和操作的完整描述,这个结构体的定义如下所示。

/kenrel/include/linux/fb.h
 struct fb_info {
     ...
     struct fb_var_screeninfo var;//记录用户可变的显示控制参数
     struct fb_fix_screeninfo fix;//记录用户固定的显示控制器的参数
     ...
     struct fb_ops *fbops;//操作函数指针,类似于 file_oparetions
     ...
 }; struct fb_ops {
     int (*fb_open)(struct fb_info *info, int user);
     int (*fb_release)(struct fb_info *info, int user);    //用于上层用户应用对 framebuffer 的读写
     ssize_t (*fb_read)(struct fb_info *info, char __user *buf,size_t count, loff_t *ppos);
     ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,size_t count, loff_t *ppos);    //用于应用层对framebuffer进行的控制
     int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,unsigned long arg);
     ...
 };



注意以下解析中,会经常将LCD设备称呼为FB设备

FB设备驱动大致分为三层
第一层:核心层
第二层:XXX平台驱动,FB驱动层
第三层:具体LCD驱动

第一层:核心层 
功能:1为底层设备驱动提供注册接口,为上层用户访问提供访问控制接口
kernel\drivers\video\fbmem.c 

第二层:FB驱动层
功能: 1 填充 struct fb_info   
      2 调用 fbmem.c的 register_framebuffer注册 fb设备
kernel\video\msm\mdss\mdss_fb.c //FB驱动 xxx平台(msm高通平台设备驱动)

第三层:具体LCD驱动
功能:1 实际LCD 初始化信息
kernel\video\msm\mdss\mdss_spi_panel.c

3.2.1 第一层:核心层
kernel\drivers\video\fbmem.c //核心层

LCD驱动核心层:fbmem.c (input.c):内核为所有LCD及显示器抽象出来的统一的驱动模型,即LCD模型或者称为Framebuffer模型,抽象出来的模型屏蔽掉了硬件的差异,为用户体统一提供LCD显示接口,上层应用可以直接操作读写该接口用来操作LCD设备。

作用:
1 为底层设备驱动提供注册接口
2 为上层用户访问提供访问控制接口

分析fbmem.c

//注册fb_info结构体
 int register_framebuffer(struct fb_info *fb_info)
 {
     int ret;    mutex_lock(®istration_lock);
 //调用do_register_framebuffer
     ret = do_register_framebuffer(fb_info);
     mutex_unlock(®istration_lock);    return ret;
 }static int do_register_framebuffer(struct fb_info *fb_info)
 {
     ...
 //创建设备节点
 fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
     ...
 //注册fb_info的时候赋值给registered_fb数组
     registered_fb[i] = fb_info;
     ...
     return 0;
 }static struct fb_info *file_fb_info(struct file *file)
 {
     struct inode *inode = file_inode(file);
     int fbidx = iminor(inode);
 //registered_fb存放已经注册的fb_info的数组,是在注册fb_info的时候赋值给registered_fb数组的,这里是从已经的注册的 fb_info中选择对应的LCD设备
    struct fb_info *info = registered_fb[fbidx];    if (info != file->private_data)
         info = NULL;
     return info;
 }static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 {
     unsigned long p = *ppos;//定义 fb_info结构体,代表一个特定的LCD设备
     struct fb_info *info = file_fb_info(file);
     u8 *buffer, *dst;
     u8 __iomem *src;
     int c, cnt = 0, err = 0;
     unsigned long total_size;
     ...
 //如果fb_info结构体中定义了fops成员变量,并且fops中定义了read成员函数,那么就调用info->fbops->fb_read
     if (info->fbops->fb_read)
         return info->fbops->fb_read(info, buf, count, ppos);    ...
    while (count) {
     ...
 //否则就执行 file_operations的默认read函数,直接copy_to_user
         if (copy_to_user(buf, buffer, c)) {
             err = -EFAULT;
             break;
         }
     ...
     }    return (err) ? err : cnt;
 } //要特别说明一下.read =        fb_read,函数
 static const struct file_operations fb_fops = {
     .owner =    THIS_MODULE,
     .read =        fb_read,
     .write =    fb_write,
     .unlocked_ioctl = fb_ioctl,
 #ifdef CONFIG_COMPAT
     .compat_ioctl = fb_compat_ioctl,
 #endif
     .mmap =        fb_mmap,
     .open =        fb_open,
     .release =    fb_release,
 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
 .get_unmapped_area = get_fb_unmapped_area,
 #endif
 #ifdef CONFIG_FB_DEFERRED_IO
     .fsync =    fb_deferred_io_fsync,
 #endif
     .llseek =    default_llseek,
 }; fbmem_init(void)
 {
     proc_create("fb", 0, NULL, &fb_proc_fops);//创建字符设备,绑定 struct file_operations fb_fops ,证明LCD驱动是字符设备
     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))//创建设备类
     fb_class = class_create(THIS_MODULE, "graphics");    return 0;
 }

 3.2.2 第二层:FB驱动层
 kernel\video\msm\mdss\mdss_fb.c //FB驱动 xxx平台(msm高通平台设备驱动)功能:
 1 填充 struct fb_info
 2 调用 fbmem.c的 register_framebuffer注册 fb设备分析mdss_fb.c
static int mdss_fb_register(struct msm_fb_data_type *mfd)
 {
     struct fb_info *fbi = mfd->fbi;
     struct fb_fix_screeninfo *fix;
     struct fb_var_screeninfo *var;    //framebuffer fb_info 信息初始化
     ...
     fix->type_aux = 0;
     ...
     var->xoffset = 0
     ...
     fbi->fbops = &mdss_fb_ops;
     ...    //registers a frame buffer device (fbmem.c中定义)
     register_framebuffer(fbi)
 }static int mdss_fb_probe(struct platform_device *pdev)
 {    pdata = dev_get_platdata(&pdev->dev);
     
     //注册一个 FB设备
     rc = mdss_fb_register(mfd);
 } static const struct dev_pm_ops mdss_fb_pm_ops = {
     SET_SYSTEM_SLEEP_PM_OPS(mdss_fb_pm_suspend, mdss_fb_pm_resume)
 }; //对应msm8909-mdss.dtsi mdss_fb0: qcom,mdss_fb_primary {
 static const struct of_device_id mdss_fb_dt_match[] = {
     { .compatible = "qcom,mdss-fb",},
     {}
 };
 EXPORT_COMPAT("qcom,mdss-fb");static struct platform_driver mdss_fb_driver = {
     .probe = mdss_fb_probe,
     .remove = mdss_fb_remove,
     .suspend = mdss_fb_suspend,
     .resume = mdss_fb_resume,
     .shutdown = mdss_fb_shutdown,
     .driver = {
         .name = "mdss_fb",  //对应设备数  msm8909-mdss.dtsi : mdss_mdp: qcom,mdss_mdp@1a00000
         .of_match_table = mdss_fb_dt_match,
         .pm = &mdss_fb_pm_ops,
     },
 };static struct fb_ops mdss_fb_ops = {
     .owner = THIS_MODULE,
     .fb_open = mdss_fb_open,
     .fb_release = mdss_fb_release,
     .fb_check_var = mdss_fb_check_var,    /* vinfo check */
     .fb_set_par = mdss_fb_set_par,    /* set the video mode */
     .fb_blank = mdss_fb_blank,    /* blank display */
     .fb_pan_display = mdss_fb_pan_display,    /* pan display */
     .fb_ioctl = mdss_fb_ioctl,    /* perform fb specific ioctl */
 #ifdef CONFIG_COMPAT
     .fb_compat_ioctl = mdss_fb_compat_ioctl,
 #endif
     .fb_mmap = mdss_fb_mmap,
 }; int __init mdss_fb_init(void)
 {
     int rc = -ENODEV;    if (platform_driver_register(&mdss_fb_driver))
         return rc;    return 0;
 }module_init(mdss_fb_init);
 3.2.3 第三层:具体LCD驱动
 kernel\video\msm\mdss\mdss_spi_panel.c功能:实际LCD 初始化信息 : 遍历设备树,寻找fb设备节点。初始化lcd屏幕,注册LCD屏幕
int mdss_spi_panel_init(struct device_node *node,struct spi_panel_data    *ctrl_pdata,bool cmd_cfg_cont_splash)
 {
 //遍历设备树 寻找 spi-panel-gc9305-qvga-cmd-dtsi: spi_gc9305_qvga_cmd: qcom,mdss_spi_gc9305_qvga_cmd
 panel_name = of_get_property(node, "qcom,mdss-spi-panel-name", NULL);
 } //find device node of spi panel
 static struct device_node *mdss_spi_find_panel_of_node(struct platform_device *pdev, char *panel_cfg)
 {
     ...
 mdss_node = of_parse_phandle(pdev->dev.of_node,"qcom,mdss-mdp", 0);
 } static int mdss_spi_panel_probe(struct platform_device *pdev)
 {    //寻找设备节点
     spi_pan_node = mdss_spi_find_panel_of_node(pdev, panel_cfg);    //屏初始化
     rc = mdss_spi_panel_init(spi_pan_node, ctrl_pdata, cmd_cfg_cont_splash);    //注册 spi lcd屏
     rc = spi_panel_device_register(spi_pan_node, ctrl_pdata);
         {
         //mdss_fb.c中提供
         rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data));
         }
 } //msm8909-mdss.dtsi
 static const struct of_device_id mdss_spi_panel_match[] = {
     { .compatible = "qcom,mdss-spi-display" },
     {},
 };static struct platform_driver this_driver = {
     .probe = mdss_spi_panel_probe,
     .driver = {
         .name = "spi_panel",//设备树中节点之前的名称 : mdss_spi: qcom,mdss_spi {
         .owner  = THIS_MODULE,
         .of_match_table = mdss_spi_panel_match,
     },
 };static int __init mdss_spi_display_init(void)
 {
     int ret;    ret = platform_driver_register(&this_driver);
     return 0;
 }
 module_init(mdss_spi_display_init);