mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
ASoC: dpcm: improve runtime update predictability
As it is, dpcm_runtime_update() performs the old path and new path update of a frontend before going on to the next frontend DAI. Depending the order of the FEs within the rtd list, the result of the update might be different. For example: * Frontend A connected to backend C, with a 48kHz playback * Frontend B connected to backend D, with a 44.1kHz playback * FE A appears before FE B in the rtd list of the card. If we reparent BE C to FE B (disconnecting BE D): * old path update of FE A will run first, and BE C will get hw_free() and shutdown() * new path update of FE B will run after and BE C, which is stopped, so it will be configured at 44.1kHz, as expected If we reparent BE D to FE A (disconnecting BE C): * new path update of FE A will run first but since BE D is still running at 44.1kHz, it won't be reconfigured (no call to startup() or hw_params()) * old path update of FE B runs after, nothing happens * In this case, we end up with a BE playing at 44.1kHz a stream which is supposed to be played at 48Khz (too slow) To improve this situation, this patch performs all the FE old paths update before going on to update the new paths. With this, the result should no longer depend on the order of the FE within the card rtd list. Please note that there might be a small performance penalty since dpcm_process_paths() is called twice per stream direction. Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
f516d32262
commit
de15d7ff5b
@ -2597,105 +2597,112 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
|
||||
{
|
||||
struct snd_soc_dapm_widget_list *list;
|
||||
int count, paths;
|
||||
|
||||
if (!fe->dai_link->dynamic)
|
||||
return 0;
|
||||
|
||||
/* only check active links */
|
||||
if (!fe->cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
/* DAPM sync will call this to update DSP paths */
|
||||
dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n",
|
||||
new ? "new" : "old", fe->dai_link->name);
|
||||
|
||||
/* skip if FE doesn't have playback capability */
|
||||
if (!fe->cpu_dai->driver->playback.channels_min ||
|
||||
!fe->codec_dai->driver->playback.channels_min)
|
||||
goto capture;
|
||||
|
||||
/* skip if FE isn't currently playing */
|
||||
if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active)
|
||||
goto capture;
|
||||
|
||||
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
|
||||
if (paths < 0) {
|
||||
dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
|
||||
fe->dai_link->name, "playback");
|
||||
return paths;
|
||||
}
|
||||
|
||||
/* update any playback paths */
|
||||
count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new);
|
||||
if (count) {
|
||||
if (new)
|
||||
dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
else
|
||||
dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
|
||||
dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
}
|
||||
|
||||
dpcm_path_put(&list);
|
||||
|
||||
capture:
|
||||
/* skip if FE doesn't have capture capability */
|
||||
if (!fe->cpu_dai->driver->capture.channels_min ||
|
||||
!fe->codec_dai->driver->capture.channels_min)
|
||||
return 0;
|
||||
|
||||
/* skip if FE isn't currently capturing */
|
||||
if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active)
|
||||
return 0;
|
||||
|
||||
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
|
||||
if (paths < 0) {
|
||||
dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
|
||||
fe->dai_link->name, "capture");
|
||||
return paths;
|
||||
}
|
||||
|
||||
/* update any old capture paths */
|
||||
count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new);
|
||||
if (count) {
|
||||
if (new)
|
||||
dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
else
|
||||
dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
|
||||
dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
}
|
||||
|
||||
dpcm_path_put(&list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
|
||||
* any DAI links.
|
||||
*/
|
||||
int soc_dpcm_runtime_update(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *fe;
|
||||
int old, new, paths;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
|
||||
/* shutdown all old paths first */
|
||||
list_for_each_entry(fe, &card->rtd_list, list) {
|
||||
struct snd_soc_dapm_widget_list *list;
|
||||
|
||||
/* make sure link is FE */
|
||||
if (!fe->dai_link->dynamic)
|
||||
continue;
|
||||
|
||||
/* only check active links */
|
||||
if (!fe->cpu_dai->active)
|
||||
continue;
|
||||
|
||||
/* DAPM sync will call this to update DSP paths */
|
||||
dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n",
|
||||
fe->dai_link->name);
|
||||
|
||||
/* skip if FE doesn't have playback capability */
|
||||
if (!fe->cpu_dai->driver->playback.channels_min
|
||||
|| !fe->codec_dai->driver->playback.channels_min)
|
||||
goto capture;
|
||||
|
||||
/* skip if FE isn't currently playing */
|
||||
if (!fe->cpu_dai->playback_active
|
||||
|| !fe->codec_dai->playback_active)
|
||||
goto capture;
|
||||
|
||||
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
|
||||
if (paths < 0) {
|
||||
dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
|
||||
fe->dai_link->name, "playback");
|
||||
mutex_unlock(&card->mutex);
|
||||
return paths;
|
||||
}
|
||||
|
||||
/* update any new playback paths */
|
||||
new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
|
||||
if (new) {
|
||||
dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
}
|
||||
|
||||
/* update any old playback paths */
|
||||
old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
|
||||
if (old) {
|
||||
dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
}
|
||||
|
||||
dpcm_path_put(&list);
|
||||
capture:
|
||||
/* skip if FE doesn't have capture capability */
|
||||
if (!fe->cpu_dai->driver->capture.channels_min
|
||||
|| !fe->codec_dai->driver->capture.channels_min)
|
||||
continue;
|
||||
|
||||
/* skip if FE isn't currently capturing */
|
||||
if (!fe->cpu_dai->capture_active
|
||||
|| !fe->codec_dai->capture_active)
|
||||
continue;
|
||||
|
||||
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
|
||||
if (paths < 0) {
|
||||
dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
|
||||
fe->dai_link->name, "capture");
|
||||
mutex_unlock(&card->mutex);
|
||||
return paths;
|
||||
}
|
||||
|
||||
/* update any new capture paths */
|
||||
new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
|
||||
if (new) {
|
||||
dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
}
|
||||
|
||||
/* update any old capture paths */
|
||||
old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
|
||||
if (old) {
|
||||
dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
|
||||
}
|
||||
|
||||
dpcm_path_put(&list);
|
||||
ret = soc_dpcm_fe_runtime_update(fe, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* bring new paths up */
|
||||
list_for_each_entry(fe, &card->rtd_list, list) {
|
||||
ret = soc_dpcm_fe_runtime_update(fe, 1);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&card->mutex);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user