Unverified Commit a60a609b authored by David Lin's avatar David Lin Committed by Mark Brown
Browse files

ASoC: nau8540: Add self recovery to improve capture quility



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: default avatarDavid Lin <CTLIN0@nuvoton.com>
Link: https://lore.kernel.org/r/20231108061658.1265065-1-CTLIN0@nuvoton.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 45f2f28b
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -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
+15 −0
Original line number Diff line number Diff line
@@ -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)