xref: /openbmc/linux/sound/usb/mixer_quirks.c (revision 6d277881)
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;
1547b9cf903STakashi Iwai 	list->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 
5977ac2246fSDamien Zammit /* Digidesign Mbox 1 helper functions */
598d497a82fSDamien Zammit 
5997ac2246fSDamien Zammit static int snd_mbox1_is_spdif_synced(struct snd_usb_audio *chip)
600d497a82fSDamien Zammit {
601d497a82fSDamien Zammit 	unsigned char buff[3];
6027ac2246fSDamien Zammit 	int err;
6037ac2246fSDamien Zammit 	int is_spdif_synced;
604d497a82fSDamien Zammit 
6057ac2246fSDamien Zammit 	/* Read clock source */
6067ac2246fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
6077ac2246fSDamien Zammit 			      usb_rcvctrlpipe(chip->dev, 0), 0x81,
6087ac2246fSDamien Zammit 			      USB_DIR_IN |
6097ac2246fSDamien Zammit 			      USB_TYPE_CLASS |
6107ac2246fSDamien Zammit 			      USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
61147ab1545STakashi Iwai 	if (err < 0)
61247ab1545STakashi Iwai 		return err;
613d497a82fSDamien Zammit 
6147ac2246fSDamien Zammit 	/* spdif sync: buff is all zeroes */
6157ac2246fSDamien Zammit 	is_spdif_synced = !(buff[0] | buff[1] | buff[2]);
6167ac2246fSDamien Zammit 	return is_spdif_synced;
617d497a82fSDamien Zammit }
618d497a82fSDamien Zammit 
6197ac2246fSDamien Zammit static int snd_mbox1_set_clk_source(struct snd_usb_audio *chip, int rate_or_zero)
6207ac2246fSDamien Zammit {
6217ac2246fSDamien Zammit 	/* 2 possibilities:	Internal    -> expects sample rate
6227ac2246fSDamien Zammit 	 *			S/PDIF sync -> expects rate = 0
6237ac2246fSDamien Zammit 	 */
6247ac2246fSDamien Zammit 	unsigned char buff[3];
6257ac2246fSDamien Zammit 
6267ac2246fSDamien Zammit 	buff[0] = (rate_or_zero >>  0) & 0xff;
6277ac2246fSDamien Zammit 	buff[1] = (rate_or_zero >>  8) & 0xff;
6287ac2246fSDamien Zammit 	buff[2] = (rate_or_zero >> 16) & 0xff;
6297ac2246fSDamien Zammit 
6307ac2246fSDamien Zammit 	/* Set clock source */
6317ac2246fSDamien Zammit 	return snd_usb_ctl_msg(chip->dev,
632d497a82fSDamien Zammit 			       usb_sndctrlpipe(chip->dev, 0), 0x1,
633d497a82fSDamien Zammit 			       USB_TYPE_CLASS |
634d497a82fSDamien Zammit 			       USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
6357ac2246fSDamien Zammit }
6367ac2246fSDamien Zammit 
6377ac2246fSDamien Zammit static int snd_mbox1_is_spdif_input(struct snd_usb_audio *chip)
6387ac2246fSDamien Zammit {
6397ac2246fSDamien Zammit 	/* Hardware gives 2 possibilities:	ANALOG Source  -> 0x01
6407ac2246fSDamien Zammit 	 *					S/PDIF Source  -> 0x02
6417ac2246fSDamien Zammit 	 */
6427ac2246fSDamien Zammit 	int err;
6437ac2246fSDamien Zammit 	unsigned char source[1];
6447ac2246fSDamien Zammit 
6457ac2246fSDamien Zammit 	/* Read input source */
646d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
647d497a82fSDamien Zammit 			      usb_rcvctrlpipe(chip->dev, 0), 0x81,
648d497a82fSDamien Zammit 			      USB_DIR_IN |
649d497a82fSDamien Zammit 			      USB_TYPE_CLASS |
6507ac2246fSDamien Zammit 			      USB_RECIP_INTERFACE, 0x00, 0x500, source, 1);
651d497a82fSDamien Zammit 	if (err < 0)
6527ac2246fSDamien Zammit 		return err;
6537ac2246fSDamien Zammit 
6547ac2246fSDamien Zammit 	return (source[0] == 2);
6557ac2246fSDamien Zammit }
6567ac2246fSDamien Zammit 
6577ac2246fSDamien Zammit static int snd_mbox1_set_input_source(struct snd_usb_audio *chip, int is_spdif)
6587ac2246fSDamien Zammit {
6597ac2246fSDamien Zammit 	/* NB: Setting the input source to S/PDIF resets the clock source to S/PDIF
6607ac2246fSDamien Zammit 	 * Hardware expects 2 possibilities:	ANALOG Source  -> 0x01
6617ac2246fSDamien Zammit 	 *					S/PDIF Source  -> 0x02
6627ac2246fSDamien Zammit 	 */
6637ac2246fSDamien Zammit 	unsigned char buff[1];
6647ac2246fSDamien Zammit 
6657ac2246fSDamien Zammit 	buff[0] = (is_spdif & 1) + 1;
6667ac2246fSDamien Zammit 
6677ac2246fSDamien Zammit 	/* Set input source */
6687ac2246fSDamien Zammit 	return snd_usb_ctl_msg(chip->dev,
6697ac2246fSDamien Zammit 			       usb_sndctrlpipe(chip->dev, 0), 0x1,
670d497a82fSDamien Zammit 			       USB_TYPE_CLASS |
6717ac2246fSDamien Zammit 			       USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
6727ac2246fSDamien Zammit }
6737ac2246fSDamien Zammit 
6747ac2246fSDamien Zammit /* Digidesign Mbox 1 clock source switch (internal/spdif) */
6757ac2246fSDamien Zammit 
6767ac2246fSDamien Zammit static int snd_mbox1_clk_switch_get(struct snd_kcontrol *kctl,
6777ac2246fSDamien Zammit 				    struct snd_ctl_elem_value *ucontrol)
6787ac2246fSDamien Zammit {
6797ac2246fSDamien Zammit 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
6807ac2246fSDamien Zammit 	struct snd_usb_audio *chip = list->mixer->chip;
6817ac2246fSDamien Zammit 	int err;
6827ac2246fSDamien Zammit 
6837ac2246fSDamien Zammit 	err = snd_usb_lock_shutdown(chip);
684d497a82fSDamien Zammit 	if (err < 0)
685d497a82fSDamien Zammit 		goto err;
686d497a82fSDamien Zammit 
6877ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
6887ac2246fSDamien Zammit 	if (err < 0)
6897ac2246fSDamien Zammit 		goto err;
6907ac2246fSDamien Zammit 
6917ac2246fSDamien Zammit 	kctl->private_value = err;
6927ac2246fSDamien Zammit 	err = 0;
6937ac2246fSDamien Zammit 	ucontrol->value.enumerated.item[0] = kctl->private_value;
694d497a82fSDamien Zammit err:
69547ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
69625a9a4f9STakashi Iwai 	return err;
69725a9a4f9STakashi Iwai }
69825a9a4f9STakashi Iwai 
6997ac2246fSDamien Zammit static int snd_mbox1_clk_switch_update(struct usb_mixer_interface *mixer, int is_spdif_sync)
7007ac2246fSDamien Zammit {
7017ac2246fSDamien Zammit 	struct snd_usb_audio *chip = mixer->chip;
7027ac2246fSDamien Zammit 	int err;
7037ac2246fSDamien Zammit 
7047ac2246fSDamien Zammit 	err = snd_usb_lock_shutdown(chip);
7057ac2246fSDamien Zammit 	if (err < 0)
7067ac2246fSDamien Zammit 		return err;
7077ac2246fSDamien Zammit 
7087ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_input(chip);
7097ac2246fSDamien Zammit 	if (err < 0)
7107ac2246fSDamien Zammit 		goto err;
7117ac2246fSDamien Zammit 
7127ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
7137ac2246fSDamien Zammit 	if (err < 0)
7147ac2246fSDamien Zammit 		goto err;
7157ac2246fSDamien Zammit 
7167ac2246fSDamien Zammit 	/* FIXME: hardcoded sample rate */
7177ac2246fSDamien Zammit 	err = snd_mbox1_set_clk_source(chip, is_spdif_sync ? 0 : 48000);
7187ac2246fSDamien Zammit 	if (err < 0)
7197ac2246fSDamien Zammit 		goto err;
7207ac2246fSDamien Zammit 
7217ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
7227ac2246fSDamien Zammit err:
7237ac2246fSDamien Zammit 	snd_usb_unlock_shutdown(chip);
7247ac2246fSDamien Zammit 	return err;
7257ac2246fSDamien Zammit }
7267ac2246fSDamien Zammit 
7277ac2246fSDamien Zammit static int snd_mbox1_clk_switch_put(struct snd_kcontrol *kctl,
72825a9a4f9STakashi Iwai 				    struct snd_ctl_elem_value *ucontrol)
72925a9a4f9STakashi Iwai {
73025a9a4f9STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
73125a9a4f9STakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
73225a9a4f9STakashi Iwai 	int err;
73325a9a4f9STakashi Iwai 	bool cur_val, new_val;
73425a9a4f9STakashi Iwai 
73525a9a4f9STakashi Iwai 	cur_val = kctl->private_value;
73625a9a4f9STakashi Iwai 	new_val = ucontrol->value.enumerated.item[0];
73725a9a4f9STakashi Iwai 	if (cur_val == new_val)
73825a9a4f9STakashi Iwai 		return 0;
73925a9a4f9STakashi Iwai 
74025a9a4f9STakashi Iwai 	kctl->private_value = new_val;
7417ac2246fSDamien Zammit 	err = snd_mbox1_clk_switch_update(mixer, new_val);
742d497a82fSDamien Zammit 	return err < 0 ? err : 1;
743d497a82fSDamien Zammit }
744d497a82fSDamien Zammit 
7457ac2246fSDamien Zammit static int snd_mbox1_clk_switch_info(struct snd_kcontrol *kcontrol,
746d497a82fSDamien Zammit 				     struct snd_ctl_elem_info *uinfo)
747d497a82fSDamien Zammit {
748d497a82fSDamien Zammit 	static const char *const texts[2] = {
749d497a82fSDamien Zammit 		"Internal",
750d497a82fSDamien Zammit 		"S/PDIF"
751d497a82fSDamien Zammit 	};
752d497a82fSDamien Zammit 
753d497a82fSDamien Zammit 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
754d497a82fSDamien Zammit }
755d497a82fSDamien Zammit 
7567ac2246fSDamien Zammit static int snd_mbox1_clk_switch_resume(struct usb_mixer_elem_list *list)
75725a9a4f9STakashi Iwai {
7587ac2246fSDamien Zammit 	return snd_mbox1_clk_switch_update(list->mixer, list->kctl->private_value);
75925a9a4f9STakashi Iwai }
76025a9a4f9STakashi Iwai 
7617ac2246fSDamien Zammit /* Digidesign Mbox 1 input source switch (analog/spdif) */
7627ac2246fSDamien Zammit 
7637ac2246fSDamien Zammit static int snd_mbox1_src_switch_get(struct snd_kcontrol *kctl,
7647ac2246fSDamien Zammit 				    struct snd_ctl_elem_value *ucontrol)
7657ac2246fSDamien Zammit {
7667ac2246fSDamien Zammit 	ucontrol->value.enumerated.item[0] = kctl->private_value;
7677ac2246fSDamien Zammit 	return 0;
7687ac2246fSDamien Zammit }
7697ac2246fSDamien Zammit 
7707ac2246fSDamien Zammit static int snd_mbox1_src_switch_update(struct usb_mixer_interface *mixer, int is_spdif_input)
7717ac2246fSDamien Zammit {
7727ac2246fSDamien Zammit 	struct snd_usb_audio *chip = mixer->chip;
7737ac2246fSDamien Zammit 	int err;
7747ac2246fSDamien Zammit 
7757ac2246fSDamien Zammit 	err = snd_usb_lock_shutdown(chip);
7767ac2246fSDamien Zammit 	if (err < 0)
7777ac2246fSDamien Zammit 		return err;
7787ac2246fSDamien Zammit 
7797ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_input(chip);
7807ac2246fSDamien Zammit 	if (err < 0)
7817ac2246fSDamien Zammit 		goto err;
7827ac2246fSDamien Zammit 
7837ac2246fSDamien Zammit 	err = snd_mbox1_set_input_source(chip, is_spdif_input);
7847ac2246fSDamien Zammit 	if (err < 0)
7857ac2246fSDamien Zammit 		goto err;
7867ac2246fSDamien Zammit 
7877ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_input(chip);
7887ac2246fSDamien Zammit 	if (err < 0)
7897ac2246fSDamien Zammit 		goto err;
7907ac2246fSDamien Zammit 
7917ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
7927ac2246fSDamien Zammit err:
7937ac2246fSDamien Zammit 	snd_usb_unlock_shutdown(chip);
7947ac2246fSDamien Zammit 	return err;
7957ac2246fSDamien Zammit }
7967ac2246fSDamien Zammit 
7977ac2246fSDamien Zammit static int snd_mbox1_src_switch_put(struct snd_kcontrol *kctl,
7987ac2246fSDamien Zammit 				    struct snd_ctl_elem_value *ucontrol)
7997ac2246fSDamien Zammit {
8007ac2246fSDamien Zammit 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
8017ac2246fSDamien Zammit 	struct usb_mixer_interface *mixer = list->mixer;
8027ac2246fSDamien Zammit 	int err;
8037ac2246fSDamien Zammit 	bool cur_val, new_val;
8047ac2246fSDamien Zammit 
8057ac2246fSDamien Zammit 	cur_val = kctl->private_value;
8067ac2246fSDamien Zammit 	new_val = ucontrol->value.enumerated.item[0];
8077ac2246fSDamien Zammit 	if (cur_val == new_val)
8087ac2246fSDamien Zammit 		return 0;
8097ac2246fSDamien Zammit 
8107ac2246fSDamien Zammit 	kctl->private_value = new_val;
8117ac2246fSDamien Zammit 	err = snd_mbox1_src_switch_update(mixer, new_val);
8127ac2246fSDamien Zammit 	return err < 0 ? err : 1;
8137ac2246fSDamien Zammit }
8147ac2246fSDamien Zammit 
8157ac2246fSDamien Zammit static int snd_mbox1_src_switch_info(struct snd_kcontrol *kcontrol,
8167ac2246fSDamien Zammit 				     struct snd_ctl_elem_info *uinfo)
8177ac2246fSDamien Zammit {
8187ac2246fSDamien Zammit 	static const char *const texts[2] = {
8197ac2246fSDamien Zammit 		"Analog",
8207ac2246fSDamien Zammit 		"S/PDIF"
8217ac2246fSDamien Zammit 	};
8227ac2246fSDamien Zammit 
8237ac2246fSDamien Zammit 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
8247ac2246fSDamien Zammit }
8257ac2246fSDamien Zammit 
8267ac2246fSDamien Zammit static int snd_mbox1_src_switch_resume(struct usb_mixer_elem_list *list)
8277ac2246fSDamien Zammit {
8287ac2246fSDamien Zammit 	return snd_mbox1_src_switch_update(list->mixer, list->kctl->private_value);
8297ac2246fSDamien Zammit }
8307ac2246fSDamien Zammit 
8317ac2246fSDamien Zammit static const struct snd_kcontrol_new snd_mbox1_clk_switch = {
832d497a82fSDamien Zammit 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
833d497a82fSDamien Zammit 	.name = "Clock Source",
834d497a82fSDamien Zammit 	.index = 0,
835d497a82fSDamien Zammit 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
8367ac2246fSDamien Zammit 	.info = snd_mbox1_clk_switch_info,
8377ac2246fSDamien Zammit 	.get = snd_mbox1_clk_switch_get,
8387ac2246fSDamien Zammit 	.put = snd_mbox1_clk_switch_put,
839d497a82fSDamien Zammit 	.private_value = 0
840d497a82fSDamien Zammit };
841d497a82fSDamien Zammit 
8427ac2246fSDamien Zammit static const struct snd_kcontrol_new snd_mbox1_src_switch = {
8437ac2246fSDamien Zammit 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8447ac2246fSDamien Zammit 	.name = "Input Source",
8457ac2246fSDamien Zammit 	.index = 1,
8467ac2246fSDamien Zammit 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
8477ac2246fSDamien Zammit 	.info = snd_mbox1_src_switch_info,
8487ac2246fSDamien Zammit 	.get = snd_mbox1_src_switch_get,
8497ac2246fSDamien Zammit 	.put = snd_mbox1_src_switch_put,
8507ac2246fSDamien Zammit 	.private_value = 0
8517ac2246fSDamien Zammit };
8527ac2246fSDamien Zammit 
8537ac2246fSDamien Zammit static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer)
854d497a82fSDamien Zammit {
8557ac2246fSDamien Zammit 	int err;
8567ac2246fSDamien Zammit 	err = add_single_ctl_with_resume(mixer, 0,
8577ac2246fSDamien Zammit 					 snd_mbox1_clk_switch_resume,
8587ac2246fSDamien Zammit 					 &snd_mbox1_clk_switch, NULL);
8597ac2246fSDamien Zammit 	if (err < 0)
8607ac2246fSDamien Zammit 		return err;
8617ac2246fSDamien Zammit 
8627ac2246fSDamien Zammit 	return add_single_ctl_with_resume(mixer, 1,
8637ac2246fSDamien Zammit 					  snd_mbox1_src_switch_resume,
8647ac2246fSDamien Zammit 					  &snd_mbox1_src_switch, NULL);
865d497a82fSDamien Zammit }
866d497a82fSDamien Zammit 
86754a8c500SDaniel Mack /* Native Instruments device quirks */
86854a8c500SDaniel Mack 
86954a8c500SDaniel Mack #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
87054a8c500SDaniel Mack 
871da6d2769STakashi Iwai static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
872da6d2769STakashi Iwai 				   struct snd_kcontrol *kctl)
873da6d2769STakashi Iwai {
874da6d2769STakashi Iwai 	struct usb_device *dev = mixer->chip->dev;
875da6d2769STakashi Iwai 	unsigned int pval = kctl->private_value;
876da6d2769STakashi Iwai 	u8 value;
877da6d2769STakashi Iwai 	int err;
878da6d2769STakashi Iwai 
879da6d2769STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
880da6d2769STakashi Iwai 			      (pval >> 16) & 0xff,
881da6d2769STakashi Iwai 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
882da6d2769STakashi Iwai 			      0, pval & 0xffff, &value, 1);
883da6d2769STakashi Iwai 	if (err < 0) {
884da6d2769STakashi Iwai 		dev_err(&dev->dev,
885da6d2769STakashi Iwai 			"unable to issue vendor read request (ret = %d)", err);
886da6d2769STakashi Iwai 		return err;
887da6d2769STakashi Iwai 	}
888da6d2769STakashi Iwai 
8892acf5a3eSColin Ian King 	kctl->private_value |= ((unsigned int)value << 24);
890da6d2769STakashi Iwai 	return 0;
891da6d2769STakashi Iwai }
892da6d2769STakashi Iwai 
89354a8c500SDaniel Mack static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
89454a8c500SDaniel Mack 					     struct snd_ctl_elem_value *ucontrol)
89554a8c500SDaniel Mack {
896da6d2769STakashi Iwai 	ucontrol->value.integer.value[0] = kcontrol->private_value >> 24;
897da6d2769STakashi Iwai 	return 0;
89854a8c500SDaniel Mack }
89954a8c500SDaniel Mack 
900da6d2769STakashi Iwai static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
901da6d2769STakashi Iwai {
902da6d2769STakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
903da6d2769STakashi Iwai 	unsigned int pval = list->kctl->private_value;
904da6d2769STakashi Iwai 	int err;
90554a8c500SDaniel Mack 
90647ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
90747ab1545STakashi Iwai 	if (err < 0)
90847ab1545STakashi Iwai 		return err;
909da6d2769STakashi Iwai 	err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
910da6d2769STakashi Iwai 			      (pval >> 16) & 0xff,
911da6d2769STakashi Iwai 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
912da6d2769STakashi Iwai 			      pval >> 24, pval & 0xffff, NULL, 0, 1000);
91347ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
914da6d2769STakashi Iwai 	return err;
91554a8c500SDaniel Mack }
91654a8c500SDaniel Mack 
91754a8c500SDaniel Mack static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
91854a8c500SDaniel Mack 					     struct snd_ctl_elem_value *ucontrol)
91954a8c500SDaniel Mack {
920da6d2769STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
921da6d2769STakashi Iwai 	u8 oldval = (kcontrol->private_value >> 24) & 0xff;
922da6d2769STakashi Iwai 	u8 newval = ucontrol->value.integer.value[0];
923da6d2769STakashi Iwai 	int err;
92454a8c500SDaniel Mack 
925da6d2769STakashi Iwai 	if (oldval == newval)
92654a8c500SDaniel Mack 		return 0;
927da6d2769STakashi Iwai 
928da6d2769STakashi Iwai 	kcontrol->private_value &= ~(0xff << 24);
929c4a359a0STakashi Iwai 	kcontrol->private_value |= (unsigned int)newval << 24;
930da6d2769STakashi Iwai 	err = snd_ni_update_cur_val(list);
931da6d2769STakashi Iwai 	return err < 0 ? err : 1;
93254a8c500SDaniel Mack }
93354a8c500SDaniel Mack 
934195727e8STakashi Iwai static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
93554a8c500SDaniel Mack 	{
93654a8c500SDaniel Mack 		.name = "Direct Thru Channel A",
93754a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
93854a8c500SDaniel Mack 	},
93954a8c500SDaniel Mack 	{
94054a8c500SDaniel Mack 		.name = "Direct Thru Channel B",
94154a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
94254a8c500SDaniel Mack 	},
94354a8c500SDaniel Mack 	{
94454a8c500SDaniel Mack 		.name = "Phono Input Channel A",
94554a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
94654a8c500SDaniel Mack 	},
94754a8c500SDaniel Mack 	{
94854a8c500SDaniel Mack 		.name = "Phono Input Channel B",
94954a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
95054a8c500SDaniel Mack 	},
95154a8c500SDaniel Mack };
95254a8c500SDaniel Mack 
953195727e8STakashi Iwai static const struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = {
95454a8c500SDaniel Mack 	{
95554a8c500SDaniel Mack 		.name = "Direct Thru Channel A",
95654a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
95754a8c500SDaniel Mack 	},
95854a8c500SDaniel Mack 	{
95954a8c500SDaniel Mack 		.name = "Direct Thru Channel B",
96054a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
96154a8c500SDaniel Mack 	},
96254a8c500SDaniel Mack 	{
96354a8c500SDaniel Mack 		.name = "Direct Thru Channel C",
96454a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x07),
96554a8c500SDaniel Mack 	},
96654a8c500SDaniel Mack 	{
96754a8c500SDaniel Mack 		.name = "Direct Thru Channel D",
96854a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x09),
96954a8c500SDaniel Mack 	},
97054a8c500SDaniel Mack 	{
97154a8c500SDaniel Mack 		.name = "Phono Input Channel A",
97254a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
97354a8c500SDaniel Mack 	},
97454a8c500SDaniel Mack 	{
97554a8c500SDaniel Mack 		.name = "Phono Input Channel B",
97654a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
97754a8c500SDaniel Mack 	},
97854a8c500SDaniel Mack 	{
97954a8c500SDaniel Mack 		.name = "Phono Input Channel C",
98054a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x07),
98154a8c500SDaniel Mack 	},
98254a8c500SDaniel Mack 	{
98354a8c500SDaniel Mack 		.name = "Phono Input Channel D",
98454a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x09),
98554a8c500SDaniel Mack 	},
98654a8c500SDaniel Mack };
98754a8c500SDaniel Mack 
98854a8c500SDaniel Mack static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
98954a8c500SDaniel Mack 					      const struct snd_kcontrol_new *kc,
99054a8c500SDaniel Mack 					      unsigned int count)
99154a8c500SDaniel Mack {
99254a8c500SDaniel Mack 	int i, err = 0;
99354a8c500SDaniel Mack 	struct snd_kcontrol_new template = {
99454a8c500SDaniel Mack 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
99554a8c500SDaniel Mack 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
99654a8c500SDaniel Mack 		.get = snd_nativeinstruments_control_get,
99754a8c500SDaniel Mack 		.put = snd_nativeinstruments_control_put,
99854a8c500SDaniel Mack 		.info = snd_ctl_boolean_mono_info,
99954a8c500SDaniel Mack 	};
100054a8c500SDaniel Mack 
100154a8c500SDaniel Mack 	for (i = 0; i < count; i++) {
1002da6d2769STakashi Iwai 		struct usb_mixer_elem_list *list;
100354a8c500SDaniel Mack 
100454a8c500SDaniel Mack 		template.name = kc[i].name;
100554a8c500SDaniel Mack 		template.private_value = kc[i].private_value;
100654a8c500SDaniel Mack 
1007da6d2769STakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
1008da6d2769STakashi Iwai 						 snd_ni_update_cur_val,
1009da6d2769STakashi Iwai 						 &template, &list);
101054a8c500SDaniel Mack 		if (err < 0)
101154a8c500SDaniel Mack 			break;
1012da6d2769STakashi Iwai 		snd_ni_control_init_val(mixer, list->kctl);
101354a8c500SDaniel Mack 	}
101454a8c500SDaniel Mack 
101554a8c500SDaniel Mack 	return err;
101654a8c500SDaniel Mack }
101754a8c500SDaniel Mack 
1018d5a0bf6cSDaniel Mack /* M-Audio FastTrack Ultra quirks */
1019e9a25e04SMatt Gruskin /* FTU Effect switch (also used by C400/C600) */
1020d34bf148SFelix Homann static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
1021d34bf148SFelix Homann 					struct snd_ctl_elem_info *uinfo)
1022d34bf148SFelix Homann {
10237bbd03e0STakashi Iwai 	static const char *const texts[8] = {
10247bbd03e0STakashi Iwai 		"Room 1", "Room 2", "Room 3", "Hall 1",
10257bbd03e0STakashi Iwai 		"Hall 2", "Plate", "Delay", "Echo"
1026d34bf148SFelix Homann 	};
1027d34bf148SFelix Homann 
10287bbd03e0STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
1029d34bf148SFelix Homann }
1030d34bf148SFelix Homann 
10310b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
10320b4e9cfcSTakashi Iwai 				   struct snd_kcontrol *kctl)
1033d34bf148SFelix Homann {
10340b4e9cfcSTakashi Iwai 	struct usb_device *dev = mixer->chip->dev;
10350b4e9cfcSTakashi Iwai 	unsigned int pval = kctl->private_value;
1036d34bf148SFelix Homann 	int err;
1037d34bf148SFelix Homann 	unsigned char value[2];
1038d34bf148SFelix Homann 
1039d34bf148SFelix Homann 	value[0] = 0x00;
1040d34bf148SFelix Homann 	value[1] = 0x00;
1041d34bf148SFelix Homann 
10420b4e9cfcSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
1043d34bf148SFelix Homann 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
10440b4e9cfcSTakashi Iwai 			      pval & 0xff00,
10450b4e9cfcSTakashi Iwai 			      snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
10460b4e9cfcSTakashi Iwai 			      value, 2);
1047d34bf148SFelix Homann 	if (err < 0)
1048d34bf148SFelix Homann 		return err;
1049d34bf148SFelix Homann 
10502acf5a3eSColin Ian King 	kctl->private_value |= (unsigned int)value[0] << 24;
1051d34bf148SFelix Homann 	return 0;
1052d34bf148SFelix Homann }
1053d34bf148SFelix Homann 
10540b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
10550b4e9cfcSTakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
10560b4e9cfcSTakashi Iwai {
10570b4e9cfcSTakashi Iwai 	ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
10580b4e9cfcSTakashi Iwai 	return 0;
10590b4e9cfcSTakashi Iwai }
10600b4e9cfcSTakashi Iwai 
10610b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
10620b4e9cfcSTakashi Iwai {
10630b4e9cfcSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
10640b4e9cfcSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
10650b4e9cfcSTakashi Iwai 	unsigned char value[2];
10660b4e9cfcSTakashi Iwai 	int err;
10670b4e9cfcSTakashi Iwai 
10680b4e9cfcSTakashi Iwai 	value[0] = pval >> 24;
10690b4e9cfcSTakashi Iwai 	value[1] = 0;
10700b4e9cfcSTakashi Iwai 
107147ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
107247ab1545STakashi Iwai 	if (err < 0)
107347ab1545STakashi Iwai 		return err;
10740b4e9cfcSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
10750b4e9cfcSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0),
10760b4e9cfcSTakashi Iwai 			      UAC_SET_CUR,
10770b4e9cfcSTakashi Iwai 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
10780b4e9cfcSTakashi Iwai 			      pval & 0xff00,
10790b4e9cfcSTakashi Iwai 			      snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
10800b4e9cfcSTakashi Iwai 			      value, 2);
108147ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
10820b4e9cfcSTakashi Iwai 	return err;
10830b4e9cfcSTakashi Iwai }
10840b4e9cfcSTakashi Iwai 
1085d34bf148SFelix Homann static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
1086d34bf148SFelix Homann 					struct snd_ctl_elem_value *ucontrol)
1087d34bf148SFelix Homann {
10880b4e9cfcSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
10890b4e9cfcSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
10900b4e9cfcSTakashi Iwai 	int cur_val, err, new_val;
1091d34bf148SFelix Homann 
10920b4e9cfcSTakashi Iwai 	cur_val = pval >> 24;
1093d34bf148SFelix Homann 	new_val = ucontrol->value.enumerated.item[0];
10940b4e9cfcSTakashi Iwai 	if (cur_val == new_val)
10950b4e9cfcSTakashi Iwai 		return 0;
1096d34bf148SFelix Homann 
10970b4e9cfcSTakashi Iwai 	kctl->private_value &= ~(0xff << 24);
10980b4e9cfcSTakashi Iwai 	kctl->private_value |= new_val << 24;
10990b4e9cfcSTakashi Iwai 	err = snd_ftu_eff_switch_update(list);
11000b4e9cfcSTakashi Iwai 	return err < 0 ? err : 1;
11011a290581STakashi Iwai }
11021a290581STakashi Iwai 
1103d847ce0eSEldad Zack static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
1104d847ce0eSEldad Zack 	int validx, int bUnitID)
1105d34bf148SFelix Homann {
1106d34bf148SFelix Homann 	static struct snd_kcontrol_new template = {
1107d34bf148SFelix Homann 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1108d34bf148SFelix Homann 		.name = "Effect Program Switch",
1109d34bf148SFelix Homann 		.index = 0,
1110d34bf148SFelix Homann 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
1111d34bf148SFelix Homann 		.info = snd_ftu_eff_switch_info,
1112d34bf148SFelix Homann 		.get = snd_ftu_eff_switch_get,
1113d34bf148SFelix Homann 		.put = snd_ftu_eff_switch_put
1114d34bf148SFelix Homann 	};
11150b4e9cfcSTakashi Iwai 	struct usb_mixer_elem_list *list;
1116d34bf148SFelix Homann 	int err;
1117d34bf148SFelix Homann 
11180b4e9cfcSTakashi Iwai 	err = add_single_ctl_with_resume(mixer, bUnitID,
11190b4e9cfcSTakashi Iwai 					 snd_ftu_eff_switch_update,
11200b4e9cfcSTakashi Iwai 					 &template, &list);
1121d34bf148SFelix Homann 	if (err < 0)
1122d34bf148SFelix Homann 		return err;
11230b4e9cfcSTakashi Iwai 	list->kctl->private_value = (validx << 8) | bUnitID;
11240b4e9cfcSTakashi Iwai 	snd_ftu_eff_switch_init(mixer, list->kctl);
1125d34bf148SFelix Homann 	return 0;
1126d34bf148SFelix Homann }
1127d5a0bf6cSDaniel Mack 
1128cfe8f97cSFelix Homann /* Create volume controls for FTU devices*/
1129cfe8f97cSFelix Homann static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
1130d5a0bf6cSDaniel Mack {
1131d5a0bf6cSDaniel Mack 	char name[64];
11328a4d1d39SFelix Homann 	unsigned int control, cmask;
1133d5a0bf6cSDaniel Mack 	int in, out, err;
1134d5a0bf6cSDaniel Mack 
11358a4d1d39SFelix Homann 	const unsigned int id = 5;
11368a4d1d39SFelix Homann 	const int val_type = USB_MIXER_S16;
11378a4d1d39SFelix Homann 
1138d5a0bf6cSDaniel Mack 	for (out = 0; out < 8; out++) {
11398a4d1d39SFelix Homann 		control = out + 1;
1140d5a0bf6cSDaniel Mack 		for (in = 0; in < 8; in++) {
11418a4d1d39SFelix Homann 			cmask = 1 << in;
1142d5a0bf6cSDaniel Mack 			snprintf(name, sizeof(name),
11438a4d1d39SFelix Homann 				"AIn%d - Out%d Capture Volume",
11448a4d1d39SFelix Homann 				in  + 1, out + 1);
11458a4d1d39SFelix Homann 			err = snd_create_std_mono_ctl(mixer, id, control,
11468a4d1d39SFelix Homann 							cmask, val_type, name,
114725ee7ef8SFelix Homann 							&snd_usb_mixer_vol_tlv);
1148d5a0bf6cSDaniel Mack 			if (err < 0)
1149d5a0bf6cSDaniel Mack 				return err;
1150d5a0bf6cSDaniel Mack 		}
1151d5a0bf6cSDaniel Mack 		for (in = 8; in < 16; in++) {
11528a4d1d39SFelix Homann 			cmask = 1 << in;
1153d5a0bf6cSDaniel Mack 			snprintf(name, sizeof(name),
11548a4d1d39SFelix Homann 				"DIn%d - Out%d Playback Volume",
11558a4d1d39SFelix Homann 				in - 7, out + 1);
11568a4d1d39SFelix Homann 			err = snd_create_std_mono_ctl(mixer, id, control,
11578a4d1d39SFelix Homann 							cmask, val_type, name,
115825ee7ef8SFelix Homann 							&snd_usb_mixer_vol_tlv);
1159d5a0bf6cSDaniel Mack 			if (err < 0)
1160d5a0bf6cSDaniel Mack 				return err;
1161d5a0bf6cSDaniel Mack 		}
1162d5a0bf6cSDaniel Mack 	}
1163d5a0bf6cSDaniel Mack 
1164d5a0bf6cSDaniel Mack 	return 0;
1165d5a0bf6cSDaniel Mack }
1166d5a0bf6cSDaniel Mack 
1167d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
1168d34bf148SFelix Homann static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
1169d34bf148SFelix Homann {
1170d34bf148SFelix Homann 	static const char name[] = "Effect Volume";
1171d34bf148SFelix Homann 	const unsigned int id = 6;
1172d34bf148SFelix Homann 	const int val_type = USB_MIXER_U8;
1173d34bf148SFelix Homann 	const unsigned int control = 2;
1174d34bf148SFelix Homann 	const unsigned int cmask = 0;
1175d34bf148SFelix Homann 
1176d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1177d34bf148SFelix Homann 					name, snd_usb_mixer_vol_tlv);
1178d34bf148SFelix Homann }
1179d34bf148SFelix Homann 
1180d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
1181d34bf148SFelix Homann static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
1182d34bf148SFelix Homann {
1183d34bf148SFelix Homann 	static const char name[] = "Effect Duration";
1184d34bf148SFelix Homann 	const unsigned int id = 6;
1185d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1186d34bf148SFelix Homann 	const unsigned int control = 3;
1187d34bf148SFelix Homann 	const unsigned int cmask = 0;
1188d34bf148SFelix Homann 
1189d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1190d34bf148SFelix Homann 					name, snd_usb_mixer_vol_tlv);
1191d34bf148SFelix Homann }
1192d34bf148SFelix Homann 
1193d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
1194d34bf148SFelix Homann static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
1195d34bf148SFelix Homann {
1196d34bf148SFelix Homann 	static const char name[] = "Effect Feedback Volume";
1197d34bf148SFelix Homann 	const unsigned int id = 6;
1198d34bf148SFelix Homann 	const int val_type = USB_MIXER_U8;
1199d34bf148SFelix Homann 	const unsigned int control = 4;
1200d34bf148SFelix Homann 	const unsigned int cmask = 0;
1201d34bf148SFelix Homann 
1202d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1203d34bf148SFelix Homann 					name, NULL);
1204d34bf148SFelix Homann }
1205d34bf148SFelix Homann 
1206d34bf148SFelix Homann static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
1207d34bf148SFelix Homann {
1208d34bf148SFelix Homann 	unsigned int cmask;
1209d34bf148SFelix Homann 	int err, ch;
1210d34bf148SFelix Homann 	char name[48];
1211d34bf148SFelix Homann 
1212d34bf148SFelix Homann 	const unsigned int id = 7;
1213d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1214d34bf148SFelix Homann 	const unsigned int control = 7;
1215d34bf148SFelix Homann 
1216d34bf148SFelix Homann 	for (ch = 0; ch < 4; ++ch) {
1217d34bf148SFelix Homann 		cmask = 1 << ch;
1218d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1219d34bf148SFelix Homann 			"Effect Return %d Volume", ch + 1);
1220d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control,
1221d34bf148SFelix Homann 						cmask, val_type, name,
1222d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1223d34bf148SFelix Homann 		if (err < 0)
1224d34bf148SFelix Homann 			return err;
1225d34bf148SFelix Homann 	}
1226d34bf148SFelix Homann 
1227d34bf148SFelix Homann 	return 0;
1228d34bf148SFelix Homann }
1229d34bf148SFelix Homann 
1230d34bf148SFelix Homann static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
1231d34bf148SFelix Homann {
1232d34bf148SFelix Homann 	unsigned int  cmask;
1233d34bf148SFelix Homann 	int err, ch;
1234d34bf148SFelix Homann 	char name[48];
1235d34bf148SFelix Homann 
1236d34bf148SFelix Homann 	const unsigned int id = 5;
1237d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1238d34bf148SFelix Homann 	const unsigned int control = 9;
1239d34bf148SFelix Homann 
1240d34bf148SFelix Homann 	for (ch = 0; ch < 8; ++ch) {
1241d34bf148SFelix Homann 		cmask = 1 << ch;
1242d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1243d34bf148SFelix Homann 			"Effect Send AIn%d Volume", ch + 1);
1244d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
1245d34bf148SFelix Homann 						val_type, name,
1246d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1247d34bf148SFelix Homann 		if (err < 0)
1248d34bf148SFelix Homann 			return err;
1249d34bf148SFelix Homann 	}
1250d34bf148SFelix Homann 	for (ch = 8; ch < 16; ++ch) {
1251d34bf148SFelix Homann 		cmask = 1 << ch;
1252d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1253d34bf148SFelix Homann 			"Effect Send DIn%d Volume", ch - 7);
1254d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
1255d34bf148SFelix Homann 						val_type, name,
1256d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1257d34bf148SFelix Homann 		if (err < 0)
1258d34bf148SFelix Homann 			return err;
1259d34bf148SFelix Homann 	}
1260d34bf148SFelix Homann 	return 0;
1261d34bf148SFelix Homann }
1262d34bf148SFelix Homann 
1263cfe8f97cSFelix Homann static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
12647536c301SMark Hills {
12658a4d1d39SFelix Homann 	int err;
12667536c301SMark Hills 
1267cfe8f97cSFelix Homann 	err = snd_ftu_create_volume_ctls(mixer);
12688a4d1d39SFelix Homann 	if (err < 0)
12698a4d1d39SFelix Homann 		return err;
12707536c301SMark Hills 
1271d847ce0eSEldad Zack 	err = snd_ftu_create_effect_switch(mixer, 1, 6);
1272d34bf148SFelix Homann 	if (err < 0)
1273d34bf148SFelix Homann 		return err;
1274d847ce0eSEldad Zack 
1275d34bf148SFelix Homann 	err = snd_ftu_create_effect_volume_ctl(mixer);
1276d34bf148SFelix Homann 	if (err < 0)
1277d34bf148SFelix Homann 		return err;
1278d34bf148SFelix Homann 
1279d34bf148SFelix Homann 	err = snd_ftu_create_effect_duration_ctl(mixer);
1280d34bf148SFelix Homann 	if (err < 0)
1281d34bf148SFelix Homann 		return err;
1282d34bf148SFelix Homann 
1283d34bf148SFelix Homann 	err = snd_ftu_create_effect_feedback_ctl(mixer);
1284d34bf148SFelix Homann 	if (err < 0)
1285d34bf148SFelix Homann 		return err;
1286d34bf148SFelix Homann 
1287d34bf148SFelix Homann 	err = snd_ftu_create_effect_return_ctls(mixer);
1288d34bf148SFelix Homann 	if (err < 0)
1289d34bf148SFelix Homann 		return err;
1290d34bf148SFelix Homann 
1291d34bf148SFelix Homann 	err = snd_ftu_create_effect_send_ctls(mixer);
1292d34bf148SFelix Homann 	if (err < 0)
1293d34bf148SFelix Homann 		return err;
1294d34bf148SFelix Homann 
12958a4d1d39SFelix Homann 	return 0;
12967536c301SMark Hills }
12977536c301SMark Hills 
12987b1eda22SDaniel Mack void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
12997b1eda22SDaniel Mack 			       unsigned char samplerate_id)
13007b1eda22SDaniel Mack {
13017b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer;
13027b1eda22SDaniel Mack 	struct usb_mixer_elem_info *cval;
13036de3c9e3STakashi Iwai 	int unitid = 12; /* SampleRate ExtensionUnit ID */
13047b1eda22SDaniel Mack 
13057b1eda22SDaniel Mack 	list_for_each_entry(mixer, &chip->mixer_list, list) {
13066de3c9e3STakashi Iwai 		if (mixer->id_elems[unitid]) {
13078c558076STakashi Iwai 			cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
13087b1eda22SDaniel Mack 			snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
13097b1eda22SDaniel Mack 						    cval->control << 8,
13107b1eda22SDaniel Mack 						    samplerate_id);
13117b1eda22SDaniel Mack 			snd_usb_mixer_notify_id(mixer, unitid);
13127b1eda22SDaniel Mack 			break;
13137b1eda22SDaniel Mack 		}
13147b1eda22SDaniel Mack 	}
13156de3c9e3STakashi Iwai }
13167b1eda22SDaniel Mack 
1317e9a25e04SMatt Gruskin /* M-Audio Fast Track C400/C600 */
1318e9a25e04SMatt Gruskin /* C400/C600 volume controls, this control needs a volume quirk, see mixer.c */
131909d8e3a7SEldad Zack static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
132009d8e3a7SEldad Zack {
132109d8e3a7SEldad Zack 	char name[64];
132209d8e3a7SEldad Zack 	unsigned int cmask, offset;
132309d8e3a7SEldad Zack 	int out, chan, err;
1324e9a25e04SMatt Gruskin 	int num_outs = 0;
1325e9a25e04SMatt Gruskin 	int num_ins = 0;
132609d8e3a7SEldad Zack 
132709d8e3a7SEldad Zack 	const unsigned int id = 0x40;
132809d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
132909d8e3a7SEldad Zack 	const int control = 1;
133009d8e3a7SEldad Zack 
1331e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1332e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1333e9a25e04SMatt Gruskin 		num_outs = 6;
1334e9a25e04SMatt Gruskin 		num_ins = 4;
1335e9a25e04SMatt Gruskin 		break;
1336e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1337e9a25e04SMatt Gruskin 		num_outs = 8;
1338e9a25e04SMatt Gruskin 		num_ins = 6;
1339e9a25e04SMatt Gruskin 		break;
1340e9a25e04SMatt Gruskin 	}
1341e9a25e04SMatt Gruskin 
1342e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs + num_ins; chan++) {
1343e9a25e04SMatt Gruskin 		for (out = 0; out < num_outs; out++) {
1344e9a25e04SMatt Gruskin 			if (chan < num_outs) {
134509d8e3a7SEldad Zack 				snprintf(name, sizeof(name),
134609d8e3a7SEldad Zack 					"PCM%d-Out%d Playback Volume",
134709d8e3a7SEldad Zack 					chan + 1, out + 1);
134809d8e3a7SEldad Zack 			} else {
134909d8e3a7SEldad Zack 				snprintf(name, sizeof(name),
135009d8e3a7SEldad Zack 					"In%d-Out%d Playback Volume",
1351e9a25e04SMatt Gruskin 					chan - num_outs + 1, out + 1);
135209d8e3a7SEldad Zack 			}
135309d8e3a7SEldad Zack 
135409d8e3a7SEldad Zack 			cmask = (out == 0) ? 0 : 1 << (out - 1);
1355e9a25e04SMatt Gruskin 			offset = chan * num_outs;
135609d8e3a7SEldad Zack 			err = snd_create_std_mono_ctl_offset(mixer, id, control,
135709d8e3a7SEldad Zack 						cmask, val_type, offset, name,
135809d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
135909d8e3a7SEldad Zack 			if (err < 0)
136009d8e3a7SEldad Zack 				return err;
136109d8e3a7SEldad Zack 		}
136209d8e3a7SEldad Zack 	}
136309d8e3a7SEldad Zack 
136409d8e3a7SEldad Zack 	return 0;
136509d8e3a7SEldad Zack }
136609d8e3a7SEldad Zack 
136709d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
136809d8e3a7SEldad Zack static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
136909d8e3a7SEldad Zack {
137009d8e3a7SEldad Zack 	static const char name[] = "Effect Volume";
137109d8e3a7SEldad Zack 	const unsigned int id = 0x43;
137209d8e3a7SEldad Zack 	const int val_type = USB_MIXER_U8;
137309d8e3a7SEldad Zack 	const unsigned int control = 3;
137409d8e3a7SEldad Zack 	const unsigned int cmask = 0;
137509d8e3a7SEldad Zack 
137609d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
137709d8e3a7SEldad Zack 					name, snd_usb_mixer_vol_tlv);
137809d8e3a7SEldad Zack }
137909d8e3a7SEldad Zack 
138009d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
138109d8e3a7SEldad Zack static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
138209d8e3a7SEldad Zack {
138309d8e3a7SEldad Zack 	static const char name[] = "Effect Duration";
138409d8e3a7SEldad Zack 	const unsigned int id = 0x43;
138509d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
138609d8e3a7SEldad Zack 	const unsigned int control = 4;
138709d8e3a7SEldad Zack 	const unsigned int cmask = 0;
138809d8e3a7SEldad Zack 
138909d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
139009d8e3a7SEldad Zack 					name, snd_usb_mixer_vol_tlv);
139109d8e3a7SEldad Zack }
139209d8e3a7SEldad Zack 
139309d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
139409d8e3a7SEldad Zack static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
139509d8e3a7SEldad Zack {
139609d8e3a7SEldad Zack 	static const char name[] = "Effect Feedback Volume";
139709d8e3a7SEldad Zack 	const unsigned int id = 0x43;
139809d8e3a7SEldad Zack 	const int val_type = USB_MIXER_U8;
139909d8e3a7SEldad Zack 	const unsigned int control = 5;
140009d8e3a7SEldad Zack 	const unsigned int cmask = 0;
140109d8e3a7SEldad Zack 
140209d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
140309d8e3a7SEldad Zack 					name, NULL);
140409d8e3a7SEldad Zack }
140509d8e3a7SEldad Zack 
140609d8e3a7SEldad Zack static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
140709d8e3a7SEldad Zack {
140809d8e3a7SEldad Zack 	char name[64];
140909d8e3a7SEldad Zack 	unsigned int cmask;
141009d8e3a7SEldad Zack 	int chan, err;
1411e9a25e04SMatt Gruskin 	int num_outs = 0;
1412e9a25e04SMatt Gruskin 	int num_ins = 0;
141309d8e3a7SEldad Zack 
141409d8e3a7SEldad Zack 	const unsigned int id = 0x42;
141509d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
141609d8e3a7SEldad Zack 	const int control = 1;
141709d8e3a7SEldad Zack 
1418e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1419e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1420e9a25e04SMatt Gruskin 		num_outs = 6;
1421e9a25e04SMatt Gruskin 		num_ins = 4;
1422e9a25e04SMatt Gruskin 		break;
1423e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1424e9a25e04SMatt Gruskin 		num_outs = 8;
1425e9a25e04SMatt Gruskin 		num_ins = 6;
1426e9a25e04SMatt Gruskin 		break;
1427e9a25e04SMatt Gruskin 	}
1428e9a25e04SMatt Gruskin 
1429e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs + num_ins; chan++) {
1430e9a25e04SMatt Gruskin 		if (chan < num_outs) {
143109d8e3a7SEldad Zack 			snprintf(name, sizeof(name),
143209d8e3a7SEldad Zack 				"Effect Send DOut%d",
143309d8e3a7SEldad Zack 				chan + 1);
143409d8e3a7SEldad Zack 		} else {
143509d8e3a7SEldad Zack 			snprintf(name, sizeof(name),
143609d8e3a7SEldad Zack 				"Effect Send AIn%d",
1437e9a25e04SMatt Gruskin 				chan - num_outs + 1);
143809d8e3a7SEldad Zack 		}
143909d8e3a7SEldad Zack 
144009d8e3a7SEldad Zack 		cmask = (chan == 0) ? 0 : 1 << (chan - 1);
144109d8e3a7SEldad Zack 		err = snd_create_std_mono_ctl(mixer, id, control,
144209d8e3a7SEldad Zack 						cmask, val_type, name,
144309d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
144409d8e3a7SEldad Zack 		if (err < 0)
144509d8e3a7SEldad Zack 			return err;
144609d8e3a7SEldad Zack 	}
144709d8e3a7SEldad Zack 
144809d8e3a7SEldad Zack 	return 0;
144909d8e3a7SEldad Zack }
145009d8e3a7SEldad Zack 
145109d8e3a7SEldad Zack static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer)
145209d8e3a7SEldad Zack {
145309d8e3a7SEldad Zack 	char name[64];
145409d8e3a7SEldad Zack 	unsigned int cmask;
145509d8e3a7SEldad Zack 	int chan, err;
1456e9a25e04SMatt Gruskin 	int num_outs = 0;
1457e9a25e04SMatt Gruskin 	int offset = 0;
145809d8e3a7SEldad Zack 
145909d8e3a7SEldad Zack 	const unsigned int id = 0x40;
146009d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
146109d8e3a7SEldad Zack 	const int control = 1;
146209d8e3a7SEldad Zack 
1463e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1464e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1465e9a25e04SMatt Gruskin 		num_outs = 6;
1466e9a25e04SMatt Gruskin 		offset = 0x3c;
1467e9a25e04SMatt Gruskin 		/* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */
1468e9a25e04SMatt Gruskin 		break;
1469e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1470e9a25e04SMatt Gruskin 		num_outs = 8;
1471e9a25e04SMatt Gruskin 		offset = 0x70;
1472e9a25e04SMatt Gruskin 		/* { 0x70, 0x79, 0x72, 0x7b, 0x74, 0x7d, 0x76, 0x7f } */
1473e9a25e04SMatt Gruskin 		break;
1474e9a25e04SMatt Gruskin 	}
1475e9a25e04SMatt Gruskin 
1476e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs; chan++) {
147709d8e3a7SEldad Zack 		snprintf(name, sizeof(name),
147809d8e3a7SEldad Zack 			"Effect Return %d",
147909d8e3a7SEldad Zack 			chan + 1);
148009d8e3a7SEldad Zack 
1481e9a25e04SMatt Gruskin 		cmask = (chan == 0) ? 0 :
1482e9a25e04SMatt Gruskin 			1 << (chan + (chan % 2) * num_outs - 1);
148309d8e3a7SEldad Zack 		err = snd_create_std_mono_ctl_offset(mixer, id, control,
148409d8e3a7SEldad Zack 						cmask, val_type, offset, name,
148509d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
148609d8e3a7SEldad Zack 		if (err < 0)
148709d8e3a7SEldad Zack 			return err;
148809d8e3a7SEldad Zack 	}
148909d8e3a7SEldad Zack 
149009d8e3a7SEldad Zack 	return 0;
149109d8e3a7SEldad Zack }
149209d8e3a7SEldad Zack 
149309d8e3a7SEldad Zack static int snd_c400_create_mixer(struct usb_mixer_interface *mixer)
149409d8e3a7SEldad Zack {
149509d8e3a7SEldad Zack 	int err;
149609d8e3a7SEldad Zack 
149709d8e3a7SEldad Zack 	err = snd_c400_create_vol_ctls(mixer);
149809d8e3a7SEldad Zack 	if (err < 0)
149909d8e3a7SEldad Zack 		return err;
150009d8e3a7SEldad Zack 
150109d8e3a7SEldad Zack 	err = snd_c400_create_effect_vol_ctls(mixer);
150209d8e3a7SEldad Zack 	if (err < 0)
150309d8e3a7SEldad Zack 		return err;
150409d8e3a7SEldad Zack 
150509d8e3a7SEldad Zack 	err = snd_c400_create_effect_ret_vol_ctls(mixer);
150609d8e3a7SEldad Zack 	if (err < 0)
150709d8e3a7SEldad Zack 		return err;
150809d8e3a7SEldad Zack 
150909d8e3a7SEldad Zack 	err = snd_ftu_create_effect_switch(mixer, 2, 0x43);
151009d8e3a7SEldad Zack 	if (err < 0)
151109d8e3a7SEldad Zack 		return err;
151209d8e3a7SEldad Zack 
151309d8e3a7SEldad Zack 	err = snd_c400_create_effect_volume_ctl(mixer);
151409d8e3a7SEldad Zack 	if (err < 0)
151509d8e3a7SEldad Zack 		return err;
151609d8e3a7SEldad Zack 
151709d8e3a7SEldad Zack 	err = snd_c400_create_effect_duration_ctl(mixer);
151809d8e3a7SEldad Zack 	if (err < 0)
151909d8e3a7SEldad Zack 		return err;
152009d8e3a7SEldad Zack 
152109d8e3a7SEldad Zack 	err = snd_c400_create_effect_feedback_ctl(mixer);
152209d8e3a7SEldad Zack 	if (err < 0)
152309d8e3a7SEldad Zack 		return err;
152409d8e3a7SEldad Zack 
152509d8e3a7SEldad Zack 	return 0;
152609d8e3a7SEldad Zack }
152709d8e3a7SEldad Zack 
1528b71dad18SMark Hills /*
1529b71dad18SMark Hills  * The mixer units for Ebox-44 are corrupt, and even where they
1530b71dad18SMark Hills  * are valid they presents mono controls as L and R channels of
1531b71dad18SMark Hills  * stereo. So we provide a good mixer here.
1532b71dad18SMark Hills  */
1533a01df925STakashi Iwai static const struct std_mono_table ebox44_table[] = {
1534989b0138SMark Hills 	{
1535989b0138SMark Hills 		.unitid = 4,
1536989b0138SMark Hills 		.control = 1,
1537989b0138SMark Hills 		.cmask = 0x0,
1538989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1539989b0138SMark Hills 		.name = "Headphone Playback Switch"
1540989b0138SMark Hills 	},
1541989b0138SMark Hills 	{
1542989b0138SMark Hills 		.unitid = 4,
1543989b0138SMark Hills 		.control = 2,
1544989b0138SMark Hills 		.cmask = 0x1,
1545989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1546989b0138SMark Hills 		.name = "Headphone A Mix Playback Volume"
1547989b0138SMark Hills 	},
1548989b0138SMark Hills 	{
1549989b0138SMark Hills 		.unitid = 4,
1550989b0138SMark Hills 		.control = 2,
1551989b0138SMark Hills 		.cmask = 0x2,
1552989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1553989b0138SMark Hills 		.name = "Headphone B Mix Playback Volume"
1554989b0138SMark Hills 	},
1555b71dad18SMark Hills 
1556989b0138SMark Hills 	{
1557989b0138SMark Hills 		.unitid = 7,
1558989b0138SMark Hills 		.control = 1,
1559989b0138SMark Hills 		.cmask = 0x0,
1560989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1561989b0138SMark Hills 		.name = "Output Playback Switch"
1562989b0138SMark Hills 	},
1563989b0138SMark Hills 	{
1564989b0138SMark Hills 		.unitid = 7,
1565989b0138SMark Hills 		.control = 2,
1566989b0138SMark Hills 		.cmask = 0x1,
1567989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1568989b0138SMark Hills 		.name = "Output A Playback Volume"
1569989b0138SMark Hills 	},
1570989b0138SMark Hills 	{
1571989b0138SMark Hills 		.unitid = 7,
1572989b0138SMark Hills 		.control = 2,
1573989b0138SMark Hills 		.cmask = 0x2,
1574989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1575989b0138SMark Hills 		.name = "Output B Playback Volume"
1576989b0138SMark Hills 	},
1577b71dad18SMark Hills 
1578989b0138SMark Hills 	{
1579989b0138SMark Hills 		.unitid = 10,
1580989b0138SMark Hills 		.control = 1,
1581989b0138SMark Hills 		.cmask = 0x0,
1582989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1583989b0138SMark Hills 		.name = "Input Capture Switch"
1584989b0138SMark Hills 	},
1585989b0138SMark Hills 	{
1586989b0138SMark Hills 		.unitid = 10,
1587989b0138SMark Hills 		.control = 2,
1588989b0138SMark Hills 		.cmask = 0x1,
1589989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1590989b0138SMark Hills 		.name = "Input A Capture Volume"
1591989b0138SMark Hills 	},
1592989b0138SMark Hills 	{
1593989b0138SMark Hills 		.unitid = 10,
1594989b0138SMark Hills 		.control = 2,
1595989b0138SMark Hills 		.cmask = 0x2,
1596989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1597989b0138SMark Hills 		.name = "Input B Capture Volume"
1598989b0138SMark Hills 	},
1599b71dad18SMark Hills 
1600b71dad18SMark Hills 	{}
1601b71dad18SMark Hills };
1602b71dad18SMark Hills 
1603066624c6SPrzemek Rudy /* Audio Advantage Micro II findings:
1604066624c6SPrzemek Rudy  *
1605066624c6SPrzemek Rudy  * Mapping spdif AES bits to vendor register.bit:
1606066624c6SPrzemek Rudy  * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00
1607066624c6SPrzemek Rudy  * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01
1608066624c6SPrzemek Rudy  * AES2: [0 0 0 0 0 0 0 0]
1609066624c6SPrzemek Rudy  * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request
1610066624c6SPrzemek Rudy  *                           (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices
1611066624c6SPrzemek Rudy  *
1612066624c6SPrzemek Rudy  * power on values:
1613066624c6SPrzemek Rudy  * r2: 0x10
1614066624c6SPrzemek Rudy  * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set
1615066624c6SPrzemek Rudy  *           just after it to 0xa0, presumably it disables/mutes some analog
1616066624c6SPrzemek Rudy  *           parts when there is no audio.)
1617066624c6SPrzemek Rudy  * r9: 0x28
1618066624c6SPrzemek Rudy  *
1619066624c6SPrzemek Rudy  * Optical transmitter on/off:
1620066624c6SPrzemek Rudy  * vendor register.bit: 9.1
1621066624c6SPrzemek Rudy  * 0 - on (0x28 register value)
1622066624c6SPrzemek Rudy  * 1 - off (0x2a register value)
1623066624c6SPrzemek Rudy  *
1624066624c6SPrzemek Rudy  */
1625066624c6SPrzemek Rudy static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
1626066624c6SPrzemek Rudy 	struct snd_ctl_elem_info *uinfo)
1627066624c6SPrzemek Rudy {
1628066624c6SPrzemek Rudy 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
1629066624c6SPrzemek Rudy 	uinfo->count = 1;
1630066624c6SPrzemek Rudy 	return 0;
1631066624c6SPrzemek Rudy }
1632066624c6SPrzemek Rudy 
1633066624c6SPrzemek Rudy static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
1634066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1635066624c6SPrzemek Rudy {
1636288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1637288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1638066624c6SPrzemek Rudy 	int err;
1639066624c6SPrzemek Rudy 	struct usb_interface *iface;
1640066624c6SPrzemek Rudy 	struct usb_host_interface *alts;
1641066624c6SPrzemek Rudy 	unsigned int ep;
1642066624c6SPrzemek Rudy 	unsigned char data[3];
1643066624c6SPrzemek Rudy 	int rate;
1644066624c6SPrzemek Rudy 
164547ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
164647ab1545STakashi Iwai 	if (err < 0)
164747ab1545STakashi Iwai 		return err;
1648288673beSTakashi Iwai 
1649066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
1650066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
1651066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[2] = 0x00;
1652066624c6SPrzemek Rudy 
1653066624c6SPrzemek Rudy 	/* use known values for that card: interface#1 altsetting#1 */
1654288673beSTakashi Iwai 	iface = usb_ifnum_to_if(chip->dev, 1);
165559e1947cSXiyu Yang 	if (!iface || iface->num_altsetting < 2) {
165659e1947cSXiyu Yang 		err = -EINVAL;
165759e1947cSXiyu Yang 		goto end;
165859e1947cSXiyu Yang 	}
1659066624c6SPrzemek Rudy 	alts = &iface->altsetting[1];
166059e1947cSXiyu Yang 	if (get_iface_desc(alts)->bNumEndpoints < 1) {
166159e1947cSXiyu Yang 		err = -EINVAL;
166259e1947cSXiyu Yang 		goto end;
166359e1947cSXiyu Yang 	}
1664066624c6SPrzemek Rudy 	ep = get_endpoint(alts, 0)->bEndpointAddress;
1665066624c6SPrzemek Rudy 
1666288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1667288673beSTakashi Iwai 			usb_rcvctrlpipe(chip->dev, 0),
1668066624c6SPrzemek Rudy 			UAC_GET_CUR,
1669066624c6SPrzemek Rudy 			USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
1670066624c6SPrzemek Rudy 			UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
1671066624c6SPrzemek Rudy 			ep,
1672066624c6SPrzemek Rudy 			data,
1673066624c6SPrzemek Rudy 			sizeof(data));
1674066624c6SPrzemek Rudy 	if (err < 0)
1675066624c6SPrzemek Rudy 		goto end;
1676066624c6SPrzemek Rudy 
1677066624c6SPrzemek Rudy 	rate = data[0] | (data[1] << 8) | (data[2] << 16);
1678066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[3] = (rate == 48000) ?
1679066624c6SPrzemek Rudy 			IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
1680066624c6SPrzemek Rudy 
1681066624c6SPrzemek Rudy 	err = 0;
1682066624c6SPrzemek Rudy  end:
168347ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1684066624c6SPrzemek Rudy 	return err;
1685066624c6SPrzemek Rudy }
1686066624c6SPrzemek Rudy 
1687288673beSTakashi Iwai static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
1688066624c6SPrzemek Rudy {
1689288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1690288673beSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
1691066624c6SPrzemek Rudy 	u8 reg;
1692288673beSTakashi Iwai 	int err;
1693066624c6SPrzemek Rudy 
169447ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
169547ab1545STakashi Iwai 	if (err < 0)
169647ab1545STakashi Iwai 		return err;
1697288673beSTakashi Iwai 
1698288673beSTakashi Iwai 	reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
1699288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1700288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1701066624c6SPrzemek Rudy 			UAC_SET_CUR,
1702066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1703066624c6SPrzemek Rudy 			reg,
1704066624c6SPrzemek Rudy 			2,
1705066624c6SPrzemek Rudy 			NULL,
1706066624c6SPrzemek Rudy 			0);
1707066624c6SPrzemek Rudy 	if (err < 0)
1708066624c6SPrzemek Rudy 		goto end;
1709066624c6SPrzemek Rudy 
1710288673beSTakashi Iwai 	reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
1711288673beSTakashi Iwai 	reg |= (pval >> 12) & 0x0f;
1712288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1713288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1714066624c6SPrzemek Rudy 			UAC_SET_CUR,
1715066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1716066624c6SPrzemek Rudy 			reg,
1717066624c6SPrzemek Rudy 			3,
1718066624c6SPrzemek Rudy 			NULL,
1719066624c6SPrzemek Rudy 			0);
1720066624c6SPrzemek Rudy 	if (err < 0)
1721066624c6SPrzemek Rudy 		goto end;
1722066624c6SPrzemek Rudy 
1723288673beSTakashi Iwai  end:
172447ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1725288673beSTakashi Iwai 	return err;
1726288673beSTakashi Iwai }
1727288673beSTakashi Iwai 
1728288673beSTakashi Iwai static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
1729288673beSTakashi Iwai 	struct snd_ctl_elem_value *ucontrol)
1730288673beSTakashi Iwai {
1731288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1732288673beSTakashi Iwai 	unsigned int pval, pval_old;
1733288673beSTakashi Iwai 	int err;
1734288673beSTakashi Iwai 
1735288673beSTakashi Iwai 	pval = pval_old = kcontrol->private_value;
1736288673beSTakashi Iwai 	pval &= 0xfffff0f0;
1737288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
1738288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[0] & 0x0f);
1739288673beSTakashi Iwai 
1740288673beSTakashi Iwai 	pval &= 0xffff0fff;
1741288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
1742066624c6SPrzemek Rudy 
1743066624c6SPrzemek Rudy 	/* The frequency bits in AES3 cannot be set via register access. */
1744066624c6SPrzemek Rudy 
1745066624c6SPrzemek Rudy 	/* Silently ignore any bits from the request that cannot be set. */
1746066624c6SPrzemek Rudy 
1747288673beSTakashi Iwai 	if (pval == pval_old)
1748288673beSTakashi Iwai 		return 0;
1749288673beSTakashi Iwai 
1750288673beSTakashi Iwai 	kcontrol->private_value = pval;
1751288673beSTakashi Iwai 	err = snd_microii_spdif_default_update(list);
1752288673beSTakashi Iwai 	return err < 0 ? err : 1;
1753066624c6SPrzemek Rudy }
1754066624c6SPrzemek Rudy 
1755066624c6SPrzemek Rudy static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
1756066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1757066624c6SPrzemek Rudy {
1758066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[0] = 0x0f;
1759066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[1] = 0xff;
1760066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[2] = 0x00;
1761066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[3] = 0x00;
1762066624c6SPrzemek Rudy 
1763066624c6SPrzemek Rudy 	return 0;
1764066624c6SPrzemek Rudy }
1765066624c6SPrzemek Rudy 
1766066624c6SPrzemek Rudy static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
1767066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1768066624c6SPrzemek Rudy {
1769066624c6SPrzemek Rudy 	ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
1770066624c6SPrzemek Rudy 
1771066624c6SPrzemek Rudy 	return 0;
1772066624c6SPrzemek Rudy }
1773066624c6SPrzemek Rudy 
1774288673beSTakashi Iwai static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
1775066624c6SPrzemek Rudy {
1776288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1777288673beSTakashi Iwai 	u8 reg = list->kctl->private_value;
1778066624c6SPrzemek Rudy 	int err;
1779066624c6SPrzemek Rudy 
178047ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
178147ab1545STakashi Iwai 	if (err < 0)
178247ab1545STakashi Iwai 		return err;
1783288673beSTakashi Iwai 
1784288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1785288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1786066624c6SPrzemek Rudy 			UAC_SET_CUR,
1787066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1788066624c6SPrzemek Rudy 			reg,
1789066624c6SPrzemek Rudy 			9,
1790066624c6SPrzemek Rudy 			NULL,
1791066624c6SPrzemek Rudy 			0);
1792066624c6SPrzemek Rudy 
179347ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1794288673beSTakashi Iwai 	return err;
1795066624c6SPrzemek Rudy }
1796066624c6SPrzemek Rudy 
1797288673beSTakashi Iwai static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
1798288673beSTakashi Iwai 	struct snd_ctl_elem_value *ucontrol)
1799288673beSTakashi Iwai {
1800288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1801288673beSTakashi Iwai 	u8 reg;
1802288673beSTakashi Iwai 	int err;
1803288673beSTakashi Iwai 
1804288673beSTakashi Iwai 	reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
1805288673beSTakashi Iwai 	if (reg != list->kctl->private_value)
1806288673beSTakashi Iwai 		return 0;
1807288673beSTakashi Iwai 
1808288673beSTakashi Iwai 	kcontrol->private_value = reg;
1809288673beSTakashi Iwai 	err = snd_microii_spdif_switch_update(list);
1810288673beSTakashi Iwai 	return err < 0 ? err : 1;
1811066624c6SPrzemek Rudy }
1812066624c6SPrzemek Rudy 
1813195727e8STakashi Iwai static const struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
1814066624c6SPrzemek Rudy 	{
1815066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_PCM,
1816066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
1817066624c6SPrzemek Rudy 		.info =     snd_microii_spdif_info,
1818066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_default_get,
1819066624c6SPrzemek Rudy 		.put =      snd_microii_spdif_default_put,
1820066624c6SPrzemek Rudy 		.private_value = 0x00000100UL,/* reset value */
1821066624c6SPrzemek Rudy 	},
1822066624c6SPrzemek Rudy 	{
1823066624c6SPrzemek Rudy 		.access =   SNDRV_CTL_ELEM_ACCESS_READ,
1824066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_PCM,
1825066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
1826066624c6SPrzemek Rudy 		.info =     snd_microii_spdif_info,
1827066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_mask_get,
1828066624c6SPrzemek Rudy 	},
1829066624c6SPrzemek Rudy 	{
1830066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_MIXER,
1831066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
1832066624c6SPrzemek Rudy 		.info =     snd_ctl_boolean_mono_info,
1833066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_switch_get,
1834066624c6SPrzemek Rudy 		.put =      snd_microii_spdif_switch_put,
1835066624c6SPrzemek Rudy 		.private_value = 0x00000028UL,/* reset value */
1836066624c6SPrzemek Rudy 	}
1837066624c6SPrzemek Rudy };
1838066624c6SPrzemek Rudy 
1839066624c6SPrzemek Rudy static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
1840066624c6SPrzemek Rudy {
1841066624c6SPrzemek Rudy 	int err, i;
1842ff40e0d4SPierre-Louis Bossart 	static const usb_mixer_elem_resume_func_t resume_funcs[] = {
1843288673beSTakashi Iwai 		snd_microii_spdif_default_update,
1844288673beSTakashi Iwai 		NULL,
1845288673beSTakashi Iwai 		snd_microii_spdif_switch_update
1846288673beSTakashi Iwai 	};
1847066624c6SPrzemek Rudy 
1848066624c6SPrzemek Rudy 	for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
1849288673beSTakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
1850288673beSTakashi Iwai 						 resume_funcs[i],
1851288673beSTakashi Iwai 						 &snd_microii_mixer_spdif[i],
1852288673beSTakashi Iwai 						 NULL);
1853066624c6SPrzemek Rudy 		if (err < 0)
1854066624c6SPrzemek Rudy 			return err;
1855066624c6SPrzemek Rudy 	}
1856066624c6SPrzemek Rudy 
185718e4753fSMikulas Patocka 	return 0;
1858066624c6SPrzemek Rudy }
1859066624c6SPrzemek Rudy 
1860388fdb8fSIan Douglas Scott /* Creative Sound Blaster E1 */
1861388fdb8fSIan Douglas Scott 
1862388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_get(struct snd_kcontrol *kcontrol,
1863388fdb8fSIan Douglas Scott 					  struct snd_ctl_elem_value *ucontrol)
1864388fdb8fSIan Douglas Scott {
1865388fdb8fSIan Douglas Scott 	ucontrol->value.integer.value[0] = kcontrol->private_value;
1866388fdb8fSIan Douglas Scott 	return 0;
1867388fdb8fSIan Douglas Scott }
1868388fdb8fSIan Douglas Scott 
1869388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_update(struct usb_mixer_interface *mixer,
1870388fdb8fSIan Douglas Scott 					     unsigned char state)
1871388fdb8fSIan Douglas Scott {
1872388fdb8fSIan Douglas Scott 	struct snd_usb_audio *chip = mixer->chip;
1873388fdb8fSIan Douglas Scott 	int err;
1874388fdb8fSIan Douglas Scott 	unsigned char buff[2];
1875388fdb8fSIan Douglas Scott 
1876388fdb8fSIan Douglas Scott 	buff[0] = 0x02;
1877388fdb8fSIan Douglas Scott 	buff[1] = state ? 0x02 : 0x00;
1878388fdb8fSIan Douglas Scott 
1879388fdb8fSIan Douglas Scott 	err = snd_usb_lock_shutdown(chip);
1880388fdb8fSIan Douglas Scott 	if (err < 0)
1881388fdb8fSIan Douglas Scott 		return err;
1882388fdb8fSIan Douglas Scott 	err = snd_usb_ctl_msg(chip->dev,
1883388fdb8fSIan Douglas Scott 			usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT,
1884388fdb8fSIan Douglas Scott 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
1885388fdb8fSIan Douglas Scott 			0x0202, 3, buff, 2);
1886388fdb8fSIan Douglas Scott 	snd_usb_unlock_shutdown(chip);
1887388fdb8fSIan Douglas Scott 	return err;
1888388fdb8fSIan Douglas Scott }
1889388fdb8fSIan Douglas Scott 
1890388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
1891388fdb8fSIan Douglas Scott 					  struct snd_ctl_elem_value *ucontrol)
1892388fdb8fSIan Douglas Scott {
1893388fdb8fSIan Douglas Scott 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1894388fdb8fSIan Douglas Scott 	unsigned char value = !!ucontrol->value.integer.value[0];
1895388fdb8fSIan Douglas Scott 	int err;
1896388fdb8fSIan Douglas Scott 
1897388fdb8fSIan Douglas Scott 	if (kcontrol->private_value == value)
1898388fdb8fSIan Douglas Scott 		return 0;
1899388fdb8fSIan Douglas Scott 	kcontrol->private_value = value;
1900388fdb8fSIan Douglas Scott 	err = snd_soundblaster_e1_switch_update(list->mixer, value);
1901388fdb8fSIan Douglas Scott 	return err < 0 ? err : 1;
1902388fdb8fSIan Douglas Scott }
1903388fdb8fSIan Douglas Scott 
1904388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list)
1905388fdb8fSIan Douglas Scott {
1906388fdb8fSIan Douglas Scott 	return snd_soundblaster_e1_switch_update(list->mixer,
1907388fdb8fSIan Douglas Scott 						 list->kctl->private_value);
1908388fdb8fSIan Douglas Scott }
1909388fdb8fSIan Douglas Scott 
1910388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_info(struct snd_kcontrol *kcontrol,
1911388fdb8fSIan Douglas Scott 					   struct snd_ctl_elem_info *uinfo)
1912388fdb8fSIan Douglas Scott {
1913388fdb8fSIan Douglas Scott 	static const char *const texts[2] = {
1914388fdb8fSIan Douglas Scott 		"Mic", "Aux"
1915388fdb8fSIan Douglas Scott 	};
1916388fdb8fSIan Douglas Scott 
1917388fdb8fSIan Douglas Scott 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
1918388fdb8fSIan Douglas Scott }
1919388fdb8fSIan Douglas Scott 
1920195727e8STakashi Iwai static const struct snd_kcontrol_new snd_soundblaster_e1_input_switch = {
1921388fdb8fSIan Douglas Scott 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1922388fdb8fSIan Douglas Scott 	.name = "Input Source",
1923388fdb8fSIan Douglas Scott 	.info = snd_soundblaster_e1_switch_info,
1924388fdb8fSIan Douglas Scott 	.get = snd_soundblaster_e1_switch_get,
1925388fdb8fSIan Douglas Scott 	.put = snd_soundblaster_e1_switch_put,
1926388fdb8fSIan Douglas Scott 	.private_value = 0,
1927388fdb8fSIan Douglas Scott };
1928388fdb8fSIan Douglas Scott 
1929388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer)
1930388fdb8fSIan Douglas Scott {
1931388fdb8fSIan Douglas Scott 	return add_single_ctl_with_resume(mixer, 0,
1932388fdb8fSIan Douglas Scott 					  snd_soundblaster_e1_switch_resume,
1933388fdb8fSIan Douglas Scott 					  &snd_soundblaster_e1_input_switch,
1934388fdb8fSIan Douglas Scott 					  NULL);
1935388fdb8fSIan Douglas Scott }
1936388fdb8fSIan Douglas Scott 
1937964af639STakashi Iwai static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
1938964af639STakashi Iwai {
1939964af639STakashi Iwai 	u16 buf = 0;
1940964af639STakashi Iwai 
1941964af639STakashi Iwai 	snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
1942964af639STakashi Iwai 			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
1943964af639STakashi Iwai 			ch, snd_usb_ctrl_intf(chip) | (id << 8),
1944964af639STakashi Iwai 			&buf, 2);
1945964af639STakashi Iwai }
1946964af639STakashi Iwai 
1947964af639STakashi Iwai static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
1948964af639STakashi Iwai {
1949964af639STakashi Iwai 	/* fix to 0dB playback volumes */
1950964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 1, 16);
1951964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 2, 16);
1952964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 1, 19);
1953964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 2, 19);
1954964af639STakashi Iwai 	return 0;
1955964af639STakashi Iwai }
1956964af639STakashi Iwai 
1957d39f1d68SJussi Laako /* RME Class Compliant device quirks */
1958d39f1d68SJussi Laako 
1959d39f1d68SJussi Laako #define SND_RME_GET_STATUS1			23
1960d39f1d68SJussi Laako #define SND_RME_GET_CURRENT_FREQ		17
1961d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM_SHIFT		16
1962d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM_MASK			0x1f
1963d39f1d68SJussi Laako #define SND_RME_CLK_AES_SHIFT			8
1964d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_SHIFT			12
1965d39f1d68SJussi Laako #define SND_RME_CLK_AES_SPDIF_MASK		0xf
1966d39f1d68SJussi Laako #define SND_RME_CLK_SYNC_SHIFT			6
1967d39f1d68SJussi Laako #define SND_RME_CLK_SYNC_MASK			0x3
1968d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL_SHIFT		18
1969d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL_MASK		0x7
1970d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM(x) \
1971d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK)
1972d39f1d68SJussi Laako #define SND_RME_CLK_AES(x) \
1973d39f1d68SJussi Laako 	((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
1974d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF(x) \
1975d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
1976d39f1d68SJussi Laako #define SND_RME_CLK_SYNC(x) \
1977d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK)
1978d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL(x) \
1979d39f1d68SJussi Laako 	((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK)
1980d39f1d68SJussi Laako #define SND_RME_CLK_AES_LOCK			0x1
1981d39f1d68SJussi Laako #define SND_RME_CLK_AES_SYNC			0x4
1982d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_LOCK			0x2
1983d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_SYNC			0x8
1984d39f1d68SJussi Laako #define SND_RME_SPDIF_IF_SHIFT			4
1985d39f1d68SJussi Laako #define SND_RME_SPDIF_FORMAT_SHIFT		5
1986d39f1d68SJussi Laako #define SND_RME_BINARY_MASK			0x1
1987d39f1d68SJussi Laako #define SND_RME_SPDIF_IF(x) \
1988d39f1d68SJussi Laako 	((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK)
1989d39f1d68SJussi Laako #define SND_RME_SPDIF_FORMAT(x) \
1990d39f1d68SJussi Laako 	((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK)
1991d39f1d68SJussi Laako 
1992d39f1d68SJussi Laako static const u32 snd_rme_rate_table[] = {
1993d39f1d68SJussi Laako 	32000, 44100, 48000, 50000,
1994d39f1d68SJussi Laako 	64000, 88200, 96000, 100000,
1995d39f1d68SJussi Laako 	128000, 176400, 192000, 200000,
1996d39f1d68SJussi Laako 	256000,	352800, 384000, 400000,
1997d39f1d68SJussi Laako 	512000, 705600, 768000, 800000
1998d39f1d68SJussi Laako };
1999d39f1d68SJussi Laako /* maximum number of items for AES and S/PDIF rates for above table */
2000d39f1d68SJussi Laako #define SND_RME_RATE_IDX_AES_SPDIF_NUM		12
2001d39f1d68SJussi Laako 
2002d39f1d68SJussi Laako enum snd_rme_domain {
2003d39f1d68SJussi Laako 	SND_RME_DOMAIN_SYSTEM,
2004d39f1d68SJussi Laako 	SND_RME_DOMAIN_AES,
2005d39f1d68SJussi Laako 	SND_RME_DOMAIN_SPDIF
2006d39f1d68SJussi Laako };
2007d39f1d68SJussi Laako 
2008d39f1d68SJussi Laako enum snd_rme_clock_status {
2009d39f1d68SJussi Laako 	SND_RME_CLOCK_NOLOCK,
2010d39f1d68SJussi Laako 	SND_RME_CLOCK_LOCK,
2011d39f1d68SJussi Laako 	SND_RME_CLOCK_SYNC
2012d39f1d68SJussi Laako };
2013d39f1d68SJussi Laako 
2014d39f1d68SJussi Laako static int snd_rme_read_value(struct snd_usb_audio *chip,
2015d39f1d68SJussi Laako 			      unsigned int item,
2016d39f1d68SJussi Laako 			      u32 *value)
2017d39f1d68SJussi Laako {
2018d39f1d68SJussi Laako 	struct usb_device *dev = chip->dev;
2019d39f1d68SJussi Laako 	int err;
2020d39f1d68SJussi Laako 
2021d39f1d68SJussi Laako 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
2022d39f1d68SJussi Laako 			      item,
2023d39f1d68SJussi Laako 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2024d39f1d68SJussi Laako 			      0, 0,
2025d39f1d68SJussi Laako 			      value, sizeof(*value));
2026d39f1d68SJussi Laako 	if (err < 0)
2027d39f1d68SJussi Laako 		dev_err(&dev->dev,
2028d39f1d68SJussi Laako 			"unable to issue vendor read request %d (ret = %d)",
2029d39f1d68SJussi Laako 			item, err);
2030d39f1d68SJussi Laako 	return err;
2031d39f1d68SJussi Laako }
2032d39f1d68SJussi Laako 
2033d39f1d68SJussi Laako static int snd_rme_get_status1(struct snd_kcontrol *kcontrol,
2034d39f1d68SJussi Laako 			       u32 *status1)
2035d39f1d68SJussi Laako {
2036d39f1d68SJussi Laako 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
2037d39f1d68SJussi Laako 	struct snd_usb_audio *chip = list->mixer->chip;
2038d39f1d68SJussi Laako 	int err;
2039d39f1d68SJussi Laako 
2040d39f1d68SJussi Laako 	err = snd_usb_lock_shutdown(chip);
2041d39f1d68SJussi Laako 	if (err < 0)
2042d39f1d68SJussi Laako 		return err;
2043d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, status1);
2044d39f1d68SJussi Laako 	snd_usb_unlock_shutdown(chip);
2045d39f1d68SJussi Laako 	return err;
2046d39f1d68SJussi Laako }
2047d39f1d68SJussi Laako 
2048d39f1d68SJussi Laako static int snd_rme_rate_get(struct snd_kcontrol *kcontrol,
2049d39f1d68SJussi Laako 			    struct snd_ctl_elem_value *ucontrol)
2050d39f1d68SJussi Laako {
2051d39f1d68SJussi Laako 	u32 status1;
2052d39f1d68SJussi Laako 	u32 rate = 0;
2053d39f1d68SJussi Laako 	int idx;
2054d39f1d68SJussi Laako 	int err;
2055d39f1d68SJussi Laako 
2056d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2057d39f1d68SJussi Laako 	if (err < 0)
2058d39f1d68SJussi Laako 		return err;
2059d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2060d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SYSTEM:
2061d39f1d68SJussi Laako 		idx = SND_RME_CLK_SYSTEM(status1);
2062d39f1d68SJussi Laako 		if (idx < ARRAY_SIZE(snd_rme_rate_table))
2063d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
2064d39f1d68SJussi Laako 		break;
2065d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:
2066d39f1d68SJussi Laako 		idx = SND_RME_CLK_AES(status1);
2067d39f1d68SJussi Laako 		if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
2068d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
2069d39f1d68SJussi Laako 		break;
2070d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:
2071d39f1d68SJussi Laako 		idx = SND_RME_CLK_SPDIF(status1);
2072d39f1d68SJussi Laako 		if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
2073d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
2074d39f1d68SJussi Laako 		break;
2075d39f1d68SJussi Laako 	default:
2076d39f1d68SJussi Laako 		return -EINVAL;
2077d39f1d68SJussi Laako 	}
2078d39f1d68SJussi Laako 	ucontrol->value.integer.value[0] = rate;
2079d39f1d68SJussi Laako 	return 0;
2080d39f1d68SJussi Laako }
2081d39f1d68SJussi Laako 
2082d39f1d68SJussi Laako static int snd_rme_sync_state_get(struct snd_kcontrol *kcontrol,
2083d39f1d68SJussi Laako 				  struct snd_ctl_elem_value *ucontrol)
2084d39f1d68SJussi Laako {
2085d39f1d68SJussi Laako 	u32 status1;
2086d39f1d68SJussi Laako 	int idx = SND_RME_CLOCK_NOLOCK;
2087d39f1d68SJussi Laako 	int err;
2088d39f1d68SJussi Laako 
2089d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2090d39f1d68SJussi Laako 	if (err < 0)
2091d39f1d68SJussi Laako 		return err;
2092d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2093d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:  /* AES */
2094d39f1d68SJussi Laako 		if (status1 & SND_RME_CLK_AES_SYNC)
2095d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_SYNC;
2096d39f1d68SJussi Laako 		else if (status1 & SND_RME_CLK_AES_LOCK)
2097d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_LOCK;
2098d39f1d68SJussi Laako 		break;
2099d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:  /* SPDIF */
2100d39f1d68SJussi Laako 		if (status1 & SND_RME_CLK_SPDIF_SYNC)
2101d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_SYNC;
2102d39f1d68SJussi Laako 		else if (status1 & SND_RME_CLK_SPDIF_LOCK)
2103d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_LOCK;
2104d39f1d68SJussi Laako 		break;
2105d39f1d68SJussi Laako 	default:
2106d39f1d68SJussi Laako 		return -EINVAL;
2107d39f1d68SJussi Laako 	}
2108d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = idx;
2109d39f1d68SJussi Laako 	return 0;
2110d39f1d68SJussi Laako }
2111d39f1d68SJussi Laako 
2112d39f1d68SJussi Laako static int snd_rme_spdif_if_get(struct snd_kcontrol *kcontrol,
2113d39f1d68SJussi Laako 				struct snd_ctl_elem_value *ucontrol)
2114d39f1d68SJussi Laako {
2115d39f1d68SJussi Laako 	u32 status1;
2116d39f1d68SJussi Laako 	int err;
2117d39f1d68SJussi Laako 
2118d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2119d39f1d68SJussi Laako 	if (err < 0)
2120d39f1d68SJussi Laako 		return err;
2121d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_IF(status1);
2122d39f1d68SJussi Laako 	return 0;
2123d39f1d68SJussi Laako }
2124d39f1d68SJussi Laako 
2125d39f1d68SJussi Laako static int snd_rme_spdif_format_get(struct snd_kcontrol *kcontrol,
2126d39f1d68SJussi Laako 				    struct snd_ctl_elem_value *ucontrol)
2127d39f1d68SJussi Laako {
2128d39f1d68SJussi Laako 	u32 status1;
2129d39f1d68SJussi Laako 	int err;
2130d39f1d68SJussi Laako 
2131d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2132d39f1d68SJussi Laako 	if (err < 0)
2133d39f1d68SJussi Laako 		return err;
2134d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_FORMAT(status1);
2135d39f1d68SJussi Laako 	return 0;
2136d39f1d68SJussi Laako }
2137d39f1d68SJussi Laako 
2138d39f1d68SJussi Laako static int snd_rme_sync_source_get(struct snd_kcontrol *kcontrol,
2139d39f1d68SJussi Laako 				   struct snd_ctl_elem_value *ucontrol)
2140d39f1d68SJussi Laako {
2141d39f1d68SJussi Laako 	u32 status1;
2142d39f1d68SJussi Laako 	int err;
2143d39f1d68SJussi Laako 
2144d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2145d39f1d68SJussi Laako 	if (err < 0)
2146d39f1d68SJussi Laako 		return err;
2147d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_CLK_SYNC(status1);
2148d39f1d68SJussi Laako 	return 0;
2149d39f1d68SJussi Laako }
2150d39f1d68SJussi Laako 
2151d39f1d68SJussi Laako static int snd_rme_current_freq_get(struct snd_kcontrol *kcontrol,
2152d39f1d68SJussi Laako 				    struct snd_ctl_elem_value *ucontrol)
2153d39f1d68SJussi Laako {
2154d39f1d68SJussi Laako 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
2155d39f1d68SJussi Laako 	struct snd_usb_audio *chip = list->mixer->chip;
2156d39f1d68SJussi Laako 	u32 status1;
2157d39f1d68SJussi Laako 	const u64 num = 104857600000000ULL;
2158d39f1d68SJussi Laako 	u32 den;
2159d39f1d68SJussi Laako 	unsigned int freq;
2160d39f1d68SJussi Laako 	int err;
2161d39f1d68SJussi Laako 
2162d39f1d68SJussi Laako 	err = snd_usb_lock_shutdown(chip);
2163d39f1d68SJussi Laako 	if (err < 0)
2164d39f1d68SJussi Laako 		return err;
2165d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, &status1);
2166d39f1d68SJussi Laako 	if (err < 0)
2167d39f1d68SJussi Laako 		goto end;
2168d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_CURRENT_FREQ, &den);
2169d39f1d68SJussi Laako 	if (err < 0)
2170d39f1d68SJussi Laako 		goto end;
2171d39f1d68SJussi Laako 	freq = (den == 0) ? 0 : div64_u64(num, den);
2172d39f1d68SJussi Laako 	freq <<= SND_RME_CLK_FREQMUL(status1);
2173d39f1d68SJussi Laako 	ucontrol->value.integer.value[0] = freq;
2174d39f1d68SJussi Laako 
2175d39f1d68SJussi Laako end:
2176d39f1d68SJussi Laako 	snd_usb_unlock_shutdown(chip);
2177d39f1d68SJussi Laako 	return err;
2178d39f1d68SJussi Laako }
2179d39f1d68SJussi Laako 
2180d39f1d68SJussi Laako static int snd_rme_rate_info(struct snd_kcontrol *kcontrol,
2181d39f1d68SJussi Laako 			     struct snd_ctl_elem_info *uinfo)
2182d39f1d68SJussi Laako {
2183d39f1d68SJussi Laako 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2184d39f1d68SJussi Laako 	uinfo->count = 1;
2185d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2186d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SYSTEM:
2187d39f1d68SJussi Laako 		uinfo->value.integer.min = 32000;
2188d39f1d68SJussi Laako 		uinfo->value.integer.max = 800000;
2189d39f1d68SJussi Laako 		break;
2190d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:
2191d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:
2192d39f1d68SJussi Laako 	default:
2193d39f1d68SJussi Laako 		uinfo->value.integer.min = 0;
2194d39f1d68SJussi Laako 		uinfo->value.integer.max = 200000;
2195d39f1d68SJussi Laako 	}
2196d39f1d68SJussi Laako 	uinfo->value.integer.step = 0;
2197d39f1d68SJussi Laako 	return 0;
2198d39f1d68SJussi Laako }
2199d39f1d68SJussi Laako 
2200d39f1d68SJussi Laako static int snd_rme_sync_state_info(struct snd_kcontrol *kcontrol,
2201d39f1d68SJussi Laako 				   struct snd_ctl_elem_info *uinfo)
2202d39f1d68SJussi Laako {
2203d39f1d68SJussi Laako 	static const char *const sync_states[] = {
2204d39f1d68SJussi Laako 		"No Lock", "Lock", "Sync"
2205d39f1d68SJussi Laako 	};
2206d39f1d68SJussi Laako 
2207d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2208d39f1d68SJussi Laako 				 ARRAY_SIZE(sync_states), sync_states);
2209d39f1d68SJussi Laako }
2210d39f1d68SJussi Laako 
2211d39f1d68SJussi Laako static int snd_rme_spdif_if_info(struct snd_kcontrol *kcontrol,
2212d39f1d68SJussi Laako 				 struct snd_ctl_elem_info *uinfo)
2213d39f1d68SJussi Laako {
2214d39f1d68SJussi Laako 	static const char *const spdif_if[] = {
2215d39f1d68SJussi Laako 		"Coaxial", "Optical"
2216d39f1d68SJussi Laako 	};
2217d39f1d68SJussi Laako 
2218d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2219d39f1d68SJussi Laako 				 ARRAY_SIZE(spdif_if), spdif_if);
2220d39f1d68SJussi Laako }
2221d39f1d68SJussi Laako 
2222d39f1d68SJussi Laako static int snd_rme_spdif_format_info(struct snd_kcontrol *kcontrol,
2223d39f1d68SJussi Laako 				     struct snd_ctl_elem_info *uinfo)
2224d39f1d68SJussi Laako {
2225d39f1d68SJussi Laako 	static const char *const optical_type[] = {
2226d39f1d68SJussi Laako 		"Consumer", "Professional"
2227d39f1d68SJussi Laako 	};
2228d39f1d68SJussi Laako 
2229d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2230d39f1d68SJussi Laako 				 ARRAY_SIZE(optical_type), optical_type);
2231d39f1d68SJussi Laako }
2232d39f1d68SJussi Laako 
2233d39f1d68SJussi Laako static int snd_rme_sync_source_info(struct snd_kcontrol *kcontrol,
2234d39f1d68SJussi Laako 				    struct snd_ctl_elem_info *uinfo)
2235d39f1d68SJussi Laako {
2236d39f1d68SJussi Laako 	static const char *const sync_sources[] = {
2237d39f1d68SJussi Laako 		"Internal", "AES", "SPDIF", "Internal"
2238d39f1d68SJussi Laako 	};
2239d39f1d68SJussi Laako 
2240d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2241d39f1d68SJussi Laako 				 ARRAY_SIZE(sync_sources), sync_sources);
2242d39f1d68SJussi Laako }
2243d39f1d68SJussi Laako 
2244195727e8STakashi Iwai static const struct snd_kcontrol_new snd_rme_controls[] = {
2245d39f1d68SJussi Laako 	{
2246d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2247d39f1d68SJussi Laako 		.name = "AES Rate",
2248d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2249d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2250d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2251d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_AES
2252d39f1d68SJussi Laako 	},
2253d39f1d68SJussi Laako 	{
2254d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2255d39f1d68SJussi Laako 		.name = "AES Sync",
2256d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2257d39f1d68SJussi Laako 		.info = snd_rme_sync_state_info,
2258d39f1d68SJussi Laako 		.get = snd_rme_sync_state_get,
2259d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_AES
2260d39f1d68SJussi Laako 	},
2261d39f1d68SJussi Laako 	{
2262d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2263d39f1d68SJussi Laako 		.name = "SPDIF Rate",
2264d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2265d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2266d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2267d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SPDIF
2268d39f1d68SJussi Laako 	},
2269d39f1d68SJussi Laako 	{
2270d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2271d39f1d68SJussi Laako 		.name = "SPDIF Sync",
2272d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2273d39f1d68SJussi Laako 		.info = snd_rme_sync_state_info,
2274d39f1d68SJussi Laako 		.get = snd_rme_sync_state_get,
2275d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SPDIF
2276d39f1d68SJussi Laako 	},
2277d39f1d68SJussi Laako 	{
2278d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2279d39f1d68SJussi Laako 		.name = "SPDIF Interface",
2280d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2281d39f1d68SJussi Laako 		.info = snd_rme_spdif_if_info,
2282d39f1d68SJussi Laako 		.get = snd_rme_spdif_if_get,
2283d39f1d68SJussi Laako 	},
2284d39f1d68SJussi Laako 	{
2285d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2286d39f1d68SJussi Laako 		.name = "SPDIF Format",
2287d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2288d39f1d68SJussi Laako 		.info = snd_rme_spdif_format_info,
2289d39f1d68SJussi Laako 		.get = snd_rme_spdif_format_get,
2290d39f1d68SJussi Laako 	},
2291d39f1d68SJussi Laako 	{
2292d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2293d39f1d68SJussi Laako 		.name = "Sync Source",
2294d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2295d39f1d68SJussi Laako 		.info = snd_rme_sync_source_info,
2296d39f1d68SJussi Laako 		.get = snd_rme_sync_source_get
2297d39f1d68SJussi Laako 	},
2298d39f1d68SJussi Laako 	{
2299d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2300d39f1d68SJussi Laako 		.name = "System Rate",
2301d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2302d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2303d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2304d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SYSTEM
2305d39f1d68SJussi Laako 	},
2306d39f1d68SJussi Laako 	{
2307d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2308d39f1d68SJussi Laako 		.name = "Current Frequency",
2309d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2310d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2311d39f1d68SJussi Laako 		.get = snd_rme_current_freq_get
2312d39f1d68SJussi Laako 	}
2313d39f1d68SJussi Laako };
2314d39f1d68SJussi Laako 
2315d39f1d68SJussi Laako static int snd_rme_controls_create(struct usb_mixer_interface *mixer)
2316d39f1d68SJussi Laako {
2317d39f1d68SJussi Laako 	int err, i;
2318d39f1d68SJussi Laako 
2319d39f1d68SJussi Laako 	for (i = 0; i < ARRAY_SIZE(snd_rme_controls); ++i) {
2320d39f1d68SJussi Laako 		err = add_single_ctl_with_resume(mixer, 0,
2321d39f1d68SJussi Laako 						 NULL,
2322d39f1d68SJussi Laako 						 &snd_rme_controls[i],
2323d39f1d68SJussi Laako 						 NULL);
2324d39f1d68SJussi Laako 		if (err < 0)
2325d39f1d68SJussi Laako 			return err;
2326d39f1d68SJussi Laako 	}
2327d39f1d68SJussi Laako 
2328d39f1d68SJussi Laako 	return 0;
2329d39f1d68SJussi Laako }
2330d39f1d68SJussi Laako 
23313e8f3bd0SThomas Ebeling /*
23323e8f3bd0SThomas Ebeling  * RME Babyface Pro (FS)
23333e8f3bd0SThomas Ebeling  *
23343e8f3bd0SThomas Ebeling  * These devices exposes a couple of DSP functions via request to EP0.
23353e8f3bd0SThomas Ebeling  * Switches are available via control registers, while routing is controlled
23363e8f3bd0SThomas Ebeling  * by controlling the volume on each possible crossing point.
233747b4f5f5SThomas Ebeling  * Volume control is linear, from -inf (dec. 0) to +6dB (dec. 65536) with
23383e8f3bd0SThomas Ebeling  * 0dB being at dec. 32768.
23393e8f3bd0SThomas Ebeling  */
23403e8f3bd0SThomas Ebeling enum {
23413e8f3bd0SThomas Ebeling 	SND_BBFPRO_CTL_REG1 = 0,
23423e8f3bd0SThomas Ebeling 	SND_BBFPRO_CTL_REG2
23433e8f3bd0SThomas Ebeling };
23443e8f3bd0SThomas Ebeling 
23453e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG_MASK 1
23463e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_IDX_MASK 0xff
23473e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_IDX_SHIFT 1
23483e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_VAL_MASK 1
23493e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_VAL_SHIFT 9
23503e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_CLK_MASTER 0
23513e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_CLK_OPTICAL 1
23523e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_PRO 7
23533e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_EMPH 8
23543e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL 10
23553e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_48V_AN1 0
23563e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_48V_AN2 1
23573e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_SENS_IN3 2
23583e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_SENS_IN4 3
23593e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_PAD_AN1 4
23603e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_PAD_AN2 5
23613e8f3bd0SThomas Ebeling 
23623e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_IDX_MASK 0x1ff
23633e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
23643e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_SHIFT 9
23653e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
236647b4f5f5SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
23673e8f3bd0SThomas Ebeling 
23683e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_CTL_REG1 0x10
23693e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_CTL_REG2 0x17
23703e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_MIXER 0x12
23713e8f3bd0SThomas Ebeling 
23723e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
23733e8f3bd0SThomas Ebeling 				 u8 index, u8 value)
23743e8f3bd0SThomas Ebeling {
23753e8f3bd0SThomas Ebeling 	int err;
23763e8f3bd0SThomas Ebeling 	u16 usb_req, usb_idx, usb_val;
23773e8f3bd0SThomas Ebeling 	struct snd_usb_audio *chip = mixer->chip;
23783e8f3bd0SThomas Ebeling 
23793e8f3bd0SThomas Ebeling 	err = snd_usb_lock_shutdown(chip);
23803e8f3bd0SThomas Ebeling 	if (err < 0)
23813e8f3bd0SThomas Ebeling 		return err;
23823e8f3bd0SThomas Ebeling 
23833e8f3bd0SThomas Ebeling 	if (reg == SND_BBFPRO_CTL_REG1) {
23843e8f3bd0SThomas Ebeling 		usb_req = SND_BBFPRO_USBREQ_CTL_REG1;
23853e8f3bd0SThomas Ebeling 		if (index == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
23863e8f3bd0SThomas Ebeling 			usb_idx = 3;
23873e8f3bd0SThomas Ebeling 			usb_val = value ? 3 : 0;
23883e8f3bd0SThomas Ebeling 		} else {
23893e8f3bd0SThomas Ebeling 			usb_idx = 1 << index;
23903e8f3bd0SThomas Ebeling 			usb_val = value ? usb_idx : 0;
23913e8f3bd0SThomas Ebeling 		}
23923e8f3bd0SThomas Ebeling 	} else {
23933e8f3bd0SThomas Ebeling 		usb_req = SND_BBFPRO_USBREQ_CTL_REG2;
23943e8f3bd0SThomas Ebeling 		usb_idx = 1 << index;
23953e8f3bd0SThomas Ebeling 		usb_val = value ? usb_idx : 0;
23963e8f3bd0SThomas Ebeling 	}
23973e8f3bd0SThomas Ebeling 
23983e8f3bd0SThomas Ebeling 	err = snd_usb_ctl_msg(chip->dev,
23993e8f3bd0SThomas Ebeling 			      usb_sndctrlpipe(chip->dev, 0), usb_req,
24003e8f3bd0SThomas Ebeling 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2401f99e24a6SThomas Ebeling 			      usb_val, usb_idx, NULL, 0);
24023e8f3bd0SThomas Ebeling 
24033e8f3bd0SThomas Ebeling 	snd_usb_unlock_shutdown(chip);
24043e8f3bd0SThomas Ebeling 	return err;
24053e8f3bd0SThomas Ebeling }
24063e8f3bd0SThomas Ebeling 
24073e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol,
24083e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
24093e8f3bd0SThomas Ebeling {
24103e8f3bd0SThomas Ebeling 	u8 reg, idx, val;
24113e8f3bd0SThomas Ebeling 	int pv;
24123e8f3bd0SThomas Ebeling 
24133e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
24143e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
24153e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
24163e8f3bd0SThomas Ebeling 	val = kcontrol->private_value >> SND_BBFPRO_CTL_VAL_SHIFT;
24173e8f3bd0SThomas Ebeling 
24183e8f3bd0SThomas Ebeling 	if ((reg == SND_BBFPRO_CTL_REG1 &&
24193e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
24203e8f3bd0SThomas Ebeling 	    (reg == SND_BBFPRO_CTL_REG2 &&
24213e8f3bd0SThomas Ebeling 	    (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
24223e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
24233e8f3bd0SThomas Ebeling 		ucontrol->value.enumerated.item[0] = val;
24243e8f3bd0SThomas Ebeling 	} else {
24253e8f3bd0SThomas Ebeling 		ucontrol->value.integer.value[0] = val;
24263e8f3bd0SThomas Ebeling 	}
24273e8f3bd0SThomas Ebeling 	return 0;
24283e8f3bd0SThomas Ebeling }
24293e8f3bd0SThomas Ebeling 
24303e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_info(struct snd_kcontrol *kcontrol,
24313e8f3bd0SThomas Ebeling 			       struct snd_ctl_elem_info *uinfo)
24323e8f3bd0SThomas Ebeling {
24333e8f3bd0SThomas Ebeling 	u8 reg, idx;
24343e8f3bd0SThomas Ebeling 	int pv;
24353e8f3bd0SThomas Ebeling 
24363e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
24373e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
24383e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
24393e8f3bd0SThomas Ebeling 
24403e8f3bd0SThomas Ebeling 	if (reg == SND_BBFPRO_CTL_REG1 &&
24413e8f3bd0SThomas Ebeling 	    idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
24423e8f3bd0SThomas Ebeling 		static const char * const texts[2] = {
24433e8f3bd0SThomas Ebeling 			"AutoSync",
24443e8f3bd0SThomas Ebeling 			"Internal"
24453e8f3bd0SThomas Ebeling 		};
24463e8f3bd0SThomas Ebeling 		return snd_ctl_enum_info(uinfo, 1, 2, texts);
24473e8f3bd0SThomas Ebeling 	} else if (reg == SND_BBFPRO_CTL_REG2 &&
24483e8f3bd0SThomas Ebeling 		   (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
24493e8f3bd0SThomas Ebeling 		    idx == SND_BBFPRO_CTL_REG2_SENS_IN4)) {
24503e8f3bd0SThomas Ebeling 		static const char * const texts[2] = {
24513e8f3bd0SThomas Ebeling 			"-10dBV",
24523e8f3bd0SThomas Ebeling 			"+4dBu"
24533e8f3bd0SThomas Ebeling 		};
24543e8f3bd0SThomas Ebeling 		return snd_ctl_enum_info(uinfo, 1, 2, texts);
24553e8f3bd0SThomas Ebeling 	}
24563e8f3bd0SThomas Ebeling 
24573e8f3bd0SThomas Ebeling 	uinfo->count = 1;
24583e8f3bd0SThomas Ebeling 	uinfo->value.integer.min = 0;
24593e8f3bd0SThomas Ebeling 	uinfo->value.integer.max = 1;
24603e8f3bd0SThomas Ebeling 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
24613e8f3bd0SThomas Ebeling 	return 0;
24623e8f3bd0SThomas Ebeling }
24633e8f3bd0SThomas Ebeling 
24643e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol,
24653e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
24663e8f3bd0SThomas Ebeling {
24673e8f3bd0SThomas Ebeling 	int err;
24683e8f3bd0SThomas Ebeling 	u8 reg, idx;
24693e8f3bd0SThomas Ebeling 	int old_value, pv, val;
24703e8f3bd0SThomas Ebeling 
24713e8f3bd0SThomas Ebeling 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
24723e8f3bd0SThomas Ebeling 	struct usb_mixer_interface *mixer = list->mixer;
24733e8f3bd0SThomas Ebeling 
24743e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
24753e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
24763e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
24773e8f3bd0SThomas Ebeling 	old_value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
24783e8f3bd0SThomas Ebeling 
24793e8f3bd0SThomas Ebeling 	if ((reg == SND_BBFPRO_CTL_REG1 &&
24803e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
24813e8f3bd0SThomas Ebeling 	    (reg == SND_BBFPRO_CTL_REG2 &&
24823e8f3bd0SThomas Ebeling 	    (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
24833e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
24843e8f3bd0SThomas Ebeling 		val = ucontrol->value.enumerated.item[0];
24853e8f3bd0SThomas Ebeling 	} else {
24863e8f3bd0SThomas Ebeling 		val = ucontrol->value.integer.value[0];
24873e8f3bd0SThomas Ebeling 	}
24883e8f3bd0SThomas Ebeling 
24893e8f3bd0SThomas Ebeling 	if (val > 1)
24903e8f3bd0SThomas Ebeling 		return -EINVAL;
24913e8f3bd0SThomas Ebeling 
24923e8f3bd0SThomas Ebeling 	if (val == old_value)
24933e8f3bd0SThomas Ebeling 		return 0;
24943e8f3bd0SThomas Ebeling 
24953e8f3bd0SThomas Ebeling 	kcontrol->private_value = reg
24963e8f3bd0SThomas Ebeling 		| ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT)
24973e8f3bd0SThomas Ebeling 		| ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT);
24983e8f3bd0SThomas Ebeling 
24993e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
25003e8f3bd0SThomas Ebeling 	return err < 0 ? err : 1;
25013e8f3bd0SThomas Ebeling }
25023e8f3bd0SThomas Ebeling 
25033e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
25043e8f3bd0SThomas Ebeling {
25053e8f3bd0SThomas Ebeling 	u8 reg, idx;
25063e8f3bd0SThomas Ebeling 	int value, pv;
25073e8f3bd0SThomas Ebeling 
25083e8f3bd0SThomas Ebeling 	pv = list->kctl->private_value;
25093e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
25103e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
25113e8f3bd0SThomas Ebeling 	value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
25123e8f3bd0SThomas Ebeling 
25133e8f3bd0SThomas Ebeling 	return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
25143e8f3bd0SThomas Ebeling }
25153e8f3bd0SThomas Ebeling 
25163e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
25173e8f3bd0SThomas Ebeling 				 u32 value)
25183e8f3bd0SThomas Ebeling {
25193e8f3bd0SThomas Ebeling 	struct snd_usb_audio *chip = mixer->chip;
25203e8f3bd0SThomas Ebeling 	int err;
25213e8f3bd0SThomas Ebeling 	u16 idx;
25223e8f3bd0SThomas Ebeling 	u16 usb_idx, usb_val;
25233e8f3bd0SThomas Ebeling 	u32 v;
25243e8f3bd0SThomas Ebeling 
25253e8f3bd0SThomas Ebeling 	err = snd_usb_lock_shutdown(chip);
25263e8f3bd0SThomas Ebeling 	if (err < 0)
25273e8f3bd0SThomas Ebeling 		return err;
25283e8f3bd0SThomas Ebeling 
25293e8f3bd0SThomas Ebeling 	idx = index & SND_BBFPRO_MIXER_IDX_MASK;
25303e8f3bd0SThomas Ebeling 	// 18 bit linear volume, split so 2 bits end up in index.
25313e8f3bd0SThomas Ebeling 	v = value & SND_BBFPRO_MIXER_VAL_MASK;
25323e8f3bd0SThomas Ebeling 	usb_idx = idx | (v & 0x3) << 14;
25333e8f3bd0SThomas Ebeling 	usb_val = (v >> 2) & 0xffff;
25343e8f3bd0SThomas Ebeling 
25353e8f3bd0SThomas Ebeling 	err = snd_usb_ctl_msg(chip->dev,
25363e8f3bd0SThomas Ebeling 			      usb_sndctrlpipe(chip->dev, 0),
25373e8f3bd0SThomas Ebeling 			      SND_BBFPRO_USBREQ_MIXER,
25383e8f3bd0SThomas Ebeling 			      USB_DIR_OUT | USB_TYPE_VENDOR |
25393e8f3bd0SThomas Ebeling 			      USB_RECIP_DEVICE,
2540f99e24a6SThomas Ebeling 			      usb_val, usb_idx, NULL, 0);
25413e8f3bd0SThomas Ebeling 
25423e8f3bd0SThomas Ebeling 	snd_usb_unlock_shutdown(chip);
25433e8f3bd0SThomas Ebeling 	return err;
25443e8f3bd0SThomas Ebeling }
25453e8f3bd0SThomas Ebeling 
25463e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol,
25473e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
25483e8f3bd0SThomas Ebeling {
25493e8f3bd0SThomas Ebeling 	ucontrol->value.integer.value[0] =
25503e8f3bd0SThomas Ebeling 		kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
25513e8f3bd0SThomas Ebeling 	return 0;
25523e8f3bd0SThomas Ebeling }
25533e8f3bd0SThomas Ebeling 
25543e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_info(struct snd_kcontrol *kcontrol,
25553e8f3bd0SThomas Ebeling 			       struct snd_ctl_elem_info *uinfo)
25563e8f3bd0SThomas Ebeling {
25573e8f3bd0SThomas Ebeling 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
25583e8f3bd0SThomas Ebeling 	uinfo->count = 1;
25593e8f3bd0SThomas Ebeling 	uinfo->value.integer.min = SND_BBFPRO_MIXER_VAL_MIN;
25603e8f3bd0SThomas Ebeling 	uinfo->value.integer.max = SND_BBFPRO_MIXER_VAL_MAX;
25613e8f3bd0SThomas Ebeling 	return 0;
25623e8f3bd0SThomas Ebeling }
25633e8f3bd0SThomas Ebeling 
25643e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol,
25653e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
25663e8f3bd0SThomas Ebeling {
25673e8f3bd0SThomas Ebeling 	int err;
25683e8f3bd0SThomas Ebeling 	u16 idx;
25693e8f3bd0SThomas Ebeling 	u32 new_val, old_value, uvalue;
25703e8f3bd0SThomas Ebeling 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
25713e8f3bd0SThomas Ebeling 	struct usb_mixer_interface *mixer = list->mixer;
25723e8f3bd0SThomas Ebeling 
25733e8f3bd0SThomas Ebeling 	uvalue = ucontrol->value.integer.value[0];
25743e8f3bd0SThomas Ebeling 	idx = kcontrol->private_value & SND_BBFPRO_MIXER_IDX_MASK;
25753e8f3bd0SThomas Ebeling 	old_value = kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
25763e8f3bd0SThomas Ebeling 
25773e8f3bd0SThomas Ebeling 	if (uvalue > SND_BBFPRO_MIXER_VAL_MAX)
25783e8f3bd0SThomas Ebeling 		return -EINVAL;
25793e8f3bd0SThomas Ebeling 
25803e8f3bd0SThomas Ebeling 	if (uvalue == old_value)
25813e8f3bd0SThomas Ebeling 		return 0;
25823e8f3bd0SThomas Ebeling 
25833e8f3bd0SThomas Ebeling 	new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK;
25843e8f3bd0SThomas Ebeling 
25853e8f3bd0SThomas Ebeling 	kcontrol->private_value = idx
25863e8f3bd0SThomas Ebeling 		| (new_val << SND_BBFPRO_MIXER_VAL_SHIFT);
25873e8f3bd0SThomas Ebeling 
25883e8f3bd0SThomas Ebeling 	err = snd_bbfpro_vol_update(mixer, idx, new_val);
25893e8f3bd0SThomas Ebeling 	return err < 0 ? err : 1;
25903e8f3bd0SThomas Ebeling }
25913e8f3bd0SThomas Ebeling 
25923e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list)
25933e8f3bd0SThomas Ebeling {
25943e8f3bd0SThomas Ebeling 	int pv = list->kctl->private_value;
25953e8f3bd0SThomas Ebeling 	u16 idx = pv & SND_BBFPRO_MIXER_IDX_MASK;
25963e8f3bd0SThomas Ebeling 	u32 val = (pv >> SND_BBFPRO_MIXER_VAL_SHIFT)
25973e8f3bd0SThomas Ebeling 		& SND_BBFPRO_MIXER_VAL_MASK;
25983e8f3bd0SThomas Ebeling 	return snd_bbfpro_vol_update(list->mixer, idx, val);
25993e8f3bd0SThomas Ebeling }
26003e8f3bd0SThomas Ebeling 
26013e8f3bd0SThomas Ebeling // Predfine elements
26023e8f3bd0SThomas Ebeling static const struct snd_kcontrol_new snd_bbfpro_ctl_control = {
26033e8f3bd0SThomas Ebeling 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
26043e8f3bd0SThomas Ebeling 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
26053e8f3bd0SThomas Ebeling 	.index = 0,
26063e8f3bd0SThomas Ebeling 	.info = snd_bbfpro_ctl_info,
26073e8f3bd0SThomas Ebeling 	.get = snd_bbfpro_ctl_get,
26083e8f3bd0SThomas Ebeling 	.put = snd_bbfpro_ctl_put
26093e8f3bd0SThomas Ebeling };
26103e8f3bd0SThomas Ebeling 
26113e8f3bd0SThomas Ebeling static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
26123e8f3bd0SThomas Ebeling 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
26133e8f3bd0SThomas Ebeling 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
26143e8f3bd0SThomas Ebeling 	.index = 0,
26153e8f3bd0SThomas Ebeling 	.info = snd_bbfpro_vol_info,
26163e8f3bd0SThomas Ebeling 	.get = snd_bbfpro_vol_get,
26173e8f3bd0SThomas Ebeling 	.put = snd_bbfpro_vol_put
26183e8f3bd0SThomas Ebeling };
26193e8f3bd0SThomas Ebeling 
26203e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg,
26213e8f3bd0SThomas Ebeling 			      u8 index, char *name)
26223e8f3bd0SThomas Ebeling {
26233e8f3bd0SThomas Ebeling 	struct snd_kcontrol_new knew = snd_bbfpro_ctl_control;
26243e8f3bd0SThomas Ebeling 
26253e8f3bd0SThomas Ebeling 	knew.name = name;
26263e8f3bd0SThomas Ebeling 	knew.private_value = (reg & SND_BBFPRO_CTL_REG_MASK)
26273e8f3bd0SThomas Ebeling 		| ((index & SND_BBFPRO_CTL_IDX_MASK)
26283e8f3bd0SThomas Ebeling 			<< SND_BBFPRO_CTL_IDX_SHIFT);
26293e8f3bd0SThomas Ebeling 
26303e8f3bd0SThomas Ebeling 	return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume,
26313e8f3bd0SThomas Ebeling 		&knew, NULL);
26323e8f3bd0SThomas Ebeling }
26333e8f3bd0SThomas Ebeling 
26343e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
26353e8f3bd0SThomas Ebeling 			      char *name)
26363e8f3bd0SThomas Ebeling {
26373e8f3bd0SThomas Ebeling 	struct snd_kcontrol_new knew = snd_bbfpro_vol_control;
26383e8f3bd0SThomas Ebeling 
26393e8f3bd0SThomas Ebeling 	knew.name = name;
26403e8f3bd0SThomas Ebeling 	knew.private_value = index & SND_BBFPRO_MIXER_IDX_MASK;
26413e8f3bd0SThomas Ebeling 
26423e8f3bd0SThomas Ebeling 	return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_vol_resume,
26433e8f3bd0SThomas Ebeling 		&knew, NULL);
26443e8f3bd0SThomas Ebeling }
26453e8f3bd0SThomas Ebeling 
26463e8f3bd0SThomas Ebeling static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
26473e8f3bd0SThomas Ebeling {
26483e8f3bd0SThomas Ebeling 	int err, i, o;
26493e8f3bd0SThomas Ebeling 	char name[48];
26503e8f3bd0SThomas Ebeling 
26513e8f3bd0SThomas Ebeling 	static const char * const input[] = {
26523e8f3bd0SThomas Ebeling 		"AN1", "AN2", "IN3", "IN4", "AS1", "AS2", "ADAT3",
26533e8f3bd0SThomas Ebeling 		"ADAT4", "ADAT5", "ADAT6", "ADAT7", "ADAT8"};
26543e8f3bd0SThomas Ebeling 
26553e8f3bd0SThomas Ebeling 	static const char * const output[] = {
26563e8f3bd0SThomas Ebeling 		"AN1", "AN2", "PH3", "PH4", "AS1", "AS2", "ADAT3", "ADAT4",
26573e8f3bd0SThomas Ebeling 		"ADAT5", "ADAT6", "ADAT7", "ADAT8"};
26583e8f3bd0SThomas Ebeling 
26593e8f3bd0SThomas Ebeling 	for (o = 0 ; o < 12 ; ++o) {
26603e8f3bd0SThomas Ebeling 		for (i = 0 ; i < 12 ; ++i) {
26613e8f3bd0SThomas Ebeling 			// Line routing
26623e8f3bd0SThomas Ebeling 			snprintf(name, sizeof(name),
26633e8f3bd0SThomas Ebeling 				 "%s-%s-%s Playback Volume",
26643e8f3bd0SThomas Ebeling 				 (i < 2 ? "Mic" : "Line"),
26653e8f3bd0SThomas Ebeling 				 input[i], output[o]);
26663e8f3bd0SThomas Ebeling 			err = snd_bbfpro_vol_add(mixer, (26 * o + i), name);
26673e8f3bd0SThomas Ebeling 			if (err < 0)
26683e8f3bd0SThomas Ebeling 				return err;
26693e8f3bd0SThomas Ebeling 
26703e8f3bd0SThomas Ebeling 			// PCM routing... yes, it is output remapping
26713e8f3bd0SThomas Ebeling 			snprintf(name, sizeof(name),
26723e8f3bd0SThomas Ebeling 				 "PCM-%s-%s Playback Volume",
26733e8f3bd0SThomas Ebeling 				 output[i], output[o]);
26743e8f3bd0SThomas Ebeling 			err = snd_bbfpro_vol_add(mixer, (26 * o + 12 + i),
26753e8f3bd0SThomas Ebeling 						 name);
26763e8f3bd0SThomas Ebeling 			if (err < 0)
26773e8f3bd0SThomas Ebeling 				return err;
26783e8f3bd0SThomas Ebeling 		}
26793e8f3bd0SThomas Ebeling 	}
26803e8f3bd0SThomas Ebeling 
26813e8f3bd0SThomas Ebeling 	// Control Reg 1
26823e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
26833e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
26843e8f3bd0SThomas Ebeling 				 "Sample Clock Source");
26853e8f3bd0SThomas Ebeling 	if (err < 0)
26863e8f3bd0SThomas Ebeling 		return err;
26873e8f3bd0SThomas Ebeling 
26883e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
26893e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_PRO,
26903e8f3bd0SThomas Ebeling 				 "IEC958 Pro Mask");
26913e8f3bd0SThomas Ebeling 	if (err < 0)
26923e8f3bd0SThomas Ebeling 		return err;
26933e8f3bd0SThomas Ebeling 
26943e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
26953e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_EMPH,
26963e8f3bd0SThomas Ebeling 				 "IEC958 Emphasis");
26973e8f3bd0SThomas Ebeling 	if (err < 0)
26983e8f3bd0SThomas Ebeling 		return err;
26993e8f3bd0SThomas Ebeling 
27003e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
27013e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL,
27023e8f3bd0SThomas Ebeling 				 "IEC958 Switch");
27033e8f3bd0SThomas Ebeling 	if (err < 0)
27043e8f3bd0SThomas Ebeling 		return err;
27053e8f3bd0SThomas Ebeling 
27063e8f3bd0SThomas Ebeling 	// Control Reg 2
27073e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
27083e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_48V_AN1,
27093e8f3bd0SThomas Ebeling 				 "Mic-AN1 48V");
27103e8f3bd0SThomas Ebeling 	if (err < 0)
27113e8f3bd0SThomas Ebeling 		return err;
27123e8f3bd0SThomas Ebeling 
27133e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
27143e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_48V_AN2,
27153e8f3bd0SThomas Ebeling 				 "Mic-AN2 48V");
27163e8f3bd0SThomas Ebeling 	if (err < 0)
27173e8f3bd0SThomas Ebeling 		return err;
27183e8f3bd0SThomas Ebeling 
27193e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
27203e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_SENS_IN3,
27213e8f3bd0SThomas Ebeling 				 "Line-IN3 Sens.");
27223e8f3bd0SThomas Ebeling 	if (err < 0)
27233e8f3bd0SThomas Ebeling 		return err;
27243e8f3bd0SThomas Ebeling 
27253e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
27263e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_SENS_IN4,
27273e8f3bd0SThomas Ebeling 				 "Line-IN4 Sens.");
27283e8f3bd0SThomas Ebeling 	if (err < 0)
27293e8f3bd0SThomas Ebeling 		return err;
27303e8f3bd0SThomas Ebeling 
27313e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
27323e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_PAD_AN1,
27333e8f3bd0SThomas Ebeling 				 "Mic-AN1 PAD");
27343e8f3bd0SThomas Ebeling 	if (err < 0)
27353e8f3bd0SThomas Ebeling 		return err;
27363e8f3bd0SThomas Ebeling 
27373e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
27383e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_PAD_AN2,
27393e8f3bd0SThomas Ebeling 				 "Mic-AN2 PAD");
27403e8f3bd0SThomas Ebeling 	if (err < 0)
27413e8f3bd0SThomas Ebeling 		return err;
27423e8f3bd0SThomas Ebeling 
27433e8f3bd0SThomas Ebeling 	return 0;
27443e8f3bd0SThomas Ebeling }
27453e8f3bd0SThomas Ebeling 
2746cdc01a15SFrantišek Kučera /*
2747a07df82cSOlivia Mackintosh  * Pioneer DJ DJM Mixers
2748cdc01a15SFrantišek Kučera  *
2749a07df82cSOlivia Mackintosh  * These devices generally have options for soft-switching the playback and
2750a07df82cSOlivia Mackintosh  * capture sources in addition to the recording level. Although different
2751a07df82cSOlivia Mackintosh  * devices have different configurations, there seems to be canonical values
2752a07df82cSOlivia Mackintosh  * for specific capture/playback types:  See the definitions of these below.
2753cdc01a15SFrantišek Kučera  *
2754a07df82cSOlivia Mackintosh  * The wValue is masked with the stereo channel number. e.g. Setting Ch2 to
2755a07df82cSOlivia Mackintosh  * capture phono would be 0x0203. Capture, playback and capture level have
2756a07df82cSOlivia Mackintosh  * different wIndexes.
2757cdc01a15SFrantišek Kučera  */
2758cdc01a15SFrantišek Kučera 
2759a07df82cSOlivia Mackintosh // Capture types
2760a07df82cSOlivia Mackintosh #define SND_DJM_CAP_LINE	0x00
2761a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CDLINE	0x01
2762fee03efcSFabian Lesniak #define SND_DJM_CAP_DIGITAL	0x02
2763a07df82cSOlivia Mackintosh #define SND_DJM_CAP_PHONO	0x03
2764a07df82cSOlivia Mackintosh #define SND_DJM_CAP_PFADER	0x06
2765a07df82cSOlivia Mackintosh #define SND_DJM_CAP_XFADERA	0x07
2766a07df82cSOlivia Mackintosh #define SND_DJM_CAP_XFADERB	0x08
2767a07df82cSOlivia Mackintosh #define SND_DJM_CAP_MIC		0x09
2768a07df82cSOlivia Mackintosh #define SND_DJM_CAP_AUX		0x0d
2769a07df82cSOlivia Mackintosh #define SND_DJM_CAP_RECOUT	0x0a
2770a07df82cSOlivia Mackintosh #define SND_DJM_CAP_NONE	0x0f
2771a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CH1PFADER	0x11
2772a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CH2PFADER	0x12
2773fee03efcSFabian Lesniak #define SND_DJM_CAP_CH3PFADER	0x13
2774fee03efcSFabian Lesniak #define SND_DJM_CAP_CH4PFADER	0x14
2775cdc01a15SFrantišek Kučera 
2776a07df82cSOlivia Mackintosh // Playback types
2777a07df82cSOlivia Mackintosh #define SND_DJM_PB_CH1		0x00
2778a07df82cSOlivia Mackintosh #define SND_DJM_PB_CH2		0x01
2779a07df82cSOlivia Mackintosh #define SND_DJM_PB_AUX		0x04
2780cdc01a15SFrantišek Kučera 
2781a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_CAP	0x8002
2782a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_CAPLVL	0x8003
2783a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_PB	0x8016
2784cdc01a15SFrantišek Kučera 
2785a07df82cSOlivia Mackintosh // kcontrol->private_value layout
2786a07df82cSOlivia Mackintosh #define SND_DJM_VALUE_MASK	0x0000ffff
2787a07df82cSOlivia Mackintosh #define SND_DJM_GROUP_MASK	0x00ff0000
2788a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE_MASK	0xff000000
2789a07df82cSOlivia Mackintosh #define SND_DJM_GROUP_SHIFT	16
2790a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE_SHIFT	24
2791cdc01a15SFrantišek Kučera 
2792a07df82cSOlivia Mackintosh // device table index
27937687850bSNicolas MURE // used for the snd_djm_devices table, so please update accordingly
2794a07df82cSOlivia Mackintosh #define SND_DJM_250MK2_IDX	0x0
2795a07df82cSOlivia Mackintosh #define SND_DJM_750_IDX		0x1
27967687850bSNicolas MURE #define SND_DJM_850_IDX		0x2
27977687850bSNicolas MURE #define SND_DJM_900NXS2_IDX	0x3
2798*6d277881SWilliam Overton #define SND_DJM_750MK2_IDX	0x4
2799cdc01a15SFrantišek Kučera 
2800cdc01a15SFrantišek Kučera 
2801a07df82cSOlivia Mackintosh #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \
2802cdc01a15SFrantišek Kučera 	.name = _name, \
2803a07df82cSOlivia Mackintosh 	.options = snd_djm_opts_##suffix, \
2804a07df82cSOlivia Mackintosh 	.noptions = ARRAY_SIZE(snd_djm_opts_##suffix), \
2805a07df82cSOlivia Mackintosh 	.default_value = _default_value, \
2806a07df82cSOlivia Mackintosh 	.wIndex = _windex }
2807cdc01a15SFrantišek Kučera 
2808a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE(suffix) { \
2809a07df82cSOlivia Mackintosh 	.controls = snd_djm_ctls_##suffix, \
2810a07df82cSOlivia Mackintosh 	.ncontrols = ARRAY_SIZE(snd_djm_ctls_##suffix) }
2811a07df82cSOlivia Mackintosh 
2812a07df82cSOlivia Mackintosh 
2813a07df82cSOlivia Mackintosh struct snd_djm_device {
2814a07df82cSOlivia Mackintosh 	const char *name;
2815a07df82cSOlivia Mackintosh 	const struct snd_djm_ctl *controls;
2816a07df82cSOlivia Mackintosh 	size_t ncontrols;
2817cdc01a15SFrantišek Kučera };
2818cdc01a15SFrantišek Kučera 
2819a07df82cSOlivia Mackintosh struct snd_djm_ctl {
2820cdc01a15SFrantišek Kučera 	const char *name;
2821a07df82cSOlivia Mackintosh 	const u16 *options;
2822a07df82cSOlivia Mackintosh 	size_t noptions;
2823a07df82cSOlivia Mackintosh 	u16 default_value;
2824a07df82cSOlivia Mackintosh 	u16 wIndex;
2825a07df82cSOlivia Mackintosh };
2826cdc01a15SFrantišek Kučera 
2827a07df82cSOlivia Mackintosh static const char *snd_djm_get_label_caplevel(u16 wvalue)
2828a07df82cSOlivia Mackintosh {
2829a07df82cSOlivia Mackintosh 	switch (wvalue) {
2830a07df82cSOlivia Mackintosh 	case 0x0000:	return "-19dB";
2831a07df82cSOlivia Mackintosh 	case 0x0100:	return "-15dB";
2832a07df82cSOlivia Mackintosh 	case 0x0200:	return "-10dB";
2833a07df82cSOlivia Mackintosh 	case 0x0300:	return "-5dB";
2834a07df82cSOlivia Mackintosh 	default:	return NULL;
2835a07df82cSOlivia Mackintosh 	}
2836a07df82cSOlivia Mackintosh };
2837a07df82cSOlivia Mackintosh 
2838b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap_common(u16 wvalue)
2839a07df82cSOlivia Mackintosh {
2840a07df82cSOlivia Mackintosh 	switch (wvalue & 0x00ff) {
2841a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_LINE:		return "Control Tone LINE";
2842a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CDLINE:	return "Control Tone CD/LINE";
2843fee03efcSFabian Lesniak 	case SND_DJM_CAP_DIGITAL:	return "Control Tone DIGITAL";
2844a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_PHONO:		return "Control Tone PHONO";
2845a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_PFADER:	return "Post Fader";
2846a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_XFADERA:	return "Cross Fader A";
2847a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_XFADERB:	return "Cross Fader B";
2848a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_MIC:		return "Mic";
2849a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_RECOUT:	return "Rec Out";
2850a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_AUX:		return "Aux";
2851a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_NONE:		return "None";
2852a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CH1PFADER:	return "Post Fader Ch1";
2853a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CH2PFADER:	return "Post Fader Ch2";
2854fee03efcSFabian Lesniak 	case SND_DJM_CAP_CH3PFADER:	return "Post Fader Ch3";
2855fee03efcSFabian Lesniak 	case SND_DJM_CAP_CH4PFADER:	return "Post Fader Ch4";
2856a07df82cSOlivia Mackintosh 	default:			return NULL;
2857a07df82cSOlivia Mackintosh 	}
2858a07df82cSOlivia Mackintosh };
2859a07df82cSOlivia Mackintosh 
2860b8db8be8SNicolas MURE // The DJM-850 has different values for CD/LINE and LINE capture
2861b8db8be8SNicolas MURE // control options than the other DJM declared in this file.
2862b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap_850(u16 wvalue)
2863b8db8be8SNicolas MURE {
2864b8db8be8SNicolas MURE 	switch (wvalue & 0x00ff) {
2865b8db8be8SNicolas MURE 	case 0x00:		return "Control Tone CD/LINE";
2866b8db8be8SNicolas MURE 	case 0x01:		return "Control Tone LINE";
2867b8db8be8SNicolas MURE 	default:		return snd_djm_get_label_cap_common(wvalue);
2868b8db8be8SNicolas MURE 	}
2869b8db8be8SNicolas MURE };
2870b8db8be8SNicolas MURE 
2871b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap(u8 device_idx, u16 wvalue)
2872b8db8be8SNicolas MURE {
2873b8db8be8SNicolas MURE 	switch (device_idx) {
2874b8db8be8SNicolas MURE 	case SND_DJM_850_IDX:		return snd_djm_get_label_cap_850(wvalue);
2875b8db8be8SNicolas MURE 	default:			return snd_djm_get_label_cap_common(wvalue);
2876b8db8be8SNicolas MURE 	}
2877b8db8be8SNicolas MURE };
2878b8db8be8SNicolas MURE 
2879a07df82cSOlivia Mackintosh static const char *snd_djm_get_label_pb(u16 wvalue)
2880a07df82cSOlivia Mackintosh {
2881a07df82cSOlivia Mackintosh 	switch (wvalue & 0x00ff) {
2882a07df82cSOlivia Mackintosh 	case SND_DJM_PB_CH1:	return "Ch1";
2883a07df82cSOlivia Mackintosh 	case SND_DJM_PB_CH2:	return "Ch2";
2884a07df82cSOlivia Mackintosh 	case SND_DJM_PB_AUX:	return "Aux";
2885a07df82cSOlivia Mackintosh 	default:		return NULL;
2886a07df82cSOlivia Mackintosh 	}
2887a07df82cSOlivia Mackintosh };
2888a07df82cSOlivia Mackintosh 
2889b8db8be8SNicolas MURE static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex)
2890a07df82cSOlivia Mackintosh {
2891a07df82cSOlivia Mackintosh 	switch (windex) {
2892a07df82cSOlivia Mackintosh 	case SND_DJM_WINDEX_CAPLVL:	return snd_djm_get_label_caplevel(wvalue);
2893b8db8be8SNicolas MURE 	case SND_DJM_WINDEX_CAP:	return snd_djm_get_label_cap(device_idx, wvalue);
2894a07df82cSOlivia Mackintosh 	case SND_DJM_WINDEX_PB:		return snd_djm_get_label_pb(wvalue);
2895a07df82cSOlivia Mackintosh 	default:			return NULL;
2896a07df82cSOlivia Mackintosh 	}
2897a07df82cSOlivia Mackintosh };
2898a07df82cSOlivia Mackintosh 
28997687850bSNicolas MURE // common DJM capture level option values
2900a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_cap_level[] = {
2901a07df82cSOlivia Mackintosh 	0x0000, 0x0100, 0x0200, 0x0300 };
2902a07df82cSOlivia Mackintosh 
29037687850bSNicolas MURE 
29047687850bSNicolas MURE // DJM-250MK2
2905a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap1[] = {
2906a07df82cSOlivia Mackintosh 	0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a };
2907a07df82cSOlivia Mackintosh 
2908a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap2[] = {
2909a07df82cSOlivia Mackintosh 	0x0203, 0x0200, 0x0206, 0x0207, 0x0208, 0x0209, 0x020d, 0x020a };
2910a07df82cSOlivia Mackintosh 
2911a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap3[] = {
2912a07df82cSOlivia Mackintosh 	0x030a, 0x0311, 0x0312, 0x0307, 0x0308, 0x0309, 0x030d };
2913a07df82cSOlivia Mackintosh 
2914a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
2915a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
2916a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
2917a07df82cSOlivia Mackintosh 
2918a07df82cSOlivia Mackintosh static const struct snd_djm_ctl snd_djm_ctls_250mk2[] = {
2919a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
2920a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Input",   250mk2_cap1, 2, SND_DJM_WINDEX_CAP),
2921a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Input",   250mk2_cap2, 2, SND_DJM_WINDEX_CAP),
2922a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Input",   250mk2_cap3, 0, SND_DJM_WINDEX_CAP),
2923a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Output",   250mk2_pb1, 0, SND_DJM_WINDEX_PB),
2924a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Output",   250mk2_pb2, 1, SND_DJM_WINDEX_PB),
2925a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Output",   250mk2_pb3, 2, SND_DJM_WINDEX_PB)
2926a07df82cSOlivia Mackintosh };
2927a07df82cSOlivia Mackintosh 
2928a07df82cSOlivia Mackintosh 
2929a07df82cSOlivia Mackintosh // DJM-750
2930a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap1[] = {
2931a07df82cSOlivia Mackintosh 	0x0101, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
2932a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap2[] = {
2933a07df82cSOlivia Mackintosh 	0x0200, 0x0201, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020f };
2934a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap3[] = {
2935a07df82cSOlivia Mackintosh 	0x0300, 0x0301, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030f };
2936a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap4[] = {
2937a07df82cSOlivia Mackintosh 	0x0401, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
2938a07df82cSOlivia Mackintosh 
2939a07df82cSOlivia Mackintosh static const struct snd_djm_ctl snd_djm_ctls_750[] = {
2940a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
2941a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Input",   750_cap1, 2, SND_DJM_WINDEX_CAP),
2942a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Input",   750_cap2, 2, SND_DJM_WINDEX_CAP),
2943a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Input",   750_cap3, 0, SND_DJM_WINDEX_CAP),
2944a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch4 Input",   750_cap4, 0, SND_DJM_WINDEX_CAP)
2945a07df82cSOlivia Mackintosh };
2946a07df82cSOlivia Mackintosh 
2947a07df82cSOlivia Mackintosh 
29487687850bSNicolas MURE // DJM-850
29497687850bSNicolas MURE static const u16 snd_djm_opts_850_cap1[] = {
29507687850bSNicolas MURE 	0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
29517687850bSNicolas MURE static const u16 snd_djm_opts_850_cap2[] = {
29527687850bSNicolas MURE 	0x0200, 0x0201, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020f };
29537687850bSNicolas MURE static const u16 snd_djm_opts_850_cap3[] = {
29547687850bSNicolas MURE 	0x0300, 0x0301, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030f };
29557687850bSNicolas MURE static const u16 snd_djm_opts_850_cap4[] = {
29567687850bSNicolas MURE 	0x0400, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
29577687850bSNicolas MURE 
29587687850bSNicolas MURE static const struct snd_djm_ctl snd_djm_ctls_850[] = {
29597687850bSNicolas MURE 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
29607687850bSNicolas MURE 	SND_DJM_CTL("Ch1 Input",   850_cap1, 1, SND_DJM_WINDEX_CAP),
29617687850bSNicolas MURE 	SND_DJM_CTL("Ch2 Input",   850_cap2, 0, SND_DJM_WINDEX_CAP),
29627687850bSNicolas MURE 	SND_DJM_CTL("Ch3 Input",   850_cap3, 0, SND_DJM_WINDEX_CAP),
29637687850bSNicolas MURE 	SND_DJM_CTL("Ch4 Input",   850_cap4, 1, SND_DJM_WINDEX_CAP)
29647687850bSNicolas MURE };
29657687850bSNicolas MURE 
29667687850bSNicolas MURE 
2967fee03efcSFabian Lesniak // DJM-900NXS2
2968fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap1[] = {
2969fee03efcSFabian Lesniak 	0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
2970fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap2[] = {
2971fee03efcSFabian Lesniak 	0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
2972fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap3[] = {
2973fee03efcSFabian Lesniak 	0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
2974fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap4[] = {
2975fee03efcSFabian Lesniak 	0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
2976fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap5[] = {
2977fee03efcSFabian Lesniak 	0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
2978fee03efcSFabian Lesniak 
2979fee03efcSFabian Lesniak static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = {
2980fee03efcSFabian Lesniak 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
2981fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch1 Input",   900nxs2_cap1, 2, SND_DJM_WINDEX_CAP),
2982fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch2 Input",   900nxs2_cap2, 2, SND_DJM_WINDEX_CAP),
2983fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch3 Input",   900nxs2_cap3, 2, SND_DJM_WINDEX_CAP),
2984fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch4 Input",   900nxs2_cap4, 2, SND_DJM_WINDEX_CAP),
2985fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch5 Input",   900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
2986fee03efcSFabian Lesniak };
2987fee03efcSFabian Lesniak 
2988*6d277881SWilliam Overton // DJM-750MK2
2989*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap1[] = {
2990*6d277881SWilliam Overton 	0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
2991*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap2[] = {
2992*6d277881SWilliam Overton 	0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
2993*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap3[] = {
2994*6d277881SWilliam Overton 	0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
2995*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap4[] = {
2996*6d277881SWilliam Overton 	0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
2997*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap5[] = {
2998*6d277881SWilliam Overton 	0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
2999*6d277881SWilliam Overton 
3000*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
3001*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
3002*6d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
3003*6d277881SWilliam Overton 
3004*6d277881SWilliam Overton 
3005*6d277881SWilliam Overton static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = {
3006*6d277881SWilliam Overton 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
3007*6d277881SWilliam Overton 	SND_DJM_CTL("Ch1 Input",   750mk2_cap1, 2, SND_DJM_WINDEX_CAP),
3008*6d277881SWilliam Overton 	SND_DJM_CTL("Ch2 Input",   750mk2_cap2, 2, SND_DJM_WINDEX_CAP),
3009*6d277881SWilliam Overton 	SND_DJM_CTL("Ch3 Input",   750mk2_cap3, 2, SND_DJM_WINDEX_CAP),
3010*6d277881SWilliam Overton 	SND_DJM_CTL("Ch4 Input",   750mk2_cap4, 2, SND_DJM_WINDEX_CAP),
3011*6d277881SWilliam Overton 	SND_DJM_CTL("Ch5 Input",   750mk2_cap5, 3, SND_DJM_WINDEX_CAP),
3012*6d277881SWilliam Overton 	SND_DJM_CTL("Ch1 Output",   750mk2_pb1, 0, SND_DJM_WINDEX_PB),
3013*6d277881SWilliam Overton 	SND_DJM_CTL("Ch2 Output",   750mk2_pb2, 1, SND_DJM_WINDEX_PB),
3014*6d277881SWilliam Overton 	SND_DJM_CTL("Ch3 Output",   750mk2_pb3, 2, SND_DJM_WINDEX_PB)
3015*6d277881SWilliam Overton };
3016*6d277881SWilliam Overton 
3017fee03efcSFabian Lesniak 
3018a07df82cSOlivia Mackintosh static const struct snd_djm_device snd_djm_devices[] = {
3019a07df82cSOlivia Mackintosh 	SND_DJM_DEVICE(250mk2),
3020fee03efcSFabian Lesniak 	SND_DJM_DEVICE(750),
3021*6d277881SWilliam Overton 	SND_DJM_DEVICE(750mk2),
30227687850bSNicolas MURE 	SND_DJM_DEVICE(850),
3023fee03efcSFabian Lesniak 	SND_DJM_DEVICE(900nxs2)
3024a07df82cSOlivia Mackintosh };
3025a07df82cSOlivia Mackintosh 
3026a07df82cSOlivia Mackintosh 
3027a07df82cSOlivia Mackintosh static int snd_djm_controls_info(struct snd_kcontrol *kctl,
3028a07df82cSOlivia Mackintosh 				struct snd_ctl_elem_info *info)
3029a07df82cSOlivia Mackintosh {
3030a07df82cSOlivia Mackintosh 	unsigned long private_value = kctl->private_value;
3031a07df82cSOlivia Mackintosh 	u8 device_idx = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
3032a07df82cSOlivia Mackintosh 	u8 ctl_idx = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
3033a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
3034a07df82cSOlivia Mackintosh 	const char *name;
3035a07df82cSOlivia Mackintosh 	const struct snd_djm_ctl *ctl;
3036a07df82cSOlivia Mackintosh 	size_t noptions;
3037a07df82cSOlivia Mackintosh 
3038a07df82cSOlivia Mackintosh 	if (ctl_idx >= device->ncontrols)
3039cdc01a15SFrantišek Kučera 		return -EINVAL;
3040cdc01a15SFrantišek Kučera 
3041a07df82cSOlivia Mackintosh 	ctl = &device->controls[ctl_idx];
3042a07df82cSOlivia Mackintosh 	noptions = ctl->noptions;
3043a07df82cSOlivia Mackintosh 	if (info->value.enumerated.item >= noptions)
3044a07df82cSOlivia Mackintosh 		info->value.enumerated.item = noptions - 1;
3045a07df82cSOlivia Mackintosh 
3046b8db8be8SNicolas MURE 	name = snd_djm_get_label(device_idx,
3047b8db8be8SNicolas MURE 				ctl->options[info->value.enumerated.item],
3048a07df82cSOlivia Mackintosh 				ctl->wIndex);
3049a07df82cSOlivia Mackintosh 	if (!name)
3050a07df82cSOlivia Mackintosh 		return -EINVAL;
3051a07df82cSOlivia Mackintosh 
305275b1a8f9SJoe Perches 	strscpy(info->value.enumerated.name, name, sizeof(info->value.enumerated.name));
3053cdc01a15SFrantišek Kučera 	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
3054cdc01a15SFrantišek Kučera 	info->count = 1;
3055a07df82cSOlivia Mackintosh 	info->value.enumerated.items = noptions;
3056cdc01a15SFrantišek Kučera 	return 0;
3057cdc01a15SFrantišek Kučera }
3058cdc01a15SFrantišek Kučera 
3059a07df82cSOlivia Mackintosh static int snd_djm_controls_update(struct usb_mixer_interface *mixer,
3060a07df82cSOlivia Mackintosh 				u8 device_idx, u8 group, u16 value)
3061cdc01a15SFrantišek Kučera {
3062cdc01a15SFrantišek Kučera 	int err;
3063a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
3064cdc01a15SFrantišek Kučera 
3065a07df82cSOlivia Mackintosh 	if ((group >= device->ncontrols) || value >= device->controls[group].noptions)
3066cdc01a15SFrantišek Kučera 		return -EINVAL;
3067cdc01a15SFrantišek Kučera 
3068cdc01a15SFrantišek Kučera 	err = snd_usb_lock_shutdown(mixer->chip);
3069cdc01a15SFrantišek Kučera 	if (err)
3070cdc01a15SFrantišek Kučera 		return err;
3071cdc01a15SFrantišek Kučera 
3072cdc01a15SFrantišek Kučera 	err = snd_usb_ctl_msg(
3073cdc01a15SFrantišek Kučera 		mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0),
3074cdc01a15SFrantišek Kučera 		USB_REQ_SET_FEATURE,
3075cdc01a15SFrantišek Kučera 		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3076a07df82cSOlivia Mackintosh 		device->controls[group].options[value],
3077a07df82cSOlivia Mackintosh 		device->controls[group].wIndex,
3078cdc01a15SFrantišek Kučera 		NULL, 0);
3079cdc01a15SFrantišek Kučera 
3080cdc01a15SFrantišek Kučera 	snd_usb_unlock_shutdown(mixer->chip);
3081cdc01a15SFrantišek Kučera 	return err;
3082cdc01a15SFrantišek Kučera }
3083cdc01a15SFrantišek Kučera 
3084a07df82cSOlivia Mackintosh static int snd_djm_controls_get(struct snd_kcontrol *kctl,
3085a07df82cSOlivia Mackintosh 				struct snd_ctl_elem_value *elem)
3086cdc01a15SFrantišek Kučera {
3087a07df82cSOlivia Mackintosh 	elem->value.enumerated.item[0] = kctl->private_value & SND_DJM_VALUE_MASK;
3088cdc01a15SFrantišek Kučera 	return 0;
3089cdc01a15SFrantišek Kučera }
3090cdc01a15SFrantišek Kučera 
3091a07df82cSOlivia Mackintosh static int snd_djm_controls_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *elem)
3092cdc01a15SFrantišek Kučera {
3093cdc01a15SFrantišek Kučera 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
3094cdc01a15SFrantišek Kučera 	struct usb_mixer_interface *mixer = list->mixer;
3095cdc01a15SFrantišek Kučera 	unsigned long private_value = kctl->private_value;
3096a07df82cSOlivia Mackintosh 
3097a07df82cSOlivia Mackintosh 	u8 device = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
3098a07df82cSOlivia Mackintosh 	u8 group = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
3099cdc01a15SFrantišek Kučera 	u16 value = elem->value.enumerated.item[0];
3100cdc01a15SFrantišek Kučera 
310150b1affcSColin Ian King 	kctl->private_value = (((unsigned long)device << SND_DJM_DEVICE_SHIFT) |
3102a07df82cSOlivia Mackintosh 			      (group << SND_DJM_GROUP_SHIFT) |
3103a07df82cSOlivia Mackintosh 			      value);
3104cdc01a15SFrantišek Kučera 
3105a07df82cSOlivia Mackintosh 	return snd_djm_controls_update(mixer, device, group, value);
3106cdc01a15SFrantišek Kučera }
3107cdc01a15SFrantišek Kučera 
3108a07df82cSOlivia Mackintosh static int snd_djm_controls_resume(struct usb_mixer_elem_list *list)
3109cdc01a15SFrantišek Kučera {
3110cdc01a15SFrantišek Kučera 	unsigned long private_value = list->kctl->private_value;
3111a07df82cSOlivia Mackintosh 	u8 device = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
3112a07df82cSOlivia Mackintosh 	u8 group = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
3113a07df82cSOlivia Mackintosh 	u16 value = (private_value & SND_DJM_VALUE_MASK);
3114cdc01a15SFrantišek Kučera 
3115a07df82cSOlivia Mackintosh 	return snd_djm_controls_update(list->mixer, device, group, value);
3116cdc01a15SFrantišek Kučera }
3117cdc01a15SFrantišek Kučera 
3118a07df82cSOlivia Mackintosh static int snd_djm_controls_create(struct usb_mixer_interface *mixer,
3119a07df82cSOlivia Mackintosh 		const u8 device_idx)
3120cdc01a15SFrantišek Kučera {
3121cdc01a15SFrantišek Kučera 	int err, i;
3122a07df82cSOlivia Mackintosh 	u16 value;
3123a07df82cSOlivia Mackintosh 
3124a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
3125a07df82cSOlivia Mackintosh 
3126cdc01a15SFrantišek Kučera 	struct snd_kcontrol_new knew = {
3127cdc01a15SFrantišek Kučera 		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
3128cdc01a15SFrantišek Kučera 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
3129cdc01a15SFrantišek Kučera 		.index = 0,
3130a07df82cSOlivia Mackintosh 		.info = snd_djm_controls_info,
3131a07df82cSOlivia Mackintosh 		.get  = snd_djm_controls_get,
3132a07df82cSOlivia Mackintosh 		.put  = snd_djm_controls_put
3133cdc01a15SFrantišek Kučera 	};
3134cdc01a15SFrantišek Kučera 
3135a07df82cSOlivia Mackintosh 	for (i = 0; i < device->ncontrols; i++) {
3136a07df82cSOlivia Mackintosh 		value = device->controls[i].default_value;
3137a07df82cSOlivia Mackintosh 		knew.name = device->controls[i].name;
3138a07df82cSOlivia Mackintosh 		knew.private_value = (
313950b1affcSColin Ian King 			((unsigned long)device_idx << SND_DJM_DEVICE_SHIFT) |
3140a07df82cSOlivia Mackintosh 			(i << SND_DJM_GROUP_SHIFT) |
3141a07df82cSOlivia Mackintosh 			value);
3142a07df82cSOlivia Mackintosh 		err = snd_djm_controls_update(mixer, device_idx, i, value);
3143cdc01a15SFrantišek Kučera 		if (err)
3144cdc01a15SFrantišek Kučera 			return err;
3145a07df82cSOlivia Mackintosh 		err = add_single_ctl_with_resume(mixer, 0, snd_djm_controls_resume,
3146cdc01a15SFrantišek Kučera 						 &knew, NULL);
3147cdc01a15SFrantišek Kučera 		if (err)
3148cdc01a15SFrantišek Kučera 			return err;
3149cdc01a15SFrantišek Kučera 	}
3150cdc01a15SFrantišek Kučera 	return 0;
3151cdc01a15SFrantišek Kučera }
3152cdc01a15SFrantišek Kučera 
31537b1eda22SDaniel Mack int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
31547b1eda22SDaniel Mack {
31553347b26cSDaniel Mack 	int err = 0;
31567b1eda22SDaniel Mack 
3157f25ecf8fSTakashi Iwai 	err = snd_usb_soundblaster_remote_init(mixer);
3158f25ecf8fSTakashi Iwai 	if (err < 0)
31597b1eda22SDaniel Mack 		return err;
31607b1eda22SDaniel Mack 
31613347b26cSDaniel Mack 	switch (mixer->chip->usb_id) {
3162d2bb390aSDetlef Urban 	/* Tascam US-16x08 */
3163d2bb390aSDetlef Urban 	case USB_ID(0x0644, 0x8047):
3164d2bb390aSDetlef Urban 		err = snd_us16x08_controls_create(mixer);
3165d2bb390aSDetlef Urban 		break;
31663347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3020):
31673347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3040):
31683347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3042):
31697cdd8d73SMathieu Bouffard 	case USB_ID(0x041e, 0x30df):
31703347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3048):
31713347b26cSDaniel Mack 		err = snd_audigy2nx_controls_create(mixer);
31723347b26cSDaniel Mack 		if (err < 0)
31733347b26cSDaniel Mack 			break;
31747449054aSTakashi Iwai 		snd_card_ro_proc_new(mixer->chip->card, "audigy2nx",
31757449054aSTakashi Iwai 				     mixer, snd_audigy2nx_proc_read);
31763347b26cSDaniel Mack 		break;
31777b1eda22SDaniel Mack 
317844832a71SVasily Khoruzhick 	/* EMU0204 */
317944832a71SVasily Khoruzhick 	case USB_ID(0x041e, 0x3f19):
318044832a71SVasily Khoruzhick 		err = snd_emu0204_controls_create(mixer);
318144832a71SVasily Khoruzhick 		break;
318244832a71SVasily Khoruzhick 
318309d8e3a7SEldad Zack 	case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
3184e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
318509d8e3a7SEldad Zack 		err = snd_c400_create_mixer(mixer);
318609d8e3a7SEldad Zack 		break;
318709d8e3a7SEldad Zack 
3188d5a0bf6cSDaniel Mack 	case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
3189d5a0bf6cSDaniel Mack 	case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
3190cfe8f97cSFelix Homann 		err = snd_ftu_create_mixer(mixer);
3191d5a0bf6cSDaniel Mack 		break;
3192d5a0bf6cSDaniel Mack 
31931d31affbSDenis Washington 	case USB_ID(0x0b05, 0x1739): /* ASUS Xonar U1 */
31941d31affbSDenis Washington 	case USB_ID(0x0b05, 0x1743): /* ASUS Xonar U1 (2) */
31951d31affbSDenis Washington 	case USB_ID(0x0b05, 0x17a0): /* ASUS Xonar U3 */
31967b1eda22SDaniel Mack 		err = snd_xonar_u1_controls_create(mixer);
31973347b26cSDaniel Mack 		break;
31987b1eda22SDaniel Mack 
3199066624c6SPrzemek Rudy 	case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */
3200066624c6SPrzemek Rudy 		err = snd_microii_controls_create(mixer);
3201066624c6SPrzemek Rudy 		break;
3202066624c6SPrzemek Rudy 
3203d497a82fSDamien Zammit 	case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */
32047ac2246fSDamien Zammit 		err = snd_mbox1_controls_create(mixer);
3205d497a82fSDamien Zammit 		break;
3206d497a82fSDamien Zammit 
32073347b26cSDaniel Mack 	case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
320854a8c500SDaniel Mack 		err = snd_nativeinstruments_create_mixer(mixer,
320954a8c500SDaniel Mack 				snd_nativeinstruments_ta6_mixers,
321054a8c500SDaniel Mack 				ARRAY_SIZE(snd_nativeinstruments_ta6_mixers));
32113347b26cSDaniel Mack 		break;
321254a8c500SDaniel Mack 
32133347b26cSDaniel Mack 	case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */
321454a8c500SDaniel Mack 		err = snd_nativeinstruments_create_mixer(mixer,
321554a8c500SDaniel Mack 				snd_nativeinstruments_ta10_mixers,
321654a8c500SDaniel Mack 				ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
32173347b26cSDaniel Mack 		break;
32187536c301SMark Hills 
32197536c301SMark Hills 	case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
3220b71dad18SMark Hills 		/* detection is disabled in mixer_maps.c */
3221b71dad18SMark Hills 		err = snd_create_std_mono_table(mixer, ebox44_table);
32227536c301SMark Hills 		break;
322376b188c4SChris J Arges 
322476b188c4SChris J Arges 	case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */
322576b188c4SChris J Arges 	case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */
322676b188c4SChris J Arges 	case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */
322776b188c4SChris J Arges 	case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */
322876b188c4SChris J Arges 	case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */
322976b188c4SChris J Arges 		err = snd_scarlett_controls_create(mixer);
323076b188c4SChris J Arges 		break;
3231388fdb8fSIan Douglas Scott 
32329e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */
32339e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */
32349e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */
32352fa96277SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8211): /* Focusrite Scarlett Solo 3rd Gen */
32362fa96277SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8210): /* Focusrite Scarlett 2i2 3rd Gen */
32374be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8212): /* Focusrite Scarlett 4i4 3rd Gen */
32384be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
32394be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
32404be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
3241265d1a90SGeoffrey D. Bennett 		err = snd_scarlett_gen2_init(mixer);
32429e4d5c1bSGeoffrey D. Bennett 		break;
32439e4d5c1bSGeoffrey D. Bennett 
3244388fdb8fSIan Douglas Scott 	case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
3245388fdb8fSIan Douglas Scott 		err = snd_soundblaster_e1_switch_create(mixer);
3246388fdb8fSIan Douglas Scott 		break;
3247964af639STakashi Iwai 	case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
3248964af639STakashi Iwai 		err = dell_dock_mixer_init(mixer);
3249964af639STakashi Iwai 		break;
3250d39f1d68SJussi Laako 
3251d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */
3252d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */
3253d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd4): /* RME */
3254d39f1d68SJussi Laako 		err = snd_rme_controls_create(mixer);
3255d39f1d68SJussi Laako 		break;
32568dc5efe3SNick Kossifidis 
32578dc5efe3SNick Kossifidis 	case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */
32588dc5efe3SNick Kossifidis 		err = snd_sc1810_init_mixer(mixer);
32598dc5efe3SNick Kossifidis 		break;
32603e8f3bd0SThomas Ebeling 	case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
32613e8f3bd0SThomas Ebeling 		err = snd_bbfpro_controls_create(mixer);
32623e8f3bd0SThomas Ebeling 		break;
3263cdc01a15SFrantišek Kučera 	case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */
3264a07df82cSOlivia Mackintosh 		err = snd_djm_controls_create(mixer, SND_DJM_250MK2_IDX);
3265a07df82cSOlivia Mackintosh 		break;
3266a07df82cSOlivia Mackintosh 	case USB_ID(0x08e4, 0x017f): /* Pioneer DJ DJM-750 */
3267a07df82cSOlivia Mackintosh 		err = snd_djm_controls_create(mixer, SND_DJM_750_IDX);
3268cdc01a15SFrantišek Kučera 		break;
3269*6d277881SWilliam Overton 	case USB_ID(0x2b73, 0x001b): /* Pioneer DJ DJM-750MK2 */
3270*6d277881SWilliam Overton 		err = snd_djm_controls_create(mixer, SND_DJM_750MK2_IDX);
3271*6d277881SWilliam Overton 		break;
32727687850bSNicolas MURE 	case USB_ID(0x08e4, 0x0163): /* Pioneer DJ DJM-850 */
32737687850bSNicolas MURE 		err = snd_djm_controls_create(mixer, SND_DJM_850_IDX);
32747687850bSNicolas MURE 		break;
3275fee03efcSFabian Lesniak 	case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
3276fee03efcSFabian Lesniak 		err = snd_djm_controls_create(mixer, SND_DJM_900NXS2_IDX);
3277fee03efcSFabian Lesniak 		break;
327854a8c500SDaniel Mack 	}
327954a8c500SDaniel Mack 
32803347b26cSDaniel Mack 	return err;
32817b1eda22SDaniel Mack }
32827b1eda22SDaniel Mack 
3283964af639STakashi Iwai #ifdef CONFIG_PM
3284964af639STakashi Iwai void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
3285964af639STakashi Iwai {
3286964af639STakashi Iwai 	switch (mixer->chip->usb_id) {
3287964af639STakashi Iwai 	case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
3288964af639STakashi Iwai 		dell_dock_mixer_init(mixer);
3289964af639STakashi Iwai 		break;
3290964af639STakashi Iwai 	}
3291964af639STakashi Iwai }
3292964af639STakashi Iwai #endif
3293964af639STakashi Iwai 
32947b1eda22SDaniel Mack void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
32957b1eda22SDaniel Mack 				    int unitid)
32967b1eda22SDaniel Mack {
32977b1eda22SDaniel Mack 	if (!mixer->rc_cfg)
32987b1eda22SDaniel Mack 		return;
32997b1eda22SDaniel Mack 	/* unit ids specific to Extigy/Audigy 2 NX: */
33007b1eda22SDaniel Mack 	switch (unitid) {
33017b1eda22SDaniel Mack 	case 0: /* remote control */
33027b1eda22SDaniel Mack 		mixer->rc_urb->dev = mixer->chip->dev;
33037b1eda22SDaniel Mack 		usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
33047b1eda22SDaniel Mack 		break;
33057b1eda22SDaniel Mack 	case 4: /* digital in jack */
33067b1eda22SDaniel Mack 	case 7: /* line in jacks */
33077b1eda22SDaniel Mack 	case 19: /* speaker out jacks */
33087b1eda22SDaniel Mack 	case 20: /* headphones out jack */
33097b1eda22SDaniel Mack 		break;
33107b1eda22SDaniel Mack 	/* live24ext: 4 = line-in jack */
33117b1eda22SDaniel Mack 	case 3:	/* hp-out jack (may actuate Mute) */
33127b1eda22SDaniel Mack 		if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
33137b1eda22SDaniel Mack 		    mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
33147b1eda22SDaniel Mack 			snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
33157b1eda22SDaniel Mack 		break;
33167b1eda22SDaniel Mack 	default:
33170ba41d91STakashi Iwai 		usb_audio_dbg(mixer->chip, "memory change in unknown unit %d\n", unitid);
33187b1eda22SDaniel Mack 		break;
33197b1eda22SDaniel Mack 	}
33207b1eda22SDaniel Mack }
33217b1eda22SDaniel Mack 
332242e3121dSAnssi Hannula static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
3323eb1a74b7SAnssi Hannula 					 struct usb_mixer_elem_info *cval,
332442e3121dSAnssi Hannula 					 struct snd_kcontrol *kctl)
332542e3121dSAnssi Hannula {
332642e3121dSAnssi Hannula 	/* Approximation using 10 ranges based on output measurement on hw v1.2.
332742e3121dSAnssi Hannula 	 * This seems close to the cubic mapping e.g. alsamixer uses. */
332842e3121dSAnssi Hannula 	static const DECLARE_TLV_DB_RANGE(scale,
332942e3121dSAnssi Hannula 		 0,  1, TLV_DB_MINMAX_ITEM(-5300, -4970),
333042e3121dSAnssi Hannula 		 2,  5, TLV_DB_MINMAX_ITEM(-4710, -4160),
333142e3121dSAnssi Hannula 		 6,  7, TLV_DB_MINMAX_ITEM(-3884, -3710),
333242e3121dSAnssi Hannula 		 8, 14, TLV_DB_MINMAX_ITEM(-3443, -2560),
333342e3121dSAnssi Hannula 		15, 16, TLV_DB_MINMAX_ITEM(-2475, -2324),
333442e3121dSAnssi Hannula 		17, 19, TLV_DB_MINMAX_ITEM(-2228, -2031),
333542e3121dSAnssi Hannula 		20, 26, TLV_DB_MINMAX_ITEM(-1910, -1393),
333642e3121dSAnssi Hannula 		27, 31, TLV_DB_MINMAX_ITEM(-1322, -1032),
333742e3121dSAnssi Hannula 		32, 40, TLV_DB_MINMAX_ITEM(-968, -490),
333842e3121dSAnssi Hannula 		41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
333942e3121dSAnssi Hannula 	);
334042e3121dSAnssi Hannula 
3341eb1a74b7SAnssi Hannula 	if (cval->min == 0 && cval->max == 50) {
3342eb1a74b7SAnssi Hannula 		usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n");
334342e3121dSAnssi Hannula 		kctl->tlv.p = scale;
334442e3121dSAnssi Hannula 		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
334542e3121dSAnssi Hannula 		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
3346eb1a74b7SAnssi Hannula 
3347eb1a74b7SAnssi Hannula 	} else if (cval->min == 0 && cval->max <= 1000) {
3348eb1a74b7SAnssi Hannula 		/* Some other clearly broken DragonFly variant.
3349eb1a74b7SAnssi Hannula 		 * At least a 0..53 variant (hw v1.0) exists.
3350eb1a74b7SAnssi Hannula 		 */
3351eb1a74b7SAnssi Hannula 		usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device");
3352eb1a74b7SAnssi Hannula 		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
3353eb1a74b7SAnssi Hannula 	}
335442e3121dSAnssi Hannula }
335542e3121dSAnssi Hannula 
335642e3121dSAnssi Hannula void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
335742e3121dSAnssi Hannula 				  struct usb_mixer_elem_info *cval, int unitid,
335842e3121dSAnssi Hannula 				  struct snd_kcontrol *kctl)
335942e3121dSAnssi Hannula {
336042e3121dSAnssi Hannula 	switch (mixer->chip->usb_id) {
336142e3121dSAnssi Hannula 	case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
3362eb1a74b7SAnssi Hannula 		if (unitid == 7 && cval->control == UAC_FU_VOLUME)
3363eb1a74b7SAnssi Hannula 			snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
336442e3121dSAnssi Hannula 		break;
33650f174b35STakashi Iwai 	/* lowest playback value is muted on C-Media devices */
33660f174b35STakashi Iwai 	case USB_ID(0x0d8c, 0x000c):
33670f174b35STakashi Iwai 	case USB_ID(0x0d8c, 0x0014):
33680f174b35STakashi Iwai 		if (strstr(kctl->id.name, "Playback"))
33690f174b35STakashi Iwai 			cval->min_mute = 1;
33700f174b35STakashi Iwai 		break;
337142e3121dSAnssi Hannula 	}
337242e3121dSAnssi Hannula }
337342e3121dSAnssi Hannula 
3374