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

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>
Link: https://patch.msgid.link/20260328-hda-proc-gpi-gpo-v1-1-fabb36564bee@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
master
Cássio Gabriel 2026-03-28 01:53:35 -03:00 committed by Takashi Iwai
parent 0da18c2dd1
commit 3bd246d1cf
2 changed files with 74 additions and 33 deletions

View File

@ -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

View File

@ -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);
}