Commit b50455fa authored by John Hsu's avatar John Hsu Committed by Mark Brown

ASoC: nau8825: cross talk suppression measurement function

The cross talk measurement function can reduce cross talk across the JKTIP
HPL) and JKR1(HPR) outputs which measures the cross talk signal level to
determine what is the cross talk reduction gain. This system works by
sending a 23Hz -24dBV sine wave into the headset output DAC and through
the PGA. The output of the PGA is then connected to an internal current
sense which measures the attenuated 23Hz signal and passing the output to
an ADC which converts the measurement to a binary code. With two separated
measurement, one for JKR1(HPR) and the other JKTIP(HPL), measurement data
can be separated read in IMM_RMS_L for HSR and HSL after each measurement.

Thus, the measurement function has four states to complete whole sequence.
(1)Prepare state : Prepare the resource for detection and transfer to HPR
IMM stat to make JKR1(HPR) impedance measure.
(2)HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer
to HPL IMM state to make JKTIP(HPL) impedance measure.
(3)HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and
transfer to IMM state to determine suppression sidetone gain.
(4)IMM state : Computes cross talk suppression sidetone gain with orignal
and cross talk signal level. Apply this gain and then restore codec con-
figuration. Then transfer to Done state for ending.

In order to get the cross talk suppression sidetone gain, we need the
function to compute log10 value and the result is round off to 3 decimal.
This function takes reference to dvb-math. The source code locates as the
following. "Linux/drivers/media/dvb-core/dvb_math.c"
Then, the orignal and cross talk signal vlues need to be characterized.
The sidetone value can be converted to decibel with the equation below.
sidetone = 20 * log (original signal level / crosstalk signal level)

Besides, the state machine for cross talk process needs interruptions to
trigger worked. We have the RMS intrruption enabled with the internal VCO
clock when headset connected. In the interrupt handler, the driver will
judge the headset is high impedance or not. If yes, the cross talk supp-
ression shouldn't apply and do nothing but relieve the protection raised
before. Otherwise, apply the cross talk suppression in the headset and
start the process.

Because the process spends a lot of time, there is an resource race issue
easily between the application and interruption. They will control codec
power and clock concurrently. In one situaiton, the jack is inserted when
playback, and then the application changes to headset device. The applica-
tion prepares the playback and interrupt handler raises work for cross
talk process together. For this case, the solution is that driver delays
soc jack report until cross talk process completes. The mechanism can
avoid application to do playback preparation before cross talk detection
is still working.
In another situaiton, the system suspends when playback. After resume, the
system restarts playback, and meanwhile jack detection restarts. The play-
back preparation and cross talk process triggered by interruptions happens
concurrently. For the case, the driver provides the semaphone to syn-
chronize the playback and interrupt handler. In order to avoid the play-
back interfered by cross talk process, the driver make the playback prepa-
ration halted until cross talk process finish. After codec resume, the
driver finds the codec dai is active, and then the driver raises the pro-
tection for cross talk function to avoid the playback recovers before
cross talk process finish.

The driver also provides cancel method to forcely cancel the cross talk
task and restores the configuration to original status. Before the codec
remove, ejection, or suspend, the driver is obliged to cancel the cross
talk detection process. It can reduce the risk of failure when quickly and
continually doing jack insertion and ejection.
Signed-off-by: default avatarJohn Hsu <KCHSU0@nuvoton.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 0cbeccdf
This diff is collapsed.
...@@ -101,8 +101,13 @@ ...@@ -101,8 +101,13 @@
#define NAU8825_ENABLE_DACR_SFT 10 #define NAU8825_ENABLE_DACR_SFT 10
#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT) #define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
#define NAU8825_ENABLE_DACL_SFT 9 #define NAU8825_ENABLE_DACL_SFT 9
#define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT)
#define NAU8825_ENABLE_ADC_SFT 8 #define NAU8825_ENABLE_ADC_SFT 8
#define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT) #define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT)
#define NAU8825_ENABLE_ADC_CLK_SFT 7
#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT)
#define NAU8825_ENABLE_DAC_CLK_SFT 6
#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT)
#define NAU8825_ENABLE_SAR_SFT 1 #define NAU8825_ENABLE_SAR_SFT 1
/* CLK_DIVIDER (0x3) */ /* CLK_DIVIDER (0x3) */
...@@ -158,6 +163,7 @@ ...@@ -158,6 +163,7 @@
/* INTERRUPT_MASK (0xf) */ /* INTERRUPT_MASK (0xf) */
#define NAU8825_IRQ_OUTPUT_EN (1 << 11) #define NAU8825_IRQ_OUTPUT_EN (1 << 11)
#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) #define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
#define NAU8825_IRQ_RMS_EN (1 << 8)
#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7) #define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5) #define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
#define NAU8825_IRQ_EJECT_EN (1 << 2) #define NAU8825_IRQ_EJECT_EN (1 << 2)
...@@ -232,10 +238,13 @@ ...@@ -232,10 +238,13 @@
/* I2S_PCM_CTRL2 (0x1d) */ /* I2S_PCM_CTRL2 (0x1d) */
#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ #define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
#define NAU8825_I2S_DRV_SFT 12
#define NAU8825_I2S_DRV_MASK (0x3 << NAU8825_I2S_DRV_SFT)
#define NAU8825_I2S_MS_SFT 3 #define NAU8825_I2S_MS_SFT 3
#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) #define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) #define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) #define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_BLK_DIV_MASK 0x7
/* BIQ_CTRL (0x20) */ /* BIQ_CTRL (0x20) */
#define NAU8825_BIQ_WRT_SFT 4 #define NAU8825_BIQ_WRT_SFT 4
...@@ -262,28 +271,72 @@ ...@@ -262,28 +271,72 @@
#define NAU8825_DAC_OVERSAMPLE_128 2 #define NAU8825_DAC_OVERSAMPLE_128 2
#define NAU8825_DAC_OVERSAMPLE_32 4 #define NAU8825_DAC_OVERSAMPLE_32 4
/* ADC_DGAIN_CTRL (0x30) */
#define NAU8825_ADC_DIG_VOL_MASK 0xff
/* MUTE_CTRL (0x31) */ /* MUTE_CTRL (0x31) */
#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9) #define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
#define NAU8825_DAC_SOFT_MUTE (1 << 9) #define NAU8825_DAC_SOFT_MUTE (1 << 9)
/* HSVOL_CTRL (0x32) */ /* HSVOL_CTRL (0x32) */
#define NAU8825_HP_MUTE (1 << 15) #define NAU8825_HP_MUTE (1 << 15)
#define NAU8825_HP_MUTE_AUTO (1 << 14)
#define NAU8825_HPL_MUTE (1 << 13)
#define NAU8825_HPR_MUTE (1 << 12)
#define NAU8825_HPL_VOL_SFT 6
#define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT)
#define NAU8825_HPR_VOL_SFT 0
#define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT)
#define NAU8825_HP_VOL_MIN 0x36
/* DACL_CTRL (0x33) */ /* DACL_CTRL (0x33) */
#define NAU8825_DACL_CH_SEL_SFT 9 #define NAU8825_DACL_CH_SEL_SFT 9
#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT) #define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT)
#define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT) #define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT)
#define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT) #define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT)
#define NAU8825_DACL_CH_VOL_MASK 0xff
/* DACR_CTRL (0x34) */ /* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9 #define NAU8825_DACR_CH_SEL_SFT 9
#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT) #define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT)
#define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT) #define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT)
#define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT) #define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT)
#define NAU8825_DACR_CH_VOL_MASK 0xff
/* IMM_MODE_CTRL (0x4C) */
#define NAU8825_IMM_THD_SFT 8
#define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT)
#define NAU8825_IMM_GEN_VOL_SFT 6
#define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT)
#define NAU8825_IMM_CYC_SFT 4
#define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT)
#define NAU8825_IMM_EN (1 << 3)
#define NAU8825_IMM_DAC_SRC_MASK 0x7
#define NAU8825_IMM_DAC_SRC_BIQ 0x0
#define NAU8825_IMM_DAC_SRC_DRC 0x1
#define NAU8825_IMM_DAC_SRC_MIX 0x2
#define NAU8825_IMM_DAC_SRC_SIN 0x3
/* CLASSG_CTRL (0x50) */ /* CLASSG_CTRL (0x50) */
#define NAU8825_CLASSG_TIMER_SFT 8 #define NAU8825_CLASSG_TIMER_SFT 8
#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT) #define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_LDAC_EN (0x1 << 2)
#define NAU8825_CLASSG_RDAC_EN (0x1 << 1)
#define NAU8825_CLASSG_EN (1 << 0) #define NAU8825_CLASSG_EN (1 << 0)
/* I2C_DEVICE_ID (0x58) */ /* I2C_DEVICE_ID (0x58) */
...@@ -292,7 +345,12 @@ ...@@ -292,7 +345,12 @@
#define NAU8825_SOFTWARE_ID_NAU8825 0x0 #define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */ /* BIAS_ADJ (0x66) */
#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8) #define NAU8825_BIAS_HPR_IMP (1 << 15)
#define NAU8825_BIAS_HPL_IMP (1 << 14)
#define NAU8825_BIAS_TESTDAC_SFT 8
#define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_VMID (1 << 6) #define NAU8825_BIAS_VMID (1 << 6)
#define NAU8825_BIAS_VMID_SEL_SFT 4 #define NAU8825_BIAS_VMID_SEL_SFT 4
#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) #define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
...@@ -311,6 +369,11 @@ ...@@ -311,6 +369,11 @@
#define NAU8825_POWERUP_ADCL (1 << 6) #define NAU8825_POWERUP_ADCL (1 << 6)
/* RDAC (0x73) */ /* RDAC (0x73) */
#define NAU8825_RDAC_FS_BCLK_ENB (1 << 15)
#define NAU8825_RDAC_EN_SFT 12
#define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT)
#define NAU8825_RDAC_CLK_EN_SFT 8
#define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT)
#define NAU8825_RDAC_CLK_DELAY_SFT 4 #define NAU8825_RDAC_CLK_DELAY_SFT 4
#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT) #define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
#define NAU8825_RDAC_VREF_SFT 2 #define NAU8825_RDAC_VREF_SFT 2
...@@ -355,12 +418,23 @@ enum { ...@@ -355,12 +418,23 @@ enum {
NAU8825_CLK_FLL_FS, NAU8825_CLK_FLL_FS,
}; };
/* Cross talk detection state */
enum {
NAU8825_XTALK_PREPARE = 0,
NAU8825_XTALK_HPR_R2L,
NAU8825_XTALK_HPL_R2L,
NAU8825_XTALK_IMM,
NAU8825_XTALK_DONE,
};
struct nau8825 { struct nau8825 {
struct device *dev; struct device *dev;
struct regmap *regmap; struct regmap *regmap;
struct snd_soc_dapm_context *dapm; struct snd_soc_dapm_context *dapm;
struct snd_soc_jack *jack; struct snd_soc_jack *jack;
struct clk *mclk; struct clk *mclk;
struct work_struct xtalk_work;
struct semaphore xtalk_sem;
int irq; int irq;
int mclk_freq; /* 0 - mclk is disabled */ int mclk_freq; /* 0 - mclk is disabled */
int button_pressed; int button_pressed;
...@@ -379,6 +453,12 @@ struct nau8825 { ...@@ -379,6 +453,12 @@ struct nau8825 {
int key_debounce; int key_debounce;
int jack_insert_debounce; int jack_insert_debounce;
int jack_eject_debounce; int jack_eject_debounce;
int high_imped;
int xtalk_state;
int xtalk_event;
int xtalk_event_mask;
bool xtalk_protect;
int imp_rms[NAU8825_XTALK_IMM];
}; };
int nau8825_enable_jack_detect(struct snd_soc_codec *codec, int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment