[alsa-devel] [RFD] Add hw_params support for hostless stream BE DAIs

Liam Girdwood liam.r.girdwood at linux.intel.com
Tue Feb 23 11:51:14 CET 2016


On Tue, 2016-02-16 at 21:47 +0530, Jeeja KP wrote:
> We have no mechanism to configure hw_params for DAIs which are hostless.
> Like a loop or DAIs connected to internal sinks/sources.
> 

Fwiw, a few years back Peter, Sebastien and I were considering a similar
mechanism for the ABE on OMAP4. We never got around to implementing or
upstreaming though....

One comment below.

> Problem Statement:
> 	During a BE-BE loop of  DSP or a SRC->SINK path, we cannot program
> 	hw_params. There are no hw_params from user as these are hostless.
> 	This scenario can happens in below 2 cases:
> 	
> Case 1: BE-BE loop aka hostless stream through SoC DSP
> 	Hostless stream can be defined by BE to BE connection and in this case
> 	the path is enabled or disabled by the state of the DAPM graph. This
> 	usually means there is a mixer control that can be used to connect or
> 	disconnect the path between both DAIs.
> Example:
> 			|--------------/----------------|
> 			|				|
> 			|				|
> 		      codec out			    modem_in
> 			|				|
> 			|				|
> 		     ssp0_TX			    ssp1_RX
> 			|				|
> 			|				|
> 		  AIFI_Playback			dummy_capture
> 			
> 	In the above case (SSP0,codec playback,SSp1 dai needs to be configured 
> 	and turned on with  the selected hw_params). 
> 
> Case 2: SoC Internal Sink or Source
> Example:
>             		WOV SINK (DAPM Sink)
> 				|
> 				|
> 				|                 
> 			     Switch
> 				|
> 				|
> 				w1
> 				|
> 			dmic rx (BE dai widget)
> 				|
> 			Capture(codec dai widget)
> 				|
> 			    DMIC AIF
> 				|
> 			      DMic
> 				|
> 		Soc DMic (MIC Widget) (SRC)
> 
> 	In the above case BE (dmic RX) dai hw_params needs to applied.
> 	
> Approach:
> 	1. Allow params to be defined for other BE dai link also. this
> 	currently applicable only for codec_codec link.
> 
> 	2. when params is defined for BE dai link, create params kcontrols
> 	and associate it with the dai widgets. this will allow params to be
> 	selected via Kcontrol for each dai defined in the dai link.
> 

The only problem we have with using a kcontrol to define params is that
we already have an existing kernel ioctl and public C API to define
SW/HW params. This would add another mechanism, unless each new kcontrol
allows modification of a single param ?

Liam

> 	3. Before powering on the dai widget and if the dai is not powered
> 	with the stream event, then dai_ops(hw_params) for the dai widget will
> 	be called.This will apply the selected hw_params.
> 
> 	4. with the above, fix_up can be removed and can use .params to 
> 	select the parameter.
> 
> Core Changes:
> 	1. params definition needs to changed, currently this is defined to used
> 	only for codec codec link. define new flag to identifying codec 
> 	codec link.
> 
> 	2. if params is defined for any BE dai link, then create kcontrol for
> 	each dai defined in the dai_link and associated the kcontrol to dai 
> 	widget.
> 
> 	3. In the powering sequence check if the dai widget and if the event is
> 	not a stream event, SND_SOC_DAPM_STREAM_NOP , then call dai ops with 
> 	the selected params. This will apply the hw_params for dai.
> 
> 1. params definition:
> 		- Add new flag in struct snd_soc_dai_link.
> 		- Based on this flag create, the codec codec link instead of
> 		  params
> struct snd_soc_dai_link {
>         const struct snd_soc_pcm_stream *params;
>         unsigned int num_params;
> 
> +       /* flag to create codec codec based on the flag*/
> +       unsigned int codec_link:1;
> +
>         unsigned int dai_fmt;           /* format to set on init */
> 
>         enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
> 
> 	- creation of dai link based on the codec_link flag
> static int soc_probe_link_dais(struct snd_soc_card *card,
> int num, int order)
>                 }
>         } else {
> 
> -               if (!dai_link->params) {
> +               if (!dai_link->codec_link) {
>                         /* create the pcm */
>                         ret = soc_new_pcm(rtd, num);
>                         if (ret < 0) {
> 						
> 2. when params is defined for BE dai link, create params kcontrols and
> associate it with the dai widgets. this will allow params to be selected
> via Kcontrol for each dai defined in the dai link.
> a. Move the param control creation from snd_soc_dapm_new_pcm and add new
> API to created kcontrols with the params.
> +snd_soc_dapm_create_param_control(struct snd_soc_card *card,
> +					const struct snd_soc_pcm_stream *params,
> +					unsigned int num_params)
> {
> 	/* create kcontrol based on the params */
> }
> 
> - modify the snd_soc_dapm_new_pcm to create the param control
> int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
> 			 const struct snd_soc_pcm_stream *params,
> 			 unsigned int num_params,
> 			 struct snd_soc_dapm_widget *source,
> 			 struct snd_soc_dapm_widget *sink)
> {
> 
> 	template.kcontrol_news = snd_soc_dapm_create_param_control(card,
> 							params, num_params);
>  	if (!template.kcontrol_news) {
>  		dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",
>  			link_name);
>  		ret = -ENOMEM;
> -		goto outfree_private_value;
> +		goto outfree_link_name;
>  	}
>  
> 
> b. add new API snd_soc_dapm_new_dai_params() to create kcontrol for dai and associated it to dai.
> +int snd_soc_dapm_new_dai_params(struct snd_soc_card *card,
> +			 const struct snd_soc_pcm_stream *params,
> +			 unsigned int num_params,
> +			 struct snd_soc_dapm_widget *dai_w)
> +{
> +	struct snd_kcontrol_new *kcontrol_news;
> +
> +	kcontrol_news = snd_soc_dapm_create_param_control(card, params,
> +								num_params);
> +	if (!kcontrol_news) {
> +		dev_err(card->dev, "ASoC: Failed to create control for %s widget\n",
> +			dai_w->name);
> +		return -EINVAL;
> +	}
> +
> +	dai_w->num_kcontrols = 1;
> +	dai_w->kcontrol_news = kcontrol_news;
> +
> +	dai_w->params = params;
> +	dai_w->num_params = num_params;
> +
> +	return 0;
> +
> +}
> +
> +++ b/include/sound/soc-dapm.h
> +int snd_soc_dapm_new_dai_params(struct snd_soc_card *card,
> +			 const struct snd_soc_pcm_stream *params,
> +			 unsigned int num_params,
> +			 struct snd_soc_dapm_widget *dai_w);
> 
> 
> c. if the be dai link has .params defined, call new API snd_soc_dapm_new_dai_params
> 
> +static int soc_link_be_params_widget(struct snd_soc_card *card,
> +				struct snd_soc_dai *dai,
> +				const struct snd_soc_pcm_stream *params,
> +				unsigned int num_params)
> +{
> +	int ret;
> +	struct snd_soc_dapm_widget *w;
> +   
> +   if (!strcmp(dai->name, "snd-soc-dummy"))
> +		return 0;
> +		
> +	if (dai->playback_widget) {
> +		w = dai->playback_widget;
> +		ret = snd_soc_dapm_new_dai_params(card, params,
> +								num_params, w);
> +
> +		if (ret != 0) {
> +			dev_err(card->dev,
> +					"ASoC: Can't create params widget=%s %d\n",
> +					w->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (dai->capture_widget) {
> +		w = dai->capture_widget;
> +		ret = snd_soc_dapm_new_dai_params(card, params,
> +								num_params, w);
> +
> +		if (ret != 0) {
> +			dev_err(card->dev,
> +				"ASoC: Can't create params widget=%s %d\n",
> +				w->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int soc_link_be_params_dai_widgets(struct snd_soc_card *card,
> +				struct snd_soc_dai_link *dai_link,
> +				struct snd_soc_pcm_runtime *rtd)
> +{
> +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
> +	int ret, i;
> +
> +	for (i = 0; i < rtd->num_codecs; i++) {
> +		struct snd_soc_dai *codec = rtd->codec_dais[i];
> +
> +		ret = soc_link_be_params_widget(card, codec,
> +							dai_link->params,
> +							dai_link->num_params);
> +
> +		if (!ret)
> +			return ret;
> +	}
> +
> +	return soc_link_be_params_widget(card, cpu_dai, dai_link->params,
> +						dai_link->num_params);
> +}
> +
> 
> @@ -1651,6 +1714,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
>  				       dai_link->stream_name, ret);
>  				return ret;
>  			}
> +			if (dai_link->params && dai_link->no_pcm) {
> +				/* create dai link controls */
> +				ret = soc_link_be_params_dai_widgets(card,
> +								dai_link, rtd);
> +				if (ret)
> +					return ret;
> +			}
>  		} else {
>  			INIT_DELAYED_WORK(&rtd->delayed_work,
>  						codec2codec_close_delayed_work);
> 
> 
> 3. Applying the hw_params for dai widget
> 
> --- a/sound/soc/soc-dapm.c
> +++ b/sound/soc/soc-dapm.c
> @@ -1398,9 +1398,64 @@ static void dapm_seq_check_event(struct snd_soc_card *
> card,
>  	}
>  }
>  
> +static int snd_soc_dai_params(struct snd_soc_dapm_widget *w, int event)
> +{
> +	struct snd_soc_dai *dai;
> +	const struct snd_soc_pcm_stream *config = w->params + w->params_select;
> +	struct snd_pcm_substream substream;
> +	struct snd_pcm_hw_params *params = NULL;
> +	u64 fmt;
> +	int ret = 0;
> +
> +	if (WARN_ON(!config))
> +		return -EINVAL;
> +
> +	dai = w->priv;
> +
> +	if (config->formats) {
> +		fmt = ffs(config->formats) - 1;
> +	} else {
> +		dev_warn(w->dapm->dev, "ASoC: Invalid format %llx specified\n",
> +			 config->formats);
> +		fmt = 0;
> +	}
> +
> +	/* Currently very limited parameter selection */
> +	params = kzalloc(sizeof(*params), GFP_KERNEL);
> +	if (!params) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
> +
> +	hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
> +		config->rate_min;
> +	hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max =
> +		config->rate_max;
> +
> +	hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
> +		= config->channels_min;
> +	hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
> +		= config->channels_max;
> +
> +	memset(&substream, 0, sizeof(substream));
> +	if (w->id == snd_soc_dapm_dai_in)
> +		substream.stream = SNDRV_PCM_STREAM_CAPTURE;
> +	else
> +		substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
> +
> +	ret = soc_dai_hw_params(&substream, params, dai);
> +	if (ret < 0)
> +		goto out;
> +out:
> +	kfree(params);
> +	return ret;
> +}
> +
>  /* Apply the coalesced changes from a DAPM sequence */
>  static void dapm_seq_run_coalesced(struct snd_soc_card *card,
> -				   struct list_head *pending)
> +				   struct list_head *pending,
> +				   int event)
>  {
>  	struct snd_soc_dapm_context *dapm;
>  	struct snd_soc_dapm_widget *w;
> @@ -1413,6 +1468,14 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
>  	dapm = w->dapm;
>  
>  	list_for_each_entry(w, pending, power_list) {
> +		if ((event == SND_SOC_DAPM_STREAM_NOP) &&
> +			((w->id == snd_soc_dapm_dai_out) ||
> +			(w->id == snd_soc_dapm_dai_in))) {
> +			snd_soc_dai_params(w, SND_SOC_DAPM_PRE_PMU);
> +		}
> +	}
> +
> +	list_for_each_entry(w, pending, power_list) {
>  		WARN_ON(reg != w->reg || dapm != w->dapm);
>  		w->power = w->new_power;
>  
> @@ -1447,6 +1510,13 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
>  		dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU);
>  		dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD);
>  	}
> +	list_for_each_entry(w, pending, power_list) {
> +		if ((event == SND_SOC_DAPM_STREAM_NOP) &&
> +			((w->id == snd_soc_dapm_dai_out) ||
> +			(w->id == snd_soc_dapm_dai_in))) {
> +			snd_soc_dai_params(w, SND_SOC_DAPM_POST_PMD);
> +		}
> +	}
>  }
>  
>  /* Apply a DAPM power sequence.
> @@ -1482,7 +1552,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
>  		if (sort[w->id] != cur_sort || w->reg != cur_reg ||
>  		    w->dapm != cur_dapm || w->subseq != cur_subseq) {
>  			if (!list_empty(&pending))
> -				dapm_seq_run_coalesced(card, &pending);
> +				dapm_seq_run_coalesced(card, &pending, event);
>  
>  			if (cur_dapm && cur_dapm->seq_notifier) {
>  				for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
> @@ -1545,7 +1615,7 @@ static void dapm_seq_run(struct snd_soc_card *card,
>  	}
>  
>  	if (!list_empty(&pending))
> -		dapm_seq_run_coalesced(card, &pending);
> +		dapm_seq_run_coalesced(card, &pending, event);
>  
>  	if (cur_dapm && cur_dapm->seq_notifier) {
>  		for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
> -- 
> 1.7.9.5
> 
> Comments welcome, based on discussion we will send patches.
> 
> --Jeeja




More information about the Alsa-devel mailing list