Android LCD整理二:mtk平台LCD流程分析(LK部分)

时间:2024-03-30 08:36:39

一、LK显示代码分析

1.platform_init代码分析

//framebuffer的大小
g_fb_size = mt_disp_get_vram_size();
//framebuffer的起始地址
g_fb_base = mblock_reserve(&g_boot_arg->mblock_info, g_fb_size, 0x10000, 0xa0000000, RANKMAX);
//mtk display系统初始化
mt_disp_init((void *)g_fb_base);
//fb预先填充black
mt_disp_fill_rect(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT, 0x0);
//将fb内容更新到lcm上
mt_disp_update(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);
//加载logo
mboot_common_load_logo((unsigned long)mt_get_logo_db_addr_pa(), "logo");
//根据启动方式选择加载的logo,填充到fb中
mt_disp_show_boot_logo();
//打开背光
mt65xx_backlight_on();
mt_disp_update(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);


2.相关重要结构体
//LCM_DRIVER,lcm相关的一系列的操作函数
typedef struct {
    const char *name;   --用于匹配lcm
    void (*set_util_funcs) (const LCM_UTIL_FUNCS *util);   --设置lcm操作函数,gpio、reset、delay、write/read cmd
    void (*get_params) (LCM_PARAMS *params);   --获取lcm的参数,接口、分辨率、porch、PLL clock、data format等
    void (*init) (void);   --lcm初始化,一般为上电复位初始化
    void (*suspend) (void);   --pm相关
    void (*resume) (void);
    /* for power-on sequence refinement */
    void (*init_power) (void);   --上电初始化
    void (*suspend_power) (void);
    void (*resume_power) (void);
   void (*update) (unsigned int x, unsigned int y, unsigned int width, unsigned int height); -设置ram的显示区域(x,y)->(x+width,y+height),只在command mode下使用
    unsigned int (*compare_id) (void);
    void (*parse_dts)(const LCM_DTS *DTS, unsigned char force_update);  --解析设备树
    /* /////////////////////////CABC backlight related function */
    void (*set_backlight) (unsigned int level);   --背光相关函数
    void (*set_backlight_cmdq) (void *handle, unsigned int level);
    void (*set_pwm) (unsigned int divider);
    unsigned int (*get_pwm) (unsigned int divider);
    void (*set_backlight_mode) (unsigned int mode);
    /* ///////////////////////// */
    int (*adjust_fps) (void *cmdq, int fps);   --调节刷新率,一般刷新率为60Hz
    /* ///////////ESD_RECOVERY////////////////////// */
    unsigned int (*esd_check) (void); --ESD检测相关,开启该功能,系统会周期性检测lcm寄存器状态,状态NG则会reset lcm
    unsigned int (*esd_recover) (void);
    unsigned int (*check_status) (void);
    unsigned int (*ata_check) (unsigned char *buffer);
    void (*read_fb) (unsigned char *buffer);
    int (*ioctl) (LCM_DRV_IOCTL_CMD cmd, unsigned int data);
    /* /////////////////////////////////////////////// */
    void (*enter_idle)(void);
    void (*exit_idle)(void);
    void (*change_fps)(unsigned int mode);
    /* //switch mode */
   void *(*switch_mode) (int mode);   --用于切换mode,command mode 和video mode切换
    void (*set_cmd) (void *handle, int *mode, unsigned int cmd_num);
    void (*set_lcm_cmd) (void *handle, unsigned int *lcm_cmd, unsigned int *lcm_count,
                         unsigned int *lcm_value);
    /* /////////////PWM///////////////////////////// */
    void (*set_pwm_for_mix) (int enable);

} LCM_DRIVER;

//LCM_PARAMS,lcm参数设置
typedef struct {
    LCM_TYPE type;   --lcm的接口类型,分为DSI、DBI、DPI        
    LCM_CTRL ctrl;   --lcm寄存器的访问方式,普遍通过MIPI LP下指令,也有的支持spi/i2c等接口访问   
    LCM_INTERFACE_ID lcm_if;   --lcm的接口id,分为DSI0、DSI1、dual port DSI、DBI0、DPI0、DPI1
    LCM_INTERFACE_ID lcm_cmd_if;   --cmd模式接口,和lcm_if一致
    /* common parameters */
    unsigned int lcm_x;  --(lcm_x,lcm_y)定义显示的起始pixel,(virtual_width,virtual_height)or(width,height)定义显示区域的宽和高
    unsigned int lcm_y;  
    unsigned int width;
    unsigned int height;
    unsigned int virtual_width;  
    unsigned int virtual_height;
    unsigned int io_select_mode;    /* DBI or DPI should select IO mode according to chip spec */


    /* particular parameters */
    LCM_DBI_PARAMS dbi;  --DBI参数
    LCM_DPI_PARAMS dpi;  --DPI参数
    LCM_DSI_PARAMS dsi;  --DSI参数
    unsigned int physical_width;
    unsigned int physical_height;
    unsigned int od_table_size;
    void *od_table;  --光学相关表格,gamma节点
} LCM_PARAMS;

//ddp(display data path)内容结构体
typedef struct {
    int                         state;  
    int                         need_trigger_overlay;  --overlay是否需要触发
    DISP_PRIMARY_PATH_MODE   mode;   --ddp模式,这里选择DIRECT_LINK_MODE
    unsigned int                last_vsync_tick;  
#ifndef DDP_LK_BOOT
    struct mutex                lock;
#endif
    disp_lcm_handle *           plcm;  --lcm相关
    cmdqRecHandle               cmdq_handle_config;   --配置线程cmdq句柄
    cmdqRecHandle               cmdq_handle_trigger;  --触发线程cmdq句柄
    disp_path_handle            dpmgr_handle;   --ddp manager句柄
    disp_path_handle            ovl2mem_path_handle;
} display_primary_path_context;

//ddp handle结构体
typedef struct {
    LCM_PARAMS          *params;  
    LCM_DRIVER          *drv;  
    LCM_INTERFACE_ID    lcm_if_id;   --接口id    
    int                 module;
    int                 is_inited;   --lcm是否初始化
    int                 is_connected;  --lcm是否连接
} disp_lcm_handle, *pdisp_lcm_handle;

//定义了支持的ddp num,即mtk支持这么多种显示方案,每种显示方案对应由不同的模块组成,构成一个二维数组,module_list_scenario
typedef enum {
    DDP_SCENARIO_PRIMARY_DISP = 0,//我们常用的显示方案
    DDP_SCENARIO_PRIMARY_RDMA0_COLOR0_DISP,
    DDP_SCENARIO_PRIMARY_RDMA0_DISP,
    DDP_SCENARIO_PRIMARY_BYPASS_RDMA,
    DDP_SCENARIO_PRIMARY_OVL_MEMOUT,
    DDP_SCENARIO_PRIMARY_DITHER_MEMOUT,
    DDP_SCENARIO_PRIMARY_UFOE_MEMOUT,
    DDP_SCENARIO_DISPLAY_INTERFACE,
    DDP_SCENARIO_PRIMARY_ALL,
    DDP_SCENARIO_SUB_DISP,
    DDP_SCENARIO_SUB_RDMA1_DISP,
    DDP_SCENARIO_SUB_OVL_MEMOUT,
    DDP_SCENARIO_SUB_ALL,
    DDP_SCENARIO_MAX
} DDP_SCENARIO_ENUM;

3.总体流程
 mt_disp_get_vram_size();
 mt_disp_init((void *)g_fb_base);
    -->DISP_GetVRamSize();
        -->DISP_GetFBRamSize();
            -->DISP_GetScreenWidth();
                -->primary_display_get_width();
            -->disp_lcm_probe(NULL, LCM_INTERFACE_NOTDEFINED);
a.power键按下释放
b.先执行main.c (vendor\vendor\mediatek\proprietary\bootable\bootloader\lk\kernel)
c.执行 platform_init(vendor\vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6735\platform.c) 
d.调用mt_disp_init((void*)g_fb_base);(vendor\vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6735\mt_disp_dev.c)
e.调用到primary_display_init(NULL);(vendor\vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6735\primary_display.c)


我这里主要分析primary_display_init函数,这个函数在kernel中也有,实现略有不同,lk只粗略讲解该函数。
这里提一个宏定义#define pgc _get_context()

(这个pgc很重要,因此提一下。这里最终相当于就是初始化了一个display_primary_path_context结构体)


在primary_display_init函数中主要调用函数如下:
1.pgc->plcm = disp_lcm_probe( lcm_name, LCM_INTERFACE_NOTDEFINED);
贴入重要代码并解释
disp_lcm_handle* disp_lcm_probe(char* plcm_name, LCM_INTERFACE_ID lcm_id)
{
......
_lcm_count();计算lcm_driver_list中lcd个数
......
lcm_drv->compare_id();通过调用对应lcd驱动中的compare_id函数来判定用的是否是当前lcd
......
return plcm;将当前应用的lcd相关信息录入plcm中并返回
}
2.lcm_param = disp_lcm_get_params(pgc->plcm); 获取param参数即对应驱动中的param
3._build_path_direct_link();//显示路径的创建(ddp路径的创建,不同的显示方案ddp路径不同)
static int _build_path_direct_link(void){
    pgc->dpmgr_handle = dpmgr_create_path(DDP_SCENARIO_PRIMARY_DISP, pgc->cmdq_handle_config);  --根据显示方案设置ddp_path_handle
    dst_module = _get_dst_module_by_lcm(pgc->plcm);  --根据lcm接口类型获得目标模块,这里为DISP_MODULE_DSI0
    dpmgr_path_set_dst_module(pgc->dpmgr_handle, dst_module);    --设置ddp的最后一个模块为DISP_MODULE_DSI0
  dpmgr_set_lcm_utils(pgc->dpmgr_handle, pgc->plcm->drv); --设置各个模块中lcm相关的操作函数,调用ddp_dsi_set_lcm_utils,最终调用lcm_drv->set_util_funcs(utils)将操作函数赋给lcm_util
}
4.dpmgr_path_set_video_mode  设置显示模式,video mode or command mode
5.dpmgr_path_init(pgc->dpmgr_handle, CMDQ_DISABLE);ddp上各个模块的初始化,ovl0、ovl1、rdma0、rdma1、color、aal、gamma、dither、dsi0
5.disp_lcm_init(pgc->plcm);lcd初始化

lk有关logo显示的函数
init_fb_screen填充framebuffer
fill_rect_with_color_by_32bit//根据旋转的角度填充fb

mt_disp_update//刷新fb有关的cache,并且调用各个模块start和trigger函数将fb内容更新到lcm上


二、display data path结构

Android LCD整理二:mtk平台LCD流程分析(LK部分)

  • 1.rotator

  图像旋转,支持多个方向旋转,也支持局部旋转

  • 2.scaler

  图像缩放,水平垂直方向缩放

  • 3.color engine

  图像颜色处理,PQ处理,改变亮度色坐标等

  • 4.OVL(overlayer)

  数据输入:direct link scaler format,YUV or RGB;memory source format.可以直接从上级模块SCL or PQ,也可以直接从memory输入

  支持4组layer:支持ROI(region of interest),自定义各layer的大小、起始地址、显示区域

  支持数据重新map:例如RGB、BGR之间转换

  支持数据类型转换:;例如YUV转RGB

  • 5.WDMA(write DMA)

  模块负责数据写入DRAM中,OVL-->WDMA1实现截图function

  • 6.RDMA(read DMA)

  模块负责从DRAM中将数据写入显示模块,如DSI、DPI、DBI(这些都是MIPI显示接口类型)

  RDMA0支持两种输入,direct link和memory input;RDMA1只支持memory input

  • 7.BLS(背光相关)

  通过pwm等调节背光,根据图片显示内容改变背光亮度(CABC)

display一些基本概念

1.video mode & command mode

  •   video mode:显示数据流通过driver IC直接显示到lcd上,为实时数据
  •   command mode:数据先更新到ram中再由ram刷新到lcd上。<1>这种模式需要ic带ram,平台无数据更新时,显示内容就由ram更新到lcd上,节省功耗。<2>1/2 or 1/3 ram(ram大小为一帧的1/2 1/3)可以实现平台较小的数据输入,输出较高分辨率的效果,平台方压缩显示数据,driver IC解压数据并显示到lcd上。

2.DSI、DBI、DPI

  •   DSI,串行接口,实现较高的数据传输
  •   DPI,并行接口,实时传输
  •   DBI,并行接口,driver IC带ram

3.MIPI DSI接口PLL计算

  总数据量=(VS+VBP+VACT+VFP)*(HS+HBP+HACT+HFP)* fps * format_bit(format_bit根据一个pixel的数据位来定,大多为24位,RGB888)

  每lane的数据量=总数据量/lane数

  DSI为差分信号,一个clock内双沿采样,传输2bit数据

  则最终PLL速率为每lane数据量/2