ASoC: add new trigger ordering method
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 8 Jun 2023 06:48:36 +0000 (06:48 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 13 Jun 2023 11:11:19 +0000 (12:11 +0100)
Current ASoC is assuming that trigger starting order is
Link -> Component -> DAI as default, and its reverse order for stopping.
But some Driver / Card want to reorder it for some reasons.
We have such flags, but is unbalance like below.

struct snd_soc_component_driver :: start_dma_last
struct snd_soc_dai_link :: stop_dma_first

We want to have more flexible, and more generic method.
This patch adds new snd_soc_trigger_order for start/stop at
component / DAI-link.

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

index 0b47603..c773338 100644 (file)
@@ -159,6 +159,15 @@ struct snd_soc_component_driver {
        int remove_order;
 
        /*
+        * soc_pcm_trigger() start/stop sequence.
+        * see also
+        *      snd_soc_dai_link
+        *      soc_pcm_trigger()
+        */
+       enum snd_soc_trigger_order trigger_start;
+       enum snd_soc_trigger_order trigger_stop;
+
+       /*
         * signal if the module handling the component should not be removed
         * if a pcm is open. Setting this would prevent the module
         * refcount being incremented in probe() but allow it be incremented
index 10e4ea0..4944258 100644 (file)
@@ -607,6 +607,14 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
 int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 
+enum snd_soc_trigger_order {
+                                               /* start                        stop                 */
+       SND_SOC_TRIGGER_ORDER_DEFAULT   = 0,    /* Link->Component->DAI         DAI->Component->Link */
+       SND_SOC_TRIGGER_ORDER_LDC,              /* Link->DAI->Component         Component->DAI->Link */
+
+       SND_SOC_TRIGGER_ORDER_MAX,
+};
+
 /* SoC PCM stream information */
 struct snd_soc_pcm_stream {
        const char *stream_name;
@@ -707,6 +715,15 @@ struct snd_soc_dai_link {
        const struct snd_soc_ops *ops;
        const struct snd_soc_compr_ops *compr_ops;
 
+       /*
+        * soc_pcm_trigger() start/stop sequence.
+        * see also
+        *      snd_soc_component_driver
+        *      soc_pcm_trigger()
+        */
+       enum snd_soc_trigger_order trigger_start;
+       enum snd_soc_trigger_order trigger_stop;
+
        /* Mark this pcm with non atomic ops */
        unsigned int nonatomic:1;
 
index 799865a..a10c928 100644 (file)
@@ -1071,49 +1071,77 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        return ret;
 }
 
+#define TRIGGER_MAX 3
+static int (* const trigger[][TRIGGER_MAX])(struct snd_pcm_substream *substream, int cmd, int rollback) = {
+       [SND_SOC_TRIGGER_ORDER_DEFAULT] = {
+               snd_soc_link_trigger,
+               snd_soc_pcm_component_trigger,
+               snd_soc_pcm_dai_trigger,
+       },
+       [SND_SOC_TRIGGER_ORDER_LDC] = {
+               snd_soc_link_trigger,
+               snd_soc_pcm_dai_trigger,
+               snd_soc_pcm_component_trigger,
+       },
+};
+
 static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
        struct snd_soc_component *component;
-       int ret = -EINVAL, _ret = 0, start_dma_last = 0, i;
+       int ret = 0, r = 0, i;
        int rollback = 0;
+       int start = 0, stop = 0;
 
+       /*
+        * select START/STOP sequence
+        */
+       for_each_rtd_components(rtd, i, component) {
+               if (component->driver->trigger_start)
+                       start = component->driver->trigger_start;
+               if (component->driver->trigger_stop)
+                       stop = component->driver->trigger_stop;
+       }
+       if (rtd->dai_link->trigger_start)
+               start = rtd->dai_link->trigger_start;
+       if (rtd->dai_link->trigger_stop)
+               stop  = rtd->dai_link->trigger_stop;
+
+       if (start < 0 || start >= SND_SOC_TRIGGER_ORDER_MAX ||
+           stop  < 0 || stop  >= SND_SOC_TRIGGER_ORDER_MAX)
+               return -EINVAL;
+
+       /* REMOVE ME */
+       for_each_rtd_components(rtd, i, component) {
+               if (component->driver->start_dma_last) {
+                       start = SND_SOC_TRIGGER_ORDER_LDC;
+                       break;
+               }
+       }
+       if (rtd->dai_link->stop_dma_first)
+               stop = SND_SOC_TRIGGER_ORDER_LDC;
+
+       /*
+        * START
+        */
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               /* Do we need to start dma last? */
-               for_each_rtd_components(rtd, i, component) {
-                       if (component->driver->start_dma_last) {
-                               start_dma_last = 1;
+               for (i = 0; i < TRIGGER_MAX; i++) {
+                       r = trigger[start][i](substream, cmd, 0);
+                       if (r < 0)
                                break;
-                       }
-               }
-
-               ret = snd_soc_link_trigger(substream, cmd, 0);
-               if (ret < 0)
-                       goto start_err;
-
-               if (start_dma_last) {
-                       ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
-                       if (ret < 0)
-                               goto start_err;
-
-                       ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
-               } else {
-                       ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
-                       if (ret < 0)
-                               goto start_err;
-
-                       ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
                }
-start_err:
-               if (ret < 0)
-                       rollback = 1;
        }
 
-       if (rollback) {
-               _ret = ret;
+       /*
+        * Rollback if START failed
+        * find correspond STOP command
+        */
+       if (r < 0) {
+               rollback = 1;
+               ret = r;
                switch (cmd) {
                case SNDRV_PCM_TRIGGER_START:
                        cmd = SNDRV_PCM_TRIGGER_STOP;
@@ -1127,34 +1155,20 @@ start_err:
                }
        }
 
+       /*
+        * STOP
+        */
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (rtd->dai_link->stop_dma_first) {
-                       ret = snd_soc_pcm_component_trigger(substream, cmd, rollback);
-                       if (ret < 0)
-                               break;
-
-                       ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback);
-                       if (ret < 0)
-                               break;
-               } else {
-                       ret = snd_soc_pcm_dai_trigger(substream, cmd, rollback);
-                       if (ret < 0)
-                               break;
-
-                       ret = snd_soc_pcm_component_trigger(substream, cmd, rollback);
-                       if (ret < 0)
-                               break;
+               for (i = TRIGGER_MAX; i > 0; i--) {
+                       r = trigger[stop][i - 1](substream, cmd, rollback);
+                       if (r < 0)
+                               ret = r;
                }
-               ret = snd_soc_link_trigger(substream, cmd, rollback);
-               break;
        }
 
-       if (_ret)
-               ret = _ret;
-
        return ret;
 }