From 8c3d2aa4cfeaba66be68ef8c351b2e099e50c25b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 24 Jan 2013 09:44:28 +0000 Subject: [PATCH 1/6] ASoC: soc-compress: Add missing brackets around else Signed-off-by: Charles Keepax Acked-by: Vinod Koul Tested-by: Jeeja KP Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 5fbfb06e8083..80040f03b021 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -112,10 +112,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream) snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); - } else + } else { rtd->pop_wait = 1; schedule_delayed_work(&rtd->delayed_work, msecs_to_jiffies(rtd->pmdown_time)); + } } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, From 15e2e6194a3ae13ffeea9b7c368b54b143f31594 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 24 Jan 2013 09:44:29 +0000 Subject: [PATCH 2/6] ASoC: soc-compress: Serialise compressed ops Use the pcm_mutex to serialise the compressed ops. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Tested-by: Jeeja KP Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 80040f03b021..c48db6391ad5 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { @@ -61,12 +63,15 @@ static int soc_compr_open(struct snd_compr_stream *cstream) codec_dai->active++; rtd->codec->active++; + mutex_unlock(&rtd->pcm_mutex); + return 0; machine_err: if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -78,6 +83,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; @@ -124,6 +131,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) SND_SOC_DAPM_STREAM_STOP); } + mutex_unlock(&rtd->pcm_mutex); return 0; } @@ -135,10 +143,12 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) - return ret; + goto out; } if (cmd == SNDRV_PCM_TRIGGER_START) @@ -146,6 +156,8 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) else if (cmd == SNDRV_PCM_TRIGGER_STOP) snd_soc_dai_digital_mute(codec_dai, 1); +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -156,6 +168,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well @@ -165,18 +179,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) - return ret; + goto out; } if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) - return ret; + goto out; } snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_START); +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -187,9 +203,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) ret = platform->driver->compr_ops->get_params(cstream, params); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -200,9 +219,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) ret = platform->driver->compr_ops->get_caps(cstream, caps); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -213,9 +235,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -225,9 +250,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->ack) ret = platform->driver->compr_ops->ack(cstream, bytes); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -237,9 +265,12 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) platform->driver->compr_ops->pointer(cstream, tstamp); + mutex_unlock(&rtd->pcm_mutex); return 0; } From 202c8f7082e87e09f861d06b1a03501047c017b5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 24 Jan 2013 09:44:30 +0000 Subject: [PATCH 3/6] ASoC: soc-compress: Initialise delayed work to power down audio Delayed work was scheduled but not initialised, this patch adds the actual work and initialises it. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Tested-by: Jeeja KP Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index c48db6391ad5..3ea79564a993 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -75,6 +75,34 @@ out: 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 + * due to DAPM power cycling. + */ +static void close_delayed_work(struct work_struct *work) +{ + struct snd_soc_pcm_runtime *rtd = + container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (rtd->pop_wait == 1) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); +} + static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -317,6 +345,9 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return ret; } + /* DAPM dai link stream work */ + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + rtd->compr = compr; compr->private_data = rtd; From 1f88eb0f0660f8b58a1fe9011f3d3a350c7dd194 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 5 Feb 2013 10:41:47 +0000 Subject: [PATCH 4/6] ASoC: soc-compress: Add support for not memory mapped DSPs The ASoC compressed API did not implement the copy callback in its compressed ops which is required for DSPs that are not memory mapped. This patch creates a local copy of the compress ops for each runtime and modifies them with a copy callback as appropriate. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 3ea79564a993..c81aeecec936 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -302,6 +302,22 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, return 0; } +static int soc_compr_copy(struct snd_compr_stream *cstream, + const char __user *buf, size_t count) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + ret = platform->driver->compr_ops->copy(cstream, buf, count); + + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -319,6 +335,7 @@ static struct snd_compr_ops soc_compr_ops = { int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -335,14 +352,25 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return -ENOMEM; } - compr->ops = &soc_compr_ops; + compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), + GFP_KERNEL); + if (compr->ops == NULL) { + dev_err(rtd->card->dev, "Cannot allocate compressed ops\n"); + ret = -ENOMEM; + goto compr_err; + } + 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) + compr->ops->copy = soc_compr_copy; + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); if (ret < 0) { pr_err("compress asoc: can't create compress for codec %s\n", codec->name); - kfree(compr); - return ret; + goto compr_err; } /* DAPM dai link stream work */ @@ -354,4 +382,8 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; + +compr_err: + kfree(compr); + return ret; } From e38b9b7478d57701fbcbaafdde169aa1a88d0eca Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 13:52:42 +0000 Subject: [PATCH 5/6] ASoC: compress: Only mute playback streams Otherwise capture activity on a compressed DAI would mute any playback on the same DAI. Signed-off-by: Mark Brown Acked-by: Vinod Koul Acked-by: Liam Girdwood --- sound/soc/soc-compress.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index c81aeecec936..35726cbf1f02 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -116,13 +116,12 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; + snd_soc_dai_digital_mute(codec_dai, 1); } else { cpu_dai->capture_active--; codec_dai->capture_active--; } - snd_soc_dai_digital_mute(codec_dai, 1); - cpu_dai->active--; codec_dai->active--; codec->active--; @@ -179,10 +178,16 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) goto out; } - if (cmd == SNDRV_PCM_TRIGGER_START) - snd_soc_dai_digital_mute(codec_dai, 0); - else if (cmd == SNDRV_PCM_TRIGGER_STOP) - snd_soc_dai_digital_mute(codec_dai, 1); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_soc_dai_digital_mute(codec_dai, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_soc_dai_digital_mute(codec_dai, 1); + break; + } + } out: mutex_unlock(&rtd->pcm_mutex); From da18396f949ecaa45007d3aeb1b81bd6da092811 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 15:44:07 +0000 Subject: [PATCH 6/6] ASoC: core: Allow digital mute for capture Help avoid noise from the power up of the capture path propagating through into the start of the recording (especially noise caused by the ramp of microphone biases) by keeping the capture muted until after we've finished powering things up with DAPM in the same manner we do for playback. This allows us to take advantage of soft mute support in the hardware more effectively and is more consistent. The core code using the existing digital mute operation is updated to take advantage of this. Some additional cases in the soc-pcm code and suspend will need separate handling but these are less practically relevant than the main runtime stream start/stop case. Rather than refactor the digital mute function in every single driver a new operation is added for drivers taking advantage of this functionality, the old operation should be phased out over time. Signed-off-by: Mark Brown Acked-by Vinod Koul Acked-by: Liam Girdwood --- include/sound/soc-dai.h | 4 +++- sound/soc/soc-compress.c | 19 +++++++++---------- sound/soc/soc-core.c | 12 ++++++++++-- sound/soc/soc-dapm.c | 6 ++++-- sound/soc/soc-pcm.c | 7 +++---- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 3953cea0ecfb..a680f23a04fb 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -126,7 +126,8 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); /* Digital Audio Interface mute */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute); +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction); struct snd_soc_dai_ops { /* @@ -157,6 +158,7 @@ struct snd_soc_dai_ops { * Called by soc-core to minimise any pops. */ int (*digital_mute)(struct snd_soc_dai *dai, int mute); + int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream); /* * ALSA PCM audio operations - all optional. diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 35726cbf1f02..b5b3db71e253 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -116,12 +116,13 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; - snd_soc_dai_digital_mute(codec_dai, 1); } else { cpu_dai->capture_active--; codec_dai->capture_active--; } + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + cpu_dai->active--; codec_dai->active--; codec->active--; @@ -178,15 +179,13 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) goto out; } - if (cstream->direction == SND_COMPRESS_PLAYBACK) { - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - snd_soc_dai_digital_mute(codec_dai, 0); - break; - case SNDRV_PCM_TRIGGER_STOP: - snd_soc_dai_digital_mute(codec_dai, 1); - break; - } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + break; } out: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063b5824..4eac22797893 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3540,12 +3540,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); * snd_soc_dai_digital_mute - configure DAI system or master clock. * @dai: DAI * @mute: mute enable + * @direction: stream to mute * * Mutes the DAI DAC. */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction) { - if (dai->driver && dai->driver->ops->digital_mute) + if (!dai->driver) + return -ENOTSUPP; + + if (dai->driver->ops->mute_stream) + return dai->driver->ops->mute_stream(dai, mute, direction); + else if (direction == SNDRV_PCM_STREAM_PLAYBACK && + dai->driver->ops->digital_mute) return dai->driver->ops->digital_mute(dai, mute); else return -ENOTSUPP; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1e36bc81e5af..4d664f3df805 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3247,14 +3247,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: - ret = snd_soc_dai_digital_mute(sink, 0); + ret = snd_soc_dai_digital_mute(sink, 0, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to unmute: %d\n", ret); ret = 0; break; case SND_SOC_DAPM_PRE_PMD: - ret = snd_soc_dai_digital_mute(sink, 1); + ret = snd_soc_dai_digital_mute(sink, 1, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index cf191e6aebbe..d675b4ae0df6 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -383,8 +383,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) /* Muting the DAC suppresses artifacts caused during digital * shutdown, for example from stopping clocks. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -488,7 +487,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START); - snd_soc_dai_digital_mute(codec_dai, 0); + snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); @@ -586,7 +585,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) /* apply codec digital mute */ if (!codec->active) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); /* free any machine hw params */ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)