Merge tag 'gpio-v4.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux...
[cascardo/linux.git] / sound / soc / soc-pcm.c
index c86dc96..e898b42 100644 (file)
@@ -599,10 +599,15 @@ platform_err:
 out:
        mutex_unlock(&rtd->pcm_mutex);
 
-       pm_runtime_put(platform->dev);
-       for (i = 0; i < rtd->num_codecs; i++)
-               pm_runtime_put(rtd->codec_dais[i]->dev);
-       pm_runtime_put(cpu_dai->dev);
+       pm_runtime_mark_last_busy(platform->dev);
+       pm_runtime_put_autosuspend(platform->dev);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               pm_runtime_mark_last_busy(rtd->codec_dais[i]->dev);
+               pm_runtime_put_autosuspend(rtd->codec_dais[i]->dev);
+       }
+
+       pm_runtime_mark_last_busy(cpu_dai->dev);
+       pm_runtime_put_autosuspend(cpu_dai->dev);
        for (i = 0; i < rtd->num_codecs; i++) {
                if (!rtd->codec_dais[i]->active)
                        pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
@@ -706,10 +711,17 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 
        mutex_unlock(&rtd->pcm_mutex);
 
-       pm_runtime_put(platform->dev);
-       for (i = 0; i < rtd->num_codecs; i++)
-               pm_runtime_put(rtd->codec_dais[i]->dev);
-       pm_runtime_put(cpu_dai->dev);
+       pm_runtime_mark_last_busy(platform->dev);
+       pm_runtime_put_autosuspend(platform->dev);
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               pm_runtime_mark_last_busy(rtd->codec_dais[i]->dev);
+               pm_runtime_put_autosuspend(rtd->codec_dais[i]->dev);
+       }
+
+       pm_runtime_mark_last_busy(cpu_dai->dev);
+       pm_runtime_put_autosuspend(cpu_dai->dev);
+
        for (i = 0; i < rtd->num_codecs; i++) {
                if (!rtd->codec_dais[i]->active)
                        pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
@@ -1213,11 +1225,10 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                struct snd_soc_dapm_widget *widget, int stream)
 {
        struct snd_soc_pcm_runtime *be;
-       int i, j;
+       int i;
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               for (i = 0; i < card->num_links; i++) {
-                       be = &card->rtd[i];
+               list_for_each_entry(be, &card->rtd_list, list) {
 
                        if (!be->dai_link->no_pcm)
                                continue;
@@ -1225,16 +1236,15 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                        if (be->cpu_dai->playback_widget == widget)
                                return be;
 
-                       for (j = 0; j < be->num_codecs; j++) {
-                               struct snd_soc_dai *dai = be->codec_dais[j];
+                       for (i = 0; i < be->num_codecs; i++) {
+                               struct snd_soc_dai *dai = be->codec_dais[i];
                                if (dai->playback_widget == widget)
                                        return be;
                        }
                }
        } else {
 
-               for (i = 0; i < card->num_links; i++) {
-                       be = &card->rtd[i];
+               list_for_each_entry(be, &card->rtd_list, list) {
 
                        if (!be->dai_link->no_pcm)
                                continue;
@@ -1242,8 +1252,8 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                        if (be->cpu_dai->capture_widget == widget)
                                return be;
 
-                       for (j = 0; j < be->num_codecs; j++) {
-                               struct snd_soc_dai *dai = be->codec_dais[j];
+                       for (i = 0; i < be->num_codecs; i++) {
+                               struct snd_soc_dai *dai = be->codec_dais[i];
                                if (dai->capture_widget == widget)
                                        return be;
                        }
@@ -1616,6 +1626,56 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
        snd_pcm_stream_unlock_irq(substream);
 }
 
+static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
+                              int stream)
+{
+       struct snd_soc_dpcm *dpcm;
+       struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+       struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai;
+       int err;
+
+       /* apply symmetry for FE */
+       if (soc_pcm_has_symmetry(fe_substream))
+               fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+       /* Symmetry only applies if we've got an active stream. */
+       if (fe_cpu_dai->active) {
+               err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
+               if (err < 0)
+                       return err;
+       }
+
+       /* apply symmetry for BE */
+       list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+               struct snd_pcm_substream *be_substream =
+                       snd_soc_dpcm_get_substream(be, stream);
+               struct snd_soc_pcm_runtime *rtd = be_substream->private_data;
+               int i;
+
+               if (soc_pcm_has_symmetry(be_substream))
+                       be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+               /* Symmetry only applies if we've got an active stream. */
+               if (rtd->cpu_dai->active) {
+                       err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai);
+                       if (err < 0)
+                               return err;
+               }
+
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       if (rtd->codec_dais[i]->active) {
+                               err = soc_pcm_apply_symmetry(be_substream,
+                                                            rtd->codec_dais[i]);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
 {
        struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
@@ -1644,6 +1704,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
        dpcm_set_fe_runtime(fe_substream);
        snd_pcm_limit_hw_rates(runtime);
 
+       ret = dpcm_apply_symmetry(fe_substream, stream);
+       if (ret < 0) {
+               dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n",
+                       ret);
+               goto unwind;
+       }
+
        dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        return 0;
 
@@ -2115,7 +2182,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
                        continue;
 
                if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
-                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
                        continue;
 
                dev_dbg(be->dev, "ASoC: prepare BE %s\n",
@@ -2343,12 +2411,12 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
  */
 int soc_dpcm_runtime_update(struct snd_soc_card *card)
 {
-       int i, old, new, paths;
+       struct snd_soc_pcm_runtime *fe;
+       int old, new, paths;
 
        mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-       for (i = 0; i < card->num_rtd; i++) {
+       list_for_each_entry(fe, &card->rtd_list, list) {
                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)