aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8978.c
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2010-01-29 08:51:26 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-02-01 09:35:08 -0500
commitb0580913797034a1001e867b8b492c75226bf77e (patch)
treef20bacd8b37a19d270528fa6a90578a8b006e2e2 /sound/soc/codecs/wm8978.c
parentb2c3e923110f6ca60ccb30cf4a6bda5211454c4f (diff)
ASoC: improve MCLKDIV calculation in wm8978, when OPCLK is not used
In case, if OPCLK is not used, and PLL is used for driving the codec, the choice of PLL output frequency could result in a needlessly imprecise system clock frequency. Use an iterative process to select a precise configuration. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/wm8978.c')
-rw-r--r--sound/soc/codecs/wm8978.c115
1 files changed, 78 insertions, 37 deletions
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index ec2624b4c370..28bb59ea6ea1 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -58,6 +58,7 @@ struct wm8978_priv {
58 unsigned int f_mclk; 58 unsigned int f_mclk;
59 unsigned int f_256fs; 59 unsigned int f_256fs;
60 unsigned int f_opclk; 60 unsigned int f_opclk;
61 int mclk_idx;
61 enum wm8978_sysclk_src sysclk; 62 enum wm8978_sysclk_src sysclk;
62 u16 reg_cache[WM8978_CACHEREGNUM]; 63 u16 reg_cache[WM8978_CACHEREGNUM];
63}; 64};
@@ -402,6 +403,35 @@ static void pll_factors(struct wm8978_pll_div *pll_div, unsigned int target,
402 403
403 pll_div->k = k; 404 pll_div->k = k;
404} 405}
406
407/* MCLK dividers */
408static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12};
409static const int mclk_denominator[] = {1, 2, 1, 1, 1, 1, 1, 1};
410
411/*
412 * find index >= idx, such that, for a given f_out,
413 * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4
414 * f_out can be f_256fs or f_opclk, currently only used for f_256fs. Can be
415 * generalised for f_opclk with suitable coefficient arrays, but currently
416 * the OPCLK divisor is calculated directly, not iteratively.
417 */
418static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk,
419 unsigned int *f_pllout)
420{
421 int i;
422
423 for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
424 unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] /
425 mclk_denominator[i];
426 if (3 * f_mclk <= f_pllout_x4 && f_pllout_x4 < 13 * f_mclk) {
427 *f_pllout = f_pllout_x4 / 4;
428 return i;
429 }
430 }
431
432 return -EINVAL;
433}
434
405/* 435/*
406 * Calculate internal frequencies and dividers, according to Figure 40 436 * Calculate internal frequencies and dividers, according to Figure 40
407 * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6 437 * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6
@@ -412,12 +442,16 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
412 struct wm8978_pll_div pll_div; 442 struct wm8978_pll_div pll_div;
413 unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk, 443 unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk,
414 f_256fs = wm8978->f_256fs; 444 f_256fs = wm8978->f_256fs;
415 unsigned int f2, opclk_div; 445 unsigned int f2;
416 446
417 if (!f_mclk) 447 if (!f_mclk)
418 return -EINVAL; 448 return -EINVAL;
419 449
420 if (f_opclk) { 450 if (f_opclk) {
451 unsigned int opclk_div;
452 /* Cannot set up MCLK divider now, do later */
453 wm8978->mclk_idx = -1;
454
421 /* 455 /*
422 * The user needs OPCLK. Choose OPCLKDIV to put 456 * The user needs OPCLK. Choose OPCLKDIV to put
423 * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4. 457 * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4.
@@ -444,7 +478,7 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
444 wm8978->f_pllout = f_opclk * opclk_div; 478 wm8978->f_pllout = f_opclk * opclk_div;
445 } else if (f_256fs) { 479 } else if (f_256fs) {
446 /* 480 /*
447 * Not using OPCLK, choose R: 481 * Not using OPCLK, but PLL is used for the codec, choose R:
448 * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12. 482 * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12.
449 * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where 483 * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where
450 * prescale = 1, or prescale = 2. Prescale is calculated inside 484 * prescale = 1, or prescale = 2. Prescale is calculated inside
@@ -453,18 +487,11 @@ static int wm8978_configure_pll(struct snd_soc_codec *codec)
453 * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK 487 * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK
454 * must be 3.781MHz <= f_MCLK <= 32.768MHz 488 * must be 3.781MHz <= f_MCLK <= 32.768MHz
455 */ 489 */
456 if (48 * f_256fs < 3 * f_mclk || 4 * f_256fs >= 13 * f_mclk) 490 int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout);
457 return -EINVAL; 491 if (idx < 0)
492 return idx;
458 493
459 /* 494 wm8978->mclk_idx = idx;
460 * MCLKDIV will be selected in .hw_params(), just choose a
461 * suitable f_PLLOUT
462 */
463 if (4 * f_256fs < 3 * f_mclk)
464 /* Will have to use MCLKDIV */
465 wm8978->f_pllout = wm8978->f_mclk * 3 / 4;
466 else
467 wm8978->f_pllout = f_256fs;
468 495
469 /* GPIO1 into default mode as input - before configuring PLL */ 496 /* GPIO1 into default mode as input - before configuring PLL */
470 snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0); 497 snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
@@ -515,6 +542,20 @@ static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
515 wm8978->f_opclk = div; 542 wm8978->f_opclk = div;
516 543
517 if (wm8978->f_mclk) 544 if (wm8978->f_mclk)
545 /*
546 * We know the MCLK frequency, the user has requested
547 * OPCLK, configure the PLL based on that and start it
548 * and OPCLK immediately. We will configure PLL to match
549 * user-requested OPCLK frquency as good as possible.
550 * In fact, it is likely, that matching the sampling
551 * rate, when it becomes known, is more important, and
552 * we will not be reconfiguring PLL then, because we
553 * must not interrupt OPCLK. But it should be fine,
554 * because typically the user will request OPCLK to run
555 * at 256fs or 512fs, and for these cases we will also
556 * find an exact MCLK divider configuration - it will
557 * be equal to or double the OPCLK divisor.
558 */
518 ret = wm8978_configure_pll(codec); 559 ret = wm8978_configure_pll(codec);
519 break; 560 break;
520 case WM8978_BCLKDIV: 561 case WM8978_BCLKDIV:
@@ -640,10 +681,6 @@ static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
640 return 0; 681 return 0;
641} 682}
642 683
643/* MCLK dividers */
644static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12};
645static const int mclk_denominator[] = {1, 2, 1, 1, 1, 1, 1, 1};
646
647/* 684/*
648 * Set PCM DAI bit size and sample rate. 685 * Set PCM DAI bit size and sample rate.
649 */ 686 */
@@ -709,9 +746,11 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream,
709 wm8978->f_256fs = params_rate(params) * 256; 746 wm8978->f_256fs = params_rate(params) * 256;
710 747
711 if (wm8978->sysclk == WM8978_MCLK) { 748 if (wm8978->sysclk == WM8978_MCLK) {
749 wm8978->mclk_idx = -1;
712 f_sel = wm8978->f_mclk; 750 f_sel = wm8978->f_mclk;
713 } else { 751 } else {
714 if (!wm8978->f_pllout) { 752 if (!wm8978->f_pllout) {
753 /* We only enter here, if OPCLK is not used */
715 int ret = wm8978_configure_pll(codec); 754 int ret = wm8978_configure_pll(codec);
716 if (ret < 0) 755 if (ret < 0)
717 return ret; 756 return ret;
@@ -719,32 +758,34 @@ static int wm8978_hw_params(struct snd_pcm_substream *substream,
719 f_sel = wm8978->f_pllout; 758 f_sel = wm8978->f_pllout;
720 } 759 }
721 760
722 /* 761 if (wm8978->mclk_idx < 0) {
723 * In some cases it is possible to reconfigure PLL to a higher frequency 762 /* Either MCLK is used directly, or OPCLK is used */
724 * by raising OPCLKDIV, but normally OPCLK is configured to 256 * fs or 763 if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
725 * 512 * fs, so, we should be fine. 764 return -EINVAL;
726 */
727 if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
728 return -EINVAL;
729 765
730 for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) { 766 for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
731 diff = abs(wm8978->f_256fs * 3 - 767 diff = abs(wm8978->f_256fs * 3 -
732 f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]); 768 f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]);
733 769
734 if (diff < diff_best) { 770 if (diff < diff_best) {
735 diff_best = diff; 771 diff_best = diff;
736 best = i; 772 best = i;
737 } 773 }
738 774
739 if (!diff) 775 if (!diff)
740 break; 776 break;
777 }
778 } else {
779 /* OPCLK not used, codec driven by PLL */
780 best = wm8978->mclk_idx;
781 diff = 0;
741 } 782 }
742 783
743 if (diff) 784 if (diff)
744 dev_warn(codec->dev, "Imprecise clock: %u%s\n", 785 dev_warn(codec->dev, "Imprecise sampling rate: %uHz%s\n",
745 f_sel * mclk_denominator[best] / mclk_numerator[best], 786 f_sel * mclk_denominator[best] / mclk_numerator[best] / 256,
746 wm8978->sysclk == WM8978_MCLK ? 787 wm8978->sysclk == WM8978_MCLK ?
747 ", consider using PLL" : ""); 788 ", consider using PLL" : "");
748 789
749 dev_dbg(codec->dev, "%s: fmt %d, rate %u, MCLK divisor #%d\n", __func__, 790 dev_dbg(codec->dev, "%s: fmt %d, rate %u, MCLK divisor #%d\n", __func__,
750 params_format(params), params_rate(params), best); 791 params_format(params), params_rate(params), best);