]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: usb-audio: Implement jack detection for HP Thunderbolt Dock G2
authorTasos Sahanidis <tasos@tasossah.com>
Wed, 26 Nov 2025 00:29:57 +0000 (02:29 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 26 Nov 2025 08:34:08 +0000 (09:34 +0100)
The HP Thunderbolt Dock G2 includes a headset jack with support for
jack detection. However, this being a UAC1 device, detection is
implemented via vendor-defined URB Controls.

Implement it in a similar way to the Dell WD15/19 docks, but with
different commands.

Signed-off-by: Tasos Sahanidis <tasos@tasossah.com>
Link: https://patch.msgid.link/20251126003805.2705503-3-tasos@tasossah.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/mixer_quirks.c

index a48eb3b8cab2a59e5e2dc360f5b3e4ac9a3ef279..fe6c2cebc7f0dbea0153c78d1386ef30228e9649 100644 (file)
@@ -2363,6 +2363,105 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
        return 0;
 }
 
+/*
+ * HP Thunderbolt Dock G2 jack detection
+ *
+ * Similar to the Dell WD15/WD19, but with different commands.
+ */
+
+#define HP_DOCK_JACK_INTERRUPT_NODE    7
+
+#define HP_DOCK_GET                    37
+
+#define HP_DOCK_JACK_PRESENCE          0xffb8
+#define HP_DOCK_JACK_PRESENCE_BIT      BIT(2)
+
+#define HP_DOCK_MIC_SENSE              0xf753
+#define HP_DOCK_MIC_SENSE_COMPLETE_BIT BIT(4)
+
+#define HP_DOCK_MIC_SENSE_MASK         (BIT(2) | BIT(1) | BIT(0))
+/* #define HP_DOCK_MIC_SENSE_PRESENT   0x2 */
+#define HP_DOCK_MIC_SENSE_NOT_PRESENT  0x4
+
+static int hp_dock_ctl_connector_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *cval = snd_kcontrol_chip(kcontrol);
+       struct snd_usb_audio *chip = cval->head.mixer->chip;
+       u32 pv = kcontrol->private_value;
+       bool presence;
+       int err;
+       u8 buf;
+
+       CLASS(snd_usb_lock, pm)(chip);
+       if (pm.err < 0)
+               return pm.err;
+
+       err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
+                      HP_DOCK_GET,
+                      USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN,
+                      0, HP_DOCK_JACK_PRESENCE, &buf, sizeof(buf));
+       if (err < 0)
+               return err;
+
+       presence = !(buf & HP_DOCK_JACK_PRESENCE_BIT);
+
+       if (pv && presence) {
+               for (int i = 0; i < 20; i++) {
+                       err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),
+                              HP_DOCK_GET,
+                              USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN,
+                              0, HP_DOCK_MIC_SENSE, &buf, sizeof(buf));
+                       if (err < 0)
+                               return err;
+
+                       /* Mic sense is complete, we have a result. */
+                       if (buf & HP_DOCK_MIC_SENSE_COMPLETE_BIT)
+                               break;
+
+                       msleep(100);
+               }
+
+               /*
+                * If we reach the retry limit without mic sense having
+                * completed, buf will contain HP_DOCK_MIC_SENSE_PRESENT,
+                * thus presence remains true even when detection fails.
+                */
+               if ((buf & HP_DOCK_MIC_SENSE_MASK) == HP_DOCK_MIC_SENSE_NOT_PRESENT)
+                       presence = false;
+       }
+       ucontrol->value.integer.value[0] = presence;
+       return 0;
+}
+
+static const struct snd_kcontrol_new hp_dock_connector_ctl_ro = {
+       .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+       .name = "", /* will be filled later manually */
+       .access = SNDRV_CTL_ELEM_ACCESS_READ,
+       .info = snd_ctl_boolean_mono_info,
+       .get = hp_dock_ctl_connector_get,
+};
+
+static int hp_dock_mixer_create(struct usb_mixer_interface *mixer)
+{
+       int err;
+
+       err = realtek_add_jack(mixer, "Headsets Playback Jack", 0,
+                              HP_DOCK_JACK_INTERRUPT_NODE,
+                              &hp_dock_connector_ctl_ro);
+       if (err < 0)
+               return err;
+
+       err = realtek_add_jack(mixer, "Headset Capture Jack", 1,
+                              HP_DOCK_JACK_INTERRUPT_NODE,
+                              &hp_dock_connector_ctl_ro);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+
 /* RME Class Compliant device quirks */
 
 #define SND_RME_GET_STATUS1                    23
@@ -4414,6 +4513,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
        case USB_ID(0x2b73, 0x0034): /* Pioneer DJ DJM-V10 */
                err = snd_djm_controls_create(mixer, SND_DJM_V10_IDX);
                break;
+       case USB_ID(0x03f0, 0x0269): /* HP TB Dock G2 */
+               err = hp_dock_mixer_create(mixer);
+               break;
        }
 
        return err;