| From bed00860b77299f7ef2085554647aa83671d470b Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Thu, 21 May 2015 03:50:23 +0000 |
| Subject: [PATCH 106/129] ASoC: rsnd: spin lock for interrupt handler |
| |
| Renesas R-Car driver interrupt handler was not locked before. |
| But now, SSI/SRC interrupt handler calls restart function |
| which should be called under spin lock. |
| Below error might happen witout this patch. |
| |
| Unable to handle kernel NULL pointer dereference at virtual address 00000048 |
| pgd = edfac000 |
| [00000048] *pgd=6e0f0831, *pte=00000000, *ppte=00000000 |
| Internal error: Oops: 17 [#1] SMP ARM |
| CPU: 0 PID: 2009 Comm: aplay Not tainted 4.1.0-rc2-dirty #4 |
| Hardware name: Generic R8A7790 (Flattened Device Tree) |
| task: eeac9040 ti: eebe8000 task.ti: eebe8000 |
| PC is at rsnd_get_adinr+0x28/0x60 |
| LR is at rsnd_src_ssiu_start+0xdc/0x19c |
| pc : [<c0409790>] lr : [<c040c068>] psr: a0000193 |
| sp : eebe9e58 ip : eebe9e68 fp : eebe9e64 |
| r10: c06ed9d0 r9 : ee919d10 r8 : 00000001 |
| r7 : 00000001 r6 : ee1cb090 r5 : 00000000 r4 : edcaa418 |
| r3 : 00000000 r2 : eea8ce00 r1 : 80000193 r0 : edcaa418 |
| ... |
| |
| Reported-by: Cao Minh Hiep <cm-hiep@jinso.co.jp> |
| Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Tested-by: Keita Kobayashi <keita.kobayashi.ym@renesas.com> |
| Tested by: Cao Minh Hiep <cm-hiep@jinso.co.jp> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| |
| (cherry picked from commit 02299d9875bab5b1e9d87ce9ae4aecf537eb12a4) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| sound/soc/sh/rcar/core.c | 12 ++++++++++-- |
| sound/soc/sh/rcar/rsnd.h | 3 +-- |
| sound/soc/sh/rcar/src.c | 11 ++++++++--- |
| sound/soc/sh/rcar/ssi.c | 14 +++++++++++--- |
| 4 files changed, 30 insertions(+), 10 deletions(-) |
| |
| --- a/sound/soc/sh/rcar/core.c |
| +++ b/sound/soc/sh/rcar/core.c |
| @@ -170,6 +170,14 @@ void rsnd_mod_quit(struct rsnd_mod *mod) |
| clk_unprepare(mod->clk); |
| } |
| |
| +int rsnd_mod_is_working(struct rsnd_mod *mod) |
| +{ |
| + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); |
| + |
| + /* see rsnd_dai_stream_init/quit() */ |
| + return !!io->substream; |
| +} |
| + |
| /* |
| * settting function |
| */ |
| @@ -362,7 +370,7 @@ static int rsnd_soc_dai_trigger(struct s |
| int ret; |
| unsigned long flags; |
| |
| - rsnd_lock(priv, flags); |
| + spin_lock_irqsave(&priv->lock, flags); |
| |
| switch (cmd) { |
| case SNDRV_PCM_TRIGGER_START: |
| @@ -400,7 +408,7 @@ static int rsnd_soc_dai_trigger(struct s |
| } |
| |
| dai_trigger_end: |
| - rsnd_unlock(priv, flags); |
| + spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return ret; |
| } |
| --- a/sound/soc/sh/rcar/rsnd.h |
| +++ b/sound/soc/sh/rcar/rsnd.h |
| @@ -303,6 +303,7 @@ int rsnd_mod_init(struct rsnd_mod *mod, |
| int id); |
| void rsnd_mod_quit(struct rsnd_mod *mod); |
| char *rsnd_mod_name(struct rsnd_mod *mod); |
| +int rsnd_mod_is_working(struct rsnd_mod *mod); |
| struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); |
| |
| /* |
| @@ -449,8 +450,6 @@ struct rsnd_priv { |
| #define rsnd_priv_to_pdev(priv) ((priv)->pdev) |
| #define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) |
| #define rsnd_priv_to_info(priv) ((priv)->info) |
| -#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) |
| -#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) |
| |
| /* |
| * rsnd_kctrl |
| --- a/sound/soc/sh/rcar/src.c |
| +++ b/sound/soc/sh/rcar/src.c |
| @@ -673,10 +673,13 @@ static int _rsnd_src_stop_gen2(struct rs |
| static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) |
| { |
| struct rsnd_mod *mod = data; |
| - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); |
| + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| |
| - if (!io) |
| - return IRQ_NONE; |
| + spin_lock(&priv->lock); |
| + |
| + /* ignore all cases if not working */ |
| + if (!rsnd_mod_is_working(mod)) |
| + goto rsnd_src_interrupt_gen2_out; |
| |
| if (rsnd_src_error_record_gen2(mod)) { |
| struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| @@ -692,6 +695,8 @@ static irqreturn_t rsnd_src_interrupt_ge |
| else |
| dev_warn(dev, "no more SRC restart\n"); |
| } |
| +rsnd_src_interrupt_gen2_out: |
| + spin_unlock(&priv->lock); |
| |
| return IRQ_HANDLED; |
| } |
| --- a/sound/soc/sh/rcar/ssi.c |
| +++ b/sound/soc/sh/rcar/ssi.c |
| @@ -424,10 +424,15 @@ static irqreturn_t rsnd_ssi_interrupt(in |
| struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
| struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); |
| int is_dma = rsnd_ssi_is_dma_mode(mod); |
| - u32 status = rsnd_mod_read(mod, SSISR); |
| + u32 status; |
| |
| - if (!io) |
| - return IRQ_NONE; |
| + spin_lock(&priv->lock); |
| + |
| + /* ignore all cases if not working */ |
| + if (!rsnd_mod_is_working(mod)) |
| + goto rsnd_ssi_interrupt_out; |
| + |
| + status = rsnd_mod_read(mod, SSISR); |
| |
| /* PIO only */ |
| if (!is_dma && (status & DIRQ)) { |
| @@ -474,6 +479,9 @@ static irqreturn_t rsnd_ssi_interrupt(in |
| |
| rsnd_ssi_record_error(ssi, status); |
| |
| +rsnd_ssi_interrupt_out: |
| + spin_unlock(&priv->lock); |
| + |
| return IRQ_HANDLED; |
| } |
| |