Merge branch 'for-5.6' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-2.6-microblaze.git] / sound / soc / soc-pcm.c
index 6d400bf..4c27c77 100644 (file)
 
 #define DPCM_MAX_BE_USERS      8
 
+#ifdef CONFIG_DEBUG_FS
+static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
+{
+       switch (state) {
+       case SND_SOC_DPCM_STATE_NEW:
+               return "new";
+       case SND_SOC_DPCM_STATE_OPEN:
+               return "open";
+       case SND_SOC_DPCM_STATE_HW_PARAMS:
+               return "hw_params";
+       case SND_SOC_DPCM_STATE_PREPARE:
+               return "prepare";
+       case SND_SOC_DPCM_STATE_START:
+               return "start";
+       case SND_SOC_DPCM_STATE_STOP:
+               return "stop";
+       case SND_SOC_DPCM_STATE_SUSPEND:
+               return "suspend";
+       case SND_SOC_DPCM_STATE_PAUSED:
+               return "paused";
+       case SND_SOC_DPCM_STATE_HW_FREE:
+               return "hw_free";
+       case SND_SOC_DPCM_STATE_CLOSE:
+               return "close";
+       }
+
+       return "unknown";
+}
+
+static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
+                              int stream, char *buf, size_t size)
+{
+       struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
+       struct snd_soc_dpcm *dpcm;
+       ssize_t offset = 0;
+       unsigned long flags;
+
+       /* FE state */
+       offset += snprintf(buf + offset, size - offset,
+                          "[%s - %s]\n", fe->dai_link->name,
+                          stream ? "Capture" : "Playback");
+
+       offset += snprintf(buf + offset, size - offset, "State: %s\n",
+                          dpcm_state_string(fe->dpcm[stream].state));
+
+       if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
+           (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
+               offset += snprintf(buf + offset, size - offset,
+                                  "Hardware Params: "
+                                  "Format = %s, Channels = %d, Rate = %d\n",
+                                  snd_pcm_format_name(params_format(params)),
+                                  params_channels(params),
+                                  params_rate(params));
+
+       /* BEs state */
+       offset += snprintf(buf + offset, size - offset, "Backends:\n");
+
+       if (list_empty(&fe->dpcm[stream].be_clients)) {
+               offset += snprintf(buf + offset, size - offset,
+                                  " No active DSP links\n");
+               goto out;
+       }
+
+       spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+       for_each_dpcm_be(fe, stream, dpcm) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+               params = &dpcm->hw_params;
+
+               offset += snprintf(buf + offset, size - offset,
+                                  "- %s\n", be->dai_link->name);
+
+               offset += snprintf(buf + offset, size - offset,
+                                  "   State: %s\n",
+                                  dpcm_state_string(be->dpcm[stream].state));
+
+               if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
+                   (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
+                       offset += snprintf(buf + offset, size - offset,
+                                          "   Hardware Params: "
+                                          "Format = %s, Channels = %d, Rate = %d\n",
+                                          snd_pcm_format_name(params_format(params)),
+                                          params_channels(params),
+                                          params_rate(params));
+       }
+       spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+out:
+       return offset;
+}
+
+static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct snd_soc_pcm_runtime *fe = file->private_data;
+       ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
+       int stream;
+       char *buf;
+
+       buf = kmalloc(out_count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       for_each_pcm_streams(stream)
+               if (snd_soc_dai_stream_valid(fe->cpu_dai, stream))
+                       offset += dpcm_show_state(fe, stream,
+                                                 buf + offset,
+                                                 out_count - offset);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations dpcm_state_fops = {
+       .open = simple_open,
+       .read = dpcm_state_read_file,
+       .llseek = default_llseek,
+};
+
+void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
+{
+       if (!rtd->dai_link)
+               return;
+
+       if (!rtd->dai_link->dynamic)
+               return;
+
+       if (!rtd->card->debugfs_card_root)
+               return;
+
+       rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
+                                                   rtd->card->debugfs_card_root);
+
+       debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
+                           rtd, &dpcm_state_fops);
+}
+
+static void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, int stream)
+{
+       char *name;
+
+       name = kasprintf(GFP_KERNEL, "%s:%s", dpcm->be->dai_link->name,
+                        stream ? "capture" : "playback");
+       if (name) {
+               dpcm->debugfs_state = debugfs_create_dir(
+                       name, dpcm->fe->debugfs_dpcm_root);
+               debugfs_create_u32("state", 0644, dpcm->debugfs_state,
+                                  &dpcm->state);
+               kfree(name);
+       }
+}
+
+static void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm)
+{
+       debugfs_remove_recursive(dpcm->debugfs_state);
+}
+
+#else
+static inline void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm,
+                                            int stream)
+{
+}
+
+static inline void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm)
+{
+}
+#endif
+
 static int soc_rtd_startup(struct snd_soc_pcm_runtime *rtd,
                           struct snd_pcm_substream *substream)
 {
@@ -82,6 +250,27 @@ static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
+static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
+                                  int stream, int action)
+{
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai;
+       int i;
+
+       lockdep_assert_held(&rtd->card->pcm_mutex);
+
+       cpu_dai->stream_active[stream] += action;
+       for_each_rtd_codec_dai(rtd, i, codec_dai)
+               codec_dai->stream_active[stream] += action;
+
+       cpu_dai->active += action;
+       cpu_dai->component->active += action;
+       for_each_rtd_codec_dai(rtd, i, codec_dai) {
+               codec_dai->active += action;
+               codec_dai->component->active += action;
+       }
+}
+
 /**
  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
  * @rtd: ASoC PCM runtime that is activated
@@ -94,28 +283,7 @@ static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd,
  */
 void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
 {
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai;
-       int i;
-
-       lockdep_assert_held(&rtd->card->pcm_mutex);
-
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               cpu_dai->playback_active++;
-               for_each_rtd_codec_dai(rtd, i, codec_dai)
-                       codec_dai->playback_active++;
-       } else {
-               cpu_dai->capture_active++;
-               for_each_rtd_codec_dai(rtd, i, codec_dai)
-                       codec_dai->capture_active++;
-       }
-
-       cpu_dai->active++;
-       cpu_dai->component->active++;
-       for_each_rtd_codec_dai(rtd, i, codec_dai) {
-               codec_dai->active++;
-               codec_dai->component->active++;
-       }
+       snd_soc_runtime_action(rtd, stream, 1);
 }
 
 /**
@@ -130,28 +298,7 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
  */
 void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
 {
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai;
-       int i;
-
-       lockdep_assert_held(&rtd->card->pcm_mutex);
-
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               cpu_dai->playback_active--;
-               for_each_rtd_codec_dai(rtd, i, codec_dai)
-                       codec_dai->playback_active--;
-       } else {
-               cpu_dai->capture_active--;
-               for_each_rtd_codec_dai(rtd, i, codec_dai)
-                       codec_dai->capture_active--;
-       }
-
-       cpu_dai->active--;
-       cpu_dai->component->active--;
-       for_each_rtd_codec_dai(rtd, i, codec_dai) {
-               codec_dai->component->active--;
-               codec_dai->active--;
-       }
+       snd_soc_runtime_action(rtd, stream, -1);
 }
 
 /**
@@ -375,29 +522,24 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
+       struct snd_soc_pcm_stream *pcm_codec, *pcm_cpu;
+       int stream = substream->stream;
        int i;
        unsigned int bits = 0, cpu_bits;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               for_each_rtd_codec_dai(rtd, i, codec_dai) {
-                       if (codec_dai->driver->playback.sig_bits == 0) {
-                               bits = 0;
-                               break;
-                       }
-                       bits = max(codec_dai->driver->playback.sig_bits, bits);
-               }
-               cpu_bits = cpu_dai->driver->playback.sig_bits;
-       } else {
-               for_each_rtd_codec_dai(rtd, i, codec_dai) {
-                       if (codec_dai->driver->capture.sig_bits == 0) {
-                               bits = 0;
-                               break;
-                       }
-                       bits = max(codec_dai->driver->capture.sig_bits, bits);
+       for_each_rtd_codec_dai(rtd, i, codec_dai) {
+               pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream);
+
+               if (pcm_codec->sig_bits == 0) {
+                       bits = 0;
+                       break;
                }
-               cpu_bits = cpu_dai->driver->capture.sig_bits;
+               bits = max(pcm_codec->sig_bits, bits);
        }
 
+       pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream);
+       cpu_bits = pcm_cpu->sig_bits;
+
        soc_pcm_set_msb(substream, bits);
        soc_pcm_set_msb(substream, cpu_bits);
 }
@@ -408,20 +550,16 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
        struct snd_pcm_hardware *hw = &runtime->hw;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai;
-       struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver;
-       struct snd_soc_dai_driver *codec_dai_drv;
        struct snd_soc_pcm_stream *codec_stream;
        struct snd_soc_pcm_stream *cpu_stream;
        unsigned int chan_min = 0, chan_max = UINT_MAX;
        unsigned int rate_min = 0, rate_max = UINT_MAX;
        unsigned int rates = UINT_MAX;
        u64 formats = ULLONG_MAX;
+       int stream = substream->stream;
        int i;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               cpu_stream = &cpu_dai_drv->playback;
-       else
-               cpu_stream = &cpu_dai_drv->capture;
+       cpu_stream = snd_soc_dai_get_pcm_stream(rtd->cpu_dai, stream);
 
        /* first calculate min/max only for CODECs in the DAI link */
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
@@ -439,11 +577,8 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
                                              substream->stream))
                        continue;
 
-               codec_dai_drv = codec_dai->driver;
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       codec_stream = &codec_dai_drv->playback;
-               else
-                       codec_stream = &codec_dai_drv->capture;
+               codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream);
+
                chan_min = max(chan_min, codec_stream->channels_min);
                chan_max = min(chan_max, codec_stream->channels_max);
                rate_min = max(rate_min, codec_stream->rate_min);
@@ -478,54 +613,109 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
        hw->rate_max = min_not_zero(hw->rate_max, rate_max);
 }
 
-static int soc_pcm_components_open(struct snd_pcm_substream *substream,
-                                  struct snd_soc_component **last)
+static int soc_pcm_components_open(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_component *last = NULL;
        struct snd_soc_component *component;
        int i, ret = 0;
 
        for_each_rtd_components(rtd, i, component) {
-               *last = component;
+               last = component;
 
                ret = snd_soc_component_module_get_when_open(component);
                if (ret < 0) {
                        dev_err(component->dev,
                                "ASoC: can't get module %s\n",
                                component->name);
-                       return ret;
+                       break;
                }
 
                ret = snd_soc_component_open(component, substream);
                if (ret < 0) {
+                       snd_soc_component_module_put_when_close(component);
                        dev_err(component->dev,
                                "ASoC: can't open component %s: %d\n",
                                component->name, ret);
-                       return ret;
+                       break;
                }
        }
-       *last = NULL;
-       return 0;
+
+       if (ret < 0) {
+               /* rollback on error */
+               for_each_rtd_components(rtd, i, component) {
+                       if (component == last)
+                               break;
+
+                       snd_soc_component_close(component, substream);
+                       snd_soc_component_module_put_when_close(component);
+               }
+       }
+
+       return ret;
 }
 
-static int soc_pcm_components_close(struct snd_pcm_substream *substream,
-                                   struct snd_soc_component *last)
+static int soc_pcm_components_close(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_component *component;
-       int i, ret = 0;
+       int i, r, ret = 0;
 
        for_each_rtd_components(rtd, i, component) {
-               if (component == last)
-                       break;
+               r = snd_soc_component_close(component, substream);
+               if (r < 0)
+                       ret = r; /* use last ret */
 
-               ret |= snd_soc_component_close(component, substream);
                snd_soc_component_module_put_when_close(component);
        }
 
        return ret;
 }
 
+/*
+ * Called by ALSA when a PCM substream is closed. Private data can be
+ * freed here. The cpu DAI, codec DAI, machine and components are also
+ * shutdown.
+ */
+static int soc_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_component *component;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai;
+       int i;
+
+       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+
+       snd_soc_runtime_deactivate(rtd, substream->stream);
+
+       snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
+       snd_soc_dai_shutdown(cpu_dai, substream);
+
+       for_each_rtd_codec_dai(rtd, i, codec_dai)
+               snd_soc_dai_shutdown(codec_dai, substream);
+
+       soc_rtd_shutdown(rtd, substream);
+
+       soc_pcm_components_close(substream);
+
+       snd_soc_dapm_stream_stop(rtd, substream->stream);
+
+       mutex_unlock(&rtd->card->pcm_mutex);
+
+       for_each_rtd_components(rtd, i, component) {
+               pm_runtime_mark_last_busy(component->dev);
+               pm_runtime_put_autosuspend(component->dev);
+       }
+
+       for_each_rtd_components(rtd, i, component)
+               if (!component->active)
+                       pinctrl_pm_select_sleep_state(component->dev);
+
+       return 0;
+}
+
 /*
  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
  * then initialized and any private data can be allocated. This also calls
@@ -549,25 +739,32 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 
        mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
 
+       ret = soc_pcm_components_open(substream);
+       if (ret < 0)
+               goto component_err;
+
+       ret = soc_rtd_startup(rtd, substream);
+       if (ret < 0) {
+               pr_err("ASoC: %s startup failed: %d\n",
+                      rtd->dai_link->name, ret);
+               goto rtd_startup_err;
+       }
+
        /* startup the audio subsystem */
        ret = snd_soc_dai_startup(cpu_dai, substream);
        if (ret < 0) {
                dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n",
                        cpu_dai->name, ret);
-               goto out;
+               goto cpu_dai_err;
        }
 
-       ret = soc_pcm_components_open(substream, &component);
-       if (ret < 0)
-               goto component_err;
-
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
                ret = snd_soc_dai_startup(codec_dai, substream);
                if (ret < 0) {
                        dev_err(codec_dai->dev,
                                "ASoC: can't open codec %s: %d\n",
                                codec_dai->name, ret);
-                       goto codec_dai_err;
+                       goto config_err;
                }
 
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -576,13 +773,6 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                        codec_dai->rx_mask = 0;
        }
 
-       ret = soc_rtd_startup(rtd, substream);
-       if (ret < 0) {
-               pr_err("ASoC: %s startup failed: %d\n",
-                      rtd->dai_link->name, ret);
-               goto machine_err;
-       }
-
        /* Dynamic PCM DAI links compat checks use dynamic capabilities */
        if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
                goto dynamic;
@@ -647,20 +837,15 @@ dynamic:
        return 0;
 
 config_err:
-       soc_rtd_shutdown(rtd, substream);
-
-machine_err:
-       i = rtd->num_codecs;
-
-codec_dai_err:
-       for_each_rtd_codec_dai_rollback(rtd, i, codec_dai)
+       for_each_rtd_codec_dai(rtd, i, codec_dai)
                snd_soc_dai_shutdown(codec_dai, substream);
+cpu_dai_err:
+       snd_soc_dai_shutdown(cpu_dai, substream);
 
+       soc_rtd_shutdown(rtd, substream);
+rtd_startup_err:
+       soc_pcm_components_close(substream);
 component_err:
-       soc_pcm_components_close(substream, component);
-
-       snd_soc_dai_shutdown(cpu_dai, substream);
-out:
        mutex_unlock(&rtd->card->pcm_mutex);
 
        for_each_rtd_components(rtd, i, component) {
@@ -685,59 +870,6 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
         */
 }
 
-/*
- * Called by ALSA when a PCM substream is closed. Private data can be
- * freed here. The cpu DAI, codec DAI, machine and components are also
- * shutdown.
- */
-static int soc_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_component *component;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai;
-       int i;
-
-       mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
-
-       snd_soc_runtime_deactivate(rtd, substream->stream);
-
-       /* clear the corresponding DAIs rate when inactive */
-       if (!cpu_dai->active)
-               cpu_dai->rate = 0;
-
-       for_each_rtd_codec_dai(rtd, i, codec_dai) {
-               if (!codec_dai->active)
-                       codec_dai->rate = 0;
-       }
-
-       snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
-
-       snd_soc_dai_shutdown(cpu_dai, substream);
-
-       for_each_rtd_codec_dai(rtd, i, codec_dai)
-               snd_soc_dai_shutdown(codec_dai, substream);
-
-       soc_rtd_shutdown(rtd, substream);
-
-       soc_pcm_components_close(substream, NULL);
-
-       snd_soc_dapm_stream_stop(rtd, substream->stream);
-
-       mutex_unlock(&rtd->card->pcm_mutex);
-
-       for_each_rtd_components(rtd, i, component) {
-               pm_runtime_mark_last_busy(component->dev);
-               pm_runtime_put_autosuspend(component->dev);
-       }
-
-       for_each_rtd_components(rtd, i, component)
-               if (!component->active)
-                       pinctrl_pm_select_sleep_state(component->dev);
-
-       return 0;
-}
-
 /*
  * Called by ALSA when the PCM substream is prepared, can set format, sample
  * rate, etc.  This function is non atomic and can be called multiple times,
@@ -822,13 +954,15 @@ static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_component *component;
-       int i, ret = 0;
+       int i, r, ret = 0;
 
        for_each_rtd_components(rtd, i, component) {
                if (component == last)
                        break;
 
-               ret |= snd_soc_component_hw_free(component, substream);
+               r = snd_soc_component_hw_free(component, substream);
+               if (r < 0)
+                       ret = r; /* use last ret */
        }
 
        return ret;
@@ -989,8 +1123,11 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
 
        /* apply codec digital mute */
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
-               if ((playback && codec_dai->playback_active == 1) ||
-                   (!playback && codec_dai->capture_active == 1))
+               int playback_active = codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK];
+               int capture_active  = codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE];
+
+               if ((playback && playback_active == 1) ||
+                   (!playback && capture_active == 1))
                        snd_soc_dai_digital_mute(codec_dai, 1,
                                                 substream->stream);
        }
@@ -1162,9 +1299,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
 {
        struct snd_soc_dpcm *dpcm;
        unsigned long flags;
-#ifdef CONFIG_DEBUG_FS
-       char *name;
-#endif
 
        /* only add new dpcms */
        for_each_dpcm_be(fe, stream, dpcm) {
@@ -1185,21 +1319,12 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
        list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
-       dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
-                       stream ? "capture" : "playback",  fe->dai_link->name,
-                       stream ? "<-" : "->", be->dai_link->name);
-
-#ifdef CONFIG_DEBUG_FS
-       name = kasprintf(GFP_KERNEL, "%s:%s", be->dai_link->name,
-                        stream ? "capture" : "playback");
-       if (name) {
-               dpcm->debugfs_state = debugfs_create_dir(name,
-                                                        fe->debugfs_dpcm_root);
-               debugfs_create_u32("state", 0644, dpcm->debugfs_state,
-                                  &dpcm->state);
-               kfree(name);
-       }
-#endif
+       dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
+                       stream ? "capture" : "playback",  fe->dai_link->name,
+                       stream ? "<-" : "->", be->dai_link->name);
+
+       dpcm_create_debugfs_state(dpcm, stream);
+
        return 1;
 }
 
@@ -1252,9 +1377,8 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
                /* BEs still alive need new FE */
                dpcm_be_reparent(fe, dpcm->be, stream);
 
-#ifdef CONFIG_DEBUG_FS
-               debugfs_remove_recursive(dpcm->debugfs_state);
-#endif
+               dpcm_remove_debugfs_state(dpcm);
+
                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                list_del(&dpcm->list_be);
                list_del(&dpcm->list_fe);
@@ -1268,74 +1392,46 @@ 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;
+       struct snd_soc_dapm_widget *w;
        struct snd_soc_dai *dai;
        int i;
 
        dev_dbg(card->dev, "ASoC: find BE for widget %s\n", widget->name);
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               for_each_card_rtds(card, be) {
+       for_each_card_rtds(card, be) {
 
-                       if (!be->dai_link->no_pcm)
-                               continue;
-
-                       dev_dbg(card->dev, "ASoC: try BE : %s\n",
-                               be->cpu_dai->playback_widget ?
-                               be->cpu_dai->playback_widget->name : "(not set)");
-
-                       if (be->cpu_dai->playback_widget == widget)
-                               return be;
+               if (!be->dai_link->no_pcm)
+                       continue;
 
-                       for_each_rtd_codec_dai(be, i, dai) {
-                               if (dai->playback_widget == widget)
-                                       return be;
-                       }
-               }
-       } else {
+               w = snd_soc_dai_get_widget(be->cpu_dai, stream);
 
-               for_each_card_rtds(card, be) {
+               dev_dbg(card->dev, "ASoC: try BE : %s\n",
+                       w ? w->name : "(not set)");
 
-                       if (!be->dai_link->no_pcm)
-                               continue;
+               if (w == widget)
+                       return be;
 
-                       dev_dbg(card->dev, "ASoC: try BE %s\n",
-                               be->cpu_dai->capture_widget ?
-                               be->cpu_dai->capture_widget->name : "(not set)");
+               for_each_rtd_codec_dai(be, i, dai) {
+                       w = snd_soc_dai_get_widget(dai, stream);
 
-                       if (be->cpu_dai->capture_widget == widget)
+                       if (w == widget)
                                return be;
-
-                       for_each_rtd_codec_dai(be, i, dai) {
-                               if (dai->capture_widget == widget)
-                                       return be;
-                       }
                }
        }
 
-       /* dai link name and stream name set correctly ? */
-       dev_err(card->dev, "ASoC: can't get %s BE for %s\n",
-               stream ? "capture" : "playback", widget->name);
+       /* Widget provided is not a BE */
        return NULL;
 }
 
-static inline struct snd_soc_dapm_widget *
-       dai_get_widget(struct snd_soc_dai *dai, int stream)
-{
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               return dai->playback_widget;
-       else
-               return dai->capture_widget;
-}
-
 static int widget_in_list(struct snd_soc_dapm_widget_list *list,
                struct snd_soc_dapm_widget *widget)
 {
+       struct snd_soc_dapm_widget *w;
        int i;
 
-       for (i = 0; i < list->num_widgets; i++) {
-               if (widget == list->widgets[i])
+       for_each_dapm_widgets(list, i, w)
+               if (widget == w)
                        return 1;
-       }
 
        return 0;
 }
@@ -1345,36 +1441,17 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
 {
        struct snd_soc_card *card = widget->dapm->card;
        struct snd_soc_pcm_runtime *rtd;
-       struct snd_soc_dai *dai;
-       int i;
-
-       if (dir == SND_SOC_DAPM_DIR_OUT) {
-               for_each_card_rtds(card, rtd) {
-                       if (!rtd->dai_link->no_pcm)
-                               continue;
-
-                       if (rtd->cpu_dai->playback_widget == widget)
-                               return true;
-
-                       for_each_rtd_codec_dai(rtd, i, dai) {
-                               if (dai->playback_widget == widget)
-                                       return true;
-                       }
-               }
-       } else { /* SND_SOC_DAPM_DIR_IN */
-               for_each_card_rtds(card, rtd) {
-                       if (!rtd->dai_link->no_pcm)
-                               continue;
+       int stream;
 
-                       if (rtd->cpu_dai->capture_widget == widget)
-                               return true;
+       /* adjust dir to stream */
+       if (dir == SND_SOC_DAPM_DIR_OUT)
+               stream = SNDRV_PCM_STREAM_PLAYBACK;
+       else
+               stream = SNDRV_PCM_STREAM_CAPTURE;
 
-                       for_each_rtd_codec_dai(rtd, i, dai) {
-                               if (dai->capture_widget == widget)
-                                       return true;
-                       }
-               }
-       }
+       rtd = dpcm_get_be(card, widget, stream);
+       if (rtd)
+               return true;
 
        return false;
 }
@@ -1395,6 +1472,11 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
        return paths;
 }
 
+void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
+{
+       snd_soc_dapm_dai_free_widgets(list);
+}
+
 static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
        struct snd_soc_dapm_widget_list **list_)
 {
@@ -1410,7 +1492,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
                unsigned int i;
 
                /* is there a valid CPU DAI widget for this BE */
-               widget = dai_get_widget(dpcm->be->cpu_dai, stream);
+               widget = snd_soc_dai_get_widget(dpcm->be->cpu_dai, stream);
 
                /* prune the BE if it's no longer in our active list */
                if (widget && widget_in_list(list, widget))
@@ -1419,7 +1501,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
                /* is there a valid CODEC DAI widget for this BE */
                do_prune = 1;
                for_each_rtd_codec_dai(dpcm->be, i, dai) {
-                       widget = dai_get_widget(dai, stream);
+                       widget = snd_soc_dai_get_widget(dai, stream);
 
                        /* prune the BE if it's no longer in our active list */
                        if (widget && widget_in_list(list, widget))
@@ -1446,12 +1528,13 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
        struct snd_soc_card *card = fe->card;
        struct snd_soc_dapm_widget_list *list = *list_;
        struct snd_soc_pcm_runtime *be;
+       struct snd_soc_dapm_widget *widget;
        int i, new = 0, err;
 
        /* Create any new FE <--> BE connections */
-       for (i = 0; i < list->num_widgets; i++) {
+       for_each_dapm_widgets(list, i, widget) {
 
-               switch (list->widgets[i]->id) {
+               switch (widget->id) {
                case snd_soc_dapm_dai_in:
                        if (stream != SNDRV_PCM_STREAM_PLAYBACK)
                                continue;
@@ -1465,10 +1548,10 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
                }
 
                /* is there a valid BE rtd for this widget */
-               be = dpcm_get_be(card, list->widgets[i], stream);
+               be = dpcm_get_be(card, widget, stream);
                if (!be) {
                        dev_err(fe->dev, "ASoC: no BE found for %s\n",
-                                       list->widgets[i]->name);
+                                       widget->name);
                        continue;
                }
 
@@ -1484,7 +1567,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
                err = dpcm_be_connect(fe, be, stream);
                if (err < 0) {
                        dev_err(fe->dev, "ASoC: can't connect %s\n",
-                               list->widgets[i]->name);
+                               widget->name);
                        break;
                } else if (err == 0) /* already connected */
                        continue;
@@ -1671,7 +1754,6 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
 
        for_each_dpcm_be(fe, stream, dpcm) {
                struct snd_soc_pcm_runtime *be = dpcm->be;
-               struct snd_soc_dai_driver *codec_dai_drv;
                struct snd_soc_pcm_stream *codec_stream;
                int i;
 
@@ -1683,11 +1765,7 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
                        if (!snd_soc_dai_stream_valid(dai, stream))
                                continue;
 
-                       codec_dai_drv = dai->driver;
-                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               codec_stream = &codec_dai_drv->playback;
-                       else
-                               codec_stream = &codec_dai_drv->capture;
+                       codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
 
                        *formats &= codec_stream->formats;
                }
@@ -1712,15 +1790,10 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
 
        for_each_dpcm_be(fe, stream, dpcm) {
                struct snd_soc_pcm_runtime *be = dpcm->be;
-               struct snd_soc_dai_driver *cpu_dai_drv =  be->cpu_dai->driver;
-               struct snd_soc_dai_driver *codec_dai_drv;
                struct snd_soc_pcm_stream *codec_stream;
                struct snd_soc_pcm_stream *cpu_stream;
 
-               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       cpu_stream = &cpu_dai_drv->playback;
-               else
-                       cpu_stream = &cpu_dai_drv->capture;
+               cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream);
 
                *channels_min = max(*channels_min, cpu_stream->channels_min);
                *channels_max = min(*channels_max, cpu_stream->channels_max);
@@ -1730,12 +1803,7 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
                 * DAIs connected to a single CPU DAI, use CPU DAI's directly
                 */
                if (be->num_codecs == 1) {
-                       codec_dai_drv = be->codec_dais[0]->driver;
-
-                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               codec_stream = &codec_dai_drv->playback;
-                       else
-                               codec_stream = &codec_dai_drv->capture;
+                       codec_stream = snd_soc_dai_get_pcm_stream(be->codec_dais[0], stream);
 
                        *channels_min = max(*channels_min,
                                            codec_stream->channels_min);
@@ -1764,17 +1832,12 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
 
        for_each_dpcm_be(fe, stream, dpcm) {
                struct snd_soc_pcm_runtime *be = dpcm->be;
-               struct snd_soc_dai_driver *cpu_dai_drv =  be->cpu_dai->driver;
-               struct snd_soc_dai_driver *codec_dai_drv;
                struct snd_soc_pcm_stream *codec_stream;
                struct snd_soc_pcm_stream *cpu_stream;
                struct snd_soc_dai *dai;
                int i;
 
-               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       cpu_stream = &cpu_dai_drv->playback;
-               else
-                       cpu_stream = &cpu_dai_drv->capture;
+               cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream);
 
                *rate_min = max(*rate_min, cpu_stream->rate_min);
                *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max);
@@ -1788,11 +1851,7 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
                        if (!snd_soc_dai_stream_valid(dai, stream))
                                continue;
 
-                       codec_dai_drv = dai->driver;
-                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               codec_stream = &codec_dai_drv->playback;
-                       else
-                               codec_stream = &codec_dai_drv->capture;
+                       codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
 
                        *rate_min = max(*rate_min, codec_stream->rate_min);
                        *rate_max = min_not_zero(*rate_max,
@@ -2652,36 +2711,12 @@ disconnect:
        return ret;
 }
 
-static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
-{
-       int ret;
-
-       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
-       ret = dpcm_run_update_startup(fe, stream);
-       if (ret < 0)
-               dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
-       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
-
-       return ret;
-}
-
-static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
-{
-       int ret;
-
-       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
-       ret = dpcm_run_update_shutdown(fe, stream);
-       if (ret < 0)
-               dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
-       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
-
-       return ret;
-}
-
 static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
 {
        struct snd_soc_dapm_widget_list *list;
+       int stream;
        int count, paths;
+       int ret;
 
        if (!fe->dai_link->dynamic)
                return 0;
@@ -2694,67 +2729,46 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
        dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n",
                new ? "new" : "old", fe->dai_link->name);
 
-       /* skip if FE doesn't have playback capability */
-       if (!snd_soc_dai_stream_valid(fe->cpu_dai,   SNDRV_PCM_STREAM_PLAYBACK) ||
-           !snd_soc_dai_stream_valid(fe->codec_dai, SNDRV_PCM_STREAM_PLAYBACK))
-               goto capture;
-
-       /* skip if FE isn't currently playing */
-       if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active)
-               goto capture;
-
-       paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
-       if (paths < 0) {
-               dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
-                        fe->dai_link->name,  "playback");
-               return paths;
-       }
-
-       /* update any playback paths */
-       count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new);
-       if (count) {
-               if (new)
-                       dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
-               else
-                       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);
-       }
+       for_each_pcm_streams(stream) {
 
-       dpcm_path_put(&list);
+               /* skip if FE doesn't have playback/capture capability */
+               if (!snd_soc_dai_stream_valid(fe->cpu_dai,   stream) ||
+                   !snd_soc_dai_stream_valid(fe->codec_dai, stream))
+                       continue;
 
-capture:
-       /* skip if FE doesn't have capture capability */
-       if (!snd_soc_dai_stream_valid(fe->cpu_dai,   SNDRV_PCM_STREAM_CAPTURE) ||
-           !snd_soc_dai_stream_valid(fe->codec_dai, SNDRV_PCM_STREAM_CAPTURE))
-               return 0;
+               /* skip if FE isn't currently playing/capturing */
+               if (!fe->cpu_dai->stream_active[stream] ||
+                   !fe->codec_dai->stream_active[stream])
+                       continue;
 
-       /* skip if FE isn't currently capturing */
-       if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active)
-               return 0;
+               paths = dpcm_path_get(fe, stream, &list);
+               if (paths < 0) {
+                       dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
+                                fe->dai_link->name,
+                                stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                                "playback" : "capture");
+                       return paths;
+               }
 
-       paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
-       if (paths < 0) {
-               dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
-                        fe->dai_link->name,  "capture");
-               return paths;
-       }
+               /* update any playback/capture paths */
+               count = dpcm_process_paths(fe, stream, &list, new);
+               if (count) {
+                       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
+                       if (new)
+                               ret = dpcm_run_update_startup(fe, stream);
+                       else
+                               ret = dpcm_run_update_shutdown(fe, stream);
+                       if (ret < 0)
+                               dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
+                       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
 
-       /* update any old capture paths */
-       count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new);
-       if (count) {
-               if (new)
-                       dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
-               else
-                       dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
+                       dpcm_clear_pending_state(fe, stream);
+                       dpcm_be_disconnect(fe, stream);
+               }
 
-               dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
-               dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
+               dpcm_path_put(&list);
        }
 
-       dpcm_path_put(&list);
-
        return 0;
 }
 
@@ -2785,33 +2799,6 @@ out:
        mutex_unlock(&card->mutex);
        return ret;
 }
-int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
-{
-       struct snd_soc_dpcm *dpcm;
-       struct snd_soc_dai *dai;
-
-       for_each_dpcm_be(fe, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
-
-               struct snd_soc_pcm_runtime *be = dpcm->be;
-               int i;
-
-               if (be->dai_link->ignore_suspend)
-                       continue;
-
-               for_each_rtd_codec_dai(be, i, dai) {
-                       struct snd_soc_dai_driver *drv = dai->driver;
-
-                       dev_dbg(be->dev, "ASoC: BE digital mute %s\n",
-                                        be->dai_link->name);
-
-                       if (drv->ops && drv->ops->digital_mute &&
-                                                       dai->playback_active)
-                               drv->ops->digital_mute(dai, mute);
-               }
-       }
-
-       return 0;
-}
 
 static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
 {
@@ -2826,8 +2813,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
 
        ret = dpcm_path_get(fe, stream, &list);
        if (ret < 0) {
-               mutex_unlock(&fe->card->mutex);
-               return ret;
+               goto open_end;
        } else if (ret == 0) {
                dev_dbg(fe->dev, "ASoC: %s no valid %s route\n",
                        fe->dai_link->name, stream ? "capture" : "playback");
@@ -2848,6 +2834,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
 
        dpcm_clear_pending_state(fe, stream);
        dpcm_path_put(&list);
+open_end:
        mutex_unlock(&fe->card->mutex);
        return ret;
 }
@@ -2888,22 +2875,19 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                capture = rtd->dai_link->dpcm_capture;
        } else {
                /* Adapt stream for codec2codec links */
-               struct snd_soc_pcm_stream *cpu_capture = rtd->dai_link->params ?
-                       &cpu_dai->driver->playback : &cpu_dai->driver->capture;
-               struct snd_soc_pcm_stream *cpu_playback = rtd->dai_link->params ?
-                       &cpu_dai->driver->capture : &cpu_dai->driver->playback;
+               int cpu_capture = rtd->dai_link->params ?
+                       SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+               int cpu_playback = rtd->dai_link->params ?
+                       SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
 
                for_each_rtd_codec_dai(rtd, i, codec_dai) {
                        if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_PLAYBACK) &&
-                           snd_soc_dai_stream_valid(cpu_dai,   SNDRV_PCM_STREAM_PLAYBACK))
+                           snd_soc_dai_stream_valid(cpu_dai,   cpu_playback))
                                playback = 1;
                        if (snd_soc_dai_stream_valid(codec_dai, SNDRV_PCM_STREAM_CAPTURE) &&
-                           snd_soc_dai_stream_valid(cpu_dai,   SNDRV_PCM_STREAM_CAPTURE))
+                           snd_soc_dai_stream_valid(cpu_dai,   cpu_capture))
                                capture = 1;
                }
-
-               capture = capture && cpu_capture->channels_min;
-               playback = playback && cpu_playback->channels_min;
        }
 
        if (rtd->dai_link->playback_only) {
@@ -3050,33 +3034,17 @@ struct snd_pcm_substream *
 }
 EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
 
-/* get the BE runtime state */
-enum snd_soc_dpcm_state
-       snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
-{
-       return be->dpcm[stream].state;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state);
-
-/* set the BE runtime state */
-void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
-               int stream, enum snd_soc_dpcm_state state)
-{
-       be->dpcm[stream].state = state;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state);
-
-/*
- * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
- * are not running, paused or suspended for the specified stream direction.
- */
-int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
-               struct snd_soc_pcm_runtime *be, int stream)
+static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
+                                   struct snd_soc_pcm_runtime *be,
+                                   int stream,
+                                   const enum snd_soc_dpcm_state *states,
+                                   int num_states)
 {
        struct snd_soc_dpcm *dpcm;
        int state;
        int ret = 1;
        unsigned long flags;
+       int i;
 
        spin_lock_irqsave(&fe->card->dpcm_lock, flags);
        for_each_dpcm_fe(be, stream, dpcm) {
@@ -3085,18 +3053,34 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
                        continue;
 
                state = dpcm->fe->dpcm[stream].state;
-               if (state == SND_SOC_DPCM_STATE_START ||
-                       state == SND_SOC_DPCM_STATE_PAUSED ||
-                       state == SND_SOC_DPCM_STATE_SUSPEND) {
-                       ret = 0;
-                       break;
+               for (i = 0; i < num_states; i++) {
+                       if (state == states[i]) {
+                               ret = 0;
+                               break;
+                       }
                }
        }
        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
-       /* it's safe to free/stop this BE DAI */
+       /* it's safe to do this BE DAI */
        return ret;
 }
+
+/*
+ * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
+ * are not running, paused or suspended for the specified stream direction.
+ */
+int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
+               struct snd_soc_pcm_runtime *be, int stream)
+{
+       const enum snd_soc_dpcm_state state[] = {
+               SND_SOC_DPCM_STATE_START,
+               SND_SOC_DPCM_STATE_PAUSED,
+               SND_SOC_DPCM_STATE_SUSPEND,
+       };
+
+       return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
+}
 EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
 
 /*
@@ -3106,168 +3090,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
 int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream)
 {
-       struct snd_soc_dpcm *dpcm;
-       int state;
-       int ret = 1;
-       unsigned long flags;
-
-       spin_lock_irqsave(&fe->card->dpcm_lock, flags);
-       for_each_dpcm_fe(be, stream, dpcm) {
-
-               if (dpcm->fe == fe)
-                       continue;
+       const enum snd_soc_dpcm_state state[] = {
+               SND_SOC_DPCM_STATE_START,
+               SND_SOC_DPCM_STATE_PAUSED,
+               SND_SOC_DPCM_STATE_SUSPEND,
+               SND_SOC_DPCM_STATE_PREPARE,
+       };
 
-               state = dpcm->fe->dpcm[stream].state;
-               if (state == SND_SOC_DPCM_STATE_START ||
-                       state == SND_SOC_DPCM_STATE_PAUSED ||
-                       state == SND_SOC_DPCM_STATE_SUSPEND ||
-                       state == SND_SOC_DPCM_STATE_PREPARE) {
-                       ret = 0;
-                       break;
-               }
-       }
-       spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
-
-       /* it's safe to change hw_params */
-       return ret;
+       return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
 }
 EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
-
-#ifdef CONFIG_DEBUG_FS
-static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
-{
-       switch (state) {
-       case SND_SOC_DPCM_STATE_NEW:
-               return "new";
-       case SND_SOC_DPCM_STATE_OPEN:
-               return "open";
-       case SND_SOC_DPCM_STATE_HW_PARAMS:
-               return "hw_params";
-       case SND_SOC_DPCM_STATE_PREPARE:
-               return "prepare";
-       case SND_SOC_DPCM_STATE_START:
-               return "start";
-       case SND_SOC_DPCM_STATE_STOP:
-               return "stop";
-       case SND_SOC_DPCM_STATE_SUSPEND:
-               return "suspend";
-       case SND_SOC_DPCM_STATE_PAUSED:
-               return "paused";
-       case SND_SOC_DPCM_STATE_HW_FREE:
-               return "hw_free";
-       case SND_SOC_DPCM_STATE_CLOSE:
-               return "close";
-       }
-
-       return "unknown";
-}
-
-static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
-                               int stream, char *buf, size_t size)
-{
-       struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
-       struct snd_soc_dpcm *dpcm;
-       ssize_t offset = 0;
-       unsigned long flags;
-
-       /* FE state */
-       offset += scnprintf(buf + offset, size - offset,
-                       "[%s - %s]\n", fe->dai_link->name,
-                       stream ? "Capture" : "Playback");
-
-       offset += scnprintf(buf + offset, size - offset, "State: %s\n",
-                       dpcm_state_string(fe->dpcm[stream].state));
-
-       if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
-           (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
-               offset += scnprintf(buf + offset, size - offset,
-                               "Hardware Params: "
-                               "Format = %s, Channels = %d, Rate = %d\n",
-                               snd_pcm_format_name(params_format(params)),
-                               params_channels(params),
-                               params_rate(params));
-
-       /* BEs state */
-       offset += scnprintf(buf + offset, size - offset, "Backends:\n");
-
-       if (list_empty(&fe->dpcm[stream].be_clients)) {
-               offset += scnprintf(buf + offset, size - offset,
-                               " No active DSP links\n");
-               goto out;
-       }
-
-       spin_lock_irqsave(&fe->card->dpcm_lock, flags);
-       for_each_dpcm_be(fe, stream, dpcm) {
-               struct snd_soc_pcm_runtime *be = dpcm->be;
-               params = &dpcm->hw_params;
-
-               offset += scnprintf(buf + offset, size - offset,
-                               "- %s\n", be->dai_link->name);
-
-               offset += scnprintf(buf + offset, size - offset,
-                               "   State: %s\n",
-                               dpcm_state_string(be->dpcm[stream].state));
-
-               if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
-                   (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
-                       offset += scnprintf(buf + offset, size - offset,
-                               "   Hardware Params: "
-                               "Format = %s, Channels = %d, Rate = %d\n",
-                               snd_pcm_format_name(params_format(params)),
-                               params_channels(params),
-                               params_rate(params));
-       }
-       spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
-out:
-       return offset;
-}
-
-static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
-                               size_t count, loff_t *ppos)
-{
-       struct snd_soc_pcm_runtime *fe = file->private_data;
-       ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
-       char *buf;
-
-       buf = kmalloc(out_count, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       if (snd_soc_dai_stream_valid(fe->cpu_dai, SNDRV_PCM_STREAM_PLAYBACK))
-               offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK,
-                                       buf + offset, out_count - offset);
-
-       if (snd_soc_dai_stream_valid(fe->cpu_dai, SNDRV_PCM_STREAM_CAPTURE))
-               offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE,
-                                       buf + offset, out_count - offset);
-
-       ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
-
-       kfree(buf);
-       return ret;
-}
-
-static const struct file_operations dpcm_state_fops = {
-       .open = simple_open,
-       .read = dpcm_state_read_file,
-       .llseek = default_llseek,
-};
-
-void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
-{
-       if (!rtd->dai_link)
-               return;
-
-       if (!rtd->dai_link->dynamic)
-               return;
-
-       if (!rtd->card->debugfs_card_root)
-               return;
-
-       rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
-                       rtd->card->debugfs_card_root);
-
-       debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
-                           rtd, &dpcm_state_fops);
-}
-#endif