Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: nau8825: Add FLL configuration

snd_soc_codec_driver.set_pll is implemented to configure the FLL.
The codec internal SYSCLK can be from either the MCLK pin directly,
or the FLL. This is configured by snd_soc_codec_driver.set_pll.

Signed-off-by: Ben Zhang <benzh@chromium.org>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Ben Zhang and committed by
Mark Brown
c86ba612 b3681308

+186 -5
+163
sound/soc/codecs/nau8825.c
··· 17 17 #include <linux/slab.h> 18 18 #include <linux/clk.h> 19 19 #include <linux/acpi.h> 20 + #include <linux/math64.h> 20 21 21 22 #include <sound/initval.h> 22 23 #include <sound/tlv.h> ··· 29 28 30 29 31 30 #include "nau8825.h" 31 + 32 + #define NAU_FREF_MAX 13500000 33 + #define NAU_FVCO_MAX 100000000 34 + #define NAU_FVCO_MIN 90000000 35 + 36 + struct nau8825_fll { 37 + int mclk_src; 38 + int ratio; 39 + int fll_frac; 40 + int fll_int; 41 + int clk_ref_div; 42 + }; 43 + 44 + struct nau8825_fll_attr { 45 + unsigned int param; 46 + unsigned int val; 47 + }; 48 + 49 + /* scaling for mclk from sysclk_src output */ 50 + static const struct nau8825_fll_attr mclk_src_scaling[] = { 51 + { 1, 0x0 }, 52 + { 2, 0x2 }, 53 + { 4, 0x3 }, 54 + { 8, 0x4 }, 55 + { 16, 0x5 }, 56 + { 32, 0x6 }, 57 + { 3, 0x7 }, 58 + { 6, 0xa }, 59 + { 12, 0xb }, 60 + { 24, 0xc }, 61 + { 48, 0xd }, 62 + { 96, 0xe }, 63 + { 5, 0xf }, 64 + }; 65 + 66 + /* ratio for input clk freq */ 67 + static const struct nau8825_fll_attr fll_ratio[] = { 68 + { 512000, 0x01 }, 69 + { 256000, 0x02 }, 70 + { 128000, 0x04 }, 71 + { 64000, 0x08 }, 72 + { 32000, 0x10 }, 73 + { 8000, 0x20 }, 74 + { 4000, 0x40 }, 75 + }; 76 + 77 + static const struct nau8825_fll_attr fll_pre_scalar[] = { 78 + { 1, 0x0 }, 79 + { 2, 0x1 }, 80 + { 4, 0x2 }, 81 + { 8, 0x3 }, 82 + }; 32 83 33 84 static const struct reg_default nau8825_reg_defaults[] = { 34 85 { NAU8825_REG_ENA_CTRL, 0x00ff }, ··· 861 808 return 0; 862 809 } 863 810 811 + /** 812 + * nau8825_calc_fll_param - Calculate FLL parameters. 813 + * @fll_in: external clock provided to codec. 814 + * @fs: sampling rate. 815 + * @fll_param: Pointer to structure of FLL parameters. 816 + * 817 + * Calculate FLL parameters to configure codec. 818 + * 819 + * Returns 0 for success or negative error code. 820 + */ 821 + static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, 822 + struct nau8825_fll *fll_param) 823 + { 824 + u64 fvco; 825 + unsigned int fref, i; 826 + 827 + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing 828 + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. 829 + * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK 830 + */ 831 + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { 832 + fref = fll_in / fll_pre_scalar[i].param; 833 + if (fref <= NAU_FREF_MAX) 834 + break; 835 + } 836 + if (i == ARRAY_SIZE(fll_pre_scalar)) 837 + return -EINVAL; 838 + fll_param->clk_ref_div = fll_pre_scalar[i].val; 839 + 840 + /* Choose the FLL ratio based on FREF */ 841 + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { 842 + if (fref >= fll_ratio[i].param) 843 + break; 844 + } 845 + if (i == ARRAY_SIZE(fll_ratio)) 846 + return -EINVAL; 847 + fll_param->ratio = fll_ratio[i].val; 848 + 849 + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. 850 + * FDCO must be within the 90MHz - 100MHz or the FFL cannot be 851 + * guaranteed across the full range of operation. 852 + * FDCO = freq_out * 2 * mclk_src_scaling 853 + */ 854 + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { 855 + fvco = 256 * fs * 2 * mclk_src_scaling[i].param; 856 + if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX) 857 + break; 858 + } 859 + if (i == ARRAY_SIZE(mclk_src_scaling)) 860 + return -EINVAL; 861 + fll_param->mclk_src = mclk_src_scaling[i].val; 862 + 863 + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional 864 + * input based on FDCO, FREF and FLL ratio. 865 + */ 866 + fvco = div_u64(fvco << 16, fref * fll_param->ratio); 867 + fll_param->fll_int = (fvco >> 16) & 0x3FF; 868 + fll_param->fll_frac = fvco & 0xFFFF; 869 + return 0; 870 + } 871 + 872 + static void nau8825_fll_apply(struct nau8825 *nau8825, 873 + struct nau8825_fll *fll_param) 874 + { 875 + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, 876 + NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src); 877 + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, 878 + NAU8825_FLL_RATIO_MASK, fll_param->ratio); 879 + /* FLL 16-bit fractional input */ 880 + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); 881 + /* FLL 10-bit integer input */ 882 + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, 883 + NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); 884 + /* FLL pre-scaler */ 885 + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, 886 + NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); 887 + /* select divided VCO input */ 888 + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, 889 + NAU8825_FLL_FILTER_SW_MASK, 0x0000); 890 + /* FLL sigma delta modulator enable */ 891 + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, 892 + NAU8825_SDM_EN_MASK, NAU8825_SDM_EN); 893 + } 894 + 895 + /* freq_out must be 256*Fs in order to achieve the best performance */ 896 + static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source, 897 + unsigned int freq_in, unsigned int freq_out) 898 + { 899 + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); 900 + struct nau8825_fll fll_param; 901 + int ret, fs; 902 + 903 + fs = freq_out / 256; 904 + ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); 905 + if (ret < 0) { 906 + dev_err(codec->dev, "Unsupported input clock %d\n", freq_in); 907 + return ret; 908 + } 909 + dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", 910 + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, 911 + fll_param.fll_int, fll_param.clk_ref_div); 912 + 913 + nau8825_fll_apply(nau8825, &fll_param); 914 + mdelay(2); 915 + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, 916 + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); 917 + return 0; 918 + } 919 + 864 920 static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, 865 921 unsigned int freq) 866 922 { ··· 1082 920 static struct snd_soc_codec_driver nau8825_codec_driver = { 1083 921 .probe = nau8825_codec_probe, 1084 922 .set_sysclk = nau8825_set_sysclk, 923 + .set_pll = nau8825_set_pll, 1085 924 .set_bias_level = nau8825_set_bias_level, 1086 925 .suspend_bias_off = true, 1087 926
+23 -5
sound/soc/codecs/nau8825.h
··· 101 101 #define NAU8825_ENABLE_SAR_SFT 1 102 102 103 103 /* CLK_DIVIDER (0x3) */ 104 - #define NAU8825_CLK_SRC_SFT 15 105 - #define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) 106 - #define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) 107 - #define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) 104 + #define NAU8825_CLK_SRC_SFT 15 105 + #define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) 106 + #define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) 107 + #define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) 108 + #define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) 109 + 110 + /* FLL1 (0x04) */ 111 + #define NAU8825_FLL_RATIO_MASK (0x7f << 0) 112 + 113 + /* FLL3 (0x06) */ 114 + #define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) 115 + 116 + /* FLL4 (0x07) */ 117 + #define NAU8825_FLL_REF_DIV_MASK (0x3 << 10) 118 + 119 + /* FLL5 (0x08) */ 120 + #define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14) 108 121 109 122 /* FLL6 (0x9) */ 110 - #define NAU8825_DCO_EN (1 << 15) 123 + #define NAU8825_DCO_EN_MASK (0x1 << 15) 124 + #define NAU8825_DCO_EN (0x1 << 15) 125 + #define NAU8825_DCO_DIS (0x0 << 15) 126 + #define NAU8825_SDM_EN_MASK (0x1 << 14) 127 + #define NAU8825_SDM_EN (0x1 << 14) 128 + #define NAU8825_SDM_DIS (0x0 << 14) 111 129 112 130 /* HSD_CTRL (0xc) */ 113 131 #define NAU8825_HSD_AUTO_MODE (1 << 6)