[PATCH] ALSA: hda/proc: show GPI and GPO state in codec proc output

Cássio Gabriel posted 1 patch 5 days, 7 hours ago
include/sound/hda_verbs.h |   7 +++-
sound/hda/common/proc.c   | 100 +++++++++++++++++++++++++++++++---------------
2 files changed, 74 insertions(+), 33 deletions(-)
[PATCH] ALSA: hda/proc: show GPI and GPO state in codec proc output
Posted by Cássio Gabriel 5 days, 7 hours ago
print_gpio() prints the GPIO capability header and the bidirectional
GPIO state, but it never reports the separate GPI and GPO pins even
though AC_PAR_GPIO_CAP exposes their counts.

The HD-audio specification defines dedicated GPI and GPO verbs
alongside the GPIO ones, so codecs with input-only or output-only
general-purpose pins currently lose that state from
/proc/asound/card*/codec#* altogether.

Add the missing read verb definitions and extend print_gpio() to dump
the GPI and GPO pins, too, while leaving the existing IO[] output
unchanged.

Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
---
 include/sound/hda_verbs.h |   7 +++-
 sound/hda/common/proc.c   | 100 +++++++++++++++++++++++++++++++---------------
 2 files changed, 74 insertions(+), 33 deletions(-)

diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h
index 006d358acce2..127e7016e4fe 100644
--- a/include/sound/hda_verbs.h
+++ b/include/sound/hda_verbs.h
@@ -56,7 +56,12 @@ enum {
 #define AC_VERB_GET_DIGI_CONVERT_1		0x0f0d
 #define AC_VERB_GET_DIGI_CONVERT_2		0x0f0e /* unused */
 #define AC_VERB_GET_VOLUME_KNOB_CONTROL		0x0f0f
-/* f10-f1a: GPIO */
+/* f10-f1a: GPI/GPO/GPIO */
+#define AC_VERB_GET_GPI_DATA			0x0f10
+#define AC_VERB_GET_GPI_WAKE_MASK		0x0f11
+#define AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK	0x0f12
+#define AC_VERB_GET_GPI_STICKY_MASK		0x0f13
+#define AC_VERB_GET_GPO_DATA			0x0f14
 #define AC_VERB_GET_GPIO_DATA			0x0f15
 #define AC_VERB_GET_GPIO_MASK			0x0f16
 #define AC_VERB_GET_GPIO_DIRECTION		0x0f17
diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c
index 3bc33c5617b2..c83796b13d3d 100644
--- a/sound/hda/common/proc.c
+++ b/sound/hda/common/proc.c
@@ -640,41 +640,78 @@ static void print_gpio(struct snd_info_buffer *buffer,
 {
 	unsigned int gpio =
 		param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
-	unsigned int enable, direction, wake, unsol, sticky, data;
-	int i, max;
+	int i, gpio_max, gpo_max, gpi_max;
+
+	gpio_max = gpio & AC_GPIO_IO_COUNT;
+	gpo_max = (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT;
+	gpi_max = (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT;
+
 	snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
 		    "unsolicited=%d, wake=%d\n",
-		    gpio & AC_GPIO_IO_COUNT,
-		    (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
-		    (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
+		    gpio_max, gpo_max, gpi_max,
 		    (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
 		    (gpio & AC_GPIO_WAKE) ? 1 : 0);
-	max = gpio & AC_GPIO_IO_COUNT;
-	if (!max || max > 8)
-		return;
-	enable = snd_hda_codec_read(codec, nid, 0,
-				    AC_VERB_GET_GPIO_MASK, 0);
-	direction = snd_hda_codec_read(codec, nid, 0,
-				       AC_VERB_GET_GPIO_DIRECTION, 0);
-	wake = snd_hda_codec_read(codec, nid, 0,
-				  AC_VERB_GET_GPIO_WAKE_MASK, 0);
-	unsol  = snd_hda_codec_read(codec, nid, 0,
-				    AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
-	sticky = snd_hda_codec_read(codec, nid, 0,
-				    AC_VERB_GET_GPIO_STICKY_MASK, 0);
-	data = snd_hda_codec_read(codec, nid, 0,
-				  AC_VERB_GET_GPIO_DATA, 0);
-	for (i = 0; i < max; ++i)
-		snd_iprintf(buffer,
-			    "  IO[%d]: enable=%d, dir=%d, wake=%d, "
-			    "sticky=%d, data=%d, unsol=%d\n", i,
-			    (enable & (1<<i)) ? 1 : 0,
-			    (direction & (1<<i)) ? 1 : 0,
-			    (wake & (1<<i)) ? 1 : 0,
-			    (sticky & (1<<i)) ? 1 : 0,
-			    (data & (1<<i)) ? 1 : 0,
-			    (unsol & (1<<i)) ? 1 : 0);
-	/* FIXME: add GPO and GPI pin information */
+
+	if (gpio_max && gpio_max <= 8) {
+		unsigned int enable, direction, wake, unsol, sticky, data;
+
+		enable = snd_hda_codec_read(codec, nid, 0,
+					    AC_VERB_GET_GPIO_MASK, 0);
+		direction = snd_hda_codec_read(codec, nid, 0,
+					       AC_VERB_GET_GPIO_DIRECTION, 0);
+		wake = snd_hda_codec_read(codec, nid, 0,
+					  AC_VERB_GET_GPIO_WAKE_MASK, 0);
+		unsol = snd_hda_codec_read(codec, nid, 0,
+					   AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK,
+					   0);
+		sticky = snd_hda_codec_read(codec, nid, 0,
+					    AC_VERB_GET_GPIO_STICKY_MASK, 0);
+		data = snd_hda_codec_read(codec, nid, 0,
+					  AC_VERB_GET_GPIO_DATA, 0);
+		for (i = 0; i < gpio_max; ++i) {
+			snd_iprintf(buffer,
+				    "  IO[%d]: enable=%d, dir=%d, wake=%d, ",
+				    i, (enable & (1 << i)) ? 1 : 0,
+				    (direction & (1 << i)) ? 1 : 0,
+				    (wake & (1 << i)) ? 1 : 0);
+			snd_iprintf(buffer,
+				    "sticky=%d, data=%d, unsol=%d\n",
+				    (sticky & (1 << i)) ? 1 : 0,
+				    (data & (1 << i)) ? 1 : 0,
+				    (unsol & (1 << i)) ? 1 : 0);
+		}
+	}
+
+	if (gpo_max && gpo_max <= 8) {
+		unsigned int gpo_data;
+
+		gpo_data = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_GPO_DATA, 0);
+		for (i = 0; i < gpo_max; ++i)
+			snd_iprintf(buffer, "  GPO[%d]: data=%d\n", i,
+				    (gpo_data & (1 << i)) ? 1 : 0);
+	}
+
+	if (gpi_max && gpi_max <= 8) {
+		unsigned int wake, unsol, sticky, data;
+
+		wake = snd_hda_codec_read(codec, nid, 0,
+					  AC_VERB_GET_GPI_WAKE_MASK, 0);
+		unsol = snd_hda_codec_read(codec, nid, 0,
+					   AC_VERB_GET_GPI_UNSOLICITED_RSP_MASK,
+					   0);
+		sticky = snd_hda_codec_read(codec, nid, 0,
+					    AC_VERB_GET_GPI_STICKY_MASK, 0);
+		data = snd_hda_codec_read(codec, nid, 0,
+					  AC_VERB_GET_GPI_DATA, 0);
+		for (i = 0; i < gpi_max; ++i)
+			snd_iprintf(buffer, "  GPI[%d]: wake=%d, sticky=%d, data=%d, unsol=%d\n",
+				    i, (wake & (1 << i)) ? 1 : 0,
+				    (sticky & (1 << i)) ? 1 : 0,
+				    (data & (1 << i)) ? 1 : 0,
+				    (unsol & (1 << i)) ? 1 : 0);
+	}
+
 	print_nid_array(buffer, codec, nid, &codec->mixers);
 	print_nid_array(buffer, codec, nid, &codec->nids);
 }
@@ -940,4 +977,3 @@ int snd_hda_codec_proc_new(struct hda_codec *codec)
 	snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
 	return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
 }
-

---
base-commit: 0ce5115cb8f38e23044b04644fce7661336c1efc
change-id: 20260328-hda-proc-gpi-gpo-950a518e7ac6

Best regards,
--  
Cássio Gabriel <cassiogabrielcontato@gmail.com>

Re: [PATCH] ALSA: hda/proc: show GPI and GPO state in codec proc output
Posted by Takashi Iwai 4 days, 22 hours ago
On Sat, 28 Mar 2026 05:53:35 +0100,
Cássio Gabriel wrote:
> 
> print_gpio() prints the GPIO capability header and the bidirectional
> GPIO state, but it never reports the separate GPI and GPO pins even
> though AC_PAR_GPIO_CAP exposes their counts.
> 
> The HD-audio specification defines dedicated GPI and GPO verbs
> alongside the GPIO ones, so codecs with input-only or output-only
> general-purpose pins currently lose that state from
> /proc/asound/card*/codec#* altogether.
> 
> Add the missing read verb definitions and extend print_gpio() to dump
> the GPI and GPO pins, too, while leaving the existing IO[] output
> unchanged.
> 
> Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>

Applied to for-next branch now.

I noticed that the verbs for setting GPI and GPO are missing.
Will submit a patch to complete those.


thanks,

Takashi
Re: [PATCH] ALSA: hda/proc: show GPI and GPO state in codec proc output
Posted by Cássio Gabriel Monteiro Pires 4 days, 17 hours ago
On 3/28/26 10:42, Takashi Iwai wrote:
> 
> I noticed that the verbs for setting GPI and GPO are missing.
> Will submit a patch to complete those.
> 
> 
> thanks,
> 
> Takashi

Cool, thank you!

-- 
Cássio