summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2019-08-06 12:45:38 +0900
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-10-05 12:47:48 +0200
commitbba121ed9c7e29ab443ecf1e44fa802192770129 (patch)
treed32c4f5e2b99371aff25cdf4b3712b8faf3b9d69 /sound
parent612791cae25e466d57b483949b5e763436b2d93a (diff)
downloadlinux-stable-bba121ed9c7e29ab443ecf1e44fa802192770129.tar.gz
linux-stable-bba121ed9c7e29ab443ecf1e44fa802192770129.tar.bz2
linux-stable-bba121ed9c7e29ab443ecf1e44fa802192770129.zip
ASoC: rsnd: don't call clk_get_rate() under atomic context
[ Upstream commit 06e8f5c842f2dbb232897ba967ea7b422745c271 ] ADG is using clk_get_rate() under atomic context, thus, we might have scheduling issue. To avoid this issue, we need to get/keep clk rate under non atomic context. We need to handle ADG as special device at Renesas Sound driver. From SW point of view, we want to impletent it as rsnd_mod_ops :: prepare, but it makes code just complicate. To avoid complicated code/patch, this patch adds new clk_rate[] array, and keep clk IN rate when rsnd_adg_clk_enable() was called. Reported-by: Leon Kong <Leon.KONG@cn.bosch.com> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Tested-by: Leon Kong <Leon.KONG@cn.bosch.com> Link: https://lore.kernel.org/r/87v9vb0xkp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/sh/rcar/adg.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index eb7879bcc6a7..686401bcd1f5 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -33,6 +33,7 @@ struct rsnd_adg {
struct clk *clkout[CLKOUTMAX];
struct clk_onecell_data onecell;
struct rsnd_mod mod;
+ int clk_rate[CLKMAX];
u32 flags;
u32 ckr;
u32 rbga;
@@ -110,9 +111,9 @@ static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
unsigned int val, en;
unsigned int min, diff;
unsigned int sel_rate[] = {
- clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
- clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
- clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
+ adg->clk_rate[CLKA], /* 0000: CLKA */
+ adg->clk_rate[CLKB], /* 0001: CLKB */
+ adg->clk_rate[CLKC], /* 0010: CLKC */
adg->rbga_rate_for_441khz, /* 0011: RBGA */
adg->rbgb_rate_for_48khz, /* 0100: RBGB */
};
@@ -328,7 +329,7 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
* AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
*/
for_each_rsnd_clk(clk, adg, i) {
- if (rate == clk_get_rate(clk))
+ if (rate == adg->clk_rate[i])
return sel_table[i];
}
@@ -394,10 +395,18 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
for_each_rsnd_clk(clk, adg, i) {
ret = 0;
- if (enable)
+ if (enable) {
ret = clk_prepare_enable(clk);
- else
+
+ /*
+ * We shouldn't use clk_get_rate() under
+ * atomic context. Let's keep it when
+ * rsnd_adg_clk_enable() was called
+ */
+ adg->clk_rate[i] = clk_get_rate(adg->clk[i]);
+ } else {
clk_disable_unprepare(clk);
+ }
if (ret < 0)
dev_warn(dev, "can't use clk %d\n", i);