Merge remote-tracking branches 'asoc/fix/adsp', 'asoc/fix/arizona', 'asoc/fix/atmel...
[linux-drm-fsl-dcu.git] / sound / soc / soc-pcm.c
index 591f0f3074c58b75eeb5b99a939bc9bf46ffae52..891b9a9bcbf885df92bad9007b3f23054ce68c18 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
@@ -147,12 +148,12 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
        }
 }
 
-static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw,
+static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
        struct snd_soc_pcm_stream *codec_stream,
        struct snd_soc_pcm_stream *cpu_stream)
 {
-       hw->rate_min = max(codec_stream->rate_min, cpu_stream->rate_min);
-       hw->rate_max = max(codec_stream->rate_max, cpu_stream->rate_max);
+       struct snd_pcm_hardware *hw = &runtime->hw;
+
        hw->channels_min = max(codec_stream->channels_min,
                cpu_stream->channels_min);
        hw->channels_max = min(codec_stream->channels_max,
@@ -165,6 +166,13 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw,
        if (cpu_stream->rates
                & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
                hw->rates |= codec_stream->rates;
+
+       snd_pcm_limit_hw_rates(runtime);
+
+       hw->rate_min = max(hw->rate_min, cpu_stream->rate_min);
+       hw->rate_min = max(hw->rate_min, codec_stream->rate_min);
+       hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
+       hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max);
 }
 
 /*
@@ -183,6 +191,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
        int ret = 0;
 
+       pinctrl_pm_select_default_state(cpu_dai->dev);
+       pinctrl_pm_select_default_state(codec_dai->dev);
        pm_runtime_get_sync(cpu_dai->dev);
        pm_runtime_get_sync(codec_dai->dev);
        pm_runtime_get_sync(platform->dev);
@@ -232,15 +242,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 
        /* Check that the codec and cpu DAIs are compatible */
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback,
+               soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback,
                        &cpu_dai_drv->playback);
        } else {
-               soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture,
+               soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture,
                        &cpu_dai_drv->capture);
        }
 
        ret = -EINVAL;
-       snd_pcm_limit_hw_rates(runtime);
        if (!runtime->hw.rates) {
                printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
                        codec_dai->name, cpu_dai->name);
@@ -317,6 +326,10 @@ out:
        pm_runtime_put(platform->dev);
        pm_runtime_put(codec_dai->dev);
        pm_runtime_put(cpu_dai->dev);
+       if (!codec_dai->active)
+               pinctrl_pm_select_sleep_state(codec_dai->dev);
+       if (!cpu_dai->active)
+               pinctrl_pm_select_sleep_state(cpu_dai->dev);
 
        return ret;
 }
@@ -426,6 +439,10 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
        pm_runtime_put(platform->dev);
        pm_runtime_put(codec_dai->dev);
        pm_runtime_put(cpu_dai->dev);
+       if (!codec_dai->active)
+               pinctrl_pm_select_sleep_state(codec_dai->dev);
+       if (!cpu_dai->active)
+               pinctrl_pm_select_sleep_state(cpu_dai->dev);
 
        return 0;
 }
@@ -583,12 +600,13 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = rtd->codec;
+       bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
        /* apply codec digital mute */
-       if (!codec->active)
+       if ((playback && codec_dai->playback_active == 1) ||
+           (!playback && codec_dai->capture_active == 1))
                snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
 
        /* free any machine hw params */