当前位置: 首页 > 工具软件 > mdp > 使用案例 >

2 MDP,DSI,FB代码分析

韦昊焜
2023-12-01

MDP分析

MDP主要对使用的硬件资源进行初始化,同时在fb设备中注册mdp的使用接口。

MDP使用的设备树在:

msm8909-mdss.dtsi

主要代码:

static int mdp3_probe(struct platform_device *pdev)
{
	int rc;
	/* 创建msm_mdp_interface结构体,里边包含mdp的初始化等,这些函数会在fb初始化时调用 */
    static struct msm_mdp_interface mdp3_interface = {
	.init_fnc = mdp3_init,
	.fb_mem_get_iommu_domain = mdp3_fb_mem_get_iommu_domain,
	.panel_register_done = mdp3_panel_register_done,
	.fb_stride = mdp3_fb_stride,
	.check_dsi_status = mdp3_check_dsi_ctrl_status,
	};
    /* 初始化mdp3_res */
	mdp3_res = devm_kzalloc(&pdev->dev, sizeof(struct mdp3_hw_resource),
				GFP_KERNEL);
	/* mdp3_res->mdss_util执行全局变量mdss_util,初始化之,fb注册时会用到 */
	mdp3_res->mdss_util = mdss_get_util_intf();
	mdp3_res->mdss_util->get_iommu_domain = mdp3_get_iommu_domain;
	mdp3_res->mdss_util->iommu_attached = is_mdss_iommu_attached;
	mdp3_res->mdss_util->iommu_ctrl = mdp3_iommu_ctrl;
	mdp3_res->mdss_util->bus_scale_set_quota = mdp3_bus_scale_set_quota;
	mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type;
	mdp3_res->mdss_util->dyn_clk_gating_ctrl =
		mdp3_dynamic_clock_gating_ctrl;
	/* 调用改函数,将mdp3_interface赋值给全局变量mdp_instance,用fb注册时初始化mdp等操作 */
	rc = mdss_fb_register_mdp_instance(&mdp3_interface);
	/* 标志mdp已经执行probe,dsi probe会检测 */
	mdp3_res->mdss_util->mdp_probe_done = true;
}	

看下mdp3_interface.init_fnc mdp3_init实现:

static int mdp3_init(struct msm_fb_data_type *mfd)
{
	int rc;

	rc = mdp3_ctrl_init(mfd);
	if (rc) {
		pr_err("mdp3 ctl init fail\n");
		return rc;
	}

	rc = mdp3_ppp_res_init(mfd);
	if (rc)
		pr_err("mdp3 ppp res init fail\n");

	return rc;
}

主要看mdp3_ctrl_init()的实现:

int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
{
	struct device *dev = mfd->fbi->dev;
	struct msm_mdp_interface *mdp3_interface = &mfd->mdp;
	struct mdp3_session_data *mdp3_session = NULL;
	u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
	int rc;
	int splash_mismatch = 0;

	pr_info("mdp3_ctrl_init\n");
	rc = mdp3_parse_dt_splash(mfd);
	if (rc)
		splash_mismatch = 1;

	mdp3_interface->on_fnc = mdp3_ctrl_on;
	mdp3_interface->off_fnc = mdp3_ctrl_off;
	mdp3_interface->do_histogram = NULL;
	mdp3_interface->cursor_update = NULL;
	mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
	mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
	mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
	mdp3_interface->lut_update = NULL;
	mdp3_interface->configure_panel = mdp3_update_panel_info;
	...
	rc = mdp3_intf_init(mdp3_session->intf);
	return rc;
}

在msm_mdp_interface的init_fnc中,为mdp3_interface添加了更多的操作,包括上下电操作,ioctl控制函数,mdp屏幕信息获取等。

DSI驱动器

解析模组厂商提供的panel的dtsi文件,从哪个文件中能够获取到panel的mode,分辨率,还有Driver IC的初始化command;

DSI驱动在mdss_dsi.c中,其中注册了两个驱动,第一个是

static struct platform_driver mdss_dsi_driver = {
	.probe = mdss_dsi_probe,
	.remove = mdss_dsi_remove,
	.shutdown = NULL,
	.driver = {
		.name = "mdss_dsi",
		.of_match_table = mdss_dsi_dt_match,
	},
};

static int mdss_dsi_register_driver(void)
{
	return platform_driver_register(&mdss_dsi_driver);
}

另一个是:

static struct platform_driver mdss_dsi_ctrl_driver = {
	.probe = mdss_dsi_ctrl_probe,
	.remove = mdss_dsi_ctrl_remove,
	.shutdown = NULL,
	.driver = {
		.name = "mdss_dsi_ctrl",
		.of_match_table = mdss_dsi_ctrl_dt_match,
	},
};

static int mdss_dsi_ctrl_register_driver(void)
{
	return platform_driver_register(&mdss_dsi_ctrl_driver);
}

先看第一个驱动的probe函数:

static int mdss_dsi_probe(struct platform_device *pdev)
{
    struct mdss_panel_cfg *pan_cfg = NULL;
	struct mdss_util_intf *util;
	/* 获得全局变量mdp_instance,在mdp probe时注册 */
	util = mdss_get_util_intf();
    /* 将dsi资源信息保存到全局变量mdss_dsi_res,初始化dsi时钟 */
    rc = mdss_dsi_res_init(pdev);
    /* 创建qcom,mdss-dsi-ctrl设备,我们这里说的第二个驱动就是该设备对应的驱动 */
    of_platform_populate(pdev->dev.of_node, mdss_dsi_ctrl_dt_match,
				NULL, &pdev->dev);
	return rc;
}

在dis_driver中,主要获取对了dsi时钟等信息,初始化了mdss_dsi_res.然后创建了dsi_ctrl的平台设备,让其可以和之后注册的dsi_ctrl驱动相匹配。

接下来看dsi_ctrl驱动的注册:

@mdss_dsi.c
static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
{
    int rc = 0;
    struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
    const char *ctrl_name;
    
    ctrl_name = of_get_property(pdev->dev.of_node, "label", NULL);
    /* panel配置 */
    dsi_pan_node = mdss_dsi_config_panel(pdev, index);
    
    rc = dsi_panel_device_register(pdev, dsi_pan_node, ctrl_pdata);
    
    return 0;
}

首先看mdss_dsi_config_panel()配置函数中的mdss_dsi_panel_init()的实现:

@mdss_dsi_panel.c
int mdss_dsi_panel_init(struct device_node *node,
	struct mdss_dsi_ctrl_pdata *ctrl_pdata,
	int ndx)
{
	int rc = 0;
	static const char *panel_name;
	struct mdss_panel_info *pinfo;

	pinfo = &ctrl_pdata->panel_data.panel_info;

	pinfo->panel_name[0] = '\0';
	/* 设置panel名字 */
	panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL);
	
	/* 找到dsi panel节点,保存在dsi_pan_node */
	dsi_pan_node = mdss_dsi_find_panel_of_node(pdev, panel_cfg);
	/* 从dsi节点读取panel信息,并保存到结果在ctrl_pdata->panel_data.panel_info中 */
	rc = mdss_panel_parse_dt(node, ctrl_pdata);

	pinfo->dynamic_switch_pending = false;
	pinfo->is_lpm_mode = false;
	pinfo->esd_rdy = false;
    // 屏上电操作
	ctrl_pdata->on = mdss_dsi_panel_on;
	ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on;
	// 屏下电操作
	ctrl_pdata->off = mdss_dsi_panel_off;
	ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config;
	// 背光设置
	ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl;
	// 切换模式
	ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode;
	ctrl_pdata->panel_data.set_idle = mdss_dsi_panel_set_idle_mode;
	ctrl_pdata->panel_data.get_idle = mdss_dsi_panel_get_idle_mode;

	return 0;
}

在配置中,首先通过mdss_panel_parse_dt()函数解析dsi和屏信息的设备设,屏的信息,在fb注册时,将使用到。然后初始化了mdss_dsi_ctrl_pdata结构体中的操作函数指针,这些函数指针将用在fb中的熄灭屏,设置屏幕亮度等操作。

主要工作在dsi_panel_device_register()函数中完成。现在看起实现:

@mdss_dsi.c
int dsi_panel_device_register(struct platform_device *ctrl_pdev,
	struct device_node *pan_node, struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
    struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
    mipi  = &(pinfo->mipi);
    /* mipi模式 */
	pinfo->type =
		((mipi->mode == DSI_VIDEO_MODE)
			? MIPI_VIDEO_PANEL : MIPI_CMD_PANEL);
	/* 后面会有event发送给dsi,会在mdss_dsi_event_handler中处理 */
	ctrl_pdata->panel_data.event_handler = mdss_dsi_event_handler;
	ctrl_pdata->panel_data.get_fb_node = mdss_dsi_get_fb_node_cb;
	mdss_dsi_ctrl_init(&ctrl_pdev->dev, ctrl_pdata);
	
	rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data));
}

FB驱动

FB驱动在mdss_fb.c中完成,实现Linux Framebuffer的注册已经相关操作。

int __init mdss_fb_init(void)
{
	int rc = -ENODEV;

	if (fb_get_options("msmfb", NULL))
		return rc;

	if (platform_driver_register(&mdss_fb_driver))
		return rc;

	return 0;
}
module_init(mdss_fb_init);

mdss_fb_driver的实现:

static const struct of_device_id mdss_fb_dt_match[] = {
	{ .compatible = "qcom,mdss-fb",},
	{}
};
EXPORT_COMPAT("qcom,mdss-fb");

static struct platform_driver mdss_fb_driver = {
	.probe = mdss_fb_probe,
	.remove = mdss_fb_remove,
	.suspend = mdss_fb_suspend,
	.resume = mdss_fb_resume,
	.shutdown = mdss_fb_shutdown,
	.driver = {
		.name = "mdss_fb",
		.of_match_table = mdss_fb_dt_match,
		.pm = &mdss_fb_pm_ops,
	},
};

compatible为"qcom,mdss-fb"在msm8909-mdss.dtsi中有定义。因此platform_driver的probe将被调用。整个fb注册过程大都也在这其中完成。

static int mdss_fb_probe(struct platform_device *pdev)
{
	struct msm_fb_data_type *mfd = NULL;
	struct mdss_panel_data *pdata;
	struct fb_info *fbi;
	int rc;
    
    /* 获取plat_data,含有在mdss_dsi中解析出的屏信息 */
    pdata = dev_get_platdata(&pdev->dev);
    /* 分配fbi结构体,用来注册fb */
    fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
    
    /* fbi->par执行mfd,保存私有数据  mfd保存了屏的所以信息 */
    mfd = (struct msm_fb_data_type *)fbi->par;
	mfd->key = MFD_KEY;
	mfd->fbi = fbi;
	mfd->panel_info = &pdata->panel_info;
	mfd->panel.type = pdata->panel_info.type;
	mfd->fb_imgType = MDP_RGBA_8888;
	...
	platform_set_drvdata(pdev, mfd);
	/* 完成fb的注册,从mfd中取出屏信息放到fbi中,进行fb注册,并包含fb给用户空间的的操作函数 */
	rc = mdss_fb_register(mfd);
	/* 初始化mdp */
	if (mfd->mdp.init_fnc) {
		rc = mfd->mdp.init_fnc(mfd);
		if (rc) {
			pr_err("init_fnc failed\n");
			return rc;
		}
	}
	
	/* 注册背光灯设备 */
	if (!lcd_backlight_registered) {
		backlight_led.brightness = mfd->panel_info->brightness_max;
		backlight_led.max_brightness = mfd->panel_info->brightness_max;
		if (led_classdev_register(&pdev->dev, &backlight_led))
			pr_err("led_classdev_register failed\n");
		else
			lcd_backlight_registered = 1;
	}
	
	mdss_fb_create_sysfs(mfd);
	/* 发送注册事件,处理的hanlder在mdss_dsi中定义 */
	mdss_fb_send_panel_event(mfd, MDSS_EVENT_FB_REGISTERED, fbi);
}

在上面代码中,首先通过dev_get_platdata()获取屏的信息,然后创建一个msm_fb_data_type结构体,将屏信息全部放在了mfd中。调用mdss_fb_register()进行fb的注册。

 类似资料: