aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8978.c
diff options
context:
space:
mode:
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);