ASoC: nau8540: Add self recovery to improve capture quility
authorDavid Lin <CTLIN0@nuvoton.com>
Wed, 8 Nov 2023 06:16:59 +0000 (14:16 +0800)
committerMark Brown <broonie@kernel.org>
Wed, 8 Nov 2023 13:21:05 +0000 (13:21 +0000)
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@nuvoton.com>
Link: https://lore.kernel.org/r/20231108061658.1265065-1-CTLIN0@nuvoton.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/nau8540.c
sound/soc/codecs/nau8540.h

index 5cf28d034f094ceb2a739286f6b1004c41252036..f66417a0f29f636ef8b6776697c081a808a9f08a 100644 (file)
@@ -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
index 305ea9207cf0b40e4a5c9b66504f03192c891814..2ce6063d462b916343fae8f0fcac3ccbf018b83d 100644 (file)
@@ -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) */
 #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
 #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)