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 _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel