mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Rithvik Vibhu <rithvikvibhu@gmail.com>
To: Takashi Iwai <tiwai@suse.de>
Cc: Jaroslav Kysela <perex@perex.cz>,
	linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
	Rithvik Vibhu <rithvikvibhu@gmail.com>
Subject: [PATCH v2 1/2] ALSA: hda: Allow jack presence to follow another pin
Date: Mon, 14 Sep 2026 18:06:56 -0700	[thread overview]
Message-ID: <20260915010657.36140-2-rithvikvibhu@gmail.com> (raw)
In-Reply-To: <20260915010657.36140-1-rithvikvibhu@gmail.com>

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


  reply	other threads:[~2026-09-15  1:07 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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     ` Rithvik Vibhu [this message]
2026-09-15  1:06     ` [PATCH v2 2/2] ALSA: hda/realtek: " Rithvik Vibhu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260915010657.36140-2-rithvikvibhu@gmail.com \
    --to=rithvikvibhu@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®