ASoC: wm8994: Support EFS mode for FLL

Later WM8994 devices support an enhanced accuracy FLL divisor mode called
EFS which allows more precise selection of fractional source to output
ratios. Support this on relevant devices.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
Mark Brown 2013-05-10 21:40:10 +01:00
parent f722406faa
commit d1a0a29958
1 changed files with 36 additions and 13 deletions

View File

@ -16,6 +16,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
@ -2005,15 +2006,16 @@ struct fll_div {
u16 outdiv; u16 outdiv;
u16 n; u16 n;
u16 k; u16 k;
u16 lambda;
u16 clk_ref_div; u16 clk_ref_div;
u16 fll_fratio; u16 fll_fratio;
}; };
static int wm8994_get_fll_config(struct fll_div *fll, static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
int freq_in, int freq_out) int freq_in, int freq_out)
{ {
u64 Kpart; u64 Kpart;
unsigned int K, Ndiv, Nmod; unsigned int K, Ndiv, Nmod, gcd_fll;
pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
@ -2062,6 +2064,8 @@ static int wm8994_get_fll_config(struct fll_div *fll,
Nmod = freq_out % freq_in; Nmod = freq_out % freq_in;
pr_debug("Nmod=%d\n", Nmod); pr_debug("Nmod=%d\n", Nmod);
switch (control->type) {
case WM8994:
/* Calculate fractional part - scale up so we can round. */ /* Calculate fractional part - scale up so we can round. */
Kpart = FIXED_FLL_SIZE * (long long)Nmod; Kpart = FIXED_FLL_SIZE * (long long)Nmod;
@ -2077,6 +2081,14 @@ static int wm8994_get_fll_config(struct fll_div *fll,
pr_debug("N=%x K=%x\n", fll->n, fll->k); pr_debug("N=%x K=%x\n", fll->n, fll->k);
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; return 0;
} }
@ -2139,9 +2151,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
* analysis bugs spewing warnings. * analysis bugs spewing warnings.
*/ */
if (freq_out) if (freq_out)
ret = wm8994_get_fll_config(&fll, freq_in, freq_out); ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
else else
ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in, ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
wm8994->fll[id].out); wm8994->fll[id].out);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -2186,6 +2198,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
WM8994_FLL1_N_MASK, WM8994_FLL1_N_MASK,
fll.n << WM8994_FLL1_N_SHIFT); fll.n << WM8994_FLL1_N_SHIFT);
if (fll.lambda) {
snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
WM8958_FLL1_LAMBDA_MASK,
fll.lambda);
snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
} else {
snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
WM8958_FLL1_EFS_ENA, 0);
}
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
WM8994_FLL1_REFCLK_DIV_MASK | WM8994_FLL1_REFCLK_DIV_MASK |