KVM: x86: hyper-v: Cache HYPERV_CPUID_NESTED_FEATURES CPUID leaf
[linux-2.6-microblaze.git] / sound / usb / mixer_quirks.c
index d35cf54..ab0d459 100644 (file)
@@ -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,13 +1935,194 @@ 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_MANUAL_MODE    72
+#define REALTEK_HDA_GET_OUT    88
+#define REALTEK_HDA_GET_IN     89
+
+#define REALTEK_AUDIO_FUNCTION_GROUP   0x01
+#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;
+       __be32 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;
+       __be32 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;
+       struct usb_device *dev = mixer->chip->dev;
+
+       /* Power down the audio codec to avoid loud pops in the next step. */
+       realtek_hda_set(mixer->chip,
+                       HDA_VERB_CMD(AC_VERB_SET_POWER_STATE,
+                                    REALTEK_AUDIO_FUNCTION_GROUP,
+                                    AC_PWRST_D3));
+
+       /*
+        * Turn off 'manual mode' in case it was enabled. This removes the need
+        * to power cycle the dock after it was attached to a Windows machine.
+        */
+       snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), REALTEK_MANUAL_MODE,
+                       USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
+                       0, 0, NULL, 0);
+
+       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;
 
        snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
                        USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
-                       ch, snd_usb_ctrl_intf(chip) | (id << 8),
+                       (UAC_FU_VOLUME << 8) | ch,
+                       snd_usb_ctrl_intf(chip) | (id << 8),
                        &buf, 2);
 }
 
@@ -3238,6 +3420,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
        case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
        case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
        case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
+       case USB_ID(0x1235, 0x820c): /* Focusrite Clarett+ 8Pre */
                err = snd_scarlett_gen2_init(mixer);
                break;
 
@@ -3245,6 +3428,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;