From 23607025303af6e84bc2cd4cabe89c21f6a22a3f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 17 Jan 2014 17:03:55 +0000 Subject: [PATCH 1/2] ASoC: DPCM: make some DPCM API calls non static for compressed usage The ASoC compressed code needs to call the internal DPCM APIs in order to dynamically route compressed data to different DAIs. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 22 ++++++++++++++++++++++ sound/soc/soc-pcm.c | 29 ++++++++++++----------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 047d657c331c..2883a7a6f9f3 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -11,6 +11,7 @@ #ifndef __LINUX_SND_SOC_DPCM_H #define __LINUX_SND_SOC_DPCM_H +#include #include #include @@ -135,4 +136,25 @@ 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_runtime_update(struct snd_soc_card *); +int dpcm_path_get(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list_); +int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list, int new); +int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream); +void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); +void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int tream); +int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd); +int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, + int event); + +static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) +{ + kfree(*list); +} + + #endif diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 42782c01e413..64bf3f827aac 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -58,7 +58,7 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); /* DPCM stream event, send event to FE and all active BEs. */ -static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, +int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) { struct snd_soc_dpcm *dpcm; @@ -773,7 +773,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, } /* disconnect a BE and FE */ -static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; @@ -869,7 +869,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, return 0; } -static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, +int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { struct snd_soc_dai *cpu_dai = fe->cpu_dai; @@ -891,11 +891,6 @@ static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, return paths; } -static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) -{ - kfree(*list); -} - static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { @@ -965,7 +960,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, continue; /* don't connect if FE is not running */ - if (!fe->dpcm[stream].runtime) + if (!fe->dpcm[stream].runtime && !fe->fe_compr) continue; /* newly connected FE and BE */ @@ -990,7 +985,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, * Find the corresponding BE DAIs that source or sink audio to this * FE substream. */ -static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, +int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list, int new) { if (new) @@ -999,7 +994,7 @@ static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, return dpcm_prune_paths(fe, stream, list); } -static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1037,7 +1032,7 @@ static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, } } -static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int err, count = 0; @@ -1186,7 +1181,7 @@ be_err: return ret; } -static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1247,7 +1242,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) return 0; } -static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1312,7 +1307,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) return 0; } -static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int ret; @@ -1442,7 +1437,7 @@ static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, return ret; } -static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, +int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd) { struct snd_soc_dpcm *dpcm; @@ -1610,7 +1605,7 @@ out: return ret; } -static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int ret = 0; From 2a99ef0fdb35a0f8d6b56ccc5d9d821e9ff100c1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 17 Jan 2014 17:03:56 +0000 Subject: [PATCH 2/2] ASoC: compress: Add suport for DPCM into compressed audio Currently compressed audio streams are statically routed from the /dev to the DAI link. Some DSPs can route compressed data to multiple BE DAIs like they do for PCM data. Add support to allow dynamically routed compressed streams using the existing DPCM infrastructure. This patch adds special FE versions of the compressed ops that work out the runtime routing. Signed-off-by: Liam Girdwood Acked-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-compress.c | 301 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 301 insertions(+), 1 deletion(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 1f741cb24f33..c0c67003a7b7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1029,6 +1029,7 @@ struct snd_soc_pcm_runtime { /* Dynamic PCM BE runtime data */ struct snd_soc_dpcm_runtime dpcm[2]; + int fe_compr; long pmdown_time; unsigned char pop_wait:1; diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 53c9ecdd119f..5e9690c85d8f 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -24,6 +24,7 @@ #include #include #include +#include static int soc_compr_open(struct snd_compr_stream *cstream) { @@ -75,6 +76,98 @@ out: return ret; } +static int soc_compr_open_fe(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; + struct snd_soc_dai *codec_dai = fe->codec_dai; + struct snd_soc_dpcm *dpcm; + struct snd_soc_dapm_widget_list *list; + int stream; + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + ret = platform->driver->compr_ops->open(cstream); + if (ret < 0) { + pr_err("compress asoc: can't open platform %s\n", platform->name); + goto out; + } + } + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { + ret = fe->dai_link->compr_ops->startup(cstream); + if (ret < 0) { + pr_err("compress asoc: %s startup failed\n", fe->dai_link->name); + goto machine_err; + } + } + + fe->dpcm[stream].runtime = fe_substream->runtime; + + if (dpcm_path_get(fe, stream, &list) <= 0) { + dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", + fe->dai_link->name, stream ? "capture" : "playback"); + } + + /* calculate valid and active FE <-> BE dpcms */ + dpcm_process_paths(fe, stream, &list, 1); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_startup(fe, stream); + if (ret < 0) { + /* clean up all links */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + fe->dpcm[stream].runtime = NULL; + goto fe_err; + } + + dpcm_clear_pending_state(fe, stream); + dpcm_path_put(&list); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + cpu_dai->playback_active++; + codec_dai->playback_active++; + } else { + cpu_dai->capture_active++; + codec_dai->capture_active++; + } + + cpu_dai->active++; + codec_dai->active++; + fe->codec->active++; + + mutex_unlock(&fe->card->mutex); + + return 0; + +fe_err: + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) + fe->dai_link->compr_ops->shutdown(cstream); +machine_err: + if (platform->driver->compr_ops && platform->driver->compr_ops->free) + platform->driver->compr_ops->free(cstream); +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + /* * Power down the audio subsystem pmdown_time msecs after close is called. * This is to ensure there are no pops or clicks in between any music tracks @@ -164,6 +257,65 @@ static int soc_compr_free(struct snd_compr_stream *cstream) return 0; } +static int soc_compr_free_fe(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; + struct snd_soc_dai *codec_dai = fe->codec_dai; + struct snd_soc_dpcm *dpcm; + int stream, ret; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + stream = SNDRV_PCM_STREAM_PLAYBACK; + cpu_dai->playback_active--; + codec_dai->playback_active--; + } else { + stream = SNDRV_PCM_STREAM_CAPTURE; + cpu_dai->capture_active--; + codec_dai->capture_active--; + } + + cpu_dai->active--; + codec_dai->active--; + fe->codec->active--; + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_free(fe, stream); + if (ret < 0) + dev_err(fe->dev, "compressed hw_free failed %d\n", ret); + + ret = dpcm_be_dai_shutdown(fe, stream); + + /* mark FE's links ready to prune */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + else + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + dpcm_be_disconnect(fe, stream); + + fe->dpcm[stream].runtime = NULL; + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) + fe->dai_link->compr_ops->shutdown(cstream); + + if (platform->driver->compr_ops && platform->driver->compr_ops->free) + platform->driver->compr_ops->free(cstream); + + mutex_unlock(&fe->card->mutex); + return 0; +} + static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) { @@ -194,6 +346,59 @@ out: return ret; } +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_soc_platform *platform = fe->platform; + int ret = 0, stream; + + if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || + cmd == SND_COMPR_TRIGGER_DRAIN) { + + if (platform->driver->compr_ops && + platform->driver->compr_ops->trigger) + return platform->driver->compr_ops->trigger(cstream, cmd); + } + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + ret = platform->driver->compr_ops->trigger(cstream, cmd); + if (ret < 0) + goto out; + } + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_trigger(fe, stream, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; + break; + } + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { @@ -241,6 +446,64 @@ err: return ret; } +static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_soc_platform *platform = fe->platform; + int ret = 0, stream; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + ret = platform->driver->compr_ops->set_params(cstream, params); + if (ret < 0) + goto out; + } + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { + ret = fe->dai_link->compr_ops->set_params(cstream); + if (ret < 0) + goto out; + } + + /* + * Create an empty hw_params for the BE as the machine driver must + * fix this up to match DSP decoder and ASRC configuration. + * I.e. machine driver fixup for compressed BE is mandatory. + */ + memset(&fe->dpcm[fe_substream->stream].hw_params, 0, + sizeof(struct snd_pcm_hw_params)); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_params(fe, stream); + if (ret < 0) + goto out; + + ret = dpcm_be_dai_prepare(fe, stream); + if (ret < 0) + goto out; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + else + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_codec *params) { @@ -360,6 +623,7 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -375,6 +639,21 @@ static struct snd_compr_ops soc_compr_ops = { .get_codec_caps = soc_compr_get_codec_caps }; +/* ASoC Dynamic Compress operations */ +static struct snd_compr_ops soc_compr_dyn_ops = { + .open = soc_compr_open_fe, + .free = soc_compr_free_fe, + .set_params = soc_compr_set_params_fe, + .get_params = soc_compr_get_params, + .set_metadata = soc_compr_set_metadata, + .get_metadata = soc_compr_get_metadata, + .trigger = soc_compr_trigger_fe, + .pointer = soc_compr_pointer, + .ack = soc_compr_ack, + .get_caps = soc_compr_get_caps, + .get_codec_caps = soc_compr_get_codec_caps +}; + /* create a new compress */ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { @@ -383,6 +662,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; + struct snd_pcm *be_pcm; char new_name[64]; int ret = 0, direction = 0; @@ -410,7 +690,26 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) ret = -ENOMEM; goto compr_err; } - memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + if (rtd->dai_link->dynamic) { + snprintf(new_name, sizeof(new_name), "(%s)", + rtd->dai_link->stream_name); + + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + 1, 0, &be_pcm); + if (ret < 0) { + dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", + rtd->dai_link->name); + goto compr_err; + } + + rtd->pcm = be_pcm; + rtd->fe_compr = 1; + be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; + memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); + } else + memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); /* Add copy callback for not memory mapped DSPs */ if (platform->driver->compr_ops && platform->driver->compr_ops->copy)