aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8974.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-06-30 14:02:32 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-06-30 14:34:23 -0400
commit91d0c3ecbaf6616c0723d7aad9b6dadad2dea43f (patch)
treebe66139d920e658ec5d5eb5e18054df3efa269aa /sound/soc/codecs/wm8974.c
parent33d81af4d12fc8863247abba1c1d706b462e89d0 (diff)
ASoC: Refresh WM8974 PLL configuration
Move away from a fixed table to runtime calculation. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/wm8974.c')
-rw-r--r--sound/soc/codecs/wm8974.c87
1 files changed, 60 insertions, 27 deletions
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 207fe3f713ed..d07bd0ddd439 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)
325} 325}
326 326
327struct pll_ { 327struct pll_ {
328 unsigned int in_hz, out_hz; 328 unsigned int pre_div:4; /* prescale - 1 */
329 unsigned int pre:4; /* prescale - 1 */
330 unsigned int n:4; 329 unsigned int n:4;
331 unsigned int k; 330 unsigned int k;
332}; 331};
333 332
334static struct pll_ pll[] = { 333static struct pll_ pll_div;
335 { 12000000, 11289600, 0, 7, 0x86c220 }, 334
336 { 12000000, 12288000, 0, 8, 0x3126e8 }, 335/* The size in bits of the pll divide multiplied by 10
337 { 13000000, 11289600, 0, 6, 0xf28bd4 }, 336 * to allow rounding later */
338 { 13000000, 12288000, 0, 7, 0x8fd525 }, 337#define FIXED_PLL_SIZE ((1 << 24) * 10)
339 { 12288000, 11289600, 0, 7, 0x59999a }, 338
340 { 11289600, 12288000, 0, 8, 0x80dee9 }, 339static void pll_factors(unsigned int target, unsigned int source)
341 { 25000000, 11289600, 1, 7, 0x39B024 }, 340{
342 { 25000000, 24576000, 1, 7, 0xdd4413 } 341 unsigned long long Kpart;
343}; 342 unsigned int K, Ndiv, Nmod;
343
344 Ndiv = target / source;
345 if (Ndiv < 6) {
346 source >>= 1;
347 pll_div.pre_div = 1;
348 Ndiv = target / source;
349 } else
350 pll_div.pre_div = 0;
351
352 if ((Ndiv < 6) || (Ndiv > 12))
353 printk(KERN_WARNING
354 "WM8974 N value %u outwith recommended range!d\n",
355 Ndiv);
356
357 pll_div.n = Ndiv;
358 Nmod = target % source;
359 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
360
361 do_div(Kpart, source);
362
363 K = Kpart & 0xFFFFFFFF;
364
365 /* Check if we need to round */
366 if ((K % 10) >= 5)
367 K += 5;
368
369 /* Move down to proper range now rounding is done */
370 K /= 10;
371
372 pll_div.k = K;
373}
344 374
345static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, 375static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
346 int pll_id, unsigned int freq_in, unsigned int freq_out) 376 int pll_id, unsigned int freq_in, unsigned int freq_out)
347{ 377{
348 struct snd_soc_codec *codec = codec_dai->codec; 378 struct snd_soc_codec *codec = codec_dai->codec;
349 int i;
350 u16 reg; 379 u16 reg;
351 380
352 if (freq_in == 0 || freq_out == 0) { 381 if (freq_in == 0 || freq_out == 0) {
382 /* Clock CODEC directly from MCLK */
383 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
384 wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
385
386 /* Turn off PLL */
353 reg = wm8974_read_reg_cache(codec, WM8974_POWER1); 387 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
354 wm8974_write(codec, WM8974_POWER1, reg & 0x1df); 388 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
355 return 0; 389 return 0;
356 } 390 }
357 391
358 for (i = 0; i < ARRAY_SIZE(pll); i++) { 392 pll_factors(freq_out*4, freq_in);
359 if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) { 393
360 wm8974_write(codec, WM8974_PLLN, 394 wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
361 (pll[i].pre << 4) | pll[i].n); 395 wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
362 wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18); 396 wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
363 wm8974_write(codec, WM8974_PLLK2, 397 wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
364 (pll[i].k >> 9) & 0x1ff); 398 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
365 wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff); 399 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
366 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
367 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
368 return 0;
369 }
370 }
371 400
372 return -EINVAL; 401 /* Run CODEC from PLL instead of MCLK */
402 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
403 wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
404
405 return 0;
373} 406}
374 407
375/* 408/*