原创文章,转载请注明出处
注:该博客基于MT6739 Android O分析,贴出来的代码会针对性删减,只留重点部分
我们已经在上一篇博客里介绍了lk阶段关于lcm驱动的加载过程,这一章我们分析一下kernel阶段的。相关流程与lk阶段有相似之处也有不同之处。
kernel-4.4\drivers\misc\mediatek\video\mt6739\videox\mtkfb.c
static int mtkfb_probe(struct platform_device *pdev)
{
struct mtkfb_device *fbdev = NULL;
struct fb_info *fbi;
int init_state;
int r = 0;
/* struct platform_device *pdev; */
long dts_gpio_state = 0;
DISPMSG("mtkfb_probe name [%s] = [%s][%p]\n", pdev->name, pdev->dev.init_name, (void *)&pdev->dev);
_parse_tag_videolfb(); //通过设备树或获取lk传过来关于lcm的name fps等信息
init_state = 0;
/* pdev = to_platform_device(dev); */
/* repo call DTS gpio module, if not necessary, invoke nothing */
dts_gpio_state = disp_dts_gpio_init_repo(pdev);
if (dts_gpio_state != 0)
dev_err(&pdev->dev, "retrieve GPIO DTS failed.");
fbi = framebuffer_alloc(sizeof(struct mtkfb_device), &(pdev->dev));
if (!fbi) {
DISPERR("unable to allocate memory for device info\n");
r = -ENOMEM;
goto cleanup;
}
mtkfb_fbi = fbi;
fbdev = (struct mtkfb_device *)fbi->par;
fbdev->fb_info = fbi;
fbdev->dev = &(pdev->dev);
dev_set_drvdata(&(pdev->dev), fbdev);
DISPMSG("mtkfb_probe: fb_pa = %pa\n", &fb_base);
disp_hal_allocate_framebuffer(fb_base, (fb_base + vramsize - 1),
(unsigned long *)(&fbdev->fb_va_base), &fb_pa);
fbdev->fb_pa_base = fb_base;
primary_display_set_frame_buffer_address((unsigned long)(fbdev->fb_va_base), fb_pa);
primary_display_init(mtkfb_find_lcm_driver(), lcd_fps, is_lcm_inited); //display的一些初始化操作,后面分析
……
}
前面lk阶段的时候我们讲过,在跑完lk准备调到kernel之前,lk会通过fdt将加载到的lcm相关信息传给kernel
static int _parse_tag_videolfb(void)
{
int ret;
struct device_node *chosen_node;
DISPCHECK("[DT][videolfb]isvideofb_parse_done = %d\n", is_videofb_parse_done);
if (is_videofb_parse_done)
return 0;
chosen_node = of_find_node_by_path("/chosen"); //在全局链表of_allnodes中,查找/chosen
if (!chosen_node)
chosen_node = of_find_node_by_path("/chosen@0");
if (chosen_node) {
ret = __parse_tag_videolfb(chosen_node); //在chosen_node中查找匹配的property,获取里边的属性值,也就是lk传来的关于lcm name fps等信息
if (!ret)
goto found;
ret = __parse_tag_videolfb_extra(chosen_node);
if (!ret)
goto found;
} else {
DISPCHECK("[DT][videolfb] of_chosen not found\n");
}
return -1;
found:
is_videofb_parse_done = 1;
DISPCHECK("[DT][videolfb] islcmfound = %d\n", islcmconnected);
DISPCHECK("[DT][videolfb] is_lcm_inited = %d\n", is_lcm_inited);
DISPCHECK("[DT][videolfb] fps = %d\n", lcd_fps);
DISPCHECK("[DT][videolfb] fb_base = 0x%lx\n", (unsigned long)fb_base);
DISPCHECK("[DT][videolfb] vram = 0x%x (%d)\n", vramsize, vramsize);
DISPCHECK("[DT][videolfb] lcmname = %s\n", mtkfb_lcm_name);
return 0;
}
static int __parse_tag_videolfb(struct device_node *node)
{
struct tag_videolfb *videolfb_tag = NULL;
unsigned long size = 0;
videolfb_tag = (struct tag_videolfb *)of_get_property(node, "atag,videolfb", (int *)&size); //获取“atag,videolfb”属性值
if (videolfb_tag) {
memset((void *)mtkfb_lcm_name, 0, sizeof(mtkfb_lcm_name));
strncpy((char *)mtkfb_lcm_name, videolfb_tag->lcmname, sizeof(mtkfb_lcm_name)); //得到lcm name
mtkfb_lcm_name[strlen(videolfb_tag->lcmname)] = '\0';
lcd_fps = videolfb_tag->fps; //得到fps
if (lcd_fps == 0)
lcd_fps = 6000;
islcmconnected = videolfb_tag->islcmfound;
vramsize = videolfb_tag->vram;
fb_base = videolfb_tag->fb_base;
is_lcm_inited = 1;
return 0;
}
DISPCHECK("[DT][videolfb] videolfb_tag not found\n");
return -1;
}
到这里,我们就得到从lk传过来的关于lcm name fps等信息了,kernel有了这些信息之后,就可以省去通过compare_id去加载lcm驱动的操作,所以读id的操作只在lk进行,kernel是不会有类似的操作的。
kernel-4.4\drivers\misc\mediatek\video\mt6739\videox\primary_display.c
int primary_display_init(char *lcm_name, unsigned int lcm_fps, int is_lcm_inited)
{
……
pgc->plcm = disp_lcm_probe(lcm_name, LCM_INTERFACE_NOTDEFINED, is_lcm_inited); //lcm probe函数,与lk阶段类似
if (unlikely(pgc->plcm == NULL)) {
DISPDBG("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 (unlikely(lcm_param == NULL)) {
DISPERR("get lcm params FAILED\n");
ret = DISP_STATUS_ERROR;
goto done;
}
……
if (likely(primary_display_mode == DIRECT_LINK_MODE)) {
_build_path_direct_link(); //与lk阶段的_display_interface_path_init类似
pgc->session_mode = DISP_SESSION_DIRECT_LINK_MODE;
DISPINFO("primary display is DIRECT LINK MODE\n");
} else {
DISPINFO("primary display mode is WRONG\n");
}
……
}
struct disp_lcm_handle *disp_lcm_probe(char *plcm_name, LCM_INTERFACE_ID lcm_id, int is_lcm_inited)
{
……
if (_lcm_count() == 0) {
DISPERR("no lcm driver defined in linux kernel driver\n");
return NULL;
} else if (_lcm_count() == 1) { //lcm count为1时
if (plcm_name == NULL) { //plcm_name为lk传过来的lcm name不会为空
lcm_drv = lcm_driver_list[0];
isLCMFound = true;
isLCMInited = false;
DISPCHECK("LCM Name NULL\n");
} else {
lcm_drv = lcm_driver_list[0]; //lcm count为1时,默认加载第一个lcm driver
if (strcmp(lcm_drv->name, plcm_name)) { //与plcm_name比较,若lk与kernel匹配不上,则直接报致命错误,所以lk与kernel中的lcm_driver_list数组要配置一致,不然开不了机
DISPERR
("FATAL ERROR!!!LCM Driver defined in kernel(%s) is different with LK(%s)\n",
lcm_drv->name, plcm_name);
return NULL;
}
isLCMInited = true;
isLCMFound = true;
}
if (!is_lcm_inited) {
isLCMFound = true;
isLCMInited = false;
}
lcmindex = 0;
} else { //lcm count大于1
if (plcm_name == NULL) {
/* TODO: we need to detect all the lcm driver */
} else {
int i = 0;
for (i = 0; i < _lcm_count(); i++) { //循环与lcm_driver_list数组对比,直到找到
lcm_drv = lcm_driver_list[i];
if (!strcmp(lcm_drv->name, plcm_name)) {
isLCMFound = true;
isLCMInited = true;
lcmindex = i;
break;
}
}
if (!isLCMFound) { //相同的,倘若lk与kernel中的lcm_driver_list数组配置不一致,则有可能出现lcm加载失败的情况
DISPERR
("FATAL ERROR: can't found lcm driver:%s in linux kernel driver\n",
plcm_name);
} else if (!is_lcm_inited) {
isLCMInited = false;
}
}
/* TODO: */
}
if (isLCMFound == false) {
DISPERR("FATAL ERROR!!!No LCM Driver defined\n");
return NULL;
}
……
}
static int _build_path_direct_link(void)
{
int ret = 0;
DISPFUNC();
pgc->mode = DIRECT_LINK_MODE;
pgc->dpmgr_handle = dpmgr_create_path(DDP_SCENARIO_PRIMARY_DISP, pgc->cmdq_handle_config); //配置handle的一些结构体成员
if (pgc->dpmgr_handle) {
DISPDBG("dpmgr create path SUCCESS(%p)\n", pgc->dpmgr_handle);
} else {
DISPERR("dpmgr create path FAIL\n");
return -1;
}
dpmgr_set_lcm_utils(pgc->dpmgr_handle, pgc->plcm->drv); //调用ddp_modules_driver[DISP_MODULE_DSI0]->set_lcm_utils,最终调用lcm_drv->set_util_funcs(utils)填充对应lcm驱动中的utils结构体成员,与lk是类似的,这里就不作过多介绍
dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC);
dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE);
dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_START);
return ret;
}
有了lk阶段的介绍,其实kernel中的许多操作都有点类似,因此不是很复杂,到这里有关lcm驱动的加载过程就介绍完了,有什么不足之处烦请指出。