summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/madera.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/madera.c')
-rw-r--r--sound/soc/codecs/madera.c165
1 files changed, 153 insertions, 12 deletions
diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c
index b537e60ef7c0..40de9d7811d1 100644
--- a/sound/soc/codecs/madera.c
+++ b/sound/soc/codecs/madera.c
@@ -163,6 +163,48 @@ static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = {
MADERA_IRQ_DSP7_BUS_ERR,
};
+int madera_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct madera_priv *priv = snd_soc_component_get_drvdata(component);
+ struct madera *madera = priv->madera;
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(madera->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(madera->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) {
+ case MADERA_CLK_SRC_MCLK1:
+ clk_idx = MADERA_MCLK1;
+ break;
+ case MADERA_CLK_SRC_MCLK2:
+ clk_idx = MADERA_MCLK2;
+ break;
+ case MADERA_CLK_SRC_MCLK3:
+ clk_idx = MADERA_MCLK3;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(madera->mclk[clk_idx].clk);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(madera->mclk[clk_idx].clk);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(madera_clk_ev);
+
static void madera_spin_sysclk(struct madera_priv *priv)
{
struct madera *madera = priv->madera;
@@ -193,9 +235,16 @@ int madera_sysclk_ev(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct madera_priv *priv = snd_soc_component_get_drvdata(component);
- madera_spin_sysclk(priv);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ madera_spin_sysclk(priv);
+ break;
+ default:
+ break;
+ }
- return 0;
+ return madera_clk_ev(w, kcontrol, event);
}
EXPORT_SYMBOL_GPL(madera_sysclk_ev);
@@ -526,6 +575,7 @@ int madera_out1_demux_put(struct snd_kcontrol *kcontrol,
usleep_range(2000, 3000); /* wait for wseq to complete */
/* change demux setting */
+ ret = 0;
if (madera->out_clamp[0])
ret = regmap_update_bits(madera->regmap,
MADERA_OUTPUT_ENABLES_1,
@@ -3816,11 +3866,75 @@ static bool madera_set_fll_phase_integrator(struct madera_fll *fll,
return reg_change;
}
+static int madera_set_fll_clks_reg(struct madera_fll *fll, bool ena,
+ unsigned int reg, unsigned int mask,
+ unsigned int shift)
+{
+ struct madera *madera = fll->madera;
+ unsigned int src;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(madera->regmap, reg, &src);
+ if (ret != 0) {
+ madera_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ src = (src & mask) >> shift;
+
+ switch (src) {
+ case MADERA_FLL_SRC_MCLK1:
+ clk = madera->mclk[MADERA_MCLK1].clk;
+ break;
+ case MADERA_FLL_SRC_MCLK2:
+ clk = madera->mclk[MADERA_MCLK2].clk;
+ break;
+ case MADERA_FLL_SRC_MCLK3:
+ clk = madera->mclk[MADERA_MCLK3].clk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
+static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLL_CONTROL_6_OFFS,
+ MADERA_FLL1_REFCLK_SRC_MASK,
+ MADERA_FLL1_REFCLK_DIV_SHIFT);
+}
+
+static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLLAO_CONTROL_6_OFFS,
+ MADERA_FLL_AO_REFCLK_SRC_MASK,
+ MADERA_FLL_AO_REFCLK_SRC_SHIFT);
+}
+
+static inline int madera_set_fllhj_clks(struct madera_fll *fll, int base, bool ena)
+{
+ return madera_set_fll_clks_reg(fll, ena,
+ base + MADERA_FLL_CONTROL_1_OFFS,
+ CS47L92_FLL1_REFCLK_SRC_MASK,
+ CS47L92_FLL1_REFCLK_SRC_SHIFT);
+}
+
static void madera_disable_fll(struct madera_fll *fll)
{
struct madera *madera = fll->madera;
unsigned int sync_base;
- bool change;
+ bool ref_change, sync_change;
switch (madera->type) {
case CS47L35:
@@ -3838,18 +3952,23 @@ static void madera_disable_fll(struct madera_fll *fll)
MADERA_FLL1_FREERUN, MADERA_FLL1_FREERUN);
regmap_update_bits_check(madera->regmap,
fll->base + MADERA_FLL_CONTROL_1_OFFS,
- MADERA_FLL1_ENA, 0, &change);
- regmap_update_bits(madera->regmap,
- sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
- MADERA_FLL1_SYNC_ENA, 0);
+ MADERA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(madera->regmap,
+ sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
+ MADERA_FLL1_SYNC_ENA, 0, &sync_change);
regmap_update_bits(madera->regmap,
fll->base + MADERA_FLL_CONTROL_1_OFFS,
MADERA_FLL1_FREERUN, 0);
madera_wait_for_fll(fll, false);
- if (change)
+ if (sync_change)
+ madera_set_fll_clks(fll, sync_base, false);
+
+ if (ref_change) {
+ madera_set_fll_clks(fll, fll->base, false);
pm_runtime_put_autosuspend(madera->dev);
+ }
}
static int madera_enable_fll(struct madera_fll *fll)
@@ -3905,6 +4024,10 @@ static int madera_enable_fll(struct madera_fll *fll)
regmap_update_bits(fll->madera->regmap,
fll->base + MADERA_FLL_CONTROL_7_OFFS,
MADERA_FLL1_GAIN_MASK, 0);
+
+ if (sync_enabled > 0)
+ madera_set_fll_clks(fll, sync_base, false);
+ madera_set_fll_clks(fll, fll->base, false);
}
/* Apply SYNCCLK setting */
@@ -3983,11 +4106,15 @@ static int madera_enable_fll(struct madera_fll *fll)
if (!already_enabled)
pm_runtime_get_sync(madera->dev);
- if (have_sync)
+ if (have_sync) {
+ madera_set_fll_clks(fll, sync_base, true);
regmap_update_bits(madera->regmap,
sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS,
MADERA_FLL1_SYNC_ENA,
MADERA_FLL1_SYNC_ENA);
+ }
+
+ madera_set_fll_clks(fll, fll->base, true);
regmap_update_bits(madera->regmap,
fll->base + MADERA_FLL_CONTROL_1_OFFS,
MADERA_FLL1_ENA, MADERA_FLL1_ENA);
@@ -4159,6 +4286,9 @@ static int madera_enable_fll_ao(struct madera_fll *fll,
fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD);
+ if (already_enabled)
+ madera_set_fllao_clks(fll, fll->base, false);
+
for (i = 0; i < patch_size; i++) {
val = patch[i].def;
@@ -4172,6 +4302,8 @@ static int madera_enable_fll_ao(struct madera_fll *fll,
regmap_write(madera->regmap, patch[i].reg, val);
}
+ madera_set_fllao_clks(fll, fll->base, true);
+
regmap_update_bits(madera->regmap,
fll->base + MADERA_FLLAO_CONTROL_1_OFFS,
MADERA_FLL_AO_ENA, MADERA_FLL_AO_ENA);
@@ -4215,8 +4347,10 @@ static int madera_disable_fll_ao(struct madera_fll *fll)
fll->base + MADERA_FLLAO_CONTROL_2_OFFS,
MADERA_FLL_AO_CTRL_UPD_MASK, 0);
- if (change)
+ if (change) {
+ madera_set_fllao_clks(fll, fll->base, false);
pm_runtime_put_autosuspend(madera->dev);
+ }
return 0;
}
@@ -4302,8 +4436,10 @@ static int madera_fllhj_disable(struct madera_fll *fll)
fll->base + MADERA_FLL_CONTROL_2_OFFS,
MADERA_FLL1_CTRL_UPD_MASK, 0);
- if (change)
+ if (change) {
+ madera_set_fllhj_clks(fll, fll->base, false);
pm_runtime_put_autosuspend(madera->dev);
+ }
return 0;
}
@@ -4475,6 +4611,9 @@ static int madera_fllhj_enable(struct madera_fll *fll)
MADERA_FLL1_HOLD_MASK,
MADERA_FLL1_HOLD_MASK);
+ if (already_enabled)
+ madera_set_fllhj_clks(fll, fll->base, false);
+
/* Apply refclk */
ret = madera_fllhj_apply(fll, fll->ref_freq);
if (ret) {
@@ -4486,6 +4625,8 @@ static int madera_fllhj_enable(struct madera_fll *fll)
CS47L92_FLL1_REFCLK_SRC_MASK,
fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT);
+ madera_set_fllhj_clks(fll, fll->base, true);
+
regmap_update_bits(madera->regmap,
fll->base + MADERA_FLL_CONTROL_1_OFFS,
MADERA_FLL1_ENA_MASK,
@@ -4573,7 +4714,7 @@ EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk);
*
* @component: Device to configure
* @output: Output number
- * @diff: True to set the output to differential mode
+ * @differential: True to set the output to differential mode
*
* Some systems use external analogue switches to connect more
* analogue devices to the CODEC than are supported by the device. In