Re: [alsa-devel] [RFD] Add hw_params support for hostless stream BE DAIs
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:
- Allow params to be defined for other BE dai link also. this
currently applicable only for codec_codec link.
- 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
- 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.
- with the above, fix_up can be removed and can use .params to
select the parameter.
Core Changes:
- params definition needs to changed, currently this is defined to used
only for codec codec link. define new flag to identifying codec codec link.
- 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.
- 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.
- 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) {
- 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);}
- 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
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:
- Allow params to be defined for other BE dai link also. this
currently applicable only for codec_codec link.
- 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
On Tue, Feb 23, 2016 at 10:51:14AM +0000, Liam Girdwood wrote:
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 ?
It's to an extent a choice between two evils here - if we've got something that looks like you should be able to play audio through it but you can't actually play audio through it and need to write a custom application to control it then how much are we winning in consistency? On the other hand the mechanisms for discovering and running through constraints that PCMs currently have are useful.
participants (3)
-
Liam Girdwood
-
Mark Brown
-
Vinod Koul