* [PATCH] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic resume
@ 2026-09-04 23:15 Rithvik Vibhu
2026-09-14 16:23 ` Takashi Iwai
0 siblings, 1 reply; 5+ messages in thread
From: Rithvik Vibhu @ 2026-09-04 23:15 UTC (permalink / raw)
To: Takashi Iwai, Jaroslav Kysela; +Cc: linux-sound, linux-kernel, Rithvik Vibhu
The existing pin-configuration quirk exposes headset mic pin 0x19, but
the ALC295 loses the correct combo-jack state across suspend-to-RAM
(S3). After resume, pin 0x19 can remain falsely present after unplug and
the capture mixer can remain routed away from the internal mic on pin
0x12. The failure occurs on both 6.12 and v7.2-rc7.
Give subsystem 1025:1539 a model-specific fixup which initializes the
Acer combo-jack coefficients, enables the full Realtek CTIA/OMTP
headset state machine, gates the headset mic with the reliable
headphone pin 0x21, and feeds the corrected cached jack state through
generic HDA mic autoswitching. This keeps the actual capture mux
synchronized across boot, hotplug, unplug and S3 resume.
Apply the correction from both the headphone and microphone callback
paths. A direct event from mic pin 0x19 otherwise bypasses the headphone
hook and can restore the unreliable raw pin sense. Invalidate and
refetch pin 0x21 when handling such an event before deriving the
corrected microphone state.
Pin 0x21 indicates that a combo-jack plug is present but cannot
distinguish three-pole headphones from a four-pole headset.
Consequently, as with other headphone-gated mic configurations, a
three-pole plug is exposed as a headset mic.
Tested with cold boot and repeated S3 cycles in the unplugged and
four-pole-headset states, including unplug and reinsert after resume.
Fixes: 51db05283f7c ("ALSA: hda/realtek: Enable headset mic for Acer Nitro 5")
Cc: stable@vger.kernel.org # 6.12+
Assisted-by: LLM
Signed-off-by: Rithvik Vibhu <rithvikvibhu@gmail.com>
---
The 6.12.y backport requires adaptation because the Realtek codec code
is located in sound/pci/hda/patch_realtek.c there.
base-commit: 3b26ceef88c110f4d188387cffa0df78657be904
sound/hda/codecs/realtek/alc269.c | 105 +++++++++++++++++++++++++++++-
1 file changed, 104 insertions(+), 1 deletion(-)
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 95b40a177..f1a83908e 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -3740,6 +3740,103 @@ static void alc2xx_fixup_headset_mic(struct hda_codec *codec,
}
}
+static void alc295_acer_nitro_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_jack_tbl *hp_jack, *mic_jack;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool auto_mic = spec->gen.auto_mic;
+ bool hp_present;
+
+ hp_jack = snd_hda_jack_tbl_get(codec, hp_pin);
+ mic_jack = snd_hda_jack_tbl_get(codec, 0x19);
+ if (!hp_jack || !mic_jack) {
+ codec_warn(codec, "failed to find Acer Nitro combo-jack entries\n");
+ return;
+ }
+
+ /* A mic callback must not reuse a possibly stale cached HP sense. */
+ if (jack && jack->nid == 0x19)
+ hp_jack->jack_dirty = 1;
+ hp_present = snd_hda_jack_detect(codec, hp_pin);
+
+ /*
+ * The codec reports a stale or inverted presence state for mic pin 0x19
+ * after S3. Pin 0x21 continues to track the shared combo jack reliably,
+ * so use it as the presence source for 0x19. Leave the entry clean so
+ * the normal report pass does not immediately replace this value with a
+ * broken raw pin-sense read.
+ */
+ mic_jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
+ if (hp_present)
+ mic_jack->pin_sense |= AC_PINSENSE_PRESENCE;
+ mic_jack->jack_dirty = 0;
+
+ /* Route the actual capture mux from the corrected jack state. */
+ spec->gen.auto_mic = 1;
+ snd_hda_gen_mic_autoswitch(codec, jack);
+ spec->gen.auto_mic = auto_mic;
+}
+
+static void alc295_acer_nitro_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ /* Refresh the reliable HP jack and the Realtek headset mode first. */
+ alc_update_headset_jack_cb(codec, jack);
+ alc295_acer_nitro_mic_autoswitch(codec, jack);
+}
+
+static void alc295_fixup_acer_nitro_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x03a1103c },
+ { }
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ alc_update_coef_idx(codec, 0x45, 0xf << 12 | 1 << 10, 5 << 12);
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ }
+
+ alc271_hp_gate_mic_jack(codec, fix, action);
+ alc_fixup_headset_mode_no_hp_mic(codec, fix, action);
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ int int_mic_idx = -1, headset_mic_idx = -1;
+ int i;
+
+ for (i = 0; i < spec->gen.input_mux.num_items; i++) {
+ if (spec->gen.imux_pins[i] == 0x12)
+ int_mic_idx = i;
+ else if (spec->gen.imux_pins[i] == 0x19)
+ headset_mic_idx = i;
+ }
+ if (int_mic_idx < 0 || headset_mic_idx < 0) {
+ codec_warn(codec,
+ "failed to find Acer Nitro mic mux entries\n");
+ return;
+ }
+
+ spec->gen.am_num_entries = 2;
+ spec->gen.am_entry[0] = (struct automic_entry) {
+ .pin = 0x12,
+ .idx = int_mic_idx,
+ };
+ spec->gen.am_entry[1] = (struct automic_entry) {
+ .pin = 0x19,
+ .idx = headset_mic_idx,
+ };
+ spec->gen.hp_automute_hook = alc295_acer_nitro_hp_automute;
+ spec->gen.mic_autoswitch_hook =
+ alc295_acer_nitro_mic_autoswitch;
+ }
+}
+
static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4335,6 +4432,7 @@ enum {
ALC287_FIXUP_THINKPAD_I2S_SPK,
ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
ALC2XX_FIXUP_HEADSET_MIC,
+ ALC295_FIXUP_ACER_NITRO_HEADSET_MODE,
ALC289_FIXUP_DELL_CS35L41_SPI_2,
ALC256_FIXUP_ACER_SFG16_MICMUTE_LED,
ALC256_FIXUP_HEADPHONE_AMP_VOL,
@@ -6854,6 +6952,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc2xx_fixup_headset_mic,
},
+ [ALC295_FIXUP_ACER_NITRO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_acer_nitro_headset_mode,
+ },
[ALC289_FIXUP_DELL_CS35L41_SPI_2] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_spi_two,
@@ -7177,7 +7279,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57",
+ ALC295_FIXUP_ACER_NITRO_HEADSET_MODE),
SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC),
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic resume
2026-09-04 23:15 [PATCH] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic resume Rithvik Vibhu
@ 2026-09-14 16:23 ` Takashi Iwai
2026-09-15 1:06 ` [PATCH v2 0/2] ALSA: hda: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
0 siblings, 1 reply; 5+ messages in thread
From: Takashi Iwai @ 2026-09-14 16:23 UTC (permalink / raw)
To: Rithvik Vibhu; +Cc: Takashi Iwai, Jaroslav Kysela, linux-sound, linux-kernel
On Sat, 05 Sep 2026 01:15:45 +0200,
Rithvik Vibhu wrote:
>
> The existing pin-configuration quirk exposes headset mic pin 0x19, but
> the ALC295 loses the correct combo-jack state across suspend-to-RAM
> (S3). After resume, pin 0x19 can remain falsely present after unplug and
> the capture mixer can remain routed away from the internal mic on pin
> 0x12. The failure occurs on both 6.12 and v7.2-rc7.
>
> Give subsystem 1025:1539 a model-specific fixup which initializes the
> Acer combo-jack coefficients, enables the full Realtek CTIA/OMTP
> headset state machine, gates the headset mic with the reliable
> headphone pin 0x21, and feeds the corrected cached jack state through
> generic HDA mic autoswitching. This keeps the actual capture mux
> synchronized across boot, hotplug, unplug and S3 resume.
>
> Apply the correction from both the headphone and microphone callback
> paths. A direct event from mic pin 0x19 otherwise bypasses the headphone
> hook and can restore the unreliable raw pin sense. Invalidate and
> refetch pin 0x21 when handling such an event before deriving the
> corrected microphone state.
>
> Pin 0x21 indicates that a combo-jack plug is present but cannot
> distinguish three-pole headphones from a four-pole headset.
> Consequently, as with other headphone-gated mic configurations, a
> three-pole plug is exposed as a headset mic.
>
> Tested with cold boot and repeated S3 cycles in the unplugged and
> four-pole-headset states, including unplug and reinsert after resume.
>
> Fixes: 51db05283f7c ("ALSA: hda/realtek: Enable headset mic for Acer Nitro 5")
> Cc: stable@vger.kernel.org # 6.12+
> Assisted-by: LLM
> Signed-off-by: Rithvik Vibhu <rithvikvibhu@gmail.com>
Thanks for the patch.
I guess I understand most of the intentions in your code, but it looks
a bit too complex and handling things unnaturally. After all, there
is no similar code there, which already appearing suspicious.
Can this be handled better with the gating stuff in
sound/hda/common/jack.c, instead?
Takashi
> ---
> The 6.12.y backport requires adaptation because the Realtek codec code
> is located in sound/pci/hda/patch_realtek.c there.
>
> base-commit: 3b26ceef88c110f4d188387cffa0df78657be904
>
> sound/hda/codecs/realtek/alc269.c | 105 +++++++++++++++++++++++++++++-
> 1 file changed, 104 insertions(+), 1 deletion(-)
>
> diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
> index 95b40a177..f1a83908e 100644
> --- a/sound/hda/codecs/realtek/alc269.c
> +++ b/sound/hda/codecs/realtek/alc269.c
> @@ -3740,6 +3740,103 @@ static void alc2xx_fixup_headset_mic(struct hda_codec *codec,
> }
> }
>
> +static void alc295_acer_nitro_mic_autoswitch(struct hda_codec *codec,
> + struct hda_jack_callback *jack)
> +{
> + struct alc_spec *spec = codec->spec;
> + struct hda_jack_tbl *hp_jack, *mic_jack;
> + hda_nid_t hp_pin = alc_get_hp_pin(spec);
> + bool auto_mic = spec->gen.auto_mic;
> + bool hp_present;
> +
> + hp_jack = snd_hda_jack_tbl_get(codec, hp_pin);
> + mic_jack = snd_hda_jack_tbl_get(codec, 0x19);
> + if (!hp_jack || !mic_jack) {
> + codec_warn(codec, "failed to find Acer Nitro combo-jack entries\n");
> + return;
> + }
> +
> + /* A mic callback must not reuse a possibly stale cached HP sense. */
> + if (jack && jack->nid == 0x19)
> + hp_jack->jack_dirty = 1;
> + hp_present = snd_hda_jack_detect(codec, hp_pin);
> +
> + /*
> + * The codec reports a stale or inverted presence state for mic pin 0x19
> + * after S3. Pin 0x21 continues to track the shared combo jack reliably,
> + * so use it as the presence source for 0x19. Leave the entry clean so
> + * the normal report pass does not immediately replace this value with a
> + * broken raw pin-sense read.
> + */
> + mic_jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
> + if (hp_present)
> + mic_jack->pin_sense |= AC_PINSENSE_PRESENCE;
> + mic_jack->jack_dirty = 0;
> +
> + /* Route the actual capture mux from the corrected jack state. */
> + spec->gen.auto_mic = 1;
> + snd_hda_gen_mic_autoswitch(codec, jack);
> + spec->gen.auto_mic = auto_mic;
> +}
> +
> +static void alc295_acer_nitro_hp_automute(struct hda_codec *codec,
> + struct hda_jack_callback *jack)
> +{
> + /* Refresh the reliable HP jack and the Realtek headset mode first. */
> + alc_update_headset_jack_cb(codec, jack);
> + alc295_acer_nitro_mic_autoswitch(codec, jack);
> +}
> +
> +static void alc295_fixup_acer_nitro_headset_mode(struct hda_codec *codec,
> + const struct hda_fixup *fix,
> + int action)
> +{
> + struct alc_spec *spec = codec->spec;
> + static const struct hda_pintbl pincfgs[] = {
> + { 0x19, 0x03a1103c },
> + { }
> + };
> +
> + if (action == HDA_FIXUP_ACT_PRE_PROBE) {
> + snd_hda_apply_pincfgs(codec, pincfgs);
> + alc_update_coef_idx(codec, 0x45, 0xf << 12 | 1 << 10, 5 << 12);
> + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
> + }
> +
> + alc271_hp_gate_mic_jack(codec, fix, action);
> + alc_fixup_headset_mode_no_hp_mic(codec, fix, action);
> +
> + if (action == HDA_FIXUP_ACT_PROBE) {
> + int int_mic_idx = -1, headset_mic_idx = -1;
> + int i;
> +
> + for (i = 0; i < spec->gen.input_mux.num_items; i++) {
> + if (spec->gen.imux_pins[i] == 0x12)
> + int_mic_idx = i;
> + else if (spec->gen.imux_pins[i] == 0x19)
> + headset_mic_idx = i;
> + }
> + if (int_mic_idx < 0 || headset_mic_idx < 0) {
> + codec_warn(codec,
> + "failed to find Acer Nitro mic mux entries\n");
> + return;
> + }
> +
> + spec->gen.am_num_entries = 2;
> + spec->gen.am_entry[0] = (struct automic_entry) {
> + .pin = 0x12,
> + .idx = int_mic_idx,
> + };
> + spec->gen.am_entry[1] = (struct automic_entry) {
> + .pin = 0x19,
> + .idx = headset_mic_idx,
> + };
> + spec->gen.hp_automute_hook = alc295_acer_nitro_hp_automute;
> + spec->gen.mic_autoswitch_hook =
> + alc295_acer_nitro_mic_autoswitch;
> + }
> +}
> +
> static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec,
> const struct hda_fixup *fix, int action)
> {
> @@ -4335,6 +4432,7 @@ enum {
> ALC287_FIXUP_THINKPAD_I2S_SPK,
> ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
> ALC2XX_FIXUP_HEADSET_MIC,
> + ALC295_FIXUP_ACER_NITRO_HEADSET_MODE,
> ALC289_FIXUP_DELL_CS35L41_SPI_2,
> ALC256_FIXUP_ACER_SFG16_MICMUTE_LED,
> ALC256_FIXUP_HEADPHONE_AMP_VOL,
> @@ -6854,6 +6952,10 @@ static const struct hda_fixup alc269_fixups[] = {
> .type = HDA_FIXUP_FUNC,
> .v.func = alc2xx_fixup_headset_mic,
> },
> + [ALC295_FIXUP_ACER_NITRO_HEADSET_MODE] = {
> + .type = HDA_FIXUP_FUNC,
> + .v.func = alc295_fixup_acer_nitro_headset_mode,
> + },
> [ALC289_FIXUP_DELL_CS35L41_SPI_2] = {
> .type = HDA_FIXUP_FUNC,
> .v.func = cs35l41_fixup_spi_two,
> @@ -7177,7 +7279,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
> SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
> SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
> SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
> - SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC),
> + SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57",
> + ALC295_FIXUP_ACER_NITRO_HEADSET_MODE),
> SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED),
> SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC),
> SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC),
> --
> 2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 0/2] ALSA: hda: Fix Acer Nitro 5 AN515-57 mic switching
2026-09-14 16:23 ` Takashi Iwai
@ 2026-09-15 1:06 ` Rithvik Vibhu
2026-09-15 1:06 ` [PATCH v2 1/2] ALSA: hda: Allow jack presence to follow another pin Rithvik Vibhu
2026-09-15 1:06 ` [PATCH v2 2/2] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
0 siblings, 2 replies; 5+ messages in thread
From: Rithvik Vibhu @ 2026-09-15 1:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Jaroslav Kysela, linux-sound, linux-kernel, Rithvik Vibhu
Hi Takashi,
Thanks for the review :)
I reworked the fix to use gating in jack.c. I believe this is what you
meant, and will fix if not.
Ordinary gating still requires the microphone pin's own presence bit.
On this laptop, pin 0x19 can report both false presence after S3 and false
absence after reinsertion, so gating alone does not cover both failures.
Patch 1 adds an opt-in mode that takes presence entirely from another pin,
using the existing gating relationship for callback propagation.
Patch 2 uses headphone pin 0x21 as the source for headset-mic pin 0x19 on
the Acer Nitro 5 AN515-57 (1025:1539).
Based on sound.git for-linus at:
c9e6e5f38bf75276605f1952b22285f5f3abcaff
Previous submission:
https://lore.kernel.org/r/20260904231545.33505-1-rithvikvibhu@gmail.com
Thanks,
Rithvik
Rithvik Vibhu (2):
ALSA: hda: Allow jack presence to follow another pin
ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic switching
sound/hda/codecs/realtek/alc269.c | 25 ++++++++++++++++++++++++-
sound/hda/common/hda_jack.h | 4 ++
sound/hda/common/jack.c | 83 ++++++++++++++++++++++++++++++++++++-
3 files changed, 110 insertions(+), 2 deletions(-)
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] ALSA: hda: Allow jack presence to follow another pin
2026-09-15 1:06 ` [PATCH v2 0/2] ALSA: hda: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
@ 2026-09-15 1:06 ` Rithvik Vibhu
2026-09-15 1:06 ` [PATCH v2 2/2] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
1 sibling, 0 replies; 5+ messages in thread
From: Rithvik Vibhu @ 2026-09-15 1:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Jaroslav Kysela, linux-sound, linux-kernel, Rithvik Vibhu
On some combo jacks, the microphone pin can report absence even when
a headset is plugged in. Ordinary jack gating still requires the
microphone pin's own presence bit, so it cannot handle this case.
Add snd_hda_jack_set_presence_source() to derive a jack's presence
entirely from another pin. Use the existing gating relationship for
callback propagation, and invalidate the related caches before
callbacks run for events from either pin.
Keep the dependent jack detectable and non-phantom so generic
microphone autoswitching can use it. Make the new behavior opt-in,
preserving ordinary gating for existing callers.
Assisted-by: LLM
Signed-off-by: Rithvik Vibhu <rithvikvibhu@gmail.com>
---
sound/hda/common/hda_jack.h | 4 ++
sound/hda/common/jack.c | 83 ++++++++++++++++++++++++++++++++++++-
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/sound/hda/common/hda_jack.h b/sound/hda/common/hda_jack.h
index e9b9970c5..8887d70ca 100644
--- a/sound/hda/common/hda_jack.h
+++ b/sound/hda/common/hda_jack.h
@@ -37,6 +37,7 @@ struct hda_jack_tbl {
unsigned int jack_detect:1; /* capable of jack-detection? */
unsigned int jack_dirty:1; /* needs to update? */
unsigned int phantom_jack:1; /* a fixed, always present port? */
+ unsigned int gating_jack_only:1; /* presence comes only from the gate */
unsigned int block_report:1; /* in a transitional state - do not report to userspace */
hda_nid_t gating_jack; /* valid when gating jack plugged */
hda_nid_t gated_jack; /* gated is dependent on this jack */
@@ -101,6 +102,9 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid);
+int snd_hda_jack_set_presence_source(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t source_nid);
+
int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
const struct hda_jack_keymap *keymap,
hda_nid_t jack_nid);
diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c
index 1d6b0f0e6..5e8045388 100644
--- a/sound/hda/common/jack.c
+++ b/sound/hda/common/jack.c
@@ -204,7 +204,7 @@ static void jack_detect_update(struct hda_codec *codec,
if (!jack->jack_dirty)
return;
- if (jack->phantom_jack)
+ if (jack->phantom_jack || jack->gating_jack_only)
jack->pin_sense = AC_PINSENSE_PRESENCE;
else
jack->pin_sense = read_pin_sense(codec, jack->nid,
@@ -405,6 +405,68 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
}
EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
+static bool is_detectable_analog_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+
+ return get_wcaps_type(wcaps) == AC_WID_PIN &&
+ !(wcaps & AC_WCAP_DIGITAL) &&
+ get_defcfg_connect(snd_hda_codec_get_pincfg(codec, nid)) ==
+ AC_JACK_PORT_COMPLEX &&
+ is_jack_detectable(codec, nid);
+}
+
+/**
+ * snd_hda_jack_set_presence_source - Use another pin's presence detection
+ * @codec: the HDA codec
+ * @nid: pin with unreliable presence detection
+ * @source_nid: pin providing presence detection
+ *
+ * Unlike ordinary gating, this ignores @nid's own pin sense. Both pins must
+ * be jack-detectable analog pins; the target remains a normal, non-phantom
+ * jack. The source's events also invoke the target's registered callbacks.
+ * Set this up during codec probing, before jack controls are created.
+ * Chained or conflicting relationships and DisplayPort MST are not supported.
+ *
+ * Return: zero on success, -EINVAL for an unsupported relationship, or
+ * -ENOMEM if a jack-table entry cannot be allocated.
+ */
+int snd_hda_jack_set_presence_source(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t source_nid)
+{
+ struct hda_jack_tbl *jack, *source;
+
+ if (codec->dp_mst || !nid || !source_nid || nid == source_nid ||
+ !is_detectable_analog_pin(codec, nid) ||
+ !is_detectable_analog_pin(codec, source_nid))
+ return -EINVAL;
+
+ jack = snd_hda_jack_tbl_get(codec, nid);
+ source = snd_hda_jack_tbl_get(codec, source_nid);
+ if ((jack && (jack->phantom_jack || jack->gated_jack ||
+ jack->key_report_jack ||
+ (jack->gating_jack && jack->gating_jack != source_nid))) ||
+ (source && (source->phantom_jack || source->gating_jack ||
+ source->key_report_jack ||
+ (source->gated_jack && source->gated_jack != nid))))
+ return -EINVAL;
+
+ if (!snd_hda_jack_tbl_new(codec, nid, 0) ||
+ !snd_hda_jack_tbl_new(codec, source_nid, 0))
+ return -ENOMEM;
+
+ /* Allocating the source entry may have moved the jack table. */
+ jack = snd_hda_jack_tbl_get(codec, nid);
+ source = snd_hda_jack_tbl_get(codec, source_nid);
+ jack->gating_jack = source_nid;
+ jack->gating_jack_only = 1;
+ jack->jack_dirty = 1;
+ source->gated_jack = nid;
+ source->jack_dirty = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_presence_source);
+
/**
* snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
* @codec: the HDA codec
@@ -741,6 +803,25 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
} else
event->jack_dirty = 1;
+ /* A target-only event must also refresh its source's cached sense. */
+ if (event->gating_jack_only) {
+ struct hda_jack_tbl *source =
+ snd_hda_jack_tbl_get_mst(codec, event->gating_jack,
+ event->dev_id);
+
+ if (source)
+ source->jack_dirty = 1;
+ }
+ if (event->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get_mst(codec, event->gated_jack,
+ event->dev_id);
+
+ /* The source need not have a callback that reads its pin sense. */
+ if (gated && gated->gating_jack_only)
+ gated->jack_dirty = 1;
+ }
+
call_jack_callback(codec, res, event);
snd_hda_jack_report_sync(codec);
}
base-commit: c9e6e5f38bf75276605f1952b22285f5f3abcaff
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic switching
2026-09-15 1:06 ` [PATCH v2 0/2] ALSA: hda: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
2026-09-15 1:06 ` [PATCH v2 1/2] ALSA: hda: Allow jack presence to follow another pin Rithvik Vibhu
@ 2026-09-15 1:06 ` Rithvik Vibhu
1 sibling, 0 replies; 5+ messages in thread
From: Rithvik Vibhu @ 2026-09-15 1:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Jaroslav Kysela, linux-sound, linux-kernel, Rithvik Vibhu
On the Acer Nitro 5 AN515-57 (1025:1539), headset-mic pin 0x19 can
report presence with nothing plugged in after suspend-to-RAM (S3).
It can also report absence after a headset is reinserted. This causes
microphone autoswitching to select the wrong input. Headphone pin
0x21 continues to report plug presence reliably.
Use 0x21 as the presence source for 0x19. Retain the existing mic pin
configuration and coefficient initialization, and enable the existing
Realtek headset-mode fixup to restore the combo jack's input mode
during initialization, resume and jack events.
Headphone presence cannot distinguish three-pole headphones from
four-pole headsets. A three-pole plug is therefore also reported as a
headset microphone and selected by microphone autoswitching.
Tested with the internal microphone and a four-pole headset, including
unplug, reinsertion and S3 suspend/resume.
Fixes: 51db05283f7c ("ALSA: hda/realtek: Enable headset mic for Acer Nitro 5")
Assisted-by: LLM
Signed-off-by: Rithvik Vibhu <rithvikvibhu@gmail.com>
---
sound/hda/codecs/realtek/alc269.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 5127a111c..40b9d1221 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -3740,6 +3740,23 @@ static void alc2xx_fixup_headset_mic(struct hda_codec *codec,
}
}
+static void alc295_fixup_acer_nitro_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int err;
+
+ alc2xx_fixup_headset_mic(codec, fix, action);
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ /* Mic pin 0x19 can report either state incorrectly after S3. */
+ err = snd_hda_jack_set_presence_source(codec, 0x19, 0x21);
+ if (err < 0)
+ codec_warn(codec, "failed to set headset presence source: %d\n",
+ err);
+ }
+ alc_fixup_headset_mode_no_hp_mic(codec, fix, action);
+}
+
static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4336,6 +4353,7 @@ enum {
ALC287_FIXUP_THINKPAD_I2S_SPK,
ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
ALC2XX_FIXUP_HEADSET_MIC,
+ ALC295_FIXUP_ACER_NITRO_HEADSET_MODE,
ALC289_FIXUP_DELL_CS35L41_SPI_2,
ALC256_FIXUP_ACER_SFG16_MICMUTE_LED,
ALC256_FIXUP_HEADPHONE_AMP_VOL,
@@ -6861,6 +6879,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc2xx_fixup_headset_mic,
},
+ [ALC295_FIXUP_ACER_NITRO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_acer_nitro_headset_mode,
+ },
[ALC289_FIXUP_DELL_CS35L41_SPI_2] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs35l41_fixup_spi_two,
@@ -7184,7 +7206,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57",
+ ALC295_FIXUP_ACER_NITRO_HEADSET_MODE),
SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC),
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-15 1:07 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-04 23:15 [PATCH] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic resume Rithvik Vibhu
2026-09-14 16:23 ` Takashi Iwai
2026-09-15 1:06 ` [PATCH v2 0/2] ALSA: hda: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
2026-09-15 1:06 ` [PATCH v2 1/2] ALSA: hda: Allow jack presence to follow another pin Rithvik Vibhu
2026-09-15 1:06 ` [PATCH v2 2/2] ALSA: hda/realtek: Fix Acer Nitro 5 AN515-57 mic switching Rithvik Vibhu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®