diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8400.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index b7350c25b61..510efa60400 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c | |||
@@ -70,6 +70,7 @@ struct wm8400_priv { | |||
70 | unsigned int sysclk; | 70 | unsigned int sysclk; |
71 | unsigned int pcmclk; | 71 | unsigned int pcmclk; |
72 | struct work_struct work; | 72 | struct work_struct work; |
73 | int fll_in, fll_out; | ||
73 | }; | 74 | }; |
74 | 75 | ||
75 | static inline unsigned int wm8400_read(struct snd_soc_codec *codec, | 76 | static inline unsigned int wm8400_read(struct snd_soc_codec *codec, |
@@ -931,6 +932,133 @@ static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
931 | return 0; | 932 | return 0; |
932 | } | 933 | } |
933 | 934 | ||
935 | struct fll_factors { | ||
936 | u16 n; | ||
937 | u16 k; | ||
938 | u16 outdiv; | ||
939 | u16 fratio; | ||
940 | u16 freq_ref; | ||
941 | }; | ||
942 | |||
943 | #define FIXED_FLL_SIZE ((1 << 16) * 10) | ||
944 | |||
945 | static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors, | ||
946 | unsigned int Fref, unsigned int Fout) | ||
947 | { | ||
948 | u64 Kpart; | ||
949 | unsigned int K, Nmod, target; | ||
950 | |||
951 | factors->outdiv = 2; | ||
952 | while (Fout * factors->outdiv < 90000000 || | ||
953 | Fout * factors->outdiv > 100000000) { | ||
954 | factors->outdiv *= 2; | ||
955 | if (factors->outdiv > 32) { | ||
956 | dev_err(wm8400->wm8400->dev, | ||
957 | "Unsupported FLL output frequency %dHz\n", | ||
958 | Fout); | ||
959 | return -EINVAL; | ||
960 | } | ||
961 | } | ||
962 | target = Fout * factors->outdiv; | ||
963 | factors->outdiv = factors->outdiv >> 2; | ||
964 | |||
965 | if (Fref < 48000) | ||
966 | factors->freq_ref = 1; | ||
967 | else | ||
968 | factors->freq_ref = 0; | ||
969 | |||
970 | if (Fref < 1000000) | ||
971 | factors->fratio = 9; | ||
972 | else | ||
973 | factors->fratio = 0; | ||
974 | |||
975 | /* Ensure we have a fractional part */ | ||
976 | do { | ||
977 | if (Fref < 1000000) | ||
978 | factors->fratio--; | ||
979 | else | ||
980 | factors->fratio++; | ||
981 | |||
982 | if (factors->fratio < 1 || factors->fratio > 8) { | ||
983 | dev_err(wm8400->wm8400->dev, | ||
984 | "Unable to calculate FRATIO\n"); | ||
985 | return -EINVAL; | ||
986 | } | ||
987 | |||
988 | factors->n = target / (Fref * factors->fratio); | ||
989 | Nmod = target % (Fref * factors->fratio); | ||
990 | } while (Nmod == 0); | ||
991 | |||
992 | /* Calculate fractional part - scale up so we can round. */ | ||
993 | Kpart = FIXED_FLL_SIZE * (long long)Nmod; | ||
994 | |||
995 | do_div(Kpart, (Fref * factors->fratio)); | ||
996 | |||
997 | K = Kpart & 0xFFFFFFFF; | ||
998 | |||
999 | if ((K % 10) >= 5) | ||
1000 | K += 5; | ||
1001 | |||
1002 | /* Move down to proper range now rounding is done */ | ||
1003 | factors->k = K / 10; | ||
1004 | |||
1005 | dev_dbg(wm8400->wm8400->dev, | ||
1006 | "FLL: Fref=%d Fout=%d N=%x K=%x, FRATIO=%x OUTDIV=%x\n", | ||
1007 | Fref, Fout, | ||
1008 | factors->n, factors->k, factors->fratio, factors->outdiv); | ||
1009 | |||
1010 | return 0; | ||
1011 | } | ||
1012 | |||
1013 | static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, | ||
1014 | unsigned int freq_in, unsigned int freq_out) | ||
1015 | { | ||
1016 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1017 | struct wm8400_priv *wm8400 = codec->private_data; | ||
1018 | struct fll_factors factors; | ||
1019 | int ret; | ||
1020 | u16 reg; | ||
1021 | |||
1022 | if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out) | ||
1023 | return 0; | ||
1024 | |||
1025 | if (freq_out != 0) { | ||
1026 | ret = fll_factors(wm8400, &factors, freq_in, freq_out); | ||
1027 | if (ret != 0) | ||
1028 | return ret; | ||
1029 | } | ||
1030 | |||
1031 | wm8400->fll_out = freq_out; | ||
1032 | wm8400->fll_in = freq_in; | ||
1033 | |||
1034 | /* We *must* disable the FLL before any changes */ | ||
1035 | reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_2); | ||
1036 | reg &= ~WM8400_FLL_ENA; | ||
1037 | wm8400_write(codec, WM8400_POWER_MANAGEMENT_2, reg); | ||
1038 | |||
1039 | reg = wm8400_read(codec, WM8400_FLL_CONTROL_1); | ||
1040 | reg &= ~WM8400_FLL_OSC_ENA; | ||
1041 | wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); | ||
1042 | |||
1043 | if (freq_out == 0) | ||
1044 | return 0; | ||
1045 | |||
1046 | reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK); | ||
1047 | reg |= WM8400_FLL_FRAC | factors.fratio; | ||
1048 | reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT; | ||
1049 | wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); | ||
1050 | |||
1051 | wm8400_write(codec, WM8400_FLL_CONTROL_2, factors.k); | ||
1052 | wm8400_write(codec, WM8400_FLL_CONTROL_3, factors.n); | ||
1053 | |||
1054 | reg = wm8400_read(codec, WM8400_FLL_CONTROL_4); | ||
1055 | reg &= WM8400_FLL_OUTDIV_MASK; | ||
1056 | reg |= factors.outdiv; | ||
1057 | wm8400_write(codec, WM8400_FLL_CONTROL_4, reg); | ||
1058 | |||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
934 | /* | 1062 | /* |
935 | * Sets ADC and Voice DAC format. | 1063 | * Sets ADC and Voice DAC format. |
936 | */ | 1064 | */ |
@@ -1188,6 +1316,7 @@ static struct snd_soc_dai_ops wm8400_dai_ops = { | |||
1188 | .set_fmt = wm8400_set_dai_fmt, | 1316 | .set_fmt = wm8400_set_dai_fmt, |
1189 | .set_clkdiv = wm8400_set_dai_clkdiv, | 1317 | .set_clkdiv = wm8400_set_dai_clkdiv, |
1190 | .set_sysclk = wm8400_set_dai_sysclk, | 1318 | .set_sysclk = wm8400_set_dai_sysclk, |
1319 | .set_pll = wm8400_set_dai_pll, | ||
1191 | }; | 1320 | }; |
1192 | 1321 | ||
1193 | /* | 1322 | /* |