ASoC: soc-pcm: care trigger rollback
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Mon, 30 Nov 2020 23:51:33 +0000 (08:51 +0900)
committerMark Brown <broonie@kernel.org>
Wed, 9 Dec 2020 12:13:38 +0000 (12:13 +0000)
soc_pcm_trigger() calls DAI/Component/Link trigger,
but some of them might be failed.

static int soc_pcm_trigger(...)
{
...
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = snd_soc_link_trigger(substream, cmd);
if (ret < 0)
break;

(*) ret = snd_soc_pcm_component_trigger(substream, cmd);
if (ret < 0)
break;

ret = snd_soc_pcm_dai_trigger(substream, cmd);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = snd_soc_pcm_dai_trigger(substream, cmd);
if (ret < 0)
break;

ret = snd_soc_pcm_component_trigger(substream, cmd);
if (ret < 0)
break;

ret = snd_soc_link_trigger(substream, cmd);
break;
}
...
}

For example, if soc_pcm_trigger() failed at (*) point,
we need to rollback previous succeeded trigger.

This patch adds trigger mark for DAI/Component/Link,
and do STOP if START/RESUME/PAUSE_RELEASE were failed.

Because it need to use new rollback parameter,
we need to modify DAI/Component/Link trigger functions in the same time.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://lore.kernel.org/r/87a6uycssd.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/soc-component.h
include/sound/soc-dai.h
include/sound/soc-link.h
include/sound/soc.h
sound/soc/soc-component.c
sound/soc/soc-dai.c
sound/soc/soc-link.c
sound/soc/soc-pcm.c

index 9140b5fa19a4cbb630c9d9ba03bb6905729c4be0..0bce41fefd3068f0e17ebf2516ae0a648d31ca1a 100644 (file)
@@ -221,6 +221,7 @@ struct snd_soc_component {
        struct snd_pcm_substream *mark_module;
        struct snd_pcm_substream *mark_open;
        struct snd_pcm_substream *mark_hw_params;
+       struct snd_pcm_substream *mark_trigger;
        struct snd_compr_stream  *mark_compr_open;
        void *mark_pm;
 
@@ -486,7 +487,7 @@ int snd_soc_pcm_component_hw_params(struct snd_pcm_substream *substream,
 void snd_soc_pcm_component_hw_free(struct snd_pcm_substream *substream,
                                   int rollback);
 int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream,
-                                 int cmd);
+                                 int cmd, int rollback);
 int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd,
                                         void *stream);
 void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd,
index 6f54401d3de9cb397d10f806af0070fe80342c33..34d0dbf73ca9c728e694e1deac4e02ca425bb5cc 100644 (file)
@@ -189,7 +189,8 @@ int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order);
 int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order);
 int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd);
 int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream);
-int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                           int rollback);
 int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream,
                                    int cmd);
 
@@ -401,6 +402,7 @@ struct snd_soc_dai {
        /* function mark */
        struct snd_pcm_substream *mark_startup;
        struct snd_pcm_substream *mark_hw_params;
+       struct snd_pcm_substream *mark_trigger;
        struct snd_compr_stream  *mark_compr_startup;
 
        /* bit field */
index 4c68b06d6fe6517eb54cf68cc171bda3e04f7ddc..9314cde1756b93718bdec4899a03bfc846961030 100644 (file)
@@ -21,8 +21,9 @@ int snd_soc_link_hw_params(struct snd_pcm_substream *substream,
                           struct snd_pcm_hw_params *params);
 void snd_soc_link_hw_free(struct snd_pcm_substream *substream,
                          int rollback);
-int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd);
 
+int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd,
+                        int rollback);
 int snd_soc_link_compr_startup(struct snd_compr_stream *cstream);
 void snd_soc_link_compr_shutdown(struct snd_compr_stream *cstream,
                                 int rollback);
index b51e96121fa1c0a86ae58467a87901ad7a5769c1..3fa6c40a63b7eaa121a2c738b9ec4ef000ddff70 100644 (file)
@@ -1042,6 +1042,7 @@ struct snd_soc_pcm_runtime {
        /* function mark */
        struct snd_pcm_substream *mark_startup;
        struct snd_pcm_substream *mark_hw_params;
+       struct snd_pcm_substream *mark_trigger;
        struct snd_compr_stream  *mark_compr_startup;
 
        /* bit field */
index 434987a6435318686e5c05fc18f2d5498cf67b5e..760523382f3c56dbd7f2b6d898309f83a0cb1534 100644 (file)
@@ -1075,22 +1075,51 @@ void snd_soc_pcm_component_hw_free(struct snd_pcm_substream *substream,
        }
 }
 
+static int soc_component_trigger(struct snd_soc_component *component,
+                                struct snd_pcm_substream *substream,
+                                int cmd)
+{
+       int ret = 0;
+
+       if (component->driver->trigger)
+               ret = component->driver->trigger(component, substream, cmd);
+
+       return soc_component_ret(component, ret);
+}
+
 int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream,
-                                 int cmd)
+                                 int cmd, int rollback)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct snd_soc_component *component;
-       int i, ret;
-
-       for_each_rtd_components(rtd, i, component) {
-               if (component->driver->trigger) {
-                       ret = component->driver->trigger(component, substream, cmd);
+       int i, r, ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               for_each_rtd_components(rtd, i, component) {
+                       ret = soc_component_trigger(component, substream, cmd);
                        if (ret < 0)
-                               return soc_component_ret(component, ret);
+                               break;
+                       soc_component_mark_push(component, substream, trigger);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               for_each_rtd_components(rtd, i, component) {
+                       if (rollback && !soc_component_mark_match(component, substream, trigger))
+                               continue;
+
+                       r = soc_component_trigger(component, substream, cmd);
+                       if (r < 0)
+                               ret = r; /* use last ret */
+                       soc_component_mark_pop(component, substream, trigger);
                }
        }
 
-       return 0;
+       return ret;
 }
 
 int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd,
index 9afc6e8c3f9f572ea35ff4fbea833222276f9aa4..cd3bb9a7983f7552ef8426e90398c25e67477bd1 100644 (file)
@@ -564,23 +564,51 @@ int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream)
        return 0;
 }
 
+static int soc_dai_trigger(struct snd_soc_dai *dai,
+                          struct snd_pcm_substream *substream, int cmd)
+{
+       int ret = 0;
+
+       if (dai->driver->ops &&
+           dai->driver->ops->trigger)
+               ret = dai->driver->ops->trigger(substream, cmd, dai);
+
+       return soc_dai_ret(dai, ret);
+}
+
 int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream,
-                           int cmd)
+                           int cmd, int rollback)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct snd_soc_dai *dai;
-       int i, ret;
+       int i, r, ret = 0;
 
-       for_each_rtd_dais(rtd, i, dai) {
-               if (dai->driver->ops &&
-                   dai->driver->ops->trigger) {
-                       ret = dai->driver->ops->trigger(substream, cmd, dai);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               for_each_rtd_dais(rtd, i, dai) {
+                       ret = soc_dai_trigger(dai, substream, cmd);
                        if (ret < 0)
-                               return soc_dai_ret(dai, ret);
+                               break;
+                       soc_dai_mark_push(dai, substream, trigger);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               for_each_rtd_dais(rtd, i, dai) {
+                       if (rollback && !soc_dai_mark_match(dai, substream, trigger))
+                               continue;
+
+                       r = soc_dai_trigger(dai, substream, cmd);
+                       if (r < 0)
+                               ret = r; /* use last ret */
+                       soc_dai_mark_pop(dai, substream, trigger);
                }
        }
 
-       return 0;
+       return ret;
 }
 
 int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream,
index 26cc60f8dcfb7d50face81ac2c2249af0bd8460b..619664cc9ab95a83d2ca41d99da7095e9b288626 100644 (file)
@@ -141,7 +141,7 @@ void snd_soc_link_hw_free(struct snd_pcm_substream *substream, int rollback)
        soc_link_mark_pop(rtd, substream, hw_params);
 }
 
-int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd)
+static int soc_link_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        int ret = 0;
@@ -153,6 +153,34 @@ int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd)
        return soc_link_ret(rtd, ret);
 }
 
+int snd_soc_link_trigger(struct snd_pcm_substream *substream, int cmd,
+                        int rollback)
+{
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = soc_link_trigger(substream, cmd);
+               if (ret < 0)
+                       break;
+               soc_link_mark_push(rtd, substream, trigger);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (rollback && !soc_link_mark_match(rtd, substream, trigger))
+                       break;
+
+               ret = soc_link_trigger(substream, cmd);
+               soc_link_mark_pop(rtd, substream, startup);
+       }
+
+       return ret;
+}
+
 int snd_soc_link_compr_startup(struct snd_compr_stream *cstream)
 {
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
index 707c1a49f6b2f12c276c6b3443a26dabb91f4cd4..ee51dc7fd8937b0d0c886f3b2f19b680db04b5f1 100644 (file)
@@ -1012,37 +1012,61 @@ out:
 
 static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
-       int ret = -EINVAL;
+       int ret = -EINVAL, _ret = 0;
+       int rollback = 0;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               ret = snd_soc_link_trigger(substream, cmd);
+               ret = snd_soc_link_trigger(substream, cmd, 0);
                if (ret < 0)
-                       break;
+                       goto start_err;
+
+               ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
+               if (ret < 0)
+                       goto start_err;
 
-               ret = snd_soc_pcm_component_trigger(substream, cmd);
+               ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
+start_err:
                if (ret < 0)
+                       rollback = 1;
+       }
+
+       if (rollback) {
+               _ret = ret;
+               switch (cmd) {
+               case SNDRV_PCM_TRIGGER_START:
+                       cmd = SNDRV_PCM_TRIGGER_STOP;
+                       break;
+               case SNDRV_PCM_TRIGGER_RESUME:
+                       cmd = SNDRV_PCM_TRIGGER_SUSPEND;
+                       break;
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+                       cmd = SNDRV_PCM_TRIGGER_PAUSE_PUSH;
                        break;
+               }
+       }
 
-               ret = snd_soc_pcm_dai_trigger(substream, cmd);
-               break;
+       switch (cmd) {
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               ret = snd_soc_pcm_dai_trigger(substream, cmd);
+               ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback);
                if (ret < 0)
                        break;
 
-               ret = snd_soc_pcm_component_trigger(substream, cmd);
+               ret = snd_soc_pcm_component_trigger(substream, cmd, rollback);
                if (ret < 0)
                        break;
 
-               ret = snd_soc_link_trigger(substream, cmd);
+               ret = snd_soc_link_trigger(substream, cmd, rollback);
                break;
        }
 
+       if (_ret)
+               ret = _ret;
+
        return ret;
 }