|
@@ -17,6 +17,7 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/acpi.h>
|
|
|
+#include <linux/math64.h>
|
|
|
|
|
|
#include <sound/initval.h>
|
|
|
#include <sound/tlv.h>
|
|
@@ -29,6 +30,58 @@
|
|
|
|
|
|
#include "nau8825.h"
|
|
|
|
|
|
+#define NAU_FREF_MAX 13500000
|
|
|
+#define NAU_FVCO_MAX 100000000
|
|
|
+#define NAU_FVCO_MIN 90000000
|
|
|
+
|
|
|
+struct nau8825_fll {
|
|
|
+ int mclk_src;
|
|
|
+ int ratio;
|
|
|
+ int fll_frac;
|
|
|
+ int fll_int;
|
|
|
+ int clk_ref_div;
|
|
|
+};
|
|
|
+
|
|
|
+struct nau8825_fll_attr {
|
|
|
+ unsigned int param;
|
|
|
+ unsigned int val;
|
|
|
+};
|
|
|
+
|
|
|
+/* scaling for mclk from sysclk_src output */
|
|
|
+static const struct nau8825_fll_attr mclk_src_scaling[] = {
|
|
|
+ { 1, 0x0 },
|
|
|
+ { 2, 0x2 },
|
|
|
+ { 4, 0x3 },
|
|
|
+ { 8, 0x4 },
|
|
|
+ { 16, 0x5 },
|
|
|
+ { 32, 0x6 },
|
|
|
+ { 3, 0x7 },
|
|
|
+ { 6, 0xa },
|
|
|
+ { 12, 0xb },
|
|
|
+ { 24, 0xc },
|
|
|
+ { 48, 0xd },
|
|
|
+ { 96, 0xe },
|
|
|
+ { 5, 0xf },
|
|
|
+};
|
|
|
+
|
|
|
+/* ratio for input clk freq */
|
|
|
+static const struct nau8825_fll_attr fll_ratio[] = {
|
|
|
+ { 512000, 0x01 },
|
|
|
+ { 256000, 0x02 },
|
|
|
+ { 128000, 0x04 },
|
|
|
+ { 64000, 0x08 },
|
|
|
+ { 32000, 0x10 },
|
|
|
+ { 8000, 0x20 },
|
|
|
+ { 4000, 0x40 },
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nau8825_fll_attr fll_pre_scalar[] = {
|
|
|
+ { 1, 0x0 },
|
|
|
+ { 2, 0x1 },
|
|
|
+ { 4, 0x2 },
|
|
|
+ { 8, 0x3 },
|
|
|
+};
|
|
|
+
|
|
|
static const struct reg_default nau8825_reg_defaults[] = {
|
|
|
{ NAU8825_REG_ENA_CTRL, 0x00ff },
|
|
|
{ NAU8825_REG_CLK_DIVIDER, 0x0050 },
|
|
@@ -808,6 +861,115 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * nau8825_calc_fll_param - Calculate FLL parameters.
|
|
|
+ * @fll_in: external clock provided to codec.
|
|
|
+ * @fs: sampling rate.
|
|
|
+ * @fll_param: Pointer to structure of FLL parameters.
|
|
|
+ *
|
|
|
+ * Calculate FLL parameters to configure codec.
|
|
|
+ *
|
|
|
+ * Returns 0 for success or negative error code.
|
|
|
+ */
|
|
|
+static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
|
|
|
+ struct nau8825_fll *fll_param)
|
|
|
+{
|
|
|
+ u64 fvco;
|
|
|
+ unsigned int fref, i;
|
|
|
+
|
|
|
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
|
|
|
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
|
|
|
+ * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
|
|
|
+ */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
|
|
|
+ fref = fll_in / fll_pre_scalar[i].param;
|
|
|
+ if (fref <= NAU_FREF_MAX)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i == ARRAY_SIZE(fll_pre_scalar))
|
|
|
+ return -EINVAL;
|
|
|
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
|
|
|
+
|
|
|
+ /* Choose the FLL ratio based on FREF */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
|
|
|
+ if (fref >= fll_ratio[i].param)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i == ARRAY_SIZE(fll_ratio))
|
|
|
+ return -EINVAL;
|
|
|
+ fll_param->ratio = fll_ratio[i].val;
|
|
|
+
|
|
|
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
|
|
|
+ * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
|
|
|
+ * guaranteed across the full range of operation.
|
|
|
+ * FDCO = freq_out * 2 * mclk_src_scaling
|
|
|
+ */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
|
|
|
+ fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
|
|
|
+ if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i == ARRAY_SIZE(mclk_src_scaling))
|
|
|
+ return -EINVAL;
|
|
|
+ fll_param->mclk_src = mclk_src_scaling[i].val;
|
|
|
+
|
|
|
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
|
|
|
+ * input based on FDCO, FREF and FLL ratio.
|
|
|
+ */
|
|
|
+ fvco = div_u64(fvco << 16, fref * fll_param->ratio);
|
|
|
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
|
|
|
+ fll_param->fll_frac = fvco & 0xFFFF;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void nau8825_fll_apply(struct nau8825 *nau8825,
|
|
|
+ struct nau8825_fll *fll_param)
|
|
|
+{
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
+ NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
|
|
|
+ NAU8825_FLL_RATIO_MASK, fll_param->ratio);
|
|
|
+ /* FLL 16-bit fractional input */
|
|
|
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
|
|
|
+ /* FLL 10-bit integer input */
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
|
|
|
+ NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
|
|
|
+ /* FLL pre-scaler */
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
|
|
|
+ NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
|
|
|
+ /* select divided VCO input */
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
|
|
|
+ NAU8825_FLL_FILTER_SW_MASK, 0x0000);
|
|
|
+ /* FLL sigma delta modulator enable */
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
|
|
|
+ NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
|
|
|
+}
|
|
|
+
|
|
|
+/* freq_out must be 256*Fs in order to achieve the best performance */
|
|
|
+static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
|
|
|
+ unsigned int freq_in, unsigned int freq_out)
|
|
|
+{
|
|
|
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
|
|
|
+ struct nau8825_fll fll_param;
|
|
|
+ int ret, fs;
|
|
|
+
|
|
|
+ fs = freq_out / 256;
|
|
|
+ ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
|
|
|
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
|
|
|
+ fll_param.fll_int, fll_param.clk_ref_div);
|
|
|
+
|
|
|
+ nau8825_fll_apply(nau8825, &fll_param);
|
|
|
+ mdelay(2);
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
|
unsigned int freq)
|
|
|
{
|
|
@@ -920,6 +1082,7 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
|
|
|
static struct snd_soc_codec_driver nau8825_codec_driver = {
|
|
|
.probe = nau8825_codec_probe,
|
|
|
.set_sysclk = nau8825_set_sysclk,
|
|
|
+ .set_pll = nau8825_set_pll,
|
|
|
.set_bias_level = nau8825_set_bias_level,
|
|
|
.suspend_bias_off = true,
|
|
|
|