欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

LCD驱动框架简述

程序员文章站 2022-03-22 14:42:58
...

LCD驱动框架简述

目录

一 LCD硬件结构框架

二 不同的硬件结构LCD的驱动框架简述

三 RGB接口LCD驱动简述(以户外手机(Android5.1)为例)

3.1 硬件线路框架
3.2 软件驱动框架
3.2.1 第一层:核心层
3.2.2 第二层:FB驱动层
3.2.3 第三层:具体LCD驱动

///////////////////////////////////////////////////////////////////////

注意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硬件结构框架如下图:
LCD驱动框架简述

二 不同的硬件结构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转换芯片,框图如下:

LCD驱动框架简述

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

LCD驱动框架简述

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(&registration_lock);
//调用do_register_framebuffer
    ret = do_register_framebuffer(fb_info);
    mutex_unlock(&registration_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,aaa@qq.com
        .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);

本文只讲框架,调试过程有机会补充,以上。