blob: 3f34f4ec9e695c3fc14d7a15e8c8d19b9ba326c2 [file] [log] [blame]
From 8ea1c6a4bcd03589fe3519d74d673198475d5271 Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Date: Mon, 15 Jun 2015 06:21:15 +0000
Subject: [PATCH 109/129] ASoC: rsnd: don't call snd_pcm_period_elapsed() under
spin lock
'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>
(cherry picked from commit 75defee0f1b3fcd91d8a304d6444635a459b8249)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
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(-)
--- 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_
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
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,
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -36,7 +36,10 @@ static void rsnd_dmaen_complete(void *da
{
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 *da
* rsnd_dai_pointer_update() will be called twice,
* ant it will breaks io->byte_pos
*/
+ spin_lock_irqsave(&priv->lock, flags);
- rsnd_dai_pointer_update(io, io->byte_per_period);
+ elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (elapsed)
+ rsnd_dai_period_elapsed(io);
}
static void rsnd_dmaen_stop(struct rsnd_dma *dma)
--- 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);
/*
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -427,6 +427,7 @@ static irqreturn_t rsnd_ssi_interrupt(in
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);
@@ -459,7 +460,7 @@ static irqreturn_t rsnd_ssi_interrupt(in
else
*buf = (rsnd_mod_read(mod, SSIRDR) >> shift);
- rsnd_dai_pointer_update(io, sizeof(*buf));
+ elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
}
/* DMA only */
@@ -484,6 +485,9 @@ static irqreturn_t rsnd_ssi_interrupt(in
rsnd_ssi_interrupt_out:
spin_unlock(&priv->lock);
+ if (elapsed)
+ rsnd_dai_period_elapsed(io);
+
return IRQ_HANDLED;
}