• msm8953 LK通过cmdline向Kernel传递LCD参数过程分析


    本文主要是基于高通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中,如下:

    1. App/aboot/aboot.c
    2. Boot_linux
    3. ->Update_cmdline
    4. ->target_display_panel_node
    5. ->gcdb_display_cmdline_arg
    6. //获取主屏信息(存储在panelstruct结构中),具体如1.1分析:
    7. ->mdss_dsi_get_panel_data
    8. //oem_data存储的是副屏的信息,
    9. //而由于oem_data.skip为false,
    10. //故在这个函数中是返回false,且slave_panel_node此时还未赋值,具体如1.2分析:
    11. ->mdss_dsi_set_panel_node
    12. //将主屏名称拷到panel_node中存储
    13. -> ->panel_node = panelstruct.paneldata->panel_node_id
    14. //根据副屏名称查找lookup_skip_panels数组,
    15. //返回panel_dt_string成员存到slave_panel_node中,具体如1.3分析:
    16. ->panel_name_to_dt_string
    17. ->最终将panel_node + lookup_skip_panels + 其他信息 存到pbuf,即display_panel_buf中
    18. //在update_cmdline中将display_panel_buf+其他数据整合
    19. ->update_cmdline
    20. //将上述update_cmdline整合后的各信息附加到设备树的bootargs信息中,具体如1.4分析:
    21. ->update_device_tree

    1.1)panelstruct是在oem_panel.c中_panel_data函数赋值,如下:

    1. struct panel_struct mdss_dsi_get_panel_data(void)
    2. {
    3. return panelstruct;
    4. }
    5. case LT8911B_1080P_VIDEO_PANEL:
    6. panelstruct->paneldata = <8911b_1080p_video_panel_data;
    7. /*
    8. static struct panel_config lt8911b_1080p_video_panel_data = {
    9. "qcom,mdss_dsi_lt8911b_1080p_video", "dsi:0:", "qcom,mdss-dsi-panel",
    10. 10, 0, "DISPLAY_1", 0, 0, 60, 0, 0, 0, 1, 10000, 0, 0, 0, 0, 0, 0, NULL
    11. };
    12. */
    13. panelstruct->panelres = <8911b_1080p_video_panel_res;
    14. panelstruct->color = <8911b_1080p_video_color;
    15. panelstruct->videopanel = <8911b_1080p_video_video_panel;
    16. panelstruct->commandpanel = <8911b_1080p_video_command_panel;
    17. panelstruct->state = <8911b_1080p_video_state;
    18. panelstruct->laneconfig = <8911b_1080p_video_lane_config;
    19. panelstruct->paneltiminginfo
    20. = <8911b_1080p_video_timing_info;
    21. panelstruct->panelresetseq
    22. = <8911b_1080p_video_reset_seq;
    23. panelstruct->backlightinfo = <8911b_1080p_video_backlight;
    24. pinfo->mipi.panel_on_cmds = lt8911b_1080p_video_on_command;
    25. pinfo->mipi.num_of_panel_on_cmds
    26. = LT8911B_1080P_VIDEO_ON_COMMAND;
    27. pinfo->mipi.panel_off_cmds = NULL;
    28. pinfo->mipi.num_of_panel_off_cmds
    29. = 0;
    30. memcpy(phy_db->timing,
    31. lt8911b_1080p_video_timings,
    32. MAX_TIMING_CONFIG * sizeof(uint32_t));
    33. pinfo->mipi.signature = LT8911B_1080P_VIDEO_SIGNATURE;
    34. panelstruct->paneldata->panel_operating_mode &= ~USE_DSI1_PLL_FLAG;
    35. break;

    相关结构体定义如下:

    1. struct panel_struct {
    2. struct panel_config *paneldata;
    3. struct panel_resolution *panelres;
    4. struct color_info *color;
    5. struct videopanel_info *videopanel;
    6. struct commandpanel_info *commandpanel;
    7. struct command_state *state;
    8. struct lane_configuration *laneconfig;
    9. struct panel_timing *paneltiminginfo;
    10. struct panel_reset_sequence *panelresetseq;
    11. struct backlight *backlightinfo;
    12. struct fb_compression fbcinfo;
    13. struct topology_config *config;
    14. };
    15. struct panel_config{
    16. char *panel_node_id;
    17. char *panel_controller;
    18. char *panel_compatible;
    19. uint16_t panel_interface;
    20. uint16_t panel_type;
    21. char *panel_destination;
    22. uint32_t panel_orientation;
    23. /* panel_clockrate is deprecated in favor of panel_bitclock_freq */
    24. uint32_t panel_clockrate;
    25. uint16_t panel_framerate;
    26. uint16_t panel_channelid;
    27. uint16_t dsi_virtualchannel_id;
    28. uint16_t panel_broadcast_mode;
    29. uint16_t panel_lp11_init;
    30. uint16_t panel_init_delay;
    31. uint16_t dsi_stream;
    32. uint8_t interleave_mode;
    33. uint32_t panel_bitclock_freq;
    34. uint32_t panel_operating_mode;
    35. uint32_t panel_with_enable_gpio;
    36. uint8_t mode_gpio_state;
    37. char *slave_panel_node_id;
    38. };

    因此执行 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中赋值,如下:

    1. struct oem_panel_data oem_data = {{'\0'}, {'\0'}, false, false, false, SIM_NONE,
    2. "dual_dsi", DSI_PLL_DEFAULT, {-1, -1}};
    3. struct oem_panel_data {
    4. char panel[MAX_PANEL_ID_LEN];
    5. char sec_panel[MAX_PANEL_ID_LEN];
    6. bool cont_splash;
    7. bool skip;
    8. bool swap_dsi_ctrl;
    9. uint32_t sim_mode;
    10. char dsi_config[DSI_CFG_SIZE];
    11. uint32_t dsi_pll_src;
    12. /* If dual-DSI, slave cfg will use 2nd index */
    13. int cfg_num[2]; /* -ve number means no overide */
    14. };
    15. void set_panel_cmd_string(const char *panel_name)
    16. {
    17. //省略无关代码
    18. ch = strstr((char *) panel_name, "sec:");
    19. if (ch) {
    20. ch += 4;
    21. ch_tmp = get_panel_token_end((const char*) ch);
    22. if (!ch_tmp)
    23. ch_tmp = ch + strlen(ch);
    24. for (i = 0; (ch + i) < ch_tmp; i++)
    25. oem_data.sec_panel[i] = *(ch + i);
    26. oem_data.sec_panel[i] = '\0';
    27. /* Topology configuration for secondary panel */
    28. ch_tmp = strstr((char *) ch, ":cfg");
    29. if (ch_tmp)
    30. oem_data.cfg_num[1] = atoi((const char*)(ch_tmp + 4));
    31. } else {
    32. oem_data.sec_panel[0] = '\0';
    33. }
    34. /* Skip LK configuration */
    35. ch = strstr((char *) panel_name, ":skip");
    36. oem_data.skip = ch ? true : false;
    37. }
    38. 传入的参数格式为: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成员,从而找到节点名称,如下:

    1. //oem_data.sec_panel在init_panel_data中调用set_panel_cmd_string进行赋值
    2. ret_val = panel_name_to_dt_string(lookup_skip_panels,
    3. ARRAY_SIZE(lookup_skip_panels), oem_data.sec_panel,
    4. &slave_panel_node);
    5. static int panel_name_to_dt_string(struct panel_lookup_list supp_panels[],
    6. uint32_t supp_panels_size,
    7. const char *panel_name, char **panel_node)
    8. {
    9. uint32_t i;
    10. //省略无关代码
    11. for (i = 0; i < supp_panels_size; i++) {
    12. if (!strncmp(panel_name, supp_panels[i].name,
    13. MAX_PANEL_ID_LEN)) {
    14. *panel_node = supp_panels[i].panel_dt_string;
    15. return supp_panels[i].is_split_dsi;
    16. }
    17. }
    18. return ERR_NOT_FOUND;
    19. }
    20. 形如:
    21. struct panel_lookup_list lookup_skip_panels[] = {
    22. {"dsi_lt8911b_1080p_dsi1_video", "qcom,mdss_dsi_lt8911b_1080p_dsi1_video", false},
    23. }

    因此,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中,如下:

    1. unsigned char *update_cmdline(const char * cmdline)
    2. {
    3. //省略无关代码
    4. if (cmdline) {
    5. if ((strstr(cmdline, DISPLAY_DEFAULT_PREFIX) == NULL) &&
    6. target_display_panel_node(display_panel_buf,
    7. MAX_PANEL_BUF_SIZE) &&
    8. strlen(display_panel_buf)) {
    9. cmdline_len += strlen(display_panel_buf);
    10. }
    11. }
    12. }
    13. int update_device_tree(void *fdt, const char *cmdline,
    14. void *ramdisk, uint32_t ramdisk_size)
    15. {
    16. //省略无关代码
    17. if (cmdline)
    18. {
    19. /* Adding the cmdline to the chosen node */
    20. ret = fdt_appendprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
    21. if (ret)
    22. {
    23. dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
    24. return ret;
    25. }
    26. }
    27. }

    二、kernel阶段分析
    1、在Start_kernel函数中获取传到内核的buf,并将buf写入_param段中,如下:

    1. init/main.c
    2. Start_kernel
    3. ->setup_arch(&command_line);
    4. ->mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
    5. ->parse_args
    6. ->parse_one
    7. 将lk中传递的数据写到__param段中
    8. ->params[i].ops->set(val, ¶ms[i])=param_set_copystring

    而param的定义如下:

    1. struct kernel_param {
    2. const char *name;
    3. struct module *mod;
    4. const struct kernel_param_ops *ops;
    5. const u16 perm;
    6. s8 level;
    7. u8 flags;
    8. union {
    9. void *arg;
    10. const struct kparam_string *str;
    11. const struct kparam_array *arr;
    12. };
    13. };
    14. const struct kernel_param_ops param_ops_string = {
    15. .set = param_set_copystring,
    16. .get = param_get_string,
    17. };
    18. int param_set_copystring(const char *val, const struct kernel_param *kp)
    19. {
    20. const struct kparam_string *kps = kp->str;
    21. if (strlen(val)+1 > kps->maxlen) {
    22. pr_err("%s: string doesn't fit in %u chars.\n",
    23. kp->name, kps->maxlen-1);
    24. return -ENOSPC;
    25. }
    26. strcpy(kps->string, val);
    27. return 0;
    28. }
    29. int param_get_string(char *buffer, const struct kernel_param *kp)
    30. {
    31. const struct kparam_string *kps = kp->str;
    32. return strlcpy(buffer, kps->string, kps->maxlen);
    33. }

    因此,boot_command_line中存储的是从lk传到kernel的参数,并将传参的参数写入_param段中,等待驱动加载时通过module_param_string使用。

    2、分析如何使用_param段中的数据,主要从LCD驱动分析。

    2.1)在mdss_mdp.c中存在驱动的传参定义,并且展开相应的宏定义后就是根据模块名在_param段中匹配屏的信息,如下;

    1. module_param_string(panel, mdss_mdp_panel, MDSS_MAX_PANEL_LEN, 0600);
    2. 参数说明:
    3. panel:模块名 mdss-mdp
    4. mdss_mdp_panel:存储屏相关的信息
    5. MDSS_MAX_PANEL_LEN:256
    6. module_param_string定义:
    7. #define module_param_string(name, string, len, perm) \
    8. static const struct kparam_string __param_string_##name \
    9. = { len, string }; \
    10. __module_param_call(MODULE_PARAM_PREFIX, name, \
    11. ¶m_ops_string, \
    12. .str = &__param_string_##name, perm, -1, 0);\
    13. __MODULE_PARM_TYPE(name, "string")
    14. //若是模块编译的为空,不是模块编译的话为 "模块名."
    15. //由于makefile中定义为:obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
    16. //因此 模块名为mdss-mdp.o,即kernel传参数的时候,参数名为 mdss-mdp.panel
    17. #ifdef MODULE
    18. #define MODULE_PARAM_PREFIX /* empty */
    19. #else
    20. #define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
    21. #endif
    22. #define __module_param_call(prefix, name, ops, arg, perm, level, flags) \
    23. /* Default value instead of permissions? */ \
    24. static const char __param_str_##name[] = prefix #name; \
    25. static struct kernel_param __moduleparam_const __param_##name \
    26. __used \
    27. __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
    28. = { __param_str_##name, THIS_MODULE, ops, \
    29. VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } }
    30. 故:
    31. module_param_string(panel, mdss_mdp_panel, MDSS_MAX_PANEL_LEN, 0600);
    32. 等价于:
    33. #define module_param_string(name, string, len, perm) \
    34. static const struct kparam_string __param_string_panel = {MDSS_MAX_PANEL_LEN, mdss_mdp_panel}; \
    35. static const char __param_str_panel[] = mdss_mdp.panel;
    36. static struct kernel_param __moduleparam_const __param_panel \
    37. __used __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
    38. = {
    39. __param_str_panel,
    40. THIS_MODULE,
    41. param_ops_string,
    42. VERIFY_OCTAL_PERMISSIONS(0600),
    43. -1,
    44. 0,
    45. .str = &__param_string_panel
    46. }

    2.2)mdss_mdp_panel数组的使用过程如下:

    1. Video/fbdev/msm/Mdss_dsi.c
    2. mdss_dsi_ctrl_probe->//由于主副屏原因,该函数会执行两轮
    3. ->mdss_dsi_config_panel
    4. ->mdss_dsi_get_panel_cfg
    5. ->ctrl->mdss_util->panel_intf_type(MDSS_PANEL_INTF_DSI)=mdss_panel_intf_type
    6. ->mdss_res->pan_cfg //在mdss_mdp_get_cmdline_config函数中赋值
    7. ->mdss_mdp_get_cmdline_config
    8. //具体如2.3分析:
    9. ->mdss_mdp_get_pan_cfg
    10. ->mdss_mdp_panel//数组
    11. //由于执行两轮,因此第一轮解析的cmdline是主屏信息,
    12. //第二轮时解析的是副屏信息
    13. //具体如2.4分析:
    14. ->mdss_dsi_find_panel_of_node

    2.3 mdss_mdp_get_pan_cfg分析:

    将参数mdss_mdp_panel进行解析到pan_cfg中,如下:

    1. static int mdss_mdp_get_pan_cfg(struct mdss_panel_cfg *pan_cfg)
    2. {
    3. char *t = NULL;
    4. char pan_intf_str[MDSS_MAX_PANEL_LEN];
    5. int rc, i, panel_len;
    6. char pan_name[MDSS_MAX_PANEL_LEN] = {'\0'};
    7. if (!pan_cfg)
    8. return -EINVAL;
    9. if (mdss_mdp_panel[0] == '0') {
    10. pr_debug("panel name is not set\n");
    11. pan_cfg->lk_cfg = false;
    12. pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
    13. return -EINVAL;
    14. } else if (mdss_mdp_panel[0] == '1') {
    15. pan_cfg->lk_cfg = true;
    16. } else {
    17. /* read from dt */
    18. pan_cfg->lk_cfg = true;
    19. pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
    20. return -EINVAL;
    21. }
    22. /* skip lk cfg and delimiter; ex: "1:" */
    23. strlcpy(pan_name, &mdss_mdp_panel[2], MDSS_MAX_PANEL_LEN);
    24. t = strnstr(pan_name, ":", MDSS_MAX_PANEL_LEN);
    25. if (!t) {
    26. pr_err("pan_name=[%s] invalid\n", pan_name);
    27. pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
    28. return -EINVAL;
    29. }
    30. for (i = 0; ((pan_name + i) < t) && (i < 4); i++)
    31. pan_intf_str[i] = *(pan_name + i);
    32. pan_intf_str[i] = 0;
    33. pr_debug("%d panel intf %s\n", __LINE__, pan_intf_str);
    34. /* point to the start of panel name */
    35. t = t + 1;
    36. strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg));
    37. pr_debug("%d: t=[%s] panel name=[%s]\n", __LINE__,
    38. t, pan_cfg->arg_cfg);
    39. panel_len = strlen(pan_cfg->arg_cfg);
    40. if (!panel_len) {
    41. pr_err("Panel name is invalid\n");
    42. pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
    43. return -EINVAL;
    44. }
    45. rc = mdss_mdp_get_pan_intf(pan_intf_str);
    46. pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc;
    47. return 0;
    48. }

    2.4)mdss_dsi_find_panel_of_node过程分析 

    1. static struct device_node *mdss_dsi_find_panel_of_node(
    2. struct platform_device *pdev, char *panel_cfg)
    3. {
    4. int len, i = 0;
    5. int ctrl_id = pdev->id - 1;
    6. char panel_name[MDSS_MAX_PANEL_LEN] = "";
    7. char ctrl_id_stream[3] = "0:";
    8. char *str1 = NULL, *str2 = NULL, *override_cfg = NULL;
    9. char cfg_np_name[MDSS_MAX_PANEL_LEN] = "";
    10. struct device_node *dsi_pan_node = NULL, *mdss_node = NULL;
    11. struct mdss_dsi_ctrl_pdata *ctrl_pdata = platform_get_drvdata(pdev);
    12. struct mdss_panel_info *pinfo = &ctrl_pdata->panel_data.panel_info;
    13. len = strlen(panel_cfg);
    14. ctrl_pdata->panel_data.dsc_cfg_np_name[0] = '\0';
    15. if (!len) {
    16. /* no panel cfg chg, parse dt */
    17. pr_debug("%s:%d: no cmd line cfg present\n",
    18. __func__, __LINE__);
    19. goto end;
    20. } else {
    21. /* check if any override parameters are set */
    22. pinfo->sim_panel_mode = 0;
    23. override_cfg = strnstr(panel_cfg, "#" OVERRIDE_CFG, len);
    24. if (override_cfg) {
    25. *override_cfg = '\0';
    26. if (mdss_dsi_set_override_cfg(override_cfg + 1,
    27. ctrl_pdata, panel_cfg))
    28. return NULL;
    29. len = strlen(panel_cfg);
    30. }
    31. if (ctrl_id == 1)
    32. strlcpy(ctrl_id_stream, "1:", 3);
    33. /* get controller number */
    34. str1 = strnstr(panel_cfg, ctrl_id_stream, len);
    35. if (!str1) {
    36. pr_err("%s: controller %s is not present in %s\n",
    37. __func__, ctrl_id_stream, panel_cfg);
    38. goto end;
    39. }
    40. if ((str1 != panel_cfg) && (*(str1-1) != ':')) {
    41. str1 += CMDLINE_DSI_CTL_NUM_STRING_LEN;
    42. pr_debug("false match with config node name in \"%s\". search again in \"%s\"\n",
    43. panel_cfg, str1);
    44. str1 = strnstr(str1, ctrl_id_stream, len);
    45. if (!str1) {
    46. pr_err("%s: 2. controller %s is not present in %s\n",
    47. __func__, ctrl_id_stream, str1);
    48. goto end;
    49. }
    50. }
    51. str1 += CMDLINE_DSI_CTL_NUM_STRING_LEN;
    52. /* get panel name */
    53. str2 = strnchr(str1, strlen(str1), ':');
    54. if (!str2) {
    55. strlcpy(panel_name, str1, MDSS_MAX_PANEL_LEN);
    56. } else {
    57. for (i = 0; (str1 + i) < str2; i++)
    58. panel_name[i] = *(str1 + i);
    59. panel_name[i] = 0;
    60. }
    61. pr_info("%s: cmdline:%s panel_name:%s\n",
    62. __func__, panel_cfg, panel_name);
    63. if (!strcmp(panel_name, NONE_PANEL))
    64. goto exit;
    65. mdss_node = of_parse_phandle(pdev->dev.of_node,
    66. "qcom,mdss-mdp", 0);
    67. if (!mdss_node) {
    68. pr_err("%s: %d: mdss_node null\n",
    69. __func__, __LINE__);
    70. return NULL;
    71. }
    72. dsi_pan_node = of_find_node_by_name(mdss_node, panel_name);
    73. if (!dsi_pan_node) {
    74. pr_err("%s: invalid pan node \"%s\"\n",
    75. __func__, panel_name);
    76. goto end;
    77. } else {
    78. /* extract config node name if present */
    79. str1 += i;
    80. str2 = strnstr(str1, "config", strlen(str1));
    81. if (str2) {
    82. str1 = strnchr(str2, strlen(str2), ':');
    83. if (str1) {
    84. for (i = 0; ((str2 + i) < str1) &&
    85. i < (MDSS_MAX_PANEL_LEN - 1); i++)
    86. cfg_np_name[i] = *(str2 + i);
    87. if ((i >= 0)
    88. && (i < MDSS_MAX_PANEL_LEN))
    89. cfg_np_name[i] = 0;
    90. } else {
    91. strlcpy(cfg_np_name, str2,
    92. MDSS_MAX_PANEL_LEN);
    93. }
    94. strlcpy(ctrl_pdata->panel_data.dsc_cfg_np_name,
    95. cfg_np_name, MDSS_MAX_PANEL_LEN);
    96. }
    97. }
    98. return dsi_pan_node;
    99. }
    100. end:
    101. if (strcmp(panel_name, NONE_PANEL))
    102. dsi_pan_node = mdss_dsi_pref_prim_panel(pdev);
    103. exit:
    104. return dsi_pan_node;
    105. }

  • 相关阅读:
    Navicat 现已支持 OceanBase 企业版
    c++新特性 noexcept 字面量 对齐方式
    云行 | 让数据奔驰在“云”间,天翼云助力贵州筑牢算力底座
    Qt下实现支持多线程的单例模式
    达梦在备份数据库时报错归档日志不连续
    SQL关联表更新
    Qt之实现圆形进度条
    【POJ No. 2777】 颜色统计 Count Color
    Apache IoTDB v1.2.0/v1.2.1 发布|增加流处理框架、动态模板等新功能
    无线智能振弦采集系统(NLM5或6多通道无线采集采发仪)
  • 原文地址:https://blog.csdn.net/qq_33782617/article/details/126312114