From 75defee0f1b3fcd91d8a304d6444635a459b8249 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 15 Jun 2015 06:21:15 +0000 Subject: 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 Tested-by: Keita Kobayashi Signed-off-by: Mark Brown --- 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(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index d460d2aa82ee..027b04392674 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 ac3756f6af60..9034f951adfe 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 03ff071d012f..e37234ea18e6 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 856917392b79..25483211a349 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; } -- cgit v1.2.1