Reading the peak data to detect abnormal data in the ADC channel. If abnormal data occurs, the driver takes recovery actions to refresh the ADC channel. Signed-off-by: David Lin <CTLIN0@xxxxxxxxxxx> --- sound/soc/codecs/nau8540.c | 49 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/nau8540.h | 15 ++++++++++++ 2 files changed, 64 insertions(+) diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 5cf28d034f09..f66417a0f29f 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -530,12 +530,61 @@ static int nau8540_set_tdm_slot(struct snd_soc_dai *dai, return 0; } +static int nau8540_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = nau8540->regmap; + unsigned int val; + int ret = 0; + + /* Reading the peak data to detect abnormal data in the ADC channel. + * If abnormal data happens, the driver takes recovery actions to + * refresh the ADC channel. + */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL, + NAU8540_CLK_AGC_EN, NAU8540_CLK_AGC_EN); + regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3, + NAU8540_ALC_CH_ALL_EN, NAU8540_ALC_CH_ALL_EN); + + regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val); + dev_dbg(nau8540->dev, "1.ADC CH1 peak data %x", val); + if (!val) { + regmap_update_bits(regmap, NAU8540_REG_MUTE, + NAU8540_PGA_CH_ALL_MUTE, NAU8540_PGA_CH_ALL_MUTE); + regmap_update_bits(regmap, NAU8540_REG_MUTE, + NAU8540_PGA_CH_ALL_MUTE, 0); + regmap_write(regmap, NAU8540_REG_RST, 0x1); + regmap_write(regmap, NAU8540_REG_RST, 0); + regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val); + dev_dbg(nau8540->dev, "2.ADC CH1 peak data %x", val); + if (!val) { + dev_err(nau8540->dev, "Channel recovery failed!!"); + ret = -EIO; + } + } + regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL, + NAU8540_CLK_AGC_EN, 0); + regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3, + NAU8540_ALC_CH_ALL_EN, 0); + break; + + default: + break; + } + + return ret; +} static const struct snd_soc_dai_ops nau8540_dai_ops = { .startup = nau8540_dai_startup, .hw_params = nau8540_hw_params, .set_fmt = nau8540_set_fmt, .set_tdm_slot = nau8540_set_tdm_slot, + .trigger = nau8540_dai_trigger, }; #define NAU8540_RATES SNDRV_PCM_RATE_8000_48000 diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h index 305ea9207cf0..2ce6063d462b 100644 --- a/sound/soc/codecs/nau8540.h +++ b/sound/soc/codecs/nau8540.h @@ -85,6 +85,7 @@ /* CLOCK_CTRL (0x02) */ #define NAU8540_CLK_ADC_EN (0x1 << 15) +#define NAU8540_CLK_AGC_EN (0x1 << 3) #define NAU8540_CLK_I2S_EN (0x1 << 1) /* CLOCK_SRC (0x03) */ @@ -168,6 +169,13 @@ #define NAU8540_TDM_OFFSET_EN (0x1 << 14) #define NAU8540_TDM_TX_MASK 0xf +/* ALC_CONTROL_3 (0x22) */ +#define NAU8540_ALC_CH1_EN (0x1 << 12) +#define NAU8540_ALC_CH2_EN (0x1 << 13) +#define NAU8540_ALC_CH3_EN (0x1 << 14) +#define NAU8540_ALC_CH4_EN (0x1 << 15) +#define NAU8540_ALC_CH_ALL_EN (0xf << 12) + /* ADC_SAMPLE_RATE (0x3A) */ #define NAU8540_CH_SYNC (0x1 << 14) #define NAU8540_ADC_OSR_MASK 0x3 @@ -181,6 +189,13 @@ #define NAU8540_VMID_SEL_SFT 4 #define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT) +/* MUTE (0x61) */ +#define NAU8540_PGA_CH1_MUTE 0x1 +#define NAU8540_PGA_CH2_MUTE 0x2 +#define NAU8540_PGA_CH3_MUTE 0x4 +#define NAU8540_PGA_CH4_MUTE 0x8 +#define NAU8540_PGA_CH_ALL_MUTE 0xf + /* MIC_BIAS (0x67) */ #define NAU8540_PU_PRE (0x1 << 8) -- 2.25.1