MTK平台LCM驱动加载过程-kernel阶段

原创文章,转载请注明出处

注:该博客基于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驱动的加载过程就介绍完了,有什么不足之处烦请指出。

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值