mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-10-29 05:40:25 -04:00 
			
		
		
		
	topology: Parse HW configurations of physical DAI links in text conf file
Users can configure the runtime supported HW configurations of a physical link by SectionHWConfig. A physical link can refer multiple HW config sections in SectionLink. Signed-off-by: Mengdong Lin <mengdong.lin@linux.intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
		
							parent
							
								
									6b4d775b97
								
							
						
					
					
						commit
						f7bf8b0cc8
					
				
					 5 changed files with 236 additions and 0 deletions
				
			
		|  | @ -631,10 +631,31 @@ extern "C" { | |||
|  * | ||||
|  *	id "0"				# used for binding to the link | ||||
|  * | ||||
|  *	hw_configs [	# runtime supported HW configurations, optional | ||||
|  *		"config1" | ||||
|  *		"config2" | ||||
|  *		... | ||||
|  *	] | ||||
|  * | ||||
|  *	default_hw_conf_id "1"		#default HW config ID for init | ||||
|  * | ||||
|  *	data "name"			# optional private data | ||||
|  * } | ||||
|  * </pre> | ||||
|  * | ||||
|  * A physical link can refer to multiple runtime supported hardware | ||||
|  * configurations, which is defined by SectionHWConfig. | ||||
|  * | ||||
|  * <pre> | ||||
|  * SectionHWConfig."name" { | ||||
|  * | ||||
|  *	id "1"				# used for binding to the config | ||||
|  *	format "I2S"			# physical audio format. | ||||
|  *	bclk   "master"			# Platform is master of bit clock | ||||
|  *	fsync  "slave"			# Platform is slave of fsync | ||||
|  * } | ||||
|  * </pre> | ||||
|  * | ||||
|  * <h4>Manifest Private Data</h4> | ||||
|  * Manfiest may have private data. Users need to define a manifest section | ||||
|  * and add the references to 1 or multiple data sections. Please refer to | ||||
|  | @ -706,6 +727,7 @@ enum snd_tplg_type { | |||
| 	SND_TPLG_TYPE_TOKEN,		/*!< Vendor tokens */ | ||||
| 	SND_TPLG_TYPE_TUPLE,		/*!< Vendor tuples */ | ||||
| 	SND_TPLG_TYPE_LINK,		/*!< Physical DAI link */ | ||||
| 	SND_TPLG_TYPE_HW_CONFIG,	/*!< Link HW config */ | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -208,6 +208,10 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, | |||
| 		list_add_tail(&elem->list, &tplg->tuple_list); | ||||
| 		elem->free = tplg_free_tuples; | ||||
| 		break; | ||||
| 	case SND_TPLG_TYPE_HW_CONFIG: | ||||
| 		list_add_tail(&elem->list, &tplg->hw_cfg_list); | ||||
| 		obj_size = sizeof(struct snd_soc_tplg_hw_config); | ||||
| 		break; | ||||
| 	default: | ||||
| 		free(elem); | ||||
| 		return NULL; | ||||
|  |  | |||
|  | @ -133,6 +133,14 @@ static int tplg_parse_config(snd_tplg_t *tplg, snd_config_t *cfg) | |||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "SectionHWConfig") == 0) { | ||||
| 			err = tplg_parse_compound(tplg, n, tplg_parse_hw_config, | ||||
| 				NULL); | ||||
| 			if (err < 0) | ||||
| 				return err; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "SectionLink") == 0 | ||||
| 			|| strcmp(id, "SectionBE") == 0) { | ||||
| 			err = tplg_parse_compound(tplg, n, tplg_parse_link, | ||||
|  | @ -455,6 +463,7 @@ snd_tplg_t *snd_tplg_new(void) | |||
| 	INIT_LIST_HEAD(&tplg->bytes_ext_list); | ||||
| 	INIT_LIST_HEAD(&tplg->token_list); | ||||
| 	INIT_LIST_HEAD(&tplg->tuple_list); | ||||
| 	INIT_LIST_HEAD(&tplg->hw_cfg_list); | ||||
| 
 | ||||
| 	return tplg; | ||||
| } | ||||
|  | @ -480,6 +489,7 @@ void snd_tplg_free(snd_tplg_t *tplg) | |||
| 	tplg_elem_free_list(&tplg->bytes_ext_list); | ||||
| 	tplg_elem_free_list(&tplg->token_list); | ||||
| 	tplg_elem_free_list(&tplg->tuple_list); | ||||
| 	tplg_elem_free_list(&tplg->hw_cfg_list); | ||||
| 
 | ||||
| 	free(tplg); | ||||
| } | ||||
|  |  | |||
|  | @ -162,6 +162,34 @@ static int build_link(snd_tplg_t *tplg, struct tplg_elem *elem) | |||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	/* hw configs */ | ||||
| 	base = &elem->ref_list; | ||||
| 	list_for_each(pos, base) { | ||||
| 
 | ||||
| 		ref = list_entry(pos, struct tplg_ref, list); | ||||
| 
 | ||||
| 		switch (ref->type) { | ||||
| 		case SND_TPLG_TYPE_HW_CONFIG: | ||||
| 			ref->elem = tplg_elem_lookup(&tplg->hw_cfg_list, | ||||
| 					ref->id, SND_TPLG_TYPE_HW_CONFIG); | ||||
| 			if (!ref->elem) { | ||||
| 				SNDERR("error: cannot find HW config '%s'" | ||||
| 				" referenced by link '%s'\n", | ||||
| 				ref->id, elem->id); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 
 | ||||
| 			memcpy(&link->hw_config[num_hw_configs], | ||||
| 				ref->elem->hw_cfg, | ||||
| 				sizeof(struct snd_soc_tplg_hw_config)); | ||||
| 			num_hw_configs++; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* add link to manifest */ | ||||
| 	tplg->manifest.dai_link_elems++; | ||||
| 
 | ||||
|  | @ -523,6 +551,54 @@ int tplg_parse_pcm(snd_tplg_t *tplg, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* parse physical link runtime supported HW configs in text conf file */ | ||||
| static int parse_hw_config_refs(snd_tplg_t *tplg, snd_config_t *cfg, | ||||
| 				struct tplg_elem *elem) | ||||
| { | ||||
| 	struct snd_soc_tplg_link_config *link = elem->link; | ||||
| 	snd_config_type_t  type; | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	snd_config_t *n; | ||||
| 	const char *id, *val = NULL; | ||||
| 
 | ||||
| 	if (snd_config_get_id(cfg, &id) < 0) | ||||
| 		return -EINVAL; | ||||
| 	type = snd_config_get_type(cfg); | ||||
| 
 | ||||
| 	/* refer to a single HW config */ | ||||
| 	if (type == SND_CONFIG_TYPE_STRING) { | ||||
| 		if (snd_config_get_string(cfg, &val) < 0) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		link->num_hw_configs = 1; | ||||
| 		return tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val); | ||||
| 	} | ||||
| 
 | ||||
| 	if (type != SND_CONFIG_TYPE_COMPOUND) { | ||||
| 		SNDERR("error: compound type expected for %s", id); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* refer to a list of HW configs */ | ||||
| 	snd_config_for_each(i, next, cfg) { | ||||
| 		const char *val; | ||||
| 
 | ||||
| 		n = snd_config_iterator_entry(i); | ||||
| 		if (snd_config_get_string(n, &val) < 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (link->num_hw_configs >= SND_SOC_TPLG_HW_CONFIG_MAX) { | ||||
| 			SNDERR("error: exceed max hw configs for link %s", id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		link->num_hw_configs++; | ||||
| 		return tplg_ref_add(elem, SND_TPLG_TYPE_HW_CONFIG, val); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Parse a physical link element in text conf file */ | ||||
| int tplg_parse_link(snd_tplg_t *tplg, | ||||
| 	snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) | ||||
|  | @ -573,6 +649,21 @@ int tplg_parse_link(snd_tplg_t *tplg, | |||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "hw_configs") == 0) { | ||||
| 			err = parse_hw_config_refs(tplg, n, elem); | ||||
| 			if (err < 0) | ||||
| 				return err; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "default_hw_conf_id") == 0) { | ||||
| 			if (snd_config_get_string(n, &val) < 0) | ||||
| 				return -EINVAL; | ||||
| 
 | ||||
| 			link->default_hw_config_id = atoi(val); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "data") == 0) { | ||||
| 			err = tplg_parse_data_refs(n, elem); | ||||
| 			if (err < 0) | ||||
|  | @ -638,6 +729,110 @@ int tplg_parse_cc(snd_tplg_t *tplg, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int get_audio_hw_format(const char *val) | ||||
| { | ||||
| 	if (!strlen(val)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (!strcmp(val, "I2S")) | ||||
| 		return SND_SOC_DAI_FORMAT_I2S; | ||||
| 
 | ||||
| 	if (!strcmp(val, "RIGHT_J")) | ||||
| 		return SND_SOC_DAI_FORMAT_RIGHT_J; | ||||
| 
 | ||||
| 	if (!strcmp(val, "LEFT_J")) | ||||
| 		return SND_SOC_DAI_FORMAT_LEFT_J; | ||||
| 
 | ||||
| 	if (!strcmp(val, "DSP_A")) | ||||
| 		return SND_SOC_DAI_FORMAT_DSP_A; | ||||
| 
 | ||||
| 	if (!strcmp(val, "LEFT_B")) | ||||
| 		return SND_SOC_DAI_FORMAT_DSP_B; | ||||
| 
 | ||||
| 	if (!strcmp(val, "AC97")) | ||||
| 		return SND_SOC_DAI_FORMAT_AC97; | ||||
| 
 | ||||
| 	if (!strcmp(val, "PDM")) | ||||
| 		return SND_SOC_DAI_FORMAT_PDM; | ||||
| 
 | ||||
| 	SNDERR("error: invalid audio HW format %s\n", val); | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, | ||||
| 			 void *private ATTRIBUTE_UNUSED) | ||||
| { | ||||
| 
 | ||||
| 	struct snd_soc_tplg_hw_config *hw_cfg; | ||||
| 	struct tplg_elem *elem; | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	snd_config_t *n; | ||||
| 	const char *id, *val = NULL; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG); | ||||
| 	if (!elem) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	hw_cfg = elem->hw_cfg; | ||||
| 	hw_cfg->size = elem->size; | ||||
| 
 | ||||
| 	tplg_dbg(" Link HW config: %s\n", elem->id); | ||||
| 
 | ||||
| 	snd_config_for_each(i, next, cfg) { | ||||
| 
 | ||||
| 		n = snd_config_iterator_entry(i); | ||||
| 		if (snd_config_get_id(n, &id) < 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* skip comments */ | ||||
| 		if (strcmp(id, "comment") == 0) | ||||
| 			continue; | ||||
| 		if (id[0] == '#') | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (strcmp(id, "id") == 0) { | ||||
| 			if (snd_config_get_string(n, &val) < 0) | ||||
| 				return -EINVAL; | ||||
| 
 | ||||
| 			hw_cfg->id = atoi(val); | ||||
| 			tplg_dbg("\t%s: %d\n", id, hw_cfg->id); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "format") == 0) { | ||||
| 			if (snd_config_get_string(n, &val) < 0) | ||||
| 				return -EINVAL; | ||||
| 
 | ||||
| 			ret = get_audio_hw_format(val); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 			hw_cfg->fmt = ret; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "bclk") == 0) { | ||||
| 			if (snd_config_get_string(n, &val) < 0) | ||||
| 				return -EINVAL; | ||||
| 
 | ||||
| 			if (!strcmp(val, "master")) | ||||
| 				hw_cfg->bclk_master = true; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "fsync") == 0) { | ||||
| 			if (snd_config_get_string(n, &val) < 0) | ||||
| 				return -EINVAL; | ||||
| 
 | ||||
| 			if (!strcmp(val, "master")) | ||||
| 				hw_cfg->fsync_master = true; | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* copy stream object */ | ||||
| static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm, | ||||
| 				struct snd_tplg_stream_template *strm_tpl) | ||||
|  |  | |||
|  | @ -74,6 +74,7 @@ struct snd_tplg { | |||
| 	struct list_head manifest_list; | ||||
| 	struct list_head pcm_config_list; | ||||
| 	struct list_head pcm_caps_list; | ||||
| 	struct list_head hw_cfg_list; | ||||
| 
 | ||||
| 	/* type-specific control lists */ | ||||
| 	struct list_head mixer_list; | ||||
|  | @ -148,6 +149,7 @@ struct tplg_elem { | |||
| 		struct snd_soc_tplg_dapm_graph_elem *route; | ||||
| 		struct snd_soc_tplg_stream *stream_cfg; | ||||
| 		struct snd_soc_tplg_stream_caps *stream_caps; | ||||
| 		struct snd_soc_tplg_hw_config *hw_cfg; | ||||
| 
 | ||||
| 		/* these do not map to UAPI structs but are internal only */ | ||||
| 		struct snd_soc_tplg_ctl_tlv *tlv; | ||||
|  | @ -226,6 +228,9 @@ int tplg_parse_link(snd_tplg_t *tplg, | |||
| int tplg_parse_cc(snd_tplg_t *tplg, | ||||
| 	snd_config_t *cfg, void *private ATTRIBUTE_UNUSED); | ||||
| 
 | ||||
| int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, | ||||
| 			 void *private ATTRIBUTE_UNUSED); | ||||
| 
 | ||||
| int tplg_build_data(snd_tplg_t *tplg); | ||||
| int tplg_build_manifest_data(snd_tplg_t *tplg); | ||||
| int tplg_build_controls(snd_tplg_t *tplg); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mengdong Lin
						Mengdong Lin