本文主要是基于高通msm8953对lcd在lk阶段通过cmdline向kernel传递参数的过程进行分析。
一、LK阶段分析
相关文件:
app/aboot/aboot.c
Target/msm8953/Target_display.c
Dev/gcdb/display/gcdb_display_param.c
Target/msm8953/oem_panel.c
在gcdb_display_cmdline_arg函数中将屏信息保存到display_panel_buf 中,并通过boot_linux->update_cmdline函数中整合其他模块信息后,在update_device_tree函数中附加到bootargs中,从而想kernel传递信息。相关代码如下:
1、整个过程主要发生在update_cmdline中,如下:
- App/aboot/aboot.c
- Boot_linux
- ->Update_cmdline
- ->target_display_panel_node
- ->gcdb_display_cmdline_arg
- //获取主屏信息(存储在panelstruct结构中),具体如1.1分析:
- ->mdss_dsi_get_panel_data
- //oem_data存储的是副屏的信息,
- //而由于oem_data.skip为false,
- //故在这个函数中是返回false,且slave_panel_node此时还未赋值,具体如1.2分析:
- ->mdss_dsi_set_panel_node
- //将主屏名称拷到panel_node中存储
- -> ->panel_node = panelstruct.paneldata->panel_node_id
- //根据副屏名称查找lookup_skip_panels数组,
- //返回panel_dt_string成员存到slave_panel_node中,具体如1.3分析:
- ->panel_name_to_dt_string
- ->最终将panel_node + lookup_skip_panels + 其他信息 存到pbuf,即display_panel_buf中
- //在update_cmdline中将display_panel_buf+其他数据整合
- ->update_cmdline
- //将上述update_cmdline整合后的各信息附加到设备树的bootargs信息中,具体如1.4分析:
- ->update_device_tree
-
-
-
1.1)panelstruct是在oem_panel.c中_panel_data函数赋值,如下:
- struct panel_struct mdss_dsi_get_panel_data(void)
- {
- return panelstruct;
- }
-
- case LT8911B_1080P_VIDEO_PANEL:
- panelstruct->paneldata = <8911b_1080p_video_panel_data;
- /*
- static struct panel_config lt8911b_1080p_video_panel_data = {
- "qcom,mdss_dsi_lt8911b_1080p_video", "dsi:0:", "qcom,mdss-dsi-panel",
- 10, 0, "DISPLAY_1", 0, 0, 60, 0, 0, 0, 1, 10000, 0, 0, 0, 0, 0, 0, NULL
- };
- */
- panelstruct->panelres = <8911b_1080p_video_panel_res;
- panelstruct->color = <8911b_1080p_video_color;
- panelstruct->videopanel = <8911b_1080p_video_video_panel;
- panelstruct->commandpanel = <8911b_1080p_video_command_panel;
- panelstruct->state = <8911b_1080p_video_state;
- panelstruct->laneconfig = <8911b_1080p_video_lane_config;
- panelstruct->paneltiminginfo
- = <8911b_1080p_video_timing_info;
- panelstruct->panelresetseq
- = <8911b_1080p_video_reset_seq;
- panelstruct->backlightinfo = <8911b_1080p_video_backlight;
-
- pinfo->mipi.panel_on_cmds = lt8911b_1080p_video_on_command;
- pinfo->mipi.num_of_panel_on_cmds
- = LT8911B_1080P_VIDEO_ON_COMMAND;
-
- pinfo->mipi.panel_off_cmds = NULL;
- pinfo->mipi.num_of_panel_off_cmds
- = 0;
- memcpy(phy_db->timing,
- lt8911b_1080p_video_timings,
- MAX_TIMING_CONFIG * sizeof(uint32_t));
- pinfo->mipi.signature = LT8911B_1080P_VIDEO_SIGNATURE;
- panelstruct->paneldata->panel_operating_mode &= ~USE_DSI1_PLL_FLAG;
- break;
相关结构体定义如下:
- struct panel_struct {
- struct panel_config *paneldata;
- struct panel_resolution *panelres;
- struct color_info *color;
- struct videopanel_info *videopanel;
- struct commandpanel_info *commandpanel;
- struct command_state *state;
- struct lane_configuration *laneconfig;
- struct panel_timing *paneltiminginfo;
- struct panel_reset_sequence *panelresetseq;
- struct backlight *backlightinfo;
- struct fb_compression fbcinfo;
- struct topology_config *config;
- };
-
- struct panel_config{
-
- char *panel_node_id;
- char *panel_controller;
- char *panel_compatible;
- uint16_t panel_interface;
- uint16_t panel_type;
- char *panel_destination;
- uint32_t panel_orientation;
- /* panel_clockrate is deprecated in favor of panel_bitclock_freq */
- uint32_t panel_clockrate;
- uint16_t panel_framerate;
- uint16_t panel_channelid;
- uint16_t dsi_virtualchannel_id;
- uint16_t panel_broadcast_mode;
- uint16_t panel_lp11_init;
- uint16_t panel_init_delay;
- uint16_t dsi_stream;
- uint8_t interleave_mode;
- uint32_t panel_bitclock_freq;
- uint32_t panel_operating_mode;
- uint32_t panel_with_enable_gpio;
- uint8_t mode_gpio_state;
- char *slave_panel_node_id;
- };
因此执行 panel_node = panelstruct.paneldata->panel_node_id时,实际为将主屏名称信息赋值给panel_node,即panel_node = qcom,mdss_dsi_lt8911b_1080p_video。
1.2)mdss_dsi_set_panel_node主要涉及oem_data数据,而oem_data数据是在set_panel_cmd_string中赋值,如下:
- struct oem_panel_data oem_data = {{'\0'}, {'\0'}, false, false, false, SIM_NONE,
- "dual_dsi", DSI_PLL_DEFAULT, {-1, -1}};
-
- struct oem_panel_data {
- char panel[MAX_PANEL_ID_LEN];
- char sec_panel[MAX_PANEL_ID_LEN];
- bool cont_splash;
- bool skip;
- bool swap_dsi_ctrl;
- uint32_t sim_mode;
- char dsi_config[DSI_CFG_SIZE];
- uint32_t dsi_pll_src;
- /* If dual-DSI, slave cfg will use 2nd index */
- int cfg_num[2]; /* -ve number means no overide */
- };
-
- void set_panel_cmd_string(const char *panel_name)
- {
- //省略无关代码
- ch = strstr((char *) panel_name, "sec:");
- if (ch) {
- ch += 4;
- ch_tmp = get_panel_token_end((const char*) ch);
- if (!ch_tmp)
- ch_tmp = ch + strlen(ch);
- for (i = 0; (ch + i) < ch_tmp; i++)
- oem_data.sec_panel[i] = *(ch + i);
- oem_data.sec_panel[i] = '\0';
-
- /* Topology configuration for secondary panel */
- ch_tmp = strstr((char *) ch, ":cfg");
- if (ch_tmp)
- oem_data.cfg_num[1] = atoi((const char*)(ch_tmp + 4));
- } else {
- oem_data.sec_panel[0] = '\0';
- }
-
- /* Skip LK configuration */
- ch = strstr((char *) panel_name, ":skip");
- oem_data.skip = ch ? true : false;
-
- }
-
- 传入的参数格式为:set_panel_cmd_string("sec:dsi_lt8911b_1080p_dsi1_video");
-
因此,oem_data.skip实际为false,oem_data.sec_panel 实际为"dsi_lt8911b_1080p_dsi1_video"字符串。
1.3)panel_name_to_dt_string是根据oem_data.sec_panel匹配lookup_skip_panels数组中的panel_dt_string成员,从而找到节点名称,如下:
- //oem_data.sec_panel在init_panel_data中调用set_panel_cmd_string进行赋值
- ret_val = panel_name_to_dt_string(lookup_skip_panels,
- ARRAY_SIZE(lookup_skip_panels), oem_data.sec_panel,
- &slave_panel_node);
-
-
- static int panel_name_to_dt_string(struct panel_lookup_list supp_panels[],
- uint32_t supp_panels_size,
- const char *panel_name, char **panel_node)
- {
- uint32_t i;
- //省略无关代码
- for (i = 0; i < supp_panels_size; i++) {
- if (!strncmp(panel_name, supp_panels[i].name,
- MAX_PANEL_ID_LEN)) {
- *panel_node = supp_panels[i].panel_dt_string;
- return supp_panels[i].is_split_dsi;
- }
- }
-
- return ERR_NOT_FOUND;
- }
- 形如:
- struct panel_lookup_list lookup_skip_panels[] = {
- {"dsi_lt8911b_1080p_dsi1_video", "qcom,mdss_dsi_lt8911b_1080p_dsi1_video", false},
- }
因此,slave_panel_node = qcom,mdss_dsi_lt8911b_1080p_dsi1_video。
故在update_cmdline 函数时 display_panel_buf的大致数据为:
mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_lt8911exb_1080p_video:1:qcom,mdss_dsi_lt8911exb_1080p_dsi1_video:cfg:dual_dsi
1.4)先在update_cmdline中display_panel_buf赋值,再update_device_tree中将cmdline的信息追加到bootargs中,如下:
- unsigned char *update_cmdline(const char * cmdline)
- {
- //省略无关代码
- if (cmdline) {
- if ((strstr(cmdline, DISPLAY_DEFAULT_PREFIX) == NULL) &&
- target_display_panel_node(display_panel_buf,
- MAX_PANEL_BUF_SIZE) &&
- strlen(display_panel_buf)) {
- cmdline_len += strlen(display_panel_buf);
- }
- }
- }
-
- int update_device_tree(void *fdt, const char *cmdline,
- void *ramdisk, uint32_t ramdisk_size)
- {
- //省略无关代码
- if (cmdline)
- {
- /* Adding the cmdline to the chosen node */
- ret = fdt_appendprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
- if (ret)
- {
- dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
- return ret;
- }
- }
- }
二、kernel阶段分析
1、在Start_kernel函数中获取传到内核的buf,并将buf写入_param段中,如下:
- init/main.c
- Start_kernel
- ->setup_arch(&command_line);
- ->mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
- ->parse_args
- ->parse_one
- 将lk中传递的数据写到__param段中
- ->params[i].ops->set(val, ¶ms[i])=param_set_copystring
而param的定义如下:
- struct kernel_param {
- const char *name;
- struct module *mod;
- const struct kernel_param_ops *ops;
- const u16 perm;
- s8 level;
- u8 flags;
- union {
- void *arg;
- const struct kparam_string *str;
- const struct kparam_array *arr;
- };
- };
-
- const struct kernel_param_ops param_ops_string = {
- .set = param_set_copystring,
- .get = param_get_string,
- };
-
- int param_set_copystring(const char *val, const struct kernel_param *kp)
- {
- const struct kparam_string *kps = kp->str;
-
- if (strlen(val)+1 > kps->maxlen) {
- pr_err("%s: string doesn't fit in %u chars.\n",
- kp->name, kps->maxlen-1);
- return -ENOSPC;
- }
- strcpy(kps->string, val);
- return 0;
- }
-
- int param_get_string(char *buffer, const struct kernel_param *kp)
- {
- const struct kparam_string *kps = kp->str;
- return strlcpy(buffer, kps->string, kps->maxlen);
- }
因此,boot_command_line中存储的是从lk传到kernel的参数,并将传参的参数写入_param段中,等待驱动加载时通过module_param_string使用。
2、分析如何使用_param段中的数据,主要从LCD驱动分析。
2.1)在mdss_mdp.c中存在驱动的传参定义,并且展开相应的宏定义后就是根据模块名在_param段中匹配屏的信息,如下;
- module_param_string(panel, mdss_mdp_panel, MDSS_MAX_PANEL_LEN, 0600);
- 参数说明:
- panel:模块名 mdss-mdp
- mdss_mdp_panel:存储屏相关的信息
- MDSS_MAX_PANEL_LEN:256
-
- module_param_string定义:
- #define module_param_string(name, string, len, perm) \
- static const struct kparam_string __param_string_##name \
- = { len, string }; \
- __module_param_call(MODULE_PARAM_PREFIX, name, \
- ¶m_ops_string, \
- .str = &__param_string_##name, perm, -1, 0);\
- __MODULE_PARM_TYPE(name, "string")
-
- //若是模块编译的为空,不是模块编译的话为 "模块名."
- //由于makefile中定义为:obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
- //因此 模块名为mdss-mdp.o,即kernel传参数的时候,参数名为 mdss-mdp.panel
- #ifdef MODULE
- #define MODULE_PARAM_PREFIX /* empty */
- #else
- #define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
- #endif
-
- #define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
- /* Default value instead of permissions? */ \
- static const char __param_str_##name[] = prefix #name; \
- static struct kernel_param __moduleparam_const __param_##name \
- __used \
- __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
- = { __param_str_##name, THIS_MODULE, ops, \
- VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }
-
- 故:
- module_param_string(panel, mdss_mdp_panel, MDSS_MAX_PANEL_LEN, 0600);
- 等价于:
- #define module_param_string(name, string, len, perm) \
- static const struct kparam_string __param_string_panel = {MDSS_MAX_PANEL_LEN, mdss_mdp_panel}; \
- static const char __param_str_panel[] = mdss_mdp.panel;
- static struct kernel_param __moduleparam_const __param_panel \
- __used __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
- = {
- __param_str_panel,
- THIS_MODULE,
- param_ops_string,
- VERIFY_OCTAL_PERMISSIONS(0600),
- -1,
- 0,
- .str = &__param_string_panel
- }
2.2)mdss_mdp_panel数组的使用过程如下:
- Video/fbdev/msm/Mdss_dsi.c
- mdss_dsi_ctrl_probe->//由于主副屏原因,该函数会执行两轮
- ->mdss_dsi_config_panel
- ->mdss_dsi_get_panel_cfg
- ->ctrl->mdss_util->panel_intf_type(MDSS_PANEL_INTF_DSI)=mdss_panel_intf_type
- ->mdss_res->pan_cfg //在mdss_mdp_get_cmdline_config函数中赋值
- ->mdss_mdp_get_cmdline_config
- //具体如2.3分析:
- ->mdss_mdp_get_pan_cfg
- ->mdss_mdp_panel//数组
- //由于执行两轮,因此第一轮解析的cmdline是主屏信息,
- //第二轮时解析的是副屏信息
- //具体如2.4分析:
- ->mdss_dsi_find_panel_of_node
2.3 mdss_mdp_get_pan_cfg分析:
将参数mdss_mdp_panel进行解析到pan_cfg中,如下:
- static int mdss_mdp_get_pan_cfg(struct mdss_panel_cfg *pan_cfg)
- {
- char *t = NULL;
- char pan_intf_str[MDSS_MAX_PANEL_LEN];
- int rc, i, panel_len;
- char pan_name[MDSS_MAX_PANEL_LEN] = {'\0'};
-
- if (!pan_cfg)
- return -EINVAL;
-
- if (mdss_mdp_panel[0] == '0') {
- pr_debug("panel name is not set\n");
- pan_cfg->lk_cfg = false;
- pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
- return -EINVAL;
- } else if (mdss_mdp_panel[0] == '1') {
- pan_cfg->lk_cfg = true;
- } else {
- /* read from dt */
- pan_cfg->lk_cfg = true;
- pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
- return -EINVAL;
- }
-
- /* skip lk cfg and delimiter; ex: "1:" */
- strlcpy(pan_name, &mdss_mdp_panel[2], MDSS_MAX_PANEL_LEN);
- t = strnstr(pan_name, ":", MDSS_MAX_PANEL_LEN);
- if (!t) {
- pr_err("pan_name=[%s] invalid\n", pan_name);
- pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
- return -EINVAL;
- }
-
- for (i = 0; ((pan_name + i) < t) && (i < 4); i++)
- pan_intf_str[i] = *(pan_name + i);
- pan_intf_str[i] = 0;
- pr_debug("%d panel intf %s\n", __LINE__, pan_intf_str);
- /* point to the start of panel name */
- t = t + 1;
- strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg));
- pr_debug("%d: t=[%s] panel name=[%s]\n", __LINE__,
- t, pan_cfg->arg_cfg);
-
- panel_len = strlen(pan_cfg->arg_cfg);
- if (!panel_len) {
- pr_err("Panel name is invalid\n");
- pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
- return -EINVAL;
- }
-
- rc = mdss_mdp_get_pan_intf(pan_intf_str);
- pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc;
- return 0;
- }
2.4)mdss_dsi_find_panel_of_node过程分析
- static struct device_node *mdss_dsi_find_panel_of_node(
- struct platform_device *pdev, char *panel_cfg)
- {
- int len, i = 0;
- int ctrl_id = pdev->id - 1;
- char panel_name[MDSS_MAX_PANEL_LEN] = "";
- char ctrl_id_stream[3] = "0:";
- char *str1 = NULL, *str2 = NULL, *override_cfg = NULL;
- char cfg_np_name[MDSS_MAX_PANEL_LEN] = "";
- struct device_node *dsi_pan_node = NULL, *mdss_node = NULL;
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = platform_get_drvdata(pdev);
- struct mdss_panel_info *pinfo = &ctrl_pdata->panel_data.panel_info;
-
- len = strlen(panel_cfg);
- ctrl_pdata->panel_data.dsc_cfg_np_name[0] = '\0';
- if (!len) {
- /* no panel cfg chg, parse dt */
- pr_debug("%s:%d: no cmd line cfg present\n",
- __func__, __LINE__);
- goto end;
- } else {
- /* check if any override parameters are set */
- pinfo->sim_panel_mode = 0;
- override_cfg = strnstr(panel_cfg, "#" OVERRIDE_CFG, len);
- if (override_cfg) {
- *override_cfg = '\0';
- if (mdss_dsi_set_override_cfg(override_cfg + 1,
- ctrl_pdata, panel_cfg))
- return NULL;
- len = strlen(panel_cfg);
- }
-
- if (ctrl_id == 1)
- strlcpy(ctrl_id_stream, "1:", 3);
-
- /* get controller number */
- str1 = strnstr(panel_cfg, ctrl_id_stream, len);
- if (!str1) {
- pr_err("%s: controller %s is not present in %s\n",
- __func__, ctrl_id_stream, panel_cfg);
- goto end;
- }
- if ((str1 != panel_cfg) && (*(str1-1) != ':')) {
- str1 += CMDLINE_DSI_CTL_NUM_STRING_LEN;
- pr_debug("false match with config node name in \"%s\". search again in \"%s\"\n",
- panel_cfg, str1);
- str1 = strnstr(str1, ctrl_id_stream, len);
- if (!str1) {
- pr_err("%s: 2. controller %s is not present in %s\n",
- __func__, ctrl_id_stream, str1);
- goto end;
- }
- }
- str1 += CMDLINE_DSI_CTL_NUM_STRING_LEN;
-
- /* get panel name */
- str2 = strnchr(str1, strlen(str1), ':');
- if (!str2) {
- strlcpy(panel_name, str1, MDSS_MAX_PANEL_LEN);
- } else {
- for (i = 0; (str1 + i) < str2; i++)
- panel_name[i] = *(str1 + i);
- panel_name[i] = 0;
- }
- pr_info("%s: cmdline:%s panel_name:%s\n",
- __func__, panel_cfg, panel_name);
- if (!strcmp(panel_name, NONE_PANEL))
- goto exit;
-
- mdss_node = of_parse_phandle(pdev->dev.of_node,
- "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_name);
- if (!dsi_pan_node) {
- pr_err("%s: invalid pan node \"%s\"\n",
- __func__, panel_name);
- goto end;
- } else {
- /* extract config node name if present */
- str1 += i;
- str2 = strnstr(str1, "config", strlen(str1));
- if (str2) {
- str1 = strnchr(str2, strlen(str2), ':');
- if (str1) {
- for (i = 0; ((str2 + i) < str1) &&
- i < (MDSS_MAX_PANEL_LEN - 1); i++)
- cfg_np_name[i] = *(str2 + i);
- if ((i >= 0)
- && (i < MDSS_MAX_PANEL_LEN))
- cfg_np_name[i] = 0;
- } else {
- strlcpy(cfg_np_name, str2,
- MDSS_MAX_PANEL_LEN);
- }
- strlcpy(ctrl_pdata->panel_data.dsc_cfg_np_name,
- cfg_np_name, MDSS_MAX_PANEL_LEN);
- }
- }
-
- return dsi_pan_node;
- }
- end:
- if (strcmp(panel_name, NONE_PANEL))
- dsi_pan_node = mdss_dsi_pref_prim_panel(pdev);
- exit:
- return dsi_pan_node;
- }