Commit 618dae11 authored by Liam Girdwood's avatar Liam Girdwood Committed by Mark Brown

ASoC: dpcm: Add runtime dynamic route update

This patch allows DPCM to dynamically alter the FE to BE PCM links
at runtime based on mixer setting updates. DAPM is looked up after
every mixer update and we perform a DPCM runtime update if the
mixer has a change of value.

This patchs adds/changes the following :-

 o Adds DPCM runtime update core.
 o Changes soc_dapm_mixer_update_power() and soc_dapm_mux_update_power()
   to return if a change has occured rather than 0. No other users check
   atm.
Signed-off-by: default avatarLiam Girdwood <lrg@ti.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent f86dcef8
...@@ -132,5 +132,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, ...@@ -132,5 +132,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
/* internal use only */ /* internal use only */
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *);
#endif #endif
...@@ -1846,7 +1846,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, ...@@ -1846,7 +1846,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
} }
return 0; return found;
} }
int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
...@@ -1858,6 +1858,8 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, ...@@ -1858,6 +1858,8 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
if (ret > 0)
soc_dpcm_runtime_update(widget);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
...@@ -1890,7 +1892,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, ...@@ -1890,7 +1892,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
} }
return 0; return found;
} }
int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
...@@ -1902,6 +1904,8 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, ...@@ -1902,6 +1904,8 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
mutex_unlock(&card->dapm_mutex); mutex_unlock(&card->dapm_mutex);
if (ret > 0)
soc_dpcm_runtime_update(widget);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
......
...@@ -1608,7 +1608,228 @@ int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) ...@@ -1608,7 +1608,228 @@ int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
return ret; return ret;
} }
static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
{
int err;
dev_dbg(fe->dev, "runtime %s close on FE %s\n",
stream ? "capture" : "playback", fe->dai_link->name);
err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
if (err < 0)
dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err);
err = dpcm_be_dai_hw_free(fe, stream);
if (err < 0)
dev_err(fe->dev,"dpcm: hw_free FE failed %d\n", err);
err = dpcm_be_dai_shutdown(fe, stream);
if (err < 0)
dev_err(fe->dev,"dpcm: shutdown FE failed %d\n", err);
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
return 0;
}
static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
int ret;
dev_dbg(fe->dev, "runtime %s open on FE %s\n",
stream ? "capture" : "playback", fe->dai_link->name);
/* Only start the BE if the FE is ready */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
return -EINVAL;
/* startup must always be called for new BEs */
ret = dpcm_be_dai_startup(fe, stream);
if (ret < 0) {
goto disconnect;
return ret;
}
/* keep going if FE state is > open */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
return 0;
ret = dpcm_be_dai_hw_params(fe, stream);
if (ret < 0) {
goto close;
return ret;
}
/* keep going if FE state is > hw_params */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
return 0;
ret = dpcm_be_dai_prepare(fe, stream);
if (ret < 0) {
goto hw_free;
return ret;
}
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
/* keep going if FE state is > prepare */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
return 0;
dev_dbg(fe->dev, "dpcm: trigger FE %s cmd start\n",
fe->dai_link->name);
ret = dpcm_be_dai_trigger(fe, stream,
SNDRV_PCM_TRIGGER_START);
if (ret < 0) {
dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
goto hw_free;
}
return 0;
hw_free:
dpcm_be_dai_hw_free(fe, stream);
close:
dpcm_be_dai_shutdown(fe, stream);
disconnect:
/* disconnect any non started BEs */
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
struct snd_soc_pcm_runtime *be = dpcm->be;
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
}
return ret;
}
static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
ret = dpcm_run_update_startup(fe, stream);
if (ret < 0)
dev_err(fe->dev, "failed to startup some BEs\n");
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
return ret;
}
static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
ret = dpcm_run_update_shutdown(fe, stream);
if (ret < 0)
dev_err(fe->dev, "failed to shutdown some BEs\n");
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
return ret;
}
/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
* any DAI links.
*/
int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
{
struct snd_soc_card *card;
int i, old, new, paths;
if (widget->codec)
card = widget->codec->card;
else if (widget->platform)
card = widget->platform->card;
else
return -EINVAL;
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dapm_widget_list *list;
struct snd_soc_pcm_runtime *fe = &card->rtd[i];
/* 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, "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)
goto capture;
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
if (paths < 0) {
dev_warn(fe->dev, "%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);
}
capture:
/* skip if FE doesn't have capture capability */
if (!fe->cpu_dai->driver->capture.channels_min)
continue;
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
if (paths < 0) {
dev_warn(fe->dev, "%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);
}
mutex_unlock(&card->mutex);
return 0;
}
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
{ {
struct snd_soc_dpcm *dpcm; struct snd_soc_dpcm *dpcm;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment