原创文章,转载请注明出处
注:该博客基于MT6739 Android O分析,贴出来的代码会针对性删减,只留重点部分
总体流程
platform_init -> mt_disp_init -> primary_display_init -> disp_lcm_probe
我们先从primary_display_init函数开始
vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6739\primary_display.c
int primary_display_init(char *lcm_name)
{
DISPFUNC()
……
if (pgc->plcm == NULL)
pgc->plcm = disp_lcm_probe( lcm_name, LCM_INTERFACE_NOTDEFINED);//disp_lcm_probe:返回一个disp_lcm_handle*的结构体指针,里面存放了找到的lcm驱动的关键参数,后面我们会详细分析
if (pgc->plcm == NULL) {
DISPCHECK("disp_lcm_probe returns null\n");
ret = DISP_STATUS_ERROR;
goto done;
} else {
DISPCHECK("disp_lcm_probe SUCCESS\n");
}
lcm_param = disp_lcm_get_params(pgc->plcm); //获取对应lcm的params
if (lcm_param == NULL) {
DISPERR("get lcm params FAILED\n");
ret = DISP_STATUS_ERROR;
goto done;
}
……
ret = disp_lcm_init(pgc->plcm); //走对应lcm的初始化操作
……
}
这里的pgc稍微有点特别,是个全局结构体指针变量,以下是它的定义。
具体就是申请并初始化了一个display_primary_path_context结构体的内存空间,然后返回指向这个结构体的指针
#define pgc _get_context()
static display_primary_path_context* _get_context(void)
{
static int is_context_inited = 0;
static display_primary_path_context g_context;
if (!is_context_inited) {
memset((void*)&g_context, 0, sizeof(display_primary_path_context));
is_context_inited = 1;
}
return &g_context;
}
下面重点分析一下disp_lcm_probe函数
vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6739\disp_lcm.c
disp_lcm_handle* disp_lcm_probe(char* plcm_name, LCM_INTERFACE_ID lcm_id)
{
DISPFUNC();
//int ret = 0;
bool isLCMFound = false;
bool isLCMConnected = false;
LCM_DRIVER *lcm_drv = NULL; //LCM_DRIVER lcm操作函数结构体
LCM_PARAMS *lcm_param = NULL; //LCM_PARAMS lcm参数结构体
disp_lcm_handle *plcm = NULL;
if (_lcm_count() == 0) { //_lcm_count()返回兼容的lcm个数
DISPERR("no lcm driver defined in linux kernel driver\n");
return NULL;
} else if (_lcm_count() == 1) {
lcm_drv = lcm_driver_list[0]; //lcm_driver_list:指针数组,存放各个lcm的LCM_DRIVER信息
isLCMFound = true; //lcm是否找到标志位
} else {
// in lk, plcm_name should always be NULL
if (plcm_name == NULL) {
int i = 0;
disp_path_handle handle = NULL;
disp_lcm_handle hlcm;
disp_lcm_handle *plcm = &hlcm;
LCM_PARAMS hlcm_param;
for (i=0; i<_lcm_count(); i++) { //_lcm_count>1的时候,进入for循环
memset((void*)&hlcm, 0, sizeof(disp_lcm_handle));
memset((void*)&hlcm_param, 0, sizeof(LCM_PARAMS));
lcm_drv= lcm_driver_list[i]; //循环获取各个lcm的LCM_DRIVER结构体指针
lcm_drv->get_params(&hlcm_param); //设置对应lcm的各种param参数并存放到hlcm_param中
plcm->drv = lcm_drv; //填充plcm drv成员
plcm->params = &hlcm_param; //填充plcm params成员
plcm->lcm_if_id = plcm->params->lcm_if;
DISPDBG("we will check lcm: %s\n", lcm_drv->name);
if (lcm_id == LCM_INTERFACE_NOTDEFINED ||(lcm_id != LCM_INTERFACE_NOTDEFINED && plcm->lcm_if_id == lcm_id)) {
handle = _display_interface_path_init(plcm); //ddp dsi的一些初始化,后面会分析
if (handle == NULL) {
DISPERR("_display_interface_path_init returns NULL\n");
goto FAIL;
}
if (lcm_drv->init_power) { //检查lcm驱动中init_power接口是否有实现
lcm_drv->init_power(); //如果有实现,先进行init power操作。针对一些对上电有特殊要求的lcm
}
if (lcm_drv->compare_id != NULL) { //检查lcm驱动中compare_id接口是否有实现,兼容的关键接口,用来判断当前lcm驱动是否与现有硬件匹配
if (lcm_drv->compare_id() != 0) { //调用compare_id接口
isLCMFound = true; //compare_id成功则说明找到了lcm,标志位置为true
_display_interface_path_deinit(handle);
DISPMSG("we will use lcm: %s\n", lcm_drv->name);
break; //找到lcm之后退出for循环
}
}
_display_interface_path_deinit(handle);
}
}
if (isLCMFound == false) { //如果以上循环都匹配不到lcm,则标志位为false
DISPERR("we have checked all lcm driver, but no lcm found\n");
lcm_drv = lcm_driver_list[0]; //标志位为false时默认加载lcm_driver_list数组中第一个lcm
isLCMFound = true;
}
} else {
int i = 0;
for (i=0; i<_lcm_count(); i++) {
lcm_drv = lcm_driver_list[i];
if (!strcmp(lcm_drv->name, plcm_name)) {
isLCMFound = true;
break;
}
}
}
}
if (isLCMFound == false) {
DISPERR("FATAL ERROR!!!No LCM Driver defined\n");
return NULL;
}
……
return plcm; //最后将plcm返回
}
到这里lcm的驱动就加载完成了,在之后platform_init会继续往下走,显示lk的开机logo。
下来我们来看看上面提到的_display_interface_path_init函数做了哪些工作
vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6739\disp_lcm.c
disp_path_handle _display_interface_path_init(disp_lcm_handle *plcm)
{
DISP_MODULE_ENUM dst_module = DISP_MODULE_NONE;
disp_ddp_path_config data_config;
disp_path_handle handle = NULL;
DISPFUNC();
if (plcm == NULL) {
DISPERR("plcm is null\n");
return NULL;
}
handle = dpmgr_create_path(DDP_SCENARIO_PRIMARY_RDMA0_DISP, NULL); //这里应该是根据disp来配置handle的一些结构体成员
if (handle) {
DISPCHECK("dpmgr create path SUCCESS(0x%p)\n", handle);
} else {
DISPCHECK("dpmgr create path FAIL\n");
return NULL;
}
dst_module = _get_dst_module_by_lcm(plcm); //根据lcm接口类型配置disp的module,这里返回的是DISP_MODULE_DSI0
dpmgr_path_set_dst_module(handle, dst_module); //将二维数组module_list_scenario[handle->scenario]中的最后一个数据设置为DISP_MODULE_DSI0
DISPCHECK("dpmgr set dst module FINISHED(%s)\n", ddp_get_module_name(dst_module));
dpmgr_set_lcm_utils(handle, plcm->drv); //调用ddp_modules_driver[DISP_MODULE_DSI0]->set_lcm_utils,最终调用lcm_drv->set_util_funcs(utils)填充对应lcm驱动中的utils结构体成员
dpmgr_path_init(handle, CMDQ_DISABLE); //调用ddp模块驱动的初始化函数
……
return handle;
}
以上相关函数位于vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6739\ddp_manager.c和
vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6739\ddp_path.c中
handle = dpmgr_create_path(DDP_SCENARIO_PRIMARY_RDMA0_DISP, NULL);
disp_path_handle dpmgr_create_path(DDP_SCENARIO_ENUM scenario, cmdqRecHandle cmdq_handle)
{
……
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; //将scenario成员设置为DDP_SCENARIO_PRIMARY_RDMA0_DISP
path_handle->hwmutexid = acquire_mutex(scenario);
……
}
dpmgr_path_set_dst_module(handle, dst_module);
int dpmgr_path_set_dst_module(disp_path_handle dp_handle,DISP_MODULE_ENUM dst_module)
{
ASSERT(dp_handle != NULL);
ddp_path_handle handle = (ddp_path_handle)dp_handle;
ASSERT((handle->scenario >= 0 && handle->scenario < DDP_SCENARIO_MAX));
DISP_LOG_I("set dst module on scenario %s, module %s\n",
ddp_get_scenario_name(handle->scenario),ddp_get_module_name(dst_module));
return ddp_set_dst_module(handle->scenario, dst_module);//这里我们知道两个参数分别为DDP_SCENARIO_PRIMARY_RDMA0_DISP和DISP_MODULE_DSI0
}
int ddp_set_dst_module(DDP_SCENARIO_ENUM scenario, DISP_MODULE_ENUM dst_module)
{
int i = 0;
DDPMSG("ddp_set_dst_module, scenario=%s, dst_module=%s\n",
ddp_get_scenario_name(scenario), ddp_get_module_name(dst_module));
if (ddp_find_module_index(scenario, dst_module) > 0) {
DDPDBG("%s is already on path\n", ddp_get_module_name(dst_module));
return 0;
}
i = ddp_get_module_num_l(module_list_scenario[scenario]) - 1;//获取module_list_scenario[DDP_SCENARIO_PRIMARY_RDMA0_DISP]的长度
ASSERT(i >= 0);
if (dst_module == DISP_MODULE_DSIDUAL) { //dst_module==DISP_MODULE_DSI0
if (i < (DDP_ENING_NUM - 1)) {
module_list_scenario[scenario][i++] = DISP_MODULE_SPLIT0;
} else {
DDPERR("set dst module over up bound\n");
return -1;
}
} else {
if (ddp_get_dst_module(scenario) == DISP_MODULE_DSIDUAL) {
if (i >= 1) {
module_list_scenario[scenario][i--] = -1;
} else {
DDPERR("set dst module over low bound\n");
return -1;
}
}
}
module_list_scenario[scenario][i] = dst_module; //将module_list_scenario[scenario]最后一个成员设置为DISP_MODULE_DSI0
if (scenario == DDP_SCENARIO_PRIMARY_ALL)
ddp_set_dst_module(DDP_SCENARIO_PRIMARY_DISP, dst_module);
else if (scenario == DDP_SCENARIO_SUB_ALL)
ddp_set_dst_module(DDP_SCENARIO_SUB_RDMA1_DISP, dst_module);
ddp_print_scenario(scenario);
return 0;
}
到这里我们可以知道
module_list_scenario[DDP_SCENARIO_PRIMARY_RDMA0_DISP][]=
{
DISP_MODULE_RDMA0, DISP_MODULE_PWM0, DISP_MODULE_DSI0,
DISP_MODULE_NONE,
}
dpmgr_set_lcm_utils(handle, plcm->drv);
int dpmgr_set_lcm_utils(disp_path_handle dp_handle, void *lcm_drv)
{
unsigned int i=0;
int module_name;
ASSERT(dp_handle != NULL);
ddp_path_handle handle = (ddp_path_handle)dp_handle;
unsigned int *modules = ddp_get_scenario_list(handle->scenario);
int module_num = ddp_get_module_num(handle->scenario);
DISP_LOG_V("path set lcm drv handle 0x%p\n",handle);
for ( i=0; i< module_num; i++) {
module_name = modules[i];
if (ddp_modules_driver[module_name] != 0) {//ddp_modules_driver[DISP_MODULE_PWM0]=NULL
if ((ddp_modules_driver[module_name]->set_lcm_utils!= 0)&&lcm_drv) {//ddp_modules_driver[DISP_MODULE_RDMA0]->set_lcm_utils=NULL
DISP_LOG_I("%s set lcm utils\n",ddp_get_module_name(module_name));
ddp_modules_driver[module_name]->set_lcm_utils(module_name, lcm_drv); //调用ddp_modules_driver[DISP_MODULE_DSI0]->set_lcm_utils
}
}
}
return 0;
}
ddp_modules_driver[module_name]->set_lcm_utils(module_name, lcm_drv);
int ddp_dsi_set_lcm_utils(DISP_MODULE_ENUM module, LCM_DRIVER *lcm_drv)
{
LCM_UTIL_FUNCS *utils = NULL;
……
utils->set_reset_pin = lcm_set_reset_pin; //设置复位脚操作函数
utils->udelay = lcm_udelay; //设置微妙延时函数
utils->mdelay = lcm_mdelay; //设置毫秒延时函数
if (module == DISP_MODULE_DSI0) { //设置dsi写数据操作接口
utils->dsi_set_cmdq = DSI_set_cmdq_wrapper_DSI0;
utils->dsi_set_cmdq_V2 = DSI_set_cmdq_V2_Wrapper_DSI0;
utils->dsi_set_cmdq_V3 = DSI_set_cmdq_V3_Wrapper_DSI0;
utils->dsi_dcs_read_lcm_reg_v2 = DSI_dcs_read_lcm_reg_v2_wrapper_DSI0;
utils->dsi_set_cmdq_V22 = DSI_set_cmdq_V2_DSI0;
utils->dsi_set_cmdq_V11 = DSI_set_cmdq_V11_wrapper_DSI0;
}
……
lcm_drv->set_util_funcs(utils); //调用对应lcm驱动中set_util_funcs接口,将utils传出去
……
}
到这里整个lk关于lcm驱动的加载过程就都介绍完了,做完上面介绍的流程之后,在跑完lk准备调到kernel之前,lk会通过fdt将加载到的lcm相关信息传给kernel,以便后续的操作
vendor\mediatek\proprietary\bootable\bootloader\lk\app\mt_boot\mt_boot.c
int boot_linux_fdt(void *kernel, unsigned *tags,
unsigned machtype,
void *ramdisk, unsigned ramdisk_sz)
{
……
ptr = (char *)target_atag_videolfb((unsigned *)buf, FDT_BUFF_SIZE);
ret = fdt_setprop(fdt, offset, "atag,videolfb", buf, ptr - buf);
if (ret) {
assert(0);
return FALSE;
}
……
}
unsigned *target_atag_videolfb(unsigned *ptr, size_t buf_size)
{
extern unsigned long long fb_addr_pa_k;
const char *lcmname = mt_disp_get_lcm_id();
unsigned *p = ptr;
char *data = NULL;
size_t data_cnt = 0;
size_t data_size = 0;
*p++ = (unsigned)(fb_addr_pa_k & 0xFFFFFFFF);
data_cnt++;
*p++ = (unsigned)((fb_addr_pa_k >> 32) & 0xFFFFFFFF);
data_cnt++;
*p++ = DISP_IsLcmFound();
data_cnt++;
*p++ = mt_disp_get_lcd_time();
data_cnt++;
*p++ = DISP_GetVRamSize();
data_cnt++;
/*
* ATAG size for ATAG_VIDEOLFB is aligned to a word and the unit of size is 4 bytes.
*/
data = (char *)p;
data_size = buf_size - data_cnt * sizeof(unsigned);
strncpy(data, lcmname, data_size - 1);
data[data_size - 1] = '\0';
p += (ROUNDUP(strlen(lcmname) + 1, 4) / 4);
dprintf(CRITICAL, "videolfb - fb_base = 0x%llx\n", fb_addr_pa_k);
dprintf(CRITICAL, "videolfb - islcmfound = %d\n", DISP_IsLcmFound());
dprintf(CRITICAL, "videolfb - fps = %d\n", mt_disp_get_lcd_time());
dprintf(CRITICAL, "videolfb - vram = %d\n", DISP_GetVRamSize());
dprintf(CRITICAL, "videolfb - lcmname = %s\n", lcmname);
return (unsigned *)p;
}
后续kernel会通过fdt获取atag,videolfb这个标签中lcm的lcmname fps等相关信息,这个我们在下一篇讲述