diff options
| author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-06-30 19:02:32 +0100 | 
|---|---|---|
| committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-06-30 19:34:23 +0100 | 
| commit | 91d0c3ecbaf6616c0723d7aad9b6dadad2dea43f (patch) | |
| tree | be66139d920e658ec5d5eb5e18054df3efa269aa | |
| parent | 33d81af4d12fc8863247abba1c1d706b462e89d0 (diff) | |
ASoC: Refresh WM8974 PLL configuration
Move away from a fixed table to runtime calculation.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
| -rw-r--r-- | sound/soc/codecs/wm8974.c | 87 | 
1 files changed, 60 insertions, 27 deletions
| diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 207fe3f713e..d07bd0ddd43 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -325,51 +325,84 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)  }  struct pll_ { -	unsigned int in_hz, out_hz; -	unsigned int pre:4; /* prescale - 1 */ +	unsigned int pre_div:4; /* prescale - 1 */  	unsigned int n:4;  	unsigned int k;  }; -static struct pll_ pll[] = { -	{ 12000000, 11289600, 0, 7, 0x86c220 }, -	{ 12000000, 12288000, 0, 8, 0x3126e8 }, -	{ 13000000, 11289600, 0, 6, 0xf28bd4 }, -	{ 13000000, 12288000, 0, 7, 0x8fd525 }, -	{ 12288000, 11289600, 0, 7, 0x59999a }, -	{ 11289600, 12288000, 0, 8, 0x80dee9 }, -	{ 25000000, 11289600, 1, 7, 0x39B024 }, -	{ 25000000, 24576000, 1, 7, 0xdd4413 } -}; +static struct pll_ pll_div; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) + +static void pll_factors(unsigned int target, unsigned int source) +{ +	unsigned long long Kpart; +	unsigned int K, Ndiv, Nmod; + +	Ndiv = target / source; +	if (Ndiv < 6) { +		source >>= 1; +		pll_div.pre_div = 1; +		Ndiv = target / source; +	} else +		pll_div.pre_div = 0; + +	if ((Ndiv < 6) || (Ndiv > 12)) +		printk(KERN_WARNING +			"WM8974 N value %u outwith recommended range!d\n", +			Ndiv); + +	pll_div.n = Ndiv; +	Nmod = target % source; +	Kpart = FIXED_PLL_SIZE * (long long)Nmod; + +	do_div(Kpart, source); + +	K = Kpart & 0xFFFFFFFF; + +	/* Check if we need to round */ +	if ((K % 10) >= 5) +		K += 5; + +	/* Move down to proper range now rounding is done */ +	K /= 10; + +	pll_div.k = K; +}  static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,  		int pll_id, unsigned int freq_in, unsigned int freq_out)  {  	struct snd_soc_codec *codec = codec_dai->codec; -	int i;  	u16 reg;  	if (freq_in == 0 || freq_out == 0) { +		/* Clock CODEC directly from MCLK */ +		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK); +		wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff); + +		/* Turn off PLL */  		reg = wm8974_read_reg_cache(codec, WM8974_POWER1);  		wm8974_write(codec, WM8974_POWER1, reg & 0x1df);  		return 0;  	} -	for (i = 0; i < ARRAY_SIZE(pll); i++) { -		if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) { -			wm8974_write(codec, WM8974_PLLN, -				     (pll[i].pre << 4) | pll[i].n); -			wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18); -			wm8974_write(codec, WM8974_PLLK2, -				     (pll[i].k >> 9) & 0x1ff); -			wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff); -			reg = wm8974_read_reg_cache(codec, WM8974_POWER1); -			wm8974_write(codec, WM8974_POWER1, reg | 0x020); -			return 0; -		} -	} +	pll_factors(freq_out*4, freq_in); + +	wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); +	wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18); +	wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff); +	wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff); +	reg = wm8974_read_reg_cache(codec, WM8974_POWER1); +	wm8974_write(codec, WM8974_POWER1, reg | 0x020); -	return -EINVAL; +	/* Run CODEC from PLL instead of MCLK */ +	reg = wm8974_read_reg_cache(codec, WM8974_CLOCK); +	wm8974_write(codec, WM8974_CLOCK, reg | 0x100); + +	return 0;  }  /* | 
