--- /dev/null
+From e8ae73dee7bf9f00cc11f7ec4a5bdfbe403f6307 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Oct 2024 23:12:49 +0100
+Subject: ALSA: usb-audio: Add quirks for Dell WD19 dock
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jan Schär <jan@jschaer.ch>
+
+[ Upstream commit 4413665dd6c528b31284119e3571c25f371e1c36 ]
+
+The WD19 family of docks has the same audio chipset as the WD15. This
+change enables jack detection on the WD19.
+
+We don't need the dell_dock_mixer_init quirk for the WD19. It is only
+needed because of the dell_alc4020_map quirk for the WD15 in
+mixer_maps.c, which disables the volume controls. Even for the WD15,
+this quirk was apparently only needed when the dock firmware was not
+updated.
+
+Signed-off-by: Jan Schär <jan@jschaer.ch>
+Cc: <stable@vger.kernel.org>
+Link: https://patch.msgid.link/20241029221249.15661-1-jan@jschaer.ch
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/usb/mixer_quirks.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
+index 25bad7a791d7a..e150c4ee2417d 100644
+--- a/sound/usb/mixer_quirks.c
++++ b/sound/usb/mixer_quirks.c
+@@ -3441,6 +3441,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+ break;
+ err = dell_dock_mixer_init(mixer);
+ break;
++ case USB_ID(0x0bda, 0x402e): /* Dell WD19 dock */
++ err = dell_dock_mixer_create(mixer);
++ break;
+
+ case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */
+ case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */
+--
+2.43.0
+
--- /dev/null
+From 106d633d0348874f77e78f2df82b505f3608d794 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 27 Jun 2022 19:18:54 +0200
+Subject: ALSA: usb-audio: Support jack detection on Dell dock
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Jan Schär <jan@jschaer.ch>
+
+[ Upstream commit 4b8ea38fabab45ad911a32a336416062553dfe9c ]
+
+The Dell WD15 dock has a headset and a line out port. Add support for
+detecting if a jack is inserted into one of these ports.
+For the headset jack, additionally determine if a mic is present.
+
+The WD15 contains an ALC4020 USB audio controller and ALC3263 audio codec
+from Realtek. It is a UAC 1 device, and UAC 1 does not support jack
+detection. Instead, jack detection works by sending HD Audio commands over
+vendor-type USB messages.
+
+I found out how it works by looking at USB captures on Windows.
+The audio codec is very similar to the one supported by
+sound/soc/codecs/rt298.c / rt298.h, some constant names and the mic
+detection are adapted from there. The realtek_add_jack function is adapted
+from build_connector_control in sound/usb/mixer.c.
+
+I tested this on a WD15 dock with the latest firmware.
+
+Signed-off-by: Jan Schär <jan@jschaer.ch>
+Link: https://lore.kernel.org/r/20220627171855.42338-1-jan@jschaer.ch
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Stable-dep-of: 4413665dd6c5 ("ALSA: usb-audio: Add quirks for Dell WD19 dock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/usb/mixer_quirks.c | 167 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 167 insertions(+)
+
+diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
+index 9d58f8d20f6a5..25bad7a791d7a 100644
+--- a/sound/usb/mixer_quirks.c
++++ b/sound/usb/mixer_quirks.c
+@@ -24,6 +24,7 @@
+ #include <sound/asoundef.h>
+ #include <sound/core.h>
+ #include <sound/control.h>
++#include <sound/hda_verbs.h>
+ #include <sound/hwdep.h>
+ #include <sound/info.h>
+ #include <sound/tlv.h>
+@@ -1934,6 +1935,169 @@ static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer)
+ NULL);
+ }
+
++/*
++ * Dell WD15 dock jack detection
++ *
++ * The WD15 contains an ALC4020 USB audio controller and ALC3263 audio codec
++ * from Realtek. It is a UAC 1 device, and UAC 1 does not support jack
++ * detection. Instead, jack detection works by sending HD Audio commands over
++ * vendor-type USB messages.
++ */
++
++#define HDA_VERB_CMD(V, N, D) (((N) << 20) | ((V) << 8) | (D))
++
++#define REALTEK_HDA_VALUE 0x0038
++
++#define REALTEK_HDA_SET 62
++#define REALTEK_HDA_GET_OUT 88
++#define REALTEK_HDA_GET_IN 89
++
++#define REALTEK_LINE1 0x1a
++#define REALTEK_VENDOR_REGISTERS 0x20
++#define REALTEK_HP_OUT 0x21
++
++#define REALTEK_CBJ_CTRL2 0x50
++
++#define REALTEK_JACK_INTERRUPT_NODE 5
++
++#define REALTEK_MIC_FLAG 0x100
++
++static int realtek_hda_set(struct snd_usb_audio *chip, u32 cmd)
++{
++ struct usb_device *dev = chip->dev;
++ u32 buf = cpu_to_be32(cmd);
++
++ return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), REALTEK_HDA_SET,
++ USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
++ REALTEK_HDA_VALUE, 0, &buf, sizeof(buf));
++}
++
++static int realtek_hda_get(struct snd_usb_audio *chip, u32 cmd, u32 *value)
++{
++ struct usb_device *dev = chip->dev;
++ int err;
++ u32 buf = cpu_to_be32(cmd);
++
++ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), REALTEK_HDA_GET_OUT,
++ USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
++ REALTEK_HDA_VALUE, 0, &buf, sizeof(buf));
++ if (err < 0)
++ return err;
++ err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), REALTEK_HDA_GET_IN,
++ USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN,
++ REALTEK_HDA_VALUE, 0, &buf, sizeof(buf));
++ if (err < 0)
++ return err;
++
++ *value = be32_to_cpu(buf);
++ return 0;
++}
++
++static int realtek_ctl_connector_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct usb_mixer_elem_info *cval = kcontrol->private_data;
++ struct snd_usb_audio *chip = cval->head.mixer->chip;
++ u32 pv = kcontrol->private_value;
++ u32 node_id = pv & 0xff;
++ u32 sense;
++ u32 cbj_ctrl2;
++ bool presence;
++ int err;
++
++ err = snd_usb_lock_shutdown(chip);
++ if (err < 0)
++ return err;
++ err = realtek_hda_get(chip,
++ HDA_VERB_CMD(AC_VERB_GET_PIN_SENSE, node_id, 0),
++ &sense);
++ if (err < 0)
++ goto err;
++ if (pv & REALTEK_MIC_FLAG) {
++ err = realtek_hda_set(chip,
++ HDA_VERB_CMD(AC_VERB_SET_COEF_INDEX,
++ REALTEK_VENDOR_REGISTERS,
++ REALTEK_CBJ_CTRL2));
++ if (err < 0)
++ goto err;
++ err = realtek_hda_get(chip,
++ HDA_VERB_CMD(AC_VERB_GET_PROC_COEF,
++ REALTEK_VENDOR_REGISTERS, 0),
++ &cbj_ctrl2);
++ if (err < 0)
++ goto err;
++ }
++err:
++ snd_usb_unlock_shutdown(chip);
++ if (err < 0)
++ return err;
++
++ presence = sense & AC_PINSENSE_PRESENCE;
++ if (pv & REALTEK_MIC_FLAG)
++ presence = presence && (cbj_ctrl2 & 0x0070) == 0x0070;
++ ucontrol->value.integer.value[0] = presence;
++ return 0;
++}
++
++static const struct snd_kcontrol_new realtek_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 = realtek_ctl_connector_get,
++};
++
++static int realtek_resume_jack(struct usb_mixer_elem_list *list)
++{
++ snd_ctl_notify(list->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
++ &list->kctl->id);
++ return 0;
++}
++
++static int realtek_add_jack(struct usb_mixer_interface *mixer,
++ char *name, u32 val)
++{
++ struct usb_mixer_elem_info *cval;
++ struct snd_kcontrol *kctl;
++
++ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
++ if (!cval)
++ return -ENOMEM;
++ snd_usb_mixer_elem_init_std(&cval->head, mixer,
++ REALTEK_JACK_INTERRUPT_NODE);
++ cval->head.resume = realtek_resume_jack;
++ cval->val_type = USB_MIXER_BOOLEAN;
++ cval->channels = 1;
++ cval->min = 0;
++ cval->max = 1;
++ kctl = snd_ctl_new1(&realtek_connector_ctl_ro, cval);
++ if (!kctl) {
++ kfree(cval);
++ return -ENOMEM;
++ }
++ kctl->private_value = val;
++ strscpy(kctl->id.name, name, sizeof(kctl->id.name));
++ kctl->private_free = snd_usb_mixer_elem_free;
++ return snd_usb_mixer_add_control(&cval->head, kctl);
++}
++
++static int dell_dock_mixer_create(struct usb_mixer_interface *mixer)
++{
++ int err;
++
++ err = realtek_add_jack(mixer, "Line Out Jack", REALTEK_LINE1);
++ if (err < 0)
++ return err;
++ err = realtek_add_jack(mixer, "Headphone Jack", REALTEK_HP_OUT);
++ if (err < 0)
++ return err;
++ err = realtek_add_jack(mixer, "Headset Mic Jack",
++ REALTEK_HP_OUT | REALTEK_MIC_FLAG);
++ if (err < 0)
++ return err;
++ return 0;
++}
++
+ static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
+ {
+ u16 buf = 0;
+@@ -3272,6 +3436,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+ err = snd_soundblaster_e1_switch_create(mixer);
+ break;
+ case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
++ err = dell_dock_mixer_create(mixer);
++ if (err < 0)
++ break;
+ err = dell_dock_mixer_init(mixer);
+ break;
+
+--
+2.43.0
+