diff mbox

[02/21] ASoC: rsnd: don't call snd_pcm_period_elapsed() under spin lock

Message ID 87r3pdv6ic.wl%kuninori.morimoto.gx@renesas.com (mailing list archive)
State Accepted
Commit 75defee0f1b3fcd91d8a304d6444635a459b8249
Headers show

Commit Message

Kuninori Morimoto June 15, 2015, 6:21 a.m. UTC
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

'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>
---
 sound/soc/sh/rcar/core.c | 20 ++++++++++++++++++--
 sound/soc/sh/rcar/dma.c  | 11 ++++++++++-
 sound/soc/sh/rcar/rsnd.h |  3 ++-
 sound/soc/sh/rcar/ssi.c  |  6 +++++-
 4 files changed, 35 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index d460d2a..027b043 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -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,
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 144308f..ac19ab1 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -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)
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 03ff071..e37234e 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -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);
 
 /*
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 8569173..2548321 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -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;
 }