一.mtk显示架构

分析代码时会看到ddp-xxx开头的文件,ddp指的是display data path,下面是6582的ddp结构,比较老的芯片,新的没找到,但是大体流程应该是类似的。

  • 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.video mode的类型

  

  •  Non-Burst Mode with Sync Pulses :传输时包括sync pulse

  •   Non-Burst Mode with Sync Events :相比上一种只是普通的同步事件

  •   Burst mode  :在scan line传输完RGB信号其余时间均为LP11模式

4.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

  

三.lk中lcm添加新的驱动

  • 1.在/dev/lcm/mtxx_lcm_list中包含这样一个结构体数组,数据定义了各种lcm设备。例如标红的是新增的lcm。
LCM_DRIVER *lcm_driver_list[] = {#if defined(NT35595_FHD_DSI_CMD_TRULY_8163)&nt35595_fhd_dsi_cmd_truly_8163_lcm_drv,
#endif#if defined(OTM1284A_HD720_DSI_VDO_TM)&otm1284a_hd720_dsi_vdo_tm_lcm_drv,
#endif
#if defined(R69338_FHD_DSI_VDO_JDI)&r69338_fhd_dsi_vdo_jdi_drv,
#endif
#if defined(FT8707_FHD_DSI_VDO_LGD)&ft8707_fhd_dsi_vdo_lgd_drv,
#endif#if defined(OTM1285A_HD720_DSI_VDO_TM)&otm1285a_hd720_dsi_vdo_tm_lcm_drv,
#endif......
}

  • 2.添加nt35595的驱动代码,截取LCM_DRIVER这部分代码
LCM_DRIVER nt35595_fhd_dsi_cmd_truly_8163_lcm_drv= {.name               = "nt35595_fhd_dsi_cmd_truly_8163",.set_util_funcs     = lcm_set_util_funcs,.get_params         = lcm_get_params,.init               = lcm_init,.suspend            = lcm_suspend,.resume             = lcm_resume,.init_power         = lcm_init_power,.resume_power       = lcm_resume_power,.suspend_power      = lcm_suspend_power,
#if (LCM_DSI_CMD_MODE).update             = lcm_update,
#endif};

  • 3.在/project/xx.mk中添加新的lcm,会根据name选取对应的驱动进行初始化操作
CUSTOM_LK_LCM="nt35595_fhd_dsi_cmd_truly_8163"

四.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;  --用于匹配lcmvoid (*set_util_funcs) (const LCM_UTIL_FUNCS *util);  --设置lcm操作函数,gpio、reset、delay、write/read cmdvoid (*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 lcmunsigned 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、DPI1LCM_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;

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内容结构体typedef struct {int                             state;  int                         need_trigger_overlay;  --overlay是否需要触发DISP_PRIMARY_PATH_MODE  mode;  --ddp模式,这里选择DIRECT_LINK_MODEunsigned int                    last_vsync_tick;  
#ifndef DDP_LK_BOOTstruct mutex                    lock;
#endifdisp_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 {cmdqRecHandle           cmdqhandle;  --配置线程cmdq句柄int                     hwmutexid;  int                     power_sate;  --是否上电DDP_MODE                mode;  --分为video和cmd mode,这里为video mode//struct mutex            mutex_lock;
    DDP_IRQ_EVENT_MAPPING   irq_event_map[DISP_PATH_EVENT_NUM];  --event与irq映射数组DPMGR_WQ_HANDLE         wq_list[DISP_PATH_EVENT_NUM];  --waitqueue数组,每类事件对应一个wqDDP_SCENARIO_ENUM       scenario;  --ddp index,根据index从ddp二维数组获得ddpdisp_ddp_path_config    last_config;  --overlay相关
} ddp_path_handle_t, *ddp_path_handle;

//disp_ddp_path_config

typedef struct {
// for ovl
  unsigned int ovl_dirty;  --模块bypass的标志
  unsigned int rdma_dirty;
  unsigned int wdma_dirty;
  unsigned int dst_dirty;
  OVL_CONFIG_STRUCT ovl_config[4];
  RDMA_CONFIG_STRUCT rdma_config;
  WDMA_CONFIG_STRUCT wdma_config;
  LCM_DSI_PARAMS dsi_config;
  LCM_DPI_PARAMS dpi_config;
  unsigned int lcm_bpp;
  unsigned int dst_w;
  unsigned int dst_h;
} disp_ddp_path_config;


//display相关事件

typedef enum{
  DISP_PATH_EVENT_FRAME_START = 0,
  DISP_PATH_EVENT_FRAME_DONE,
  DISP_PATH_EVENT_FRAME_REG_UPDATE,
  DISP_PATH_EVENT_FRAME_TARGET_LINE,
  DISP_PATH_EVENT_FRAME_COMPLETE,
  DISP_PATH_EVENT_FRAME_STOP,
  DISP_PATH_EVENT_IF_CMD_DONE,
  DISP_PATH_EVENT_IF_VSYNC,
  DISP_PATH_EVENT_AAL_TRIGGER,
  DISP_PATH_EVENT_COLOR_TRIGGER,
  DISP_PATH_EVENT_NUM,
  DISP_PATH_EVENT_NONE = 0xff,
}DISP_PATH_EVENT;

//定义了支持的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;
int module_list_scenario[DDP_SCENARIO_MAX][DDP_ENING_NUM] = {/*PRIMARY_DISP*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_RDMA0, DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1, -1},/*PRIMARY_RDMA0_COLOR0_DISP*/ {DISP_MODULE_RDMA0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER,DISP_MODULE_UFOE, DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1, -1},/*PRIMARY_RDMA0_DISP*/ {DISP_MODULE_RDMA0, DISP_MODULE_DSI0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},/*PRIMARY_BYPASS_RDMA*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_UFOE, DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1, -1},/*PRIMARY_OVL_MEMOUT*/ {DISP_MODULE_OVL0, DISP_MODULE_WDMA0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},/*PRIMARY_DITHER_MEMOUT*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_WDMA0, -1, -1, -1, -1, -1},/*PRIMARY_UFOE_MEMOUT*/ {DISP_MODULE_OVL0, DISP_MODULE_COLOR0, DISP_MODULE_CCORR,DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER, DISP_MODULE_RDMA0, DISP_MODULE_UFOE, DISP_MODULE_WDMA0,-1, -1, -1},/*SUB_DISP*/ {DISP_MODULE_OVL1, DISP_MODULE_RDMA1, DISP_MODULE_DPI, -1,-1,-1,-1,-1,-1,-1,-1},/*SUB_RDMA1_DISP*/ {DISP_MODULE_RDMA1, DISP_MODULE_DPI, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},/*SUB_OVL_MEMOUT*/ {DISP_MODULE_OVL1, DISP_MODULE_WDMA1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1},/*PRIMARY_ALL*/ {DISP_MODULE_OVL0, DISP_MODULE_WDMA0, DISP_MODULE_COLOR0,DISP_MODULE_CCORR, DISP_MODULE_AAL, DISP_MODULE_GAMMA, DISP_MODULE_DITHER,DISP_MODULE_RDMA0,DISP_MODULE_PWM0, DISP_MODULE_DSI0, -1, -1},/*SUB_ALL*/ {DISP_MODULE_OVL1, DISP_MODULE_WDMA1, DISP_MODULE_RDMA1, DISP_MODULE_DPI, -1,-1, -1,-1,-1,-1, -1,-1},/*MULTIPLE_OVL*/ {DISP_MODULE_OVL1, DISP_MODULE_OVL0, DISP_MODULE_WDMA0, -1, -1, -1, -1, -1, -1,-1,-1,-1},
};

3.代码分析

<3.1>找到匹配的lcm并进行disp_lcm_handle结构体初始化

 mt_disp_get_vram_size();-->DISP_GetVRamSize();-->DISP_GetFBRamSize();-->DISP_GetScreenWidth();-->primary_display_get_width();            -->disp_lcm_probe(NULL, LCM_INTERFACE_NOTDEFINED);

//disp_lcm_probe,根据lcm_driver_list找到匹配的lcm,实现plcm结构体初始化disp_lcm_handle* disp_lcm_probe(char* plcm_name, LCM_INTERFACE_ID lcm_id)
{if (_lcm_count() == 0) {DISPERR("no lcm driver defined in linux kernel driver\n");return NULL;} else if (_lcm_count() == 1) {lcm_drv = lcm_driver_list[0];    --这里只连接一个lcmisLCMFound = true;} else {        ......}plcm = &_disp_lcm_driver[0];lcm_param = &_disp_lcm_params;if (plcm && lcm_param) {plcm->params = lcm_param;plcm->drv = lcm_drv;  }plcm->drv->get_params(plcm->params);  --执行get_params函数,初始化plcm->paramsplcm->lcm_if_id = plcm->params->lcm_if;  --接口id
} //其中一颗FHD IC的驱动参数
static void lcm_get_params(LCM_PARAMS * params)
{memset(params, 0, sizeof(LCM_PARAMS));params->type = LCM_TYPE_DSI;  --接口类型,还可以是DBI、DPIparams->width = FRAME_WIDTH;  --显示宽度params->height = FRAME_HEIGHT;  --显示高度// enable tearing-freeparams->dbi.te_mode = LCM_DBI_TE_MODE_DISABLED;  --TE模式disableparams->dbi.te_edge_polarity = LCM_POLARITY_RISING;  --DBI相关,这里表示上升沿采样#if (LCM_DSI_CMD_MODE)params->dsi.mode   = CMD_MODE;#elseparams->dsi.mode   = SYNC_PULSE_VDO_MODE;//BURST_VDO_MODE;  --video mode#endif// DSI/* Command mode setting */params->dsi.LANE_NUM = LCM_FOUR_LANE;  --mipi four lane//The following defined the fomat for data coming from LCD engine.params->dsi.data_format.color_order = LCM_COLOR_ORDER_RGB;  --pixel的color顺序,这里是RGB,有些panel pixel是BGR排列params->dsi.data_format.trans_seq = LCM_DSI_TRANS_SEQ_MSB_FIRST;  --高位先传输params->dsi.data_format.padding = LCM_DSI_PADDING_ON_LSB;  --这个暂时不清楚???params->dsi.data_format.format = LCM_DSI_FORMAT_RGB888;  --RGB888的排列// Highly depends on LCD driver capability.params->dsi.packet_size = 256;  --包的最大byte数// Video mode setting    params->dsi.PS = LCM_PACKED_PS_24BIT_RGB888;  --24bit RGB888排列params->dsi.vertical_sync_active = 3;  --vs,porch设定,需要满足driver IC spec的要求params->dsi.vertical_backporch = 5;  --vbpparams->dsi.vertical_frontporch = 8;  --vfpparams->dsi.vertical_active_line = FRAME_HEIGHT;  --vact params->dsi.horizontal_sync_active = 50;  --hsparams->dsi.horizontal_backporch = 100;  --hbpparams->dsi.horizontal_frontporch = 100;  --hfpparams->dsi.horizontal_active_pixel = FRAME_WIDTH;  --hactparams->dsi.PLL_CLOCK = 481;  --mipi速率
}

<3.2>ddp中各个模块和lcm配置初始化

mt_disp_init((void *)g_fb_base);

  • <3.2.1>创建显示路径(display data path)
int primary_display_init(char *lcm_name)
{......

pgc->plcm = disp_lcm_probe( lcm_name, LCM_INTERFACE_NOTDEFINED);

  if (primary_display_mode == DIRECT_LINK_MODE) {_build_path_direct_link();    ......
}

static int _build_path_direct_link(void)
{DISP_MODULE_ENUM dst_module = 0;pgc->mode = DIRECT_LINK_MODE;pgc->dpmgr_handle = dpmgr_create_path(DDP_SCENARIO_PRIMARY_DISP, pgc->cmdq_handle_config);  --根据显示方案设置ddp_path_handledst_module = _get_dst_module_by_lcm(pgc->plcm);  --根据lcm接口类型获得目标模块,这里为DISP_MODULE_DSI0dpmgr_path_set_dst_module(pgc->dpmgr_handle, dst_module);    --设置ddp的最后一个模块为DISP_MODULE_DSI0dpmgr_set_lcm_utils(pgc->dpmgr_handle, pgc->plcm->drv);  --设置各个模块中lcm相关的操作函数,调用ddp_dsi_set_lcm_utils,最终调用lcm_drv->set_util_funcs(utils)将操作函数赋给lcm_util}

disp_path_handle dpmgr_create_path(DDP_SCENARIO_ENUM scenario, cmdqRecHandle cmdq_handle)
{
    int i =0;
    int module_name ;
    ddp_path_handle path_handle = NULL;
    int * modules = ddp_get_scenario_list(scenario);
    int module_num = ddp_get_module_num(scenario);
    DDP_MANAGER_CONTEXT * content = _get_context();
    //path_handle = kzalloc(sizeof(uint8_t*) * sizeof(ddp_path_handle_t), GFP_KERNEL);
    memset((void*)(&g_handle), 0, sizeof(ddp_path_handle_t));
path_handle = &g_handle;
    if (NULL != path_handle) {
path_handle->cmdqhandle = cmdq_handle;
path_handle->scenario = scenario;
path_handle->hwmutexid = acquire_mutex(scenario);
        assign_default_irqs_table(scenario,path_handle->irq_event_map);
        DISP_LOG_I("create handle 0x%p on scenario %s\n",path_handle,ddp_get_scenario_name(scenario));
        for ( i=0; i< module_num; i++) {
           module_name = modules[i];
           DISP_LOG_V(" scenario %s include module %s\n",ddp_get_scenario_name(scenario),ddp_get_module_name(module_name));
           content->module_usage_table[module_name]++;
           content->module_path_table[module_name] = path_handle;
        }
    content->handle_cnt ++;
    content->handle_pool[path_handle->hwmutexid] = path_handle;
    } else {
        DISP_LOG_E("Fail to create handle on scenario %s\n",ddp_get_scenario_name(scenario));
    }
    return path_handle;
}

 

  • <3.2.2>ddp设置显示模式,video mode or command mode
dpmgr_path_set_video_mode(pgc->dpmgr_handle, primary_display_is_video_mode());

int dpmgr_path_set_video_mode(disp_path_handle dp_handle, int is_vdo_mode)
{ddp_path_handle handle = NULL;ASSERT(dp_handle != NULL);handle = (ddp_path_handle)dp_handle;handle->mode = is_vdo_mode ?  DDP_VIDEO_MODE : DDP_CMD_MODE;DISP_LOG_I("set scenario %s mode %s\n",ddp_get_scenario_name(handle->scenario),is_vdo_mode ? "Video Mode":"Cmd Mode");return 0;
}

  • <3.2.3>ddp上各个模块的初始化,ovl0、ovl1、rdma0、rdma1、color、aal、gamma、dither、dsi0
dpmgr_path_init(pgc->dpmgr_handle, CMDQ_DISABLE);

//调用各个模块的init函数int  dpmgr_path_init(disp_path_handle dp_handle, int encmdq)
{ddp_path_handle handle = (ddp_path_handle)dp_handle;int * modules = ddp_get_scenario_list(handle->scenario);int module_num = ddp_get_module_num(handle->scenario);cmdqRecHandle cmdqHandle = encmdq ? handle->cmdqhandle : NULL;//turn off m4u
    ddp_path_m4u_off();//open top clock
    ddp_path_top_clock_on();//seting mutexddp_mutex_set(handle->hwmutexid,handle->scenario,handle->mode,cmdqHandle);//connect path;ddp_connect_path(handle->scenario,cmdqHandle);// each module initfor ( i=0; i< module_num; i++) {module_name = modules[i];if (ddp_modules_driver[module_name] != 0) {if (ddp_modules_driver[module_name]->init!= 0) {ddp_modules_driver[module_name]->init(module_name, cmdqHandle);}if (ddp_modules_driver[module_name]->set_listener!= 0) {ddp_modules_driver[module_name]->set_listener(module_name,dpmgr_module_notify);}}}//after init this path will power on;handle->power_sate = 1;return 0;
}

  • <3.2.4>ddp上各个模块的配置
//data_config结构体描述lcm分辨率、接口类型、数据排列等信息  disp_ddp_path_config data_config;memset((void*)&data_config, 0, sizeof(disp_ddp_path_config));
//这里只考虑DSI接口memcpy(&(data_config.dsi_config), &(lcm_param->dsi), sizeof(LCM_DSI_PARAMS));data_config.dst_w = disp_lcm_width(pgc->plcm);data_config.dst_h = disp_lcm_height(pgc->plcm);if (lcm_param->type == LCM_TYPE_DSI) {if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB888)data_config.lcm_bpp = 24;else if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB565)data_config.lcm_bpp = 16;else if (lcm_param->dsi.data_format.format == LCM_DSI_FORMAT_RGB666)data_config.lcm_bpp = 18;} else if (lcm_param->type == LCM_TYPE_DPI) {if ( lcm_param->dpi.format == LCM_DPI_FORMAT_RGB888)data_config.lcm_bpp = 24;else if ( lcm_param->dpi.format == LCM_DPI_FORMAT_RGB565)data_config.lcm_bpp = 16;if ( lcm_param->dpi.format == LCM_DPI_FORMAT_RGB666)data_config.lcm_bpp = 18;}data_config.dst_dirty = 1;  --打开DSI0模块配置(config函数会对dst_dirty进行check,为0则直接返回)ret = dpmgr_path_config(pgc->dpmgr_handle, &data_config, CMDQ_DISABLE);

//调用各个模块的config函数int dpmgr_path_config(disp_path_handle dp_handle, disp_ddp_path_config * config, int encmdq)
{int i=0;int module_name ;ASSERT(dp_handle != NULL);ddp_path_handle handle = (ddp_path_handle)dp_handle;int * modules =  ddp_get_scenario_list(handle->scenario);int module_num = ddp_get_module_num(handle->scenario);cmdqRecHandle cmdqHandle = encmdq ? handle->cmdqhandle : NULL;memcpy(&handle->last_config, config, sizeof(disp_ddp_path_config));for ( i=0; i< module_num; i++) {module_name = modules[i];if (ddp_modules_driver[module_name] != 0) {if (ddp_modules_driver[module_name]->config!= 0) {ddp_modules_driver[module_name]->config(module_name, config, cmdqHandle);}}}return 0;
}

  • <3.2.5>lcm初始化,通过回读0x0A寄存器判断lcm是否连接
//依次调用lcm的init_power和init函数,实现上电复位初始化的动作,并通过回读0x0A寄存器判断跟lcm之前是否连接OKint disp_lcm_init(disp_lcm_handle *plcm)
{LCM_DRIVER *lcm_drv = NULL;bool isLCMConnected = false;if (_is_lcm_inited(plcm)) {lcm_drv = plcm->drv;if (lcm_drv->init_power) {lcm_drv->init_power();}if (lcm_drv->init) {if (!disp_lcm_is_inited(plcm)) {lcm_drv->init();}} else {return -1;}if (LCM_TYPE_DSI == plcm->params->type) {ret = DSI_dcs_read_lcm_reg_v2(_get_dst_module_by_lcm(plcm), NULL, 0x0A, (UINT8 *)&buffer,1);if (ret == 0) {isLCMConnected = 0;} else {isLCMConnected = 1;}}if (plcm->params->dsi.edp_panel == 1) {isLCMConnected = 1;}plcm->is_connected = isLCMConnected;return 0;} else {return -1;}
}

  • <3.2.6>关联event与irq,并且使能event,例如一帧刷完这个event,便产生一个中断
//映射event和中断,并且使能中断事件if (primary_display_is_video_mode()) {if (lcm_param->dsi.lfr_enable == 1) {dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_DSI0_FRAME_DONE);} else {dpmgr_map_event_to_irq(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC, DDP_IRQ_RDMA0_DONE);}} else {}dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC);dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE);pgc->state = 1;

int dpmgr_enable_event(disp_path_handle dp_handle, DISP_PATH_EVENT event)
{ASSERT(dp_handle != NULL);ddp_path_handle handle = (ddp_path_handle)dp_handle;DPMGR_WQ_HANDLE *wq_handle = &handle->wq_list[event];ddp_get_scenario_name(handle->scenario),event,handle->irq_event_map[event].irq_bit);if (!wq_handle->init) {//init_waitqueue_head(&(wq_handle->wq));wq_handle->init = 1;wq_handle->data= 0;wq_handle->event = event;}return 0;
}

//mtk display支持16类事件
typedef enum{DISP_PATH_EVENT_FRAME_START = 0,DISP_PATH_EVENT_FRAME_DONE,DISP_PATH_EVENT_FRAME_REG_UPDATE,DISP_PATH_EVENT_FRAME_TARGET_LINE,DISP_PATH_EVENT_FRAME_COMPLETE,DISP_PATH_EVENT_FRAME_STOP,DISP_PATH_EVENT_IF_CMD_DONE,DISP_PATH_EVENT_IF_VSYNC,DISP_PATH_EVENT_AAL_TRIGGER,DISP_PATH_EVENT_COLOR_TRIGGER,DISP_PATH_EVENT_NUM,DISP_PATH_EVENT_NONE = 0xff,
}DISP_PATH_EVENT;

//display相关的中断类型,ddp irq由模块和其包含的中断组成
typedef enum {DDP_IRQ_RDMA0_REG_UPDATE =      (DISP_MODULE_RDMA0 <<16  | 0x1<<0),DDP_IRQ_RDMA0_START =           (DISP_MODULE_RDMA0 <<16  | 0x1<<1),DDP_IRQ_RDMA0_DONE  =           (DISP_MODULE_RDMA0 <<16  | 0x1<<2),DDP_IRQ_RDMA0_UNDERFLOW =       (DISP_MODULE_RDMA0 <<16  | 0x1<<3),DDP_IRQ_RDMA0_TARGET_LINE =     (DISP_MODULE_RDMA0 <<16  | 0x1<<4),DDP_IRQ_RDMA1_REG_UPDATE =      (DISP_MODULE_RDMA1 <<16  | 0x1<<0),DDP_IRQ_RDMA1_START =           (DISP_MODULE_RDMA1 <<16  | 0x1<<1),DDP_IRQ_RDMA1_DONE  =           (DISP_MODULE_RDMA1 <<16  | 0x1<<2),DDP_IRQ_RDMA1_UNDERFLOW =       (DISP_MODULE_RDMA1 <<16  | 0x1<<3),DDP_IRQ_RDMA1_TARGET_LINE =     (DISP_MODULE_RDMA1 <<16  | 0x1<<4),DDP_IRQ_RDMA2_REG_UPDATE =      (DISP_MODULE_RDMA2 <<16  | 0x1<<0),DDP_IRQ_RDMA2_START =           (DISP_MODULE_RDMA2 <<16  | 0x1<<1),DDP_IRQ_RDMA2_DONE  =           (DISP_MODULE_RDMA2 <<16  | 0x1<<2),DDP_IRQ_RDMA2_UNDERFLOW =       (DISP_MODULE_RDMA2 <<16  | 0x1<<3),DDP_IRQ_RDMA2_TARGET_LINE =     (DISP_MODULE_RDMA2 <<16  | 0x1<<4),DDP_IRQ_WDMA0_FRAME_COMPLETE =  (DISP_MODULE_WDMA0<<16   | 0x1<<0),DDP_IRQ_WDMA1_FRAME_COMPLETE =  (DISP_MODULE_WDMA1<<16   | 0x1<<0),DDP_IRQ_DSI0_EXT_TE =           (DISP_MODULE_DSI0 <<16  | 0x1<<2),DDP_IRQ_DSI0_FRAME_DONE =       (DISP_MODULE_DSI0 <<16  | 0x1<<4),DDP_IRQ_UNKNOW =                (DISP_MODULE_UNKNOWN<<16 | 0x1<<0),} DDP_IRQ_BIT;

  • <3.2.7>显示输入的配置--各层layer的初始化
//lk阶段定义了2层layer(最多支持4层layer),FB_LAYER和BOOT_MENU_LAYER,    disp_input_config input;memset(&input, 0, sizeof(disp_input_config));input.layer     = BOOT_MENU_LAYER;input.layer_en  = 1;input.fmt       = redoffset_32bit ? eBGRA8888 : eRGBA8888;input.addr      = boot_mode_addr;input.src_x     = 0;input.src_y     = 0;input.src_w     = CFG_DISPLAY_WIDTH;input.src_h     = CFG_DISPLAY_HEIGHT;input.src_pitch = CFG_DISPLAY_WIDTH*4;input.dst_x     = 0;input.dst_y     = 0;input.dst_w     = CFG_DISPLAY_WIDTH;input.dst_h     = CFG_DISPLAY_HEIGHT;input.aen       = 1;input.alpha     = 0xff;primary_display_config_input(&input);memset(&input, 0, sizeof(disp_input_config));input.layer     = FB_LAYER;input.layer_en  = 1;input.fmt       = redoffset_32bit ? eBGRA8888 : eRGBA8888;input.addr      = fb_addr_pa;input.src_x     = 0;input.src_y     = 0;input.src_w     = CFG_DISPLAY_WIDTH;input.src_h     = CFG_DISPLAY_HEIGHT;input.src_pitch = ALIGN_TO(CFG_DISPLAY_WIDTH, MTK_FB_ALIGNMENT)*4;input.dst_x     = 0;input.dst_y     = 0;input.dst_w     = CFG_DISPLAY_WIDTH;input.dst_h     = CFG_DISPLAY_HEIGHT;input.aen       = 1;input.alpha     = 0xff;primary_display_config_input(&input);

int primary_display_config_input(disp_input_config* input)
{
    // all dirty should be cleared in dpmgr_path_get_last_config()memcpy((void*)&data_config, (void*)dpmgr_path_get_last_config(pgc->dpmgr_handle), sizeof(disp_ddp_path_config));// no need do this dirty = 0; dpmgr_path_get_last_config do this.data_config.dst_dirty = 0;data_config.ovl_dirty = 0;data_config.rdma_dirty = 0;data_config.wdma_dirty = 0;if (pgc->mode == DIRECT_LINK_MODE || pgc->mode == DECOUPLE_MODE) {if (dpmgr_path_is_busy(pgc->dpmgr_handle)) {  --检查各个模块是否处于busy状态if (primary_display_is_video_mode()) {dpmgr_wait_event_timeout(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE, HZ*1);}}ret = _convert_disp_input_to_ovl(&(data_config.ovl_config[input->layer]), input);  --将各层layer信息传递给overlaydata_config.ovl_dirty = 1;  --打开overlay模块配置,执行config函数(config函数会对ovl_dirty进行check,为0则直接返回,相当于bypass)ret = dpmgr_path_config(pgc->dpmgr_handle, &data_config, primary_display_use_cmdq);// this is used for decouple mode, to indicate whether we need to trigger ovlpgc->need_trigger_overlay = 1;    --设置显示需要触发overlay}
}

disp_ddp_path_config *dpmgr_path_get_last_config(disp_path_handle dp_handle)
{ddp_path_handle handle = (ddp_path_handle)dp_handle;handle->last_config.ovl_dirty  =0;handle->last_config.rdma_dirty =0;handle->last_config.wdma_dirty =0;handle->last_config.dst_dirty =0;return &handle->last_config;
}

<3.3>填充framebuffer并显示到lcm上

mt_disp_fill_rect(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT, 0x0);

void mt_disp_fill_rect(UINT32 left, UINT32 top,UINT32 right, UINT32 bottom,UINT32 color)
{dprintf(INFO, "[lk logo: %s %d]\n",__FUNCTION__,__LINE__);init_fb_screen();RECT_REGION_T rect = {left, top, right, bottom};fill_rect_with_color(mt_get_fb_addr(), rect, color, phical_screen);
}

void init_fb_screen()
{unsigned int fb_size = mt_get_fb_size();logo_addr = mt_get_logo_db_addr();......phical_screen.need180Adjust = 1;   // need sync with chip driverif (0 == strncmp(MTK_LCM_PHYSICAL_ROTATION, "270", 3)) {phical_screen.rotation = 270;} else if (0 == strncmp(MTK_LCM_PHYSICAL_ROTATION, "90", 2)) {phical_screen.rotation = 90;} else if (0 == strncmp(MTK_LCM_PHYSICAL_ROTATION, "180", 3) && (phical_screen.need180Adjust == 1)) {phical_screen.rotation = 180;} else {phical_screen.rotation = 0;}......
}

void fill_rect_with_color(void *fill_addr, RECT_REGION_T rect, unsigned int src_color, LCM_SCREEN_T phical_screen)
{fill_rect_with_color_by_32bit((unsigned int *)fill_addr,  rect, src_color, phical_screen);
}

//根据旋转的角度填充fbvoid fill_rect_with_color_by_32bit(unsigned int *fill_addr, RECT_REGION_T rect, unsigned int src_color, LCM_SCREEN_T phical_screen)
{for(i = rect.top; i < rect.bottom; i++){for(j = rect.left; j < rect.right; j++){switch (phical_screen.rotation){case 90:dst_addr = fill_addr + ((virtual_width * i + virtual_width - j));break;case 270:dst_addr = fill_addr + ((virtual_width * (virtual_height - i - 1)+ j));break;case 180:dst_addr = fill_addr + virtual_width * (virtual_height - i)- j-1;break;default:dst_addr = fill_addr + virtual_width * i + j;}fill_point_buffer(dst_addr, src_color, phical_screen, 32);}}
}

mt_disp_update(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);

//刷新fb有关的cache,并且调用各个模块start和trigger函数将fb内容更新到lcm上void mt_disp_update(UINT32 x, UINT32 y, UINT32 width, UINT32 height)
{arch_clean_cache_range((unsigned int)fb_addr, DISP_GetFBRamSize());primary_display_trigger(TRUE);
}

int primary_display_trigger(int blocking)
{dpmgr_path_start(pgc->dpmgr_handle, primary_display_use_cmdq);dpmgr_path_trigger(pgc->dpmgr_handle, NULL,primary_display_use_cmdq);
}

五.替换lk开机启动画面

/dev/logo/目录下定义了一系列分辨率的logo文件夹,/project/xx.mk包含如下的宏定义,即logo画面在/dev/logo/fhd目录下

BOOT_LOGO := fhd

里面包含了充电/normal boot等类型的开机画面,可以将自己需求的画面替换之。

 

  

  

转载于:https://www.cnblogs.com/ant-man/p/9139104.html

mtk-lk display代码分析相关推荐

  1. Android MTK Camera驱动代码分析

    一.Camera调用过程:      imgsensor起到承上启下的作用,在系统起来时会创建整个camera驱动运行的环境,其中主要的文件和函数如下框图所示,先设备挂载时会调用注册platform设 ...

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

    一.LK显示代码分析 1.platform_init代码分析 //framebuffer的大小 g_fb_size = mt_disp_get_vram_size(); //framebuffer的起 ...

  3. MTK LIGHT 代码分析

    MTK LIGHT 代码分析 项目上需要做些客制化的东西,需要用到light 一块的东西,好久以前看过,但是没有记录下来,这次重新看看,然后记录下来. lightservice start priva ...

  4. host速度 mtk usb_mtk-usb代码分析之usb gadget

    基于mt6750T,Android 7.0,kernel 3.18.35,本文主要从USB设备的角度进行分析.(代码部分有省略) 我们的android手机通过usb连入电脑,可以选择多种模式,例如传输 ...

  5. host速度 mtk usb_mtk-usb代码分析之枚举过程

    基于mt6750T,Android 7.0,kernel 3.18.35,本文主要简述了USB的枚举过程,主要是从host的角度来看. 一.USB的拓扑结构 简单来说,USB由host和device两 ...

  6. android启动流程之lk,Android系统之LK启动流程分析(一)

    1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...

  7. mtk LK流程简介

    mtk LK流程简介 一.LK简介 Lk的主要功能: 1.初始化硬件模块,比如时钟,中断,UART,USB,LCD,PMIC,eMMC等.打开MMU,使能I/D-cache,加速lk执行,显示logo ...

  8. DRM驱动代码分析:开机过程中显示驱动做了什么

    前言: 有些信息是在网上查资料后总结的,没有去追代码验证.如果有说得不对的地方,欢迎提出指正.感谢! 手机启动的大致流程 1.长按开机键 2.执行存储在ROM里(应该是某一个固定地址或是预定义的地址) ...

  9. crt0.S(_main)代码分析

    crt0,S(_main)代码分析 --- 1. 设置sp寄存器地址 //设置SP栈指针 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG ...

最新文章

  1. 7纳米duv和euv_要超车台积电 三星宣布采用EUV技术7纳米制程完成验证
  2. 第九周项目三-人数不定的工资类
  3. left join 临时表_不懂SQL优化?那你就OUT了——表连接的优化
  4. 为什么本地图片都不能直接浏览器_微软工程师帮助谷歌解决Chromium浏览器中一个很长久的细节问题...
  5. 科技感的动态设计方法-2
  6. Google Mock启蒙篇 [1] (Google C++ Mocking Framework for Dummies 翻译)
  7. python字典的键可以是元组吗_python字典的键可以是元组吗
  8. psutil python库
  9. Java基础系列13:JDBC批处理简介
  10. B. Creating the Contest(水题)
  11. python基础语法手册-Python学习手册(第4版)pdf
  12. 合作式智能运输系统 车用通信系统应用层及应用数据交互标准 第二阶段_为什么一定要了解OPC UA TSN——未来的工业通信标准...
  13. java 可达性算法实现,垃圾标记阶段算法之可达性分析算法
  14. 机器学习项目中不可忽视的一个密辛 - 大数定理
  15. python 极坐标图_Python中的极坐标图问题,未考虑角度
  16. java j2ee j2se_Java EE / J2EE与J2SE / JDK版本之间的关联
  17. 如何应对VB中对象库注册失败(MSCOMCTL.OCX)
  18. 微信公众号开发-----实现模板、图文、文本、音乐、图片推送
  19. 【十四】【vlc-android】aout音频输出模块源码实现分析【Part 2】
  20. Android 应用程序开发

热门文章

  1. 改进YOLOv7系列:首发结合最新Centralized Feature Pyramid集中特征金字塔,通过COCO数据集验证强势涨点
  2. 容联云发送短信验证码
  3. MOB短信验证码开发
  4. 数据结构——树(树的基本概念)
  5. 【面朝大厂】面试官:你对MySQL中的索引了解多少?
  6. 当你输入信用卡号码的时候,有没有担心输错了而造成损失呢?其实可以不必这么担心,因为并不是一个随便的信用卡号码都是合法的,它必须通过Luhn算法来验证通过。 该校验的过程:1、从卡号最后一位数字开始,逆
  7. C# Spire.XLS 无限制 使用教程
  8. 康奈尔大学统计学数据科学与计算机学院,美国康奈尔大学统计学专业.pdf
  9. 51单片机学习:LCD12864液晶显示实验(带字库)
  10. Nvidia GPU的浮点计算能力(FP64/FP32/FP16)