diff options
-rw-r--r-- | sound/soc/codecs/wm8960.c | 93 |
1 files changed, 64 insertions, 29 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index ce159f13e7a4..36c84549da23 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c | |||
@@ -672,10 +672,70 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk, | |||
672 | return *bclk_idx; | 672 | return *bclk_idx; |
673 | } | 673 | } |
674 | 674 | ||
675 | /** | ||
676 | * wm8960_configure_pll - checks if there is a PLL out frequency available | ||
677 | * The PLL out frequency must be chosen such that: | ||
678 | * - sysclk = lrclk * dac_divs | ||
679 | * - freq_out = sysclk * sysclk_divs | ||
680 | * - 10 * sysclk = bclk * bclk_divs | ||
681 | * | ||
682 | * @codec: codec structure | ||
683 | * @freq_in: input frequency used to derive freq out via PLL | ||
684 | * @sysclk_idx: sysclk_divs index for found sysclk | ||
685 | * @dac_idx: dac_divs index for found lrclk | ||
686 | * @bclk_idx: bclk_divs index for found bclk | ||
687 | * | ||
688 | * Returns: | ||
689 | * -1, in case no PLL frequency out available was found | ||
690 | * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using | ||
691 | * (@sysclk_idx, @dac_idx, @bclk_idx) dividers | ||
692 | */ | ||
693 | static | ||
694 | int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, | ||
695 | int *sysclk_idx, int *dac_idx, int *bclk_idx) | ||
696 | { | ||
697 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
698 | int sysclk, bclk, lrclk, freq_out; | ||
699 | int diff, best_freq_out; | ||
700 | int i, j, k; | ||
701 | |||
702 | bclk = wm8960->bclk; | ||
703 | lrclk = wm8960->lrclk; | ||
704 | |||
705 | *bclk_idx = -1; | ||
706 | |||
707 | for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { | ||
708 | if (sysclk_divs[i] == -1) | ||
709 | continue; | ||
710 | for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { | ||
711 | sysclk = lrclk * dac_divs[j]; | ||
712 | freq_out = sysclk * sysclk_divs[i]; | ||
713 | |||
714 | for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { | ||
715 | if (!is_pll_freq_available(freq_in, freq_out)) | ||
716 | continue; | ||
717 | |||
718 | diff = sysclk - bclk * bclk_divs[k] / 10; | ||
719 | if (diff == 0) { | ||
720 | *sysclk_idx = i; | ||
721 | *dac_idx = j; | ||
722 | *bclk_idx = k; | ||
723 | best_freq_out = freq_out; | ||
724 | break; | ||
725 | } | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | |||
730 | if (*bclk_idx != -1) | ||
731 | wm8960_set_pll(codec, freq_in, best_freq_out); | ||
732 | |||
733 | return *bclk_idx; | ||
734 | } | ||
675 | static int wm8960_configure_clocking(struct snd_soc_codec *codec) | 735 | static int wm8960_configure_clocking(struct snd_soc_codec *codec) |
676 | { | 736 | { |
677 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | 737 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); |
678 | int sysclk, bclk, lrclk, freq_out, freq_in; | 738 | int freq_out, freq_in; |
679 | u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); | 739 | u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); |
680 | int i, j, k; | 740 | int i, j, k; |
681 | int ret; | 741 | int ret; |
@@ -692,8 +752,6 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) | |||
692 | } | 752 | } |
693 | 753 | ||
694 | freq_in = wm8960->freq_in; | 754 | freq_in = wm8960->freq_in; |
695 | bclk = wm8960->bclk; | ||
696 | lrclk = wm8960->lrclk; | ||
697 | /* | 755 | /* |
698 | * If it's sysclk auto mode, check if the MCLK can provide sysclk or | 756 | * If it's sysclk auto mode, check if the MCLK can provide sysclk or |
699 | * not. If MCLK can provide sysclk, using MCLK to provide sysclk | 757 | * not. If MCLK can provide sysclk, using MCLK to provide sysclk |
@@ -720,33 +778,10 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) | |||
720 | return -EINVAL; | 778 | return -EINVAL; |
721 | } | 779 | } |
722 | } | 780 | } |
723 | /* get a available pll out frequency and set pll */ | ||
724 | for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { | ||
725 | if (sysclk_divs[i] == -1) | ||
726 | continue; | ||
727 | for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { | ||
728 | sysclk = lrclk * dac_divs[j]; | ||
729 | freq_out = sysclk * sysclk_divs[i]; | ||
730 | |||
731 | for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { | ||
732 | if (sysclk == bclk * bclk_divs[k] / 10 && | ||
733 | is_pll_freq_available(freq_in, freq_out)) { | ||
734 | wm8960_set_pll(codec, | ||
735 | freq_in, freq_out); | ||
736 | break; | ||
737 | } else { | ||
738 | continue; | ||
739 | } | ||
740 | } | ||
741 | if (k != ARRAY_SIZE(bclk_divs)) | ||
742 | break; | ||
743 | } | ||
744 | if (j != ARRAY_SIZE(dac_divs)) | ||
745 | break; | ||
746 | } | ||
747 | 781 | ||
748 | if (i == ARRAY_SIZE(sysclk_divs)) { | 782 | ret = wm8960_configure_pll(codec, freq_in, &i, &j, &k); |
749 | dev_err(codec->dev, "failed to configure clock\n"); | 783 | if (ret < 0) { |
784 | dev_err(codec->dev, "failed to configure clock via PLL\n"); | ||
750 | return -EINVAL; | 785 | return -EINVAL; |
751 | } | 786 | } |
752 | 787 | ||