xref: /openbmc/linux/sound/usb/mixer_quirks.c (revision 4be47798)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27b1eda22SDaniel Mack /*
37b1eda22SDaniel Mack  *   USB Audio Driver for ALSA
47b1eda22SDaniel Mack  *
57b1eda22SDaniel Mack  *   Quirks and vendor-specific extensions for mixer interfaces
67b1eda22SDaniel Mack  *
77b1eda22SDaniel Mack  *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
87b1eda22SDaniel Mack  *
97b1eda22SDaniel Mack  *   Many codes borrowed from audio.c by
107b1eda22SDaniel Mack  *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
117b1eda22SDaniel Mack  *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
127b1eda22SDaniel Mack  *
13066624c6SPrzemek Rudy  *   Audio Advantage Micro II support added by:
14066624c6SPrzemek Rudy  *	    Przemek Rudy (prudy1@o2.pl)
157b1eda22SDaniel Mack  */
167b1eda22SDaniel Mack 
17388fdb8fSIan Douglas Scott #include <linux/hid.h>
187b1eda22SDaniel Mack #include <linux/init.h>
19d39f1d68SJussi Laako #include <linux/math64.h>
2036db0456SStephen Rothwell #include <linux/slab.h>
217b1eda22SDaniel Mack #include <linux/usb.h>
227b1eda22SDaniel Mack #include <linux/usb/audio.h>
237b1eda22SDaniel Mack 
24066624c6SPrzemek Rudy #include <sound/asoundef.h>
257b1eda22SDaniel Mack #include <sound/core.h>
267b1eda22SDaniel Mack #include <sound/control.h>
277b1eda22SDaniel Mack #include <sound/hwdep.h>
287b1eda22SDaniel Mack #include <sound/info.h>
2942e3121dSAnssi Hannula #include <sound/tlv.h>
307b1eda22SDaniel Mack 
317b1eda22SDaniel Mack #include "usbaudio.h"
32f0b5e634SDaniel Mack #include "mixer.h"
337b1eda22SDaniel Mack #include "mixer_quirks.h"
3476b188c4SChris J Arges #include "mixer_scarlett.h"
359e4d5c1bSGeoffrey D. Bennett #include "mixer_scarlett_gen2.h"
36d2bb390aSDetlef Urban #include "mixer_us16x08.h"
378dc5efe3SNick Kossifidis #include "mixer_s1810c.h"
387b1eda22SDaniel Mack #include "helper.h"
397b1eda22SDaniel Mack 
40b71dad18SMark Hills struct std_mono_table {
41b71dad18SMark Hills 	unsigned int unitid, control, cmask;
42b71dad18SMark Hills 	int val_type;
43b71dad18SMark Hills 	const char *name;
44b71dad18SMark Hills 	snd_kcontrol_tlv_rw_t *tlv_callback;
45b71dad18SMark Hills };
46b71dad18SMark Hills 
478a4d1d39SFelix Homann /* This function allows for the creation of standard UAC controls.
488a4d1d39SFelix Homann  * See the quirks for M-Audio FTUs or Ebox-44.
498a4d1d39SFelix Homann  * If you don't want to set a TLV callback pass NULL.
508a4d1d39SFelix Homann  *
518a4d1d39SFelix Homann  * Since there doesn't seem to be a devices that needs a multichannel
528a4d1d39SFelix Homann  * version, we keep it mono for simplicity.
538a4d1d39SFelix Homann  */
549f814105SEldad Zack static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
558a4d1d39SFelix Homann 				unsigned int unitid,
568a4d1d39SFelix Homann 				unsigned int control,
578a4d1d39SFelix Homann 				unsigned int cmask,
588a4d1d39SFelix Homann 				int val_type,
599f814105SEldad Zack 				unsigned int idx_off,
608a4d1d39SFelix Homann 				const char *name,
618a4d1d39SFelix Homann 				snd_kcontrol_tlv_rw_t *tlv_callback)
628a4d1d39SFelix Homann {
638a4d1d39SFelix Homann 	struct usb_mixer_elem_info *cval;
648a4d1d39SFelix Homann 	struct snd_kcontrol *kctl;
658a4d1d39SFelix Homann 
668a4d1d39SFelix Homann 	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
678a4d1d39SFelix Homann 	if (!cval)
688a4d1d39SFelix Homann 		return -ENOMEM;
698a4d1d39SFelix Homann 
703360b84bSTakashi Iwai 	snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
718a4d1d39SFelix Homann 	cval->val_type = val_type;
728a4d1d39SFelix Homann 	cval->channels = 1;
738a4d1d39SFelix Homann 	cval->control = control;
748a4d1d39SFelix Homann 	cval->cmask = cmask;
759f814105SEldad Zack 	cval->idx_off = idx_off;
768a4d1d39SFelix Homann 
777df4a691SMark Hills 	/* get_min_max() is called only for integer volumes later,
787df4a691SMark Hills 	 * so provide a short-cut for booleans */
798a4d1d39SFelix Homann 	cval->min = 0;
808a4d1d39SFelix Homann 	cval->max = 1;
818a4d1d39SFelix Homann 	cval->res = 0;
828a4d1d39SFelix Homann 	cval->dBmin = 0;
838a4d1d39SFelix Homann 	cval->dBmax = 0;
848a4d1d39SFelix Homann 
858a4d1d39SFelix Homann 	/* Create control */
868a4d1d39SFelix Homann 	kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
878a4d1d39SFelix Homann 	if (!kctl) {
888a4d1d39SFelix Homann 		kfree(cval);
898a4d1d39SFelix Homann 		return -ENOMEM;
908a4d1d39SFelix Homann 	}
918a4d1d39SFelix Homann 
928a4d1d39SFelix Homann 	/* Set name */
938a4d1d39SFelix Homann 	snprintf(kctl->id.name, sizeof(kctl->id.name), name);
94eef90451SChris J Arges 	kctl->private_free = snd_usb_mixer_elem_free;
958a4d1d39SFelix Homann 
968a4d1d39SFelix Homann 	/* set TLV */
978a4d1d39SFelix Homann 	if (tlv_callback) {
988a4d1d39SFelix Homann 		kctl->tlv.c = tlv_callback;
998a4d1d39SFelix Homann 		kctl->vd[0].access |=
1008a4d1d39SFelix Homann 			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
1018a4d1d39SFelix Homann 			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1028a4d1d39SFelix Homann 	}
1038a4d1d39SFelix Homann 	/* Add control to mixer */
1043360b84bSTakashi Iwai 	return snd_usb_mixer_add_control(&cval->head, kctl);
1058a4d1d39SFelix Homann }
1068a4d1d39SFelix Homann 
1079f814105SEldad Zack static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
1089f814105SEldad Zack 				unsigned int unitid,
1099f814105SEldad Zack 				unsigned int control,
1109f814105SEldad Zack 				unsigned int cmask,
1119f814105SEldad Zack 				int val_type,
1129f814105SEldad Zack 				const char *name,
1139f814105SEldad Zack 				snd_kcontrol_tlv_rw_t *tlv_callback)
1149f814105SEldad Zack {
1159f814105SEldad Zack 	return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask,
1169f814105SEldad Zack 		val_type, 0 /* Offset */, name, tlv_callback);
1179f814105SEldad Zack }
1189f814105SEldad Zack 
1197b1eda22SDaniel Mack /*
120b71dad18SMark Hills  * Create a set of standard UAC controls from a table
121b71dad18SMark Hills  */
122b71dad18SMark Hills static int snd_create_std_mono_table(struct usb_mixer_interface *mixer,
123a01df925STakashi Iwai 				     const struct std_mono_table *t)
124b71dad18SMark Hills {
125b71dad18SMark Hills 	int err;
126b71dad18SMark Hills 
127b71dad18SMark Hills 	while (t->name != NULL) {
128b71dad18SMark Hills 		err = snd_create_std_mono_ctl(mixer, t->unitid, t->control,
129b71dad18SMark Hills 				t->cmask, t->val_type, t->name, t->tlv_callback);
130b71dad18SMark Hills 		if (err < 0)
131b71dad18SMark Hills 			return err;
132b71dad18SMark Hills 		t++;
133b71dad18SMark Hills 	}
134b71dad18SMark Hills 
135b71dad18SMark Hills 	return 0;
136b71dad18SMark Hills }
137b71dad18SMark Hills 
1389cf3689bSTakashi Iwai static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer,
1399cf3689bSTakashi Iwai 				      int id,
1409cf3689bSTakashi Iwai 				      usb_mixer_elem_resume_func_t resume,
1419cf3689bSTakashi Iwai 				      const struct snd_kcontrol_new *knew,
1429cf3689bSTakashi Iwai 				      struct usb_mixer_elem_list **listp)
1439cf3689bSTakashi Iwai {
1449cf3689bSTakashi Iwai 	struct usb_mixer_elem_list *list;
1459cf3689bSTakashi Iwai 	struct snd_kcontrol *kctl;
1469cf3689bSTakashi Iwai 
1479cf3689bSTakashi Iwai 	list = kzalloc(sizeof(*list), GFP_KERNEL);
1489cf3689bSTakashi Iwai 	if (!list)
1499cf3689bSTakashi Iwai 		return -ENOMEM;
1509cf3689bSTakashi Iwai 	if (listp)
1519cf3689bSTakashi Iwai 		*listp = list;
1529cf3689bSTakashi Iwai 	list->mixer = mixer;
1539cf3689bSTakashi Iwai 	list->id = id;
15444609fc0SKai-Heng Feng 	list->reset_resume = resume;
1559cf3689bSTakashi Iwai 	kctl = snd_ctl_new1(knew, list);
1569cf3689bSTakashi Iwai 	if (!kctl) {
1579cf3689bSTakashi Iwai 		kfree(list);
1589cf3689bSTakashi Iwai 		return -ENOMEM;
1599cf3689bSTakashi Iwai 	}
1609cf3689bSTakashi Iwai 	kctl->private_free = snd_usb_mixer_elem_free;
161220345e9STakashi Iwai 	/* don't use snd_usb_mixer_add_control() here, this is a special list element */
162220345e9STakashi Iwai 	return snd_usb_mixer_add_list(list, kctl, false);
1639cf3689bSTakashi Iwai }
1649cf3689bSTakashi Iwai 
165b71dad18SMark Hills /*
1667b1eda22SDaniel Mack  * Sound Blaster remote control configuration
1677b1eda22SDaniel Mack  *
1687b1eda22SDaniel Mack  * format of remote control data:
1697b1eda22SDaniel Mack  * Extigy:       xx 00
1707b1eda22SDaniel Mack  * Audigy 2 NX:  06 80 xx 00 00 00
1717b1eda22SDaniel Mack  * Live! 24-bit: 06 80 xx yy 22 83
1727b1eda22SDaniel Mack  */
1737b1eda22SDaniel Mack static const struct rc_config {
1747b1eda22SDaniel Mack 	u32 usb_id;
1757b1eda22SDaniel Mack 	u8  offset;
1767b1eda22SDaniel Mack 	u8  length;
1777b1eda22SDaniel Mack 	u8  packet_length;
1787b1eda22SDaniel Mack 	u8  min_packet_length; /* minimum accepted length of the URB result */
1797b1eda22SDaniel Mack 	u8  mute_mixer_id;
1807b1eda22SDaniel Mack 	u32 mute_code;
1817b1eda22SDaniel Mack } rc_configs[] = {
1827b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
1837b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
1847b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
185ca8dc34eSMandar Joshi 	{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 */
1867cdd8d73SMathieu Bouffard 	{ USB_ID(0x041e, 0x30df), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 Pro */
1873dc8523fSDmitry M. Fedin 	{ USB_ID(0x041e, 0x3237), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 Pro */
188fec90088SMirko Dietrich 	{ USB_ID(0x041e, 0x3263), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 Pro */
1897b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
1907b1eda22SDaniel Mack };
1917b1eda22SDaniel Mack 
1927b1eda22SDaniel Mack static void snd_usb_soundblaster_remote_complete(struct urb *urb)
1937b1eda22SDaniel Mack {
1947b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = urb->context;
1957b1eda22SDaniel Mack 	const struct rc_config *rc = mixer->rc_cfg;
1967b1eda22SDaniel Mack 	u32 code;
1977b1eda22SDaniel Mack 
1987b1eda22SDaniel Mack 	if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
1997b1eda22SDaniel Mack 		return;
2007b1eda22SDaniel Mack 
2017b1eda22SDaniel Mack 	code = mixer->rc_buffer[rc->offset];
2027b1eda22SDaniel Mack 	if (rc->length == 2)
2037b1eda22SDaniel Mack 		code |= mixer->rc_buffer[rc->offset + 1] << 8;
2047b1eda22SDaniel Mack 
2057b1eda22SDaniel Mack 	/* the Mute button actually changes the mixer control */
2067b1eda22SDaniel Mack 	if (code == rc->mute_code)
2077b1eda22SDaniel Mack 		snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
2087b1eda22SDaniel Mack 	mixer->rc_code = code;
2097b1eda22SDaniel Mack 	wmb();
2107b1eda22SDaniel Mack 	wake_up(&mixer->rc_waitq);
2117b1eda22SDaniel Mack }
2127b1eda22SDaniel Mack 
2137b1eda22SDaniel Mack static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
2147b1eda22SDaniel Mack 				     long count, loff_t *offset)
2157b1eda22SDaniel Mack {
2167b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = hw->private_data;
2177b1eda22SDaniel Mack 	int err;
2187b1eda22SDaniel Mack 	u32 rc_code;
2197b1eda22SDaniel Mack 
2207b1eda22SDaniel Mack 	if (count != 1 && count != 4)
2217b1eda22SDaniel Mack 		return -EINVAL;
2227b1eda22SDaniel Mack 	err = wait_event_interruptible(mixer->rc_waitq,
2237b1eda22SDaniel Mack 				       (rc_code = xchg(&mixer->rc_code, 0)) != 0);
2247b1eda22SDaniel Mack 	if (err == 0) {
2257b1eda22SDaniel Mack 		if (count == 1)
2267b1eda22SDaniel Mack 			err = put_user(rc_code, buf);
2277b1eda22SDaniel Mack 		else
2287b1eda22SDaniel Mack 			err = put_user(rc_code, (u32 __user *)buf);
2297b1eda22SDaniel Mack 	}
2307b1eda22SDaniel Mack 	return err < 0 ? err : count;
2317b1eda22SDaniel Mack }
2327b1eda22SDaniel Mack 
233680ef72aSAl Viro static __poll_t snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
2347b1eda22SDaniel Mack 					    poll_table *wait)
2357b1eda22SDaniel Mack {
2367b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = hw->private_data;
2377b1eda22SDaniel Mack 
2387b1eda22SDaniel Mack 	poll_wait(file, &mixer->rc_waitq, wait);
239a9a08845SLinus Torvalds 	return mixer->rc_code ? EPOLLIN | EPOLLRDNORM : 0;
2407b1eda22SDaniel Mack }
2417b1eda22SDaniel Mack 
2427b1eda22SDaniel Mack static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
2437b1eda22SDaniel Mack {
2447b1eda22SDaniel Mack 	struct snd_hwdep *hwdep;
2457b1eda22SDaniel Mack 	int err, len, i;
2467b1eda22SDaniel Mack 
2477b1eda22SDaniel Mack 	for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
2487b1eda22SDaniel Mack 		if (rc_configs[i].usb_id == mixer->chip->usb_id)
2497b1eda22SDaniel Mack 			break;
2507b1eda22SDaniel Mack 	if (i >= ARRAY_SIZE(rc_configs))
2517b1eda22SDaniel Mack 		return 0;
2527b1eda22SDaniel Mack 	mixer->rc_cfg = &rc_configs[i];
2537b1eda22SDaniel Mack 
2547b1eda22SDaniel Mack 	len = mixer->rc_cfg->packet_length;
2557b1eda22SDaniel Mack 
2567b1eda22SDaniel Mack 	init_waitqueue_head(&mixer->rc_waitq);
2577b1eda22SDaniel Mack 	err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
2587b1eda22SDaniel Mack 	if (err < 0)
2597b1eda22SDaniel Mack 		return err;
2607b1eda22SDaniel Mack 	snprintf(hwdep->name, sizeof(hwdep->name),
2617b1eda22SDaniel Mack 		 "%s remote control", mixer->chip->card->shortname);
2627b1eda22SDaniel Mack 	hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
2637b1eda22SDaniel Mack 	hwdep->private_data = mixer;
2647b1eda22SDaniel Mack 	hwdep->ops.read = snd_usb_sbrc_hwdep_read;
2657b1eda22SDaniel Mack 	hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
2667b1eda22SDaniel Mack 	hwdep->exclusive = 1;
2677b1eda22SDaniel Mack 
2687b1eda22SDaniel Mack 	mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
2697b1eda22SDaniel Mack 	if (!mixer->rc_urb)
2707b1eda22SDaniel Mack 		return -ENOMEM;
2717b1eda22SDaniel Mack 	mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
2727b1eda22SDaniel Mack 	if (!mixer->rc_setup_packet) {
2737b1eda22SDaniel Mack 		usb_free_urb(mixer->rc_urb);
2747b1eda22SDaniel Mack 		mixer->rc_urb = NULL;
2757b1eda22SDaniel Mack 		return -ENOMEM;
2767b1eda22SDaniel Mack 	}
2777b1eda22SDaniel Mack 	mixer->rc_setup_packet->bRequestType =
2787b1eda22SDaniel Mack 		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
2797b1eda22SDaniel Mack 	mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
2807b1eda22SDaniel Mack 	mixer->rc_setup_packet->wValue = cpu_to_le16(0);
2817b1eda22SDaniel Mack 	mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
2827b1eda22SDaniel Mack 	mixer->rc_setup_packet->wLength = cpu_to_le16(len);
2837b1eda22SDaniel Mack 	usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
2847b1eda22SDaniel Mack 			     usb_rcvctrlpipe(mixer->chip->dev, 0),
2857b1eda22SDaniel Mack 			     (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
2867b1eda22SDaniel Mack 			     snd_usb_soundblaster_remote_complete, mixer);
2877b1eda22SDaniel Mack 	return 0;
2887b1eda22SDaniel Mack }
2897b1eda22SDaniel Mack 
2907b1eda22SDaniel Mack #define snd_audigy2nx_led_info		snd_ctl_boolean_mono_info
2917b1eda22SDaniel Mack 
2927b1eda22SDaniel Mack static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2937b1eda22SDaniel Mack {
2949cf3689bSTakashi Iwai 	ucontrol->value.integer.value[0] = kcontrol->private_value >> 8;
2957b1eda22SDaniel Mack 	return 0;
2967b1eda22SDaniel Mack }
2977b1eda22SDaniel Mack 
2989cf3689bSTakashi Iwai static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
2999cf3689bSTakashi Iwai 				    int value, int index)
3007b1eda22SDaniel Mack {
3019cf3689bSTakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
3029cf3689bSTakashi Iwai 	int err;
3037b1eda22SDaniel Mack 
30447ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
30547ab1545STakashi Iwai 	if (err < 0)
30647ab1545STakashi Iwai 		return err;
30747ab1545STakashi Iwai 
3089cf3689bSTakashi Iwai 	if (chip->usb_id == USB_ID(0x041e, 0x3042))
3099cf3689bSTakashi Iwai 		err = snd_usb_ctl_msg(chip->dev,
3109cf3689bSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x24,
311ca8dc34eSMandar Joshi 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
31217d900c4SClemens Ladisch 			      !value, 0, NULL, 0);
3137cdd8d73SMathieu Bouffard 	/* USB X-Fi S51 Pro */
3149cf3689bSTakashi Iwai 	if (chip->usb_id == USB_ID(0x041e, 0x30df))
3159cf3689bSTakashi Iwai 		err = snd_usb_ctl_msg(chip->dev,
3169cf3689bSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x24,
3177cdd8d73SMathieu Bouffard 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
31817d900c4SClemens Ladisch 			      !value, 0, NULL, 0);
319ca8dc34eSMandar Joshi 	else
3209cf3689bSTakashi Iwai 		err = snd_usb_ctl_msg(chip->dev,
3219cf3689bSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x24,
3227b1eda22SDaniel Mack 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
32317d900c4SClemens Ladisch 			      value, index + 2, NULL, 0);
32447ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
3257b1eda22SDaniel Mack 	return err;
3267b1eda22SDaniel Mack }
3277b1eda22SDaniel Mack 
3289cf3689bSTakashi Iwai static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
3299cf3689bSTakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3307b1eda22SDaniel Mack {
3319cf3689bSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
3329cf3689bSTakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
3339cf3689bSTakashi Iwai 	int index = kcontrol->private_value & 0xff;
334e87359efSDan Carpenter 	unsigned int value = ucontrol->value.integer.value[0];
3359cf3689bSTakashi Iwai 	int old_value = kcontrol->private_value >> 8;
3369cf3689bSTakashi Iwai 	int err;
3379cf3689bSTakashi Iwai 
3389cf3689bSTakashi Iwai 	if (value > 1)
3399cf3689bSTakashi Iwai 		return -EINVAL;
3409cf3689bSTakashi Iwai 	if (value == old_value)
3419cf3689bSTakashi Iwai 		return 0;
3429cf3689bSTakashi Iwai 	kcontrol->private_value = (value << 8) | index;
3439cf3689bSTakashi Iwai 	err = snd_audigy2nx_led_update(mixer, value, index);
3449cf3689bSTakashi Iwai 	return err < 0 ? err : 1;
3459cf3689bSTakashi Iwai }
3469cf3689bSTakashi Iwai 
3479cf3689bSTakashi Iwai static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
3489cf3689bSTakashi Iwai {
3499cf3689bSTakashi Iwai 	int priv_value = list->kctl->private_value;
3509cf3689bSTakashi Iwai 
3519cf3689bSTakashi Iwai 	return snd_audigy2nx_led_update(list->mixer, priv_value >> 8,
3529cf3689bSTakashi Iwai 					priv_value & 0xff);
3539cf3689bSTakashi Iwai }
3549cf3689bSTakashi Iwai 
3559cf3689bSTakashi Iwai /* name and private_value are set dynamically */
356905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_audigy2nx_control = {
3577b1eda22SDaniel Mack 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3587b1eda22SDaniel Mack 	.info = snd_audigy2nx_led_info,
3597b1eda22SDaniel Mack 	.get = snd_audigy2nx_led_get,
3607b1eda22SDaniel Mack 	.put = snd_audigy2nx_led_put,
3619cf3689bSTakashi Iwai };
3629cf3689bSTakashi Iwai 
3639cf3689bSTakashi Iwai static const char * const snd_audigy2nx_led_names[] = {
3649cf3689bSTakashi Iwai 	"CMSS LED Switch",
3659cf3689bSTakashi Iwai 	"Power LED Switch",
3669cf3689bSTakashi Iwai 	"Dolby Digital LED Switch",
3677b1eda22SDaniel Mack };
3687b1eda22SDaniel Mack 
3697b1eda22SDaniel Mack static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
3707b1eda22SDaniel Mack {
3717b1eda22SDaniel Mack 	int i, err;
3727b1eda22SDaniel Mack 
3739cf3689bSTakashi Iwai 	for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) {
3749cf3689bSTakashi Iwai 		struct snd_kcontrol_new knew;
3759cf3689bSTakashi Iwai 
376ca8dc34eSMandar Joshi 		/* USB X-Fi S51 doesn't have a CMSS LED */
377ca8dc34eSMandar Joshi 		if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
378ca8dc34eSMandar Joshi 			continue;
3797cdd8d73SMathieu Bouffard 		/* USB X-Fi S51 Pro doesn't have one either */
3807cdd8d73SMathieu Bouffard 		if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
3817cdd8d73SMathieu Bouffard 			continue;
3827b1eda22SDaniel Mack 		if (i > 1 && /* Live24ext has 2 LEDs only */
3837b1eda22SDaniel Mack 			(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
384ca8dc34eSMandar Joshi 			 mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
3857cdd8d73SMathieu Bouffard 			 mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
3867b1eda22SDaniel Mack 			 mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
3877b1eda22SDaniel Mack 			break;
3889cf3689bSTakashi Iwai 
3899cf3689bSTakashi Iwai 		knew = snd_audigy2nx_control;
3909cf3689bSTakashi Iwai 		knew.name = snd_audigy2nx_led_names[i];
3919cf3689bSTakashi Iwai 		knew.private_value = (1 << 8) | i; /* LED on as default */
3929cf3689bSTakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
3939cf3689bSTakashi Iwai 						 snd_audigy2nx_led_resume,
3949cf3689bSTakashi Iwai 						 &knew, NULL);
3957b1eda22SDaniel Mack 		if (err < 0)
3967b1eda22SDaniel Mack 			return err;
3977b1eda22SDaniel Mack 	}
3987b1eda22SDaniel Mack 	return 0;
3997b1eda22SDaniel Mack }
4007b1eda22SDaniel Mack 
4017b1eda22SDaniel Mack static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
4027b1eda22SDaniel Mack 				    struct snd_info_buffer *buffer)
4037b1eda22SDaniel Mack {
4047b1eda22SDaniel Mack 	static const struct sb_jack {
4057b1eda22SDaniel Mack 		int unitid;
4067b1eda22SDaniel Mack 		const char *name;
4077b1eda22SDaniel Mack 	}  jacks_audigy2nx[] = {
4087b1eda22SDaniel Mack 		{4,  "dig in "},
4097b1eda22SDaniel Mack 		{7,  "line in"},
4107b1eda22SDaniel Mack 		{19, "spk out"},
4117b1eda22SDaniel Mack 		{20, "hph out"},
4127b1eda22SDaniel Mack 		{-1, NULL}
4137b1eda22SDaniel Mack 	}, jacks_live24ext[] = {
4147b1eda22SDaniel Mack 		{4,  "line in"}, /* &1=Line, &2=Mic*/
4157b1eda22SDaniel Mack 		{3,  "hph out"}, /* headphones */
4167b1eda22SDaniel Mack 		{0,  "RC     "}, /* last command, 6 bytes see rc_config above */
4177b1eda22SDaniel Mack 		{-1, NULL}
4187b1eda22SDaniel Mack 	};
4197b1eda22SDaniel Mack 	const struct sb_jack *jacks;
4207b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = entry->private_data;
4217b1eda22SDaniel Mack 	int i, err;
4227b1eda22SDaniel Mack 	u8 buf[3];
4237b1eda22SDaniel Mack 
4247b1eda22SDaniel Mack 	snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
4257b1eda22SDaniel Mack 	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
4267b1eda22SDaniel Mack 		jacks = jacks_audigy2nx;
4277b1eda22SDaniel Mack 	else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
4287b1eda22SDaniel Mack 		 mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
4297b1eda22SDaniel Mack 		jacks = jacks_live24ext;
4307b1eda22SDaniel Mack 	else
4317b1eda22SDaniel Mack 		return;
4327b1eda22SDaniel Mack 
4337b1eda22SDaniel Mack 	for (i = 0; jacks[i].name; ++i) {
4347b1eda22SDaniel Mack 		snd_iprintf(buffer, "%s: ", jacks[i].name);
43547ab1545STakashi Iwai 		err = snd_usb_lock_shutdown(mixer->chip);
43647ab1545STakashi Iwai 		if (err < 0)
43747ab1545STakashi Iwai 			return;
4387b1eda22SDaniel Mack 		err = snd_usb_ctl_msg(mixer->chip->dev,
4397b1eda22SDaniel Mack 				      usb_rcvctrlpipe(mixer->chip->dev, 0),
4407b1eda22SDaniel Mack 				      UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
4417b1eda22SDaniel Mack 				      USB_RECIP_INTERFACE, 0,
44217d900c4SClemens Ladisch 				      jacks[i].unitid << 8, buf, 3);
44347ab1545STakashi Iwai 		snd_usb_unlock_shutdown(mixer->chip);
4447b1eda22SDaniel Mack 		if (err == 3 && (buf[0] == 3 || buf[0] == 6))
4457b1eda22SDaniel Mack 			snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
4467b1eda22SDaniel Mack 		else
4477b1eda22SDaniel Mack 			snd_iprintf(buffer, "?\n");
4487b1eda22SDaniel Mack 	}
4497b1eda22SDaniel Mack }
4507b1eda22SDaniel Mack 
45144832a71SVasily Khoruzhick /* EMU0204 */
45244832a71SVasily Khoruzhick static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol,
45344832a71SVasily Khoruzhick 				      struct snd_ctl_elem_info *uinfo)
45444832a71SVasily Khoruzhick {
4557bbd03e0STakashi Iwai 	static const char * const texts[2] = {"1/2", "3/4"};
45644832a71SVasily Khoruzhick 
4577bbd03e0STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
45844832a71SVasily Khoruzhick }
45944832a71SVasily Khoruzhick 
46044832a71SVasily Khoruzhick static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
46144832a71SVasily Khoruzhick 				     struct snd_ctl_elem_value *ucontrol)
46244832a71SVasily Khoruzhick {
46344832a71SVasily Khoruzhick 	ucontrol->value.enumerated.item[0] = kcontrol->private_value;
46444832a71SVasily Khoruzhick 	return 0;
46544832a71SVasily Khoruzhick }
46644832a71SVasily Khoruzhick 
4675f503ee9STakashi Iwai static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
4685f503ee9STakashi Iwai 					int value)
46944832a71SVasily Khoruzhick {
4705f503ee9STakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
4715f503ee9STakashi Iwai 	int err;
47244832a71SVasily Khoruzhick 	unsigned char buf[2];
47344832a71SVasily Khoruzhick 
47447ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
47547ab1545STakashi Iwai 	if (err < 0)
47647ab1545STakashi Iwai 		return err;
4775f503ee9STakashi Iwai 
4785f503ee9STakashi Iwai 	buf[0] = 0x01;
4795f503ee9STakashi Iwai 	buf[1] = value ? 0x02 : 0x01;
4805f503ee9STakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
4815f503ee9STakashi Iwai 		      usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
48244832a71SVasily Khoruzhick 		      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
48344832a71SVasily Khoruzhick 		      0x0400, 0x0e00, buf, 2);
48447ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
48544832a71SVasily Khoruzhick 	return err;
48644832a71SVasily Khoruzhick }
48744832a71SVasily Khoruzhick 
4885f503ee9STakashi Iwai static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
4895f503ee9STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
49044832a71SVasily Khoruzhick {
4915f503ee9STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
4925f503ee9STakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
4935f503ee9STakashi Iwai 	unsigned int value = ucontrol->value.enumerated.item[0];
4945f503ee9STakashi Iwai 	int err;
4955f503ee9STakashi Iwai 
4965f503ee9STakashi Iwai 	if (value > 1)
4975f503ee9STakashi Iwai 		return -EINVAL;
4985f503ee9STakashi Iwai 
4995f503ee9STakashi Iwai 	if (value == kcontrol->private_value)
5005f503ee9STakashi Iwai 		return 0;
5015f503ee9STakashi Iwai 
5025f503ee9STakashi Iwai 	kcontrol->private_value = value;
5035f503ee9STakashi Iwai 	err = snd_emu0204_ch_switch_update(mixer, value);
5045f503ee9STakashi Iwai 	return err < 0 ? err : 1;
5055f503ee9STakashi Iwai }
5065f503ee9STakashi Iwai 
5075f503ee9STakashi Iwai static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
5085f503ee9STakashi Iwai {
5095f503ee9STakashi Iwai 	return snd_emu0204_ch_switch_update(list->mixer,
5105f503ee9STakashi Iwai 					    list->kctl->private_value);
5115f503ee9STakashi Iwai }
5125f503ee9STakashi Iwai 
513195727e8STakashi Iwai static const struct snd_kcontrol_new snd_emu0204_control = {
51444832a71SVasily Khoruzhick 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
51544832a71SVasily Khoruzhick 	.name = "Front Jack Channels",
51644832a71SVasily Khoruzhick 	.info = snd_emu0204_ch_switch_info,
51744832a71SVasily Khoruzhick 	.get = snd_emu0204_ch_switch_get,
51844832a71SVasily Khoruzhick 	.put = snd_emu0204_ch_switch_put,
51944832a71SVasily Khoruzhick 	.private_value = 0,
52044832a71SVasily Khoruzhick };
52144832a71SVasily Khoruzhick 
52244832a71SVasily Khoruzhick static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
52344832a71SVasily Khoruzhick {
5245f503ee9STakashi Iwai 	return add_single_ctl_with_resume(mixer, 0,
5255f503ee9STakashi Iwai 					  snd_emu0204_ch_switch_resume,
5265f503ee9STakashi Iwai 					  &snd_emu0204_control, NULL);
52744832a71SVasily Khoruzhick }
52844832a71SVasily Khoruzhick 
5291d31affbSDenis Washington /* ASUS Xonar U1 / U3 controls */
5301d31affbSDenis Washington 
5317b1eda22SDaniel Mack static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
5327b1eda22SDaniel Mack 				   struct snd_ctl_elem_value *ucontrol)
5337b1eda22SDaniel Mack {
5342bfb14c3STakashi Iwai 	ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02);
5357b1eda22SDaniel Mack 	return 0;
5367b1eda22SDaniel Mack }
5377b1eda22SDaniel Mack 
5382bfb14c3STakashi Iwai static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer,
5392bfb14c3STakashi Iwai 				      unsigned char status)
5402bfb14c3STakashi Iwai {
5412bfb14c3STakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
5422bfb14c3STakashi Iwai 	int err;
5432bfb14c3STakashi Iwai 
54447ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
54547ab1545STakashi Iwai 	if (err < 0)
54647ab1545STakashi Iwai 		return err;
5472bfb14c3STakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
5482bfb14c3STakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x08,
5492bfb14c3STakashi Iwai 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
5502bfb14c3STakashi Iwai 			      50, 0, &status, 1);
55147ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
5522bfb14c3STakashi Iwai 	return err;
5532bfb14c3STakashi Iwai }
5542bfb14c3STakashi Iwai 
5557b1eda22SDaniel Mack static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
5567b1eda22SDaniel Mack 				   struct snd_ctl_elem_value *ucontrol)
5577b1eda22SDaniel Mack {
5582bfb14c3STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
5597b1eda22SDaniel Mack 	u8 old_status, new_status;
5602bfb14c3STakashi Iwai 	int err;
5617b1eda22SDaniel Mack 
5622bfb14c3STakashi Iwai 	old_status = kcontrol->private_value;
5637b1eda22SDaniel Mack 	if (ucontrol->value.integer.value[0])
5647b1eda22SDaniel Mack 		new_status = old_status | 0x02;
5657b1eda22SDaniel Mack 	else
5667b1eda22SDaniel Mack 		new_status = old_status & ~0x02;
5672bfb14c3STakashi Iwai 	if (new_status == old_status)
5682bfb14c3STakashi Iwai 		return 0;
5692bfb14c3STakashi Iwai 
5702bfb14c3STakashi Iwai 	kcontrol->private_value = new_status;
5712bfb14c3STakashi Iwai 	err = snd_xonar_u1_switch_update(list->mixer, new_status);
5722bfb14c3STakashi Iwai 	return err < 0 ? err : 1;
5732bfb14c3STakashi Iwai }
5742bfb14c3STakashi Iwai 
5752bfb14c3STakashi Iwai static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
5762bfb14c3STakashi Iwai {
5772bfb14c3STakashi Iwai 	return snd_xonar_u1_switch_update(list->mixer,
5782bfb14c3STakashi Iwai 					  list->kctl->private_value);
5797b1eda22SDaniel Mack }
5807b1eda22SDaniel Mack 
581195727e8STakashi Iwai static const struct snd_kcontrol_new snd_xonar_u1_output_switch = {
5827b1eda22SDaniel Mack 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5837b1eda22SDaniel Mack 	.name = "Digital Playback Switch",
5847b1eda22SDaniel Mack 	.info = snd_ctl_boolean_mono_info,
5857b1eda22SDaniel Mack 	.get = snd_xonar_u1_switch_get,
5867b1eda22SDaniel Mack 	.put = snd_xonar_u1_switch_put,
5872bfb14c3STakashi Iwai 	.private_value = 0x05,
5887b1eda22SDaniel Mack };
5897b1eda22SDaniel Mack 
5907b1eda22SDaniel Mack static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
5917b1eda22SDaniel Mack {
5922bfb14c3STakashi Iwai 	return add_single_ctl_with_resume(mixer, 0,
5932bfb14c3STakashi Iwai 					  snd_xonar_u1_switch_resume,
5942bfb14c3STakashi Iwai 					  &snd_xonar_u1_output_switch, NULL);
5957b1eda22SDaniel Mack }
5967b1eda22SDaniel Mack 
597d497a82fSDamien Zammit /* Digidesign Mbox 1 clock source switch (internal/spdif) */
598d497a82fSDamien Zammit 
599d497a82fSDamien Zammit static int snd_mbox1_switch_get(struct snd_kcontrol *kctl,
600d497a82fSDamien Zammit 				struct snd_ctl_elem_value *ucontrol)
601d497a82fSDamien Zammit {
602d497a82fSDamien Zammit 	ucontrol->value.enumerated.item[0] = kctl->private_value;
603d497a82fSDamien Zammit 	return 0;
604d497a82fSDamien Zammit }
605d497a82fSDamien Zammit 
60625a9a4f9STakashi Iwai static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val)
607d497a82fSDamien Zammit {
60825a9a4f9STakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
609d497a82fSDamien Zammit 	int err;
610d497a82fSDamien Zammit 	unsigned char buff[3];
611d497a82fSDamien Zammit 
61247ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
61347ab1545STakashi Iwai 	if (err < 0)
61447ab1545STakashi Iwai 		return err;
615d497a82fSDamien Zammit 
616d497a82fSDamien Zammit 	/* Prepare for magic command to toggle clock source */
617d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
618d497a82fSDamien Zammit 				usb_rcvctrlpipe(chip->dev, 0), 0x81,
619d497a82fSDamien Zammit 				USB_DIR_IN |
620d497a82fSDamien Zammit 				USB_TYPE_CLASS |
621d497a82fSDamien Zammit 				USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
622d497a82fSDamien Zammit 	if (err < 0)
623d497a82fSDamien Zammit 		goto err;
624d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
625d497a82fSDamien Zammit 				usb_rcvctrlpipe(chip->dev, 0), 0x81,
626d497a82fSDamien Zammit 				USB_DIR_IN |
627d497a82fSDamien Zammit 				USB_TYPE_CLASS |
628d497a82fSDamien Zammit 				USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
629d497a82fSDamien Zammit 	if (err < 0)
630d497a82fSDamien Zammit 		goto err;
631d497a82fSDamien Zammit 
632d497a82fSDamien Zammit 	/* 2 possibilities:	Internal    -> send sample rate
633d497a82fSDamien Zammit 	 *			S/PDIF sync -> send zeroes
634d497a82fSDamien Zammit 	 * NB: Sample rate locked to 48kHz on purpose to
635d497a82fSDamien Zammit 	 *     prevent user from resetting the sample rate
636d497a82fSDamien Zammit 	 *     while S/PDIF sync is enabled and confusing
637d497a82fSDamien Zammit 	 *     this configuration.
638d497a82fSDamien Zammit 	 */
63925a9a4f9STakashi Iwai 	if (val == 0) {
640d497a82fSDamien Zammit 		buff[0] = 0x80;
641d497a82fSDamien Zammit 		buff[1] = 0xbb;
642d497a82fSDamien Zammit 		buff[2] = 0x00;
643d497a82fSDamien Zammit 	} else {
644d497a82fSDamien Zammit 		buff[0] = buff[1] = buff[2] = 0x00;
645d497a82fSDamien Zammit 	}
646d497a82fSDamien Zammit 
647d497a82fSDamien Zammit 	/* Send the magic command to toggle the clock source */
648d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
649d497a82fSDamien Zammit 				usb_sndctrlpipe(chip->dev, 0), 0x1,
650d497a82fSDamien Zammit 				USB_TYPE_CLASS |
651d497a82fSDamien Zammit 				USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
652d497a82fSDamien Zammit 	if (err < 0)
653d497a82fSDamien Zammit 		goto err;
654d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
655d497a82fSDamien Zammit 				usb_rcvctrlpipe(chip->dev, 0), 0x81,
656d497a82fSDamien Zammit 				USB_DIR_IN |
657d497a82fSDamien Zammit 				USB_TYPE_CLASS |
658d497a82fSDamien Zammit 				USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
659d497a82fSDamien Zammit 	if (err < 0)
660d497a82fSDamien Zammit 		goto err;
661d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
662d497a82fSDamien Zammit 				usb_rcvctrlpipe(chip->dev, 0), 0x81,
663d497a82fSDamien Zammit 				USB_DIR_IN |
664d497a82fSDamien Zammit 				USB_TYPE_CLASS |
665d497a82fSDamien Zammit 				USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3);
666d497a82fSDamien Zammit 	if (err < 0)
667d497a82fSDamien Zammit 		goto err;
668d497a82fSDamien Zammit 
669d497a82fSDamien Zammit err:
67047ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
67125a9a4f9STakashi Iwai 	return err;
67225a9a4f9STakashi Iwai }
67325a9a4f9STakashi Iwai 
67425a9a4f9STakashi Iwai static int snd_mbox1_switch_put(struct snd_kcontrol *kctl,
67525a9a4f9STakashi Iwai 				struct snd_ctl_elem_value *ucontrol)
67625a9a4f9STakashi Iwai {
67725a9a4f9STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
67825a9a4f9STakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
67925a9a4f9STakashi Iwai 	int err;
68025a9a4f9STakashi Iwai 	bool cur_val, new_val;
68125a9a4f9STakashi Iwai 
68225a9a4f9STakashi Iwai 	cur_val = kctl->private_value;
68325a9a4f9STakashi Iwai 	new_val = ucontrol->value.enumerated.item[0];
68425a9a4f9STakashi Iwai 	if (cur_val == new_val)
68525a9a4f9STakashi Iwai 		return 0;
68625a9a4f9STakashi Iwai 
68725a9a4f9STakashi Iwai 	kctl->private_value = new_val;
68825a9a4f9STakashi Iwai 	err = snd_mbox1_switch_update(mixer, new_val);
689d497a82fSDamien Zammit 	return err < 0 ? err : 1;
690d497a82fSDamien Zammit }
691d497a82fSDamien Zammit 
692d497a82fSDamien Zammit static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol,
693d497a82fSDamien Zammit 				 struct snd_ctl_elem_info *uinfo)
694d497a82fSDamien Zammit {
695d497a82fSDamien Zammit 	static const char *const texts[2] = {
696d497a82fSDamien Zammit 		"Internal",
697d497a82fSDamien Zammit 		"S/PDIF"
698d497a82fSDamien Zammit 	};
699d497a82fSDamien Zammit 
700d497a82fSDamien Zammit 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
701d497a82fSDamien Zammit }
702d497a82fSDamien Zammit 
70325a9a4f9STakashi Iwai static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list)
70425a9a4f9STakashi Iwai {
70525a9a4f9STakashi Iwai 	return snd_mbox1_switch_update(list->mixer, list->kctl->private_value);
70625a9a4f9STakashi Iwai }
70725a9a4f9STakashi Iwai 
708195727e8STakashi Iwai static const struct snd_kcontrol_new snd_mbox1_switch = {
709d497a82fSDamien Zammit 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
710d497a82fSDamien Zammit 	.name = "Clock Source",
711d497a82fSDamien Zammit 	.index = 0,
712d497a82fSDamien Zammit 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
713d497a82fSDamien Zammit 	.info = snd_mbox1_switch_info,
714d497a82fSDamien Zammit 	.get = snd_mbox1_switch_get,
715d497a82fSDamien Zammit 	.put = snd_mbox1_switch_put,
716d497a82fSDamien Zammit 	.private_value = 0
717d497a82fSDamien Zammit };
718d497a82fSDamien Zammit 
719d497a82fSDamien Zammit static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer)
720d497a82fSDamien Zammit {
72125a9a4f9STakashi Iwai 	return add_single_ctl_with_resume(mixer, 0,
72225a9a4f9STakashi Iwai 					  snd_mbox1_switch_resume,
72325a9a4f9STakashi Iwai 					  &snd_mbox1_switch, NULL);
724d497a82fSDamien Zammit }
725d497a82fSDamien Zammit 
72654a8c500SDaniel Mack /* Native Instruments device quirks */
72754a8c500SDaniel Mack 
72854a8c500SDaniel Mack #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
72954a8c500SDaniel Mack 
730da6d2769STakashi Iwai static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
731da6d2769STakashi Iwai 				   struct snd_kcontrol *kctl)
732da6d2769STakashi Iwai {
733da6d2769STakashi Iwai 	struct usb_device *dev = mixer->chip->dev;
734da6d2769STakashi Iwai 	unsigned int pval = kctl->private_value;
735da6d2769STakashi Iwai 	u8 value;
736da6d2769STakashi Iwai 	int err;
737da6d2769STakashi Iwai 
738da6d2769STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
739da6d2769STakashi Iwai 			      (pval >> 16) & 0xff,
740da6d2769STakashi Iwai 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
741da6d2769STakashi Iwai 			      0, pval & 0xffff, &value, 1);
742da6d2769STakashi Iwai 	if (err < 0) {
743da6d2769STakashi Iwai 		dev_err(&dev->dev,
744da6d2769STakashi Iwai 			"unable to issue vendor read request (ret = %d)", err);
745da6d2769STakashi Iwai 		return err;
746da6d2769STakashi Iwai 	}
747da6d2769STakashi Iwai 
7482acf5a3eSColin Ian King 	kctl->private_value |= ((unsigned int)value << 24);
749da6d2769STakashi Iwai 	return 0;
750da6d2769STakashi Iwai }
751da6d2769STakashi Iwai 
75254a8c500SDaniel Mack static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
75354a8c500SDaniel Mack 					     struct snd_ctl_elem_value *ucontrol)
75454a8c500SDaniel Mack {
755da6d2769STakashi Iwai 	ucontrol->value.integer.value[0] = kcontrol->private_value >> 24;
756da6d2769STakashi Iwai 	return 0;
75754a8c500SDaniel Mack }
75854a8c500SDaniel Mack 
759da6d2769STakashi Iwai static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
760da6d2769STakashi Iwai {
761da6d2769STakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
762da6d2769STakashi Iwai 	unsigned int pval = list->kctl->private_value;
763da6d2769STakashi Iwai 	int err;
76454a8c500SDaniel Mack 
76547ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
76647ab1545STakashi Iwai 	if (err < 0)
76747ab1545STakashi Iwai 		return err;
768da6d2769STakashi Iwai 	err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
769da6d2769STakashi Iwai 			      (pval >> 16) & 0xff,
770da6d2769STakashi Iwai 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
771da6d2769STakashi Iwai 			      pval >> 24, pval & 0xffff, NULL, 0, 1000);
77247ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
773da6d2769STakashi Iwai 	return err;
77454a8c500SDaniel Mack }
77554a8c500SDaniel Mack 
77654a8c500SDaniel Mack static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
77754a8c500SDaniel Mack 					     struct snd_ctl_elem_value *ucontrol)
77854a8c500SDaniel Mack {
779da6d2769STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
780da6d2769STakashi Iwai 	u8 oldval = (kcontrol->private_value >> 24) & 0xff;
781da6d2769STakashi Iwai 	u8 newval = ucontrol->value.integer.value[0];
782da6d2769STakashi Iwai 	int err;
78354a8c500SDaniel Mack 
784da6d2769STakashi Iwai 	if (oldval == newval)
78554a8c500SDaniel Mack 		return 0;
786da6d2769STakashi Iwai 
787da6d2769STakashi Iwai 	kcontrol->private_value &= ~(0xff << 24);
788c4a359a0STakashi Iwai 	kcontrol->private_value |= (unsigned int)newval << 24;
789da6d2769STakashi Iwai 	err = snd_ni_update_cur_val(list);
790da6d2769STakashi Iwai 	return err < 0 ? err : 1;
79154a8c500SDaniel Mack }
79254a8c500SDaniel Mack 
793195727e8STakashi Iwai static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
79454a8c500SDaniel Mack 	{
79554a8c500SDaniel Mack 		.name = "Direct Thru Channel A",
79654a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
79754a8c500SDaniel Mack 	},
79854a8c500SDaniel Mack 	{
79954a8c500SDaniel Mack 		.name = "Direct Thru Channel B",
80054a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
80154a8c500SDaniel Mack 	},
80254a8c500SDaniel Mack 	{
80354a8c500SDaniel Mack 		.name = "Phono Input Channel A",
80454a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
80554a8c500SDaniel Mack 	},
80654a8c500SDaniel Mack 	{
80754a8c500SDaniel Mack 		.name = "Phono Input Channel B",
80854a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
80954a8c500SDaniel Mack 	},
81054a8c500SDaniel Mack };
81154a8c500SDaniel Mack 
812195727e8STakashi Iwai static const struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = {
81354a8c500SDaniel Mack 	{
81454a8c500SDaniel Mack 		.name = "Direct Thru Channel A",
81554a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
81654a8c500SDaniel Mack 	},
81754a8c500SDaniel Mack 	{
81854a8c500SDaniel Mack 		.name = "Direct Thru Channel B",
81954a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
82054a8c500SDaniel Mack 	},
82154a8c500SDaniel Mack 	{
82254a8c500SDaniel Mack 		.name = "Direct Thru Channel C",
82354a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x07),
82454a8c500SDaniel Mack 	},
82554a8c500SDaniel Mack 	{
82654a8c500SDaniel Mack 		.name = "Direct Thru Channel D",
82754a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x09),
82854a8c500SDaniel Mack 	},
82954a8c500SDaniel Mack 	{
83054a8c500SDaniel Mack 		.name = "Phono Input Channel A",
83154a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
83254a8c500SDaniel Mack 	},
83354a8c500SDaniel Mack 	{
83454a8c500SDaniel Mack 		.name = "Phono Input Channel B",
83554a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
83654a8c500SDaniel Mack 	},
83754a8c500SDaniel Mack 	{
83854a8c500SDaniel Mack 		.name = "Phono Input Channel C",
83954a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x07),
84054a8c500SDaniel Mack 	},
84154a8c500SDaniel Mack 	{
84254a8c500SDaniel Mack 		.name = "Phono Input Channel D",
84354a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x09),
84454a8c500SDaniel Mack 	},
84554a8c500SDaniel Mack };
84654a8c500SDaniel Mack 
84754a8c500SDaniel Mack static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
84854a8c500SDaniel Mack 					      const struct snd_kcontrol_new *kc,
84954a8c500SDaniel Mack 					      unsigned int count)
85054a8c500SDaniel Mack {
85154a8c500SDaniel Mack 	int i, err = 0;
85254a8c500SDaniel Mack 	struct snd_kcontrol_new template = {
85354a8c500SDaniel Mack 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
85454a8c500SDaniel Mack 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
85554a8c500SDaniel Mack 		.get = snd_nativeinstruments_control_get,
85654a8c500SDaniel Mack 		.put = snd_nativeinstruments_control_put,
85754a8c500SDaniel Mack 		.info = snd_ctl_boolean_mono_info,
85854a8c500SDaniel Mack 	};
85954a8c500SDaniel Mack 
86054a8c500SDaniel Mack 	for (i = 0; i < count; i++) {
861da6d2769STakashi Iwai 		struct usb_mixer_elem_list *list;
86254a8c500SDaniel Mack 
86354a8c500SDaniel Mack 		template.name = kc[i].name;
86454a8c500SDaniel Mack 		template.private_value = kc[i].private_value;
86554a8c500SDaniel Mack 
866da6d2769STakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
867da6d2769STakashi Iwai 						 snd_ni_update_cur_val,
868da6d2769STakashi Iwai 						 &template, &list);
86954a8c500SDaniel Mack 		if (err < 0)
87054a8c500SDaniel Mack 			break;
871da6d2769STakashi Iwai 		snd_ni_control_init_val(mixer, list->kctl);
87254a8c500SDaniel Mack 	}
87354a8c500SDaniel Mack 
87454a8c500SDaniel Mack 	return err;
87554a8c500SDaniel Mack }
87654a8c500SDaniel Mack 
877d5a0bf6cSDaniel Mack /* M-Audio FastTrack Ultra quirks */
878e9a25e04SMatt Gruskin /* FTU Effect switch (also used by C400/C600) */
879d34bf148SFelix Homann static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
880d34bf148SFelix Homann 					struct snd_ctl_elem_info *uinfo)
881d34bf148SFelix Homann {
8827bbd03e0STakashi Iwai 	static const char *const texts[8] = {
8837bbd03e0STakashi Iwai 		"Room 1", "Room 2", "Room 3", "Hall 1",
8847bbd03e0STakashi Iwai 		"Hall 2", "Plate", "Delay", "Echo"
885d34bf148SFelix Homann 	};
886d34bf148SFelix Homann 
8877bbd03e0STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
888d34bf148SFelix Homann }
889d34bf148SFelix Homann 
8900b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
8910b4e9cfcSTakashi Iwai 				   struct snd_kcontrol *kctl)
892d34bf148SFelix Homann {
8930b4e9cfcSTakashi Iwai 	struct usb_device *dev = mixer->chip->dev;
8940b4e9cfcSTakashi Iwai 	unsigned int pval = kctl->private_value;
895d34bf148SFelix Homann 	int err;
896d34bf148SFelix Homann 	unsigned char value[2];
897d34bf148SFelix Homann 
898d34bf148SFelix Homann 	value[0] = 0x00;
899d34bf148SFelix Homann 	value[1] = 0x00;
900d34bf148SFelix Homann 
9010b4e9cfcSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
902d34bf148SFelix Homann 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
9030b4e9cfcSTakashi Iwai 			      pval & 0xff00,
9040b4e9cfcSTakashi Iwai 			      snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
9050b4e9cfcSTakashi Iwai 			      value, 2);
906d34bf148SFelix Homann 	if (err < 0)
907d34bf148SFelix Homann 		return err;
908d34bf148SFelix Homann 
9092acf5a3eSColin Ian King 	kctl->private_value |= (unsigned int)value[0] << 24;
910d34bf148SFelix Homann 	return 0;
911d34bf148SFelix Homann }
912d34bf148SFelix Homann 
9130b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
9140b4e9cfcSTakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
9150b4e9cfcSTakashi Iwai {
9160b4e9cfcSTakashi Iwai 	ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
9170b4e9cfcSTakashi Iwai 	return 0;
9180b4e9cfcSTakashi Iwai }
9190b4e9cfcSTakashi Iwai 
9200b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
9210b4e9cfcSTakashi Iwai {
9220b4e9cfcSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
9230b4e9cfcSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
9240b4e9cfcSTakashi Iwai 	unsigned char value[2];
9250b4e9cfcSTakashi Iwai 	int err;
9260b4e9cfcSTakashi Iwai 
9270b4e9cfcSTakashi Iwai 	value[0] = pval >> 24;
9280b4e9cfcSTakashi Iwai 	value[1] = 0;
9290b4e9cfcSTakashi Iwai 
93047ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
93147ab1545STakashi Iwai 	if (err < 0)
93247ab1545STakashi Iwai 		return err;
9330b4e9cfcSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
9340b4e9cfcSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0),
9350b4e9cfcSTakashi Iwai 			      UAC_SET_CUR,
9360b4e9cfcSTakashi Iwai 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
9370b4e9cfcSTakashi Iwai 			      pval & 0xff00,
9380b4e9cfcSTakashi Iwai 			      snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
9390b4e9cfcSTakashi Iwai 			      value, 2);
94047ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
9410b4e9cfcSTakashi Iwai 	return err;
9420b4e9cfcSTakashi Iwai }
9430b4e9cfcSTakashi Iwai 
944d34bf148SFelix Homann static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
945d34bf148SFelix Homann 					struct snd_ctl_elem_value *ucontrol)
946d34bf148SFelix Homann {
9470b4e9cfcSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
9480b4e9cfcSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
9490b4e9cfcSTakashi Iwai 	int cur_val, err, new_val;
950d34bf148SFelix Homann 
9510b4e9cfcSTakashi Iwai 	cur_val = pval >> 24;
952d34bf148SFelix Homann 	new_val = ucontrol->value.enumerated.item[0];
9530b4e9cfcSTakashi Iwai 	if (cur_val == new_val)
9540b4e9cfcSTakashi Iwai 		return 0;
955d34bf148SFelix Homann 
9560b4e9cfcSTakashi Iwai 	kctl->private_value &= ~(0xff << 24);
9570b4e9cfcSTakashi Iwai 	kctl->private_value |= new_val << 24;
9580b4e9cfcSTakashi Iwai 	err = snd_ftu_eff_switch_update(list);
9590b4e9cfcSTakashi Iwai 	return err < 0 ? err : 1;
9601a290581STakashi Iwai }
9611a290581STakashi Iwai 
962d847ce0eSEldad Zack static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
963d847ce0eSEldad Zack 	int validx, int bUnitID)
964d34bf148SFelix Homann {
965d34bf148SFelix Homann 	static struct snd_kcontrol_new template = {
966d34bf148SFelix Homann 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
967d34bf148SFelix Homann 		.name = "Effect Program Switch",
968d34bf148SFelix Homann 		.index = 0,
969d34bf148SFelix Homann 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
970d34bf148SFelix Homann 		.info = snd_ftu_eff_switch_info,
971d34bf148SFelix Homann 		.get = snd_ftu_eff_switch_get,
972d34bf148SFelix Homann 		.put = snd_ftu_eff_switch_put
973d34bf148SFelix Homann 	};
9740b4e9cfcSTakashi Iwai 	struct usb_mixer_elem_list *list;
975d34bf148SFelix Homann 	int err;
976d34bf148SFelix Homann 
9770b4e9cfcSTakashi Iwai 	err = add_single_ctl_with_resume(mixer, bUnitID,
9780b4e9cfcSTakashi Iwai 					 snd_ftu_eff_switch_update,
9790b4e9cfcSTakashi Iwai 					 &template, &list);
980d34bf148SFelix Homann 	if (err < 0)
981d34bf148SFelix Homann 		return err;
9820b4e9cfcSTakashi Iwai 	list->kctl->private_value = (validx << 8) | bUnitID;
9830b4e9cfcSTakashi Iwai 	snd_ftu_eff_switch_init(mixer, list->kctl);
984d34bf148SFelix Homann 	return 0;
985d34bf148SFelix Homann }
986d5a0bf6cSDaniel Mack 
987cfe8f97cSFelix Homann /* Create volume controls for FTU devices*/
988cfe8f97cSFelix Homann static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
989d5a0bf6cSDaniel Mack {
990d5a0bf6cSDaniel Mack 	char name[64];
9918a4d1d39SFelix Homann 	unsigned int control, cmask;
992d5a0bf6cSDaniel Mack 	int in, out, err;
993d5a0bf6cSDaniel Mack 
9948a4d1d39SFelix Homann 	const unsigned int id = 5;
9958a4d1d39SFelix Homann 	const int val_type = USB_MIXER_S16;
9968a4d1d39SFelix Homann 
997d5a0bf6cSDaniel Mack 	for (out = 0; out < 8; out++) {
9988a4d1d39SFelix Homann 		control = out + 1;
999d5a0bf6cSDaniel Mack 		for (in = 0; in < 8; in++) {
10008a4d1d39SFelix Homann 			cmask = 1 << in;
1001d5a0bf6cSDaniel Mack 			snprintf(name, sizeof(name),
10028a4d1d39SFelix Homann 				"AIn%d - Out%d Capture Volume",
10038a4d1d39SFelix Homann 				in  + 1, out + 1);
10048a4d1d39SFelix Homann 			err = snd_create_std_mono_ctl(mixer, id, control,
10058a4d1d39SFelix Homann 							cmask, val_type, name,
100625ee7ef8SFelix Homann 							&snd_usb_mixer_vol_tlv);
1007d5a0bf6cSDaniel Mack 			if (err < 0)
1008d5a0bf6cSDaniel Mack 				return err;
1009d5a0bf6cSDaniel Mack 		}
1010d5a0bf6cSDaniel Mack 		for (in = 8; in < 16; in++) {
10118a4d1d39SFelix Homann 			cmask = 1 << in;
1012d5a0bf6cSDaniel Mack 			snprintf(name, sizeof(name),
10138a4d1d39SFelix Homann 				"DIn%d - Out%d Playback Volume",
10148a4d1d39SFelix Homann 				in - 7, out + 1);
10158a4d1d39SFelix Homann 			err = snd_create_std_mono_ctl(mixer, id, control,
10168a4d1d39SFelix Homann 							cmask, val_type, name,
101725ee7ef8SFelix Homann 							&snd_usb_mixer_vol_tlv);
1018d5a0bf6cSDaniel Mack 			if (err < 0)
1019d5a0bf6cSDaniel Mack 				return err;
1020d5a0bf6cSDaniel Mack 		}
1021d5a0bf6cSDaniel Mack 	}
1022d5a0bf6cSDaniel Mack 
1023d5a0bf6cSDaniel Mack 	return 0;
1024d5a0bf6cSDaniel Mack }
1025d5a0bf6cSDaniel Mack 
1026d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
1027d34bf148SFelix Homann static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
1028d34bf148SFelix Homann {
1029d34bf148SFelix Homann 	static const char name[] = "Effect Volume";
1030d34bf148SFelix Homann 	const unsigned int id = 6;
1031d34bf148SFelix Homann 	const int val_type = USB_MIXER_U8;
1032d34bf148SFelix Homann 	const unsigned int control = 2;
1033d34bf148SFelix Homann 	const unsigned int cmask = 0;
1034d34bf148SFelix Homann 
1035d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1036d34bf148SFelix Homann 					name, snd_usb_mixer_vol_tlv);
1037d34bf148SFelix Homann }
1038d34bf148SFelix Homann 
1039d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
1040d34bf148SFelix Homann static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
1041d34bf148SFelix Homann {
1042d34bf148SFelix Homann 	static const char name[] = "Effect Duration";
1043d34bf148SFelix Homann 	const unsigned int id = 6;
1044d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1045d34bf148SFelix Homann 	const unsigned int control = 3;
1046d34bf148SFelix Homann 	const unsigned int cmask = 0;
1047d34bf148SFelix Homann 
1048d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1049d34bf148SFelix Homann 					name, snd_usb_mixer_vol_tlv);
1050d34bf148SFelix Homann }
1051d34bf148SFelix Homann 
1052d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
1053d34bf148SFelix Homann static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
1054d34bf148SFelix Homann {
1055d34bf148SFelix Homann 	static const char name[] = "Effect Feedback Volume";
1056d34bf148SFelix Homann 	const unsigned int id = 6;
1057d34bf148SFelix Homann 	const int val_type = USB_MIXER_U8;
1058d34bf148SFelix Homann 	const unsigned int control = 4;
1059d34bf148SFelix Homann 	const unsigned int cmask = 0;
1060d34bf148SFelix Homann 
1061d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1062d34bf148SFelix Homann 					name, NULL);
1063d34bf148SFelix Homann }
1064d34bf148SFelix Homann 
1065d34bf148SFelix Homann static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
1066d34bf148SFelix Homann {
1067d34bf148SFelix Homann 	unsigned int cmask;
1068d34bf148SFelix Homann 	int err, ch;
1069d34bf148SFelix Homann 	char name[48];
1070d34bf148SFelix Homann 
1071d34bf148SFelix Homann 	const unsigned int id = 7;
1072d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1073d34bf148SFelix Homann 	const unsigned int control = 7;
1074d34bf148SFelix Homann 
1075d34bf148SFelix Homann 	for (ch = 0; ch < 4; ++ch) {
1076d34bf148SFelix Homann 		cmask = 1 << ch;
1077d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1078d34bf148SFelix Homann 			"Effect Return %d Volume", ch + 1);
1079d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control,
1080d34bf148SFelix Homann 						cmask, val_type, name,
1081d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1082d34bf148SFelix Homann 		if (err < 0)
1083d34bf148SFelix Homann 			return err;
1084d34bf148SFelix Homann 	}
1085d34bf148SFelix Homann 
1086d34bf148SFelix Homann 	return 0;
1087d34bf148SFelix Homann }
1088d34bf148SFelix Homann 
1089d34bf148SFelix Homann static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
1090d34bf148SFelix Homann {
1091d34bf148SFelix Homann 	unsigned int  cmask;
1092d34bf148SFelix Homann 	int err, ch;
1093d34bf148SFelix Homann 	char name[48];
1094d34bf148SFelix Homann 
1095d34bf148SFelix Homann 	const unsigned int id = 5;
1096d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1097d34bf148SFelix Homann 	const unsigned int control = 9;
1098d34bf148SFelix Homann 
1099d34bf148SFelix Homann 	for (ch = 0; ch < 8; ++ch) {
1100d34bf148SFelix Homann 		cmask = 1 << ch;
1101d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1102d34bf148SFelix Homann 			"Effect Send AIn%d Volume", ch + 1);
1103d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
1104d34bf148SFelix Homann 						val_type, name,
1105d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1106d34bf148SFelix Homann 		if (err < 0)
1107d34bf148SFelix Homann 			return err;
1108d34bf148SFelix Homann 	}
1109d34bf148SFelix Homann 	for (ch = 8; ch < 16; ++ch) {
1110d34bf148SFelix Homann 		cmask = 1 << ch;
1111d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1112d34bf148SFelix Homann 			"Effect Send DIn%d Volume", ch - 7);
1113d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
1114d34bf148SFelix Homann 						val_type, name,
1115d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1116d34bf148SFelix Homann 		if (err < 0)
1117d34bf148SFelix Homann 			return err;
1118d34bf148SFelix Homann 	}
1119d34bf148SFelix Homann 	return 0;
1120d34bf148SFelix Homann }
1121d34bf148SFelix Homann 
1122cfe8f97cSFelix Homann static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
11237536c301SMark Hills {
11248a4d1d39SFelix Homann 	int err;
11257536c301SMark Hills 
1126cfe8f97cSFelix Homann 	err = snd_ftu_create_volume_ctls(mixer);
11278a4d1d39SFelix Homann 	if (err < 0)
11288a4d1d39SFelix Homann 		return err;
11297536c301SMark Hills 
1130d847ce0eSEldad Zack 	err = snd_ftu_create_effect_switch(mixer, 1, 6);
1131d34bf148SFelix Homann 	if (err < 0)
1132d34bf148SFelix Homann 		return err;
1133d847ce0eSEldad Zack 
1134d34bf148SFelix Homann 	err = snd_ftu_create_effect_volume_ctl(mixer);
1135d34bf148SFelix Homann 	if (err < 0)
1136d34bf148SFelix Homann 		return err;
1137d34bf148SFelix Homann 
1138d34bf148SFelix Homann 	err = snd_ftu_create_effect_duration_ctl(mixer);
1139d34bf148SFelix Homann 	if (err < 0)
1140d34bf148SFelix Homann 		return err;
1141d34bf148SFelix Homann 
1142d34bf148SFelix Homann 	err = snd_ftu_create_effect_feedback_ctl(mixer);
1143d34bf148SFelix Homann 	if (err < 0)
1144d34bf148SFelix Homann 		return err;
1145d34bf148SFelix Homann 
1146d34bf148SFelix Homann 	err = snd_ftu_create_effect_return_ctls(mixer);
1147d34bf148SFelix Homann 	if (err < 0)
1148d34bf148SFelix Homann 		return err;
1149d34bf148SFelix Homann 
1150d34bf148SFelix Homann 	err = snd_ftu_create_effect_send_ctls(mixer);
1151d34bf148SFelix Homann 	if (err < 0)
1152d34bf148SFelix Homann 		return err;
1153d34bf148SFelix Homann 
11548a4d1d39SFelix Homann 	return 0;
11557536c301SMark Hills }
11567536c301SMark Hills 
11577b1eda22SDaniel Mack void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
11587b1eda22SDaniel Mack 			       unsigned char samplerate_id)
11597b1eda22SDaniel Mack {
11607b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer;
11617b1eda22SDaniel Mack 	struct usb_mixer_elem_info *cval;
11626de3c9e3STakashi Iwai 	int unitid = 12; /* SampleRate ExtensionUnit ID */
11637b1eda22SDaniel Mack 
11647b1eda22SDaniel Mack 	list_for_each_entry(mixer, &chip->mixer_list, list) {
11656de3c9e3STakashi Iwai 		if (mixer->id_elems[unitid]) {
11668c558076STakashi Iwai 			cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
11677b1eda22SDaniel Mack 			snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
11687b1eda22SDaniel Mack 						    cval->control << 8,
11697b1eda22SDaniel Mack 						    samplerate_id);
11707b1eda22SDaniel Mack 			snd_usb_mixer_notify_id(mixer, unitid);
11717b1eda22SDaniel Mack 			break;
11727b1eda22SDaniel Mack 		}
11737b1eda22SDaniel Mack 	}
11746de3c9e3STakashi Iwai }
11757b1eda22SDaniel Mack 
1176e9a25e04SMatt Gruskin /* M-Audio Fast Track C400/C600 */
1177e9a25e04SMatt Gruskin /* C400/C600 volume controls, this control needs a volume quirk, see mixer.c */
117809d8e3a7SEldad Zack static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
117909d8e3a7SEldad Zack {
118009d8e3a7SEldad Zack 	char name[64];
118109d8e3a7SEldad Zack 	unsigned int cmask, offset;
118209d8e3a7SEldad Zack 	int out, chan, err;
1183e9a25e04SMatt Gruskin 	int num_outs = 0;
1184e9a25e04SMatt Gruskin 	int num_ins = 0;
118509d8e3a7SEldad Zack 
118609d8e3a7SEldad Zack 	const unsigned int id = 0x40;
118709d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
118809d8e3a7SEldad Zack 	const int control = 1;
118909d8e3a7SEldad Zack 
1190e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1191e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1192e9a25e04SMatt Gruskin 		num_outs = 6;
1193e9a25e04SMatt Gruskin 		num_ins = 4;
1194e9a25e04SMatt Gruskin 		break;
1195e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1196e9a25e04SMatt Gruskin 		num_outs = 8;
1197e9a25e04SMatt Gruskin 		num_ins = 6;
1198e9a25e04SMatt Gruskin 		break;
1199e9a25e04SMatt Gruskin 	}
1200e9a25e04SMatt Gruskin 
1201e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs + num_ins; chan++) {
1202e9a25e04SMatt Gruskin 		for (out = 0; out < num_outs; out++) {
1203e9a25e04SMatt Gruskin 			if (chan < num_outs) {
120409d8e3a7SEldad Zack 				snprintf(name, sizeof(name),
120509d8e3a7SEldad Zack 					"PCM%d-Out%d Playback Volume",
120609d8e3a7SEldad Zack 					chan + 1, out + 1);
120709d8e3a7SEldad Zack 			} else {
120809d8e3a7SEldad Zack 				snprintf(name, sizeof(name),
120909d8e3a7SEldad Zack 					"In%d-Out%d Playback Volume",
1210e9a25e04SMatt Gruskin 					chan - num_outs + 1, out + 1);
121109d8e3a7SEldad Zack 			}
121209d8e3a7SEldad Zack 
121309d8e3a7SEldad Zack 			cmask = (out == 0) ? 0 : 1 << (out - 1);
1214e9a25e04SMatt Gruskin 			offset = chan * num_outs;
121509d8e3a7SEldad Zack 			err = snd_create_std_mono_ctl_offset(mixer, id, control,
121609d8e3a7SEldad Zack 						cmask, val_type, offset, name,
121709d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
121809d8e3a7SEldad Zack 			if (err < 0)
121909d8e3a7SEldad Zack 				return err;
122009d8e3a7SEldad Zack 		}
122109d8e3a7SEldad Zack 	}
122209d8e3a7SEldad Zack 
122309d8e3a7SEldad Zack 	return 0;
122409d8e3a7SEldad Zack }
122509d8e3a7SEldad Zack 
122609d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
122709d8e3a7SEldad Zack static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
122809d8e3a7SEldad Zack {
122909d8e3a7SEldad Zack 	static const char name[] = "Effect Volume";
123009d8e3a7SEldad Zack 	const unsigned int id = 0x43;
123109d8e3a7SEldad Zack 	const int val_type = USB_MIXER_U8;
123209d8e3a7SEldad Zack 	const unsigned int control = 3;
123309d8e3a7SEldad Zack 	const unsigned int cmask = 0;
123409d8e3a7SEldad Zack 
123509d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
123609d8e3a7SEldad Zack 					name, snd_usb_mixer_vol_tlv);
123709d8e3a7SEldad Zack }
123809d8e3a7SEldad Zack 
123909d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
124009d8e3a7SEldad Zack static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
124109d8e3a7SEldad Zack {
124209d8e3a7SEldad Zack 	static const char name[] = "Effect Duration";
124309d8e3a7SEldad Zack 	const unsigned int id = 0x43;
124409d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
124509d8e3a7SEldad Zack 	const unsigned int control = 4;
124609d8e3a7SEldad Zack 	const unsigned int cmask = 0;
124709d8e3a7SEldad Zack 
124809d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
124909d8e3a7SEldad Zack 					name, snd_usb_mixer_vol_tlv);
125009d8e3a7SEldad Zack }
125109d8e3a7SEldad Zack 
125209d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
125309d8e3a7SEldad Zack static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
125409d8e3a7SEldad Zack {
125509d8e3a7SEldad Zack 	static const char name[] = "Effect Feedback Volume";
125609d8e3a7SEldad Zack 	const unsigned int id = 0x43;
125709d8e3a7SEldad Zack 	const int val_type = USB_MIXER_U8;
125809d8e3a7SEldad Zack 	const unsigned int control = 5;
125909d8e3a7SEldad Zack 	const unsigned int cmask = 0;
126009d8e3a7SEldad Zack 
126109d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
126209d8e3a7SEldad Zack 					name, NULL);
126309d8e3a7SEldad Zack }
126409d8e3a7SEldad Zack 
126509d8e3a7SEldad Zack static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
126609d8e3a7SEldad Zack {
126709d8e3a7SEldad Zack 	char name[64];
126809d8e3a7SEldad Zack 	unsigned int cmask;
126909d8e3a7SEldad Zack 	int chan, err;
1270e9a25e04SMatt Gruskin 	int num_outs = 0;
1271e9a25e04SMatt Gruskin 	int num_ins = 0;
127209d8e3a7SEldad Zack 
127309d8e3a7SEldad Zack 	const unsigned int id = 0x42;
127409d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
127509d8e3a7SEldad Zack 	const int control = 1;
127609d8e3a7SEldad Zack 
1277e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1278e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1279e9a25e04SMatt Gruskin 		num_outs = 6;
1280e9a25e04SMatt Gruskin 		num_ins = 4;
1281e9a25e04SMatt Gruskin 		break;
1282e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1283e9a25e04SMatt Gruskin 		num_outs = 8;
1284e9a25e04SMatt Gruskin 		num_ins = 6;
1285e9a25e04SMatt Gruskin 		break;
1286e9a25e04SMatt Gruskin 	}
1287e9a25e04SMatt Gruskin 
1288e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs + num_ins; chan++) {
1289e9a25e04SMatt Gruskin 		if (chan < num_outs) {
129009d8e3a7SEldad Zack 			snprintf(name, sizeof(name),
129109d8e3a7SEldad Zack 				"Effect Send DOut%d",
129209d8e3a7SEldad Zack 				chan + 1);
129309d8e3a7SEldad Zack 		} else {
129409d8e3a7SEldad Zack 			snprintf(name, sizeof(name),
129509d8e3a7SEldad Zack 				"Effect Send AIn%d",
1296e9a25e04SMatt Gruskin 				chan - num_outs + 1);
129709d8e3a7SEldad Zack 		}
129809d8e3a7SEldad Zack 
129909d8e3a7SEldad Zack 		cmask = (chan == 0) ? 0 : 1 << (chan - 1);
130009d8e3a7SEldad Zack 		err = snd_create_std_mono_ctl(mixer, id, control,
130109d8e3a7SEldad Zack 						cmask, val_type, name,
130209d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
130309d8e3a7SEldad Zack 		if (err < 0)
130409d8e3a7SEldad Zack 			return err;
130509d8e3a7SEldad Zack 	}
130609d8e3a7SEldad Zack 
130709d8e3a7SEldad Zack 	return 0;
130809d8e3a7SEldad Zack }
130909d8e3a7SEldad Zack 
131009d8e3a7SEldad Zack static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer)
131109d8e3a7SEldad Zack {
131209d8e3a7SEldad Zack 	char name[64];
131309d8e3a7SEldad Zack 	unsigned int cmask;
131409d8e3a7SEldad Zack 	int chan, err;
1315e9a25e04SMatt Gruskin 	int num_outs = 0;
1316e9a25e04SMatt Gruskin 	int offset = 0;
131709d8e3a7SEldad Zack 
131809d8e3a7SEldad Zack 	const unsigned int id = 0x40;
131909d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
132009d8e3a7SEldad Zack 	const int control = 1;
132109d8e3a7SEldad Zack 
1322e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1323e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1324e9a25e04SMatt Gruskin 		num_outs = 6;
1325e9a25e04SMatt Gruskin 		offset = 0x3c;
1326e9a25e04SMatt Gruskin 		/* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */
1327e9a25e04SMatt Gruskin 		break;
1328e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1329e9a25e04SMatt Gruskin 		num_outs = 8;
1330e9a25e04SMatt Gruskin 		offset = 0x70;
1331e9a25e04SMatt Gruskin 		/* { 0x70, 0x79, 0x72, 0x7b, 0x74, 0x7d, 0x76, 0x7f } */
1332e9a25e04SMatt Gruskin 		break;
1333e9a25e04SMatt Gruskin 	}
1334e9a25e04SMatt Gruskin 
1335e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs; chan++) {
133609d8e3a7SEldad Zack 		snprintf(name, sizeof(name),
133709d8e3a7SEldad Zack 			"Effect Return %d",
133809d8e3a7SEldad Zack 			chan + 1);
133909d8e3a7SEldad Zack 
1340e9a25e04SMatt Gruskin 		cmask = (chan == 0) ? 0 :
1341e9a25e04SMatt Gruskin 			1 << (chan + (chan % 2) * num_outs - 1);
134209d8e3a7SEldad Zack 		err = snd_create_std_mono_ctl_offset(mixer, id, control,
134309d8e3a7SEldad Zack 						cmask, val_type, offset, name,
134409d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
134509d8e3a7SEldad Zack 		if (err < 0)
134609d8e3a7SEldad Zack 			return err;
134709d8e3a7SEldad Zack 	}
134809d8e3a7SEldad Zack 
134909d8e3a7SEldad Zack 	return 0;
135009d8e3a7SEldad Zack }
135109d8e3a7SEldad Zack 
135209d8e3a7SEldad Zack static int snd_c400_create_mixer(struct usb_mixer_interface *mixer)
135309d8e3a7SEldad Zack {
135409d8e3a7SEldad Zack 	int err;
135509d8e3a7SEldad Zack 
135609d8e3a7SEldad Zack 	err = snd_c400_create_vol_ctls(mixer);
135709d8e3a7SEldad Zack 	if (err < 0)
135809d8e3a7SEldad Zack 		return err;
135909d8e3a7SEldad Zack 
136009d8e3a7SEldad Zack 	err = snd_c400_create_effect_vol_ctls(mixer);
136109d8e3a7SEldad Zack 	if (err < 0)
136209d8e3a7SEldad Zack 		return err;
136309d8e3a7SEldad Zack 
136409d8e3a7SEldad Zack 	err = snd_c400_create_effect_ret_vol_ctls(mixer);
136509d8e3a7SEldad Zack 	if (err < 0)
136609d8e3a7SEldad Zack 		return err;
136709d8e3a7SEldad Zack 
136809d8e3a7SEldad Zack 	err = snd_ftu_create_effect_switch(mixer, 2, 0x43);
136909d8e3a7SEldad Zack 	if (err < 0)
137009d8e3a7SEldad Zack 		return err;
137109d8e3a7SEldad Zack 
137209d8e3a7SEldad Zack 	err = snd_c400_create_effect_volume_ctl(mixer);
137309d8e3a7SEldad Zack 	if (err < 0)
137409d8e3a7SEldad Zack 		return err;
137509d8e3a7SEldad Zack 
137609d8e3a7SEldad Zack 	err = snd_c400_create_effect_duration_ctl(mixer);
137709d8e3a7SEldad Zack 	if (err < 0)
137809d8e3a7SEldad Zack 		return err;
137909d8e3a7SEldad Zack 
138009d8e3a7SEldad Zack 	err = snd_c400_create_effect_feedback_ctl(mixer);
138109d8e3a7SEldad Zack 	if (err < 0)
138209d8e3a7SEldad Zack 		return err;
138309d8e3a7SEldad Zack 
138409d8e3a7SEldad Zack 	return 0;
138509d8e3a7SEldad Zack }
138609d8e3a7SEldad Zack 
1387b71dad18SMark Hills /*
1388b71dad18SMark Hills  * The mixer units for Ebox-44 are corrupt, and even where they
1389b71dad18SMark Hills  * are valid they presents mono controls as L and R channels of
1390b71dad18SMark Hills  * stereo. So we provide a good mixer here.
1391b71dad18SMark Hills  */
1392a01df925STakashi Iwai static const struct std_mono_table ebox44_table[] = {
1393989b0138SMark Hills 	{
1394989b0138SMark Hills 		.unitid = 4,
1395989b0138SMark Hills 		.control = 1,
1396989b0138SMark Hills 		.cmask = 0x0,
1397989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1398989b0138SMark Hills 		.name = "Headphone Playback Switch"
1399989b0138SMark Hills 	},
1400989b0138SMark Hills 	{
1401989b0138SMark Hills 		.unitid = 4,
1402989b0138SMark Hills 		.control = 2,
1403989b0138SMark Hills 		.cmask = 0x1,
1404989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1405989b0138SMark Hills 		.name = "Headphone A Mix Playback Volume"
1406989b0138SMark Hills 	},
1407989b0138SMark Hills 	{
1408989b0138SMark Hills 		.unitid = 4,
1409989b0138SMark Hills 		.control = 2,
1410989b0138SMark Hills 		.cmask = 0x2,
1411989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1412989b0138SMark Hills 		.name = "Headphone B Mix Playback Volume"
1413989b0138SMark Hills 	},
1414b71dad18SMark Hills 
1415989b0138SMark Hills 	{
1416989b0138SMark Hills 		.unitid = 7,
1417989b0138SMark Hills 		.control = 1,
1418989b0138SMark Hills 		.cmask = 0x0,
1419989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1420989b0138SMark Hills 		.name = "Output Playback Switch"
1421989b0138SMark Hills 	},
1422989b0138SMark Hills 	{
1423989b0138SMark Hills 		.unitid = 7,
1424989b0138SMark Hills 		.control = 2,
1425989b0138SMark Hills 		.cmask = 0x1,
1426989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1427989b0138SMark Hills 		.name = "Output A Playback Volume"
1428989b0138SMark Hills 	},
1429989b0138SMark Hills 	{
1430989b0138SMark Hills 		.unitid = 7,
1431989b0138SMark Hills 		.control = 2,
1432989b0138SMark Hills 		.cmask = 0x2,
1433989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1434989b0138SMark Hills 		.name = "Output B Playback Volume"
1435989b0138SMark Hills 	},
1436b71dad18SMark Hills 
1437989b0138SMark Hills 	{
1438989b0138SMark Hills 		.unitid = 10,
1439989b0138SMark Hills 		.control = 1,
1440989b0138SMark Hills 		.cmask = 0x0,
1441989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1442989b0138SMark Hills 		.name = "Input Capture Switch"
1443989b0138SMark Hills 	},
1444989b0138SMark Hills 	{
1445989b0138SMark Hills 		.unitid = 10,
1446989b0138SMark Hills 		.control = 2,
1447989b0138SMark Hills 		.cmask = 0x1,
1448989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1449989b0138SMark Hills 		.name = "Input A Capture Volume"
1450989b0138SMark Hills 	},
1451989b0138SMark Hills 	{
1452989b0138SMark Hills 		.unitid = 10,
1453989b0138SMark Hills 		.control = 2,
1454989b0138SMark Hills 		.cmask = 0x2,
1455989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1456989b0138SMark Hills 		.name = "Input B Capture Volume"
1457989b0138SMark Hills 	},
1458b71dad18SMark Hills 
1459b71dad18SMark Hills 	{}
1460b71dad18SMark Hills };
1461b71dad18SMark Hills 
1462066624c6SPrzemek Rudy /* Audio Advantage Micro II findings:
1463066624c6SPrzemek Rudy  *
1464066624c6SPrzemek Rudy  * Mapping spdif AES bits to vendor register.bit:
1465066624c6SPrzemek Rudy  * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00
1466066624c6SPrzemek Rudy  * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01
1467066624c6SPrzemek Rudy  * AES2: [0 0 0 0 0 0 0 0]
1468066624c6SPrzemek Rudy  * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request
1469066624c6SPrzemek Rudy  *                           (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices
1470066624c6SPrzemek Rudy  *
1471066624c6SPrzemek Rudy  * power on values:
1472066624c6SPrzemek Rudy  * r2: 0x10
1473066624c6SPrzemek Rudy  * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set
1474066624c6SPrzemek Rudy  *           just after it to 0xa0, presumably it disables/mutes some analog
1475066624c6SPrzemek Rudy  *           parts when there is no audio.)
1476066624c6SPrzemek Rudy  * r9: 0x28
1477066624c6SPrzemek Rudy  *
1478066624c6SPrzemek Rudy  * Optical transmitter on/off:
1479066624c6SPrzemek Rudy  * vendor register.bit: 9.1
1480066624c6SPrzemek Rudy  * 0 - on (0x28 register value)
1481066624c6SPrzemek Rudy  * 1 - off (0x2a register value)
1482066624c6SPrzemek Rudy  *
1483066624c6SPrzemek Rudy  */
1484066624c6SPrzemek Rudy static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
1485066624c6SPrzemek Rudy 	struct snd_ctl_elem_info *uinfo)
1486066624c6SPrzemek Rudy {
1487066624c6SPrzemek Rudy 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
1488066624c6SPrzemek Rudy 	uinfo->count = 1;
1489066624c6SPrzemek Rudy 	return 0;
1490066624c6SPrzemek Rudy }
1491066624c6SPrzemek Rudy 
1492066624c6SPrzemek Rudy static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
1493066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1494066624c6SPrzemek Rudy {
1495288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1496288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1497066624c6SPrzemek Rudy 	int err;
1498066624c6SPrzemek Rudy 	struct usb_interface *iface;
1499066624c6SPrzemek Rudy 	struct usb_host_interface *alts;
1500066624c6SPrzemek Rudy 	unsigned int ep;
1501066624c6SPrzemek Rudy 	unsigned char data[3];
1502066624c6SPrzemek Rudy 	int rate;
1503066624c6SPrzemek Rudy 
150447ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
150547ab1545STakashi Iwai 	if (err < 0)
150647ab1545STakashi Iwai 		return err;
1507288673beSTakashi Iwai 
1508066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
1509066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
1510066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[2] = 0x00;
1511066624c6SPrzemek Rudy 
1512066624c6SPrzemek Rudy 	/* use known values for that card: interface#1 altsetting#1 */
1513288673beSTakashi Iwai 	iface = usb_ifnum_to_if(chip->dev, 1);
151459e1947cSXiyu Yang 	if (!iface || iface->num_altsetting < 2) {
151559e1947cSXiyu Yang 		err = -EINVAL;
151659e1947cSXiyu Yang 		goto end;
151759e1947cSXiyu Yang 	}
1518066624c6SPrzemek Rudy 	alts = &iface->altsetting[1];
151959e1947cSXiyu Yang 	if (get_iface_desc(alts)->bNumEndpoints < 1) {
152059e1947cSXiyu Yang 		err = -EINVAL;
152159e1947cSXiyu Yang 		goto end;
152259e1947cSXiyu Yang 	}
1523066624c6SPrzemek Rudy 	ep = get_endpoint(alts, 0)->bEndpointAddress;
1524066624c6SPrzemek Rudy 
1525288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1526288673beSTakashi Iwai 			usb_rcvctrlpipe(chip->dev, 0),
1527066624c6SPrzemek Rudy 			UAC_GET_CUR,
1528066624c6SPrzemek Rudy 			USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
1529066624c6SPrzemek Rudy 			UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
1530066624c6SPrzemek Rudy 			ep,
1531066624c6SPrzemek Rudy 			data,
1532066624c6SPrzemek Rudy 			sizeof(data));
1533066624c6SPrzemek Rudy 	if (err < 0)
1534066624c6SPrzemek Rudy 		goto end;
1535066624c6SPrzemek Rudy 
1536066624c6SPrzemek Rudy 	rate = data[0] | (data[1] << 8) | (data[2] << 16);
1537066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[3] = (rate == 48000) ?
1538066624c6SPrzemek Rudy 			IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
1539066624c6SPrzemek Rudy 
1540066624c6SPrzemek Rudy 	err = 0;
1541066624c6SPrzemek Rudy  end:
154247ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1543066624c6SPrzemek Rudy 	return err;
1544066624c6SPrzemek Rudy }
1545066624c6SPrzemek Rudy 
1546288673beSTakashi Iwai static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
1547066624c6SPrzemek Rudy {
1548288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1549288673beSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
1550066624c6SPrzemek Rudy 	u8 reg;
1551288673beSTakashi Iwai 	int err;
1552066624c6SPrzemek Rudy 
155347ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
155447ab1545STakashi Iwai 	if (err < 0)
155547ab1545STakashi Iwai 		return err;
1556288673beSTakashi Iwai 
1557288673beSTakashi Iwai 	reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
1558288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1559288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1560066624c6SPrzemek Rudy 			UAC_SET_CUR,
1561066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1562066624c6SPrzemek Rudy 			reg,
1563066624c6SPrzemek Rudy 			2,
1564066624c6SPrzemek Rudy 			NULL,
1565066624c6SPrzemek Rudy 			0);
1566066624c6SPrzemek Rudy 	if (err < 0)
1567066624c6SPrzemek Rudy 		goto end;
1568066624c6SPrzemek Rudy 
1569288673beSTakashi Iwai 	reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
1570288673beSTakashi Iwai 	reg |= (pval >> 12) & 0x0f;
1571288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1572288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1573066624c6SPrzemek Rudy 			UAC_SET_CUR,
1574066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1575066624c6SPrzemek Rudy 			reg,
1576066624c6SPrzemek Rudy 			3,
1577066624c6SPrzemek Rudy 			NULL,
1578066624c6SPrzemek Rudy 			0);
1579066624c6SPrzemek Rudy 	if (err < 0)
1580066624c6SPrzemek Rudy 		goto end;
1581066624c6SPrzemek Rudy 
1582288673beSTakashi Iwai  end:
158347ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1584288673beSTakashi Iwai 	return err;
1585288673beSTakashi Iwai }
1586288673beSTakashi Iwai 
1587288673beSTakashi Iwai static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
1588288673beSTakashi Iwai 	struct snd_ctl_elem_value *ucontrol)
1589288673beSTakashi Iwai {
1590288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1591288673beSTakashi Iwai 	unsigned int pval, pval_old;
1592288673beSTakashi Iwai 	int err;
1593288673beSTakashi Iwai 
1594288673beSTakashi Iwai 	pval = pval_old = kcontrol->private_value;
1595288673beSTakashi Iwai 	pval &= 0xfffff0f0;
1596288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
1597288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[0] & 0x0f);
1598288673beSTakashi Iwai 
1599288673beSTakashi Iwai 	pval &= 0xffff0fff;
1600288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
1601066624c6SPrzemek Rudy 
1602066624c6SPrzemek Rudy 	/* The frequency bits in AES3 cannot be set via register access. */
1603066624c6SPrzemek Rudy 
1604066624c6SPrzemek Rudy 	/* Silently ignore any bits from the request that cannot be set. */
1605066624c6SPrzemek Rudy 
1606288673beSTakashi Iwai 	if (pval == pval_old)
1607288673beSTakashi Iwai 		return 0;
1608288673beSTakashi Iwai 
1609288673beSTakashi Iwai 	kcontrol->private_value = pval;
1610288673beSTakashi Iwai 	err = snd_microii_spdif_default_update(list);
1611288673beSTakashi Iwai 	return err < 0 ? err : 1;
1612066624c6SPrzemek Rudy }
1613066624c6SPrzemek Rudy 
1614066624c6SPrzemek Rudy static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
1615066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1616066624c6SPrzemek Rudy {
1617066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[0] = 0x0f;
1618066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[1] = 0xff;
1619066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[2] = 0x00;
1620066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[3] = 0x00;
1621066624c6SPrzemek Rudy 
1622066624c6SPrzemek Rudy 	return 0;
1623066624c6SPrzemek Rudy }
1624066624c6SPrzemek Rudy 
1625066624c6SPrzemek Rudy static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
1626066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1627066624c6SPrzemek Rudy {
1628066624c6SPrzemek Rudy 	ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
1629066624c6SPrzemek Rudy 
1630066624c6SPrzemek Rudy 	return 0;
1631066624c6SPrzemek Rudy }
1632066624c6SPrzemek Rudy 
1633288673beSTakashi Iwai static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
1634066624c6SPrzemek Rudy {
1635288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1636288673beSTakashi Iwai 	u8 reg = list->kctl->private_value;
1637066624c6SPrzemek Rudy 	int err;
1638066624c6SPrzemek Rudy 
163947ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
164047ab1545STakashi Iwai 	if (err < 0)
164147ab1545STakashi Iwai 		return err;
1642288673beSTakashi Iwai 
1643288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1644288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1645066624c6SPrzemek Rudy 			UAC_SET_CUR,
1646066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1647066624c6SPrzemek Rudy 			reg,
1648066624c6SPrzemek Rudy 			9,
1649066624c6SPrzemek Rudy 			NULL,
1650066624c6SPrzemek Rudy 			0);
1651066624c6SPrzemek Rudy 
165247ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1653288673beSTakashi Iwai 	return err;
1654066624c6SPrzemek Rudy }
1655066624c6SPrzemek Rudy 
1656288673beSTakashi Iwai static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
1657288673beSTakashi Iwai 	struct snd_ctl_elem_value *ucontrol)
1658288673beSTakashi Iwai {
1659288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1660288673beSTakashi Iwai 	u8 reg;
1661288673beSTakashi Iwai 	int err;
1662288673beSTakashi Iwai 
1663288673beSTakashi Iwai 	reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
1664288673beSTakashi Iwai 	if (reg != list->kctl->private_value)
1665288673beSTakashi Iwai 		return 0;
1666288673beSTakashi Iwai 
1667288673beSTakashi Iwai 	kcontrol->private_value = reg;
1668288673beSTakashi Iwai 	err = snd_microii_spdif_switch_update(list);
1669288673beSTakashi Iwai 	return err < 0 ? err : 1;
1670066624c6SPrzemek Rudy }
1671066624c6SPrzemek Rudy 
1672195727e8STakashi Iwai static const struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
1673066624c6SPrzemek Rudy 	{
1674066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_PCM,
1675066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
1676066624c6SPrzemek Rudy 		.info =     snd_microii_spdif_info,
1677066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_default_get,
1678066624c6SPrzemek Rudy 		.put =      snd_microii_spdif_default_put,
1679066624c6SPrzemek Rudy 		.private_value = 0x00000100UL,/* reset value */
1680066624c6SPrzemek Rudy 	},
1681066624c6SPrzemek Rudy 	{
1682066624c6SPrzemek Rudy 		.access =   SNDRV_CTL_ELEM_ACCESS_READ,
1683066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_PCM,
1684066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
1685066624c6SPrzemek Rudy 		.info =     snd_microii_spdif_info,
1686066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_mask_get,
1687066624c6SPrzemek Rudy 	},
1688066624c6SPrzemek Rudy 	{
1689066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_MIXER,
1690066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
1691066624c6SPrzemek Rudy 		.info =     snd_ctl_boolean_mono_info,
1692066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_switch_get,
1693066624c6SPrzemek Rudy 		.put =      snd_microii_spdif_switch_put,
1694066624c6SPrzemek Rudy 		.private_value = 0x00000028UL,/* reset value */
1695066624c6SPrzemek Rudy 	}
1696066624c6SPrzemek Rudy };
1697066624c6SPrzemek Rudy 
1698066624c6SPrzemek Rudy static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
1699066624c6SPrzemek Rudy {
1700066624c6SPrzemek Rudy 	int err, i;
1701ff40e0d4SPierre-Louis Bossart 	static const usb_mixer_elem_resume_func_t resume_funcs[] = {
1702288673beSTakashi Iwai 		snd_microii_spdif_default_update,
1703288673beSTakashi Iwai 		NULL,
1704288673beSTakashi Iwai 		snd_microii_spdif_switch_update
1705288673beSTakashi Iwai 	};
1706066624c6SPrzemek Rudy 
1707066624c6SPrzemek Rudy 	for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
1708288673beSTakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
1709288673beSTakashi Iwai 						 resume_funcs[i],
1710288673beSTakashi Iwai 						 &snd_microii_mixer_spdif[i],
1711288673beSTakashi Iwai 						 NULL);
1712066624c6SPrzemek Rudy 		if (err < 0)
1713066624c6SPrzemek Rudy 			return err;
1714066624c6SPrzemek Rudy 	}
1715066624c6SPrzemek Rudy 
171618e4753fSMikulas Patocka 	return 0;
1717066624c6SPrzemek Rudy }
1718066624c6SPrzemek Rudy 
1719388fdb8fSIan Douglas Scott /* Creative Sound Blaster E1 */
1720388fdb8fSIan Douglas Scott 
1721388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_get(struct snd_kcontrol *kcontrol,
1722388fdb8fSIan Douglas Scott 					  struct snd_ctl_elem_value *ucontrol)
1723388fdb8fSIan Douglas Scott {
1724388fdb8fSIan Douglas Scott 	ucontrol->value.integer.value[0] = kcontrol->private_value;
1725388fdb8fSIan Douglas Scott 	return 0;
1726388fdb8fSIan Douglas Scott }
1727388fdb8fSIan Douglas Scott 
1728388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_update(struct usb_mixer_interface *mixer,
1729388fdb8fSIan Douglas Scott 					     unsigned char state)
1730388fdb8fSIan Douglas Scott {
1731388fdb8fSIan Douglas Scott 	struct snd_usb_audio *chip = mixer->chip;
1732388fdb8fSIan Douglas Scott 	int err;
1733388fdb8fSIan Douglas Scott 	unsigned char buff[2];
1734388fdb8fSIan Douglas Scott 
1735388fdb8fSIan Douglas Scott 	buff[0] = 0x02;
1736388fdb8fSIan Douglas Scott 	buff[1] = state ? 0x02 : 0x00;
1737388fdb8fSIan Douglas Scott 
1738388fdb8fSIan Douglas Scott 	err = snd_usb_lock_shutdown(chip);
1739388fdb8fSIan Douglas Scott 	if (err < 0)
1740388fdb8fSIan Douglas Scott 		return err;
1741388fdb8fSIan Douglas Scott 	err = snd_usb_ctl_msg(chip->dev,
1742388fdb8fSIan Douglas Scott 			usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT,
1743388fdb8fSIan Douglas Scott 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
1744388fdb8fSIan Douglas Scott 			0x0202, 3, buff, 2);
1745388fdb8fSIan Douglas Scott 	snd_usb_unlock_shutdown(chip);
1746388fdb8fSIan Douglas Scott 	return err;
1747388fdb8fSIan Douglas Scott }
1748388fdb8fSIan Douglas Scott 
1749388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
1750388fdb8fSIan Douglas Scott 					  struct snd_ctl_elem_value *ucontrol)
1751388fdb8fSIan Douglas Scott {
1752388fdb8fSIan Douglas Scott 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1753388fdb8fSIan Douglas Scott 	unsigned char value = !!ucontrol->value.integer.value[0];
1754388fdb8fSIan Douglas Scott 	int err;
1755388fdb8fSIan Douglas Scott 
1756388fdb8fSIan Douglas Scott 	if (kcontrol->private_value == value)
1757388fdb8fSIan Douglas Scott 		return 0;
1758388fdb8fSIan Douglas Scott 	kcontrol->private_value = value;
1759388fdb8fSIan Douglas Scott 	err = snd_soundblaster_e1_switch_update(list->mixer, value);
1760388fdb8fSIan Douglas Scott 	return err < 0 ? err : 1;
1761388fdb8fSIan Douglas Scott }
1762388fdb8fSIan Douglas Scott 
1763388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list)
1764388fdb8fSIan Douglas Scott {
1765388fdb8fSIan Douglas Scott 	return snd_soundblaster_e1_switch_update(list->mixer,
1766388fdb8fSIan Douglas Scott 						 list->kctl->private_value);
1767388fdb8fSIan Douglas Scott }
1768388fdb8fSIan Douglas Scott 
1769388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_info(struct snd_kcontrol *kcontrol,
1770388fdb8fSIan Douglas Scott 					   struct snd_ctl_elem_info *uinfo)
1771388fdb8fSIan Douglas Scott {
1772388fdb8fSIan Douglas Scott 	static const char *const texts[2] = {
1773388fdb8fSIan Douglas Scott 		"Mic", "Aux"
1774388fdb8fSIan Douglas Scott 	};
1775388fdb8fSIan Douglas Scott 
1776388fdb8fSIan Douglas Scott 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
1777388fdb8fSIan Douglas Scott }
1778388fdb8fSIan Douglas Scott 
1779195727e8STakashi Iwai static const struct snd_kcontrol_new snd_soundblaster_e1_input_switch = {
1780388fdb8fSIan Douglas Scott 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1781388fdb8fSIan Douglas Scott 	.name = "Input Source",
1782388fdb8fSIan Douglas Scott 	.info = snd_soundblaster_e1_switch_info,
1783388fdb8fSIan Douglas Scott 	.get = snd_soundblaster_e1_switch_get,
1784388fdb8fSIan Douglas Scott 	.put = snd_soundblaster_e1_switch_put,
1785388fdb8fSIan Douglas Scott 	.private_value = 0,
1786388fdb8fSIan Douglas Scott };
1787388fdb8fSIan Douglas Scott 
1788388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer)
1789388fdb8fSIan Douglas Scott {
1790388fdb8fSIan Douglas Scott 	return add_single_ctl_with_resume(mixer, 0,
1791388fdb8fSIan Douglas Scott 					  snd_soundblaster_e1_switch_resume,
1792388fdb8fSIan Douglas Scott 					  &snd_soundblaster_e1_input_switch,
1793388fdb8fSIan Douglas Scott 					  NULL);
1794388fdb8fSIan Douglas Scott }
1795388fdb8fSIan Douglas Scott 
1796964af639STakashi Iwai static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
1797964af639STakashi Iwai {
1798964af639STakashi Iwai 	u16 buf = 0;
1799964af639STakashi Iwai 
1800964af639STakashi Iwai 	snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
1801964af639STakashi Iwai 			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
1802964af639STakashi Iwai 			ch, snd_usb_ctrl_intf(chip) | (id << 8),
1803964af639STakashi Iwai 			&buf, 2);
1804964af639STakashi Iwai }
1805964af639STakashi Iwai 
1806964af639STakashi Iwai static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
1807964af639STakashi Iwai {
1808964af639STakashi Iwai 	/* fix to 0dB playback volumes */
1809964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 1, 16);
1810964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 2, 16);
1811964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 1, 19);
1812964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 2, 19);
1813964af639STakashi Iwai 	return 0;
1814964af639STakashi Iwai }
1815964af639STakashi Iwai 
1816d39f1d68SJussi Laako /* RME Class Compliant device quirks */
1817d39f1d68SJussi Laako 
1818d39f1d68SJussi Laako #define SND_RME_GET_STATUS1			23
1819d39f1d68SJussi Laako #define SND_RME_GET_CURRENT_FREQ		17
1820d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM_SHIFT		16
1821d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM_MASK			0x1f
1822d39f1d68SJussi Laako #define SND_RME_CLK_AES_SHIFT			8
1823d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_SHIFT			12
1824d39f1d68SJussi Laako #define SND_RME_CLK_AES_SPDIF_MASK		0xf
1825d39f1d68SJussi Laako #define SND_RME_CLK_SYNC_SHIFT			6
1826d39f1d68SJussi Laako #define SND_RME_CLK_SYNC_MASK			0x3
1827d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL_SHIFT		18
1828d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL_MASK		0x7
1829d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM(x) \
1830d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK)
1831d39f1d68SJussi Laako #define SND_RME_CLK_AES(x) \
1832d39f1d68SJussi Laako 	((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
1833d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF(x) \
1834d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
1835d39f1d68SJussi Laako #define SND_RME_CLK_SYNC(x) \
1836d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK)
1837d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL(x) \
1838d39f1d68SJussi Laako 	((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK)
1839d39f1d68SJussi Laako #define SND_RME_CLK_AES_LOCK			0x1
1840d39f1d68SJussi Laako #define SND_RME_CLK_AES_SYNC			0x4
1841d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_LOCK			0x2
1842d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_SYNC			0x8
1843d39f1d68SJussi Laako #define SND_RME_SPDIF_IF_SHIFT			4
1844d39f1d68SJussi Laako #define SND_RME_SPDIF_FORMAT_SHIFT		5
1845d39f1d68SJussi Laako #define SND_RME_BINARY_MASK			0x1
1846d39f1d68SJussi Laako #define SND_RME_SPDIF_IF(x) \
1847d39f1d68SJussi Laako 	((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK)
1848d39f1d68SJussi Laako #define SND_RME_SPDIF_FORMAT(x) \
1849d39f1d68SJussi Laako 	((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK)
1850d39f1d68SJussi Laako 
1851d39f1d68SJussi Laako static const u32 snd_rme_rate_table[] = {
1852d39f1d68SJussi Laako 	32000, 44100, 48000, 50000,
1853d39f1d68SJussi Laako 	64000, 88200, 96000, 100000,
1854d39f1d68SJussi Laako 	128000, 176400, 192000, 200000,
1855d39f1d68SJussi Laako 	256000,	352800, 384000, 400000,
1856d39f1d68SJussi Laako 	512000, 705600, 768000, 800000
1857d39f1d68SJussi Laako };
1858d39f1d68SJussi Laako /* maximum number of items for AES and S/PDIF rates for above table */
1859d39f1d68SJussi Laako #define SND_RME_RATE_IDX_AES_SPDIF_NUM		12
1860d39f1d68SJussi Laako 
1861d39f1d68SJussi Laako enum snd_rme_domain {
1862d39f1d68SJussi Laako 	SND_RME_DOMAIN_SYSTEM,
1863d39f1d68SJussi Laako 	SND_RME_DOMAIN_AES,
1864d39f1d68SJussi Laako 	SND_RME_DOMAIN_SPDIF
1865d39f1d68SJussi Laako };
1866d39f1d68SJussi Laako 
1867d39f1d68SJussi Laako enum snd_rme_clock_status {
1868d39f1d68SJussi Laako 	SND_RME_CLOCK_NOLOCK,
1869d39f1d68SJussi Laako 	SND_RME_CLOCK_LOCK,
1870d39f1d68SJussi Laako 	SND_RME_CLOCK_SYNC
1871d39f1d68SJussi Laako };
1872d39f1d68SJussi Laako 
1873d39f1d68SJussi Laako static int snd_rme_read_value(struct snd_usb_audio *chip,
1874d39f1d68SJussi Laako 			      unsigned int item,
1875d39f1d68SJussi Laako 			      u32 *value)
1876d39f1d68SJussi Laako {
1877d39f1d68SJussi Laako 	struct usb_device *dev = chip->dev;
1878d39f1d68SJussi Laako 	int err;
1879d39f1d68SJussi Laako 
1880d39f1d68SJussi Laako 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
1881d39f1d68SJussi Laako 			      item,
1882d39f1d68SJussi Laako 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1883d39f1d68SJussi Laako 			      0, 0,
1884d39f1d68SJussi Laako 			      value, sizeof(*value));
1885d39f1d68SJussi Laako 	if (err < 0)
1886d39f1d68SJussi Laako 		dev_err(&dev->dev,
1887d39f1d68SJussi Laako 			"unable to issue vendor read request %d (ret = %d)",
1888d39f1d68SJussi Laako 			item, err);
1889d39f1d68SJussi Laako 	return err;
1890d39f1d68SJussi Laako }
1891d39f1d68SJussi Laako 
1892d39f1d68SJussi Laako static int snd_rme_get_status1(struct snd_kcontrol *kcontrol,
1893d39f1d68SJussi Laako 			       u32 *status1)
1894d39f1d68SJussi Laako {
1895d39f1d68SJussi Laako 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1896d39f1d68SJussi Laako 	struct snd_usb_audio *chip = list->mixer->chip;
1897d39f1d68SJussi Laako 	int err;
1898d39f1d68SJussi Laako 
1899d39f1d68SJussi Laako 	err = snd_usb_lock_shutdown(chip);
1900d39f1d68SJussi Laako 	if (err < 0)
1901d39f1d68SJussi Laako 		return err;
1902d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, status1);
1903d39f1d68SJussi Laako 	snd_usb_unlock_shutdown(chip);
1904d39f1d68SJussi Laako 	return err;
1905d39f1d68SJussi Laako }
1906d39f1d68SJussi Laako 
1907d39f1d68SJussi Laako static int snd_rme_rate_get(struct snd_kcontrol *kcontrol,
1908d39f1d68SJussi Laako 			    struct snd_ctl_elem_value *ucontrol)
1909d39f1d68SJussi Laako {
1910d39f1d68SJussi Laako 	u32 status1;
1911d39f1d68SJussi Laako 	u32 rate = 0;
1912d39f1d68SJussi Laako 	int idx;
1913d39f1d68SJussi Laako 	int err;
1914d39f1d68SJussi Laako 
1915d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
1916d39f1d68SJussi Laako 	if (err < 0)
1917d39f1d68SJussi Laako 		return err;
1918d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
1919d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SYSTEM:
1920d39f1d68SJussi Laako 		idx = SND_RME_CLK_SYSTEM(status1);
1921d39f1d68SJussi Laako 		if (idx < ARRAY_SIZE(snd_rme_rate_table))
1922d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
1923d39f1d68SJussi Laako 		break;
1924d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:
1925d39f1d68SJussi Laako 		idx = SND_RME_CLK_AES(status1);
1926d39f1d68SJussi Laako 		if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
1927d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
1928d39f1d68SJussi Laako 		break;
1929d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:
1930d39f1d68SJussi Laako 		idx = SND_RME_CLK_SPDIF(status1);
1931d39f1d68SJussi Laako 		if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
1932d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
1933d39f1d68SJussi Laako 		break;
1934d39f1d68SJussi Laako 	default:
1935d39f1d68SJussi Laako 		return -EINVAL;
1936d39f1d68SJussi Laako 	}
1937d39f1d68SJussi Laako 	ucontrol->value.integer.value[0] = rate;
1938d39f1d68SJussi Laako 	return 0;
1939d39f1d68SJussi Laako }
1940d39f1d68SJussi Laako 
1941d39f1d68SJussi Laako static int snd_rme_sync_state_get(struct snd_kcontrol *kcontrol,
1942d39f1d68SJussi Laako 				  struct snd_ctl_elem_value *ucontrol)
1943d39f1d68SJussi Laako {
1944d39f1d68SJussi Laako 	u32 status1;
1945d39f1d68SJussi Laako 	int idx = SND_RME_CLOCK_NOLOCK;
1946d39f1d68SJussi Laako 	int err;
1947d39f1d68SJussi Laako 
1948d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
1949d39f1d68SJussi Laako 	if (err < 0)
1950d39f1d68SJussi Laako 		return err;
1951d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
1952d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:  /* AES */
1953d39f1d68SJussi Laako 		if (status1 & SND_RME_CLK_AES_SYNC)
1954d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_SYNC;
1955d39f1d68SJussi Laako 		else if (status1 & SND_RME_CLK_AES_LOCK)
1956d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_LOCK;
1957d39f1d68SJussi Laako 		break;
1958d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:  /* SPDIF */
1959d39f1d68SJussi Laako 		if (status1 & SND_RME_CLK_SPDIF_SYNC)
1960d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_SYNC;
1961d39f1d68SJussi Laako 		else if (status1 & SND_RME_CLK_SPDIF_LOCK)
1962d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_LOCK;
1963d39f1d68SJussi Laako 		break;
1964d39f1d68SJussi Laako 	default:
1965d39f1d68SJussi Laako 		return -EINVAL;
1966d39f1d68SJussi Laako 	}
1967d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = idx;
1968d39f1d68SJussi Laako 	return 0;
1969d39f1d68SJussi Laako }
1970d39f1d68SJussi Laako 
1971d39f1d68SJussi Laako static int snd_rme_spdif_if_get(struct snd_kcontrol *kcontrol,
1972d39f1d68SJussi Laako 				struct snd_ctl_elem_value *ucontrol)
1973d39f1d68SJussi Laako {
1974d39f1d68SJussi Laako 	u32 status1;
1975d39f1d68SJussi Laako 	int err;
1976d39f1d68SJussi Laako 
1977d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
1978d39f1d68SJussi Laako 	if (err < 0)
1979d39f1d68SJussi Laako 		return err;
1980d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_IF(status1);
1981d39f1d68SJussi Laako 	return 0;
1982d39f1d68SJussi Laako }
1983d39f1d68SJussi Laako 
1984d39f1d68SJussi Laako static int snd_rme_spdif_format_get(struct snd_kcontrol *kcontrol,
1985d39f1d68SJussi Laako 				    struct snd_ctl_elem_value *ucontrol)
1986d39f1d68SJussi Laako {
1987d39f1d68SJussi Laako 	u32 status1;
1988d39f1d68SJussi Laako 	int err;
1989d39f1d68SJussi Laako 
1990d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
1991d39f1d68SJussi Laako 	if (err < 0)
1992d39f1d68SJussi Laako 		return err;
1993d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_FORMAT(status1);
1994d39f1d68SJussi Laako 	return 0;
1995d39f1d68SJussi Laako }
1996d39f1d68SJussi Laako 
1997d39f1d68SJussi Laako static int snd_rme_sync_source_get(struct snd_kcontrol *kcontrol,
1998d39f1d68SJussi Laako 				   struct snd_ctl_elem_value *ucontrol)
1999d39f1d68SJussi Laako {
2000d39f1d68SJussi Laako 	u32 status1;
2001d39f1d68SJussi Laako 	int err;
2002d39f1d68SJussi Laako 
2003d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2004d39f1d68SJussi Laako 	if (err < 0)
2005d39f1d68SJussi Laako 		return err;
2006d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_CLK_SYNC(status1);
2007d39f1d68SJussi Laako 	return 0;
2008d39f1d68SJussi Laako }
2009d39f1d68SJussi Laako 
2010d39f1d68SJussi Laako static int snd_rme_current_freq_get(struct snd_kcontrol *kcontrol,
2011d39f1d68SJussi Laako 				    struct snd_ctl_elem_value *ucontrol)
2012d39f1d68SJussi Laako {
2013d39f1d68SJussi Laako 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
2014d39f1d68SJussi Laako 	struct snd_usb_audio *chip = list->mixer->chip;
2015d39f1d68SJussi Laako 	u32 status1;
2016d39f1d68SJussi Laako 	const u64 num = 104857600000000ULL;
2017d39f1d68SJussi Laako 	u32 den;
2018d39f1d68SJussi Laako 	unsigned int freq;
2019d39f1d68SJussi Laako 	int err;
2020d39f1d68SJussi Laako 
2021d39f1d68SJussi Laako 	err = snd_usb_lock_shutdown(chip);
2022d39f1d68SJussi Laako 	if (err < 0)
2023d39f1d68SJussi Laako 		return err;
2024d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, &status1);
2025d39f1d68SJussi Laako 	if (err < 0)
2026d39f1d68SJussi Laako 		goto end;
2027d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_CURRENT_FREQ, &den);
2028d39f1d68SJussi Laako 	if (err < 0)
2029d39f1d68SJussi Laako 		goto end;
2030d39f1d68SJussi Laako 	freq = (den == 0) ? 0 : div64_u64(num, den);
2031d39f1d68SJussi Laako 	freq <<= SND_RME_CLK_FREQMUL(status1);
2032d39f1d68SJussi Laako 	ucontrol->value.integer.value[0] = freq;
2033d39f1d68SJussi Laako 
2034d39f1d68SJussi Laako end:
2035d39f1d68SJussi Laako 	snd_usb_unlock_shutdown(chip);
2036d39f1d68SJussi Laako 	return err;
2037d39f1d68SJussi Laako }
2038d39f1d68SJussi Laako 
2039d39f1d68SJussi Laako static int snd_rme_rate_info(struct snd_kcontrol *kcontrol,
2040d39f1d68SJussi Laako 			     struct snd_ctl_elem_info *uinfo)
2041d39f1d68SJussi Laako {
2042d39f1d68SJussi Laako 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2043d39f1d68SJussi Laako 	uinfo->count = 1;
2044d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2045d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SYSTEM:
2046d39f1d68SJussi Laako 		uinfo->value.integer.min = 32000;
2047d39f1d68SJussi Laako 		uinfo->value.integer.max = 800000;
2048d39f1d68SJussi Laako 		break;
2049d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:
2050d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:
2051d39f1d68SJussi Laako 	default:
2052d39f1d68SJussi Laako 		uinfo->value.integer.min = 0;
2053d39f1d68SJussi Laako 		uinfo->value.integer.max = 200000;
2054d39f1d68SJussi Laako 	}
2055d39f1d68SJussi Laako 	uinfo->value.integer.step = 0;
2056d39f1d68SJussi Laako 	return 0;
2057d39f1d68SJussi Laako }
2058d39f1d68SJussi Laako 
2059d39f1d68SJussi Laako static int snd_rme_sync_state_info(struct snd_kcontrol *kcontrol,
2060d39f1d68SJussi Laako 				   struct snd_ctl_elem_info *uinfo)
2061d39f1d68SJussi Laako {
2062d39f1d68SJussi Laako 	static const char *const sync_states[] = {
2063d39f1d68SJussi Laako 		"No Lock", "Lock", "Sync"
2064d39f1d68SJussi Laako 	};
2065d39f1d68SJussi Laako 
2066d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2067d39f1d68SJussi Laako 				 ARRAY_SIZE(sync_states), sync_states);
2068d39f1d68SJussi Laako }
2069d39f1d68SJussi Laako 
2070d39f1d68SJussi Laako static int snd_rme_spdif_if_info(struct snd_kcontrol *kcontrol,
2071d39f1d68SJussi Laako 				 struct snd_ctl_elem_info *uinfo)
2072d39f1d68SJussi Laako {
2073d39f1d68SJussi Laako 	static const char *const spdif_if[] = {
2074d39f1d68SJussi Laako 		"Coaxial", "Optical"
2075d39f1d68SJussi Laako 	};
2076d39f1d68SJussi Laako 
2077d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2078d39f1d68SJussi Laako 				 ARRAY_SIZE(spdif_if), spdif_if);
2079d39f1d68SJussi Laako }
2080d39f1d68SJussi Laako 
2081d39f1d68SJussi Laako static int snd_rme_spdif_format_info(struct snd_kcontrol *kcontrol,
2082d39f1d68SJussi Laako 				     struct snd_ctl_elem_info *uinfo)
2083d39f1d68SJussi Laako {
2084d39f1d68SJussi Laako 	static const char *const optical_type[] = {
2085d39f1d68SJussi Laako 		"Consumer", "Professional"
2086d39f1d68SJussi Laako 	};
2087d39f1d68SJussi Laako 
2088d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2089d39f1d68SJussi Laako 				 ARRAY_SIZE(optical_type), optical_type);
2090d39f1d68SJussi Laako }
2091d39f1d68SJussi Laako 
2092d39f1d68SJussi Laako static int snd_rme_sync_source_info(struct snd_kcontrol *kcontrol,
2093d39f1d68SJussi Laako 				    struct snd_ctl_elem_info *uinfo)
2094d39f1d68SJussi Laako {
2095d39f1d68SJussi Laako 	static const char *const sync_sources[] = {
2096d39f1d68SJussi Laako 		"Internal", "AES", "SPDIF", "Internal"
2097d39f1d68SJussi Laako 	};
2098d39f1d68SJussi Laako 
2099d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2100d39f1d68SJussi Laako 				 ARRAY_SIZE(sync_sources), sync_sources);
2101d39f1d68SJussi Laako }
2102d39f1d68SJussi Laako 
2103195727e8STakashi Iwai static const struct snd_kcontrol_new snd_rme_controls[] = {
2104d39f1d68SJussi Laako 	{
2105d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2106d39f1d68SJussi Laako 		.name = "AES Rate",
2107d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2108d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2109d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2110d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_AES
2111d39f1d68SJussi Laako 	},
2112d39f1d68SJussi Laako 	{
2113d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2114d39f1d68SJussi Laako 		.name = "AES Sync",
2115d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2116d39f1d68SJussi Laako 		.info = snd_rme_sync_state_info,
2117d39f1d68SJussi Laako 		.get = snd_rme_sync_state_get,
2118d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_AES
2119d39f1d68SJussi Laako 	},
2120d39f1d68SJussi Laako 	{
2121d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2122d39f1d68SJussi Laako 		.name = "SPDIF Rate",
2123d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2124d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2125d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2126d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SPDIF
2127d39f1d68SJussi Laako 	},
2128d39f1d68SJussi Laako 	{
2129d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2130d39f1d68SJussi Laako 		.name = "SPDIF Sync",
2131d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2132d39f1d68SJussi Laako 		.info = snd_rme_sync_state_info,
2133d39f1d68SJussi Laako 		.get = snd_rme_sync_state_get,
2134d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SPDIF
2135d39f1d68SJussi Laako 	},
2136d39f1d68SJussi Laako 	{
2137d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2138d39f1d68SJussi Laako 		.name = "SPDIF Interface",
2139d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2140d39f1d68SJussi Laako 		.info = snd_rme_spdif_if_info,
2141d39f1d68SJussi Laako 		.get = snd_rme_spdif_if_get,
2142d39f1d68SJussi Laako 	},
2143d39f1d68SJussi Laako 	{
2144d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2145d39f1d68SJussi Laako 		.name = "SPDIF Format",
2146d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2147d39f1d68SJussi Laako 		.info = snd_rme_spdif_format_info,
2148d39f1d68SJussi Laako 		.get = snd_rme_spdif_format_get,
2149d39f1d68SJussi Laako 	},
2150d39f1d68SJussi Laako 	{
2151d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2152d39f1d68SJussi Laako 		.name = "Sync Source",
2153d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2154d39f1d68SJussi Laako 		.info = snd_rme_sync_source_info,
2155d39f1d68SJussi Laako 		.get = snd_rme_sync_source_get
2156d39f1d68SJussi Laako 	},
2157d39f1d68SJussi Laako 	{
2158d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2159d39f1d68SJussi Laako 		.name = "System Rate",
2160d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2161d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2162d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2163d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SYSTEM
2164d39f1d68SJussi Laako 	},
2165d39f1d68SJussi Laako 	{
2166d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2167d39f1d68SJussi Laako 		.name = "Current Frequency",
2168d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2169d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2170d39f1d68SJussi Laako 		.get = snd_rme_current_freq_get
2171d39f1d68SJussi Laako 	}
2172d39f1d68SJussi Laako };
2173d39f1d68SJussi Laako 
2174d39f1d68SJussi Laako static int snd_rme_controls_create(struct usb_mixer_interface *mixer)
2175d39f1d68SJussi Laako {
2176d39f1d68SJussi Laako 	int err, i;
2177d39f1d68SJussi Laako 
2178d39f1d68SJussi Laako 	for (i = 0; i < ARRAY_SIZE(snd_rme_controls); ++i) {
2179d39f1d68SJussi Laako 		err = add_single_ctl_with_resume(mixer, 0,
2180d39f1d68SJussi Laako 						 NULL,
2181d39f1d68SJussi Laako 						 &snd_rme_controls[i],
2182d39f1d68SJussi Laako 						 NULL);
2183d39f1d68SJussi Laako 		if (err < 0)
2184d39f1d68SJussi Laako 			return err;
2185d39f1d68SJussi Laako 	}
2186d39f1d68SJussi Laako 
2187d39f1d68SJussi Laako 	return 0;
2188d39f1d68SJussi Laako }
2189d39f1d68SJussi Laako 
21903e8f3bd0SThomas Ebeling /*
21913e8f3bd0SThomas Ebeling  * RME Babyface Pro (FS)
21923e8f3bd0SThomas Ebeling  *
21933e8f3bd0SThomas Ebeling  * These devices exposes a couple of DSP functions via request to EP0.
21943e8f3bd0SThomas Ebeling  * Switches are available via control registers, while routing is controlled
21953e8f3bd0SThomas Ebeling  * by controlling the volume on each possible crossing point.
219647b4f5f5SThomas Ebeling  * Volume control is linear, from -inf (dec. 0) to +6dB (dec. 65536) with
21973e8f3bd0SThomas Ebeling  * 0dB being at dec. 32768.
21983e8f3bd0SThomas Ebeling  */
21993e8f3bd0SThomas Ebeling enum {
22003e8f3bd0SThomas Ebeling 	SND_BBFPRO_CTL_REG1 = 0,
22013e8f3bd0SThomas Ebeling 	SND_BBFPRO_CTL_REG2
22023e8f3bd0SThomas Ebeling };
22033e8f3bd0SThomas Ebeling 
22043e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG_MASK 1
22053e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_IDX_MASK 0xff
22063e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_IDX_SHIFT 1
22073e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_VAL_MASK 1
22083e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_VAL_SHIFT 9
22093e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_CLK_MASTER 0
22103e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_CLK_OPTICAL 1
22113e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_PRO 7
22123e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_EMPH 8
22133e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL 10
22143e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_48V_AN1 0
22153e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_48V_AN2 1
22163e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_SENS_IN3 2
22173e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_SENS_IN4 3
22183e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_PAD_AN1 4
22193e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_PAD_AN2 5
22203e8f3bd0SThomas Ebeling 
22213e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_IDX_MASK 0x1ff
22223e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
22233e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_SHIFT 9
22243e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
222547b4f5f5SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
22263e8f3bd0SThomas Ebeling 
22273e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_CTL_REG1 0x10
22283e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_CTL_REG2 0x17
22293e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_MIXER 0x12
22303e8f3bd0SThomas Ebeling 
22313e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
22323e8f3bd0SThomas Ebeling 				 u8 index, u8 value)
22333e8f3bd0SThomas Ebeling {
22343e8f3bd0SThomas Ebeling 	int err;
22353e8f3bd0SThomas Ebeling 	u16 usb_req, usb_idx, usb_val;
22363e8f3bd0SThomas Ebeling 	struct snd_usb_audio *chip = mixer->chip;
22373e8f3bd0SThomas Ebeling 
22383e8f3bd0SThomas Ebeling 	err = snd_usb_lock_shutdown(chip);
22393e8f3bd0SThomas Ebeling 	if (err < 0)
22403e8f3bd0SThomas Ebeling 		return err;
22413e8f3bd0SThomas Ebeling 
22423e8f3bd0SThomas Ebeling 	if (reg == SND_BBFPRO_CTL_REG1) {
22433e8f3bd0SThomas Ebeling 		usb_req = SND_BBFPRO_USBREQ_CTL_REG1;
22443e8f3bd0SThomas Ebeling 		if (index == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
22453e8f3bd0SThomas Ebeling 			usb_idx = 3;
22463e8f3bd0SThomas Ebeling 			usb_val = value ? 3 : 0;
22473e8f3bd0SThomas Ebeling 		} else {
22483e8f3bd0SThomas Ebeling 			usb_idx = 1 << index;
22493e8f3bd0SThomas Ebeling 			usb_val = value ? usb_idx : 0;
22503e8f3bd0SThomas Ebeling 		}
22513e8f3bd0SThomas Ebeling 	} else {
22523e8f3bd0SThomas Ebeling 		usb_req = SND_BBFPRO_USBREQ_CTL_REG2;
22533e8f3bd0SThomas Ebeling 		usb_idx = 1 << index;
22543e8f3bd0SThomas Ebeling 		usb_val = value ? usb_idx : 0;
22553e8f3bd0SThomas Ebeling 	}
22563e8f3bd0SThomas Ebeling 
22573e8f3bd0SThomas Ebeling 	err = snd_usb_ctl_msg(chip->dev,
22583e8f3bd0SThomas Ebeling 			      usb_sndctrlpipe(chip->dev, 0), usb_req,
22593e8f3bd0SThomas Ebeling 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2260f99e24a6SThomas Ebeling 			      usb_val, usb_idx, NULL, 0);
22613e8f3bd0SThomas Ebeling 
22623e8f3bd0SThomas Ebeling 	snd_usb_unlock_shutdown(chip);
22633e8f3bd0SThomas Ebeling 	return err;
22643e8f3bd0SThomas Ebeling }
22653e8f3bd0SThomas Ebeling 
22663e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol,
22673e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
22683e8f3bd0SThomas Ebeling {
22693e8f3bd0SThomas Ebeling 	u8 reg, idx, val;
22703e8f3bd0SThomas Ebeling 	int pv;
22713e8f3bd0SThomas Ebeling 
22723e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
22733e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
22743e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
22753e8f3bd0SThomas Ebeling 	val = kcontrol->private_value >> SND_BBFPRO_CTL_VAL_SHIFT;
22763e8f3bd0SThomas Ebeling 
22773e8f3bd0SThomas Ebeling 	if ((reg == SND_BBFPRO_CTL_REG1 &&
22783e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
22793e8f3bd0SThomas Ebeling 	    (reg == SND_BBFPRO_CTL_REG2 &&
22803e8f3bd0SThomas Ebeling 	    (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
22813e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
22823e8f3bd0SThomas Ebeling 		ucontrol->value.enumerated.item[0] = val;
22833e8f3bd0SThomas Ebeling 	} else {
22843e8f3bd0SThomas Ebeling 		ucontrol->value.integer.value[0] = val;
22853e8f3bd0SThomas Ebeling 	}
22863e8f3bd0SThomas Ebeling 	return 0;
22873e8f3bd0SThomas Ebeling }
22883e8f3bd0SThomas Ebeling 
22893e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_info(struct snd_kcontrol *kcontrol,
22903e8f3bd0SThomas Ebeling 			       struct snd_ctl_elem_info *uinfo)
22913e8f3bd0SThomas Ebeling {
22923e8f3bd0SThomas Ebeling 	u8 reg, idx;
22933e8f3bd0SThomas Ebeling 	int pv;
22943e8f3bd0SThomas Ebeling 
22953e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
22963e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
22973e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
22983e8f3bd0SThomas Ebeling 
22993e8f3bd0SThomas Ebeling 	if (reg == SND_BBFPRO_CTL_REG1 &&
23003e8f3bd0SThomas Ebeling 	    idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
23013e8f3bd0SThomas Ebeling 		static const char * const texts[2] = {
23023e8f3bd0SThomas Ebeling 			"AutoSync",
23033e8f3bd0SThomas Ebeling 			"Internal"
23043e8f3bd0SThomas Ebeling 		};
23053e8f3bd0SThomas Ebeling 		return snd_ctl_enum_info(uinfo, 1, 2, texts);
23063e8f3bd0SThomas Ebeling 	} else if (reg == SND_BBFPRO_CTL_REG2 &&
23073e8f3bd0SThomas Ebeling 		   (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
23083e8f3bd0SThomas Ebeling 		    idx == SND_BBFPRO_CTL_REG2_SENS_IN4)) {
23093e8f3bd0SThomas Ebeling 		static const char * const texts[2] = {
23103e8f3bd0SThomas Ebeling 			"-10dBV",
23113e8f3bd0SThomas Ebeling 			"+4dBu"
23123e8f3bd0SThomas Ebeling 		};
23133e8f3bd0SThomas Ebeling 		return snd_ctl_enum_info(uinfo, 1, 2, texts);
23143e8f3bd0SThomas Ebeling 	}
23153e8f3bd0SThomas Ebeling 
23163e8f3bd0SThomas Ebeling 	uinfo->count = 1;
23173e8f3bd0SThomas Ebeling 	uinfo->value.integer.min = 0;
23183e8f3bd0SThomas Ebeling 	uinfo->value.integer.max = 1;
23193e8f3bd0SThomas Ebeling 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
23203e8f3bd0SThomas Ebeling 	return 0;
23213e8f3bd0SThomas Ebeling }
23223e8f3bd0SThomas Ebeling 
23233e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol,
23243e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
23253e8f3bd0SThomas Ebeling {
23263e8f3bd0SThomas Ebeling 	int err;
23273e8f3bd0SThomas Ebeling 	u8 reg, idx;
23283e8f3bd0SThomas Ebeling 	int old_value, pv, val;
23293e8f3bd0SThomas Ebeling 
23303e8f3bd0SThomas Ebeling 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
23313e8f3bd0SThomas Ebeling 	struct usb_mixer_interface *mixer = list->mixer;
23323e8f3bd0SThomas Ebeling 
23333e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
23343e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
23353e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
23363e8f3bd0SThomas Ebeling 	old_value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
23373e8f3bd0SThomas Ebeling 
23383e8f3bd0SThomas Ebeling 	if ((reg == SND_BBFPRO_CTL_REG1 &&
23393e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
23403e8f3bd0SThomas Ebeling 	    (reg == SND_BBFPRO_CTL_REG2 &&
23413e8f3bd0SThomas Ebeling 	    (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
23423e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
23433e8f3bd0SThomas Ebeling 		val = ucontrol->value.enumerated.item[0];
23443e8f3bd0SThomas Ebeling 	} else {
23453e8f3bd0SThomas Ebeling 		val = ucontrol->value.integer.value[0];
23463e8f3bd0SThomas Ebeling 	}
23473e8f3bd0SThomas Ebeling 
23483e8f3bd0SThomas Ebeling 	if (val > 1)
23493e8f3bd0SThomas Ebeling 		return -EINVAL;
23503e8f3bd0SThomas Ebeling 
23513e8f3bd0SThomas Ebeling 	if (val == old_value)
23523e8f3bd0SThomas Ebeling 		return 0;
23533e8f3bd0SThomas Ebeling 
23543e8f3bd0SThomas Ebeling 	kcontrol->private_value = reg
23553e8f3bd0SThomas Ebeling 		| ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT)
23563e8f3bd0SThomas Ebeling 		| ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT);
23573e8f3bd0SThomas Ebeling 
23583e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
23593e8f3bd0SThomas Ebeling 	return err < 0 ? err : 1;
23603e8f3bd0SThomas Ebeling }
23613e8f3bd0SThomas Ebeling 
23623e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
23633e8f3bd0SThomas Ebeling {
23643e8f3bd0SThomas Ebeling 	u8 reg, idx;
23653e8f3bd0SThomas Ebeling 	int value, pv;
23663e8f3bd0SThomas Ebeling 
23673e8f3bd0SThomas Ebeling 	pv = list->kctl->private_value;
23683e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
23693e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
23703e8f3bd0SThomas Ebeling 	value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
23713e8f3bd0SThomas Ebeling 
23723e8f3bd0SThomas Ebeling 	return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
23733e8f3bd0SThomas Ebeling }
23743e8f3bd0SThomas Ebeling 
23753e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
23763e8f3bd0SThomas Ebeling 				 u32 value)
23773e8f3bd0SThomas Ebeling {
23783e8f3bd0SThomas Ebeling 	struct snd_usb_audio *chip = mixer->chip;
23793e8f3bd0SThomas Ebeling 	int err;
23803e8f3bd0SThomas Ebeling 	u16 idx;
23813e8f3bd0SThomas Ebeling 	u16 usb_idx, usb_val;
23823e8f3bd0SThomas Ebeling 	u32 v;
23833e8f3bd0SThomas Ebeling 
23843e8f3bd0SThomas Ebeling 	err = snd_usb_lock_shutdown(chip);
23853e8f3bd0SThomas Ebeling 	if (err < 0)
23863e8f3bd0SThomas Ebeling 		return err;
23873e8f3bd0SThomas Ebeling 
23883e8f3bd0SThomas Ebeling 	idx = index & SND_BBFPRO_MIXER_IDX_MASK;
23893e8f3bd0SThomas Ebeling 	// 18 bit linear volume, split so 2 bits end up in index.
23903e8f3bd0SThomas Ebeling 	v = value & SND_BBFPRO_MIXER_VAL_MASK;
23913e8f3bd0SThomas Ebeling 	usb_idx = idx | (v & 0x3) << 14;
23923e8f3bd0SThomas Ebeling 	usb_val = (v >> 2) & 0xffff;
23933e8f3bd0SThomas Ebeling 
23943e8f3bd0SThomas Ebeling 	err = snd_usb_ctl_msg(chip->dev,
23953e8f3bd0SThomas Ebeling 			      usb_sndctrlpipe(chip->dev, 0),
23963e8f3bd0SThomas Ebeling 			      SND_BBFPRO_USBREQ_MIXER,
23973e8f3bd0SThomas Ebeling 			      USB_DIR_OUT | USB_TYPE_VENDOR |
23983e8f3bd0SThomas Ebeling 			      USB_RECIP_DEVICE,
2399f99e24a6SThomas Ebeling 			      usb_val, usb_idx, NULL, 0);
24003e8f3bd0SThomas Ebeling 
24013e8f3bd0SThomas Ebeling 	snd_usb_unlock_shutdown(chip);
24023e8f3bd0SThomas Ebeling 	return err;
24033e8f3bd0SThomas Ebeling }
24043e8f3bd0SThomas Ebeling 
24053e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol,
24063e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
24073e8f3bd0SThomas Ebeling {
24083e8f3bd0SThomas Ebeling 	ucontrol->value.integer.value[0] =
24093e8f3bd0SThomas Ebeling 		kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
24103e8f3bd0SThomas Ebeling 	return 0;
24113e8f3bd0SThomas Ebeling }
24123e8f3bd0SThomas Ebeling 
24133e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_info(struct snd_kcontrol *kcontrol,
24143e8f3bd0SThomas Ebeling 			       struct snd_ctl_elem_info *uinfo)
24153e8f3bd0SThomas Ebeling {
24163e8f3bd0SThomas Ebeling 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
24173e8f3bd0SThomas Ebeling 	uinfo->count = 1;
24183e8f3bd0SThomas Ebeling 	uinfo->value.integer.min = SND_BBFPRO_MIXER_VAL_MIN;
24193e8f3bd0SThomas Ebeling 	uinfo->value.integer.max = SND_BBFPRO_MIXER_VAL_MAX;
24203e8f3bd0SThomas Ebeling 	return 0;
24213e8f3bd0SThomas Ebeling }
24223e8f3bd0SThomas Ebeling 
24233e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol,
24243e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
24253e8f3bd0SThomas Ebeling {
24263e8f3bd0SThomas Ebeling 	int err;
24273e8f3bd0SThomas Ebeling 	u16 idx;
24283e8f3bd0SThomas Ebeling 	u32 new_val, old_value, uvalue;
24293e8f3bd0SThomas Ebeling 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
24303e8f3bd0SThomas Ebeling 	struct usb_mixer_interface *mixer = list->mixer;
24313e8f3bd0SThomas Ebeling 
24323e8f3bd0SThomas Ebeling 	uvalue = ucontrol->value.integer.value[0];
24333e8f3bd0SThomas Ebeling 	idx = kcontrol->private_value & SND_BBFPRO_MIXER_IDX_MASK;
24343e8f3bd0SThomas Ebeling 	old_value = kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
24353e8f3bd0SThomas Ebeling 
24363e8f3bd0SThomas Ebeling 	if (uvalue > SND_BBFPRO_MIXER_VAL_MAX)
24373e8f3bd0SThomas Ebeling 		return -EINVAL;
24383e8f3bd0SThomas Ebeling 
24393e8f3bd0SThomas Ebeling 	if (uvalue == old_value)
24403e8f3bd0SThomas Ebeling 		return 0;
24413e8f3bd0SThomas Ebeling 
24423e8f3bd0SThomas Ebeling 	new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK;
24433e8f3bd0SThomas Ebeling 
24443e8f3bd0SThomas Ebeling 	kcontrol->private_value = idx
24453e8f3bd0SThomas Ebeling 		| (new_val << SND_BBFPRO_MIXER_VAL_SHIFT);
24463e8f3bd0SThomas Ebeling 
24473e8f3bd0SThomas Ebeling 	err = snd_bbfpro_vol_update(mixer, idx, new_val);
24483e8f3bd0SThomas Ebeling 	return err < 0 ? err : 1;
24493e8f3bd0SThomas Ebeling }
24503e8f3bd0SThomas Ebeling 
24513e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list)
24523e8f3bd0SThomas Ebeling {
24533e8f3bd0SThomas Ebeling 	int pv = list->kctl->private_value;
24543e8f3bd0SThomas Ebeling 	u16 idx = pv & SND_BBFPRO_MIXER_IDX_MASK;
24553e8f3bd0SThomas Ebeling 	u32 val = (pv >> SND_BBFPRO_MIXER_VAL_SHIFT)
24563e8f3bd0SThomas Ebeling 		& SND_BBFPRO_MIXER_VAL_MASK;
24573e8f3bd0SThomas Ebeling 	return snd_bbfpro_vol_update(list->mixer, idx, val);
24583e8f3bd0SThomas Ebeling }
24593e8f3bd0SThomas Ebeling 
24603e8f3bd0SThomas Ebeling // Predfine elements
24613e8f3bd0SThomas Ebeling static const struct snd_kcontrol_new snd_bbfpro_ctl_control = {
24623e8f3bd0SThomas Ebeling 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24633e8f3bd0SThomas Ebeling 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
24643e8f3bd0SThomas Ebeling 	.index = 0,
24653e8f3bd0SThomas Ebeling 	.info = snd_bbfpro_ctl_info,
24663e8f3bd0SThomas Ebeling 	.get = snd_bbfpro_ctl_get,
24673e8f3bd0SThomas Ebeling 	.put = snd_bbfpro_ctl_put
24683e8f3bd0SThomas Ebeling };
24693e8f3bd0SThomas Ebeling 
24703e8f3bd0SThomas Ebeling static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
24713e8f3bd0SThomas Ebeling 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24723e8f3bd0SThomas Ebeling 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
24733e8f3bd0SThomas Ebeling 	.index = 0,
24743e8f3bd0SThomas Ebeling 	.info = snd_bbfpro_vol_info,
24753e8f3bd0SThomas Ebeling 	.get = snd_bbfpro_vol_get,
24763e8f3bd0SThomas Ebeling 	.put = snd_bbfpro_vol_put
24773e8f3bd0SThomas Ebeling };
24783e8f3bd0SThomas Ebeling 
24793e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg,
24803e8f3bd0SThomas Ebeling 			      u8 index, char *name)
24813e8f3bd0SThomas Ebeling {
24823e8f3bd0SThomas Ebeling 	struct snd_kcontrol_new knew = snd_bbfpro_ctl_control;
24833e8f3bd0SThomas Ebeling 
24843e8f3bd0SThomas Ebeling 	knew.name = name;
24853e8f3bd0SThomas Ebeling 	knew.private_value = (reg & SND_BBFPRO_CTL_REG_MASK)
24863e8f3bd0SThomas Ebeling 		| ((index & SND_BBFPRO_CTL_IDX_MASK)
24873e8f3bd0SThomas Ebeling 			<< SND_BBFPRO_CTL_IDX_SHIFT);
24883e8f3bd0SThomas Ebeling 
24893e8f3bd0SThomas Ebeling 	return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume,
24903e8f3bd0SThomas Ebeling 		&knew, NULL);
24913e8f3bd0SThomas Ebeling }
24923e8f3bd0SThomas Ebeling 
24933e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
24943e8f3bd0SThomas Ebeling 			      char *name)
24953e8f3bd0SThomas Ebeling {
24963e8f3bd0SThomas Ebeling 	struct snd_kcontrol_new knew = snd_bbfpro_vol_control;
24973e8f3bd0SThomas Ebeling 
24983e8f3bd0SThomas Ebeling 	knew.name = name;
24993e8f3bd0SThomas Ebeling 	knew.private_value = index & SND_BBFPRO_MIXER_IDX_MASK;
25003e8f3bd0SThomas Ebeling 
25013e8f3bd0SThomas Ebeling 	return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_vol_resume,
25023e8f3bd0SThomas Ebeling 		&knew, NULL);
25033e8f3bd0SThomas Ebeling }
25043e8f3bd0SThomas Ebeling 
25053e8f3bd0SThomas Ebeling static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
25063e8f3bd0SThomas Ebeling {
25073e8f3bd0SThomas Ebeling 	int err, i, o;
25083e8f3bd0SThomas Ebeling 	char name[48];
25093e8f3bd0SThomas Ebeling 
25103e8f3bd0SThomas Ebeling 	static const char * const input[] = {
25113e8f3bd0SThomas Ebeling 		"AN1", "AN2", "IN3", "IN4", "AS1", "AS2", "ADAT3",
25123e8f3bd0SThomas Ebeling 		"ADAT4", "ADAT5", "ADAT6", "ADAT7", "ADAT8"};
25133e8f3bd0SThomas Ebeling 
25143e8f3bd0SThomas Ebeling 	static const char * const output[] = {
25153e8f3bd0SThomas Ebeling 		"AN1", "AN2", "PH3", "PH4", "AS1", "AS2", "ADAT3", "ADAT4",
25163e8f3bd0SThomas Ebeling 		"ADAT5", "ADAT6", "ADAT7", "ADAT8"};
25173e8f3bd0SThomas Ebeling 
25183e8f3bd0SThomas Ebeling 	for (o = 0 ; o < 12 ; ++o) {
25193e8f3bd0SThomas Ebeling 		for (i = 0 ; i < 12 ; ++i) {
25203e8f3bd0SThomas Ebeling 			// Line routing
25213e8f3bd0SThomas Ebeling 			snprintf(name, sizeof(name),
25223e8f3bd0SThomas Ebeling 				 "%s-%s-%s Playback Volume",
25233e8f3bd0SThomas Ebeling 				 (i < 2 ? "Mic" : "Line"),
25243e8f3bd0SThomas Ebeling 				 input[i], output[o]);
25253e8f3bd0SThomas Ebeling 			err = snd_bbfpro_vol_add(mixer, (26 * o + i), name);
25263e8f3bd0SThomas Ebeling 			if (err < 0)
25273e8f3bd0SThomas Ebeling 				return err;
25283e8f3bd0SThomas Ebeling 
25293e8f3bd0SThomas Ebeling 			// PCM routing... yes, it is output remapping
25303e8f3bd0SThomas Ebeling 			snprintf(name, sizeof(name),
25313e8f3bd0SThomas Ebeling 				 "PCM-%s-%s Playback Volume",
25323e8f3bd0SThomas Ebeling 				 output[i], output[o]);
25333e8f3bd0SThomas Ebeling 			err = snd_bbfpro_vol_add(mixer, (26 * o + 12 + i),
25343e8f3bd0SThomas Ebeling 						 name);
25353e8f3bd0SThomas Ebeling 			if (err < 0)
25363e8f3bd0SThomas Ebeling 				return err;
25373e8f3bd0SThomas Ebeling 		}
25383e8f3bd0SThomas Ebeling 	}
25393e8f3bd0SThomas Ebeling 
25403e8f3bd0SThomas Ebeling 	// Control Reg 1
25413e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
25423e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
25433e8f3bd0SThomas Ebeling 				 "Sample Clock Source");
25443e8f3bd0SThomas Ebeling 	if (err < 0)
25453e8f3bd0SThomas Ebeling 		return err;
25463e8f3bd0SThomas Ebeling 
25473e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
25483e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_PRO,
25493e8f3bd0SThomas Ebeling 				 "IEC958 Pro Mask");
25503e8f3bd0SThomas Ebeling 	if (err < 0)
25513e8f3bd0SThomas Ebeling 		return err;
25523e8f3bd0SThomas Ebeling 
25533e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
25543e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_EMPH,
25553e8f3bd0SThomas Ebeling 				 "IEC958 Emphasis");
25563e8f3bd0SThomas Ebeling 	if (err < 0)
25573e8f3bd0SThomas Ebeling 		return err;
25583e8f3bd0SThomas Ebeling 
25593e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
25603e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL,
25613e8f3bd0SThomas Ebeling 				 "IEC958 Switch");
25623e8f3bd0SThomas Ebeling 	if (err < 0)
25633e8f3bd0SThomas Ebeling 		return err;
25643e8f3bd0SThomas Ebeling 
25653e8f3bd0SThomas Ebeling 	// Control Reg 2
25663e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
25673e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_48V_AN1,
25683e8f3bd0SThomas Ebeling 				 "Mic-AN1 48V");
25693e8f3bd0SThomas Ebeling 	if (err < 0)
25703e8f3bd0SThomas Ebeling 		return err;
25713e8f3bd0SThomas Ebeling 
25723e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
25733e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_48V_AN2,
25743e8f3bd0SThomas Ebeling 				 "Mic-AN2 48V");
25753e8f3bd0SThomas Ebeling 	if (err < 0)
25763e8f3bd0SThomas Ebeling 		return err;
25773e8f3bd0SThomas Ebeling 
25783e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
25793e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_SENS_IN3,
25803e8f3bd0SThomas Ebeling 				 "Line-IN3 Sens.");
25813e8f3bd0SThomas Ebeling 	if (err < 0)
25823e8f3bd0SThomas Ebeling 		return err;
25833e8f3bd0SThomas Ebeling 
25843e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
25853e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_SENS_IN4,
25863e8f3bd0SThomas Ebeling 				 "Line-IN4 Sens.");
25873e8f3bd0SThomas Ebeling 	if (err < 0)
25883e8f3bd0SThomas Ebeling 		return err;
25893e8f3bd0SThomas Ebeling 
25903e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
25913e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_PAD_AN1,
25923e8f3bd0SThomas Ebeling 				 "Mic-AN1 PAD");
25933e8f3bd0SThomas Ebeling 	if (err < 0)
25943e8f3bd0SThomas Ebeling 		return err;
25953e8f3bd0SThomas Ebeling 
25963e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
25973e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_PAD_AN2,
25983e8f3bd0SThomas Ebeling 				 "Mic-AN2 PAD");
25993e8f3bd0SThomas Ebeling 	if (err < 0)
26003e8f3bd0SThomas Ebeling 		return err;
26013e8f3bd0SThomas Ebeling 
26023e8f3bd0SThomas Ebeling 	return 0;
26033e8f3bd0SThomas Ebeling }
26043e8f3bd0SThomas Ebeling 
2605cdc01a15SFrantišek Kučera /*
2606a07df82cSOlivia Mackintosh  * Pioneer DJ DJM Mixers
2607cdc01a15SFrantišek Kučera  *
2608a07df82cSOlivia Mackintosh  * These devices generally have options for soft-switching the playback and
2609a07df82cSOlivia Mackintosh  * capture sources in addition to the recording level. Although different
2610a07df82cSOlivia Mackintosh  * devices have different configurations, there seems to be canonical values
2611a07df82cSOlivia Mackintosh  * for specific capture/playback types:  See the definitions of these below.
2612cdc01a15SFrantišek Kučera  *
2613a07df82cSOlivia Mackintosh  * The wValue is masked with the stereo channel number. e.g. Setting Ch2 to
2614a07df82cSOlivia Mackintosh  * capture phono would be 0x0203. Capture, playback and capture level have
2615a07df82cSOlivia Mackintosh  * different wIndexes.
2616cdc01a15SFrantišek Kučera  */
2617cdc01a15SFrantišek Kučera 
2618a07df82cSOlivia Mackintosh // Capture types
2619a07df82cSOlivia Mackintosh #define SND_DJM_CAP_LINE	0x00
2620a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CDLINE	0x01
2621fee03efcSFabian Lesniak #define SND_DJM_CAP_DIGITAL	0x02
2622a07df82cSOlivia Mackintosh #define SND_DJM_CAP_PHONO	0x03
2623a07df82cSOlivia Mackintosh #define SND_DJM_CAP_PFADER	0x06
2624a07df82cSOlivia Mackintosh #define SND_DJM_CAP_XFADERA	0x07
2625a07df82cSOlivia Mackintosh #define SND_DJM_CAP_XFADERB	0x08
2626a07df82cSOlivia Mackintosh #define SND_DJM_CAP_MIC		0x09
2627a07df82cSOlivia Mackintosh #define SND_DJM_CAP_AUX		0x0d
2628a07df82cSOlivia Mackintosh #define SND_DJM_CAP_RECOUT	0x0a
2629a07df82cSOlivia Mackintosh #define SND_DJM_CAP_NONE	0x0f
2630a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CH1PFADER	0x11
2631a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CH2PFADER	0x12
2632fee03efcSFabian Lesniak #define SND_DJM_CAP_CH3PFADER	0x13
2633fee03efcSFabian Lesniak #define SND_DJM_CAP_CH4PFADER	0x14
2634cdc01a15SFrantišek Kučera 
2635a07df82cSOlivia Mackintosh // Playback types
2636a07df82cSOlivia Mackintosh #define SND_DJM_PB_CH1		0x00
2637a07df82cSOlivia Mackintosh #define SND_DJM_PB_CH2		0x01
2638a07df82cSOlivia Mackintosh #define SND_DJM_PB_AUX		0x04
2639cdc01a15SFrantišek Kučera 
2640a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_CAP	0x8002
2641a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_CAPLVL	0x8003
2642a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_PB	0x8016
2643cdc01a15SFrantišek Kučera 
2644a07df82cSOlivia Mackintosh // kcontrol->private_value layout
2645a07df82cSOlivia Mackintosh #define SND_DJM_VALUE_MASK	0x0000ffff
2646a07df82cSOlivia Mackintosh #define SND_DJM_GROUP_MASK	0x00ff0000
2647a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE_MASK	0xff000000
2648a07df82cSOlivia Mackintosh #define SND_DJM_GROUP_SHIFT	16
2649a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE_SHIFT	24
2650cdc01a15SFrantišek Kučera 
2651a07df82cSOlivia Mackintosh // device table index
26527687850bSNicolas MURE // used for the snd_djm_devices table, so please update accordingly
2653a07df82cSOlivia Mackintosh #define SND_DJM_250MK2_IDX	0x0
2654a07df82cSOlivia Mackintosh #define SND_DJM_750_IDX		0x1
26557687850bSNicolas MURE #define SND_DJM_850_IDX		0x2
26567687850bSNicolas MURE #define SND_DJM_900NXS2_IDX	0x3
2657cdc01a15SFrantišek Kučera 
2658cdc01a15SFrantišek Kučera 
2659a07df82cSOlivia Mackintosh #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \
2660cdc01a15SFrantišek Kučera 	.name = _name, \
2661a07df82cSOlivia Mackintosh 	.options = snd_djm_opts_##suffix, \
2662a07df82cSOlivia Mackintosh 	.noptions = ARRAY_SIZE(snd_djm_opts_##suffix), \
2663a07df82cSOlivia Mackintosh 	.default_value = _default_value, \
2664a07df82cSOlivia Mackintosh 	.wIndex = _windex }
2665cdc01a15SFrantišek Kučera 
2666a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE(suffix) { \
2667a07df82cSOlivia Mackintosh 	.controls = snd_djm_ctls_##suffix, \
2668a07df82cSOlivia Mackintosh 	.ncontrols = ARRAY_SIZE(snd_djm_ctls_##suffix) }
2669a07df82cSOlivia Mackintosh 
2670a07df82cSOlivia Mackintosh 
2671a07df82cSOlivia Mackintosh struct snd_djm_device {
2672a07df82cSOlivia Mackintosh 	const char *name;
2673a07df82cSOlivia Mackintosh 	const struct snd_djm_ctl *controls;
2674a07df82cSOlivia Mackintosh 	size_t ncontrols;
2675cdc01a15SFrantišek Kučera };
2676cdc01a15SFrantišek Kučera 
2677a07df82cSOlivia Mackintosh struct snd_djm_ctl {
2678cdc01a15SFrantišek Kučera 	const char *name;
2679a07df82cSOlivia Mackintosh 	const u16 *options;
2680a07df82cSOlivia Mackintosh 	size_t noptions;
2681a07df82cSOlivia Mackintosh 	u16 default_value;
2682a07df82cSOlivia Mackintosh 	u16 wIndex;
2683a07df82cSOlivia Mackintosh };
2684cdc01a15SFrantišek Kučera 
2685a07df82cSOlivia Mackintosh static const char *snd_djm_get_label_caplevel(u16 wvalue)
2686a07df82cSOlivia Mackintosh {
2687a07df82cSOlivia Mackintosh 	switch (wvalue) {
2688a07df82cSOlivia Mackintosh 	case 0x0000:	return "-19dB";
2689a07df82cSOlivia Mackintosh 	case 0x0100:	return "-15dB";
2690a07df82cSOlivia Mackintosh 	case 0x0200:	return "-10dB";
2691a07df82cSOlivia Mackintosh 	case 0x0300:	return "-5dB";
2692a07df82cSOlivia Mackintosh 	default:	return NULL;
2693a07df82cSOlivia Mackintosh 	}
2694a07df82cSOlivia Mackintosh };
2695a07df82cSOlivia Mackintosh 
2696b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap_common(u16 wvalue)
2697a07df82cSOlivia Mackintosh {
2698a07df82cSOlivia Mackintosh 	switch (wvalue & 0x00ff) {
2699a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_LINE:		return "Control Tone LINE";
2700a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CDLINE:	return "Control Tone CD/LINE";
2701fee03efcSFabian Lesniak 	case SND_DJM_CAP_DIGITAL:	return "Control Tone DIGITAL";
2702a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_PHONO:		return "Control Tone PHONO";
2703a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_PFADER:	return "Post Fader";
2704a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_XFADERA:	return "Cross Fader A";
2705a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_XFADERB:	return "Cross Fader B";
2706a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_MIC:		return "Mic";
2707a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_RECOUT:	return "Rec Out";
2708a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_AUX:		return "Aux";
2709a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_NONE:		return "None";
2710a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CH1PFADER:	return "Post Fader Ch1";
2711a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CH2PFADER:	return "Post Fader Ch2";
2712fee03efcSFabian Lesniak 	case SND_DJM_CAP_CH3PFADER:	return "Post Fader Ch3";
2713fee03efcSFabian Lesniak 	case SND_DJM_CAP_CH4PFADER:	return "Post Fader Ch4";
2714a07df82cSOlivia Mackintosh 	default:			return NULL;
2715a07df82cSOlivia Mackintosh 	}
2716a07df82cSOlivia Mackintosh };
2717a07df82cSOlivia Mackintosh 
2718b8db8be8SNicolas MURE // The DJM-850 has different values for CD/LINE and LINE capture
2719b8db8be8SNicolas MURE // control options than the other DJM declared in this file.
2720b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap_850(u16 wvalue)
2721b8db8be8SNicolas MURE {
2722b8db8be8SNicolas MURE 	switch (wvalue & 0x00ff) {
2723b8db8be8SNicolas MURE 	case 0x00:		return "Control Tone CD/LINE";
2724b8db8be8SNicolas MURE 	case 0x01:		return "Control Tone LINE";
2725b8db8be8SNicolas MURE 	default:		return snd_djm_get_label_cap_common(wvalue);
2726b8db8be8SNicolas MURE 	}
2727b8db8be8SNicolas MURE };
2728b8db8be8SNicolas MURE 
2729b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap(u8 device_idx, u16 wvalue)
2730b8db8be8SNicolas MURE {
2731b8db8be8SNicolas MURE 	switch (device_idx) {
2732b8db8be8SNicolas MURE 	case SND_DJM_850_IDX:		return snd_djm_get_label_cap_850(wvalue);
2733b8db8be8SNicolas MURE 	default:			return snd_djm_get_label_cap_common(wvalue);
2734b8db8be8SNicolas MURE 	}
2735b8db8be8SNicolas MURE };
2736b8db8be8SNicolas MURE 
2737a07df82cSOlivia Mackintosh static const char *snd_djm_get_label_pb(u16 wvalue)
2738a07df82cSOlivia Mackintosh {
2739a07df82cSOlivia Mackintosh 	switch (wvalue & 0x00ff) {
2740a07df82cSOlivia Mackintosh 	case SND_DJM_PB_CH1:	return "Ch1";
2741a07df82cSOlivia Mackintosh 	case SND_DJM_PB_CH2:	return "Ch2";
2742a07df82cSOlivia Mackintosh 	case SND_DJM_PB_AUX:	return "Aux";
2743a07df82cSOlivia Mackintosh 	default:		return NULL;
2744a07df82cSOlivia Mackintosh 	}
2745a07df82cSOlivia Mackintosh };
2746a07df82cSOlivia Mackintosh 
2747b8db8be8SNicolas MURE static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex)
2748a07df82cSOlivia Mackintosh {
2749a07df82cSOlivia Mackintosh 	switch (windex) {
2750a07df82cSOlivia Mackintosh 	case SND_DJM_WINDEX_CAPLVL:	return snd_djm_get_label_caplevel(wvalue);
2751b8db8be8SNicolas MURE 	case SND_DJM_WINDEX_CAP:	return snd_djm_get_label_cap(device_idx, wvalue);
2752a07df82cSOlivia Mackintosh 	case SND_DJM_WINDEX_PB:		return snd_djm_get_label_pb(wvalue);
2753a07df82cSOlivia Mackintosh 	default:			return NULL;
2754a07df82cSOlivia Mackintosh 	}
2755a07df82cSOlivia Mackintosh };
2756a07df82cSOlivia Mackintosh 
27577687850bSNicolas MURE // common DJM capture level option values
2758a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_cap_level[] = {
2759a07df82cSOlivia Mackintosh 	0x0000, 0x0100, 0x0200, 0x0300 };
2760a07df82cSOlivia Mackintosh 
27617687850bSNicolas MURE 
27627687850bSNicolas MURE // DJM-250MK2
2763a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap1[] = {
2764a07df82cSOlivia Mackintosh 	0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a };
2765a07df82cSOlivia Mackintosh 
2766a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap2[] = {
2767a07df82cSOlivia Mackintosh 	0x0203, 0x0200, 0x0206, 0x0207, 0x0208, 0x0209, 0x020d, 0x020a };
2768a07df82cSOlivia Mackintosh 
2769a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap3[] = {
2770a07df82cSOlivia Mackintosh 	0x030a, 0x0311, 0x0312, 0x0307, 0x0308, 0x0309, 0x030d };
2771a07df82cSOlivia Mackintosh 
2772a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
2773a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
2774a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
2775a07df82cSOlivia Mackintosh 
2776a07df82cSOlivia Mackintosh static const struct snd_djm_ctl snd_djm_ctls_250mk2[] = {
2777a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
2778a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Input",   250mk2_cap1, 2, SND_DJM_WINDEX_CAP),
2779a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Input",   250mk2_cap2, 2, SND_DJM_WINDEX_CAP),
2780a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Input",   250mk2_cap3, 0, SND_DJM_WINDEX_CAP),
2781a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Output",   250mk2_pb1, 0, SND_DJM_WINDEX_PB),
2782a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Output",   250mk2_pb2, 1, SND_DJM_WINDEX_PB),
2783a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Output",   250mk2_pb3, 2, SND_DJM_WINDEX_PB)
2784a07df82cSOlivia Mackintosh };
2785a07df82cSOlivia Mackintosh 
2786a07df82cSOlivia Mackintosh 
2787a07df82cSOlivia Mackintosh // DJM-750
2788a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap1[] = {
2789a07df82cSOlivia Mackintosh 	0x0101, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
2790a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap2[] = {
2791a07df82cSOlivia Mackintosh 	0x0200, 0x0201, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020f };
2792a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap3[] = {
2793a07df82cSOlivia Mackintosh 	0x0300, 0x0301, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030f };
2794a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap4[] = {
2795a07df82cSOlivia Mackintosh 	0x0401, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
2796a07df82cSOlivia Mackintosh 
2797a07df82cSOlivia Mackintosh static const struct snd_djm_ctl snd_djm_ctls_750[] = {
2798a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
2799a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Input",   750_cap1, 2, SND_DJM_WINDEX_CAP),
2800a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Input",   750_cap2, 2, SND_DJM_WINDEX_CAP),
2801a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Input",   750_cap3, 0, SND_DJM_WINDEX_CAP),
2802a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch4 Input",   750_cap4, 0, SND_DJM_WINDEX_CAP)
2803a07df82cSOlivia Mackintosh };
2804a07df82cSOlivia Mackintosh 
2805a07df82cSOlivia Mackintosh 
28067687850bSNicolas MURE // DJM-850
28077687850bSNicolas MURE static const u16 snd_djm_opts_850_cap1[] = {
28087687850bSNicolas MURE 	0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
28097687850bSNicolas MURE static const u16 snd_djm_opts_850_cap2[] = {
28107687850bSNicolas MURE 	0x0200, 0x0201, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020f };
28117687850bSNicolas MURE static const u16 snd_djm_opts_850_cap3[] = {
28127687850bSNicolas MURE 	0x0300, 0x0301, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030f };
28137687850bSNicolas MURE static const u16 snd_djm_opts_850_cap4[] = {
28147687850bSNicolas MURE 	0x0400, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
28157687850bSNicolas MURE 
28167687850bSNicolas MURE static const struct snd_djm_ctl snd_djm_ctls_850[] = {
28177687850bSNicolas MURE 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
28187687850bSNicolas MURE 	SND_DJM_CTL("Ch1 Input",   850_cap1, 1, SND_DJM_WINDEX_CAP),
28197687850bSNicolas MURE 	SND_DJM_CTL("Ch2 Input",   850_cap2, 0, SND_DJM_WINDEX_CAP),
28207687850bSNicolas MURE 	SND_DJM_CTL("Ch3 Input",   850_cap3, 0, SND_DJM_WINDEX_CAP),
28217687850bSNicolas MURE 	SND_DJM_CTL("Ch4 Input",   850_cap4, 1, SND_DJM_WINDEX_CAP)
28227687850bSNicolas MURE };
28237687850bSNicolas MURE 
28247687850bSNicolas MURE 
2825fee03efcSFabian Lesniak // DJM-900NXS2
2826fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap1[] = {
2827fee03efcSFabian Lesniak 	0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
2828fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap2[] = {
2829fee03efcSFabian Lesniak 	0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
2830fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap3[] = {
2831fee03efcSFabian Lesniak 	0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
2832fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap4[] = {
2833fee03efcSFabian Lesniak 	0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
2834fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap5[] = {
2835fee03efcSFabian Lesniak 	0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
2836fee03efcSFabian Lesniak 
2837fee03efcSFabian Lesniak static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = {
2838fee03efcSFabian Lesniak 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
2839fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch1 Input",   900nxs2_cap1, 2, SND_DJM_WINDEX_CAP),
2840fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch2 Input",   900nxs2_cap2, 2, SND_DJM_WINDEX_CAP),
2841fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch3 Input",   900nxs2_cap3, 2, SND_DJM_WINDEX_CAP),
2842fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch4 Input",   900nxs2_cap4, 2, SND_DJM_WINDEX_CAP),
2843fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch5 Input",   900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
2844fee03efcSFabian Lesniak };
2845fee03efcSFabian Lesniak 
2846fee03efcSFabian Lesniak 
2847a07df82cSOlivia Mackintosh static const struct snd_djm_device snd_djm_devices[] = {
2848a07df82cSOlivia Mackintosh 	SND_DJM_DEVICE(250mk2),
2849fee03efcSFabian Lesniak 	SND_DJM_DEVICE(750),
28507687850bSNicolas MURE 	SND_DJM_DEVICE(850),
2851fee03efcSFabian Lesniak 	SND_DJM_DEVICE(900nxs2)
2852a07df82cSOlivia Mackintosh };
2853a07df82cSOlivia Mackintosh 
2854a07df82cSOlivia Mackintosh 
2855a07df82cSOlivia Mackintosh static int snd_djm_controls_info(struct snd_kcontrol *kctl,
2856a07df82cSOlivia Mackintosh 				struct snd_ctl_elem_info *info)
2857a07df82cSOlivia Mackintosh {
2858a07df82cSOlivia Mackintosh 	unsigned long private_value = kctl->private_value;
2859a07df82cSOlivia Mackintosh 	u8 device_idx = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
2860a07df82cSOlivia Mackintosh 	u8 ctl_idx = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
2861a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
2862a07df82cSOlivia Mackintosh 	const char *name;
2863a07df82cSOlivia Mackintosh 	const struct snd_djm_ctl *ctl;
2864a07df82cSOlivia Mackintosh 	size_t noptions;
2865a07df82cSOlivia Mackintosh 
2866a07df82cSOlivia Mackintosh 	if (ctl_idx >= device->ncontrols)
2867cdc01a15SFrantišek Kučera 		return -EINVAL;
2868cdc01a15SFrantišek Kučera 
2869a07df82cSOlivia Mackintosh 	ctl = &device->controls[ctl_idx];
2870a07df82cSOlivia Mackintosh 	noptions = ctl->noptions;
2871a07df82cSOlivia Mackintosh 	if (info->value.enumerated.item >= noptions)
2872a07df82cSOlivia Mackintosh 		info->value.enumerated.item = noptions - 1;
2873a07df82cSOlivia Mackintosh 
2874b8db8be8SNicolas MURE 	name = snd_djm_get_label(device_idx,
2875b8db8be8SNicolas MURE 				ctl->options[info->value.enumerated.item],
2876a07df82cSOlivia Mackintosh 				ctl->wIndex);
2877a07df82cSOlivia Mackintosh 	if (!name)
2878a07df82cSOlivia Mackintosh 		return -EINVAL;
2879a07df82cSOlivia Mackintosh 
288075b1a8f9SJoe Perches 	strscpy(info->value.enumerated.name, name, sizeof(info->value.enumerated.name));
2881cdc01a15SFrantišek Kučera 	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2882cdc01a15SFrantišek Kučera 	info->count = 1;
2883a07df82cSOlivia Mackintosh 	info->value.enumerated.items = noptions;
2884cdc01a15SFrantišek Kučera 	return 0;
2885cdc01a15SFrantišek Kučera }
2886cdc01a15SFrantišek Kučera 
2887a07df82cSOlivia Mackintosh static int snd_djm_controls_update(struct usb_mixer_interface *mixer,
2888a07df82cSOlivia Mackintosh 				u8 device_idx, u8 group, u16 value)
2889cdc01a15SFrantišek Kučera {
2890cdc01a15SFrantišek Kučera 	int err;
2891a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
2892cdc01a15SFrantišek Kučera 
2893a07df82cSOlivia Mackintosh 	if ((group >= device->ncontrols) || value >= device->controls[group].noptions)
2894cdc01a15SFrantišek Kučera 		return -EINVAL;
2895cdc01a15SFrantišek Kučera 
2896cdc01a15SFrantišek Kučera 	err = snd_usb_lock_shutdown(mixer->chip);
2897cdc01a15SFrantišek Kučera 	if (err)
2898cdc01a15SFrantišek Kučera 		return err;
2899cdc01a15SFrantišek Kučera 
2900cdc01a15SFrantišek Kučera 	err = snd_usb_ctl_msg(
2901cdc01a15SFrantišek Kučera 		mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0),
2902cdc01a15SFrantišek Kučera 		USB_REQ_SET_FEATURE,
2903cdc01a15SFrantišek Kučera 		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2904a07df82cSOlivia Mackintosh 		device->controls[group].options[value],
2905a07df82cSOlivia Mackintosh 		device->controls[group].wIndex,
2906cdc01a15SFrantišek Kučera 		NULL, 0);
2907cdc01a15SFrantišek Kučera 
2908cdc01a15SFrantišek Kučera 	snd_usb_unlock_shutdown(mixer->chip);
2909cdc01a15SFrantišek Kučera 	return err;
2910cdc01a15SFrantišek Kučera }
2911cdc01a15SFrantišek Kučera 
2912a07df82cSOlivia Mackintosh static int snd_djm_controls_get(struct snd_kcontrol *kctl,
2913a07df82cSOlivia Mackintosh 				struct snd_ctl_elem_value *elem)
2914cdc01a15SFrantišek Kučera {
2915a07df82cSOlivia Mackintosh 	elem->value.enumerated.item[0] = kctl->private_value & SND_DJM_VALUE_MASK;
2916cdc01a15SFrantišek Kučera 	return 0;
2917cdc01a15SFrantišek Kučera }
2918cdc01a15SFrantišek Kučera 
2919a07df82cSOlivia Mackintosh static int snd_djm_controls_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *elem)
2920cdc01a15SFrantišek Kučera {
2921cdc01a15SFrantišek Kučera 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
2922cdc01a15SFrantišek Kučera 	struct usb_mixer_interface *mixer = list->mixer;
2923cdc01a15SFrantišek Kučera 	unsigned long private_value = kctl->private_value;
2924a07df82cSOlivia Mackintosh 
2925a07df82cSOlivia Mackintosh 	u8 device = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
2926a07df82cSOlivia Mackintosh 	u8 group = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
2927cdc01a15SFrantišek Kučera 	u16 value = elem->value.enumerated.item[0];
2928cdc01a15SFrantišek Kučera 
292950b1affcSColin Ian King 	kctl->private_value = (((unsigned long)device << SND_DJM_DEVICE_SHIFT) |
2930a07df82cSOlivia Mackintosh 			      (group << SND_DJM_GROUP_SHIFT) |
2931a07df82cSOlivia Mackintosh 			      value);
2932cdc01a15SFrantišek Kučera 
2933a07df82cSOlivia Mackintosh 	return snd_djm_controls_update(mixer, device, group, value);
2934cdc01a15SFrantišek Kučera }
2935cdc01a15SFrantišek Kučera 
2936a07df82cSOlivia Mackintosh static int snd_djm_controls_resume(struct usb_mixer_elem_list *list)
2937cdc01a15SFrantišek Kučera {
2938cdc01a15SFrantišek Kučera 	unsigned long private_value = list->kctl->private_value;
2939a07df82cSOlivia Mackintosh 	u8 device = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
2940a07df82cSOlivia Mackintosh 	u8 group = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
2941a07df82cSOlivia Mackintosh 	u16 value = (private_value & SND_DJM_VALUE_MASK);
2942cdc01a15SFrantišek Kučera 
2943a07df82cSOlivia Mackintosh 	return snd_djm_controls_update(list->mixer, device, group, value);
2944cdc01a15SFrantišek Kučera }
2945cdc01a15SFrantišek Kučera 
2946a07df82cSOlivia Mackintosh static int snd_djm_controls_create(struct usb_mixer_interface *mixer,
2947a07df82cSOlivia Mackintosh 		const u8 device_idx)
2948cdc01a15SFrantišek Kučera {
2949cdc01a15SFrantišek Kučera 	int err, i;
2950a07df82cSOlivia Mackintosh 	u16 value;
2951a07df82cSOlivia Mackintosh 
2952a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
2953a07df82cSOlivia Mackintosh 
2954cdc01a15SFrantišek Kučera 	struct snd_kcontrol_new knew = {
2955cdc01a15SFrantišek Kučera 		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
2956cdc01a15SFrantišek Kučera 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
2957cdc01a15SFrantišek Kučera 		.index = 0,
2958a07df82cSOlivia Mackintosh 		.info = snd_djm_controls_info,
2959a07df82cSOlivia Mackintosh 		.get  = snd_djm_controls_get,
2960a07df82cSOlivia Mackintosh 		.put  = snd_djm_controls_put
2961cdc01a15SFrantišek Kučera 	};
2962cdc01a15SFrantišek Kučera 
2963a07df82cSOlivia Mackintosh 	for (i = 0; i < device->ncontrols; i++) {
2964a07df82cSOlivia Mackintosh 		value = device->controls[i].default_value;
2965a07df82cSOlivia Mackintosh 		knew.name = device->controls[i].name;
2966a07df82cSOlivia Mackintosh 		knew.private_value = (
296750b1affcSColin Ian King 			((unsigned long)device_idx << SND_DJM_DEVICE_SHIFT) |
2968a07df82cSOlivia Mackintosh 			(i << SND_DJM_GROUP_SHIFT) |
2969a07df82cSOlivia Mackintosh 			value);
2970a07df82cSOlivia Mackintosh 		err = snd_djm_controls_update(mixer, device_idx, i, value);
2971cdc01a15SFrantišek Kučera 		if (err)
2972cdc01a15SFrantišek Kučera 			return err;
2973a07df82cSOlivia Mackintosh 		err = add_single_ctl_with_resume(mixer, 0, snd_djm_controls_resume,
2974cdc01a15SFrantišek Kučera 						 &knew, NULL);
2975cdc01a15SFrantišek Kučera 		if (err)
2976cdc01a15SFrantišek Kučera 			return err;
2977cdc01a15SFrantišek Kučera 	}
2978cdc01a15SFrantišek Kučera 	return 0;
2979cdc01a15SFrantišek Kučera }
2980cdc01a15SFrantišek Kučera 
29817b1eda22SDaniel Mack int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
29827b1eda22SDaniel Mack {
29833347b26cSDaniel Mack 	int err = 0;
29847b1eda22SDaniel Mack 
2985f25ecf8fSTakashi Iwai 	err = snd_usb_soundblaster_remote_init(mixer);
2986f25ecf8fSTakashi Iwai 	if (err < 0)
29877b1eda22SDaniel Mack 		return err;
29887b1eda22SDaniel Mack 
29893347b26cSDaniel Mack 	switch (mixer->chip->usb_id) {
2990d2bb390aSDetlef Urban 	/* Tascam US-16x08 */
2991d2bb390aSDetlef Urban 	case USB_ID(0x0644, 0x8047):
2992d2bb390aSDetlef Urban 		err = snd_us16x08_controls_create(mixer);
2993d2bb390aSDetlef Urban 		break;
29943347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3020):
29953347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3040):
29963347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3042):
29977cdd8d73SMathieu Bouffard 	case USB_ID(0x041e, 0x30df):
29983347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3048):
29993347b26cSDaniel Mack 		err = snd_audigy2nx_controls_create(mixer);
30003347b26cSDaniel Mack 		if (err < 0)
30013347b26cSDaniel Mack 			break;
30027449054aSTakashi Iwai 		snd_card_ro_proc_new(mixer->chip->card, "audigy2nx",
30037449054aSTakashi Iwai 				     mixer, snd_audigy2nx_proc_read);
30043347b26cSDaniel Mack 		break;
30057b1eda22SDaniel Mack 
300644832a71SVasily Khoruzhick 	/* EMU0204 */
300744832a71SVasily Khoruzhick 	case USB_ID(0x041e, 0x3f19):
300844832a71SVasily Khoruzhick 		err = snd_emu0204_controls_create(mixer);
300944832a71SVasily Khoruzhick 		break;
301044832a71SVasily Khoruzhick 
301109d8e3a7SEldad Zack 	case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
3012e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
301309d8e3a7SEldad Zack 		err = snd_c400_create_mixer(mixer);
301409d8e3a7SEldad Zack 		break;
301509d8e3a7SEldad Zack 
3016d5a0bf6cSDaniel Mack 	case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
3017d5a0bf6cSDaniel Mack 	case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
3018cfe8f97cSFelix Homann 		err = snd_ftu_create_mixer(mixer);
3019d5a0bf6cSDaniel Mack 		break;
3020d5a0bf6cSDaniel Mack 
30211d31affbSDenis Washington 	case USB_ID(0x0b05, 0x1739): /* ASUS Xonar U1 */
30221d31affbSDenis Washington 	case USB_ID(0x0b05, 0x1743): /* ASUS Xonar U1 (2) */
30231d31affbSDenis Washington 	case USB_ID(0x0b05, 0x17a0): /* ASUS Xonar U3 */
30247b1eda22SDaniel Mack 		err = snd_xonar_u1_controls_create(mixer);
30253347b26cSDaniel Mack 		break;
30267b1eda22SDaniel Mack 
3027066624c6SPrzemek Rudy 	case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */
3028066624c6SPrzemek Rudy 		err = snd_microii_controls_create(mixer);
3029066624c6SPrzemek Rudy 		break;
3030066624c6SPrzemek Rudy 
3031d497a82fSDamien Zammit 	case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */
3032d497a82fSDamien Zammit 		err = snd_mbox1_create_sync_switch(mixer);
3033d497a82fSDamien Zammit 		break;
3034d497a82fSDamien Zammit 
30353347b26cSDaniel Mack 	case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
303654a8c500SDaniel Mack 		err = snd_nativeinstruments_create_mixer(mixer,
303754a8c500SDaniel Mack 				snd_nativeinstruments_ta6_mixers,
303854a8c500SDaniel Mack 				ARRAY_SIZE(snd_nativeinstruments_ta6_mixers));
30393347b26cSDaniel Mack 		break;
304054a8c500SDaniel Mack 
30413347b26cSDaniel Mack 	case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */
304254a8c500SDaniel Mack 		err = snd_nativeinstruments_create_mixer(mixer,
304354a8c500SDaniel Mack 				snd_nativeinstruments_ta10_mixers,
304454a8c500SDaniel Mack 				ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
30453347b26cSDaniel Mack 		break;
30467536c301SMark Hills 
30477536c301SMark Hills 	case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
3048b71dad18SMark Hills 		/* detection is disabled in mixer_maps.c */
3049b71dad18SMark Hills 		err = snd_create_std_mono_table(mixer, ebox44_table);
30507536c301SMark Hills 		break;
305176b188c4SChris J Arges 
305276b188c4SChris J Arges 	case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */
305376b188c4SChris J Arges 	case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */
305476b188c4SChris J Arges 	case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */
305576b188c4SChris J Arges 	case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */
305676b188c4SChris J Arges 	case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */
305776b188c4SChris J Arges 		err = snd_scarlett_controls_create(mixer);
305876b188c4SChris J Arges 		break;
3059388fdb8fSIan Douglas Scott 
30609e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */
30619e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */
30629e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */
3063*4be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8212): /* Focusrite Scarlett 4i4 3rd Gen */
3064*4be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
3065*4be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
3066*4be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
3067265d1a90SGeoffrey D. Bennett 		err = snd_scarlett_gen2_init(mixer);
30689e4d5c1bSGeoffrey D. Bennett 		break;
30699e4d5c1bSGeoffrey D. Bennett 
3070388fdb8fSIan Douglas Scott 	case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
3071388fdb8fSIan Douglas Scott 		err = snd_soundblaster_e1_switch_create(mixer);
3072388fdb8fSIan Douglas Scott 		break;
3073964af639STakashi Iwai 	case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
3074964af639STakashi Iwai 		err = dell_dock_mixer_init(mixer);
3075964af639STakashi Iwai 		break;
3076d39f1d68SJussi Laako 
3077d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */
3078d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */
3079d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd4): /* RME */
3080d39f1d68SJussi Laako 		err = snd_rme_controls_create(mixer);
3081d39f1d68SJussi Laako 		break;
30828dc5efe3SNick Kossifidis 
30838dc5efe3SNick Kossifidis 	case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */
30848dc5efe3SNick Kossifidis 		err = snd_sc1810_init_mixer(mixer);
30858dc5efe3SNick Kossifidis 		break;
30863e8f3bd0SThomas Ebeling 	case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
30873e8f3bd0SThomas Ebeling 		err = snd_bbfpro_controls_create(mixer);
30883e8f3bd0SThomas Ebeling 		break;
3089cdc01a15SFrantišek Kučera 	case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */
3090a07df82cSOlivia Mackintosh 		err = snd_djm_controls_create(mixer, SND_DJM_250MK2_IDX);
3091a07df82cSOlivia Mackintosh 		break;
3092a07df82cSOlivia Mackintosh 	case USB_ID(0x08e4, 0x017f): /* Pioneer DJ DJM-750 */
3093a07df82cSOlivia Mackintosh 		err = snd_djm_controls_create(mixer, SND_DJM_750_IDX);
3094cdc01a15SFrantišek Kučera 		break;
30957687850bSNicolas MURE 	case USB_ID(0x08e4, 0x0163): /* Pioneer DJ DJM-850 */
30967687850bSNicolas MURE 		err = snd_djm_controls_create(mixer, SND_DJM_850_IDX);
30977687850bSNicolas MURE 		break;
3098fee03efcSFabian Lesniak 	case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
3099fee03efcSFabian Lesniak 		err = snd_djm_controls_create(mixer, SND_DJM_900NXS2_IDX);
3100fee03efcSFabian Lesniak 		break;
310154a8c500SDaniel Mack 	}
310254a8c500SDaniel Mack 
31033347b26cSDaniel Mack 	return err;
31047b1eda22SDaniel Mack }
31057b1eda22SDaniel Mack 
3106964af639STakashi Iwai #ifdef CONFIG_PM
3107964af639STakashi Iwai void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
3108964af639STakashi Iwai {
3109964af639STakashi Iwai 	switch (mixer->chip->usb_id) {
3110964af639STakashi Iwai 	case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
3111964af639STakashi Iwai 		dell_dock_mixer_init(mixer);
3112964af639STakashi Iwai 		break;
3113964af639STakashi Iwai 	}
3114964af639STakashi Iwai }
3115964af639STakashi Iwai #endif
3116964af639STakashi Iwai 
31177b1eda22SDaniel Mack void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
31187b1eda22SDaniel Mack 				    int unitid)
31197b1eda22SDaniel Mack {
31207b1eda22SDaniel Mack 	if (!mixer->rc_cfg)
31217b1eda22SDaniel Mack 		return;
31227b1eda22SDaniel Mack 	/* unit ids specific to Extigy/Audigy 2 NX: */
31237b1eda22SDaniel Mack 	switch (unitid) {
31247b1eda22SDaniel Mack 	case 0: /* remote control */
31257b1eda22SDaniel Mack 		mixer->rc_urb->dev = mixer->chip->dev;
31267b1eda22SDaniel Mack 		usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
31277b1eda22SDaniel Mack 		break;
31287b1eda22SDaniel Mack 	case 4: /* digital in jack */
31297b1eda22SDaniel Mack 	case 7: /* line in jacks */
31307b1eda22SDaniel Mack 	case 19: /* speaker out jacks */
31317b1eda22SDaniel Mack 	case 20: /* headphones out jack */
31327b1eda22SDaniel Mack 		break;
31337b1eda22SDaniel Mack 	/* live24ext: 4 = line-in jack */
31347b1eda22SDaniel Mack 	case 3:	/* hp-out jack (may actuate Mute) */
31357b1eda22SDaniel Mack 		if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
31367b1eda22SDaniel Mack 		    mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
31377b1eda22SDaniel Mack 			snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
31387b1eda22SDaniel Mack 		break;
31397b1eda22SDaniel Mack 	default:
31400ba41d91STakashi Iwai 		usb_audio_dbg(mixer->chip, "memory change in unknown unit %d\n", unitid);
31417b1eda22SDaniel Mack 		break;
31427b1eda22SDaniel Mack 	}
31437b1eda22SDaniel Mack }
31447b1eda22SDaniel Mack 
314542e3121dSAnssi Hannula static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
3146eb1a74b7SAnssi Hannula 					 struct usb_mixer_elem_info *cval,
314742e3121dSAnssi Hannula 					 struct snd_kcontrol *kctl)
314842e3121dSAnssi Hannula {
314942e3121dSAnssi Hannula 	/* Approximation using 10 ranges based on output measurement on hw v1.2.
315042e3121dSAnssi Hannula 	 * This seems close to the cubic mapping e.g. alsamixer uses. */
315142e3121dSAnssi Hannula 	static const DECLARE_TLV_DB_RANGE(scale,
315242e3121dSAnssi Hannula 		 0,  1, TLV_DB_MINMAX_ITEM(-5300, -4970),
315342e3121dSAnssi Hannula 		 2,  5, TLV_DB_MINMAX_ITEM(-4710, -4160),
315442e3121dSAnssi Hannula 		 6,  7, TLV_DB_MINMAX_ITEM(-3884, -3710),
315542e3121dSAnssi Hannula 		 8, 14, TLV_DB_MINMAX_ITEM(-3443, -2560),
315642e3121dSAnssi Hannula 		15, 16, TLV_DB_MINMAX_ITEM(-2475, -2324),
315742e3121dSAnssi Hannula 		17, 19, TLV_DB_MINMAX_ITEM(-2228, -2031),
315842e3121dSAnssi Hannula 		20, 26, TLV_DB_MINMAX_ITEM(-1910, -1393),
315942e3121dSAnssi Hannula 		27, 31, TLV_DB_MINMAX_ITEM(-1322, -1032),
316042e3121dSAnssi Hannula 		32, 40, TLV_DB_MINMAX_ITEM(-968, -490),
316142e3121dSAnssi Hannula 		41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
316242e3121dSAnssi Hannula 	);
316342e3121dSAnssi Hannula 
3164eb1a74b7SAnssi Hannula 	if (cval->min == 0 && cval->max == 50) {
3165eb1a74b7SAnssi Hannula 		usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n");
316642e3121dSAnssi Hannula 		kctl->tlv.p = scale;
316742e3121dSAnssi Hannula 		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
316842e3121dSAnssi Hannula 		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
3169eb1a74b7SAnssi Hannula 
3170eb1a74b7SAnssi Hannula 	} else if (cval->min == 0 && cval->max <= 1000) {
3171eb1a74b7SAnssi Hannula 		/* Some other clearly broken DragonFly variant.
3172eb1a74b7SAnssi Hannula 		 * At least a 0..53 variant (hw v1.0) exists.
3173eb1a74b7SAnssi Hannula 		 */
3174eb1a74b7SAnssi Hannula 		usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device");
3175eb1a74b7SAnssi Hannula 		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
3176eb1a74b7SAnssi Hannula 	}
317742e3121dSAnssi Hannula }
317842e3121dSAnssi Hannula 
317942e3121dSAnssi Hannula void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
318042e3121dSAnssi Hannula 				  struct usb_mixer_elem_info *cval, int unitid,
318142e3121dSAnssi Hannula 				  struct snd_kcontrol *kctl)
318242e3121dSAnssi Hannula {
318342e3121dSAnssi Hannula 	switch (mixer->chip->usb_id) {
318442e3121dSAnssi Hannula 	case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
3185eb1a74b7SAnssi Hannula 		if (unitid == 7 && cval->control == UAC_FU_VOLUME)
3186eb1a74b7SAnssi Hannula 			snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
318742e3121dSAnssi Hannula 		break;
31880f174b35STakashi Iwai 	/* lowest playback value is muted on C-Media devices */
31890f174b35STakashi Iwai 	case USB_ID(0x0d8c, 0x000c):
31900f174b35STakashi Iwai 	case USB_ID(0x0d8c, 0x0014):
31910f174b35STakashi Iwai 		if (strstr(kctl->id.name, "Playback"))
31920f174b35STakashi Iwai 			cval->min_mute = 1;
31930f174b35STakashi Iwai 		break;
319442e3121dSAnssi Hannula 	}
319542e3121dSAnssi Hannula }
319642e3121dSAnssi Hannula 
3197