ASoC: rsnd: don't call snd_pcm_period_elapsed() under spin lock
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Mon, 15 Jun 2015 06:21:15 +0000 (06:21 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 16 Jun 2015 11:34:01 +0000 (12:34 +0100)
'a9e1ac1a9e4585b5("ASoC: rsnd: spin lock for interrupt handler")'
added spin lock under interrupt handler to solve HW restart issue.

OTOH, current rsnd driver calls snd_pcm_period_elapsed() from
rsnd_dai_pointer_update(). but, it will be called under spin lock
if SSI was PIO mode.

If it was called under spin lock, it will call
snd_pcm_update_state() -> snd_pcm_drain_done().
Then, it calls rsnd_soc_dai_trigger() and will be dead-lock.
This patch doesn't call rsnd_dai_pointer_update() under spin lock

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: Keita Kobayashi <keita.kobayashi.ym@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/ssi.c

index d460d2aa82ee6743e4a4ef6d1c55149411cc4478..027b04392674ef0a63d62b5a17f4dcce2a46f8ce 100644 (file)
@@ -302,7 +302,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
        return pos;
 }
 
-void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
+bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
 {
        io->byte_pos += byte;
 
@@ -319,8 +319,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
                        io->next_period_byte = io->byte_per_period;
                }
 
-               snd_pcm_period_elapsed(substream);
+               return true;
        }
+
+       return false;
+}
+
+void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
+{
+       struct snd_pcm_substream *substream = io->substream;
+
+       /*
+        * this function should be called...
+        *
+        * - if rsnd_dai_pointer_update() returns true
+        * - without spin lock
+        */
+
+       snd_pcm_period_elapsed(substream);
 }
 
 static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
index ac3756f6af603e9a21fe79988b00d13477da6fab..9034f951adfe808c8ab5a1fd90fa107388cca2ac 100644 (file)
@@ -36,7 +36,10 @@ static void rsnd_dmaen_complete(void *data)
 {
        struct rsnd_dma *dma = (struct rsnd_dma *)data;
        struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       bool elapsed = false;
+       unsigned long flags;
 
        /*
         * Renesas sound Gen1 needs 1 DMAC,
@@ -49,8 +52,14 @@ static void rsnd_dmaen_complete(void *data)
         * rsnd_dai_pointer_update() will be called twice,
         * ant it will breaks io->byte_pos
         */
+       spin_lock_irqsave(&priv->lock, flags);
+
+       elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
 
-       rsnd_dai_pointer_update(io, io->byte_per_period);
+       if (elapsed)
+               rsnd_dai_period_elapsed(io);
 }
 
 static void rsnd_dmaen_stop(struct rsnd_dma *dma)
index 03ff071d012f1277a588213546af345face488e8..e37234ea18e61daed23ac892053901b51275eb50 100644 (file)
@@ -355,7 +355,8 @@ struct rsnd_dai {
 
 struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
 
-void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
 
 /*
index 856917392b790fa89439fb8b2b11364d15140618..25483211a349f86ec0093aa16988577836a5c125 100644 (file)
@@ -426,6 +426,7 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        int is_dma = rsnd_ssi_is_dma_mode(mod);
        u32 status;
+       bool elapsed = false;
 
        spin_lock(&priv->lock);
 
@@ -451,7 +452,7 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
                else
                        *buf = rsnd_mod_read(mod, SSIRDR);
 
-               rsnd_dai_pointer_update(io, sizeof(*buf));
+               elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
        }
 
        /* DMA only */
@@ -476,6 +477,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 rsnd_ssi_interrupt_out:
        spin_unlock(&priv->lock);
 
+       if (elapsed)
+               rsnd_dai_period_elapsed(io);
+
        return IRQ_HANDLED;
 }