[alsa-devel] [PATCH 0/6] ASoC: improve codec to codec link support
As explained in this previous series [0], on Amlogic, we are using codec to codec links to deal with the glue which is between the i2s backends and the synopsys hdmi controller.
This worked well until I tried to .get_eld() support in the dw-hdmi-i2s driver. Doing so adds channel mapping controls to the codec. This shown several problem
1) .pcm_new() is not called on codec to codec links. struct snd_soc_pcm_runtime do not even have a valid .pcm 2) struct snd_pcm_substream and struct snd_pcm_runtime are ephemeral This is a problem if a control needs to access them
The goal of this patchset is to resolve the above issues and improve the codec to codec link support enough to correctly handle the hdmi-codec
The support of these codec to codec links could probably be improved in the future to behave like any other link and use soc_pcm_open(), soc_pcm_hw_params(), etc...
The challenge lies in the dapm mutex. The soc_pcm call dapm function locking this mutex but the dapm mutex is already held in snd_soc_dai_link_event()
[0]: https://lkml.kernel.org/r/20190515131858.32130-1-jbrunet@baylibre.com
Jerome Brunet (6): ASoC: codec2codec: run callbacks in order ASoC: codec2codec: name link using stream direction ASoC: codec2codec: deal with params when necessary ASoC: create pcm for codec2codec links as well ASoC: codec2codec: remove ephemeral variables ASoC: codec2codec: fill some of the runtime stream parameters
sound/soc/soc-core.c | 42 +++------- sound/soc/soc-dapm.c | 186 +++++++++++++++++++++++++++---------------- sound/soc/soc-pcm.c | 35 +++++++- 3 files changed, 162 insertions(+), 101 deletions(-)
When handling dai_link events on codec to codec links, run all .startup() callbacks on sinks and sources before running any .hw_params(). Same goes for hw_free() and shutdown(). This is closer to the behavior of regular dai links
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- sound/soc/soc-dapm.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1d04612601ad..5348abda7ce2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3835,11 +3835,6 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, goto out; } source->active++; - ret = snd_soc_dai_hw_params(source, &substream, params); - if (ret < 0) - goto out; - - dapm_update_dai_unlocked(&substream, params, source); }
substream.stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -3853,7 +3848,24 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, goto out; } sink->active++; - ret = snd_soc_dai_hw_params(sink, &substream, params); + } + + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + snd_soc_dapm_widget_for_each_source_path(w, path) { + source = path->source->priv; + + ret = soc_dai_hw_params(&substream, params, source); + if (ret < 0) + goto out; + + dapm_update_dai_unlocked(&substream, params, source); + } + + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + + ret = soc_dai_hw_params(&substream, params, sink); if (ret < 0) goto out;
@@ -3889,9 +3901,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, substream.stream = SNDRV_PCM_STREAM_CAPTURE; snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv; - snd_soc_dai_hw_free(source, &substream); + }
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + snd_soc_dai_hw_free(sink, &substream); + } + + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + snd_soc_dapm_widget_for_each_source_path(w, path) { + source = path->source->priv; source->active--; snd_soc_dai_shutdown(source, &substream); } @@ -3899,9 +3920,6 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, substream.stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv; - - snd_soc_dai_hw_free(sink, &substream); - sink->active--; snd_soc_dai_shutdown(sink, &substream); }
On Wed, Jul 24, 2019 at 06:24:00PM +0200, Jerome Brunet wrote:
When handling dai_link events on codec to codec links, run all .startup() callbacks on sinks and sources before running any .hw_params(). Same goes for hw_free() and shutdown(). This is closer to the behavior of regular dai links
This looks good but needs rebasing against -next due to Morimoto-san's recent DAI changes:
CC sound/soc/soc-dapm.o sound/soc/soc-dapm.c: In function ‘snd_soc_dai_link_event’: sound/soc/soc-dapm.c:3857:10: error: implicit declaration of function ‘soc_dai_hw_params’; did you mean ‘snd_soc_dai_hw_params’? [-Werror=implicit-function-declaration] ret = soc_dai_hw_params(&substream, params, source); ^~~~~~~~~~~~~~~~~ snd_soc_dai_hw_params
On Thu 25 Jul 2019 at 14:00, Mark Brown broonie@kernel.org wrote:
On Wed, Jul 24, 2019 at 06:24:00PM +0200, Jerome Brunet wrote:
When handling dai_link events on codec to codec links, run all .startup() callbacks on sinks and sources before running any .hw_params(). Same goes for hw_free() and shutdown(). This is closer to the behavior of regular dai links
This looks good but needs rebasing against -next due to Morimoto-san's recent DAI changes:
CC sound/soc/soc-dapm.o sound/soc/soc-dapm.c: In function ‘snd_soc_dai_link_event’: sound/soc/soc-dapm.c:3857:10: error: implicit declaration of function ‘soc_dai_hw_params’; did you mean ‘snd_soc_dai_hw_params’? [-Werror=implicit-function-declaration] ret = soc_dai_hw_params(&substream, params, source); ^~~~~~~~~~~~~~~~~ snd_soc_dai_hw_params
I did rebase against next and saw Morimoto-san's patchset. I must have messed up when formatting the patches, sorry about that. I'll resend.
At the moment, codec to codec dai link widgets are named after the cpu dai and the 1st codec valid on the link. This might be confusing if there is multiple valid codecs on the link for one stream direction.
Instead, use the dai link name and the stream direction to name the the dai link widget
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- sound/soc/soc-dapm.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5348abda7ce2..d20cd89513a4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4056,8 +4056,7 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
static struct snd_soc_dapm_widget * snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) + char *id) { struct snd_soc_dapm_widget template; struct snd_soc_dapm_widget *w; @@ -4067,7 +4066,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, int ret;
link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", - source->name, sink->name); + rtd->dai_link->name, id); if (!link_name) return ERR_PTR(-ENOMEM);
@@ -4247,15 +4246,13 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, }
for_each_rtd_codec_dai(rtd, i, codec_dai) { - /* connect BE DAI playback if widgets are valid */ codec = codec_dai->playback_widget;
if (playback_cpu && codec) { if (!playback) { playback = snd_soc_dapm_new_dai(card, rtd, - playback_cpu, - codec); + "playback"); if (IS_ERR(playback)) { dev_err(rtd->dev, "ASoC: Failed to create DAI %s: %ld\n", @@ -4284,8 +4281,7 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, if (codec && capture_cpu) { if (!capture) { capture = snd_soc_dapm_new_dai(card, rtd, - codec, - capture_cpu); + "capture"); if (IS_ERR(capture)) { dev_err(rtd->dev, "ASoC: Failed to create DAI %s: %ld\n",
When there is an event on codec to codec dai_link, we only need to deal with params if the event is SND_SOC_DAPM_PRE_PMU, when .hw_params() is called. For the other events, it is useless.
Also, params does not need to be dynamically allocated as it does not need to survive the event.
Last, dealing with the codec to codec params just before calling .hw_params() callbacks give change to either party on the link to alter params content in .startup(), which might be useful in some cases
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- sound/soc/soc-dapm.c | 81 ++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 30 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d20cd89513a4..aa6e47beaec3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3764,25 +3764,12 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
-static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int +snd_soc_dai_link_prepare_params(struct snd_soc_dapm_widget *w, + struct snd_pcm_hw_params *params, + const struct snd_soc_pcm_stream *config) { - struct snd_soc_dapm_path *path; - struct snd_soc_dai *source, *sink; - struct snd_soc_pcm_runtime *rtd = w->priv; - const struct snd_soc_pcm_stream *config; - struct snd_pcm_substream substream; - struct snd_pcm_hw_params *params = NULL; - struct snd_pcm_runtime *runtime = NULL; unsigned int fmt; - int ret = 0; - - config = rtd->dai_link->params + rtd->params_select; - - if (WARN_ON(!config) || - WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || - list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) - return -EINVAL;
/* Be a little careful as we don't want to overflow the mask array */ if (config->formats) { @@ -3791,26 +3778,41 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, 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; + return -EINVAL; } - snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
+ memset(params, 0, sizeof(*params)); + + 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;
+ return 0; +} + +static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_dapm_path *path; + struct snd_soc_dai *source, *sink; + struct snd_soc_pcm_runtime *rtd = w->priv; + const struct snd_soc_pcm_stream *config; + struct snd_pcm_substream substream; + struct snd_pcm_hw_params params; + struct snd_pcm_runtime *runtime = NULL; + int ret = 0; + + if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || + list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) + return -EINVAL; + memset(&substream, 0, sizeof(substream));
/* Allocate a dummy snd_pcm_runtime for startup() and other ops() */ @@ -3850,27 +3852,47 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, sink->active++; }
+ /* + * Note: getting the config after .startup() gives a chance to + * either party on the link to alter the configuration if + * necessary + */ + config = rtd->dai_link->params + rtd->params_select; + if (WARN_ON(!config)) { + dev_err(w->dapm->dev, "ASoC: link config missing\n"); + ret = -EINVAL; + goto out; + } + + ret = snd_soc_dai_link_prepare_params(w, ¶ms, config); + if (ret < 0) { + dev_err(w->dapm->dev, "ASoC: link params prepare failed: %d\n", + ret); + goto out; + } + substream.stream = SNDRV_PCM_STREAM_CAPTURE; snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv;
- ret = soc_dai_hw_params(&substream, params, source); + ret = soc_dai_hw_params(&substream, ¶ms, source); if (ret < 0) goto out;
- dapm_update_dai_unlocked(&substream, params, source); + dapm_update_dai_unlocked(&substream, ¶ms, source); }
substream.stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv;
- ret = soc_dai_hw_params(&substream, params, sink); + ret = soc_dai_hw_params(&substream, ¶ms, sink); if (ret < 0) goto out;
- dapm_update_dai_unlocked(&substream, params, sink); + dapm_update_dai_unlocked(&substream, ¶ms, sink); } + break;
case SND_SOC_DAPM_POST_PMU: @@ -3932,7 +3954,6 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
out: kfree(runtime); - kfree(params); return ret; }
On Wed, Jul 24, 2019 at 06:24:02PM +0200, Jerome Brunet wrote:
Also, params does not need to be dynamically allocated as it does not need to survive the event.
It's dynamically allocated because it's a pretty large structure and so the limited stack sizes the kernel has make it a bit uncomfortable to put it on the stack.
On Thu 25 Jul 2019 at 13:55, Mark Brown broonie@kernel.org wrote:
On Wed, Jul 24, 2019 at 06:24:02PM +0200, Jerome Brunet wrote:
Also, params does not need to be dynamically allocated as it does not need to survive the event.
It's dynamically allocated because it's a pretty large structure and so the limited stack sizes the kernel has make it a bit uncomfortable to put it on the stack.
Ok, I'll revert this in v2
At the moment, codec to codec links uses an ephemeral variable for the struct snd_pcm_substream. Also the struct snd_soc_pcm_runtime does not have real struct snd_pcm.
This might a problem if the functions used by a codec on codec to codec link expect these structures to exist, and keep on existing during the life of the codec.
For example, it is the case of the hdmi-codec, which uses snd_pcm_add_chmap_ctls(). For the controls to works, the pcm and substream must to exist.
This change is first step, it create pcm (and substreams) for codec to codec links, in the same way as dpcm backend links.
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- sound/soc/soc-core.c | 42 ++++++++++++------------------------------ sound/soc/soc-pcm.c | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 33 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7ecfe641ca46..9e8eb93c8a3f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -447,16 +447,6 @@ static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) flush_delayed_work(&rtd->delayed_work); }
-static void codec2codec_close_delayed_work(struct work_struct *work) -{ - /* - * Currently nothing to do for c2c links - * Since c2c links are internal nodes in the DAPM graph and - * don't interface with the outside world or application layer - * we don't have to do any special handling on close. - */ -} - #ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ int snd_soc_suspend(struct device *dev) @@ -1552,27 +1542,19 @@ static int soc_probe_link_dais(struct snd_soc_card *card, return ret; }
- if (!dai_link->params) { - /* create the pcm */ - ret = soc_new_pcm(rtd, num); - if (ret < 0) { - dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", - dai_link->stream_name, ret); - return ret; - } - ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); - if (ret < 0) - return ret; - ret = soc_link_dai_pcm_new(rtd->codec_dais, - rtd->num_codecs, rtd); - if (ret < 0) - return ret; - } else { - INIT_DELAYED_WORK(&rtd->delayed_work, - codec2codec_close_delayed_work); + /* create the pcm */ + ret = soc_new_pcm(rtd, num); + if (ret < 0) { + dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", + dai_link->stream_name, ret); + return ret; } - - return 0; + ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); + if (ret < 0) + return ret; + ret = soc_link_dai_pcm_new(rtd->codec_dais, + rtd->num_codecs, rtd); + return ret; }
static int soc_bind_aux_dev(struct snd_soc_card *card, int num) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index fabeac164a6c..30264bc592f6 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -678,6 +678,16 @@ static void close_delayed_work(struct work_struct *work) mutex_unlock(&rtd->pcm_mutex); }
+static void codec2codec_close_delayed_work(struct work_struct *work) +{ + /* + * Currently nothing to do for c2c links + * Since c2c links are internal nodes in the DAPM graph and + * don't interface with the outside world or application layer + * we don't have to do any special handling on close. + */ +} + /* * Called by ALSA when a PCM substream is closed. Private data can be * freed here. The cpu DAI, codec DAI, machine and components are also @@ -3011,6 +3021,12 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) playback = rtd->dai_link->dpcm_playback; capture = rtd->dai_link->dpcm_capture; } else { + /* Adapt stream for codec2codec links */ + struct snd_soc_pcm_stream *cpu_capture = rtd->dai_link->params ? + &cpu_dai->driver->playback : &cpu_dai->driver->capture; + struct snd_soc_pcm_stream *cpu_playback = rtd->dai_link->params ? + &cpu_dai->driver->capture : &cpu_dai->driver->playback; + for_each_rtd_codec_dai(rtd, i, codec_dai) { if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) && snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK)) @@ -3019,6 +3035,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE)) capture = 1; } + + capture = capture && cpu_capture->channels_min; + playback = playback && cpu_playback->channels_min; }
if (rtd->dai_link->playback_only) { @@ -3032,7 +3051,13 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) }
/* create the PCM */ - if (rtd->dai_link->no_pcm) { + if (rtd->dai_link->params) { + snprintf(new_name, sizeof(new_name), "codec2codec(%s)", + rtd->dai_link->stream_name); + + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + playback, capture, &pcm); + } else if (rtd->dai_link->no_pcm) { snprintf(new_name, sizeof(new_name), "(%s)", rtd->dai_link->stream_name);
@@ -3059,13 +3084,17 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);
/* DAPM dai link stream work */ - INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + if (rtd->dai_link->params) + INIT_DELAYED_WORK(&rtd->delayed_work, + codec2codec_close_delayed_work); + else + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; pcm->private_data = rtd;
- if (rtd->dai_link->no_pcm) { + if (rtd->dai_link->no_pcm || rtd->dai_link->params) { if (playback) pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; if (capture)
Now that codec to codec links struct snd_soc_pcm_runtime have lasting pcm and substreams, let's use them. Alsa allocate and keep the struct snd_pcm_runtime as long as the link is powered.
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- sound/soc/soc-dapm.c | 86 ++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 38 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index aa6e47beaec3..e0eedff5fe94 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3802,48 +3802,45 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, { struct snd_soc_dapm_path *path; struct snd_soc_dai *source, *sink; - struct snd_soc_pcm_runtime *rtd = w->priv; const struct snd_soc_pcm_stream *config; - struct snd_pcm_substream substream; + struct snd_pcm_substream *substream = w->priv; + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_hw_params params; struct snd_pcm_runtime *runtime = NULL; - int ret = 0; + int ret = 0, saved_stream = substream->stream;
if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) return -EINVAL;
- memset(&substream, 0, sizeof(substream)); - - /* Allocate a dummy snd_pcm_runtime for startup() and other ops() */ - runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); - if (!runtime) { - ret = -ENOMEM; - goto out; - } - substream.runtime = runtime; - substream.private_data = rtd; - switch (event) { case SND_SOC_DAPM_PRE_PMU: - substream.stream = SNDRV_PCM_STREAM_CAPTURE; + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) { + ret = -ENOMEM; + goto out; + } + substream->runtime = runtime; + + substream->stream = SNDRV_PCM_STREAM_CAPTURE; snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv;
- ret = snd_soc_dai_startup(source, &substream); + ret = snd_soc_dai_startup(source, substream); if (ret < 0) { dev_err(source->dev, "ASoC: startup() failed: %d\n", ret); goto out; + } source->active++; }
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + substream->stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv;
- ret = snd_soc_dai_startup(sink, &substream); + ret = snd_soc_dai_startup(sink, substream); if (ret < 0) { dev_err(sink->dev, "ASoC: startup() failed: %d\n", ret); @@ -3852,6 +3849,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, sink->active++; }
+ substream->hw_opened = 1; + /* * Note: getting the config after .startup() gives a chance to * either party on the link to alter the configuration if @@ -3871,26 +3870,26 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, goto out; }
- substream.stream = SNDRV_PCM_STREAM_CAPTURE; + substream->stream = SNDRV_PCM_STREAM_CAPTURE; snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv;
- ret = soc_dai_hw_params(&substream, ¶ms, source); + ret = soc_dai_hw_params(substream, ¶ms, source); if (ret < 0) goto out;
- dapm_update_dai_unlocked(&substream, ¶ms, source); + dapm_update_dai_unlocked(substream, ¶ms, source); }
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + substream->stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv;
- ret = soc_dai_hw_params(&substream, ¶ms, sink); + ret = soc_dai_hw_params(substream, ¶ms, sink); if (ret < 0) goto out;
- dapm_update_dai_unlocked(&substream, ¶ms, sink); + dapm_update_dai_unlocked(substream, ¶ms, sink); }
break; @@ -3920,40 +3919,45 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, ret = 0; }
- substream.stream = SNDRV_PCM_STREAM_CAPTURE; + substream->stream = SNDRV_PCM_STREAM_CAPTURE; snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv; - snd_soc_dai_hw_free(source, &substream); + snd_soc_dai_hw_free(source, substream); }
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + substream->stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv; - snd_soc_dai_hw_free(sink, &substream); + snd_soc_dai_hw_free(sink, substream); }
- substream.stream = SNDRV_PCM_STREAM_CAPTURE; + substream->stream = SNDRV_PCM_STREAM_CAPTURE; snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv; source->active--; - snd_soc_dai_shutdown(source, &substream); + snd_soc_dai_shutdown(source, substream); }
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + substream->stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv; sink->active--; - snd_soc_dai_shutdown(sink, &substream); + snd_soc_dai_shutdown(sink, substream); } break;
+ case SND_SOC_DAPM_POST_PMD: + kfree(substream->runtime); + break; + default: WARN(1, "Unknown event %d\n", event); ret = -EINVAL; }
out: - kfree(runtime); + /* Restore the substream direction */ + substream->stream = saved_stream; return ret; }
@@ -4076,9 +4080,11 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, }
static struct snd_soc_dapm_widget * -snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, +snd_soc_dapm_new_dai(struct snd_soc_card *card, + struct snd_pcm_substream *substream, char *id) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dapm_widget template; struct snd_soc_dapm_widget *w; const char **w_param_text; @@ -4097,7 +4103,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, template.name = link_name; template.event = snd_soc_dai_link_event; template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD; + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; template.kcontrol_news = NULL;
/* allocate memory for control, only in case of multiple configs */ @@ -4132,7 +4138,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd, goto outfree_kcontrol_news; }
- w->priv = rtd; + w->priv = substream;
return w;
@@ -4254,6 +4260,8 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card, struct snd_soc_dai *codec_dai; struct snd_soc_dapm_widget *playback = NULL, *capture = NULL; struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu; + struct snd_pcm_substream *substream; + struct snd_pcm_str *streams = rtd->pcm->streams; int i;
if (rtd->dai_link->params) { @@ -4272,7 +4280,8 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
if (playback_cpu && codec) { if (!playback) { - playback = snd_soc_dapm_new_dai(card, rtd, + substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + playback = snd_soc_dapm_new_dai(card, substream, "playback"); if (IS_ERR(playback)) { dev_err(rtd->dev, @@ -4301,7 +4310,8 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
if (codec && capture_cpu) { if (!capture) { - capture = snd_soc_dapm_new_dai(card, rtd, + substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; + capture = snd_soc_dapm_new_dai(card, substream, "capture"); if (IS_ERR(capture)) { dev_err(rtd->dev,
Set the information provided struct snd_soc_pcm_stream in the struct snd_pcm_runtime of the codec to codec link.
Signed-off-by: Jerome Brunet jbrunet@baylibre.com --- sound/soc/soc-dapm.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e0eedff5fe94..a6bc3eda275d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3892,6 +3892,11 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, dapm_update_dai_unlocked(substream, ¶ms, sink); }
+ runtime->format = params_format(¶ms); + runtime->subformat = params_subformat(¶ms); + runtime->channels = params_channels(¶ms); + runtime->rate = params_rate(¶ms); + break;
case SND_SOC_DAPM_POST_PMU:
participants (2)
-
Jerome Brunet
-
Mark Brown