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

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    = &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;

相关结构体定义如下:

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, &params[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,			\
			    &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)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;
}

三、总结

首先是通过module_param_string在start_kernel 中去解析从lk传来的cmdline(boot_command_line)并保存在Mdss_mdp_panel上,接着通过在mdss_mdp_get_pan_cfg函数中复制到pan_cfg中,最后在mdss_dsi_find_panel_of_node中根据pan_cfg对指定的dtsi文件进行解析获取panel参数。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于高通8155单安卓基线SD卡启动,root参数可以通过修改cmdline传递。具体方法如下: 1. 在SD卡的根目录下新建一个名为"cmdline"的文件。 2. 在cmdline文件中添加以下内容: androidboot.selinux=permissive androidboot.hardware=qcom androidboot.console=ttyMSM0 console=ttyMSM0,115200n8 androidboot.serialno=android12345678 androidboot.baseband=msm androidboot.bootdevice=msm_sdcc.1 androidboot.ddr_type=lpddr4 androidboot.boottime=9999999999 androidboot.memcg=1 lpm_levels.sleep_disabled=1 androidboot.verifiedbootstate=orange androidboot.oemandroidver=8.1.0 androidboot.keymaster=1 androidboot.avb_version=1.0 androidboot.fmp_config=1 androidboot.hlos=0x07 kernel.memcg=1 androidboot.boot_recovery=1 androidboot.bootreason=kernel_panic androidboot.slot_suffix=_a androidboot.hardware.revision=8300 androidboot.bootloader=unknown androidboot.secure_hardware=1 androidboot.cid=0x0 androidboot.ab_update=1 androidboot.dtbo_idx=0 androidboot.vbmeta.device_state=unlocked androidboot.bootloader_version=unknown androidboot.bootimage.build.date=Fri Mar 26 11:07:52 UTC 2021 androidboot.bootimage.build.date.utc=1616741272 androidboot.bootimage.build.fingerprint=google/coral/coral:11/RQ2A.210305.006/7119741:user/release-keys 其中,需要添加的root参数为"androidboot.selinux=permissive",添加到最前面即可。 3. 保存文件并将SD卡插入设备中,重新启动设备。 这样,设备就会以permissive模式启动,并且root权限已经被授予。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值