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,