From f21d232b2ba3997c1fd33cbe37ff47f72d2a9f2a Mon Sep 17 00:00:00 2001 From: Steven 'Steve' Kendall Date: Wed, 7 Oct 2026 00:31:24 +0000 Subject: [PATCH] ALSA: hda/cs8409: Fix for Dell Cirrus audio jack detect 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 Link: https://patch.msgid.link/20261007-fix-headphone-plug-cirrus-dell-v5-1-c28146b18d5c@chromium.org Signed-off-by: Takashi Iwai --- sound/hda/codecs/cirrus/cs8409-tables.c | 9 ++++- sound/hda/codecs/cirrus/cs8409.c | 52 +++++++++++++++++++++++-- sound/hda/codecs/cirrus/cs8409.h | 4 ++ 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c index b9ec8fb8eab7..d0cef8492c99 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 c43ff3ef75b6..12ee71d7a35c 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 @@ -951,6 +977,8 @@ static void cs8409_remove(struct hda_codec *codec) { struct cs8409_spec *spec = codec->spec; + /* Disable jack detect work */ + disable_delayed_work_sync(&spec->jack_detect_work); /* Cancel i2c clock disable timer, and disable clock if left enabled */ cancel_delayed_work_sync(&spec->i2c_clk_work); cs8409_disable_i2c_clock(codec); @@ -1020,6 +1048,9 @@ static int cs8409_cs42l42_suspend(struct hda_codec *codec) cs8409_enable_ur(codec, 0); + /* Cancel jack detect work first as it accesses CS42L42 over I2C */ + cancel_delayed_work_sync(&spec->jack_detect_work); + for (i = 0; i < spec->num_scodecs; i++) cs42l42_suspend(spec->scodecs[i]); @@ -1153,6 +1184,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 +1231,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 +1247,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 be1714a84fff..dacc1083b9d4 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,