[PATCH] ASoC: rsnd: stop all working stream when .remove

Kuninori Morimoto
2017-09-01T04:34:51Z

From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

Driver should stop all working stream when .remove timing.
Current Renesas sound driver is assuming that all stream was
stopped when .remove but it was wrong.
This patch stops all working stream when .remove, otherwise
kernel will get damage for example in below case.
Special thanks to Truong, Hiep

> cd /sys/bus/platform/drivers/rcar_sound
> aplay xxx.wav &
> echo ec500000.sound > unbind

Reported-by: Hiep Cao Minh <cm-hiep@jinso.co.jp>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
sound/soc/sh/rcar/core.c | 68 ++++++++++++++++++++++++++++++++++--------------
1 file changed, 48 insertions(+), 20 deletions(-)

diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 1071332..1bf472f 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -197,12 +197,6 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
}
}

-int rsnd_io_is_working(struct rsnd_dai_stream *io)
-{
- /* see rsnd_dai_stream_init/quit() */
- return !!io->substream;
-}
-
int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
@@ -610,20 +604,24 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
return &rdai->capture;
}

-static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+int rsnd_io_is_working(struct rsnd_dai_stream *io)
+{
+ /* see rsnd_dai_stream_init/quit() */
+ return !!io->substream;
+}
+
+#define rsnd_io_start(priv, io, sub) rsnd_io_operation(priv, io, sub)
+#define rsnd_io_stop(priv, io) rsnd_io_operation(priv, io, NULL)
+static int rsnd_io_operation(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream)
{
- struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
- struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
- struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
int ret;
unsigned long flags;

spin_lock_irqsave(&priv->lock, flags);

- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream) {
rsnd_dai_stream_init(io, substream);

ret = rsnd_dai_call(init, io, priv);
@@ -638,9 +636,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
if (ret < 0)
goto dai_trigger_end;

- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
+ } else {
ret = rsnd_dai_call(irq, io, priv, 0);

ret |= rsnd_dai_call(stop, io, priv);
@@ -648,9 +644,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret |= rsnd_dai_call(quit, io, priv);

rsnd_dai_stream_quit(io);
- break;
- default:
- ret = -EINVAL;
}

dai_trigger_end:
@@ -659,6 +652,30 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}

+static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = rsnd_io_start(priv, io, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = rsnd_io_stop(priv, io);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
@@ -1477,6 +1494,17 @@ static int rsnd_remove(struct platform_device *pdev)
};
int ret = 0, i;

+ /*
+ * Stop all working io
+ */
+ for_each_rsnd_dai(rdai, priv, i) {
+ if (rsnd_io_is_working(&rdai->playback))
+ rsnd_io_stop(priv, &rdai->playback);
+
+ if (rsnd_io_is_working(&rdai->capture))
+ rsnd_io_stop(priv, &rdai->capture);
+ }
+
pm_runtime_disable(&pdev->dev);

for_each_rsnd_dai(rdai, priv, i) {
--
1.9.1