ASoC: nau8825: improve crosstalk measurement protection
authorJohn Hsu <KCHSU0@nuvoton.com>
Fri, 1 Dec 2017 02:01:37 +0000 (10:01 +0800)
committerMark Brown <broonie@kernel.org>
Mon, 4 Dec 2017 18:14:49 +0000 (18:14 +0000)
The sequence of crosstalk measurement is not robust. The driver
add more protections to make it stronger. The improvements as follows:
(1)Give backup table the default value. The values are the same
with the default of hardware registers. The impact will decline
once restoring the backup table in the wrong way.
(2)Add xtalk_baktab_initialized flag. The flag can keep the
initiation status of backup table. It helps to backup and restore
the backup-table correctly.
(3)Add cancel parameter in the restore function. The volume ramping
should do in crosstalk DONE state only. Otherwise, the delay action
is not allowed because the restore may happen during the jack
eject interruption.
(4)Add xtalk_protect condition check before scheduling work in ISR.
It makes sure the semaphore hold during the crosstalk measurement.
The sequence is still under protection from playback interference.

Signed-off-by: John Hsu <KCHSU0@nuvoton.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/nau8825.c
sound/soc/codecs/nau8825.h

index 5778ead..2aea642 100644 (file)
@@ -194,10 +194,10 @@ static const struct reg_default nau8825_reg_defaults[] = {
 
 /* register backup table when cross talk detection */
 static struct reg_default nau8825_xtalk_baktab[] = {
-       { NAU8825_REG_ADC_DGAIN_CTRL, 0 },
+       { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
        { NAU8825_REG_HSVOL_CTRL, 0 },
-       { NAU8825_REG_DACL_CTRL, 0 },
-       { NAU8825_REG_DACR_CTRL, 0 },
+       { NAU8825_REG_DACL_CTRL, 0x00cf },
+       { NAU8825_REG_DACR_CTRL, 0x02cf },
 };
 
 static const unsigned short logtable[256] = {
@@ -455,22 +455,32 @@ static void nau8825_xtalk_backup(struct nau8825 *nau8825)
 {
        int i;
 
+       if (nau8825->xtalk_baktab_initialized)
+               return;
+
        /* Backup some register values to backup table */
        for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++)
                regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
                                &nau8825_xtalk_baktab[i].def);
+
+       nau8825->xtalk_baktab_initialized = true;
 }
 
-static void nau8825_xtalk_restore(struct nau8825 *nau8825)
+static void nau8825_xtalk_restore(struct nau8825 *nau8825, bool cause_cancel)
 {
        int i, volume;
 
+       if (!nau8825->xtalk_baktab_initialized)
+               return;
+
        /* Restore register values from backup table; When the driver restores
-        * the headphone volumem, it needs recover to original level gradually
-        * with 3dB per step for less pop noise.
+        * the headphone volume in XTALK_DONE state, it needs recover to
+        * original level gradually with 3dB per step for less pop noise.
+        * Otherwise, the restore should do ASAP.
         */
        for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) {
-               if (nau8825_xtalk_baktab[i].reg == NAU8825_REG_HSVOL_CTRL) {
+               if (!cause_cancel && nau8825_xtalk_baktab[i].reg ==
+                       NAU8825_REG_HSVOL_CTRL) {
                        /* Ramping up the volume change to reduce pop noise */
                        volume = nau8825_xtalk_baktab[i].def &
                                NAU8825_HPR_VOL_MASK;
@@ -480,6 +490,8 @@ static void nau8825_xtalk_restore(struct nau8825 *nau8825)
                regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
                                nau8825_xtalk_baktab[i].def);
        }
+
+       nau8825->xtalk_baktab_initialized = false;
 }
 
 static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825)
@@ -645,7 +657,7 @@ static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825)
                NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0);
 }
 
-static void nau8825_xtalk_clean(struct nau8825 *nau8825)
+static void nau8825_xtalk_clean(struct nau8825 *nau8825, bool cause_cancel)
 {
        /* Enable internal VCO needed for interruptions */
        nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
@@ -661,7 +673,7 @@ static void nau8825_xtalk_clean(struct nau8825 *nau8825)
                NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK |
                NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE);
        /* Restore value of specific register for cross talk */
-       nau8825_xtalk_restore(nau8825);
+       nau8825_xtalk_restore(nau8825, cause_cancel);
 }
 
 static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol)
@@ -780,7 +792,7 @@ static void nau8825_xtalk_measure(struct nau8825 *nau8825)
                dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone);
                regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL,
                                        (sidetone << 8) | sidetone);
-               nau8825_xtalk_clean(nau8825);
+               nau8825_xtalk_clean(nau8825, false);
                nau8825->xtalk_state = NAU8825_XTALK_DONE;
                break;
        default:
@@ -823,7 +835,7 @@ static void nau8825_xtalk_cancel(struct nau8825 *nau8825)
        if (nau8825->xtalk_enable && nau8825->xtalk_state !=
                NAU8825_XTALK_DONE) {
                cancel_work_sync(&nau8825->xtalk_work);
-               nau8825_xtalk_clean(nau8825);
+               nau8825_xtalk_clean(nau8825, true);
        }
        /* Reset parameters for cross talk suppression function */
        nau8825_sema_reset(nau8825);
@@ -1713,8 +1725,11 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
                                                nau8825->xtalk_protect = false;
                                }
                                /* Startup cross talk detection process */
-                               nau8825->xtalk_state = NAU8825_XTALK_PREPARE;
-                               schedule_work(&nau8825->xtalk_work);
+                               if (nau8825->xtalk_protect) {
+                                       nau8825->xtalk_state =
+                                               NAU8825_XTALK_PREPARE;
+                                       schedule_work(&nau8825->xtalk_work);
+                               }
                        } else {
                                /* The cross talk suppression shouldn't apply
                                 * in the headset with high impedance. Thus,
@@ -1741,7 +1756,8 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
                        nau8825->xtalk_event_mask = event_mask;
                }
        } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) {
-               if (nau8825->xtalk_enable)
+               /* crosstalk detection enable and process on going */
+               if (nau8825->xtalk_enable && nau8825->xtalk_protect)
                        schedule_work(&nau8825->xtalk_work);
                clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ;
        } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) ==
@@ -2578,6 +2594,7 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
         */
        nau8825->xtalk_state = NAU8825_XTALK_DONE;
        nau8825->xtalk_protect = false;
+       nau8825->xtalk_baktab_initialized = false;
        sema_init(&nau8825->xtalk_sem, 1);
        INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work);
 
index 199d6ea..f7e7321 100644 (file)
@@ -477,6 +477,7 @@ struct nau8825 {
        bool xtalk_protect;
        int imp_rms[NAU8825_XTALK_IMM];
        int xtalk_enable;
+       bool xtalk_baktab_initialized; /* True if initialized. */
 };
 
 int nau8825_enable_jack_detect(struct snd_soc_codec *codec,