高通-LCD驱动框架简述

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驱动框架简述_第1张图片

二 不同的硬件结构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驱动框架简述_第2张图片

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张图片

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);

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

你可能感兴趣的:(Linux驱动)