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

展讯平台android display驱动分析

程序员文章站 2022-07-14 10:00:46
...

本文以展讯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总线。
展讯平台android display驱动分析

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的。
展讯平台android display驱动分析

dithering

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

输出接口

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

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

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

2 display驱动

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

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,
    ...
};

展讯平台android display驱动分析

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