ASoC: component: Add sync_stop PCM ops
[sfrench/cifs-2.6.git] / sound / soc / soc-pcm.c
index e163dde5eab1d708a98b0767318811fa4f2491d8..4457ac374a0ec77665587237bb9c285a5cf8e705 100644 (file)
@@ -118,11 +118,8 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
        if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
                return true;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component)
                ignore &= !component->driver->use_pmdown_time;
-       }
 
        return ignore;
 }
@@ -435,8 +432,7 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream,
        struct snd_soc_component *component;
        int ret = 0;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
+       for_each_rtd_components(rtd, rtdcom, component) {
                *last = component;
 
                ret = snd_soc_component_module_get_when_open(component);
@@ -467,9 +463,7 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream,
        struct snd_soc_component *component;
        int ret = 0;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                if (component == last)
                        break;
 
@@ -500,9 +494,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        for_each_rtd_codec_dai(rtd, i, codec_dai)
                pinctrl_pm_select_default_state(codec_dai->dev);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                pm_runtime_get_sync(component->dev);
        }
 
@@ -625,9 +617,7 @@ component_err:
 out:
        mutex_unlock(&rtd->card->pcm_mutex);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                pm_runtime_mark_last_busy(component->dev);
                pm_runtime_put_autosuspend(component->dev);
        }
@@ -740,9 +730,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 
        mutex_unlock(&rtd->card->pcm_mutex);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                pm_runtime_mark_last_busy(component->dev);
                pm_runtime_put_autosuspend(component->dev);
        }
@@ -782,9 +770,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                ret = snd_soc_component_prepare(component, substream);
                if (ret < 0) {
                        dev_err(component->dev,
@@ -849,9 +835,7 @@ static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_component *component;
        int ret = 0;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                if (component == last)
                        break;
 
@@ -945,9 +929,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
 
        snd_soc_dapm_update_dai(substream, params, cpu_dai);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                ret = snd_soc_component_hw_params(component, substream, params);
                if (ret < 0) {
                        dev_err(component->dev,
@@ -1047,7 +1029,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_component *component;
@@ -1056,24 +1038,56 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        struct snd_soc_dai *codec_dai;
        int i, ret;
 
+       if (rtd->dai_link->ops->trigger) {
+               ret = rtd->dai_link->ops->trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       for_each_rtd_components(rtd, rtdcom, component) {
+               ret = snd_soc_component_trigger(component, substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
+       if (ret < 0)
+               return ret;
+
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
                ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
                if (ret < 0)
                        return ret;
        }
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
+       return 0;
+}
 
-               ret = snd_soc_component_trigger(component, substream, cmd);
+static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai;
+       int i, ret;
+
+       for_each_rtd_codec_dai(rtd, i, codec_dai) {
+               ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
                if (ret < 0)
                        return ret;
        }
 
-       snd_soc_dai_trigger(cpu_dai, substream, cmd);
+       ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
        if (ret < 0)
                return ret;
 
+       for_each_rtd_components(rtd, rtdcom, component) {
+               ret = snd_soc_component_trigger(component, substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
        if (rtd->dai_link->ops->trigger) {
                ret = rtd->dai_link->ops->trigger(substream, cmd);
                if (ret < 0)
@@ -1083,6 +1097,28 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        return 0;
 }
 
+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       int ret;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = soc_pcm_trigger_start(substream, cmd);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = soc_pcm_trigger_stop(substream, cmd);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
 static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                                   int cmd)
 {
@@ -1097,7 +1133,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                        return ret;
        }
 
-       snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
+       ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
        if (ret < 0)
                return ret;
 
@@ -1146,6 +1182,7 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
 {
        struct snd_soc_dpcm *dpcm;
        unsigned long flags;
+       char *name;
 
        /* only add new dpcms */
        for_each_dpcm_be(fe, stream, dpcm) {
@@ -1171,9 +1208,15 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
                        stream ? "<-" : "->", be->dai_link->name);
 
 #ifdef CONFIG_DEBUG_FS
-       dpcm->debugfs_state = debugfs_create_dir(be->dai_link->name,
-                                                fe->debugfs_dpcm_root);
-       debugfs_create_u32("state", 0644, dpcm->debugfs_state, &dpcm->state);
+       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
        return 1;
 }
@@ -1378,6 +1421,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
        struct snd_soc_dapm_widget *widget;
        struct snd_soc_dai *dai;
        int prune = 0;
+       int do_prune;
 
        /* Destroy any old FE <--> BE connections */
        for_each_dpcm_be(fe, stream, dpcm) {
@@ -1391,13 +1435,16 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
                        continue;
 
                /* 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);
 
                        /* prune the BE if it's no longer in our active list */
                        if (widget && widget_in_list(list, widget))
-                               continue;
+                               do_prune = 0;
                }
+               if (!do_prune)
+                       continue;
 
                dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
                        stream ? "capture" : "playback",
@@ -2282,42 +2329,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
 }
 EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
 
+static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
+                                 int cmd, bool fe_first)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       int ret;
+
+       /* call trigger on the frontend before the backend. */
+       if (fe_first) {
+               dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
+                       fe->dai_link->name, cmd);
+
+               ret = soc_pcm_trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+
+               ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+               return ret;
+       }
+
+       /* call trigger on the frontend after the backend. */
+       ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
+               fe->dai_link->name, cmd);
+
+       ret = soc_pcm_trigger(substream, cmd);
+
+       return ret;
+}
+
 static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *fe = substream->private_data;
-       int stream = substream->stream, ret;
+       int stream = substream->stream;
+       int ret = 0;
        enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
        switch (trigger) {
        case SND_SOC_DPCM_TRIGGER_PRE:
-               /* call trigger on the frontend before the backend. */
-
-               dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
-                               fe->dai_link->name, cmd);
-
-               ret = soc_pcm_trigger(substream, cmd);
-               if (ret < 0) {
-                       dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
-                       goto out;
+               switch (cmd) {
+               case SNDRV_PCM_TRIGGER_START:
+               case SNDRV_PCM_TRIGGER_RESUME:
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+                       break;
+               case SNDRV_PCM_TRIGGER_STOP:
+               case SNDRV_PCM_TRIGGER_SUSPEND:
+               case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
                }
-
-               ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
                break;
        case SND_SOC_DPCM_TRIGGER_POST:
-               /* call trigger on the frontend after the backend. */
-
-               ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
-               if (ret < 0) {
-                       dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
-                       goto out;
+               switch (cmd) {
+               case SNDRV_PCM_TRIGGER_START:
+               case SNDRV_PCM_TRIGGER_RESUME:
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+                       break;
+               case SNDRV_PCM_TRIGGER_STOP:
+               case SNDRV_PCM_TRIGGER_SUSPEND:
+               case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
                }
-
-               dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
-                               fe->dai_link->name, cmd);
-
-               ret = soc_pcm_trigger(substream, cmd);
                break;
        case SND_SOC_DPCM_TRIGGER_BESPOKE:
                /* bespoke trigger() - handles both FE and BEs */
@@ -2326,10 +2412,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
                                fe->dai_link->name, cmd);
 
                ret = soc_pcm_bespoke_trigger(substream, cmd);
-               if (ret < 0) {
-                       dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
-                       goto out;
-               }
                break;
        default:
                dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
@@ -2338,6 +2420,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
                goto out;
        }
 
+       if (ret < 0) {
+               dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
+                       cmd, ret);
+               goto out;
+       }
+
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
@@ -2817,6 +2905,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        struct snd_soc_dai *codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
        struct snd_pcm *pcm;
        char new_name[64];
        int ret = 0, playback = 0, capture = 0;
@@ -2916,7 +3005,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                rtd->ops.hw_free        = dpcm_fe_dai_hw_free;
                rtd->ops.close          = dpcm_fe_dai_close;
                rtd->ops.pointer        = soc_pcm_pointer;
-               rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
        } else {
                rtd->ops.open           = soc_pcm_open;
                rtd->ops.hw_params      = soc_pcm_hw_params;
@@ -2925,20 +3013,20 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                rtd->ops.hw_free        = soc_pcm_hw_free;
                rtd->ops.close          = soc_pcm_close;
                rtd->ops.pointer        = soc_pcm_pointer;
-               rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
        }
 
-       for_each_rtdcom(rtd, rtdcom) {
-               const struct snd_pcm_ops *ops = rtdcom->component->driver->ops;
-
-               if (!ops)
-                       continue;
+       for_each_rtd_components(rtd, rtdcom, component) {
+               const struct snd_soc_component_driver *drv = component->driver;
 
-               if (ops->copy_user)
+               if (drv->ioctl)
+                       rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
+               if (drv->sync_stop)
+                       rtd->ops.sync_stop      = snd_soc_pcm_component_sync_stop;
+               if (drv->copy_user)
                        rtd->ops.copy_user      = snd_soc_pcm_component_copy_user;
-               if (ops->page)
+               if (drv->page)
                        rtd->ops.page           = snd_soc_pcm_component_page;
-               if (ops->mmap)
+               if (drv->mmap)
                        rtd->ops.mmap           = snd_soc_pcm_component_mmap;
        }