Merge tag 'platform-drivers-x86-v3.19-1' of git://git.infradead.org/users/dvhart...
[firefly-linux-kernel-4.4.55.git] / sound / usb / mixer_quirks.c
index 8c9bf4b7aaf0e003db413347efe1ca2ae09053fe..dc9df007d3e33358a51b73bb6446d81f6d427ab1 100644 (file)
@@ -41,6 +41,7 @@
 #include "usbaudio.h"
 #include "mixer.h"
 #include "mixer_quirks.h"
+#include "mixer_scarlett.h"
 #include "helper.h"
 
 extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
@@ -52,13 +53,6 @@ struct std_mono_table {
        snd_kcontrol_tlv_rw_t *tlv_callback;
 };
 
-/* private_free callback */
-static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
-{
-       kfree(kctl->private_data);
-       kctl->private_data = NULL;
-}
-
 /* This function allows for the creation of standard UAC controls.
  * See the quirks for M-Audio FTUs or Ebox-44.
  * If you don't want to set a TLV callback pass NULL.
@@ -75,7 +69,6 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
                                const char *name,
                                snd_kcontrol_tlv_rw_t *tlv_callback)
 {
-       int err;
        struct usb_mixer_elem_info *cval;
        struct snd_kcontrol *kctl;
 
@@ -83,8 +76,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
        if (!cval)
                return -ENOMEM;
 
-       cval->id = unitid;
-       cval->mixer = mixer;
+       snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
        cval->val_type = val_type;
        cval->channels = 1;
        cval->control = control;
@@ -108,7 +100,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
 
        /* Set name */
        snprintf(kctl->id.name, sizeof(kctl->id.name), name);
-       kctl->private_free = usb_mixer_elem_free;
+       kctl->private_free = snd_usb_mixer_elem_free;
 
        /* set TLV */
        if (tlv_callback) {
@@ -118,11 +110,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
                        SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
        }
        /* Add control to mixer */
-       err = snd_usb_mixer_add_control(mixer, kctl);
-       if (err < 0)
-               return err;
-
-       return 0;
+       return snd_usb_mixer_add_control(&cval->head, kctl);
 }
 
 static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
@@ -156,6 +144,32 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer,
        return 0;
 }
 
+static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer,
+                                     int id,
+                                     usb_mixer_elem_resume_func_t resume,
+                                     const struct snd_kcontrol_new *knew,
+                                     struct usb_mixer_elem_list **listp)
+{
+       struct usb_mixer_elem_list *list;
+       struct snd_kcontrol *kctl;
+
+       list = kzalloc(sizeof(*list), GFP_KERNEL);
+       if (!list)
+               return -ENOMEM;
+       if (listp)
+               *listp = list;
+       list->mixer = mixer;
+       list->id = id;
+       list->resume = resume;
+       kctl = snd_ctl_new1(knew, list);
+       if (!kctl) {
+               kfree(list);
+               return -ENOMEM;
+       }
+       kctl->private_free = snd_usb_mixer_elem_free;
+       return snd_usb_mixer_add_control(list, kctl);
+}
+
 /*
  * Sound Blaster remote control configuration
  *
@@ -283,84 +297,90 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
 
 static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int index = kcontrol->private_value;
-
-       ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
+       ucontrol->value.integer.value[0] = kcontrol->private_value >> 8;
        return 0;
 }
 
-static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
+                                   int value, int index)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int index = kcontrol->private_value;
-       int value = ucontrol->value.integer.value[0];
-       int err, changed;
+       struct snd_usb_audio *chip = mixer->chip;
+       int err;
 
-       if (value > 1)
-               return -EINVAL;
-       changed = value != mixer->audigy2nx_leds[index];
-       down_read(&mixer->chip->shutdown_rwsem);
-       if (mixer->chip->shutdown) {
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown) {
                err = -ENODEV;
                goto out;
        }
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042))
-               err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+       if (chip->usb_id == USB_ID(0x041e, 0x3042))
+               err = snd_usb_ctl_msg(chip->dev,
+                             usb_sndctrlpipe(chip->dev, 0), 0x24,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                              !value, 0, NULL, 0);
        /* USB X-Fi S51 Pro */
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
-               err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+       if (chip->usb_id == USB_ID(0x041e, 0x30df))
+               err = snd_usb_ctl_msg(chip->dev,
+                             usb_sndctrlpipe(chip->dev, 0), 0x24,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                              !value, 0, NULL, 0);
        else
-               err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+               err = snd_usb_ctl_msg(chip->dev,
+                             usb_sndctrlpipe(chip->dev, 0), 0x24,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                              value, index + 2, NULL, 0);
  out:
-       up_read(&mixer->chip->shutdown_rwsem);
-       if (err < 0)
-               return err;
-       mixer->audigy2nx_leds[index] = value;
-       return changed;
+       up_read(&chip->shutdown_rwsem);
+       return err;
 }
 
-static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "CMSS LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 0,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Power LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 1,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Dolby Digital LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 2,
-       },
+static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+       struct usb_mixer_interface *mixer = list->mixer;
+       int index = kcontrol->private_value & 0xff;
+       int value = ucontrol->value.integer.value[0];
+       int old_value = kcontrol->private_value >> 8;
+       int err;
+
+       if (value > 1)
+               return -EINVAL;
+       if (value == old_value)
+               return 0;
+       kcontrol->private_value = (value << 8) | index;
+       err = snd_audigy2nx_led_update(mixer, value, index);
+       return err < 0 ? err : 1;
+}
+
+static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
+{
+       int priv_value = list->kctl->private_value;
+
+       return snd_audigy2nx_led_update(list->mixer, priv_value >> 8,
+                                       priv_value & 0xff);
+}
+
+/* name and private_value are set dynamically */
+static struct snd_kcontrol_new snd_audigy2nx_control = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_audigy2nx_led_info,
+       .get = snd_audigy2nx_led_get,
+       .put = snd_audigy2nx_led_put,
+};
+
+static const char * const snd_audigy2nx_led_names[] = {
+       "CMSS LED Switch",
+       "Power LED Switch",
+       "Dolby Digital LED Switch",
 };
 
 static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
 {
        int i, err;
 
-       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) {
+               struct snd_kcontrol_new knew;
+
                /* USB X-Fi S51 doesn't have a CMSS LED */
                if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
                        continue;
@@ -373,12 +393,16 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
                         mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
                         mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
                        break; 
-               err = snd_ctl_add(mixer->chip->card,
-                                 snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+
+               knew = snd_audigy2nx_control;
+               knew.name = snd_audigy2nx_led_names[i];
+               knew.private_value = (1 << 8) | i; /* LED on as default */
+               err = add_single_ctl_with_resume(mixer, 0,
+                                                snd_audigy2nx_led_resume,
+                                                &knew, NULL);
                if (err < 0)
                        return err;
        }
-       mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
        return 0;
 }
 
@@ -437,19 +461,9 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
 static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_info *uinfo)
 {
-       static const char *texts[2] = {"1/2",
-                                      "3/4"
-       };
+       static const char * const texts[2] = {"1/2", "3/4"};
 
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = 2;
-       if (uinfo->value.enumerated.item > 1)
-               uinfo->value.enumerated.item = 1;
-       strcpy(uinfo->value.enumerated.name,
-               texts[uinfo->value.enumerated.item]);
-
-       return 0;
+       return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
 }
 
 static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
@@ -459,100 +473,122 @@ static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
+static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
+                                       int value)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       unsigned int value = ucontrol->value.enumerated.item[0];
-       int err, changed;
+       struct snd_usb_audio *chip = mixer->chip;
+       int err;
        unsigned char buf[2];
 
-       if (value > 1)
-               return -EINVAL;
-
-       buf[0] = 0x01;
-       buf[1] = value ? 0x02 : 0x01;
-
-       changed = value != kcontrol->private_value;
-       down_read(&mixer->chip->shutdown_rwsem);
+       down_read(&chip->shutdown_rwsem);
        if (mixer->chip->shutdown) {
                err = -ENODEV;
                goto out;
        }
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                     usb_sndctrlpipe(mixer->chip->dev, 0), UAC_SET_CUR,
+
+       buf[0] = 0x01;
+       buf[1] = value ? 0x02 : 0x01;
+       err = snd_usb_ctl_msg(chip->dev,
+                     usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
                      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
                      0x0400, 0x0e00, buf, 2);
  out:
-       up_read(&mixer->chip->shutdown_rwsem);
-       if (err < 0)
-               return err;
+       up_read(&chip->shutdown_rwsem);
+       return err;
+}
+
+static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+       struct usb_mixer_interface *mixer = list->mixer;
+       unsigned int value = ucontrol->value.enumerated.item[0];
+       int err;
+
+       if (value > 1)
+               return -EINVAL;
+
+       if (value == kcontrol->private_value)
+               return 0;
+
        kcontrol->private_value = value;
-       return changed;
+       err = snd_emu0204_ch_switch_update(mixer, value);
+       return err < 0 ? err : 1;
 }
 
+static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
+{
+       return snd_emu0204_ch_switch_update(list->mixer,
+                                           list->kctl->private_value);
+}
 
-static struct snd_kcontrol_new snd_emu0204_controls[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Front Jack Channels",
-               .info = snd_emu0204_ch_switch_info,
-               .get = snd_emu0204_ch_switch_get,
-               .put = snd_emu0204_ch_switch_put,
-               .private_value = 0,
-       },
+static struct snd_kcontrol_new snd_emu0204_control = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Front Jack Channels",
+       .info = snd_emu0204_ch_switch_info,
+       .get = snd_emu0204_ch_switch_get,
+       .put = snd_emu0204_ch_switch_put,
+       .private_value = 0,
 };
 
 static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
 {
-       int i, err;
-
-       for (i = 0; i < ARRAY_SIZE(snd_emu0204_controls); ++i) {
-               err = snd_ctl_add(mixer->chip->card,
-                       snd_ctl_new1(&snd_emu0204_controls[i], mixer));
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
+       return add_single_ctl_with_resume(mixer, 0,
+                                         snd_emu0204_ch_switch_resume,
+                                         &snd_emu0204_control, NULL);
 }
+
 /* ASUS Xonar U1 / U3 controls */
 
 static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-
-       ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
+       ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02);
        return 0;
 }
 
+static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer,
+                                     unsigned char status)
+{
+       struct snd_usb_audio *chip = mixer->chip;
+       int err;
+
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown)
+               err = -ENODEV;
+       else
+               err = snd_usb_ctl_msg(chip->dev,
+                             usb_sndctrlpipe(chip->dev, 0), 0x08,
+                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                             50, 0, &status, 1);
+       up_read(&chip->shutdown_rwsem);
+       return err;
+}
+
 static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
        u8 old_status, new_status;
-       int err, changed;
+       int err;
 
-       old_status = mixer->xonar_u1_status;
+       old_status = kcontrol->private_value;
        if (ucontrol->value.integer.value[0])
                new_status = old_status | 0x02;
        else
                new_status = old_status & ~0x02;
-       changed = new_status != old_status;
-       down_read(&mixer->chip->shutdown_rwsem);
-       if (mixer->chip->shutdown)
-               err = -ENODEV;
-       else
-               err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
-                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                             50, 0, &new_status, 1);
-       up_read(&mixer->chip->shutdown_rwsem);
-       if (err < 0)
-               return err;
-       mixer->xonar_u1_status = new_status;
-       return changed;
+       if (new_status == old_status)
+               return 0;
+
+       kcontrol->private_value = new_status;
+       err = snd_xonar_u1_switch_update(list->mixer, new_status);
+       return err < 0 ? err : 1;
+}
+
+static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
+{
+       return snd_xonar_u1_switch_update(list->mixer,
+                                         list->kctl->private_value);
 }
 
 static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
@@ -561,82 +597,213 @@ static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
        .info = snd_ctl_boolean_mono_info,
        .get = snd_xonar_u1_switch_get,
        .put = snd_xonar_u1_switch_put,
+       .private_value = 0x05,
 };
 
 static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
 {
+       return add_single_ctl_with_resume(mixer, 0,
+                                         snd_xonar_u1_switch_resume,
+                                         &snd_xonar_u1_output_switch, NULL);
+}
+
+/* Digidesign Mbox 1 clock source switch (internal/spdif) */
+
+static int snd_mbox1_switch_get(struct snd_kcontrol *kctl,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = kctl->private_value;
+       return 0;
+}
+
+static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val)
+{
+       struct snd_usb_audio *chip = mixer->chip;
        int err;
+       unsigned char buff[3];
+
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown) {
+               err = -ENODEV;
+               goto err;
+       }
 
-       err = snd_ctl_add(mixer->chip->card,
-                         snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
+       /* Prepare for magic command to toggle clock source */
+       err = snd_usb_ctl_msg(chip->dev,
+                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
+                               USB_DIR_IN |
+                               USB_TYPE_CLASS |
+                               USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
        if (err < 0)
-               return err;
-       mixer->xonar_u1_status = 0x05;
-       return 0;
+               goto err;
+       err = snd_usb_ctl_msg(chip->dev,
+                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
+                               USB_DIR_IN |
+                               USB_TYPE_CLASS |
+                               USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+       if (err < 0)
+               goto err;
+
+       /* 2 possibilities:     Internal    -> send sample rate
+        *                      S/PDIF sync -> send zeroes
+        * NB: Sample rate locked to 48kHz on purpose to
+        *     prevent user from resetting the sample rate
+        *     while S/PDIF sync is enabled and confusing
+        *     this configuration.
+        */
+       if (val == 0) {
+               buff[0] = 0x80;
+               buff[1] = 0xbb;
+               buff[2] = 0x00;
+       } else {
+               buff[0] = buff[1] = buff[2] = 0x00;
+       }
+
+       /* Send the magic command to toggle the clock source */
+       err = snd_usb_ctl_msg(chip->dev,
+                               usb_sndctrlpipe(chip->dev, 0), 0x1,
+                               USB_TYPE_CLASS |
+                               USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+       if (err < 0)
+               goto err;
+       err = snd_usb_ctl_msg(chip->dev,
+                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
+                               USB_DIR_IN |
+                               USB_TYPE_CLASS |
+                               USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
+       if (err < 0)
+               goto err;
+       err = snd_usb_ctl_msg(chip->dev,
+                               usb_rcvctrlpipe(chip->dev, 0), 0x81,
+                               USB_DIR_IN |
+                               USB_TYPE_CLASS |
+                               USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3);
+       if (err < 0)
+               goto err;
+
+err:
+       up_read(&chip->shutdown_rwsem);
+       return err;
+}
+
+static int snd_mbox1_switch_put(struct snd_kcontrol *kctl,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
+       struct usb_mixer_interface *mixer = list->mixer;
+       int err;
+       bool cur_val, new_val;
+
+       cur_val = kctl->private_value;
+       new_val = ucontrol->value.enumerated.item[0];
+       if (cur_val == new_val)
+               return 0;
+
+       kctl->private_value = new_val;
+       err = snd_mbox1_switch_update(mixer, new_val);
+       return err < 0 ? err : 1;
+}
+
+static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_info *uinfo)
+{
+       static const char *const texts[2] = {
+               "Internal",
+               "S/PDIF"
+       };
+
+       return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
+}
+
+static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list)
+{
+       return snd_mbox1_switch_update(list->mixer, list->kctl->private_value);
+}
+
+static struct snd_kcontrol_new snd_mbox1_switch = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Clock Source",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info = snd_mbox1_switch_info,
+       .get = snd_mbox1_switch_get,
+       .put = snd_mbox1_switch_put,
+       .private_value = 0
+};
+
+static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer)
+{
+       return add_single_ctl_with_resume(mixer, 0,
+                                         snd_mbox1_switch_resume,
+                                         &snd_mbox1_switch, NULL);
 }
 
 /* Native Instruments device quirks */
 
 #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
 
-static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
-                                            struct snd_ctl_elem_value *ucontrol)
+static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
+                                  struct snd_kcontrol *kctl)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
        struct usb_device *dev = mixer->chip->dev;
-       u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
-       u16 wIndex = kcontrol->private_value & 0xffff;
-       u8 tmp;
-       int ret;
-
-       down_read(&mixer->chip->shutdown_rwsem);
-       if (mixer->chip->shutdown)
-               ret = -ENODEV;
-       else
-               ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
-                                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                                 0, wIndex,
-                                 &tmp, sizeof(tmp));
-       up_read(&mixer->chip->shutdown_rwsem);
+       unsigned int pval = kctl->private_value;
+       u8 value;
+       int err;
 
-       if (ret < 0) {
+       err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             (pval >> 16) & 0xff,
+                             USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+                             0, pval & 0xffff, &value, 1);
+       if (err < 0) {
                dev_err(&dev->dev,
-                       "unable to issue vendor read request (ret = %d)", ret);
-               return ret;
+                       "unable to issue vendor read request (ret = %d)", err);
+               return err;
        }
 
-       ucontrol->value.integer.value[0] = tmp;
+       kctl->private_value |= (value << 24);
+       return 0;
+}
 
+static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
+                                            struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = kcontrol->private_value >> 24;
        return 0;
 }
 
+static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
+{
+       struct snd_usb_audio *chip = list->mixer->chip;
+       unsigned int pval = list->kctl->private_value;
+       int err;
+
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown)
+               err = -ENODEV;
+       else
+               err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
+                                     (pval >> 16) & 0xff,
+                                     USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+                                     pval >> 24, pval & 0xffff, NULL, 0, 1000);
+       up_read(&chip->shutdown_rwsem);
+       return err;
+}
+
 static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
                                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       struct usb_device *dev = mixer->chip->dev;
-       u8 bRequest = (kcontrol->private_value >> 16) & 0xff;
-       u16 wIndex = kcontrol->private_value & 0xffff;
-       u16 wValue = ucontrol->value.integer.value[0];
-       int ret;
-
-       down_read(&mixer->chip->shutdown_rwsem);
-       if (mixer->chip->shutdown)
-               ret = -ENODEV;
-       else
-               ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest,
-                                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
-                                 wValue, wIndex,
-                                 NULL, 0, 1000);
-       up_read(&mixer->chip->shutdown_rwsem);
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+       u8 oldval = (kcontrol->private_value >> 24) & 0xff;
+       u8 newval = ucontrol->value.integer.value[0];
+       int err;
 
-       if (ret < 0) {
-               dev_err(&dev->dev,
-                       "unable to issue vendor write request (ret = %d)", ret);
-               return ret;
-       }
+       if (oldval == newval)
+               return 0;
 
-       return 0;
+       kcontrol->private_value &= ~(0xff << 24);
+       kcontrol->private_value |= newval;
+       err = snd_ni_update_cur_val(list);
+       return err < 0 ? err : 1;
 }
 
 static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
@@ -707,16 +874,17 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
        };
 
        for (i = 0; i < count; i++) {
-               struct snd_kcontrol *c;
+               struct usb_mixer_elem_list *list;
 
                template.name = kc[i].name;
                template.private_value = kc[i].private_value;
 
-               c = snd_ctl_new1(&template, mixer);
-               err = snd_ctl_add(mixer->chip->card, c);
-
+               err = add_single_ctl_with_resume(mixer, 0,
+                                                snd_ni_update_cur_val,
+                                                &template, &list);
                if (err < 0)
                        break;
+               snd_ni_control_init_val(mixer, list->kctl);
        }
 
        return err;
@@ -724,170 +892,88 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
 
 /* M-Audio FastTrack Ultra quirks */
 /* FTU Effect switch (also used by C400/C600) */
-struct snd_ftu_eff_switch_priv_val {
-       struct usb_mixer_interface *mixer;
-       int cached_value;
-       int is_cached;
-       int bUnitID;
-       int validx;
-};
-
 static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_info *uinfo)
 {
-       static const char *texts[8] = {"Room 1",
-                                      "Room 2",
-                                      "Room 3",
-                                      "Hall 1",
-                                      "Hall 2",
-                                      "Plate",
-                                      "Delay",
-                                      "Echo"
+       static const char *const texts[8] = {
+               "Room 1", "Room 2", "Room 3", "Hall 1",
+               "Hall 2", "Plate", "Delay", "Echo"
        };
 
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = 8;
-       if (uinfo->value.enumerated.item > 7)
-               uinfo->value.enumerated.item = 7;
-       strcpy(uinfo->value.enumerated.name,
-               texts[uinfo->value.enumerated.item]);
-
-       return 0;
+       return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
 }
 
-static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
-                                       struct snd_ctl_elem_value *ucontrol)
+static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
+                                  struct snd_kcontrol *kctl)
 {
-       struct snd_usb_audio *chip;
-       struct usb_mixer_interface *mixer;
-       struct snd_ftu_eff_switch_priv_val *pval;
+       struct usb_device *dev = mixer->chip->dev;
+       unsigned int pval = kctl->private_value;
        int err;
        unsigned char value[2];
-       int id, validx;
-
-       const int val_len = 2;
 
        value[0] = 0x00;
        value[1] = 0x00;
 
-       pval = (struct snd_ftu_eff_switch_priv_val *)
-               kctl->private_value;
+       err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
+                             USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+                             pval & 0xff00,
+                             snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
+                             value, 2);
+       if (err < 0)
+               return err;
 
-       if (pval->is_cached) {
-               ucontrol->value.enumerated.item[0] = pval->cached_value;
-               return 0;
-       }
+       kctl->private_value |= value[0] << 24;
+       return 0;
+}
 
-       mixer = (struct usb_mixer_interface *) pval->mixer;
-       if (snd_BUG_ON(!mixer))
-               return -EINVAL;
+static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
+       return 0;
+}
 
-       chip = (struct snd_usb_audio *) mixer->chip;
-       if (snd_BUG_ON(!chip))
-               return -EINVAL;
+static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
+{
+       struct snd_usb_audio *chip = list->mixer->chip;
+       unsigned int pval = list->kctl->private_value;
+       unsigned char value[2];
+       int err;
 
-       id = pval->bUnitID;
-       validx = pval->validx;
+       value[0] = pval >> 24;
+       value[1] = 0;
 
-       down_read(&mixer->chip->shutdown_rwsem);
-       if (mixer->chip->shutdown)
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown)
                err = -ENODEV;
        else
                err = snd_usb_ctl_msg(chip->dev,
-                       usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
-                       USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
-                       validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
-                       value, val_len);
-       up_read(&mixer->chip->shutdown_rwsem);
-       if (err < 0)
-               return err;
-
-       ucontrol->value.enumerated.item[0] = value[0];
-       pval->cached_value = value[0];
-       pval->is_cached = 1;
-
-       return 0;
+                                     usb_sndctrlpipe(chip->dev, 0),
+                                     UAC_SET_CUR,
+                                     USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+                                     pval & 0xff00,
+                                     snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
+                                     value, 2);
+       up_read(&chip->shutdown_rwsem);
+       return err;
 }
 
 static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
                                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_usb_audio *chip;
-       struct snd_ftu_eff_switch_priv_val *pval;
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
+       unsigned int pval = list->kctl->private_value;
+       int cur_val, err, new_val;
 
-       struct usb_mixer_interface *mixer;
-       int changed, cur_val, err, new_val;
-       unsigned char value[2];
-       int id, validx;
-
-       const int val_len = 2;
-
-       changed = 0;
-
-       pval = (struct snd_ftu_eff_switch_priv_val *)
-               kctl->private_value;
-       cur_val = pval->cached_value;
+       cur_val = pval >> 24;
        new_val = ucontrol->value.enumerated.item[0];
+       if (cur_val == new_val)
+               return 0;
 
-       mixer = (struct usb_mixer_interface *) pval->mixer;
-       if (snd_BUG_ON(!mixer))
-               return -EINVAL;
-
-       chip = (struct snd_usb_audio *) mixer->chip;
-       if (snd_BUG_ON(!chip))
-               return -EINVAL;
-
-       id = pval->bUnitID;
-       validx = pval->validx;
-
-       if (!pval->is_cached) {
-               /* Read current value */
-               down_read(&mixer->chip->shutdown_rwsem);
-               if (mixer->chip->shutdown)
-                       err = -ENODEV;
-               else
-                       err = snd_usb_ctl_msg(chip->dev,
-                               usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
-                               USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
-                               validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
-                               value, val_len);
-               up_read(&mixer->chip->shutdown_rwsem);
-               if (err < 0)
-                       return err;
-
-               cur_val = value[0];
-               pval->cached_value = cur_val;
-               pval->is_cached = 1;
-       }
-       /* update value if needed */
-       if (cur_val != new_val) {
-               value[0] = new_val;
-               value[1] = 0;
-               down_read(&mixer->chip->shutdown_rwsem);
-               if (mixer->chip->shutdown)
-                       err = -ENODEV;
-               else
-                       err = snd_usb_ctl_msg(chip->dev,
-                               usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
-                               USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
-                               validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
-                               value, val_len);
-               up_read(&mixer->chip->shutdown_rwsem);
-               if (err < 0)
-                       return err;
-
-               pval->cached_value = new_val;
-               pval->is_cached = 1;
-               changed = 1;
-       }
-
-       return changed;
-}
-
-static void kctl_private_value_free(struct snd_kcontrol *kctl)
-{
-       kfree((void *)kctl->private_value);
+       kctl->private_value &= ~(0xff << 24);
+       kctl->private_value |= new_val << 24;
+       err = snd_ftu_eff_switch_update(list);
+       return err < 0 ? err : 1;
 }
 
 static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
@@ -902,33 +988,16 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
                .get = snd_ftu_eff_switch_get,
                .put = snd_ftu_eff_switch_put
        };
-
+       struct usb_mixer_elem_list *list;
        int err;
-       struct snd_kcontrol *kctl;
-       struct snd_ftu_eff_switch_priv_val *pval;
-
-       pval = kzalloc(sizeof(*pval), GFP_KERNEL);
-       if (!pval)
-               return -ENOMEM;
-
-       pval->cached_value = 0;
-       pval->is_cached = 0;
-       pval->mixer = mixer;
-       pval->bUnitID = bUnitID;
-       pval->validx = validx;
-
-       template.private_value = (unsigned long) pval;
-       kctl = snd_ctl_new1(&template, mixer->chip);
-       if (!kctl) {
-               kfree(pval);
-               return -ENOMEM;
-       }
 
-       kctl->private_free = kctl_private_value_free;
-       err = snd_ctl_add(mixer->chip->card, kctl);
+       err = add_single_ctl_with_resume(mixer, bUnitID,
+                                        snd_ftu_eff_switch_update,
+                                        &template, &list);
        if (err < 0)
                return err;
-
+       list->kctl->private_value = (validx << 8) | bUnitID;
+       snd_ftu_eff_switch_init(mixer, list->kctl);
        return 0;
 }
 
@@ -1110,7 +1179,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
        int unitid = 12; /* SamleRate ExtensionUnit ID */
 
        list_for_each_entry(mixer, &chip->mixer_list, list) {
-               cval = mixer->id_elems[unitid];
+               cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid];
                if (cval) {
                        snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
                                                    cval->control << 8,
@@ -1440,7 +1509,8 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
 static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+       struct snd_usb_audio *chip = list->mixer->chip;
        int err;
        struct usb_interface *iface;
        struct usb_host_interface *alts;
@@ -1448,17 +1518,23 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
        unsigned char data[3];
        int rate;
 
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown) {
+               err = -ENODEV;
+               goto end;
+       }
+
        ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
        ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
        ucontrol->value.iec958.status[2] = 0x00;
 
        /* use known values for that card: interface#1 altsetting#1 */
-       iface = usb_ifnum_to_if(mixer->chip->dev, 1);
+       iface = usb_ifnum_to_if(chip->dev, 1);
        alts = &iface->altsetting[1];
        ep = get_endpoint(alts, 0)->bEndpointAddress;
 
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                       usb_rcvctrlpipe(mixer->chip->dev, 0),
+       err = snd_usb_ctl_msg(chip->dev,
+                       usb_rcvctrlpipe(chip->dev, 0),
                        UAC_GET_CUR,
                        USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
                        UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
@@ -1473,22 +1549,27 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
                        IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
 
        err = 0;
-end:
+ end:
+       up_read(&chip->shutdown_rwsem);
        return err;
 }
 
-static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int err;
+       struct snd_usb_audio *chip = list->mixer->chip;
+       unsigned int pval = list->kctl->private_value;
        u8 reg;
-       unsigned long priv_backup = kcontrol->private_value;
+       int err;
 
-       reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) |
-                       (ucontrol->value.iec958.status[0] & 0x0f);
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                       usb_sndctrlpipe(mixer->chip->dev, 0),
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown) {
+               err = -ENODEV;
+               goto end;
+       }
+
+       reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
+       err = snd_usb_ctl_msg(chip->dev,
+                       usb_sndctrlpipe(chip->dev, 0),
                        UAC_SET_CUR,
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                        reg,
@@ -1498,15 +1579,10 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
        if (err < 0)
                goto end;
 
-       kcontrol->private_value &= 0xfffff0f0;
-       kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
-       kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f);
-
-       reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ?
-                       0xa0 : 0x20;
-       reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f;
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                       usb_sndctrlpipe(mixer->chip->dev, 0),
+       reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
+       reg |= (pval >> 12) & 0x0f;
+       err = snd_usb_ctl_msg(chip->dev,
+                       usb_sndctrlpipe(chip->dev, 0),
                        UAC_SET_CUR,
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                        reg,
@@ -1516,16 +1592,36 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
        if (err < 0)
                goto end;
 
-       kcontrol->private_value &= 0xffff0fff;
-       kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
+ end:
+       up_read(&chip->shutdown_rwsem);
+       return err;
+}
+
+static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+       unsigned int pval, pval_old;
+       int err;
+
+       pval = pval_old = kcontrol->private_value;
+       pval &= 0xfffff0f0;
+       pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
+       pval |= (ucontrol->value.iec958.status[0] & 0x0f);
+
+       pval &= 0xffff0fff;
+       pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
 
        /* The frequency bits in AES3 cannot be set via register access. */
 
        /* Silently ignore any bits from the request that cannot be set. */
 
-       err = (priv_backup != kcontrol->private_value);
-end:
-       return err;
+       if (pval == pval_old)
+               return 0;
+
+       kcontrol->private_value = pval;
+       err = snd_microii_spdif_default_update(list);
+       return err < 0 ? err : 1;
 }
 
 static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
@@ -1547,15 +1643,20 @@ static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
 {
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       struct snd_usb_audio *chip = list->mixer->chip;
+       u8 reg = list->kctl->private_value;
        int err;
-       u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
 
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                       usb_sndctrlpipe(mixer->chip->dev, 0),
+       down_read(&chip->shutdown_rwsem);
+       if (chip->shutdown) {
+               err = -ENODEV;
+               goto end;
+       }
+
+       err = snd_usb_ctl_msg(chip->dev,
+                       usb_sndctrlpipe(chip->dev, 0),
                        UAC_SET_CUR,
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
                        reg,
@@ -1563,15 +1664,27 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
                        NULL,
                        0);
 
-       if (!err) {
-               err = (reg != (kcontrol->private_value & 0x0ff));
-               if (err)
-                       kcontrol->private_value = reg;
-       }
-
+ end:
+       up_read(&chip->shutdown_rwsem);
        return err;
 }
 
+static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
+       u8 reg;
+       int err;
+
+       reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
+       if (reg != list->kctl->private_value)
+               return 0;
+
+       kcontrol->private_value = reg;
+       err = snd_microii_spdif_switch_update(list);
+       return err < 0 ? err : 1;
+}
+
 static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
        {
                .iface =    SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1601,10 +1714,17 @@ static struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
 static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
 {
        int err, i;
+       static usb_mixer_elem_resume_func_t resume_funcs[] = {
+               snd_microii_spdif_default_update,
+               NULL,
+               snd_microii_spdif_switch_update
+       };
 
        for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
-               err = snd_ctl_add(mixer->chip->card,
-                       snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer));
+               err = add_single_ctl_with_resume(mixer, 0,
+                                                resume_funcs[i],
+                                                &snd_microii_mixer_spdif[i],
+                                                NULL);
                if (err < 0)
                        return err;
        }
@@ -1661,6 +1781,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
                err = snd_microii_controls_create(mixer);
                break;
 
+       case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */
+               err = snd_mbox1_create_sync_switch(mixer);
+               break;
+
        case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
                err = snd_nativeinstruments_create_mixer(mixer,
                                snd_nativeinstruments_ta6_mixers,
@@ -1677,6 +1801,14 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
                /* detection is disabled in mixer_maps.c */
                err = snd_create_std_mono_table(mixer, ebox44_table);
                break;
+
+       case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */
+       case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */
+       case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */
+       case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */
+       case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */
+               err = snd_scarlett_controls_create(mixer);
+               break;
        }
 
        return err;