@@ -30,20 +30,11 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
- ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
- 32768, params_rate(params) * 256);
- if (ret < 0) {
- pr_err("%s - failed to set wm8904 codec PLL.", __func__);
- return ret;
- }
-
/*
* As here wm8904 use FLL output as its system clock
- * so calling set_sysclk won't care freq parameter
- * then we pass 0
*/
ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL,
- 0, SND_SOC_CLOCK_IN);
+ params_rate(params) * 256, SND_SOC_CLOCK_IN);
if (ret < 0) {
pr_err("%s -failed to set wm8904 SYSCLK\n", __func__);
return ret;
@@ -1331,6 +1331,7 @@ config SND_SOC_WM8903
config SND_SOC_WM8904
tristate "Wolfson Microelectronics WM8904 CODEC"
depends on I2C
+ select SND_SOC_WM_FLL
config SND_SOC_WM8940
tristate
@@ -24,6 +24,7 @@
#include <sound/tlv.h>
#include <sound/wm8904.h>
+#include "wm_fll.h"
#include "wm8904.h"
enum wm8904_type {
@@ -66,12 +67,8 @@ struct wm8904_priv {
int retune_mobile_cfg;
struct soc_enum retune_mobile_enum;
- /* FLL setup */
- int fll_src;
- int fll_fref;
- int fll_fout;
-
/* Clocking configuration */
+ struct wm_fll_data fll;
unsigned int mclk_rate;
int sysclk_src;
unsigned int sysclk_rate;
@@ -311,35 +308,111 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg)
}
}
-static int wm8904_configure_clocking(struct snd_soc_component *component)
+static void wm8904_unprepare_sysclk(struct wm8904_priv *priv)
{
+ switch (priv->sysclk_src) {
+ case WM8904_CLK_MCLK:
+ clk_disable_unprepare(priv->mclk);
+ break;
+
+ case WM8904_CLK_FLL:
+ wm_fll_disable(&priv->fll);
+ break;
+ }
+}
+
+static int wm8904_prepare_sysclk(struct wm8904_priv *priv)
+{
+ int err;
+
+ switch (priv->sysclk_src) {
+ case WM8904_CLK_MCLK:
+ err = clk_set_rate(priv->mclk, priv->mclk_rate);
+ if (!err)
+ err = clk_prepare_enable(priv->mclk);
+ break;
+
+ case WM8904_CLK_FLL:
+ err = wm_fll_enable(&priv->fll);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void wm8904_disable_sysclk(struct wm8904_priv *priv)
+{
+ regmap_update_bits(priv->regmap, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA, 0);
+ wm8904_unprepare_sysclk(priv);
+}
+
+static int wm8904_enable_sysclk(struct wm8904_priv *priv)
+{
+ int err;
+
+ err = wm8904_prepare_sysclk(priv);
+ if (err < 0)
+ return err;
+
+ err = regmap_update_bits(priv->regmap, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA_MASK, WM8904_CLK_SYS_ENA);
+ if (err < 0)
+ wm8904_unprepare_sysclk(priv);
+
+ return err;
+}
+
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int rate, int dir)
+{
+ struct snd_soc_component *component = dai->component;
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
- unsigned int clock0, clock2, rate;
+ unsigned int clock0, clock2;
+ int err;
+
+ switch (clk_id) {
+ case WM8904_CLK_MCLK:
+ case WM8904_CLK_FLL:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_id == wm8904->sysclk_src && rate == wm8904->mclk_rate)
+ return 0;
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, rate);
/* Gate the clock while we're updating to avoid misclocking */
clock2 = snd_soc_component_read32(component, WM8904_CLOCK_RATES_2);
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_SYSCLK_SRC, 0);
+ wm8904_disable_sysclk(wm8904);
+
+ wm8904->sysclk_src = clk_id;
+ wm8904->mclk_rate = rate;
- /* This should be done on init() for bypass paths */
switch (wm8904->sysclk_src) {
case WM8904_CLK_MCLK:
- dev_dbg(component->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
+ dev_dbg(component->dev, "Using %dHz MCLK\n", rate);
clock2 &= ~WM8904_SYSCLK_SRC;
- rate = wm8904->mclk_rate;
-
- /* Ensure the FLL is stopped */
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
break;
case WM8904_CLK_FLL:
- dev_dbg(component->dev, "Using %dHz FLL clock\n",
- wm8904->fll_fout);
+ err = wm_fll_set_rate(&wm8904->fll, rate);
+ if (err < 0) {
+ dev_err(component->dev, "Failed to set FLL rate: %d\n", err);
+ return err;
+ }
+
+ dev_dbg(component->dev, "Using %dHz FLL clock\n", rate);
clock2 |= WM8904_SYSCLK_SRC;
- rate = wm8904->fll_fout;
break;
default:
@@ -356,11 +429,18 @@ static int wm8904_configure_clocking(struct snd_soc_component *component)
wm8904->sysclk_rate = rate;
}
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
- clock0);
-
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0,
+ WM8904_MCLK_DIV, clock0);
snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2);
+ WM8904_SYSCLK_SRC, clock2);
+
+ if (clock2 & WM8904_CLK_SYS_ENA) {
+ err = wm8904_enable_sysclk(wm8904);
+ if (err < 0) {
+ dev_err(component->dev, "Failed to reenable CLK_SYS: %d\n", err);
+ return err;
+ }
+ }
dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
@@ -655,33 +735,21 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- /* If we're using the FLL then we only start it when
- * required; we assume that the configuration has been
- * done previously and all we need to do is kick it
- * off.
- */
- switch (wm8904->sysclk_src) {
- case WM8904_CLK_FLL:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA,
- WM8904_FLL_OSC_ENA);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_ENA,
- WM8904_FLL_ENA);
- break;
-
- default:
- break;
- }
+ ret = wm8904_prepare_sysclk(wm8904);
+ if (ret)
+ dev_err(component->dev,
+ "Failed to prepare SYSCLK: %d\n", ret);
+ else
+ dev_dbg(component->dev, "SYSCLK on\n");
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+ wm8904_unprepare_sysclk(wm8904);
+ dev_dbg(component->dev, "SYSCLK off\n");
break;
}
@@ -1289,7 +1357,7 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
- int ret, i, best, best_val, cur_val;
+ int i, best, best_val, cur_val;
unsigned int aif1 = 0;
unsigned int aif2 = 0;
unsigned int aif3 = 0;
@@ -1324,13 +1392,8 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
-
dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8904->bclk);
- ret = wm8904_configure_clocking(component);
- if (ret != 0)
- return ret;
-
/* Select nearest CLK_SYS_RATE */
best = 0;
best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio)
@@ -1382,8 +1445,8 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
}
}
wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div;
- dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
- bclk_divs[best].div, wm8904->bclk);
+ dev_dbg(component->dev, "Selected BCLK_DIV of %d.%d for %dHz BCLK\n",
+ bclk_divs[best].div / 10, bclk_divs[best].div % 10, wm8904->bclk);
aif2 |= bclk_divs[best].bclk_div;
/* LRCLK is a simple fraction of BCLK */
@@ -1410,34 +1473,6 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-
-static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct snd_soc_component *component = dai->component;
- struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
-
- switch (clk_id) {
- case WM8904_CLK_MCLK:
- priv->sysclk_src = clk_id;
- priv->mclk_rate = freq;
- break;
-
- case WM8904_CLK_FLL:
- priv->sysclk_src = clk_id;
- break;
-
- default:
- return -EINVAL;
- }
-
- dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
-
- wm8904_configure_clocking(component);
-
- return 0;
-}
-
static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
@@ -1577,253 +1612,6 @@ static int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-struct _fll_div {
- u16 fll_fratio;
- u16 fll_outdiv;
- u16 fll_clk_ref_div;
- u16 n;
- u16 k;
-};
-
-/* The size in bits of the FLL divide multiplied by 10
- * to allow rounding later */
-#define FIXED_FLL_SIZE ((1 << 16) * 10)
-
-static struct {
- unsigned int min;
- unsigned int max;
- u16 fll_fratio;
- int ratio;
-} fll_fratios[] = {
- { 0, 64000, 4, 16 },
- { 64000, 128000, 3, 8 },
- { 128000, 256000, 2, 4 },
- { 256000, 1000000, 1, 2 },
- { 1000000, 13500000, 0, 1 },
-};
-
-static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
- unsigned int Fout)
-{
- u64 Kpart;
- unsigned int K, Ndiv, Nmod, target;
- unsigned int div;
- int i;
-
- /* Fref must be <=13.5MHz */
- div = 1;
- fll_div->fll_clk_ref_div = 0;
- while ((Fref / div) > 13500000) {
- div *= 2;
- fll_div->fll_clk_ref_div++;
-
- if (div > 8) {
- pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
- Fref);
- return -EINVAL;
- }
- }
-
- pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
-
- /* Apply the division for our remaining calculations */
- Fref /= div;
-
- /* Fvco should be 90-100MHz; don't check the upper bound */
- div = 4;
- while (Fout * div < 90000000) {
- div++;
- if (div > 64) {
- pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
- Fout);
- return -EINVAL;
- }
- }
- target = Fout * div;
- fll_div->fll_outdiv = div - 1;
-
- pr_debug("Fvco=%dHz\n", target);
-
- /* Find an appropriate FLL_FRATIO and factor it out of the target */
- for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
- if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
- fll_div->fll_fratio = fll_fratios[i].fll_fratio;
- target /= fll_fratios[i].ratio;
- break;
- }
- }
- if (i == ARRAY_SIZE(fll_fratios)) {
- pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
- return -EINVAL;
- }
-
- /* Now, calculate N.K */
- Ndiv = target / Fref;
-
- fll_div->n = Ndiv;
- Nmod = target % Fref;
- pr_debug("Nmod=%d\n", Nmod);
-
- /* Calculate fractional part - scale up so we can round. */
- Kpart = FIXED_FLL_SIZE * (long long)Nmod;
-
- do_div(Kpart, Fref);
-
- K = Kpart & 0xFFFFFFFF;
-
- if ((K % 10) >= 5)
- K += 5;
-
- /* Move down to proper range now rounding is done */
- fll_div->k = K / 10;
-
- pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
- fll_div->n, fll_div->k,
- fll_div->fll_fratio, fll_div->fll_outdiv,
- fll_div->fll_clk_ref_div);
-
- return 0;
-}
-
-static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
-{
- struct snd_soc_component *component = dai->component;
- struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
- struct _fll_div fll_div;
- int ret, val;
- int clock2, fll1;
-
- /* Any change? */
- if (source == wm8904->fll_src && Fref == wm8904->fll_fref &&
- Fout == wm8904->fll_fout)
- return 0;
-
- clock2 = snd_soc_component_read32(component, WM8904_CLOCK_RATES_2);
-
- if (Fout == 0) {
- dev_dbg(component->dev, "FLL disabled\n");
-
- wm8904->fll_fref = 0;
- wm8904->fll_fout = 0;
-
- /* Gate SYSCLK to avoid glitches */
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA, 0);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
-
- goto out;
- }
-
- /* Validate the FLL ID */
- switch (source) {
- case WM8904_FLL_MCLK:
- case WM8904_FLL_LRCLK:
- case WM8904_FLL_BCLK:
- ret = fll_factors(&fll_div, Fref, Fout);
- if (ret != 0)
- return ret;
- break;
-
- case WM8904_FLL_FREE_RUNNING:
- dev_dbg(component->dev, "Using free running FLL\n");
- /* Force 12MHz and output/4 for now */
- Fout = 12000000;
- Fref = 12000000;
-
- memset(&fll_div, 0, sizeof(fll_div));
- fll_div.fll_outdiv = 3;
- break;
-
- default:
- dev_err(component->dev, "Unknown FLL ID %d\n", fll_id);
- return -EINVAL;
- }
-
- /* Save current state then disable the FLL and SYSCLK to avoid
- * misclocking */
- fll1 = snd_soc_component_read32(component, WM8904_FLL_CONTROL_1);
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA, 0);
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
-
- /* Unlock forced oscilator control to switch it on/off */
- snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1,
- WM8904_USER_KEY, WM8904_USER_KEY);
-
- if (fll_id == WM8904_FLL_FREE_RUNNING) {
- val = WM8904_FLL_FRC_NCO;
- } else {
- val = 0;
- }
-
- snd_soc_component_update_bits(component, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
- val);
- snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1,
- WM8904_USER_KEY, 0);
-
- switch (fll_id) {
- case WM8904_FLL_MCLK:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_SRC_MASK, 0);
- break;
-
- case WM8904_FLL_LRCLK:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_SRC_MASK, 1);
- break;
-
- case WM8904_FLL_BCLK:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_SRC_MASK, 2);
- break;
- }
-
- if (fll_div.k)
- val = WM8904_FLL_FRACN_ENA;
- else
- val = 0;
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_FRACN_ENA, val);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_2,
- WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK,
- (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) |
- (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT));
-
- snd_soc_component_write(component, WM8904_FLL_CONTROL_3, fll_div.k);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
- fll_div.n << WM8904_FLL_N_SHIFT);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_DIV_MASK,
- fll_div.fll_clk_ref_div
- << WM8904_FLL_CLK_REF_DIV_SHIFT);
-
- dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
-
- wm8904->fll_fref = Fref;
- wm8904->fll_fout = Fout;
- wm8904->fll_src = source;
-
- /* Enable the FLL if it was previously active */
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA, fll1);
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_ENA, fll1);
-
-out:
- /* Reenable SYSCLK if it was previously active */
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA, clock2);
-
- return 0;
-}
-
static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_component *component = codec_dai->component;
@@ -1871,15 +1659,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
return ret;
}
- ret = clk_prepare_enable(wm8904->mclk);
- if (ret) {
- dev_err(component->dev,
- "Failed to enable MCLK: %d\n", ret);
- regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
- wm8904->supplies);
- return ret;
- }
-
regcache_cache_only(wm8904->regmap, false);
regcache_sync(wm8904->regmap);
@@ -1922,7 +1701,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
wm8904->supplies);
- clk_disable_unprepare(wm8904->mclk);
break;
}
return 0;
@@ -1937,7 +1715,6 @@ static const struct snd_soc_dai_ops wm8904_dai_ops = {
.set_sysclk = wm8904_set_sysclk,
.set_fmt = wm8904_set_fmt,
.set_tdm_slot = wm8904_set_tdm_slot,
- .set_pll = wm8904_set_fll,
.hw_params = wm8904_hw_params,
.digital_mute = wm8904_digital_mute,
};
@@ -2123,6 +1900,15 @@ static const struct regmap_config wm8904_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults),
};
+static const struct wm_fll_desc wm8904_fll_desc = {
+ .ctl_offset = WM8904_FLL_CONTROL_1,
+ .int_offset = WM8904_INTERRUPT_STATUS,
+ .int_mask = WM8904_FLL_LOCK_EINT_MASK,
+ .nco_reg0 = WM8904_FLL_NCO_TEST_0,
+ .nco_reg1 = WM8904_FLL_NCO_TEST_1,
+ .clk_ref_map = { FLL_REF_MCLK, FLL_REF_BCLK, FLL_REF_FSCLK, /* reserved */ 0 },
+};
+
#ifdef CONFIG_OF
static const struct of_device_id wm8904_of_match[] = {
{
@@ -2165,6 +1951,19 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ wm8904->fll.regmap = wm8904->regmap;
+ wm8904->fll.desc = &wm8904_fll_desc;
+ ret = wm_fll_init_with_clk(&wm8904->fll);
+ if (ret)
+ return ret;
+
+ ret = wm_fll_set_parent(&wm8904->fll, FLL_REF_MCLK);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to select MCLK as FLL input: %d\n",
+ ret);
+ return ret;
+ }
+
if (i2c->dev.of_node) {
const struct of_device_id *match;
@@ -2276,6 +2075,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
WM8904_POBCTRL, 0);
/* Can leave the device powered off until we need it */
+ wm8904_disable_sysclk(wm8904);
regcache_cache_only(wm8904->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
@@ -13,11 +13,6 @@
#define WM8904_CLK_MCLK 1
#define WM8904_CLK_FLL 2
-#define WM8904_FLL_MCLK 1
-#define WM8904_FLL_BCLK 2
-#define WM8904_FLL_LRCLK 3
-#define WM8904_FLL_FREE_RUNNING 4
-
/*
* Register values.
*/
Rework FLL handling to use common code introduced earlier. Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl> --- sound/soc/atmel/atmel_wm8904.c | 11 +- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/wm8904.c | 476 ++++++++++----------------------- sound/soc/codecs/wm8904.h | 5 - 4 files changed, 140 insertions(+), 353 deletions(-)