From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
clk_prepare_enable/unprepare() uses mutex inside, and it uses __schedule(). Then, raw_spin_lock/unlock_irq() is called, and it breaks Renesas sound driver's spin lock irq. This patch moves clk_prepare_enable/unprepare to out of spin lock area. Special thanks to Das Biju.
Reported-by: Das Biju biju.das@bp.renesas.com Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v2 -> v3
- no change
sound/soc/sh/rcar/core.c | 29 ++++++++++++++++++++++++++++- sound/soc/sh/rcar/dvc.c | 12 +----------- sound/soc/sh/rcar/rsnd.h | 9 +++++++-- sound/soc/sh/rcar/src.c | 6 ++---- sound/soc/sh/rcar/ssi.c | 22 ++++++++++++++++++---- 5 files changed, 56 insertions(+), 22 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 54042d9..e6c1c7a4 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -190,7 +190,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 mask = 1 << __rsnd_mod_shift_##func; \ + u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ int ret = 0; \ if ((mod->status & mask) == call) { \ @@ -222,6 +222,27 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ret; \ })
+int rsnd_mod_clk(struct rsnd_mod *mod, int enable) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* + * clk_prepare_enable/unprepare() should not be called + * from inside spin lock + */ + if (enable) + clk_prepare_enable(mod->clk); + else + clk_disable_unprepare(mod->clk); + + dev_dbg(dev, "%s[%d] clk %s\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + enable ? "ON" : "OFF"); + + return 0; +} + static int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { @@ -345,6 +366,9 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, int ret; unsigned long flags;
+ if (cmd == SNDRV_PCM_TRIGGER_START) + rsnd_dai_call(clk, io, 1); + rsnd_lock(priv, flags);
switch (cmd) { @@ -385,6 +409,9 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, dai_trigger_end: rsnd_unlock(priv, flags);
+ if (cmd == SNDRV_PCM_TRIGGER_STOP) + rsnd_dai_call(clk, io, 0); + return ret; }
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index aeeef13..9420349 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -166,8 +166,6 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, return -EINVAL; }
- rsnd_mod_hw_start(dvc_mod); - /* * fixme * it doesn't support CTU/MIX @@ -191,14 +189,6 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, return 0; }
-static int rsnd_dvc_quit(struct rsnd_mod *mod, - struct rsnd_priv *priv) -{ - rsnd_mod_hw_stop(mod); - - return 0; -} - static int rsnd_dvc_start(struct rsnd_mod *mod, struct rsnd_priv *priv) { @@ -286,9 +276,9 @@ static struct rsnd_mod_ops rsnd_dvc_ops = { .probe = rsnd_dvc_probe_gen2, .remove = rsnd_dvc_remove_gen2, .init = rsnd_dvc_init, - .quit = rsnd_dvc_quit, .start = rsnd_dvc_start, .stop = rsnd_dvc_stop, + .clk = rsnd_mod_clk, .pcm_new = rsnd_dvc_pcm_new, };
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 52c401c..40a12f4 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -237,6 +237,7 @@ struct rsnd_mod_ops { struct rsnd_priv *priv); int (*stop)(struct rsnd_mod *mod, struct rsnd_priv *priv); + int (*clk)(struct rsnd_mod *mod, int enable); int (*pcm_new)(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd); int (*fallback)(struct rsnd_mod *mod, @@ -262,6 +263,9 @@ struct rsnd_mod { * 2 0: start 1: stop * 3 0: pcm_new * 4 0: fallback + * + * 31 bit is always called (see __rsnd_mod_call) + * 31 0: clk */ #define __rsnd_mod_shift_probe 0 #define __rsnd_mod_shift_remove 0 @@ -271,6 +275,7 @@ struct rsnd_mod { #define __rsnd_mod_shift_stop 2 #define __rsnd_mod_shift_pcm_new 3 #define __rsnd_mod_shift_fallback 4 +#define __rsnd_mod_shift_clk 31 /* always called */
#define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 1 @@ -280,13 +285,12 @@ struct rsnd_mod { #define __rsnd_mod_call_stop 1 #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 +#define __rsnd_mod_call_clk 0
#define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) #define rsnd_mod_to_dma(mod) (&(mod)->dma) #define rsnd_mod_to_io(mod) ((mod)->io) #define rsnd_mod_id(mod) ((mod)->id) -#define rsnd_mod_hw_start(mod) clk_prepare_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable_unprepare((mod)->clk)
void rsnd_mod_init(struct rsnd_mod *mod, struct rsnd_mod_ops *ops, @@ -294,6 +298,7 @@ void rsnd_mod_init(struct rsnd_mod *mod, enum rsnd_mod_type type, int id); char *rsnd_mod_name(struct rsnd_mod *mod); +int rsnd_mod_clk(struct rsnd_mod *mod, int enable); struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
/* diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index cc93f32..83032ee 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -292,8 +292,6 @@ static int rsnd_src_init(struct rsnd_mod *mod) { struct rsnd_src *src = rsnd_mod_to_src(mod);
- rsnd_mod_hw_start(mod); - src->err = 0;
/* @@ -311,8 +309,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv);
- rsnd_mod_hw_stop(mod); - if (src->err) dev_warn(dev, "%s[%d] under/over flow err = %d\n", rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); @@ -523,6 +519,7 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = { .quit = rsnd_src_quit, .start = rsnd_src_start_gen1, .stop = rsnd_src_stop_gen1, + .clk = rsnd_mod_clk, };
/* @@ -803,6 +800,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = { .quit = rsnd_src_quit, .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, + .clk = rsnd_mod_clk, };
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 060d3d2..583ca97 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -176,6 +176,22 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) rsnd_adg_ssi_clk_stop(&ssi->mod); }
+static int rsnd_ssi_clk(struct rsnd_mod *mod, int enable) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + + rsnd_mod_clk(mod, enable); + + if (rsnd_rdai_is_clk_master(rdai)) { + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_mod_clk(&ssi->parent->mod, enable); + } + + return 0; +} + static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, struct rsnd_dai_stream *io) { @@ -186,8 +202,6 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, u32 cr;
if (0 == ssi->usrcnt) { - rsnd_mod_hw_start(&ssi->mod); - if (rsnd_rdai_is_clk_master(rdai)) { if (rsnd_ssi_clk_from_parent(ssi)) rsnd_ssi_hw_start(ssi->parent, io); @@ -258,8 +272,6 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) else rsnd_ssi_master_clk_stop(ssi); } - - rsnd_mod_hw_stop(&ssi->mod); }
dev_dbg(dev, "%s[%d] hw stopped\n", @@ -462,6 +474,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .clk = rsnd_ssi_clk, };
static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, @@ -581,6 +594,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_dma_start, .stop = rsnd_ssi_dma_stop, + .clk = rsnd_ssi_clk, .fallback = rsnd_ssi_fallback, };