[PATCH 0/5] ASoC: codecs: fix ES8326 performance and pop noise

Hi ,
5 patches here for the es8326 driver...
We get some issues regarding crosstalk, THD+N performance and pop noise from customer's project. Five patches are used to fix these issues. We did tests with the new driver. The test results form test department met our expectations.
Thanks, Michael.
Zhu Ning (5): ASoC: codecs: ES8326: improving crosstalk performance ASoC: codecs: ES8326: Improving the THD+N performance ASoC: codecs: ES8326: Adding new volume kcontrols ASoC: codecs: ES8326: Minimize the pop noise on headphone ASoC: codecs: ES8326: fix the capture noise issue
sound/soc/codecs/es8326.c | 216 +++++++++++++++++++++++++++++--------- sound/soc/codecs/es8326.h | 8 +- 2 files changed, 175 insertions(+), 49 deletions(-)

We change the crosstalk parameter in es8326_resume function to improve crosstalk performance. Adding crosstalk kcontrol to enhance the flexibility of crosstalk debugging in machine. Adding ES8326_DAC_CROSSTALK macro to declare the crosstalk register.
Signed-off-by: Zhu Ning zhuning0077@gmail.com --- sound/soc/codecs/es8326.c | 82 +++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/es8326.h | 1 + 2 files changed, 83 insertions(+)
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index fa890f6205e2..82d1c4f8324c 100755 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -45,6 +45,82 @@ struct es8326_priv { int jack_remove_retry; };
+static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h); + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h &= 0x20; + crosstalk_l &= 0xf0; + crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4; + ucontrol->value.integer.value[0] = crosstalk; + + return 0; +} + +static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + crosstalk = ucontrol->value.integer.value[0]; + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h = (crosstalk & 0x10) << 1; + crosstalk_l &= 0x0f; + crosstalk_l |= (crosstalk & 0x0f) << 4; + regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE, + 0x20, crosstalk_h); + regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l); + + return 0; +} + +static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h); + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h &= 0x10; + crosstalk_l &= 0x0f; + crosstalk = crosstalk_h | crosstalk_l; + ucontrol->value.integer.value[0] = crosstalk; + + return 0; +} + +static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int crosstalk_h, crosstalk_l; + unsigned int crosstalk; + + crosstalk = ucontrol->value.integer.value[0]; + regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l); + crosstalk_h = crosstalk & 0x10; + crosstalk_l &= 0xf0; + crosstalk_l |= crosstalk & 0x0f; + regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE, + 0x10, crosstalk_h); + regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l); + + return 0; +} + static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0); @@ -102,6 +178,10 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL, 0, 0x0f, 0, drc_target_tlv),
+ SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0, + es8326_crosstalk1_get, es8326_crosstalk1_set), + SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, + es8326_crosstalk2_get, es8326_crosstalk2_set), };
static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { @@ -844,6 +924,8 @@ static int es8326_resume(struct snd_soc_component *component) regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00); /* calibrate for B version */ es8326_calibrate(component); + regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa); + regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00); /* turn off headphone out */ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00); /* set ADC and DAC in low power mode */ diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 90a08351d6ac..dfef808673f4 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -72,6 +72,7 @@ #define ES8326_DAC_VOL 0x50 #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 +#define ES8326_DAC_CROSSTALK 0x55 #define ES8326_HPJACK_TIMER 0x56 #define ES8326_HPDET_TYPE 0x57 #define ES8326_INT_SOURCE 0x58

We update the values of some registers in the initialization sequence in es8326_resume function to improve THD+N performance. THD+N performance decreases if the output level on headphone is close to full scale. So we change the register setting in es8326_jack_detect_handler function to improve THD+N performance if headphone pulgged. Also, the register setting should be restored when the headset is unplugged
Signed-off-by: Zhu Ning zhuning0077@gmail.com --- sound/soc/codecs/es8326.c | 21 +++++++++++++-------- sound/soc/codecs/es8326.h | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 82d1c4f8324c..10157a4bd500 100755 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -752,6 +752,8 @@ static void es8326_jack_detect_handler(struct work_struct *work) es8326->hp = 0; } regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03); /* * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event */ @@ -777,6 +779,8 @@ static void es8326_jack_detect_handler(struct work_struct *work) regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); + regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x08); queue_delayed_work(system_wq, &es8326->jack_detect_work, msecs_to_jiffies(400)); es8326->hp = 1; @@ -846,14 +850,14 @@ static int es8326_calibrate(struct snd_soc_component *component) if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) { dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n"); regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0); - regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01); + regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x03); regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30); regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed); regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08); regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1); regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03); regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f); - regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x03); + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23); regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88); usleep_range(15000, 20000); regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); @@ -894,13 +898,13 @@ static int es8326_resume(struct snd_soc_component *component) /* reset internal clock state */ regmap_write(es8326->regmap, ES8326_RESET, 0x1f); regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); usleep_range(10000, 15000); regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xe9); - regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4b); + regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xcb); /* set headphone default type and detect pin */ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83); regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); - regmap_write(es8326->regmap, ES8326_HP_MISC, 0x30);
/* set internal oscillator as clock source of headpone cp */ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x89); @@ -908,14 +912,15 @@ static int es8326_resume(struct snd_soc_component *component) /* clock manager reset release */ regmap_write(es8326->regmap, ES8326_RESET, 0x17); /* set headphone detection as half scan mode */ - regmap_write(es8326->regmap, ES8326_HP_MISC, 0x30); + regmap_write(es8326->regmap, ES8326_HP_MISC, 0x3d); regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
/* enable headphone driver */ + regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4); regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7); usleep_range(2000, 5000); - regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xa3); - regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xb3); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33); regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); @@ -946,7 +951,7 @@ static int es8326_resume(struct snd_soc_component *component) (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
- regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL); diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index dfef808673f4..4234bbb900c4 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -101,7 +101,7 @@ #define ES8326_MUTE (3 << 0)
/* ES8326_CLK_CTL */ -#define ES8326_CLK_ON (0x7f << 0) +#define ES8326_CLK_ON (0x7e << 0) #define ES8326_CLK_OFF (0 << 0)
/* ES8326_CLK_INV */

ES8326 features a headphone volume control register and four DAC volume control registers. We add new volume Kcontrols for these registers to enhance the configurability of the volume settings, providing users with greater flexibility.
Signed-off-by: Zhu Ning zhuning0077@gmail.com --- sound/soc/codecs/es8326.c | 30 ++++++++++++++++++++++++++++++ sound/soc/codecs/es8326.h | 5 ++++- 2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 10157a4bd500..c7f89c990178 100755 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -151,12 +151,34 @@ static const char *const winsize[] = { static const char *const dacpol_txt[] = { "Normal", "R Invert", "L Invert", "L + R Invert" };
+static const char *const hpvol[] = { + "0 dB", + "-6 dB", + "-24 dB", + "+3.5 dB", + "-3.5 dB", + "-18 dB", +}; +static const unsigned int hp_vol_values[] = { 0, 1, 2, 4, 5, 6 }; + +static const char *const hp_spkvol_switch[] = { + "HPVOL: HPL+HPL, SPKVOL: HPL+HPL", + "HPVOL: HPL+HPR, SPKVOL: HPL+HPR", + "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR", + "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR", +}; + static const struct soc_enum dacpol = SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); static const struct soc_enum drc_winsize = SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize); +static const struct soc_enum hp_vol = + SOC_VALUE_ENUM_DOUBLE(ES8326_HP_VOL, 4, 0, 7, + ARRAY_SIZE(hpvol), hpvol, hp_vol_values); +static const struct soc_enum hpvol_spkvol_switch = + SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch);
static const struct snd_kcontrol_new es8326_snd_controls[] = { SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), @@ -182,6 +204,14 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { es8326_crosstalk1_get, es8326_crosstalk1_set), SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, es8326_crosstalk2_get, es8326_crosstalk2_set), + + SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv), + + SOC_ENUM("HP Volume", hp_vol), + SOC_ENUM("HPVol SPKVol switch", hpvol_spkvol_switch), };
static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 4234bbb900c4..ee12caef8105 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -69,7 +69,7 @@ #define ES8326_DAC_DSM 0x4D #define ES8326_DAC_RAMPRATE 0x4E #define ES8326_DAC_VPPSCALE 0x4F -#define ES8326_DAC_VOL 0x50 +#define ES8326_DACL_VOL 0x50 #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 #define ES8326_DAC_CROSSTALK 0x55 @@ -81,6 +81,9 @@ #define ES8326_SDINOUT23_IO 0x5B #define ES8326_JACK_PULSE 0x5C
+#define ES8326_DACR_VOL 0xF4 +#define ES8326_SPKL_VOL 0xF5 +#define ES8326_SPKR_VOL 0xF6 #define ES8326_HP_MISC 0xF7 #define ES8326_CTIA_OMTP_STA 0xF8 #define ES8326_PULLUP_CTL 0xF9

On Fri, Jan 19, 2024 at 07:28:56PM +0800, Zhu Ning wrote:
+static const char *const hpvol[] = {
- "0 dB",
- "-6 dB",
- "-24 dB",
- "+3.5 dB",
- "-3.5 dB",
- "-18 dB",
+}; +static const unsigned int hp_vol_values[] = { 0, 1, 2, 4, 5, 6 };
Volumes should be standard volume controls with TLV information rather than enumerations. I see that you can't just use a standard volume control here since there's an invalid value that needs to be skipped over, and it doesn't help that there's no ordering to the values either. I think the best thing would probably be to open code a custom volume control - this feels weird enough that it's probably not worrying about trying for reuse. Just have put and get functions that map 0..6 into the register values for the volumes in ascending order, then you can have a control that looks normal to userspace.

Hi Zhu,
kernel test robot noticed the following build errors:
[auto build test ERROR on broonie-sound/for-next] [also build test ERROR on tiwai-sound/for-next tiwai-sound/for-linus linus/master next-20240119] [cannot apply to v6.7] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Zhu-Ning/ASoC-codecs-ES8326-i... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20240119112858.2982-4-zhuning0077%40gmail.com patch subject: [PATCH 3/5] ASoC: codecs: ES8326: Adding new volume kcontrols config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20240120/202401201033.SJS8Mtlr-lkp@i...) compiler: m68k-linux-gcc (GCC) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240120/202401201033.SJS8Mtlr-lkp@i...)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202401201033.SJS8Mtlr-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from sound/soc/codecs/es8326.c:16:
sound/soc/codecs/es8326.c:184:47: error: 'ES8326_DAC_VOL' undeclared here (not in a function); did you mean 'ES8326_DACR_VOL'?
184 | SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), | ^~~~~~~~~~~~~~ include/sound/soc.h:34:17: note: in definition of macro 'SOC_DOUBLE_VALUE' 34 | {.reg = xreg, .rreg = xreg, .shift = shift_left, \ | ^~~~ include/sound/soc.h:80:26: note: in expansion of macro 'SOC_SINGLE_VALUE' 80 | .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } | ^~~~~~~~~~~~~~~~ sound/soc/codecs/es8326.c:184:9: note: in expansion of macro 'SOC_SINGLE_TLV' 184 | SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), | ^~~~~~~~~~~~~~
vim +184 sound/soc/codecs/es8326.c
b7cd5a85ede3b0 Zhu Ning 2024-01-19 170 5c439937775d77 Zhu Ning 2022-08-16 171 static const struct soc_enum dacpol = 5c439937775d77 Zhu Ning 2022-08-16 172 SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); 5c439937775d77 Zhu Ning 2022-08-16 173 static const struct soc_enum alc_winsize = 5c439937775d77 Zhu Ning 2022-08-16 174 SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); 5c439937775d77 Zhu Ning 2022-08-16 175 static const struct soc_enum drc_winsize = 5c439937775d77 Zhu Ning 2022-08-16 176 SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize); b7cd5a85ede3b0 Zhu Ning 2024-01-19 177 static const struct soc_enum hp_vol = b7cd5a85ede3b0 Zhu Ning 2024-01-19 178 SOC_VALUE_ENUM_DOUBLE(ES8326_HP_VOL, 4, 0, 7, b7cd5a85ede3b0 Zhu Ning 2024-01-19 179 ARRAY_SIZE(hpvol), hpvol, hp_vol_values); b7cd5a85ede3b0 Zhu Ning 2024-01-19 180 static const struct soc_enum hpvol_spkvol_switch = b7cd5a85ede3b0 Zhu Ning 2024-01-19 181 SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch); 5c439937775d77 Zhu Ning 2022-08-16 182 5c439937775d77 Zhu Ning 2022-08-16 183 static const struct snd_kcontrol_new es8326_snd_controls[] = { 5c439937775d77 Zhu Ning 2022-08-16 @184 SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), 5c439937775d77 Zhu Ning 2022-08-16 185 SOC_ENUM("Playback Polarity", dacpol), 5c439937775d77 Zhu Ning 2022-08-16 186 SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate), 5c439937775d77 Zhu Ning 2022-08-16 187 SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv), 5c439937775d77 Zhu Ning 2022-08-16 188 SOC_ENUM("DRC Winsize", drc_winsize), 5c439937775d77 Zhu Ning 2022-08-16 189 SOC_SINGLE_TLV("DRC Target Level", ES8326_DRC_WINSIZE, 0, 0x0f, 0, drc_target_tlv), 5c439937775d77 Zhu Ning 2022-08-16 190 5c439937775d77 Zhu Ning 2022-08-16 191 SOC_DOUBLE_R_TLV("ADC Capture Volume", ES8326_ADC1_VOL, ES8326_ADC2_VOL, 0, 0xff, 0, 5c439937775d77 Zhu Ning 2022-08-16 192 adc_vol_tlv), 5c439937775d77 Zhu Ning 2022-08-16 193 SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv), 5c439937775d77 Zhu Ning 2022-08-16 194 SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv), 5c439937775d77 Zhu Ning 2022-08-16 195 SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate), 5c439937775d77 Zhu Ning 2022-08-16 196 SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0), 5c439937775d77 Zhu Ning 2022-08-16 197 SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL, 5c439937775d77 Zhu Ning 2022-08-16 198 0, 4, 0, drc_recovery_tlv), 5c439937775d77 Zhu Ning 2022-08-16 199 SOC_ENUM("ALC Capture Winsize", alc_winsize), 5c439937775d77 Zhu Ning 2022-08-16 200 SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL, 5c439937775d77 Zhu Ning 2022-08-16 201 0, 0x0f, 0, drc_target_tlv), 5c439937775d77 Zhu Ning 2022-08-16 202 e21a9e2701cda5 Zhu Ning 2024-01-19 203 SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0, e21a9e2701cda5 Zhu Ning 2024-01-19 204 es8326_crosstalk1_get, es8326_crosstalk1_set), e21a9e2701cda5 Zhu Ning 2024-01-19 205 SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, e21a9e2701cda5 Zhu Ning 2024-01-19 206 es8326_crosstalk2_get, es8326_crosstalk2_set), b7cd5a85ede3b0 Zhu Ning 2024-01-19 207 b7cd5a85ede3b0 Zhu Ning 2024-01-19 208 SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), b7cd5a85ede3b0 Zhu Ning 2024-01-19 209 SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv), b7cd5a85ede3b0 Zhu Ning 2024-01-19 210 SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv), b7cd5a85ede3b0 Zhu Ning 2024-01-19 211 SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv), b7cd5a85ede3b0 Zhu Ning 2024-01-19 212 b7cd5a85ede3b0 Zhu Ning 2024-01-19 213 SOC_ENUM("HP Volume", hp_vol), b7cd5a85ede3b0 Zhu Ning 2024-01-19 214 SOC_ENUM("HPVol SPKVol switch", hpvol_spkvol_switch), 5c439937775d77 Zhu Ning 2022-08-16 215 }; 5c439937775d77 Zhu Ning 2022-08-16 216

We modify the register settings to minimize headphone pop noise during ES8326 power-up and music start/stop.
Signed-off-by: Zhu Ning zhuning0077@gmail.com --- sound/soc/codecs/es8326.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-)
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index c7f89c990178..fd3e73c4b7e9 100755 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -553,7 +553,8 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, ES8326_MUTE); - regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xf0); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, + 0x30, 0x00); } else { if (!es8326->calibrated) { regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL); @@ -566,8 +567,13 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); es8326->calibrated = true; } + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01); + usleep_range(1000, 5000); + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + usleep_range(1000, 5000); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30); regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); - regmap_write(es8326->regmap, ES8326_HP_VOL, 0x91); regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, ~(ES8326_MUTE)); @@ -587,23 +593,20 @@ static int es8326_set_bias_level(struct snd_soc_component *codec, if (ret) return ret;
- regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02); + usleep_range(5000, 10000); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40); regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20); - - regmap_update_bits(es8326->regmap, ES8326_RESET, - ES8326_CSM_ON, ES8326_CSM_ON); + regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00); regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT); break; @@ -807,6 +810,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) * Don't report jack status. */ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f); @@ -850,13 +854,10 @@ static void es8326_jack_detect_handler(struct work_struct *work) static irqreturn_t es8326_irq(int irq, void *dev_id) { struct es8326_priv *es8326 = dev_id; - struct snd_soc_component *comp = es8326->component;
if (!es8326->jack) goto out;
- es8326_enable_micbias(comp); - if (es8326->jack->status & SND_JACK_HEADSET) queue_delayed_work(system_wq, &es8326->jack_detect_work, msecs_to_jiffies(10)); @@ -973,6 +974,14 @@ static int es8326_resume(struct snd_soc_component *component) regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08); regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15);
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | + ((es8326->version == ES8326_VERSION_B) ? + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); + usleep_range(5000, 10000); + es8326_enable_micbias(es8326->component); + usleep_range(50000, 70000); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); regmap_write(es8326->regmap, ES8326_INT_SOURCE, (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); regmap_write(es8326->regmap, ES8326_INTOUT_IO, @@ -989,11 +998,6 @@ static int es8326_resume(struct snd_soc_component *component) regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, ES8326_MUTE);
- regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | - ((es8326->version == ES8326_VERSION_B) ? - (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : - (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); - regmap_write(es8326->regmap, ES8326_HP_VOL, 0x11);
es8326->jack_remove_retry = 0; es8326->hp = 0;

We get a noise issue during the startup of recording. We update the register setting and dapm widgets to fix this issue. we change callback type of es8326_mute function to mute_stream. ES8326_ADC_MUTE is moved to es8326_mute function so it can be turned on at last and turned off at first.
Signed-off-by: Zhu Ning zhuning0077@gmail.com --- sound/soc/codecs/es8326.c | 63 ++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 31 deletions(-)
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index fd3e73c4b7e9..4026d939e051 100755 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -227,12 +227,6 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0),
- /* ADC Digital Mute */ - SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0), - SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0), - SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0), - SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0), - /* Analog Power Supply*/ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1), SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1), @@ -252,15 +246,10 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { };
static const struct snd_soc_dapm_route es8326_dapm_routes[] = { - {"ADC L1", NULL, "MIC1"}, - {"ADC R1", NULL, "MIC2"}, - {"ADC L2", NULL, "MIC3"}, - {"ADC R2", NULL, "MIC4"}, - - {"ADC L", NULL, "ADC L1"}, - {"ADC R", NULL, "ADC R1"}, - {"ADC L", NULL, "ADC L2"}, - {"ADC R", NULL, "ADC R2"}, + {"ADC L", NULL, "MIC1"}, + {"ADC R", NULL, "MIC2"}, + {"ADC L", NULL, "MIC3"}, + {"ADC R", NULL, "MIC4"},
{"I2S OUT", NULL, "ADC L"}, {"I2S OUT", NULL, "ADC R"}, @@ -550,11 +539,16 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) unsigned int offset_l, offset_r;
if (mute) { - regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); - regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, - ES8326_MUTE_MASK, ES8326_MUTE); - regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, - 0x30, 0x00); + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ES8326_MUTE); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, + 0x30, 0x00); + } else { + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x0F); + } } else { if (!es8326->calibrated) { regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL); @@ -567,16 +561,22 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); es8326->calibrated = true; } - regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01); - usleep_range(1000, 5000); - regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); - usleep_range(1000, 5000); - regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20); - regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30); - regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); - regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); - regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, - ES8326_MUTE_MASK, ~(ES8326_MUTE)); + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01); + usleep_range(1000, 5000); + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + usleep_range(1000, 5000); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ~(ES8326_MUTE)); + } else { + msleep(300); + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x00); + } } return 0; } @@ -626,7 +626,7 @@ static const struct snd_soc_dai_ops es8326_ops = { .set_fmt = es8326_set_dai_fmt, .set_sysclk = es8326_set_dai_sysclk, .mute_stream = es8326_mute, - .no_capture_mute = 1, + .no_capture_mute = 0, };
static struct snd_soc_dai_driver es8326_dai = { @@ -998,6 +998,7 @@ static int es8326_resume(struct snd_soc_component *component) regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, ES8326_MUTE);
+ regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f);
es8326->jack_remove_retry = 0; es8326->hp = 0;
participants (3)
-
kernel test robot
-
Mark Brown
-
Zhu Ning