展讯平台android display驱动分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/flaoter/article/details/78452145

本文以展讯tshak平台的display controller为硬件模型,来说明Android底层显示驱动的实现原理。该硬件模型较基础和简单,目前大多SOC平台的硬件设计都变得越来越复杂,实现功能也越来越全面,但基于此平台的设计更方便理解。

1 display控制器

下图的结构即是显示控制器的模块组成,它内部集成了lcd显示相关的控制器:lcdc和dispc,其中lcdc仅支持MCU接口类型(DBI),而dispc支持DBI和DPI接口类型。
图片最上面的实现是lcdc,直接通过DBI进行了输出。
图片中间较复杂的内容是dispc,图像数据流流向
ddr sdram ——> dispc ——> panel,dispc通过axi总线连接ddr controller并获取ddr sdram中的图像数据,经由dispc处理后通过dbi/dpi等interface将图像输出给屏幕。
图片最下面是寄存器配置相关,一般寄存器配置在SOC内部通过ahb总线。
dispc_tshark

blending

展讯平台dispc支持两层图像显示: image 层/OSD 层,image 层支持的图像格式: YUV422/YUV420/YUV400/RGB888/RGB565/RGB666/RGB555/PACK data,OSD 层支持的图像格式: RGB888/RGB565/RGB666/RGB555。下图是dispc内部blending的功能示意。
video和camera的图像一般是YUV格式的,UI的图像是RGB的。
dispc_blending

dithering

支持RGB888->RGB 666, RGB888->RGB 565。

输出接口

  1. DBI接口, 就是通常所讲的MCU模式。LCD panel内部一般需要一块sram,刷新操作由lcd panel完成。控制简单方便,无需时钟和同步信号。
    dispc_dbi
  2. DPI接口,就是RGB模式。LCD panel内部一般不需要sram,刷新操作由外部输入的vsync完成。数据线和控制线分离显示数据直接写屏,速度快。
    dispc_dpi_mode
    dpi时序图如下面两幅图所示。
    dispc_dpi
    dispc_dpi1

  3. mipi mode
    手机平台目前最常用的方案,内容较多,在此不详述,可参考专业文档。

为方便理解输出接口,下面附一张屏幕显示驱动芯片ili9486的结构图,它与dispc的输出接口相连。它支持MCU,SPI,RGB,MIPI各种接口。
dispc_ili9486

2 display驱动

首先show一下display驱动的结构图,由三个层次组成,分别为fb, dispc和panel。
fb_driver

static int sprdfb_probe(struct platform_device *pdev)
{
    struct fb_info *fb = NULL;
    struct sprdfb_device *dev = NULL;
    int ret = 0;
    //fb中结构体fb_info的申请和分配
    fb = framebuffer_alloc(sizeof(struct sprdfb_device), &pdev->dev);

    dev = fb->par;
    dev->fb = fb;
#ifdef CONFIG_OF
    dev->of_dev = &(pdev->dev);
    dev->dev_id = of_alias_get_id(pdev->dev.of_node, "lcd");
#else
    dev->dev_id = pdev->id;
#endif
    if((SPRDFB_MAINLCD_ID != dev->dev_id) &&(SPRDFB_SUBLCD_ID != dev->dev_id)){
        printk(KERN_ERR "sprdfb: sprdfb_probe fail. (unsupported device id)\n");
        goto err0;
    }

    switch(SPRDFB_IN_DATA_TYPE){
    case SPRD_IN_DATA_TYPE_ABGR888:
        dev->bpp = 32;
        break;
    case SPRD_IN_DATA_TYPE_BGR565:
        dev->bpp = 16;
        break;
    default:
        dev->bpp = 32;
        break;
    }

    if(SPRDFB_MAINLCD_ID == dev->dev_id){
        dev->ctrl = &sprdfb_dispc_ctrl;  //sprd dispc控制器
#ifdef CONFIG_OF
        if(0 != of_address_to_resource(pdev->dev.of_node, 0, &r)){
            printk(KERN_ERR "sprdfb: sprdfb_probe fail. (can't get register base address)\n");
            goto err0;
        }
        //寄存器
        g_dispc_base_addr = (unsigned long)ioremap_nocache(r.start,
                resource_size(&r));
        if(!g_dispc_base_addr)
            BUG();
        printk("sprdfb: set g_dispc_base_addr = %ld\n", g_dispc_base_addr);
#endif
    }

    dev->frame_count = 0;
    dev->logo_buffer_addr_v = 0;
    dev->capability = sprdfb_config_capability();

    if(sprdfb_panel_get(dev)){
#if defined(CONFIG_FB_LCD_OLED_BACKLIGHT)
        if (dev->panel->ops->panel_dimming_init)
                dev->panel->ops->panel_dimming_init(dev->panel, &pdev->dev);
#endif
        dev->panel_ready = true;
        //一般显示相关驱动在uboot阶段已进行过初始化,所以此处可以直接使用
        dev->ctrl->logo_proc(dev);
    }else{
        dev->panel_ready = false;
    }

    dev->ctrl->early_init(dev); //sprd dispc控制器的early_init,其中有vsync中断的申请

    if(!dev->panel_ready){
        if (!sprdfb_panel_probe(dev)) {  //panel注册
            ret = -EIO;
            goto cleanup;
        }
    }

    ret = setup_fb_mem(dev, pdev);  //申请fb的memory,一般在dts中有reservemem
    if (ret) {
        goto cleanup;
    }


//配置fb_info成员参数,除了变量信息外,还会设置ops信息,其中最常用的就是mmap函数,将
//framebuffer的物理内存映射给usersapce使用
    setup_fb_info(dev);  
    /* register framebuffer device */
    ret = register_framebuffer(fb);     //fb设备注册
    if (ret) {
        printk(KERN_ERR "sprdfb: sprdfb_probe register framebuffer fail.\n");
        goto cleanup;
    }
    platform_set_drvdata(pdev, dev);
    sprdfb_create_sysfs(dev);
    dev->ctrl->init(dev);  //sprd dispc控制器的init

...

    return 0;
}

struct display_ctrl sprdfb_dispc_ctrl = {
    .name       = "dispc",
    .early_init     = sprdfb_dispc_early_init,
    .init           = sprdfb_dispc_init,
    .uninit     = sprdfb_dispc_uninit,
    .refresh        = sprdfb_dispc_refresh,
    .logo_proc      = sprdfb_dispc_logo_proc,
    .update_clk = dispc_update_clk_intf,
    .is_refresh_done = sprdfb_is_refresh_done,
    ...
};

dispc_probe

3 调试问题

帧率

dpi_clk的计算方法如下:
dpi_clk=(witdh+hfp+hbp+hsync)* (height+vfp+vbp+vsync)*fps

例如需配置帧率的话需要根据配置的dpi_clock,对除width/height这类图像固有参数以外参数进行配置。
dpi_clk=64M,如需配置720p的显示帧率为60的话,可使用后面对应的可变参数(720+4+80+4)*(1280+18+17+5)*60= 64M

图像问题

memory dump & screencap

猜你喜欢

转载自blog.csdn.net/flaoter/article/details/78452145