@@ -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
@@ -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);
@@ -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;
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@rere.qmqm.pl> --- sound/soc/codecs/Kconfig | 2 + sound/soc/codecs/wm8994.c | 281 +++++++++++--------------------------- sound/soc/codecs/wm8994.h | 4 +- 3 files changed, 84 insertions(+), 203 deletions(-)