[alsa-devel] [RFD] Add hw_params support for hostless stream BE DAIs
Vinod Koul
vinod.koul at intel.com
Fri Feb 26 07:47:31 CET 2016
On Tue, Feb 23, 2016 at 10:51:14AM +0000, Liam Girdwood wrote:
> 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....
Okay great then :-)
>
> 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 ?
Today c-c links specify this the PCM parameters thru these controls so we
took the same approach.
If we decide that we should have more granular control we can update this
part :)
Moreover in driver we do sepcify constraints on the user for selecting PCM
params. This is our way of specifying constrained values to users :D
--
~Vinod
> > 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
>
>
--
~Vinod
More information about the Alsa-devel
mailing list