summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/nau8825.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/nau8825.c')
-rw-r--r--sound/soc/codecs/nau8825.c150
1 files changed, 129 insertions, 21 deletions
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index f4eb999761a4..9e0e4ddf128e 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -53,6 +53,7 @@ struct nau8825_fll {
int mclk_src;
int ratio;
int fll_frac;
+ int fll_frac_num;
int fll_int;
int clk_ref_div;
};
@@ -178,6 +179,8 @@ static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_CLASSG_CTRL, 0x0 },
{ NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
{ NAU8825_REG_MISC_CTRL, 0x0 },
+ { NAU8825_REG_FLL2_LOWER, 0x0 },
+ { NAU8825_REG_FLL2_UPPER, 0x0 },
{ NAU8825_REG_BIAS_ADJ, 0x0 },
{ NAU8825_REG_TRIM_SETTINGS, 0x0 },
{ NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
@@ -200,6 +203,23 @@ static struct reg_default nau8825_xtalk_baktab[] = {
{ NAU8825_REG_DACR_CTRL, 0x02cf },
};
+/* The regmap patch for Rev C */
+static const struct reg_sequence nau8825_regmap_patch[] = {
+ { NAU8825_REG_FLL2, 0x0000 },
+ { NAU8825_REG_FLL4, 0x8010 },
+ { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 },
+ { NAU8825_REG_INTERRUPT_MASK, 0x0800 },
+ { NAU8825_REG_DACL_CTRL, 0x00cf },
+ { NAU8825_REG_DACR_CTRL, 0x02cf },
+ { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 },
+ { NAU8825_REG_FLL2_LOWER, 0x26e9 },
+ { NAU8825_REG_FLL2_UPPER, 0x0031 },
+ { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 },
+ { NAU8825_REG_ANALOG_ADC_2, 0x0220 },
+ { NAU8825_REG_MIC_BIAS, 0x0046 },
+};
+
+
static const unsigned short logtable[256] = {
0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
@@ -608,8 +628,13 @@ static void nau8825_xtalk_prepare(struct nau8825 *nau8825)
regmap_update_bits(nau8825->regmap,
NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0);
/* Power up left and right DAC */
- regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
- NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
}
static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
@@ -622,9 +647,14 @@ static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
/* Power down left and right DAC */
- regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
- NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
- NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+
/* Enable the TESTDAC and disable L/R HP impedance */
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP |
@@ -855,7 +885,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
case NAU8825_REG_MISC_CTRL:
- case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+ case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER:
case NAU8825_REG_BIAS_ADJ:
case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
@@ -881,6 +911,7 @@ static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_IMM_MODE_CTRL:
case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
case NAU8825_REG_MISC_CTRL:
+ case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER:
case NAU8825_REG_BIAS_ADJ:
case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
@@ -911,6 +942,32 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
}
}
+static int nau8825_fepga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA,
+ NAU8825_ACDC_CTRL_MASK,
+ NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN);
+ msleep(40);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_DISCHRG_EN, 0);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA,
+ NAU8825_ACDC_CTRL_MASK, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -970,10 +1027,25 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
/* Disables the TESTDAC to let DAC signal pass through. */
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_TESTDAC_EN, 0);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
break;
case SND_SOC_DAPM_POST_PMD:
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ else
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+
break;
default:
return -EINVAL;
@@ -1127,8 +1199,8 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC"),
SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
- SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
- NULL, 0),
+ SND_SOC_DAPM_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+ NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0,
nau8825_adc_event, SND_SOC_DAPM_POST_PMU |
@@ -1181,12 +1253,13 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("Output DACL", 7,
- NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event,
+ SND_SOC_NOPM, 0, 0, nau8825_output_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_S("Output DACR", 7,
- NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event,
+ SND_SOC_NOPM, 0, 0, nau8825_output_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
/* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0),
@@ -1678,6 +1751,10 @@ static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
{
struct regmap *regmap = nau8825->regmap;
+ /* Enable HSD function */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE);
+
/* Enable headset jack type detection complete interruption and
* jack ejection interruption.
*/
@@ -1929,6 +2006,9 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ /* Disable HSD function */
+ regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0);
+
/* Leaving HPOL/R grounded after jack insert by default. They will be
* ungrounded as part of the widget power up sequence at the beginning
* of playback to reduce pop.
@@ -2173,9 +2253,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
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,
- NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
/* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
* signal to avoid any glitches due to power up transients in both
* the analog and digital DAC circuit.
@@ -2307,9 +2388,12 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
/* 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_max << 16, fref * fll_param->ratio);
- fll_param->fll_int = (fvco >> 16) & 0x3FF;
- fll_param->fll_frac = fvco & 0xFFFF;
+ fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF;
+ if (fll_param->fll_frac_num == 16)
+ fll_param->fll_frac = fvco & 0xFFFF;
+ else
+ fll_param->fll_frac = fvco & 0xFFFFFF;
return 0;
}
@@ -2323,8 +2407,16 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
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 16/24 bit fractional input */
+ if (fll_param->fll_frac_num == 16)
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2,
+ fll_param->fll_frac);
+ else {
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER,
+ fll_param->fll_frac & 0xffff);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER,
+ (fll_param->fll_frac >> 16) & 0xff);
+ }
/* FLL 10-bit integer input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
@@ -2366,6 +2458,11 @@ static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int
struct nau8825_fll fll_param;
int ret, fs;
+ if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825)
+ fll_param.fll_frac_num = 16;
+ else
+ fll_param.fll_frac_num = 24;
+
fs = freq_out / 256;
ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
if (ret < 0) {
@@ -2897,8 +2994,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c)
ret);
return ret;
}
- if ((value & NAU8825_SOFTWARE_ID_MASK) !=
- NAU8825_SOFTWARE_ID_NAU8825) {
+ nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK;
+ switch (nau8825->sw_id) {
+ case NAU8825_SOFTWARE_ID_NAU8825:
+ break;
+ case NAU8825_SOFTWARE_ID_NAU8825C:
+ ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch,
+ ARRAY_SIZE(nau8825_regmap_patch));
+ if (ret) {
+ dev_err(dev, "Failed to register Rev C patch: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
dev_err(dev, "Not a NAU8825 chip\n");
return -ENODEV;
}
@@ -2944,7 +3052,7 @@ static struct i2c_driver nau8825_driver = {
.of_match_table = of_match_ptr(nau8825_of_ids),
.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
},
- .probe_new = nau8825_i2c_probe,
+ .probe = nau8825_i2c_probe,
.remove = nau8825_i2c_remove,
.id_table = nau8825_i2c_ids,
};