[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251106063459.115006-1-wangdich9700@163.com>
Date: Thu, 6 Nov 2025 14:34:59 +0800
From: wangdich9700@....com
To: lgirdwood@...il.com,
broonie@...nel.org,
perex@...ex.cz,
tiwai@...e.com,
cezary.rojewski@...el.com
Cc: linux-kernel@...r.kernel.org,
linux-sound@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org,
wangdicheng <wangdich9700@....com>,
wangdicheng <wangdicheng@...inos.cn>
Subject: [PATCH] [PATCH v3] ALSA: hda/conexant: Fix pop noise on CX11880/SN6140 codecs
From: wangdicheng <wangdich9700@....com>
Pop noise mitigation: When headphones are unplugged during playback, mute
speaker DAC (0x17) immediately and restore after 20ms delay to avoid
audible popping. This fix is specifically for CX11880 (0x14f11f86) and
SN6140 (0x14f11f87) codecs based on testing verification.
Signed-off-by: wangdicheng <wangdicheng@...inos.cn>
---
V2 -> V3:
- Fixed container_of usage by storing codec pointer in spec structure
- Added cancellation of delayed work when headphone is re-plugged
- Limited the fix to specific device IDs (0x14f11f86, 0x14f11f87) based on testing
- Added proper cleanup in remove function
sound/hda/codecs/conexant.c | 73 +++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c
index 5fcbc1312c69..f2f447ab749e 100644
--- a/sound/hda/codecs/conexant.c
+++ b/sound/hda/codecs/conexant.c
@@ -43,6 +43,10 @@ struct conexant_spec {
unsigned int gpio_mute_led_mask;
unsigned int gpio_mic_led_mask;
bool is_cx11880_sn6140;
+
+ /* Pop noise mitigation */
+ struct hda_codec *codec;
+ struct delayed_work pop_mitigation_work;
};
@@ -212,10 +216,74 @@ static void cx_auto_shutdown(struct hda_codec *codec)
static void cx_remove(struct hda_codec *codec)
{
+ struct conexant_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->pop_mitigation_work);
cx_auto_shutdown(codec);
snd_hda_gen_remove(codec);
}
+static void mute_unmute_speaker(struct hda_codec *codec, hda_nid_t nid, bool mute)
+{
+ unsigned int conn_sel, dac, conn_list, gain_left, gain_right;
+
+ conn_sel = snd_hda_codec_read(codec, nid, 0, 0xf01, 0x0);
+ conn_list = snd_hda_codec_read(codec, nid, 0, 0xf02, 0x0);
+
+ dac = ((conn_list >> (conn_sel * 8)) & 0xff);
+ if (dac == 0)
+ return;
+
+ gain_left = snd_hda_codec_read(codec, dac, 0, 0xba0, 0x0);
+ gain_right = snd_hda_codec_read(codec, dac, 0, 0xb80, 0x0);
+
+ if (mute) {
+ gain_left |= 0x80;
+ gain_right |= 0x80;
+ } else {
+ gain_left &= (~(0x80));
+ gain_right &= (~(0x80));
+ }
+
+ snd_hda_codec_write(codec, dac, 0, 0x3a0, gain_left);
+ snd_hda_codec_write(codec, dac, 0, 0x390, gain_right);
+
+ if (mute) {
+ snd_hda_codec_write(codec, nid, 0, 0x707, 0);
+ codec_dbg(codec, "mute_speaker, set 0x%x PinCtrl to 0.\n", nid);
+ } else {
+ snd_hda_codec_write(codec, nid, 0, 0x707, 0x40);
+ codec_dbg(codec, "unmute_speaker, set 0x%x PinCtrl to 0x40.\n", nid);
+ }
+}
+
+static void pop_mitigation_worker(struct work_struct *work)
+{
+ struct conexant_spec *spec = container_of(work, struct conexant_spec,
+ pop_mitigation_work.work);
+ struct hda_codec *codec = spec->codec;
+
+ mute_unmute_speaker(codec, 0x17, false);
+}
+
+static void cx_auto_pop_mitigation(struct hda_codec *codec,
+ struct hda_jack_callback *event)
+{
+ struct conexant_spec *spec = codec->spec;
+ int phone_present;
+
+ phone_present = snd_hda_codec_read(codec, 0x16, 0, 0xf09, 0x0);
+ if (!(phone_present & 0x80000000)) {
+ /* Headphone unplugged, mute speaker immediately */
+ mute_unmute_speaker(codec, 0x17, true);
+ /* Schedule unmute after 20ms delay */
+ schedule_delayed_work(&spec->pop_mitigation_work, msecs_to_jiffies(20));
+ } else {
+ /* Headphone plugged in, cancel any pending unmute */
+ cancel_delayed_work_sync(&spec->pop_mitigation_work);
+ }
+}
+
static void cx_process_headset_plugin(struct hda_codec *codec)
{
unsigned int val;
@@ -1178,6 +1246,9 @@ static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id)
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+
+ spec->codec = codec;
+ INIT_DELAYED_WORK(&spec->pop_mitigation_work, pop_mitigation_worker);
snd_hda_gen_spec_init(&spec->gen);
codec->spec = spec;
@@ -1187,6 +1258,8 @@ static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id)
case 0x14f11f87:
spec->is_cx11880_sn6140 = true;
snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref);
+ /* Enable pop noise mitigation for both codecs */
+ snd_hda_jack_detect_enable_callback(codec, 0x16, cx_auto_pop_mitigation);
break;
}
--
2.25.1
Powered by blists - more mailing lists