aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/tlv320aic3x.c
diff options
context:
space:
mode:
authorPeter Meerwald <pmeerw@pmeerw.net>2009-12-14 08:44:56 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-12-16 15:59:53 -0500
commit255173b40db448ce063a2caa680a552fb637ad20 (patch)
tree9004d64e426fffc3ad3cf594a5b89cc6bb386b90 /sound/soc/codecs/tlv320aic3x.c
parent3497b91946a3df42830c826939424d98251a3b0d (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.c75
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
900found:
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);