aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2013-05-10 16:40:10 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-05-13 01:40:12 -0400
commitd1a0a2995855e8d583c5cf97dbf0f6b376668c45 (patch)
tree032123b019239176a99764b2ef2f2b892f768a6f
parentf722406faae2d073cc1d01063d1123c35425939e (diff)
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>
-rw-r--r--sound/soc/codecs/wm8994.c49
1 files changed, 36 insertions, 13 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 1eb152cb1097..9f32dd8660d5 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -16,6 +16,7 @@
16#include <linux/init.h> 16#include <linux/init.h>
17#include <linux/delay.h> 17#include <linux/delay.h>
18#include <linux/pm.h> 18#include <linux/pm.h>
19#include <linux/gcd.h>
19#include <linux/i2c.h> 20#include <linux/i2c.h>
20#include <linux/platform_device.h> 21#include <linux/platform_device.h>
21#include <linux/pm_runtime.h> 22#include <linux/pm_runtime.h>
@@ -2005,15 +2006,16 @@ struct fll_div {
2005 u16 outdiv; 2006 u16 outdiv;
2006 u16 n; 2007 u16 n;
2007 u16 k; 2008 u16 k;
2009 u16 lambda;
2008 u16 clk_ref_div; 2010 u16 clk_ref_div;
2009 u16 fll_fratio; 2011 u16 fll_fratio;
2010}; 2012};
2011 2013
2012static int wm8994_get_fll_config(struct fll_div *fll, 2014static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
2013 int freq_in, int freq_out) 2015 int freq_in, int freq_out)
2014{ 2016{
2015 u64 Kpart; 2017 u64 Kpart;
2016 unsigned int K, Ndiv, Nmod; 2018 unsigned int K, Ndiv, Nmod, gcd_fll;
2017 2019
2018 pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); 2020 pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
2019 2021
@@ -2062,20 +2064,30 @@ static int wm8994_get_fll_config(struct fll_div *fll,
2062 Nmod = freq_out % freq_in; 2064 Nmod = freq_out % freq_in;
2063 pr_debug("Nmod=%d\n", Nmod); 2065 pr_debug("Nmod=%d\n", Nmod);
2064 2066
2065 /* Calculate fractional part - scale up so we can round. */ 2067 switch (control->type) {
2066 Kpart = FIXED_FLL_SIZE * (long long)Nmod; 2068 case WM8994:
2069 /* Calculate fractional part - scale up so we can round. */
2070 Kpart = FIXED_FLL_SIZE * (long long)Nmod;
2067 2071
2068 do_div(Kpart, freq_in); 2072 do_div(Kpart, freq_in);
2069 2073
2070 K = Kpart & 0xFFFFFFFF; 2074 K = Kpart & 0xFFFFFFFF;
2071 2075
2072 if ((K % 10) >= 5) 2076 if ((K % 10) >= 5)
2073 K += 5; 2077 K += 5;
2074 2078
2075 /* Move down to proper range now rounding is done */ 2079 /* Move down to proper range now rounding is done */
2076 fll->k = K / 10; 2080 fll->k = K / 10;
2077 2081
2078 pr_debug("N=%x K=%x\n", fll->n, fll->k); 2082 pr_debug("N=%x K=%x\n", fll->n, fll->k);
2083
2084 default:
2085 gcd_fll = gcd(freq_out, freq_in);
2086
2087 fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
2088 fll->lambda = freq_in / gcd_fll;
2089
2090 }
2079 2091
2080 return 0; 2092 return 0;
2081} 2093}
@@ -2139,9 +2151,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
2139 * analysis bugs spewing warnings. 2151 * analysis bugs spewing warnings.
2140 */ 2152 */
2141 if (freq_out) 2153 if (freq_out)
2142 ret = wm8994_get_fll_config(&fll, freq_in, freq_out); 2154 ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
2143 else 2155 else
2144 ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in, 2156 ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
2145 wm8994->fll[id].out); 2157 wm8994->fll[id].out);
2146 if (ret < 0) 2158 if (ret < 0)
2147 return ret; 2159 return ret;
@@ -2186,6 +2198,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
2186 WM8994_FLL1_N_MASK, 2198 WM8994_FLL1_N_MASK,
2187 fll.n << WM8994_FLL1_N_SHIFT); 2199 fll.n << WM8994_FLL1_N_SHIFT);
2188 2200
2201 if (fll.lambda) {
2202 snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset,
2203 WM8958_FLL1_LAMBDA_MASK,
2204 fll.lambda);
2205 snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
2206 WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
2207 } else {
2208 snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset,
2209 WM8958_FLL1_EFS_ENA, 0);
2210 }
2211
2189 snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, 2212 snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
2190 WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | 2213 WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
2191 WM8994_FLL1_REFCLK_DIV_MASK | 2214 WM8994_FLL1_REFCLK_DIV_MASK |