ASoC: add new trigger ordering method

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>
This commit is contained in:
Kuninori Morimoto 2023-06-08 06:48:36 +00:00 committed by Mark Brown
parent 597d364cd7
commit 356caf663d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 90 additions and 50 deletions

View File

@ -158,6 +158,15 @@ struct snd_soc_component_driver {
int probe_order;
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

View 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;

View 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;
}