diff options
Diffstat (limited to 'sound/soc/codecs/wm8978.c')
-rw-r--r-- | sound/soc/codecs/wm8978.c | 115 |
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 */ | ||
408 | static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12}; | ||
409 | static 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 | */ | ||
418 | static 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 */ | ||
644 | static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12}; | ||
645 | static 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); |