diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-06-30 14:02:32 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-06-30 14:34:23 -0400 |
commit | 91d0c3ecbaf6616c0723d7aad9b6dadad2dea43f (patch) | |
tree | be66139d920e658ec5d5eb5e18054df3efa269aa /sound/soc | |
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>
Diffstat (limited to 'sound/soc')
-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 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 | ||
327 | struct pll_ { | 327 | struct 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 | ||
334 | static struct pll_ pll[] = { | 333 | static 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 }, | 339 | static 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 | ||
345 | static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, | 375 | static 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 | /* |