diff options
author | Peter Meerwald <pmeerw@pmeerw.net> | 2009-12-14 08:44:56 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-12-16 15:59:53 -0500 |
commit | 255173b40db448ce063a2caa680a552fb637ad20 (patch) | |
tree | 9004d64e426fffc3ad3cf594a5b89cc6bb386b90 /sound/soc/codecs/tlv320aic3x.c | |
parent | 3497b91946a3df42830c826939424d98251a3b0d (diff) |
ASoC: PLL computation in TLV320AIC3x SoC driver
fix precision of PLL computation for TLV320AIC3x SoC driver,
test results are at http://pmeerw.net/clk
Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Acked-by: Vladimir Barinov <vova.barinov@gmail.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/tlv320aic3x.c')
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 75 |
1 files changed, 49 insertions, 26 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 2b4dc2b0b017..5a8f53ce2250 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c | |||
@@ -765,9 +765,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, | |||
765 | struct snd_soc_codec *codec = socdev->card->codec; | 765 | struct snd_soc_codec *codec = socdev->card->codec; |
766 | struct aic3x_priv *aic3x = codec->private_data; | 766 | struct aic3x_priv *aic3x = codec->private_data; |
767 | int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; | 767 | int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; |
768 | u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; | 768 | u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; |
769 | u16 pll_d = 1; | 769 | u16 d, pll_d = 1; |
770 | u8 reg; | 770 | u8 reg; |
771 | int clk; | ||
771 | 772 | ||
772 | /* select data word length */ | 773 | /* select data word length */ |
773 | data = | 774 | data = |
@@ -833,48 +834,70 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, | |||
833 | if (bypass_pll) | 834 | if (bypass_pll) |
834 | return 0; | 835 | return 0; |
835 | 836 | ||
836 | /* Use PLL | 837 | /* Use PLL, compute apropriate setup for j, d, r and p, the closest |
837 | * find an apropriate setup for j, d, r and p by iterating over | 838 | * one wins the game. Try with d==0 first, next with d!=0. |
838 | * p and r - j and d are calculated for each fraction. | 839 | * Constraints for j are according to the datasheet. |
839 | * Up to 128 values are probed, the closest one wins the game. | ||
840 | * The sysclk is divided by 1000 to prevent integer overflows. | 840 | * The sysclk is divided by 1000 to prevent integer overflows. |
841 | */ | 841 | */ |
842 | |||
842 | codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000); | 843 | codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000); |
843 | 844 | ||
844 | for (r = 1; r <= 16; r++) | 845 | for (r = 1; r <= 16; r++) |
845 | for (p = 1; p <= 8; p++) { | 846 | for (p = 1; p <= 8; p++) { |
846 | int clk, tmp = (codec_clk * pll_r * 10) / pll_p; | 847 | for (j = 4; j <= 55; j++) { |
847 | u8 j = tmp / 10000; | 848 | /* This is actually 1000*((j+(d/10000))*r)/p |
848 | u16 d = tmp % 10000; | 849 | * The term had to be converted to get |
850 | * rid of the division by 10000; d = 0 here | ||
851 | */ | ||
852 | int clk = (1000 * j * r) / p; | ||
853 | |||
854 | /* Check whether this values get closer than | ||
855 | * the best ones we had before | ||
856 | */ | ||
857 | if (abs(codec_clk - clk) < | ||
858 | abs(codec_clk - last_clk)) { | ||
859 | pll_j = j; pll_d = 0; | ||
860 | pll_r = r; pll_p = p; | ||
861 | last_clk = clk; | ||
862 | } | ||
863 | |||
864 | /* Early exit for exact matches */ | ||
865 | if (clk == codec_clk) | ||
866 | goto found; | ||
867 | } | ||
868 | } | ||
849 | 869 | ||
850 | if (j > 63) | 870 | /* try with d != 0 */ |
851 | continue; | 871 | for (p = 1; p <= 8; p++) { |
872 | j = codec_clk * p / 1000; | ||
852 | 873 | ||
853 | if (d != 0 && aic3x->sysclk < 10000000) | 874 | if (j < 4 || j > 11) |
854 | continue; | 875 | continue; |
855 | 876 | ||
856 | /* This is actually 1000 * ((j + (d/10000)) * r) / p | 877 | /* do not use codec_clk here since we'd loose precision */ |
857 | * The term had to be converted to get rid of the | 878 | d = ((2048 * p * fsref) - j * aic3x->sysclk) |
858 | * division by 10000 */ | 879 | * 100 / (aic3x->sysclk/100); |
859 | clk = ((10000 * j * r) + (d * r)) / (10 * p); | ||
860 | 880 | ||
861 | /* check whether this values get closer than the best | 881 | clk = (10000 * j + d) / (10 * p); |
862 | * ones we had before */ | ||
863 | if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) { | ||
864 | pll_j = j; pll_d = d; pll_r = r; pll_p = p; | ||
865 | last_clk = clk; | ||
866 | } | ||
867 | 882 | ||
868 | /* Early exit for exact matches */ | 883 | /* check whether this values get closer than the best |
869 | if (clk == codec_clk) | 884 | * ones we had before */ |
870 | break; | 885 | if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) { |
886 | pll_j = j; pll_d = d; pll_r = 1; pll_p = p; | ||
887 | last_clk = clk; | ||
871 | } | 888 | } |
872 | 889 | ||
890 | /* Early exit for exact matches */ | ||
891 | if (clk == codec_clk) | ||
892 | goto found; | ||
893 | } | ||
894 | |||
873 | if (last_clk == 0) { | 895 | if (last_clk == 0) { |
874 | printk(KERN_ERR "%s(): unable to setup PLL\n", __func__); | 896 | printk(KERN_ERR "%s(): unable to setup PLL\n", __func__); |
875 | return -EINVAL; | 897 | return -EINVAL; |
876 | } | 898 | } |
877 | 899 | ||
900 | found: | ||
878 | data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); | 901 | data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); |
879 | aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); | 902 | aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); |
880 | aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT); | 903 | aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT); |