|
@@ -43,6 +43,8 @@
|
|
|
#define GAIN_AUGMENT 22500
|
|
|
#define SIDETONE_BASE 207000
|
|
|
|
|
|
+/* the maximum frequency of CLK_ADC and CLK_DAC */
|
|
|
+#define CLK_DA_AD_MAX 6144000
|
|
|
|
|
|
static int nau8825_configure_sysclk(struct nau8825 *nau8825,
|
|
|
int clk_id, unsigned int freq);
|
|
@@ -95,6 +97,27 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = {
|
|
|
{ 8, 0x3 },
|
|
|
};
|
|
|
|
|
|
+/* over sampling rate */
|
|
|
+struct nau8825_osr_attr {
|
|
|
+ unsigned int osr;
|
|
|
+ unsigned int clk_src;
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nau8825_osr_attr osr_dac_sel[] = {
|
|
|
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
|
|
|
+ { 256, 0 }, /* OSR 256, SRC 1 */
|
|
|
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
|
|
|
+ { 0, 0 },
|
|
|
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nau8825_osr_attr osr_adc_sel[] = {
|
|
|
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
|
|
|
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
|
|
|
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
|
|
|
+ { 256, 0 }, /* OSR 256, SRC 1 */
|
|
|
+};
|
|
|
+
|
|
|
static const struct reg_default nau8825_reg_defaults[] = {
|
|
|
{ NAU8825_REG_ENA_CTRL, 0x00ff },
|
|
|
{ NAU8825_REG_IIC_ADDR_SET, 0x0 },
|
|
@@ -1179,15 +1202,64 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
|
|
|
{"HPOR", NULL, "Class G"},
|
|
|
};
|
|
|
|
|
|
+static int nau8825_clock_check(struct nau8825 *nau8825,
|
|
|
+ int stream, int rate, int osr)
|
|
|
+{
|
|
|
+ int osrate;
|
|
|
+
|
|
|
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
|
|
|
+ return -EINVAL;
|
|
|
+ osrate = osr_dac_sel[osr].osr;
|
|
|
+ } else {
|
|
|
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
|
|
|
+ return -EINVAL;
|
|
|
+ osrate = osr_adc_sel[osr].osr;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!osrate || rate * osr > CLK_DA_AD_MAX) {
|
|
|
+ dev_err(nau8825->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int nau8825_hw_params(struct snd_pcm_substream *substream,
|
|
|
struct snd_pcm_hw_params *params,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
|
struct snd_soc_codec *codec = dai->codec;
|
|
|
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
|
|
|
- unsigned int val_len = 0;
|
|
|
+ unsigned int val_len = 0, osr;
|
|
|
+
|
|
|
+ nau8825_sema_acquire(nau8825, 3 * HZ);
|
|
|
|
|
|
- nau8825_sema_acquire(nau8825, 2 * HZ);
|
|
|
+ /* CLK_DAC or CLK_ADC = OSR * FS
|
|
|
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
|
|
|
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
|
|
|
+ * values must be selected such that the maximum frequency is less
|
|
|
+ * than 6.144 MHz.
|
|
|
+ */
|
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
+ regmap_read(nau8825->regmap, NAU8825_REG_DAC_CTRL1, &osr);
|
|
|
+ osr &= NAU8825_DAC_OVERSAMPLE_MASK;
|
|
|
+ if (nau8825_clock_check(nau8825, substream->stream,
|
|
|
+ params_rate(params), osr))
|
|
|
+ return -EINVAL;
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
+ NAU8825_CLK_DAC_SRC_MASK,
|
|
|
+ osr_dac_sel[osr].clk_src << NAU8825_CLK_DAC_SRC_SFT);
|
|
|
+ } else {
|
|
|
+ regmap_read(nau8825->regmap, NAU8825_REG_ADC_RATE, &osr);
|
|
|
+ osr &= NAU8825_ADC_SYNC_DOWN_MASK;
|
|
|
+ if (nau8825_clock_check(nau8825, substream->stream,
|
|
|
+ params_rate(params), osr))
|
|
|
+ return -EINVAL;
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
+ NAU8825_CLK_ADC_SRC_MASK,
|
|
|
+ osr_adc_sel[osr].clk_src << NAU8825_CLK_ADC_SRC_SFT);
|
|
|
+ }
|
|
|
|
|
|
switch (params_width(params)) {
|
|
|
case 16:
|
|
@@ -1221,7 +1293,7 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
|
|
|
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
|
|
|
unsigned int ctrl1_val = 0, ctrl2_val = 0;
|
|
|
|
|
|
- nau8825_sema_acquire(nau8825, 2 * HZ);
|
|
|
+ nau8825_sema_acquire(nau8825, 3 * HZ);
|
|
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
@@ -1774,9 +1846,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
|
|
|
* (audible hiss). Set it to something better.
|
|
|
*/
|
|
|
regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
|
|
|
- NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
|
|
|
+ NAU8825_ADC_SYNC_DOWN_MASK | NAU8825_ADC_SINC4_EN,
|
|
|
+ NAU8825_ADC_SYNC_DOWN_64);
|
|
|
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
|
|
|
- NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
|
|
|
+ NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64);
|
|
|
/* Disable DACR/L power */
|
|
|
regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
|
|
|
NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
|
|
@@ -1811,6 +1884,9 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
|
|
|
NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L);
|
|
|
regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
|
|
|
NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R);
|
|
|
+ /* Disable short Frame Sync detection logic */
|
|
|
+ regmap_update_bits(regmap, NAU8825_REG_LEFT_TIME_SLOT,
|
|
|
+ NAU8825_DIS_FS_SHORT_DET, NAU8825_DIS_FS_SHORT_DET);
|
|
|
}
|
|
|
|
|
|
static const struct regmap_config nau8825_regmap_config = {
|
|
@@ -1919,8 +1995,10 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
|
|
|
regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK,
|
|
|
NAU8825_CLK_SRC_MCLK | fll_param->mclk_src);
|
|
|
+ /* Make DSP operate at high speed for better performance. */
|
|
|
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
|
|
|
- NAU8825_FLL_RATIO_MASK, fll_param->ratio);
|
|
|
+ NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK,
|
|
|
+ fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT));
|
|
|
/* FLL 16-bit fractional input */
|
|
|
regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
|
|
|
/* FLL 10-bit integer input */
|
|
@@ -1936,19 +2014,22 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
|
|
|
regmap_update_bits(nau8825->regmap,
|
|
|
NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
|
|
|
if (fll_param->fll_frac) {
|
|
|
+ /* set FLL loop filter enable and cutoff frequency at 500Khz */
|
|
|
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
|
|
|
NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
|
|
|
NAU8825_FLL_FTR_SW_MASK,
|
|
|
NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
|
|
|
NAU8825_FLL_FTR_SW_FILTER);
|
|
|
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
|
|
|
- NAU8825_SDM_EN, NAU8825_SDM_EN);
|
|
|
+ NAU8825_SDM_EN | NAU8825_CUTOFF500,
|
|
|
+ NAU8825_SDM_EN | NAU8825_CUTOFF500);
|
|
|
} else {
|
|
|
+ /* disable FLL loop filter and cutoff frequency */
|
|
|
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
|
|
|
NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
|
|
|
NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU);
|
|
|
- regmap_update_bits(nau8825->regmap,
|
|
|
- NAU8825_REG_FLL6, NAU8825_SDM_EN, 0);
|
|
|
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
|
|
|
+ NAU8825_SDM_EN | NAU8825_CUTOFF500, 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2014,6 +2095,9 @@ static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap)
|
|
|
NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
|
|
|
regmap_update_bits(regmap, NAU8825_REG_FLL6,
|
|
|
NAU8825_DCO_EN, 0);
|
|
|
+ /* Make DSP operate as default setting for power saving. */
|
|
|
+ regmap_update_bits(regmap, NAU8825_REG_FLL1,
|
|
|
+ NAU8825_ICTRL_LATCH_MASK, 0);
|
|
|
}
|
|
|
|
|
|
static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
@@ -2038,7 +2122,7 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
|
* fered by cross talk process, the driver make the playback
|
|
|
* preparation halted until cross talk process finish.
|
|
|
*/
|
|
|
- nau8825_sema_acquire(nau8825, 2 * HZ);
|
|
|
+ nau8825_sema_acquire(nau8825, 3 * HZ);
|
|
|
nau8825_configure_mclk_as_sysclk(regmap);
|
|
|
/* MCLK not changed by clock tree */
|
|
|
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
|
|
@@ -2057,10 +2141,13 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
|
NAU8825_DCO_EN, NAU8825_DCO_EN);
|
|
|
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
|
|
|
- /* Decrease the VCO frequency for power saving */
|
|
|
+ /* Decrease the VCO frequency and make DSP operate
|
|
|
+ * as default setting for power saving.
|
|
|
+ */
|
|
|
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
|
|
|
NAU8825_CLK_MCLK_SRC_MASK, 0xf);
|
|
|
regmap_update_bits(regmap, NAU8825_REG_FLL1,
|
|
|
+ NAU8825_ICTRL_LATCH_MASK |
|
|
|
NAU8825_FLL_RATIO_MASK, 0x10);
|
|
|
regmap_update_bits(regmap, NAU8825_REG_FLL6,
|
|
|
NAU8825_SDM_EN, NAU8825_SDM_EN);
|
|
@@ -2083,9 +2170,14 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
|
* fered by cross talk process, the driver make the playback
|
|
|
* preparation halted until cross talk process finish.
|
|
|
*/
|
|
|
- nau8825_sema_acquire(nau8825, 2 * HZ);
|
|
|
+ nau8825_sema_acquire(nau8825, 3 * HZ);
|
|
|
+ /* Higher FLL reference input frequency can only set lower
|
|
|
+ * gain error, such as 0000 for input reference from MCLK
|
|
|
+ * 12.288Mhz.
|
|
|
+ */
|
|
|
regmap_update_bits(regmap, NAU8825_REG_FLL3,
|
|
|
- NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_MCLK);
|
|
|
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
|
|
|
+ NAU8825_FLL_CLK_SRC_MCLK | 0);
|
|
|
/* Release the semaphone. */
|
|
|
nau8825_sema_release(nau8825);
|
|
|
|
|
@@ -2100,9 +2192,17 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
|
* fered by cross talk process, the driver make the playback
|
|
|
* preparation halted until cross talk process finish.
|
|
|
*/
|
|
|
- nau8825_sema_acquire(nau8825, 2 * HZ);
|
|
|
+ nau8825_sema_acquire(nau8825, 3 * HZ);
|
|
|
+ /* If FLL reference input is from low frequency source,
|
|
|
+ * higher error gain can apply such as 0xf which has
|
|
|
+ * the most sensitive gain error correction threshold,
|
|
|
+ * Therefore, FLL has the most accurate DCO to
|
|
|
+ * target frequency.
|
|
|
+ */
|
|
|
regmap_update_bits(regmap, NAU8825_REG_FLL3,
|
|
|
- NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_BLK);
|
|
|
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
|
|
|
+ NAU8825_FLL_CLK_SRC_BLK |
|
|
|
+ (0xf << NAU8825_GAIN_ERR_SFT));
|
|
|
/* Release the semaphone. */
|
|
|
nau8825_sema_release(nau8825);
|
|
|
|
|
@@ -2118,9 +2218,17 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
|
|
|
* fered by cross talk process, the driver make the playback
|
|
|
* preparation halted until cross talk process finish.
|
|
|
*/
|
|
|
- nau8825_sema_acquire(nau8825, 2 * HZ);
|
|
|
+ nau8825_sema_acquire(nau8825, 3 * HZ);
|
|
|
+ /* If FLL reference input is from low frequency source,
|
|
|
+ * higher error gain can apply such as 0xf which has
|
|
|
+ * the most sensitive gain error correction threshold,
|
|
|
+ * Therefore, FLL has the most accurate DCO to
|
|
|
+ * target frequency.
|
|
|
+ */
|
|
|
regmap_update_bits(regmap, NAU8825_REG_FLL3,
|
|
|
- NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_FS);
|
|
|
+ NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK,
|
|
|
+ NAU8825_FLL_CLK_SRC_FS |
|
|
|
+ (0xf << NAU8825_GAIN_ERR_SFT));
|
|
|
/* Release the semaphone. */
|
|
|
nau8825_sema_release(nau8825);
|
|
|
|