一.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; --用於匹配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;
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_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 { 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數組,每類事件對應一個wq DDP_SCENARIO_ENUM scenario; --ddp index,根據index從ddp二維數組獲得ddp disp_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]; --這里只連接一個lcm isLCMFound = 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->params plcm->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、DPI params->width = FRAME_WIDTH; --顯示寬度 params->height = FRAME_HEIGHT; --顯示高度 // enable tearing-free params->dbi.te_mode = LCM_DBI_TE_MODE_DISABLED; --TE模式disable params->dbi.te_edge_polarity = LCM_POLARITY_RISING; --DBI相關,這里表示上升沿采樣 #if (LCM_DSI_CMD_MODE) params->dsi.mode = CMD_MODE; #else params->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; --vbp params->dsi.vertical_frontporch = 8; --vfp params->dsi.vertical_active_line = FRAME_HEIGHT; --vact params->dsi.horizontal_sync_active = 50; --hs params->dsi.horizontal_backporch = 100; --hbp params->dsi.horizontal_frontporch = 100; --hfp params->dsi.horizontal_active_pixel = FRAME_WIDTH; --hact params->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_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
}
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 mutex ddp_mutex_set(handle->hwmutexid, handle->scenario, handle->mode, cmdqHandle); //connect path; ddp_connect_path(handle->scenario,cmdqHandle); // each module init for ( 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之前是否連接OK
int 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信息傳遞給overlay data_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 ovl pgc->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 driver if (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); }
//根據旋轉的角度填充fb
void 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等類型的開機畫面,可以將自己需求的畫面替換之。