platform:msm8976 android 7.11
- 在lk层,常用的方法是通过读取panel的id来确定当前使用的panel的型号,然后将panel的型号放在cmdline里面,如下:
project:/ # cat /proc/cmdline
… … … androidconfig.secureboot=enabled mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_ili9885_lide_hcg_auo_fhd_video:1:none:cfg:single_dsi … … … - 在kernel层,通过cmdline寻找panel的dtsi文件节点并得到属性,这个过程是在mdss_dsi_config_panel函数进行的,如下:
(x:\work\project\kernel\drivers\video\msm\mdss\Mdss_dsi.c)mdss_dsi_ctrl_probe–>mdss_dsi_config_panel
static struct device_node *mdss_dsi_config_panel(struct platform_device *pdev)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = platform_get_drvdata(pdev);
char panel_cfg[MDSS_MAX_PANEL_LEN];
struct device_node dsi_pan_node = NULL;
int rc = 0;
if (!ctrl_pdata) {
pr_err("%s: Unable to get the ctrl_pdata\n", func);
return NULL;
}
/ DSI panels can be different between controllers /
rc = mdss_dsi_get_panel_cfg(panel_cfg, ctrl_pdata);
if (!rc)
/ dsi panel cfg not present /
pr_warn("%s:%d:dsi specific cfg not present\n",
func, LINE);
/ find panel device node */
dsi_pan_node = mdss_dsi_find_panel_of_node(pdev, panel_cfg);
if (!dsi_pan_node) {
pr_err("%s: can’t find panel node %s\n", func, panel_cfg);
of_node_put(dsi_pan_node);
return NULL;
}
rc = mdss_dsi_panel_init(dsi_pan_node, ctrl_pdata);
if (rc) {
pr_err("%s: dsi panel init failed\n", func);
of_node_put(dsi_pan_node);
return NULL;
}
return dsi_pan_node;
} - mdss_dsi_config_panel函数主要是由三个部分组成:
3.1. 解析lk层传给kernel层cmdline的mdss_mdp.panel属性,在mdss_dsi_get_panel_cfg函数进行,
3.2. 根据cmdline的mdss_mdp.panel属性寻找panel的dtsi文件节点,在mdss_dsi_find_panel_of_node函数进行
3.3. 解析panel dtsi文件节点中的属性并配置panel的操作集,在mdss_dsi_panel_init函数进行 - 下面我们依次来说明这三个部分,首先是mdss_dsi_get_panel_cfg函数
vim x:\work\project\kernel\drivers\video\msm\mdss\Mdss_dsi.c
static int mdss_dsi_get_panel_cfg(char *panel_cfg,
struct mdss_dsi_ctrl_pdata *ctrl)
{
int rc;
struct mdss_panel_cfg *pan_cfg = NULL;
if (!panel_cfg)
return MDSS_PANEL_INTF_INVALID;
pan_cfg = ctrl->mdss_util->panel_intf_type(MDSS_PANEL_INTF_DSI);
if (IS_ERR(pan_cfg)) {
return PTR_ERR(pan_cfg);
} else if (!pan_cfg) {
panel_cfg[0] = 0;
return 0;
}
pr_debug("%s:%d: cfg:[%s]\n", func, LINE,
pan_cfg->arg_cfg);
rc = strlcpy(panel_cfg, pan_cfg->arg_cfg,
sizeof(pan_cfg->arg_cfg));
return rc;
}
通过日志,我们看到panel_cfg的字符串内容(0:qcom,mdss_dsi_ili9885_lide_hcg_auo_fhd_video:1:none:cfg:single_dsi),如下:
[ 1.045387] mdss_dsi_get_panel_cfg:629: cfg:[0:qcom,mdss_dsi_ili9885_lide_hcg_auo_fhd_video:1:none:cfg:single_dsi]
//0表示dsi0,说明panel_cfg的字符串内容包含panel的型号名称和panel所属的dsi number - 接下来我们来看看mdss_dsi_find_panel_of_node函数
vim x:\work\project\kernel\drivers\video\msm\mdss\Mdss_dsi.c
static struct device_node *mdss_dsi_find_panel_of_node(
struct platform_device *pdev, char *panel_cfg)
{
int len, i;
int ctrl_id = pdev->id - 1;
char panel_name[MDSS_MAX_PANEL_LEN];
char ctrl_id_stream[3] = “0:”;
char *stream = NULL, *pan = NULL;
struct device_node *dsi_pan_node = NULL, mdss_node = NULL;
len = strlen(panel_cfg);
if (!len) {
/ no panel cfg chg, parse dt */
pr_debug("%s:%d: no cmd line cfg present\n",
func, LINE);
goto end;
} else {
if (ctrl_id == 1)
strlcpy(ctrl_id_stream, “1:”, 3);
stream = strnstr(panel_cfg, ctrl_id_stream, len);
if (!stream) {
pr_err(“controller config is not present\n”);
goto end;
}
stream += 2;
pan = strnchr(stream, strlen(stream), ‘:’);
if (!pan) {
strlcpy(panel_name, stream, MDSS_MAX_PANEL_LEN); //根据panel_cfg的字符串的内容,得到panel的型号名称是qcom,mdss_dsi_ili9885_lide_hcg_auo_fhd_video
} else {
for (i = 0; (stream + i) < pan; i++)
panel_name[i] = *(stream + i);
panel_name[i] = 0;
}
pr_debug("%s:%d:%s:%s\n", func, LINE,
panel_cfg, panel_name);
//日志如下:[ 1.045392] mdss_dsi_find_panel_of_node:2355:0:qcom,mdss_dsi_ili9885_lide_hcg_auo_fhd_video:1:none:cfg:single_dsi:qcom,mdss_dsi_ili9885_lide_hcg_auo_fhd_video
mdss_node = of_parse_phandle(pdev->dev.of_node, //得到mdss_mdp节点,因为所有的panel型号都是挂在mdss_mdp节点下面,mdss_mdp节点的compatible = “qcom,mdss_mdp”;
“qcom,mdss-mdp”, 0);
if (!mdss_node) {
pr_err("%s: %d: mdss_node null\n",
func, LINE);
return NULL;
}
dsi_pan_node = of_find_node_by_name(mdss_node, //根据panel的型号名称找到panel型号的dtsi文件节点
panel_name);
if (!dsi_pan_node) {
pr_err("%s: invalid pan node, selecting prim panel\n",
func);
goto end;
}
return dsi_pan_node; //返回panel型号的dtsi文件节点
}
end:
if (strcmp(panel_name, NONE_PANEL))
dsi_pan_node = mdss_dsi_pref_prim_panel(pdev);
return dsi_pan_node;
} - 接下来我们来看看mdss_dsi_panel_init函数
vim x:\work\xxx\kernel\drivers\video\msm\mdss\Mdss_dsi_panel.c
int mdss_dsi_panel_init(struct device_node *node,
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
int rc = 0;
static const char *panel_name;
struct mdss_panel_info *pinfo;
if (!node || !ctrl_pdata) {
pr_err("%s: Invalid arguments\n", func);
return -ENODEV;
}
pinfo = &ctrl_pdata->panel_data.panel_info;
pr_debug("%s:%d\n", func, LINE);
pinfo->panel_name[0] = ‘\0’;
panel_name = of_get_property(node, “qcom,mdss-dsi-panel-name”, NULL); //得到panel节点的qcom,mdss-dsi-panel-name属性
if (!panel_name) {
pr_info("%s:%d, Panel name not specified\n",
func, LINE);
} else {
pr_info("%s: Panel Name = %s\n", func, panel_name);
strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN);
}
rc = mdss_panel_parse_dt(node, ctrl_pdata); //解析panel节点下面其他的属性
if (rc) {
pr_err("%s:%d panel dt parse failed\n", func, LINE);
return rc;
}
mdss_dsi_set_prim_panel(ctrl_pdata);
pinfo->dynamic_switch_pending = false;
pinfo->is_lpm_mode = false;
pinfo->esd_rdy = false;
ctrl_pdata->on = mdss_dsi_panel_on; //以下都是panel的操作集,唤醒屏幕的操作
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; //设置panel的背光操作函数
ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; //panel mode的切换,如:video2command,command2video
return 0;
}