Rework FLL handling to use common code. This uses polling for now to wait for FLL lock. Signed-off-by: Michał Mirosław <mirq-linux@xxxxxxxxxxxx> --- sound/soc/codecs/Kconfig | 2 + sound/soc/codecs/wm8994.c | 281 +++++++++++--------------------------- sound/soc/codecs/wm8994.h | 4 +- 3 files changed, 84 insertions(+), 203 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1a680023af7d..1ff6290ce18d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1382,6 +1382,8 @@ config SND_SOC_WM8993 config SND_SOC_WM8994 tristate + select SND_SOC_WM_FLL + select SND_SOC_WM_FLL_EFS config SND_SOC_WM8995 tristate diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index c3d06e8bc54f..d0dbc352303b 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2030,101 +2030,57 @@ static const struct snd_soc_dapm_route wm8958_intercon[] = { { "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" }, }; -/* The size in bits of the FLL divide multiplied by 10 - * to allow rounding later */ -#define FIXED_FLL_SIZE ((1 << 16) * 10) - -struct fll_div { - u16 outdiv; - u16 n; - u16 k; - u16 lambda; - u16 clk_ref_div; - u16 fll_fratio; +static const struct wm_fll_desc wm8994_fll_desc[2] = { + /* FLL1 */ + { + .ctl_offset = WM8994_FLL1_CONTROL_1, + .int_offset = WM8994_INTERRUPT_RAW_STATUS_2, + .int_mask = WM8994_IM_FLL1_LOCK_EINT_MASK, + .nco_reg0 = WM8994_FLL1_CONTROL_5, + .frc_nco_shift = 6, + .nco_reg1 = WM8994_FLL1_CONTROL_5, + .frc_nco_val_shift = 7, + .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK }, + }, + /* FLL2 */ + { + .ctl_offset = WM8994_FLL2_CONTROL_1, + .int_offset = WM8994_INTERRUPT_RAW_STATUS_2, + .int_mask = WM8994_IM_FLL2_LOCK_EINT_MASK, + .nco_reg0 = WM8994_FLL2_CONTROL_5, + .frc_nco_shift = 6, + .nco_reg1 = WM8994_FLL2_CONTROL_5, + .frc_nco_val_shift = 7, + .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK }, + }, }; -static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, - int freq_in, int freq_out) -{ - u64 Kpart; - unsigned int K, Ndiv, Nmod, gcd_fll; - - pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); - - /* Scale the input frequency down to <= 13.5MHz */ - fll->clk_ref_div = 0; - while (freq_in > 13500000) { - fll->clk_ref_div++; - freq_in /= 2; - - if (fll->clk_ref_div > 3) - return -EINVAL; - } - pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in); - - /* Scale the output to give 90MHz<=Fvco<=100MHz */ - fll->outdiv = 3; - while (freq_out * (fll->outdiv + 1) < 90000000) { - fll->outdiv++; - if (fll->outdiv > 63) - return -EINVAL; - } - freq_out *= fll->outdiv + 1; - pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out); - - if (freq_in > 1000000) { - fll->fll_fratio = 0; - } else if (freq_in > 256000) { - fll->fll_fratio = 1; - freq_in *= 2; - } else if (freq_in > 128000) { - fll->fll_fratio = 2; - freq_in *= 4; - } else if (freq_in > 64000) { - fll->fll_fratio = 3; - freq_in *= 8; - } else { - fll->fll_fratio = 4; - freq_in *= 16; - } - pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in); - - /* Now, calculate N.K */ - Ndiv = freq_out / freq_in; - - fll->n = Ndiv; - Nmod = freq_out % freq_in; - pr_debug("Nmod=%d\n", Nmod); - - switch (control->type) { - case WM8994: - /* Calculate fractional part - scale up so we can round. */ - Kpart = FIXED_FLL_SIZE * (long long)Nmod; - - do_div(Kpart, freq_in); - - K = Kpart & 0xFFFFFFFF; - - if ((K % 10) >= 5) - K += 5; - - /* Move down to proper range now rounding is done */ - fll->k = K / 10; - fll->lambda = 0; - - pr_debug("N=%x K=%x\n", fll->n, fll->k); - break; - - default: - gcd_fll = gcd(freq_out, freq_in); - - fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll; - fll->lambda = freq_in / gcd_fll; - - } - - return 0; -} +static const struct wm_fll_desc wm8958_fll_desc[2] = { + /* FLL1 */ + { + .ctl_offset = WM8994_FLL1_CONTROL_1, + .int_offset = WM8994_INTERRUPT_RAW_STATUS_2, + .int_mask = WM8994_IM_FLL1_LOCK_EINT_MASK, + .nco_reg0 = WM8994_FLL1_CONTROL_5, + .frc_nco_shift = 6, + .nco_reg1 = WM8994_FLL1_CONTROL_5, + .frc_nco_val_shift = 7, + .efs_offset = WM8958_FLL1_EFS_1, + .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK }, + }, + /* FLL2 */ + { + .ctl_offset = WM8994_FLL2_CONTROL_1, + .int_offset = WM8994_INTERRUPT_RAW_STATUS_2, + .int_mask = WM8994_IM_FLL2_LOCK_EINT_MASK, + .nco_reg0 = WM8994_FLL2_CONTROL_5, + .frc_nco_shift = 6, + .nco_reg1 = WM8994_FLL2_CONTROL_5, + .frc_nco_val_shift = 7, + .efs_offset = WM8958_FLL1_EFS_1, + .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK }, + }, +}; static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, unsigned int freq_in, unsigned int freq_out) @@ -2132,9 +2088,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); struct wm8994 *control = wm8994->wm8994; int reg_offset, ret; - struct fll_div fll; u16 reg, clk1, aif_reg, aif_src; - unsigned long timeout; bool was_enabled; switch (id) { @@ -2152,9 +2106,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, return -EINVAL; } - reg = snd_soc_component_read32(component, WM8994_FLL1_CONTROL_1 + reg_offset); - was_enabled = reg & WM8994_FLL1_ENA; - switch (src) { case 0: /* Allow no source specification when stopping */ @@ -2166,10 +2117,12 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, case WM8994_FLL_SRC_MCLK2: case WM8994_FLL_SRC_LRCLK: case WM8994_FLL_SRC_BCLK: + src = wm8994_fll_desc[0].clk_ref_map[src - WM8994_FLL_SRC_MCLK1]; break; case WM8994_FLL_SRC_INTERNAL: freq_in = 12000000; freq_out = 12000000; + src = FLL_REF_OSC; break; default: return -EINVAL; @@ -2180,18 +2133,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, wm8994->fll[id].in == freq_in && wm8994->fll[id].out == freq_out) return 0; - /* If we're stopping the FLL redo the old config - no - * registers will actually be written but we avoid GCC flow - * analysis bugs spewing warnings. - */ - if (freq_out) - ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out); - else - ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in, - wm8994->fll[id].out); - if (ret < 0) - return ret; - /* Make sure that we're not providing SYSCLK right now */ clk1 = snd_soc_component_read32(component, WM8994_CLOCKING_1); if (clk1 & WM8994_SYSCLK_SRC) @@ -2207,9 +2148,11 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, return -EBUSY; } - /* We always need to disable the FLL while reconfiguring */ - snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset, - WM8994_FLL1_ENA, 0); + was_enabled = wm_fll_is_enabled(&wm8994->fll_hw[id]) > 0; + + ret = wm_fll_disable(&wm8994->fll_hw[id]); + if (ret) + return ret; if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK && freq_in == freq_out && freq_out) { @@ -2217,46 +2160,21 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset, WM8958_FLL1_BYP, WM8958_FLL1_BYP); goto out; + } else if (wm8994->fll_byp) { + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset, + WM8958_FLL1_BYP, 0); } - reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) | - (fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT); - snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_2 + reg_offset, - WM8994_FLL1_OUTDIV_MASK | - WM8994_FLL1_FRATIO_MASK, reg); - - snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_3 + reg_offset, - WM8994_FLL1_K_MASK, fll.k); - - snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_4 + reg_offset, - WM8994_FLL1_N_MASK, - fll.n << WM8994_FLL1_N_SHIFT); - - if (fll.lambda) { - snd_soc_component_update_bits(component, WM8958_FLL1_EFS_1 + reg_offset, - WM8958_FLL1_LAMBDA_MASK, - fll.lambda); - snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset, - WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA); - } else { - snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset, - WM8958_FLL1_EFS_ENA, 0); - } - - snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset, - WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | - WM8994_FLL1_REFCLK_DIV_MASK | - WM8994_FLL1_REFCLK_SRC_MASK, - ((src == WM8994_FLL_SRC_INTERNAL) - << WM8994_FLL1_FRC_NCO_SHIFT) | - (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) | - (src - 1)); - - /* Clear any pending completion from a previous failure */ - try_wait_for_completion(&wm8994->fll_locked[id]); - - /* Enable (with fractional mode if required) */ if (freq_out) { + wm8994->fll_hw[id].freq_in = freq_in; + ret = wm_fll_set_parent(&wm8994->fll_hw[id], src); + if (!ret) + ret = wm_fll_set_rate(&wm8994->fll_hw[id], freq_out); + if (!ret) + ret = wm_fll_enable(&wm8994->fll_hw[id]); + if (ret < 0) + return ret; + /* Enable VMID if we need it */ if (!was_enabled) { active_reference(component); @@ -2273,27 +2191,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, break; } } - - reg = WM8994_FLL1_ENA; - - if (fll.k) - reg |= WM8994_FLL1_FRAC; - if (src == WM8994_FLL_SRC_INTERNAL) - reg |= WM8994_FLL1_OSC_ENA; - - snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset, - WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA | - WM8994_FLL1_FRAC, reg); - - if (wm8994->fll_locked_irq) { - timeout = wait_for_completion_timeout(&wm8994->fll_locked[id], - msecs_to_jiffies(10)); - if (timeout == 0) - dev_warn(component->dev, - "Timed out waiting for FLL lock\n"); - } else { - msleep(5); - } } else { if (was_enabled) { switch (control->type) { @@ -2350,15 +2247,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, return 0; } -static irqreturn_t wm8994_fll_locked_irq(int irq, void *data) -{ - struct completion *completion = data; - - complete(completion); - - return IRQ_HANDLED; -} - static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src, @@ -3992,6 +3880,18 @@ static int wm8994_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, control->regmap); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_hw); ++i) { + wm8994->fll_hw[i].regmap = control->regmap; + if (control->type == WM8994) + wm8994->fll_hw[i].desc = wm8994_fll_desc; + else + wm8994->fll_hw[i].desc = wm8958_fll_desc; + + ret = wm_fll_init(&wm8994->fll_hw[i]); + if (ret) + return ret; + } + wm8994->hubs.component = component; mutex_init(&wm8994->accdet_lock); @@ -4013,9 +3913,6 @@ static int wm8994_component_probe(struct snd_soc_component *component) INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work); - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) - init_completion(&wm8994->fll_locked[i]); - wm8994->micdet_irq = control->pdata.micdet_irq; /* By default use idle_bias_off, will override for WM8994 */ @@ -4166,16 +4063,6 @@ static int wm8994_component_probe(struct snd_soc_component *component) break; } - wm8994->fll_locked_irq = true; - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) { - ret = wm8994_request_irq(wm8994->wm8994, - WM8994_IRQ_FLL1_LOCK + i, - wm8994_fll_locked_irq, "FLL lock", - &wm8994->fll_locked[i]); - if (ret != 0) - wm8994->fll_locked_irq = false; - } - /* Make sure we can read from the GPIOs if they're inputs */ pm_runtime_get_sync(component->dev); @@ -4377,9 +4264,6 @@ static int wm8994_component_probe(struct snd_soc_component *component) wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994); if (wm8994->micdet_irq) free_irq(wm8994->micdet_irq, wm8994); - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) - wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, - &wm8994->fll_locked[i]); wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, &wm8994->hubs); wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component); @@ -4393,11 +4277,6 @@ static void wm8994_component_remove(struct snd_soc_component *component) { struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); struct wm8994 *control = wm8994->wm8994; - int i; - - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) - wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, - &wm8994->fll_locked[i]); wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, &wm8994->hubs); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 1d6f2abe1c11..9c61d95c9053 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -13,6 +13,7 @@ #include <linux/mutex.h> #include "wm_hubs.h" +#include "wm_fll.h" /* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ #define WM8994_SYSCLK_MCLK1 1 @@ -80,8 +81,7 @@ struct wm8994_priv { int aifdiv[2]; int channels[2]; struct wm8994_fll_config fll[2], fll_suspend[2]; - struct completion fll_locked[2]; - bool fll_locked_irq; + struct wm_fll_data fll_hw[2]; bool fll_byp; bool clk_has_run; -- 2.20.1 _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx https://mailman.alsa-project.org/mailman/listinfo/alsa-devel