Msm8953 LK transmits LCD parameter process analysis to Kernel through cmdline

This article is mainly based on Qualcomm msm8953 to analyze the process of lcd passing parameters to kernel through cmdline in lk stage.

1. LK stage analysis

Related documents:

app/aboot/aboot.c

Target/msm8953/Target_display.c

Dev/gcdb/display/gcdb_display_param.c

Target/msm8953/oem_panel.c

Save the screen information to display_panel_buf in the gcdb_display_cmdline_arg function, and after integrating other module information in the boot_linux->update_cmdline function, attach it to the bootargs in the update_device_tree function, so as to pass information to the kernel. The relevant code is as follows:

1. The whole process mainly occurs in update_cmdline, as follows:

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 is assigned in the _panel_data function in oem_panel.c, as follows:

struct panel_struct mdss_dsi_get_panel_data(void)
{
	return panelstruct;
}

case LT8911B_1080P_VIDEO_PANEL:
        panelstruct->paneldata    = &lt8911b_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     = &lt8911b_1080p_video_panel_res;
		panelstruct->color        = &lt8911b_1080p_video_color;
		panelstruct->videopanel   = &lt8911b_1080p_video_video_panel;
		panelstruct->commandpanel = &lt8911b_1080p_video_command_panel;
		panelstruct->state        = &lt8911b_1080p_video_state;
		panelstruct->laneconfig   = &lt8911b_1080p_video_lane_config;
		panelstruct->paneltiminginfo
			= &lt8911b_1080p_video_timing_info;
		panelstruct->panelresetseq
					 = &lt8911b_1080p_video_reset_seq;
		panelstruct->backlightinfo = &lt8911b_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;

The related structures are defined as follows:

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;
};

Therefore, when executing panel_node = panelstruct.paneldata->panel_node_id, the name information of the main screen is actually assigned to panel_node, that is, panel_node = qcom,mdss_dsi_lt8911b_1080p_video.

1.2) mdss_dsi_set_panel_node mainly involves oem_data data, and oem_data data is assigned in set_panel_cmd_string, as follows:

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");

Therefore, oem_data.skip is actually false, and oem_data.sec_panel is actually "dsi_lt8911b_1080p_dsi1_video" string.

1.3) panel_name_to_dt_string matches the panel_dt_string member in the lookup_skip_panels array according to oem_data.sec_panel to find the node name, as follows:

//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},
}

Therefore, slave_panel_node = qcom,mdss_dsi_lt8911b_1080p_dsi1_video.

Therefore, the approximate data of display_panel_buf in the update_cmdline function is:

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) First assign display_panel_buf in update_cmdline, and then add cmdline information to bootargs in update_device_tree, as follows:

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;
		}
	}
}

2. Kernel stage analysis
1. Obtain the buf passed to the kernel in the Start_kernel function, and write buf into the _param segment, as follows:

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, &params[i])=param_set_copystring

And param is defined as follows:

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);
}

Therefore, the parameters passed from lk to the kernel are stored in the boot_command_line, and the passed parameters are written into the _param segment, and used by module_param_string when waiting for the driver to load.

2. Analyze how to use the data in the _param section, mainly from the LCD driver analysis.

2.1) There is a driver parameter passing definition in mdss_mdp.c, and after expanding the corresponding macro definition, it will match the screen information in the _param section according to the module name, as follows;

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,			\
			    &param_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) The process of using the mdss_mdp_panel array is as follows:

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 analysis:

Parse the parameter mdss_mdp_panel into pan_cfg, as follows:

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 process analysis 

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;
}

3. Summary

First, parse the cmdline (boot_command_line) from lk in start_kernel through module_param_string and save it on Mdss_mdp_panel, then copy it to pan_cfg in the mdss_mdp_get_pan_cfg function, and finally parse the specified dtsi file according to pan_cfg in mdss_dsi_find_panel_of_node to obtain the panel parameter.

Guess you like

Origin blog.csdn.net/qq_33782617/article/details/126312114