[PATCH v4] ALSA: hda/cs8409: Fix for Dell Cirrus audio jack detect
From: Steven 'Steve' Kendall
Date: Thu Sep 17 2026 - 21:27:25 EST
On some models like the Dell Inspiron 15 3520, jack
detection does not work. This patch switches to
delayed work to fix jack plug on Chrome OS for this model.
Assisted-by: LLM
Signed-off-by: Steven 'Steve' Kendall <skend@xxxxxxxxxxxx>
---
Changes in v4:
- Delayed logic is now limited to the device with the issue.
- Tested for regression on Ubuntu 26.04 and looked good.
- Link to v3: https://patch.msgid.link/20260915-fix-headphone-plug-cirrus-dell-v3-1-6dc555b00f61@xxxxxxxxxxxx
Changes in v3:
- Unified sleep time for both cases.
- Removed some superfluous logic after some additional testing.
- Link to v2: https://patch.msgid.link/20260718-fix-headphone-plug-cirrus-dell-v2-1-f88121570d62@xxxxxxxxxxxx
Changes in v2:
- Changes from v1 have been removed.
- New approach uses delayed work and also solves the issue on this machine.
- I'm now using the latest release of b4. Hopefully this addresses the formatting issues I was having!
- Link to v1: https://lore.kernel.org/r/20260713-fix-headphone-plug-cirrus-dell-v1-1-3c5157cd45cd@xxxxxxxxxxxx
---
sound/hda/codecs/cirrus/cs8409-tables.c | 9 +++++-
sound/hda/codecs/cirrus/cs8409.c | 51 ++++++++++++++++++++++++++++++---
sound/hda/codecs/cirrus/cs8409.h | 4 +++
3 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c
index b9ec8fb8eab7c..d0cef8492c994 100644
--- a/sound/hda/codecs/cirrus/cs8409-tables.c
+++ b/sound/hda/codecs/cirrus/cs8409-tables.c
@@ -594,7 +594,7 @@ const struct hda_quirk cs8409_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN),
SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN),
SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN),
- SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK_DELAYED_JD),
SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK),
SND_PCI_QUIRK(0x1028, 0x0BB5, "Warlock N3 15 TGL-U Nuvoton EC", CS8409_WARLOCK),
@@ -625,6 +625,7 @@ const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_BULLSEYE, .name = "bullseye" },
{ .id = CS8409_WARLOCK, .name = "warlock" },
{ .id = CS8409_WARLOCK_MLK, .name = "warlock mlk" },
+ { .id = CS8409_WARLOCK_MLK_DELAYED_JD, .name = "warlock mlk delayed jd" },
{ .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" },
{ .id = CS8409_CYBORG, .name = "cyborg" },
{ .id = CS8409_DOLPHIN, .name = "dolphin" },
@@ -652,6 +653,12 @@ const struct hda_fixup cs8409_fixups[] = {
.chained = true,
.chain_id = CS8409_FIXUPS,
},
+ [CS8409_WARLOCK_MLK_DELAYED_JD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
[CS8409_WARLOCK_MLK_DUAL_MIC] = {
.type = HDA_FIXUP_PINS,
.v.pins = cs8409_cs42l42_pincfgs,
diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c
index c43ff3ef75b6e..056a31d121b5c 100644
--- a/sound/hda/codecs/cirrus/cs8409.c
+++ b/sound/hda/codecs/cirrus/cs8409.c
@@ -56,6 +56,9 @@ static int cs8409_parse_auto_config(struct hda_codec *codec)
}
static void cs8409_disable_i2c_clock_worker(struct work_struct *work);
+static void cs8409_jack_detect_worker(struct work_struct *work);
+static void cs42l42_run_jack_detect(struct sub_codec *cs42l42);
+static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr);
static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
{
@@ -69,6 +72,7 @@ static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
codec->power_save_node = 1;
mutex_init(&spec->i2c_mux);
INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker);
+ INIT_DELAYED_WORK(&spec->jack_detect_work, cs8409_jack_detect_worker);
snd_hda_gen_spec_init(&spec->gen);
return spec;
@@ -115,6 +119,28 @@ static void cs8409_disable_i2c_clock_worker(struct work_struct *work)
cs8409_disable_i2c_clock(spec->codec);
}
+/*
+ * cs8409_jack_detect_worker - Perform initial jack detection once hardware is settled
+ *
+ * Defer initial jack detection. On ChromeOS and possibly other platforms this
+ * delay is required for jack detection to work as expected.
+ */
+static void cs8409_jack_detect_worker(struct work_struct *work)
+{
+ struct cs8409_spec *spec = container_of(work, struct cs8409_spec, jack_detect_work.work);
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ if (spec->init_done && spec->build_ctrl_done && !cs42l42->hp_jack_in) {
+ int reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ /* error case */
+ if (reg_ts_status < 0)
+ return;
+
+ cs42l42_run_jack_detect(cs42l42);
+ }
+}
+
/*
* cs8409_enable_i2c_clock - Enable I2C clocks
* @codec: the codec instance
@@ -954,6 +980,8 @@ static void cs8409_remove(struct hda_codec *codec)
/* Cancel i2c clock disable timer, and disable clock if left enabled */
cancel_delayed_work_sync(&spec->i2c_clk_work);
cs8409_disable_i2c_clock(codec);
+ /* Also cancel jack detect work */
+ cancel_delayed_work_sync(&spec->jack_detect_work);
snd_hda_gen_remove(codec);
}
@@ -1026,6 +1054,8 @@ static int cs8409_cs42l42_suspend(struct hda_codec *codec)
/* Cancel i2c clock disable timer, and disable clock if left enabled */
cancel_delayed_work_sync(&spec->i2c_clk_work);
cs8409_disable_i2c_clock(codec);
+ /* Also cancel jack detect work */
+ cancel_delayed_work_sync(&spec->jack_detect_work);
snd_hda_shutup_pins(codec);
@@ -1153,6 +1183,9 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
break;
+ case CS8409_WARLOCK_MLK_DELAYED_JD:
+ spec->delay_jack_detect = 1;
+ fallthrough;
case CS8409_WARLOCK_MLK:
case CS8409_WARLOCK_MLK_DUAL_MIC:
spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
@@ -1197,8 +1230,13 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
cs8409_cs42l42_hw_init(codec);
spec->init_done = 1;
if (spec->init_done && spec->build_ctrl_done
- && !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
- cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) {
+ if (spec->delay_jack_detect)
+ schedule_delayed_work(&spec->jack_detect_work,
+ msecs_to_jiffies(CS8409_JACK_DETECT_DELAY_MS));
+ else
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ }
break;
case HDA_FIXUP_ACT_BUILD:
spec->build_ctrl_done = 1;
@@ -1208,8 +1246,13 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
* Run immediately after init.
*/
if (spec->init_done && spec->build_ctrl_done
- && !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
- cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ && !spec->scodecs[CS8409_CODEC0]->hp_jack_in) {
+ if (spec->delay_jack_detect)
+ schedule_delayed_work(&spec->jack_detect_work,
+ msecs_to_jiffies(CS8409_JACK_DETECT_DELAY_MS));
+ else
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ }
break;
default:
break;
diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h
index be1714a84fff4..dacc1083b9d46 100644
--- a/sound/hda/codecs/cirrus/cs8409.h
+++ b/sound/hda/codecs/cirrus/cs8409.h
@@ -247,6 +247,7 @@ enum cs8409_coefficient_index_registers {
#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
+#define CS8409_JACK_DETECT_DELAY_MS 100
/* Dolphin */
@@ -266,6 +267,7 @@ enum {
CS8409_BULLSEYE,
CS8409_WARLOCK,
CS8409_WARLOCK_MLK,
+ CS8409_WARLOCK_MLK_DELAYED_JD,
CS8409_WARLOCK_MLK_DUAL_MIC,
CS8409_CYBORG,
CS8409_FIXUPS,
@@ -339,12 +341,14 @@ struct cs8409_spec {
unsigned int i2c_clck_enabled;
unsigned int dev_addr;
struct delayed_work i2c_clk_work;
+ struct delayed_work jack_detect_work;
unsigned int playback_started:1;
unsigned int capture_started:1;
unsigned int init_done:1;
unsigned int build_ctrl_done:1;
unsigned int speaker_muted:1;
+ unsigned int delay_jack_detect:1;
/* verb exec op override */
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
---
base-commit: df2908090cda368b01ff43709f51890076c56157
change-id: 20260710-fix-headphone-plug-cirrus-dell-5e3b49da5f52
Best regards,
--
Steven 'Steve' Kendall <skend@xxxxxxxxxxxx>