xref: /openbmc/linux/sound/usb/mixer_quirks.c (revision 771ad4df)
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>
274b8ea38fSJan Schär #include <sound/hda_verbs.h>
287b1eda22SDaniel Mack #include <sound/hwdep.h>
297b1eda22SDaniel Mack #include <sound/info.h>
3042e3121dSAnssi Hannula #include <sound/tlv.h>
317b1eda22SDaniel Mack 
327b1eda22SDaniel Mack #include "usbaudio.h"
33f0b5e634SDaniel Mack #include "mixer.h"
347b1eda22SDaniel Mack #include "mixer_quirks.h"
3576b188c4SChris J Arges #include "mixer_scarlett.h"
36771ad4dfSGeoffrey D. Bennett #include "mixer_scarlett2.h"
37d2bb390aSDetlef Urban #include "mixer_us16x08.h"
388dc5efe3SNick Kossifidis #include "mixer_s1810c.h"
397b1eda22SDaniel Mack #include "helper.h"
407b1eda22SDaniel Mack 
41b71dad18SMark Hills struct std_mono_table {
42b71dad18SMark Hills 	unsigned int unitid, control, cmask;
43b71dad18SMark Hills 	int val_type;
44b71dad18SMark Hills 	const char *name;
45b71dad18SMark Hills 	snd_kcontrol_tlv_rw_t *tlv_callback;
46b71dad18SMark Hills };
47b71dad18SMark Hills 
488a4d1d39SFelix Homann /* This function allows for the creation of standard UAC controls.
498a4d1d39SFelix Homann  * See the quirks for M-Audio FTUs or Ebox-44.
508a4d1d39SFelix Homann  * If you don't want to set a TLV callback pass NULL.
518a4d1d39SFelix Homann  *
528a4d1d39SFelix Homann  * Since there doesn't seem to be a devices that needs a multichannel
538a4d1d39SFelix Homann  * version, we keep it mono for simplicity.
548a4d1d39SFelix Homann  */
snd_create_std_mono_ctl_offset(struct usb_mixer_interface * mixer,unsigned int unitid,unsigned int control,unsigned int cmask,int val_type,unsigned int idx_off,const char * name,snd_kcontrol_tlv_rw_t * tlv_callback)559f814105SEldad Zack static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
568a4d1d39SFelix Homann 				unsigned int unitid,
578a4d1d39SFelix Homann 				unsigned int control,
588a4d1d39SFelix Homann 				unsigned int cmask,
598a4d1d39SFelix Homann 				int val_type,
609f814105SEldad Zack 				unsigned int idx_off,
618a4d1d39SFelix Homann 				const char *name,
628a4d1d39SFelix Homann 				snd_kcontrol_tlv_rw_t *tlv_callback)
638a4d1d39SFelix Homann {
648a4d1d39SFelix Homann 	struct usb_mixer_elem_info *cval;
658a4d1d39SFelix Homann 	struct snd_kcontrol *kctl;
668a4d1d39SFelix Homann 
678a4d1d39SFelix Homann 	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
688a4d1d39SFelix Homann 	if (!cval)
698a4d1d39SFelix Homann 		return -ENOMEM;
708a4d1d39SFelix Homann 
713360b84bSTakashi Iwai 	snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
728a4d1d39SFelix Homann 	cval->val_type = val_type;
738a4d1d39SFelix Homann 	cval->channels = 1;
748a4d1d39SFelix Homann 	cval->control = control;
758a4d1d39SFelix Homann 	cval->cmask = cmask;
769f814105SEldad Zack 	cval->idx_off = idx_off;
778a4d1d39SFelix Homann 
787df4a691SMark Hills 	/* get_min_max() is called only for integer volumes later,
797df4a691SMark Hills 	 * so provide a short-cut for booleans */
808a4d1d39SFelix Homann 	cval->min = 0;
818a4d1d39SFelix Homann 	cval->max = 1;
828a4d1d39SFelix Homann 	cval->res = 0;
838a4d1d39SFelix Homann 	cval->dBmin = 0;
848a4d1d39SFelix Homann 	cval->dBmax = 0;
858a4d1d39SFelix Homann 
868a4d1d39SFelix Homann 	/* Create control */
878a4d1d39SFelix Homann 	kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
888a4d1d39SFelix Homann 	if (!kctl) {
898a4d1d39SFelix Homann 		kfree(cval);
908a4d1d39SFelix Homann 		return -ENOMEM;
918a4d1d39SFelix Homann 	}
928a4d1d39SFelix Homann 
938a4d1d39SFelix Homann 	/* Set name */
948a4d1d39SFelix Homann 	snprintf(kctl->id.name, sizeof(kctl->id.name), name);
95eef90451SChris J Arges 	kctl->private_free = snd_usb_mixer_elem_free;
968a4d1d39SFelix Homann 
978a4d1d39SFelix Homann 	/* set TLV */
988a4d1d39SFelix Homann 	if (tlv_callback) {
998a4d1d39SFelix Homann 		kctl->tlv.c = tlv_callback;
1008a4d1d39SFelix Homann 		kctl->vd[0].access |=
1018a4d1d39SFelix Homann 			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
1028a4d1d39SFelix Homann 			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
1038a4d1d39SFelix Homann 	}
1048a4d1d39SFelix Homann 	/* Add control to mixer */
1053360b84bSTakashi Iwai 	return snd_usb_mixer_add_control(&cval->head, kctl);
1068a4d1d39SFelix Homann }
1078a4d1d39SFelix Homann 
snd_create_std_mono_ctl(struct usb_mixer_interface * mixer,unsigned int unitid,unsigned int control,unsigned int cmask,int val_type,const char * name,snd_kcontrol_tlv_rw_t * tlv_callback)1089f814105SEldad Zack static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
1099f814105SEldad Zack 				unsigned int unitid,
1109f814105SEldad Zack 				unsigned int control,
1119f814105SEldad Zack 				unsigned int cmask,
1129f814105SEldad Zack 				int val_type,
1139f814105SEldad Zack 				const char *name,
1149f814105SEldad Zack 				snd_kcontrol_tlv_rw_t *tlv_callback)
1159f814105SEldad Zack {
1169f814105SEldad Zack 	return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask,
1179f814105SEldad Zack 		val_type, 0 /* Offset */, name, tlv_callback);
1189f814105SEldad Zack }
1199f814105SEldad Zack 
1207b1eda22SDaniel Mack /*
121b71dad18SMark Hills  * Create a set of standard UAC controls from a table
122b71dad18SMark Hills  */
snd_create_std_mono_table(struct usb_mixer_interface * mixer,const struct std_mono_table * t)123b71dad18SMark Hills static int snd_create_std_mono_table(struct usb_mixer_interface *mixer,
124a01df925STakashi Iwai 				     const struct std_mono_table *t)
125b71dad18SMark Hills {
126b71dad18SMark Hills 	int err;
127b71dad18SMark Hills 
128b71dad18SMark Hills 	while (t->name != NULL) {
129b71dad18SMark Hills 		err = snd_create_std_mono_ctl(mixer, t->unitid, t->control,
130b71dad18SMark Hills 				t->cmask, t->val_type, t->name, t->tlv_callback);
131b71dad18SMark Hills 		if (err < 0)
132b71dad18SMark Hills 			return err;
133b71dad18SMark Hills 		t++;
134b71dad18SMark Hills 	}
135b71dad18SMark Hills 
136b71dad18SMark Hills 	return 0;
137b71dad18SMark Hills }
138b71dad18SMark Hills 
add_single_ctl_with_resume(struct usb_mixer_interface * mixer,int id,usb_mixer_elem_resume_func_t resume,const struct snd_kcontrol_new * knew,struct usb_mixer_elem_list ** listp)1399cf3689bSTakashi Iwai static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer,
1409cf3689bSTakashi Iwai 				      int id,
1419cf3689bSTakashi Iwai 				      usb_mixer_elem_resume_func_t resume,
1429cf3689bSTakashi Iwai 				      const struct snd_kcontrol_new *knew,
1439cf3689bSTakashi Iwai 				      struct usb_mixer_elem_list **listp)
1449cf3689bSTakashi Iwai {
1459cf3689bSTakashi Iwai 	struct usb_mixer_elem_list *list;
1469cf3689bSTakashi Iwai 	struct snd_kcontrol *kctl;
1479cf3689bSTakashi Iwai 
1489cf3689bSTakashi Iwai 	list = kzalloc(sizeof(*list), GFP_KERNEL);
1499cf3689bSTakashi Iwai 	if (!list)
1509cf3689bSTakashi Iwai 		return -ENOMEM;
1519cf3689bSTakashi Iwai 	if (listp)
1529cf3689bSTakashi Iwai 		*listp = list;
1539cf3689bSTakashi Iwai 	list->mixer = mixer;
1549cf3689bSTakashi Iwai 	list->id = id;
1557b9cf903STakashi Iwai 	list->resume = resume;
1569cf3689bSTakashi Iwai 	kctl = snd_ctl_new1(knew, list);
1579cf3689bSTakashi Iwai 	if (!kctl) {
1589cf3689bSTakashi Iwai 		kfree(list);
1599cf3689bSTakashi Iwai 		return -ENOMEM;
1609cf3689bSTakashi Iwai 	}
1619cf3689bSTakashi Iwai 	kctl->private_free = snd_usb_mixer_elem_free;
162220345e9STakashi Iwai 	/* don't use snd_usb_mixer_add_control() here, this is a special list element */
163220345e9STakashi Iwai 	return snd_usb_mixer_add_list(list, kctl, false);
1649cf3689bSTakashi Iwai }
1659cf3689bSTakashi Iwai 
166b71dad18SMark Hills /*
1677b1eda22SDaniel Mack  * Sound Blaster remote control configuration
1687b1eda22SDaniel Mack  *
1697b1eda22SDaniel Mack  * format of remote control data:
1707b1eda22SDaniel Mack  * Extigy:       xx 00
1717b1eda22SDaniel Mack  * Audigy 2 NX:  06 80 xx 00 00 00
1727b1eda22SDaniel Mack  * Live! 24-bit: 06 80 xx yy 22 83
1737b1eda22SDaniel Mack  */
1747b1eda22SDaniel Mack static const struct rc_config {
1757b1eda22SDaniel Mack 	u32 usb_id;
1767b1eda22SDaniel Mack 	u8  offset;
1777b1eda22SDaniel Mack 	u8  length;
1787b1eda22SDaniel Mack 	u8  packet_length;
1797b1eda22SDaniel Mack 	u8  min_packet_length; /* minimum accepted length of the URB result */
1807b1eda22SDaniel Mack 	u8  mute_mixer_id;
1817b1eda22SDaniel Mack 	u32 mute_code;
1827b1eda22SDaniel Mack } rc_configs[] = {
1837b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
1847b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
1857b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
186ca8dc34eSMandar Joshi 	{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 */
1877cdd8d73SMathieu Bouffard 	{ USB_ID(0x041e, 0x30df), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 Pro */
1883dc8523fSDmitry M. Fedin 	{ USB_ID(0x041e, 0x3237), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 Pro */
189fec90088SMirko Dietrich 	{ USB_ID(0x041e, 0x3263), 0, 1, 1, 1,  1,  0x000d }, /* Usb X-Fi S51 Pro */
1907b1eda22SDaniel Mack 	{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
1917b1eda22SDaniel Mack };
1927b1eda22SDaniel Mack 
snd_usb_soundblaster_remote_complete(struct urb * urb)1937b1eda22SDaniel Mack static void snd_usb_soundblaster_remote_complete(struct urb *urb)
1947b1eda22SDaniel Mack {
1957b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = urb->context;
1967b1eda22SDaniel Mack 	const struct rc_config *rc = mixer->rc_cfg;
1977b1eda22SDaniel Mack 	u32 code;
1987b1eda22SDaniel Mack 
1997b1eda22SDaniel Mack 	if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
2007b1eda22SDaniel Mack 		return;
2017b1eda22SDaniel Mack 
2027b1eda22SDaniel Mack 	code = mixer->rc_buffer[rc->offset];
2037b1eda22SDaniel Mack 	if (rc->length == 2)
2047b1eda22SDaniel Mack 		code |= mixer->rc_buffer[rc->offset + 1] << 8;
2057b1eda22SDaniel Mack 
2067b1eda22SDaniel Mack 	/* the Mute button actually changes the mixer control */
2077b1eda22SDaniel Mack 	if (code == rc->mute_code)
2087b1eda22SDaniel Mack 		snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
2097b1eda22SDaniel Mack 	mixer->rc_code = code;
2107b1eda22SDaniel Mack 	wmb();
2117b1eda22SDaniel Mack 	wake_up(&mixer->rc_waitq);
2127b1eda22SDaniel Mack }
2137b1eda22SDaniel Mack 
snd_usb_sbrc_hwdep_read(struct snd_hwdep * hw,char __user * buf,long count,loff_t * offset)2147b1eda22SDaniel Mack static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
2157b1eda22SDaniel Mack 				     long count, loff_t *offset)
2167b1eda22SDaniel Mack {
2177b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = hw->private_data;
2187b1eda22SDaniel Mack 	int err;
2197b1eda22SDaniel Mack 	u32 rc_code;
2207b1eda22SDaniel Mack 
2217b1eda22SDaniel Mack 	if (count != 1 && count != 4)
2227b1eda22SDaniel Mack 		return -EINVAL;
2237b1eda22SDaniel Mack 	err = wait_event_interruptible(mixer->rc_waitq,
2247b1eda22SDaniel Mack 				       (rc_code = xchg(&mixer->rc_code, 0)) != 0);
2257b1eda22SDaniel Mack 	if (err == 0) {
2267b1eda22SDaniel Mack 		if (count == 1)
2277b1eda22SDaniel Mack 			err = put_user(rc_code, buf);
2287b1eda22SDaniel Mack 		else
2297b1eda22SDaniel Mack 			err = put_user(rc_code, (u32 __user *)buf);
2307b1eda22SDaniel Mack 	}
2317b1eda22SDaniel Mack 	return err < 0 ? err : count;
2327b1eda22SDaniel Mack }
2337b1eda22SDaniel Mack 
snd_usb_sbrc_hwdep_poll(struct snd_hwdep * hw,struct file * file,poll_table * wait)234680ef72aSAl Viro static __poll_t snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
2357b1eda22SDaniel Mack 					    poll_table *wait)
2367b1eda22SDaniel Mack {
2377b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = hw->private_data;
2387b1eda22SDaniel Mack 
2397b1eda22SDaniel Mack 	poll_wait(file, &mixer->rc_waitq, wait);
240a9a08845SLinus Torvalds 	return mixer->rc_code ? EPOLLIN | EPOLLRDNORM : 0;
2417b1eda22SDaniel Mack }
2427b1eda22SDaniel Mack 
snd_usb_soundblaster_remote_init(struct usb_mixer_interface * mixer)2437b1eda22SDaniel Mack static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
2447b1eda22SDaniel Mack {
2457b1eda22SDaniel Mack 	struct snd_hwdep *hwdep;
2467b1eda22SDaniel Mack 	int err, len, i;
2477b1eda22SDaniel Mack 
2487b1eda22SDaniel Mack 	for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
2497b1eda22SDaniel Mack 		if (rc_configs[i].usb_id == mixer->chip->usb_id)
2507b1eda22SDaniel Mack 			break;
2517b1eda22SDaniel Mack 	if (i >= ARRAY_SIZE(rc_configs))
2527b1eda22SDaniel Mack 		return 0;
2537b1eda22SDaniel Mack 	mixer->rc_cfg = &rc_configs[i];
2547b1eda22SDaniel Mack 
2557b1eda22SDaniel Mack 	len = mixer->rc_cfg->packet_length;
2567b1eda22SDaniel Mack 
2577b1eda22SDaniel Mack 	init_waitqueue_head(&mixer->rc_waitq);
2587b1eda22SDaniel Mack 	err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
2597b1eda22SDaniel Mack 	if (err < 0)
2607b1eda22SDaniel Mack 		return err;
2617b1eda22SDaniel Mack 	snprintf(hwdep->name, sizeof(hwdep->name),
2627b1eda22SDaniel Mack 		 "%s remote control", mixer->chip->card->shortname);
2637b1eda22SDaniel Mack 	hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
2647b1eda22SDaniel Mack 	hwdep->private_data = mixer;
2657b1eda22SDaniel Mack 	hwdep->ops.read = snd_usb_sbrc_hwdep_read;
2667b1eda22SDaniel Mack 	hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
2677b1eda22SDaniel Mack 	hwdep->exclusive = 1;
2687b1eda22SDaniel Mack 
2697b1eda22SDaniel Mack 	mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
2707b1eda22SDaniel Mack 	if (!mixer->rc_urb)
2717b1eda22SDaniel Mack 		return -ENOMEM;
2727b1eda22SDaniel Mack 	mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
2737b1eda22SDaniel Mack 	if (!mixer->rc_setup_packet) {
2747b1eda22SDaniel Mack 		usb_free_urb(mixer->rc_urb);
2757b1eda22SDaniel Mack 		mixer->rc_urb = NULL;
2767b1eda22SDaniel Mack 		return -ENOMEM;
2777b1eda22SDaniel Mack 	}
2787b1eda22SDaniel Mack 	mixer->rc_setup_packet->bRequestType =
2797b1eda22SDaniel Mack 		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
2807b1eda22SDaniel Mack 	mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
2817b1eda22SDaniel Mack 	mixer->rc_setup_packet->wValue = cpu_to_le16(0);
2827b1eda22SDaniel Mack 	mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
2837b1eda22SDaniel Mack 	mixer->rc_setup_packet->wLength = cpu_to_le16(len);
2847b1eda22SDaniel Mack 	usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
2857b1eda22SDaniel Mack 			     usb_rcvctrlpipe(mixer->chip->dev, 0),
2867b1eda22SDaniel Mack 			     (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
2877b1eda22SDaniel Mack 			     snd_usb_soundblaster_remote_complete, mixer);
2887b1eda22SDaniel Mack 	return 0;
2897b1eda22SDaniel Mack }
2907b1eda22SDaniel Mack 
2917b1eda22SDaniel Mack #define snd_audigy2nx_led_info		snd_ctl_boolean_mono_info
2927b1eda22SDaniel Mack 
snd_audigy2nx_led_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2937b1eda22SDaniel Mack static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
2947b1eda22SDaniel Mack {
2959cf3689bSTakashi Iwai 	ucontrol->value.integer.value[0] = kcontrol->private_value >> 8;
2967b1eda22SDaniel Mack 	return 0;
2977b1eda22SDaniel Mack }
2987b1eda22SDaniel Mack 
snd_audigy2nx_led_update(struct usb_mixer_interface * mixer,int value,int index)2999cf3689bSTakashi Iwai static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
3009cf3689bSTakashi Iwai 				    int value, int index)
3017b1eda22SDaniel Mack {
3029cf3689bSTakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
3039cf3689bSTakashi Iwai 	int err;
3047b1eda22SDaniel Mack 
30547ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
30647ab1545STakashi Iwai 	if (err < 0)
30747ab1545STakashi Iwai 		return err;
30847ab1545STakashi Iwai 
3099cf3689bSTakashi Iwai 	if (chip->usb_id == USB_ID(0x041e, 0x3042))
3109cf3689bSTakashi Iwai 		err = snd_usb_ctl_msg(chip->dev,
3119cf3689bSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x24,
312ca8dc34eSMandar Joshi 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
31317d900c4SClemens Ladisch 			      !value, 0, NULL, 0);
3147cdd8d73SMathieu Bouffard 	/* USB X-Fi S51 Pro */
3159cf3689bSTakashi Iwai 	if (chip->usb_id == USB_ID(0x041e, 0x30df))
3169cf3689bSTakashi Iwai 		err = snd_usb_ctl_msg(chip->dev,
3179cf3689bSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x24,
3187cdd8d73SMathieu Bouffard 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
31917d900c4SClemens Ladisch 			      !value, 0, NULL, 0);
320ca8dc34eSMandar Joshi 	else
3219cf3689bSTakashi Iwai 		err = snd_usb_ctl_msg(chip->dev,
3229cf3689bSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x24,
3237b1eda22SDaniel Mack 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
32417d900c4SClemens Ladisch 			      value, index + 2, NULL, 0);
32547ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
3267b1eda22SDaniel Mack 	return err;
3277b1eda22SDaniel Mack }
3287b1eda22SDaniel Mack 
snd_audigy2nx_led_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3299cf3689bSTakashi Iwai static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
3309cf3689bSTakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
3317b1eda22SDaniel Mack {
3329cf3689bSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
3339cf3689bSTakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
3349cf3689bSTakashi Iwai 	int index = kcontrol->private_value & 0xff;
335e87359efSDan Carpenter 	unsigned int value = ucontrol->value.integer.value[0];
3369cf3689bSTakashi Iwai 	int old_value = kcontrol->private_value >> 8;
3379cf3689bSTakashi Iwai 	int err;
3389cf3689bSTakashi Iwai 
3399cf3689bSTakashi Iwai 	if (value > 1)
3409cf3689bSTakashi Iwai 		return -EINVAL;
3419cf3689bSTakashi Iwai 	if (value == old_value)
3429cf3689bSTakashi Iwai 		return 0;
3439cf3689bSTakashi Iwai 	kcontrol->private_value = (value << 8) | index;
3449cf3689bSTakashi Iwai 	err = snd_audigy2nx_led_update(mixer, value, index);
3459cf3689bSTakashi Iwai 	return err < 0 ? err : 1;
3469cf3689bSTakashi Iwai }
3479cf3689bSTakashi Iwai 
snd_audigy2nx_led_resume(struct usb_mixer_elem_list * list)3489cf3689bSTakashi Iwai static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
3499cf3689bSTakashi Iwai {
3509cf3689bSTakashi Iwai 	int priv_value = list->kctl->private_value;
3519cf3689bSTakashi Iwai 
3529cf3689bSTakashi Iwai 	return snd_audigy2nx_led_update(list->mixer, priv_value >> 8,
3539cf3689bSTakashi Iwai 					priv_value & 0xff);
3549cf3689bSTakashi Iwai }
3559cf3689bSTakashi Iwai 
3569cf3689bSTakashi Iwai /* name and private_value are set dynamically */
357905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_audigy2nx_control = {
3587b1eda22SDaniel Mack 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3597b1eda22SDaniel Mack 	.info = snd_audigy2nx_led_info,
3607b1eda22SDaniel Mack 	.get = snd_audigy2nx_led_get,
3617b1eda22SDaniel Mack 	.put = snd_audigy2nx_led_put,
3629cf3689bSTakashi Iwai };
3639cf3689bSTakashi Iwai 
3649cf3689bSTakashi Iwai static const char * const snd_audigy2nx_led_names[] = {
3659cf3689bSTakashi Iwai 	"CMSS LED Switch",
3669cf3689bSTakashi Iwai 	"Power LED Switch",
3679cf3689bSTakashi Iwai 	"Dolby Digital LED Switch",
3687b1eda22SDaniel Mack };
3697b1eda22SDaniel Mack 
snd_audigy2nx_controls_create(struct usb_mixer_interface * mixer)3707b1eda22SDaniel Mack static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
3717b1eda22SDaniel Mack {
3727b1eda22SDaniel Mack 	int i, err;
3737b1eda22SDaniel Mack 
3749cf3689bSTakashi Iwai 	for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) {
3759cf3689bSTakashi Iwai 		struct snd_kcontrol_new knew;
3769cf3689bSTakashi Iwai 
377ca8dc34eSMandar Joshi 		/* USB X-Fi S51 doesn't have a CMSS LED */
378ca8dc34eSMandar Joshi 		if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
379ca8dc34eSMandar Joshi 			continue;
3807cdd8d73SMathieu Bouffard 		/* USB X-Fi S51 Pro doesn't have one either */
3817cdd8d73SMathieu Bouffard 		if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
3827cdd8d73SMathieu Bouffard 			continue;
3837b1eda22SDaniel Mack 		if (i > 1 && /* Live24ext has 2 LEDs only */
3847b1eda22SDaniel Mack 			(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
385ca8dc34eSMandar Joshi 			 mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
3867cdd8d73SMathieu Bouffard 			 mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
3877b1eda22SDaniel Mack 			 mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
3887b1eda22SDaniel Mack 			break;
3899cf3689bSTakashi Iwai 
3909cf3689bSTakashi Iwai 		knew = snd_audigy2nx_control;
3919cf3689bSTakashi Iwai 		knew.name = snd_audigy2nx_led_names[i];
3929cf3689bSTakashi Iwai 		knew.private_value = (1 << 8) | i; /* LED on as default */
3939cf3689bSTakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
3949cf3689bSTakashi Iwai 						 snd_audigy2nx_led_resume,
3959cf3689bSTakashi Iwai 						 &knew, NULL);
3967b1eda22SDaniel Mack 		if (err < 0)
3977b1eda22SDaniel Mack 			return err;
3987b1eda22SDaniel Mack 	}
3997b1eda22SDaniel Mack 	return 0;
4007b1eda22SDaniel Mack }
4017b1eda22SDaniel Mack 
snd_audigy2nx_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)4027b1eda22SDaniel Mack static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
4037b1eda22SDaniel Mack 				    struct snd_info_buffer *buffer)
4047b1eda22SDaniel Mack {
4057b1eda22SDaniel Mack 	static const struct sb_jack {
4067b1eda22SDaniel Mack 		int unitid;
4077b1eda22SDaniel Mack 		const char *name;
4087b1eda22SDaniel Mack 	}  jacks_audigy2nx[] = {
4097b1eda22SDaniel Mack 		{4,  "dig in "},
4107b1eda22SDaniel Mack 		{7,  "line in"},
4117b1eda22SDaniel Mack 		{19, "spk out"},
4127b1eda22SDaniel Mack 		{20, "hph out"},
4137b1eda22SDaniel Mack 		{-1, NULL}
4147b1eda22SDaniel Mack 	}, jacks_live24ext[] = {
4157b1eda22SDaniel Mack 		{4,  "line in"}, /* &1=Line, &2=Mic*/
4167b1eda22SDaniel Mack 		{3,  "hph out"}, /* headphones */
4177b1eda22SDaniel Mack 		{0,  "RC     "}, /* last command, 6 bytes see rc_config above */
4187b1eda22SDaniel Mack 		{-1, NULL}
4197b1eda22SDaniel Mack 	};
4207b1eda22SDaniel Mack 	const struct sb_jack *jacks;
4217b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer = entry->private_data;
4227b1eda22SDaniel Mack 	int i, err;
4237b1eda22SDaniel Mack 	u8 buf[3];
4247b1eda22SDaniel Mack 
4257b1eda22SDaniel Mack 	snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
4267b1eda22SDaniel Mack 	if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
4277b1eda22SDaniel Mack 		jacks = jacks_audigy2nx;
4287b1eda22SDaniel Mack 	else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
4297b1eda22SDaniel Mack 		 mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
4307b1eda22SDaniel Mack 		jacks = jacks_live24ext;
4317b1eda22SDaniel Mack 	else
4327b1eda22SDaniel Mack 		return;
4337b1eda22SDaniel Mack 
4347b1eda22SDaniel Mack 	for (i = 0; jacks[i].name; ++i) {
4357b1eda22SDaniel Mack 		snd_iprintf(buffer, "%s: ", jacks[i].name);
43647ab1545STakashi Iwai 		err = snd_usb_lock_shutdown(mixer->chip);
43747ab1545STakashi Iwai 		if (err < 0)
43847ab1545STakashi Iwai 			return;
4397b1eda22SDaniel Mack 		err = snd_usb_ctl_msg(mixer->chip->dev,
4407b1eda22SDaniel Mack 				      usb_rcvctrlpipe(mixer->chip->dev, 0),
4417b1eda22SDaniel Mack 				      UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
4427b1eda22SDaniel Mack 				      USB_RECIP_INTERFACE, 0,
44317d900c4SClemens Ladisch 				      jacks[i].unitid << 8, buf, 3);
44447ab1545STakashi Iwai 		snd_usb_unlock_shutdown(mixer->chip);
4457b1eda22SDaniel Mack 		if (err == 3 && (buf[0] == 3 || buf[0] == 6))
4467b1eda22SDaniel Mack 			snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
4477b1eda22SDaniel Mack 		else
4487b1eda22SDaniel Mack 			snd_iprintf(buffer, "?\n");
4497b1eda22SDaniel Mack 	}
4507b1eda22SDaniel Mack }
4517b1eda22SDaniel Mack 
45244832a71SVasily Khoruzhick /* EMU0204 */
snd_emu0204_ch_switch_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)45344832a71SVasily Khoruzhick static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol,
45444832a71SVasily Khoruzhick 				      struct snd_ctl_elem_info *uinfo)
45544832a71SVasily Khoruzhick {
4567bbd03e0STakashi Iwai 	static const char * const texts[2] = {"1/2", "3/4"};
45744832a71SVasily Khoruzhick 
4587bbd03e0STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
45944832a71SVasily Khoruzhick }
46044832a71SVasily Khoruzhick 
snd_emu0204_ch_switch_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)46144832a71SVasily Khoruzhick static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
46244832a71SVasily Khoruzhick 				     struct snd_ctl_elem_value *ucontrol)
46344832a71SVasily Khoruzhick {
46444832a71SVasily Khoruzhick 	ucontrol->value.enumerated.item[0] = kcontrol->private_value;
46544832a71SVasily Khoruzhick 	return 0;
46644832a71SVasily Khoruzhick }
46744832a71SVasily Khoruzhick 
snd_emu0204_ch_switch_update(struct usb_mixer_interface * mixer,int value)4685f503ee9STakashi Iwai static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
4695f503ee9STakashi Iwai 					int value)
47044832a71SVasily Khoruzhick {
4715f503ee9STakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
4725f503ee9STakashi Iwai 	int err;
47344832a71SVasily Khoruzhick 	unsigned char buf[2];
47444832a71SVasily Khoruzhick 
47547ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
47647ab1545STakashi Iwai 	if (err < 0)
47747ab1545STakashi Iwai 		return err;
4785f503ee9STakashi Iwai 
4795f503ee9STakashi Iwai 	buf[0] = 0x01;
4805f503ee9STakashi Iwai 	buf[1] = value ? 0x02 : 0x01;
4815f503ee9STakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
4825f503ee9STakashi Iwai 		      usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
48344832a71SVasily Khoruzhick 		      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
48444832a71SVasily Khoruzhick 		      0x0400, 0x0e00, buf, 2);
48547ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
48644832a71SVasily Khoruzhick 	return err;
48744832a71SVasily Khoruzhick }
48844832a71SVasily Khoruzhick 
snd_emu0204_ch_switch_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)4895f503ee9STakashi Iwai static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
4905f503ee9STakashi Iwai 				     struct snd_ctl_elem_value *ucontrol)
49144832a71SVasily Khoruzhick {
4925f503ee9STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
4935f503ee9STakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
4945f503ee9STakashi Iwai 	unsigned int value = ucontrol->value.enumerated.item[0];
4955f503ee9STakashi Iwai 	int err;
4965f503ee9STakashi Iwai 
4975f503ee9STakashi Iwai 	if (value > 1)
4985f503ee9STakashi Iwai 		return -EINVAL;
4995f503ee9STakashi Iwai 
5005f503ee9STakashi Iwai 	if (value == kcontrol->private_value)
5015f503ee9STakashi Iwai 		return 0;
5025f503ee9STakashi Iwai 
5035f503ee9STakashi Iwai 	kcontrol->private_value = value;
5045f503ee9STakashi Iwai 	err = snd_emu0204_ch_switch_update(mixer, value);
5055f503ee9STakashi Iwai 	return err < 0 ? err : 1;
5065f503ee9STakashi Iwai }
5075f503ee9STakashi Iwai 
snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list * list)5085f503ee9STakashi Iwai static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
5095f503ee9STakashi Iwai {
5105f503ee9STakashi Iwai 	return snd_emu0204_ch_switch_update(list->mixer,
5115f503ee9STakashi Iwai 					    list->kctl->private_value);
5125f503ee9STakashi Iwai }
5135f503ee9STakashi Iwai 
514195727e8STakashi Iwai static const struct snd_kcontrol_new snd_emu0204_control = {
51544832a71SVasily Khoruzhick 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
51644832a71SVasily Khoruzhick 	.name = "Front Jack Channels",
51744832a71SVasily Khoruzhick 	.info = snd_emu0204_ch_switch_info,
51844832a71SVasily Khoruzhick 	.get = snd_emu0204_ch_switch_get,
51944832a71SVasily Khoruzhick 	.put = snd_emu0204_ch_switch_put,
52044832a71SVasily Khoruzhick 	.private_value = 0,
52144832a71SVasily Khoruzhick };
52244832a71SVasily Khoruzhick 
snd_emu0204_controls_create(struct usb_mixer_interface * mixer)52344832a71SVasily Khoruzhick static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
52444832a71SVasily Khoruzhick {
5255f503ee9STakashi Iwai 	return add_single_ctl_with_resume(mixer, 0,
5265f503ee9STakashi Iwai 					  snd_emu0204_ch_switch_resume,
5275f503ee9STakashi Iwai 					  &snd_emu0204_control, NULL);
52844832a71SVasily Khoruzhick }
52944832a71SVasily Khoruzhick 
5301d31affbSDenis Washington /* ASUS Xonar U1 / U3 controls */
5311d31affbSDenis Washington 
snd_xonar_u1_switch_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)5327b1eda22SDaniel Mack static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
5337b1eda22SDaniel Mack 				   struct snd_ctl_elem_value *ucontrol)
5347b1eda22SDaniel Mack {
5352bfb14c3STakashi Iwai 	ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02);
5367b1eda22SDaniel Mack 	return 0;
5377b1eda22SDaniel Mack }
5387b1eda22SDaniel Mack 
snd_xonar_u1_switch_update(struct usb_mixer_interface * mixer,unsigned char status)5392bfb14c3STakashi Iwai static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer,
5402bfb14c3STakashi Iwai 				      unsigned char status)
5412bfb14c3STakashi Iwai {
5422bfb14c3STakashi Iwai 	struct snd_usb_audio *chip = mixer->chip;
5432bfb14c3STakashi Iwai 	int err;
5442bfb14c3STakashi Iwai 
54547ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
54647ab1545STakashi Iwai 	if (err < 0)
54747ab1545STakashi Iwai 		return err;
5482bfb14c3STakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
5492bfb14c3STakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0), 0x08,
5502bfb14c3STakashi Iwai 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
5512bfb14c3STakashi Iwai 			      50, 0, &status, 1);
55247ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
5532bfb14c3STakashi Iwai 	return err;
5542bfb14c3STakashi Iwai }
5552bfb14c3STakashi Iwai 
snd_xonar_u1_switch_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)5567b1eda22SDaniel Mack static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
5577b1eda22SDaniel Mack 				   struct snd_ctl_elem_value *ucontrol)
5587b1eda22SDaniel Mack {
5592bfb14c3STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
5607b1eda22SDaniel Mack 	u8 old_status, new_status;
5612bfb14c3STakashi Iwai 	int err;
5627b1eda22SDaniel Mack 
5632bfb14c3STakashi Iwai 	old_status = kcontrol->private_value;
5647b1eda22SDaniel Mack 	if (ucontrol->value.integer.value[0])
5657b1eda22SDaniel Mack 		new_status = old_status | 0x02;
5667b1eda22SDaniel Mack 	else
5677b1eda22SDaniel Mack 		new_status = old_status & ~0x02;
5682bfb14c3STakashi Iwai 	if (new_status == old_status)
5692bfb14c3STakashi Iwai 		return 0;
5702bfb14c3STakashi Iwai 
5712bfb14c3STakashi Iwai 	kcontrol->private_value = new_status;
5722bfb14c3STakashi Iwai 	err = snd_xonar_u1_switch_update(list->mixer, new_status);
5732bfb14c3STakashi Iwai 	return err < 0 ? err : 1;
5742bfb14c3STakashi Iwai }
5752bfb14c3STakashi Iwai 
snd_xonar_u1_switch_resume(struct usb_mixer_elem_list * list)5762bfb14c3STakashi Iwai static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
5772bfb14c3STakashi Iwai {
5782bfb14c3STakashi Iwai 	return snd_xonar_u1_switch_update(list->mixer,
5792bfb14c3STakashi Iwai 					  list->kctl->private_value);
5807b1eda22SDaniel Mack }
5817b1eda22SDaniel Mack 
582195727e8STakashi Iwai static const struct snd_kcontrol_new snd_xonar_u1_output_switch = {
5837b1eda22SDaniel Mack 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5847b1eda22SDaniel Mack 	.name = "Digital Playback Switch",
5857b1eda22SDaniel Mack 	.info = snd_ctl_boolean_mono_info,
5867b1eda22SDaniel Mack 	.get = snd_xonar_u1_switch_get,
5877b1eda22SDaniel Mack 	.put = snd_xonar_u1_switch_put,
5882bfb14c3STakashi Iwai 	.private_value = 0x05,
5897b1eda22SDaniel Mack };
5907b1eda22SDaniel Mack 
snd_xonar_u1_controls_create(struct usb_mixer_interface * mixer)5917b1eda22SDaniel Mack static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
5927b1eda22SDaniel Mack {
5932bfb14c3STakashi Iwai 	return add_single_ctl_with_resume(mixer, 0,
5942bfb14c3STakashi Iwai 					  snd_xonar_u1_switch_resume,
5952bfb14c3STakashi Iwai 					  &snd_xonar_u1_output_switch, NULL);
5967b1eda22SDaniel Mack }
5977b1eda22SDaniel Mack 
5987ac2246fSDamien Zammit /* Digidesign Mbox 1 helper functions */
599d497a82fSDamien Zammit 
snd_mbox1_is_spdif_synced(struct snd_usb_audio * chip)6007ac2246fSDamien Zammit static int snd_mbox1_is_spdif_synced(struct snd_usb_audio *chip)
601d497a82fSDamien Zammit {
602d497a82fSDamien Zammit 	unsigned char buff[3];
6037ac2246fSDamien Zammit 	int err;
6047ac2246fSDamien Zammit 	int is_spdif_synced;
605d497a82fSDamien Zammit 
6067ac2246fSDamien Zammit 	/* Read clock source */
6077ac2246fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
6087ac2246fSDamien Zammit 			      usb_rcvctrlpipe(chip->dev, 0), 0x81,
6097ac2246fSDamien Zammit 			      USB_DIR_IN |
6107ac2246fSDamien Zammit 			      USB_TYPE_CLASS |
6117ac2246fSDamien Zammit 			      USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
61247ab1545STakashi Iwai 	if (err < 0)
61347ab1545STakashi Iwai 		return err;
614d497a82fSDamien Zammit 
6157ac2246fSDamien Zammit 	/* spdif sync: buff is all zeroes */
6167ac2246fSDamien Zammit 	is_spdif_synced = !(buff[0] | buff[1] | buff[2]);
6177ac2246fSDamien Zammit 	return is_spdif_synced;
618d497a82fSDamien Zammit }
619d497a82fSDamien Zammit 
snd_mbox1_set_clk_source(struct snd_usb_audio * chip,int rate_or_zero)6207ac2246fSDamien Zammit static int snd_mbox1_set_clk_source(struct snd_usb_audio *chip, int rate_or_zero)
6217ac2246fSDamien Zammit {
6227ac2246fSDamien Zammit 	/* 2 possibilities:	Internal    -> expects sample rate
6237ac2246fSDamien Zammit 	 *			S/PDIF sync -> expects rate = 0
6247ac2246fSDamien Zammit 	 */
6257ac2246fSDamien Zammit 	unsigned char buff[3];
6267ac2246fSDamien Zammit 
6277ac2246fSDamien Zammit 	buff[0] = (rate_or_zero >>  0) & 0xff;
6287ac2246fSDamien Zammit 	buff[1] = (rate_or_zero >>  8) & 0xff;
6297ac2246fSDamien Zammit 	buff[2] = (rate_or_zero >> 16) & 0xff;
6307ac2246fSDamien Zammit 
6317ac2246fSDamien Zammit 	/* Set clock source */
6327ac2246fSDamien Zammit 	return snd_usb_ctl_msg(chip->dev,
633d497a82fSDamien Zammit 			       usb_sndctrlpipe(chip->dev, 0), 0x1,
634d497a82fSDamien Zammit 			       USB_TYPE_CLASS |
635d497a82fSDamien Zammit 			       USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
6367ac2246fSDamien Zammit }
6377ac2246fSDamien Zammit 
snd_mbox1_is_spdif_input(struct snd_usb_audio * chip)6387ac2246fSDamien Zammit static int snd_mbox1_is_spdif_input(struct snd_usb_audio *chip)
6397ac2246fSDamien Zammit {
6407ac2246fSDamien Zammit 	/* Hardware gives 2 possibilities:	ANALOG Source  -> 0x01
6417ac2246fSDamien Zammit 	 *					S/PDIF Source  -> 0x02
6427ac2246fSDamien Zammit 	 */
6437ac2246fSDamien Zammit 	int err;
6447ac2246fSDamien Zammit 	unsigned char source[1];
6457ac2246fSDamien Zammit 
6467ac2246fSDamien Zammit 	/* Read input source */
647d497a82fSDamien Zammit 	err = snd_usb_ctl_msg(chip->dev,
648d497a82fSDamien Zammit 			      usb_rcvctrlpipe(chip->dev, 0), 0x81,
649d497a82fSDamien Zammit 			      USB_DIR_IN |
650d497a82fSDamien Zammit 			      USB_TYPE_CLASS |
6517ac2246fSDamien Zammit 			      USB_RECIP_INTERFACE, 0x00, 0x500, source, 1);
652d497a82fSDamien Zammit 	if (err < 0)
6537ac2246fSDamien Zammit 		return err;
6547ac2246fSDamien Zammit 
6557ac2246fSDamien Zammit 	return (source[0] == 2);
6567ac2246fSDamien Zammit }
6577ac2246fSDamien Zammit 
snd_mbox1_set_input_source(struct snd_usb_audio * chip,int is_spdif)6587ac2246fSDamien Zammit static int snd_mbox1_set_input_source(struct snd_usb_audio *chip, int is_spdif)
6597ac2246fSDamien Zammit {
6607ac2246fSDamien Zammit 	/* NB: Setting the input source to S/PDIF resets the clock source to S/PDIF
6617ac2246fSDamien Zammit 	 * Hardware expects 2 possibilities:	ANALOG Source  -> 0x01
6627ac2246fSDamien Zammit 	 *					S/PDIF Source  -> 0x02
6637ac2246fSDamien Zammit 	 */
6647ac2246fSDamien Zammit 	unsigned char buff[1];
6657ac2246fSDamien Zammit 
6667ac2246fSDamien Zammit 	buff[0] = (is_spdif & 1) + 1;
6677ac2246fSDamien Zammit 
6687ac2246fSDamien Zammit 	/* Set input source */
6697ac2246fSDamien Zammit 	return snd_usb_ctl_msg(chip->dev,
6707ac2246fSDamien Zammit 			       usb_sndctrlpipe(chip->dev, 0), 0x1,
671d497a82fSDamien Zammit 			       USB_TYPE_CLASS |
6727ac2246fSDamien Zammit 			       USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
6737ac2246fSDamien Zammit }
6747ac2246fSDamien Zammit 
6757ac2246fSDamien Zammit /* Digidesign Mbox 1 clock source switch (internal/spdif) */
6767ac2246fSDamien Zammit 
snd_mbox1_clk_switch_get(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)6777ac2246fSDamien Zammit static int snd_mbox1_clk_switch_get(struct snd_kcontrol *kctl,
6787ac2246fSDamien Zammit 				    struct snd_ctl_elem_value *ucontrol)
6797ac2246fSDamien Zammit {
6807ac2246fSDamien Zammit 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
6817ac2246fSDamien Zammit 	struct snd_usb_audio *chip = list->mixer->chip;
6827ac2246fSDamien Zammit 	int err;
6837ac2246fSDamien Zammit 
6847ac2246fSDamien Zammit 	err = snd_usb_lock_shutdown(chip);
685d497a82fSDamien Zammit 	if (err < 0)
686d497a82fSDamien Zammit 		goto err;
687d497a82fSDamien Zammit 
6887ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
6897ac2246fSDamien Zammit 	if (err < 0)
6907ac2246fSDamien Zammit 		goto err;
6917ac2246fSDamien Zammit 
6927ac2246fSDamien Zammit 	kctl->private_value = err;
6937ac2246fSDamien Zammit 	err = 0;
6947ac2246fSDamien Zammit 	ucontrol->value.enumerated.item[0] = kctl->private_value;
695d497a82fSDamien Zammit err:
69647ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
69725a9a4f9STakashi Iwai 	return err;
69825a9a4f9STakashi Iwai }
69925a9a4f9STakashi Iwai 
snd_mbox1_clk_switch_update(struct usb_mixer_interface * mixer,int is_spdif_sync)7007ac2246fSDamien Zammit static int snd_mbox1_clk_switch_update(struct usb_mixer_interface *mixer, int is_spdif_sync)
7017ac2246fSDamien Zammit {
7027ac2246fSDamien Zammit 	struct snd_usb_audio *chip = mixer->chip;
7037ac2246fSDamien Zammit 	int err;
7047ac2246fSDamien Zammit 
7057ac2246fSDamien Zammit 	err = snd_usb_lock_shutdown(chip);
7067ac2246fSDamien Zammit 	if (err < 0)
7077ac2246fSDamien Zammit 		return err;
7087ac2246fSDamien Zammit 
7097ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_input(chip);
7107ac2246fSDamien Zammit 	if (err < 0)
7117ac2246fSDamien Zammit 		goto err;
7127ac2246fSDamien Zammit 
7137ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
7147ac2246fSDamien Zammit 	if (err < 0)
7157ac2246fSDamien Zammit 		goto err;
7167ac2246fSDamien Zammit 
7177ac2246fSDamien Zammit 	/* FIXME: hardcoded sample rate */
7187ac2246fSDamien Zammit 	err = snd_mbox1_set_clk_source(chip, is_spdif_sync ? 0 : 48000);
7197ac2246fSDamien Zammit 	if (err < 0)
7207ac2246fSDamien Zammit 		goto err;
7217ac2246fSDamien Zammit 
7227ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
7237ac2246fSDamien Zammit err:
7247ac2246fSDamien Zammit 	snd_usb_unlock_shutdown(chip);
7257ac2246fSDamien Zammit 	return err;
7267ac2246fSDamien Zammit }
7277ac2246fSDamien Zammit 
snd_mbox1_clk_switch_put(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)7287ac2246fSDamien Zammit static int snd_mbox1_clk_switch_put(struct snd_kcontrol *kctl,
72925a9a4f9STakashi Iwai 				    struct snd_ctl_elem_value *ucontrol)
73025a9a4f9STakashi Iwai {
73125a9a4f9STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
73225a9a4f9STakashi Iwai 	struct usb_mixer_interface *mixer = list->mixer;
73325a9a4f9STakashi Iwai 	int err;
73425a9a4f9STakashi Iwai 	bool cur_val, new_val;
73525a9a4f9STakashi Iwai 
73625a9a4f9STakashi Iwai 	cur_val = kctl->private_value;
73725a9a4f9STakashi Iwai 	new_val = ucontrol->value.enumerated.item[0];
73825a9a4f9STakashi Iwai 	if (cur_val == new_val)
73925a9a4f9STakashi Iwai 		return 0;
74025a9a4f9STakashi Iwai 
74125a9a4f9STakashi Iwai 	kctl->private_value = new_val;
7427ac2246fSDamien Zammit 	err = snd_mbox1_clk_switch_update(mixer, new_val);
743d497a82fSDamien Zammit 	return err < 0 ? err : 1;
744d497a82fSDamien Zammit }
745d497a82fSDamien Zammit 
snd_mbox1_clk_switch_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)7467ac2246fSDamien Zammit static int snd_mbox1_clk_switch_info(struct snd_kcontrol *kcontrol,
747d497a82fSDamien Zammit 				     struct snd_ctl_elem_info *uinfo)
748d497a82fSDamien Zammit {
749d497a82fSDamien Zammit 	static const char *const texts[2] = {
750d497a82fSDamien Zammit 		"Internal",
751d497a82fSDamien Zammit 		"S/PDIF"
752d497a82fSDamien Zammit 	};
753d497a82fSDamien Zammit 
754d497a82fSDamien Zammit 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
755d497a82fSDamien Zammit }
756d497a82fSDamien Zammit 
snd_mbox1_clk_switch_resume(struct usb_mixer_elem_list * list)7577ac2246fSDamien Zammit static int snd_mbox1_clk_switch_resume(struct usb_mixer_elem_list *list)
75825a9a4f9STakashi Iwai {
7597ac2246fSDamien Zammit 	return snd_mbox1_clk_switch_update(list->mixer, list->kctl->private_value);
76025a9a4f9STakashi Iwai }
76125a9a4f9STakashi Iwai 
7627ac2246fSDamien Zammit /* Digidesign Mbox 1 input source switch (analog/spdif) */
7637ac2246fSDamien Zammit 
snd_mbox1_src_switch_get(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)7647ac2246fSDamien Zammit static int snd_mbox1_src_switch_get(struct snd_kcontrol *kctl,
7657ac2246fSDamien Zammit 				    struct snd_ctl_elem_value *ucontrol)
7667ac2246fSDamien Zammit {
7677ac2246fSDamien Zammit 	ucontrol->value.enumerated.item[0] = kctl->private_value;
7687ac2246fSDamien Zammit 	return 0;
7697ac2246fSDamien Zammit }
7707ac2246fSDamien Zammit 
snd_mbox1_src_switch_update(struct usb_mixer_interface * mixer,int is_spdif_input)7717ac2246fSDamien Zammit static int snd_mbox1_src_switch_update(struct usb_mixer_interface *mixer, int is_spdif_input)
7727ac2246fSDamien Zammit {
7737ac2246fSDamien Zammit 	struct snd_usb_audio *chip = mixer->chip;
7747ac2246fSDamien Zammit 	int err;
7757ac2246fSDamien Zammit 
7767ac2246fSDamien Zammit 	err = snd_usb_lock_shutdown(chip);
7777ac2246fSDamien Zammit 	if (err < 0)
7787ac2246fSDamien Zammit 		return err;
7797ac2246fSDamien Zammit 
7807ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_input(chip);
7817ac2246fSDamien Zammit 	if (err < 0)
7827ac2246fSDamien Zammit 		goto err;
7837ac2246fSDamien Zammit 
7847ac2246fSDamien Zammit 	err = snd_mbox1_set_input_source(chip, is_spdif_input);
7857ac2246fSDamien Zammit 	if (err < 0)
7867ac2246fSDamien Zammit 		goto err;
7877ac2246fSDamien Zammit 
7887ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_input(chip);
7897ac2246fSDamien Zammit 	if (err < 0)
7907ac2246fSDamien Zammit 		goto err;
7917ac2246fSDamien Zammit 
7927ac2246fSDamien Zammit 	err = snd_mbox1_is_spdif_synced(chip);
7937ac2246fSDamien Zammit err:
7947ac2246fSDamien Zammit 	snd_usb_unlock_shutdown(chip);
7957ac2246fSDamien Zammit 	return err;
7967ac2246fSDamien Zammit }
7977ac2246fSDamien Zammit 
snd_mbox1_src_switch_put(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)7987ac2246fSDamien Zammit static int snd_mbox1_src_switch_put(struct snd_kcontrol *kctl,
7997ac2246fSDamien Zammit 				    struct snd_ctl_elem_value *ucontrol)
8007ac2246fSDamien Zammit {
8017ac2246fSDamien Zammit 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
8027ac2246fSDamien Zammit 	struct usb_mixer_interface *mixer = list->mixer;
8037ac2246fSDamien Zammit 	int err;
8047ac2246fSDamien Zammit 	bool cur_val, new_val;
8057ac2246fSDamien Zammit 
8067ac2246fSDamien Zammit 	cur_val = kctl->private_value;
8077ac2246fSDamien Zammit 	new_val = ucontrol->value.enumerated.item[0];
8087ac2246fSDamien Zammit 	if (cur_val == new_val)
8097ac2246fSDamien Zammit 		return 0;
8107ac2246fSDamien Zammit 
8117ac2246fSDamien Zammit 	kctl->private_value = new_val;
8127ac2246fSDamien Zammit 	err = snd_mbox1_src_switch_update(mixer, new_val);
8137ac2246fSDamien Zammit 	return err < 0 ? err : 1;
8147ac2246fSDamien Zammit }
8157ac2246fSDamien Zammit 
snd_mbox1_src_switch_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)8167ac2246fSDamien Zammit static int snd_mbox1_src_switch_info(struct snd_kcontrol *kcontrol,
8177ac2246fSDamien Zammit 				     struct snd_ctl_elem_info *uinfo)
8187ac2246fSDamien Zammit {
8197ac2246fSDamien Zammit 	static const char *const texts[2] = {
8207ac2246fSDamien Zammit 		"Analog",
8217ac2246fSDamien Zammit 		"S/PDIF"
8227ac2246fSDamien Zammit 	};
8237ac2246fSDamien Zammit 
8247ac2246fSDamien Zammit 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
8257ac2246fSDamien Zammit }
8267ac2246fSDamien Zammit 
snd_mbox1_src_switch_resume(struct usb_mixer_elem_list * list)8277ac2246fSDamien Zammit static int snd_mbox1_src_switch_resume(struct usb_mixer_elem_list *list)
8287ac2246fSDamien Zammit {
8297ac2246fSDamien Zammit 	return snd_mbox1_src_switch_update(list->mixer, list->kctl->private_value);
8307ac2246fSDamien Zammit }
8317ac2246fSDamien Zammit 
8327ac2246fSDamien Zammit static const struct snd_kcontrol_new snd_mbox1_clk_switch = {
833d497a82fSDamien Zammit 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
834d497a82fSDamien Zammit 	.name = "Clock Source",
835d497a82fSDamien Zammit 	.index = 0,
836d497a82fSDamien Zammit 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
8377ac2246fSDamien Zammit 	.info = snd_mbox1_clk_switch_info,
8387ac2246fSDamien Zammit 	.get = snd_mbox1_clk_switch_get,
8397ac2246fSDamien Zammit 	.put = snd_mbox1_clk_switch_put,
840d497a82fSDamien Zammit 	.private_value = 0
841d497a82fSDamien Zammit };
842d497a82fSDamien Zammit 
8437ac2246fSDamien Zammit static const struct snd_kcontrol_new snd_mbox1_src_switch = {
8447ac2246fSDamien Zammit 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8457ac2246fSDamien Zammit 	.name = "Input Source",
8467ac2246fSDamien Zammit 	.index = 1,
8477ac2246fSDamien Zammit 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
8487ac2246fSDamien Zammit 	.info = snd_mbox1_src_switch_info,
8497ac2246fSDamien Zammit 	.get = snd_mbox1_src_switch_get,
8507ac2246fSDamien Zammit 	.put = snd_mbox1_src_switch_put,
8517ac2246fSDamien Zammit 	.private_value = 0
8527ac2246fSDamien Zammit };
8537ac2246fSDamien Zammit 
snd_mbox1_controls_create(struct usb_mixer_interface * mixer)8547ac2246fSDamien Zammit static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer)
855d497a82fSDamien Zammit {
8567ac2246fSDamien Zammit 	int err;
8577ac2246fSDamien Zammit 	err = add_single_ctl_with_resume(mixer, 0,
8587ac2246fSDamien Zammit 					 snd_mbox1_clk_switch_resume,
8597ac2246fSDamien Zammit 					 &snd_mbox1_clk_switch, NULL);
8607ac2246fSDamien Zammit 	if (err < 0)
8617ac2246fSDamien Zammit 		return err;
8627ac2246fSDamien Zammit 
8637ac2246fSDamien Zammit 	return add_single_ctl_with_resume(mixer, 1,
8647ac2246fSDamien Zammit 					  snd_mbox1_src_switch_resume,
8657ac2246fSDamien Zammit 					  &snd_mbox1_src_switch, NULL);
866d497a82fSDamien Zammit }
867d497a82fSDamien Zammit 
86854a8c500SDaniel Mack /* Native Instruments device quirks */
86954a8c500SDaniel Mack 
87054a8c500SDaniel Mack #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
87154a8c500SDaniel Mack 
snd_ni_control_init_val(struct usb_mixer_interface * mixer,struct snd_kcontrol * kctl)872da6d2769STakashi Iwai static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
873da6d2769STakashi Iwai 				   struct snd_kcontrol *kctl)
874da6d2769STakashi Iwai {
875da6d2769STakashi Iwai 	struct usb_device *dev = mixer->chip->dev;
876da6d2769STakashi Iwai 	unsigned int pval = kctl->private_value;
877da6d2769STakashi Iwai 	u8 value;
878da6d2769STakashi Iwai 	int err;
879da6d2769STakashi Iwai 
880da6d2769STakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
881da6d2769STakashi Iwai 			      (pval >> 16) & 0xff,
882da6d2769STakashi Iwai 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
883da6d2769STakashi Iwai 			      0, pval & 0xffff, &value, 1);
884da6d2769STakashi Iwai 	if (err < 0) {
885da6d2769STakashi Iwai 		dev_err(&dev->dev,
886da6d2769STakashi Iwai 			"unable to issue vendor read request (ret = %d)", err);
887da6d2769STakashi Iwai 		return err;
888da6d2769STakashi Iwai 	}
889da6d2769STakashi Iwai 
8902acf5a3eSColin Ian King 	kctl->private_value |= ((unsigned int)value << 24);
891da6d2769STakashi Iwai 	return 0;
892da6d2769STakashi Iwai }
893da6d2769STakashi Iwai 
snd_nativeinstruments_control_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)89454a8c500SDaniel Mack static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
89554a8c500SDaniel Mack 					     struct snd_ctl_elem_value *ucontrol)
89654a8c500SDaniel Mack {
897da6d2769STakashi Iwai 	ucontrol->value.integer.value[0] = kcontrol->private_value >> 24;
898da6d2769STakashi Iwai 	return 0;
89954a8c500SDaniel Mack }
90054a8c500SDaniel Mack 
snd_ni_update_cur_val(struct usb_mixer_elem_list * list)901da6d2769STakashi Iwai static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
902da6d2769STakashi Iwai {
903da6d2769STakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
904da6d2769STakashi Iwai 	unsigned int pval = list->kctl->private_value;
905da6d2769STakashi Iwai 	int err;
90654a8c500SDaniel Mack 
90747ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
90847ab1545STakashi Iwai 	if (err < 0)
90947ab1545STakashi Iwai 		return err;
910da6d2769STakashi Iwai 	err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
911da6d2769STakashi Iwai 			      (pval >> 16) & 0xff,
912da6d2769STakashi Iwai 			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
913da6d2769STakashi Iwai 			      pval >> 24, pval & 0xffff, NULL, 0, 1000);
91447ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
915da6d2769STakashi Iwai 	return err;
91654a8c500SDaniel Mack }
91754a8c500SDaniel Mack 
snd_nativeinstruments_control_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)91854a8c500SDaniel Mack static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
91954a8c500SDaniel Mack 					     struct snd_ctl_elem_value *ucontrol)
92054a8c500SDaniel Mack {
921da6d2769STakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
922da6d2769STakashi Iwai 	u8 oldval = (kcontrol->private_value >> 24) & 0xff;
923da6d2769STakashi Iwai 	u8 newval = ucontrol->value.integer.value[0];
924da6d2769STakashi Iwai 	int err;
92554a8c500SDaniel Mack 
926da6d2769STakashi Iwai 	if (oldval == newval)
92754a8c500SDaniel Mack 		return 0;
928da6d2769STakashi Iwai 
929da6d2769STakashi Iwai 	kcontrol->private_value &= ~(0xff << 24);
930c4a359a0STakashi Iwai 	kcontrol->private_value |= (unsigned int)newval << 24;
931da6d2769STakashi Iwai 	err = snd_ni_update_cur_val(list);
932da6d2769STakashi Iwai 	return err < 0 ? err : 1;
93354a8c500SDaniel Mack }
93454a8c500SDaniel Mack 
935195727e8STakashi Iwai static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
93654a8c500SDaniel Mack 	{
93754a8c500SDaniel Mack 		.name = "Direct Thru Channel A",
93854a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
93954a8c500SDaniel Mack 	},
94054a8c500SDaniel Mack 	{
94154a8c500SDaniel Mack 		.name = "Direct Thru Channel B",
94254a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
94354a8c500SDaniel Mack 	},
94454a8c500SDaniel Mack 	{
94554a8c500SDaniel Mack 		.name = "Phono Input Channel A",
94654a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
94754a8c500SDaniel Mack 	},
94854a8c500SDaniel Mack 	{
94954a8c500SDaniel Mack 		.name = "Phono Input Channel B",
95054a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
95154a8c500SDaniel Mack 	},
95254a8c500SDaniel Mack };
95354a8c500SDaniel Mack 
954195727e8STakashi Iwai static const struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = {
95554a8c500SDaniel Mack 	{
95654a8c500SDaniel Mack 		.name = "Direct Thru Channel A",
95754a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
95854a8c500SDaniel Mack 	},
95954a8c500SDaniel Mack 	{
96054a8c500SDaniel Mack 		.name = "Direct Thru Channel B",
96154a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
96254a8c500SDaniel Mack 	},
96354a8c500SDaniel Mack 	{
96454a8c500SDaniel Mack 		.name = "Direct Thru Channel C",
96554a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x07),
96654a8c500SDaniel Mack 	},
96754a8c500SDaniel Mack 	{
96854a8c500SDaniel Mack 		.name = "Direct Thru Channel D",
96954a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x01, 0x09),
97054a8c500SDaniel Mack 	},
97154a8c500SDaniel Mack 	{
97254a8c500SDaniel Mack 		.name = "Phono Input Channel A",
97354a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
97454a8c500SDaniel Mack 	},
97554a8c500SDaniel Mack 	{
97654a8c500SDaniel Mack 		.name = "Phono Input Channel B",
97754a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
97854a8c500SDaniel Mack 	},
97954a8c500SDaniel Mack 	{
98054a8c500SDaniel Mack 		.name = "Phono Input Channel C",
98154a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x07),
98254a8c500SDaniel Mack 	},
98354a8c500SDaniel Mack 	{
98454a8c500SDaniel Mack 		.name = "Phono Input Channel D",
98554a8c500SDaniel Mack 		.private_value = _MAKE_NI_CONTROL(0x02, 0x09),
98654a8c500SDaniel Mack 	},
98754a8c500SDaniel Mack };
98854a8c500SDaniel Mack 
snd_nativeinstruments_create_mixer(struct usb_mixer_interface * mixer,const struct snd_kcontrol_new * kc,unsigned int count)98954a8c500SDaniel Mack static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
99054a8c500SDaniel Mack 					      const struct snd_kcontrol_new *kc,
99154a8c500SDaniel Mack 					      unsigned int count)
99254a8c500SDaniel Mack {
99354a8c500SDaniel Mack 	int i, err = 0;
99454a8c500SDaniel Mack 	struct snd_kcontrol_new template = {
99554a8c500SDaniel Mack 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
99654a8c500SDaniel Mack 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
99754a8c500SDaniel Mack 		.get = snd_nativeinstruments_control_get,
99854a8c500SDaniel Mack 		.put = snd_nativeinstruments_control_put,
99954a8c500SDaniel Mack 		.info = snd_ctl_boolean_mono_info,
100054a8c500SDaniel Mack 	};
100154a8c500SDaniel Mack 
100254a8c500SDaniel Mack 	for (i = 0; i < count; i++) {
1003da6d2769STakashi Iwai 		struct usb_mixer_elem_list *list;
100454a8c500SDaniel Mack 
100554a8c500SDaniel Mack 		template.name = kc[i].name;
100654a8c500SDaniel Mack 		template.private_value = kc[i].private_value;
100754a8c500SDaniel Mack 
1008da6d2769STakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
1009da6d2769STakashi Iwai 						 snd_ni_update_cur_val,
1010da6d2769STakashi Iwai 						 &template, &list);
101154a8c500SDaniel Mack 		if (err < 0)
101254a8c500SDaniel Mack 			break;
1013da6d2769STakashi Iwai 		snd_ni_control_init_val(mixer, list->kctl);
101454a8c500SDaniel Mack 	}
101554a8c500SDaniel Mack 
101654a8c500SDaniel Mack 	return err;
101754a8c500SDaniel Mack }
101854a8c500SDaniel Mack 
1019d5a0bf6cSDaniel Mack /* M-Audio FastTrack Ultra quirks */
1020e9a25e04SMatt Gruskin /* FTU Effect switch (also used by C400/C600) */
snd_ftu_eff_switch_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)1021d34bf148SFelix Homann static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
1022d34bf148SFelix Homann 					struct snd_ctl_elem_info *uinfo)
1023d34bf148SFelix Homann {
10247bbd03e0STakashi Iwai 	static const char *const texts[8] = {
10257bbd03e0STakashi Iwai 		"Room 1", "Room 2", "Room 3", "Hall 1",
10267bbd03e0STakashi Iwai 		"Hall 2", "Plate", "Delay", "Echo"
1027d34bf148SFelix Homann 	};
1028d34bf148SFelix Homann 
10297bbd03e0STakashi Iwai 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
1030d34bf148SFelix Homann }
1031d34bf148SFelix Homann 
snd_ftu_eff_switch_init(struct usb_mixer_interface * mixer,struct snd_kcontrol * kctl)10320b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
10330b4e9cfcSTakashi Iwai 				   struct snd_kcontrol *kctl)
1034d34bf148SFelix Homann {
10350b4e9cfcSTakashi Iwai 	struct usb_device *dev = mixer->chip->dev;
10360b4e9cfcSTakashi Iwai 	unsigned int pval = kctl->private_value;
1037d34bf148SFelix Homann 	int err;
1038d34bf148SFelix Homann 	unsigned char value[2];
1039d34bf148SFelix Homann 
1040d34bf148SFelix Homann 	value[0] = 0x00;
1041d34bf148SFelix Homann 	value[1] = 0x00;
1042d34bf148SFelix Homann 
10430b4e9cfcSTakashi Iwai 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
1044d34bf148SFelix Homann 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
10450b4e9cfcSTakashi Iwai 			      pval & 0xff00,
10460b4e9cfcSTakashi Iwai 			      snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
10470b4e9cfcSTakashi Iwai 			      value, 2);
1048d34bf148SFelix Homann 	if (err < 0)
1049d34bf148SFelix Homann 		return err;
1050d34bf148SFelix Homann 
10512acf5a3eSColin Ian King 	kctl->private_value |= (unsigned int)value[0] << 24;
1052d34bf148SFelix Homann 	return 0;
1053d34bf148SFelix Homann }
1054d34bf148SFelix Homann 
snd_ftu_eff_switch_get(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)10550b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
10560b4e9cfcSTakashi Iwai 					struct snd_ctl_elem_value *ucontrol)
10570b4e9cfcSTakashi Iwai {
10580b4e9cfcSTakashi Iwai 	ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
10590b4e9cfcSTakashi Iwai 	return 0;
10600b4e9cfcSTakashi Iwai }
10610b4e9cfcSTakashi Iwai 
snd_ftu_eff_switch_update(struct usb_mixer_elem_list * list)10620b4e9cfcSTakashi Iwai static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
10630b4e9cfcSTakashi Iwai {
10640b4e9cfcSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
10650b4e9cfcSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
10660b4e9cfcSTakashi Iwai 	unsigned char value[2];
10670b4e9cfcSTakashi Iwai 	int err;
10680b4e9cfcSTakashi Iwai 
10690b4e9cfcSTakashi Iwai 	value[0] = pval >> 24;
10700b4e9cfcSTakashi Iwai 	value[1] = 0;
10710b4e9cfcSTakashi Iwai 
107247ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
107347ab1545STakashi Iwai 	if (err < 0)
107447ab1545STakashi Iwai 		return err;
10750b4e9cfcSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
10760b4e9cfcSTakashi Iwai 			      usb_sndctrlpipe(chip->dev, 0),
10770b4e9cfcSTakashi Iwai 			      UAC_SET_CUR,
10780b4e9cfcSTakashi Iwai 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
10790b4e9cfcSTakashi Iwai 			      pval & 0xff00,
10800b4e9cfcSTakashi Iwai 			      snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
10810b4e9cfcSTakashi Iwai 			      value, 2);
108247ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
10830b4e9cfcSTakashi Iwai 	return err;
10840b4e9cfcSTakashi Iwai }
10850b4e9cfcSTakashi Iwai 
snd_ftu_eff_switch_put(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)1086d34bf148SFelix Homann static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
1087d34bf148SFelix Homann 					struct snd_ctl_elem_value *ucontrol)
1088d34bf148SFelix Homann {
10890b4e9cfcSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
10900b4e9cfcSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
10910b4e9cfcSTakashi Iwai 	int cur_val, err, new_val;
1092d34bf148SFelix Homann 
10930b4e9cfcSTakashi Iwai 	cur_val = pval >> 24;
1094d34bf148SFelix Homann 	new_val = ucontrol->value.enumerated.item[0];
10950b4e9cfcSTakashi Iwai 	if (cur_val == new_val)
10960b4e9cfcSTakashi Iwai 		return 0;
1097d34bf148SFelix Homann 
10980b4e9cfcSTakashi Iwai 	kctl->private_value &= ~(0xff << 24);
10990b4e9cfcSTakashi Iwai 	kctl->private_value |= new_val << 24;
11000b4e9cfcSTakashi Iwai 	err = snd_ftu_eff_switch_update(list);
11010b4e9cfcSTakashi Iwai 	return err < 0 ? err : 1;
11021a290581STakashi Iwai }
11031a290581STakashi Iwai 
snd_ftu_create_effect_switch(struct usb_mixer_interface * mixer,int validx,int bUnitID)1104d847ce0eSEldad Zack static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
1105d847ce0eSEldad Zack 	int validx, int bUnitID)
1106d34bf148SFelix Homann {
1107d34bf148SFelix Homann 	static struct snd_kcontrol_new template = {
1108d34bf148SFelix Homann 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1109d34bf148SFelix Homann 		.name = "Effect Program Switch",
1110d34bf148SFelix Homann 		.index = 0,
1111d34bf148SFelix Homann 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
1112d34bf148SFelix Homann 		.info = snd_ftu_eff_switch_info,
1113d34bf148SFelix Homann 		.get = snd_ftu_eff_switch_get,
1114d34bf148SFelix Homann 		.put = snd_ftu_eff_switch_put
1115d34bf148SFelix Homann 	};
11160b4e9cfcSTakashi Iwai 	struct usb_mixer_elem_list *list;
1117d34bf148SFelix Homann 	int err;
1118d34bf148SFelix Homann 
11190b4e9cfcSTakashi Iwai 	err = add_single_ctl_with_resume(mixer, bUnitID,
11200b4e9cfcSTakashi Iwai 					 snd_ftu_eff_switch_update,
11210b4e9cfcSTakashi Iwai 					 &template, &list);
1122d34bf148SFelix Homann 	if (err < 0)
1123d34bf148SFelix Homann 		return err;
11240b4e9cfcSTakashi Iwai 	list->kctl->private_value = (validx << 8) | bUnitID;
11250b4e9cfcSTakashi Iwai 	snd_ftu_eff_switch_init(mixer, list->kctl);
1126d34bf148SFelix Homann 	return 0;
1127d34bf148SFelix Homann }
1128d5a0bf6cSDaniel Mack 
1129cfe8f97cSFelix Homann /* Create volume controls for FTU devices*/
snd_ftu_create_volume_ctls(struct usb_mixer_interface * mixer)1130cfe8f97cSFelix Homann static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
1131d5a0bf6cSDaniel Mack {
1132d5a0bf6cSDaniel Mack 	char name[64];
11338a4d1d39SFelix Homann 	unsigned int control, cmask;
1134d5a0bf6cSDaniel Mack 	int in, out, err;
1135d5a0bf6cSDaniel Mack 
11368a4d1d39SFelix Homann 	const unsigned int id = 5;
11378a4d1d39SFelix Homann 	const int val_type = USB_MIXER_S16;
11388a4d1d39SFelix Homann 
1139d5a0bf6cSDaniel Mack 	for (out = 0; out < 8; out++) {
11408a4d1d39SFelix Homann 		control = out + 1;
1141d5a0bf6cSDaniel Mack 		for (in = 0; in < 8; in++) {
11428a4d1d39SFelix Homann 			cmask = 1 << in;
1143d5a0bf6cSDaniel Mack 			snprintf(name, sizeof(name),
11448a4d1d39SFelix Homann 				"AIn%d - Out%d Capture Volume",
11458a4d1d39SFelix Homann 				in  + 1, out + 1);
11468a4d1d39SFelix Homann 			err = snd_create_std_mono_ctl(mixer, id, control,
11478a4d1d39SFelix Homann 							cmask, val_type, name,
114825ee7ef8SFelix Homann 							&snd_usb_mixer_vol_tlv);
1149d5a0bf6cSDaniel Mack 			if (err < 0)
1150d5a0bf6cSDaniel Mack 				return err;
1151d5a0bf6cSDaniel Mack 		}
1152d5a0bf6cSDaniel Mack 		for (in = 8; in < 16; in++) {
11538a4d1d39SFelix Homann 			cmask = 1 << in;
1154d5a0bf6cSDaniel Mack 			snprintf(name, sizeof(name),
11558a4d1d39SFelix Homann 				"DIn%d - Out%d Playback Volume",
11568a4d1d39SFelix Homann 				in - 7, out + 1);
11578a4d1d39SFelix Homann 			err = snd_create_std_mono_ctl(mixer, id, control,
11588a4d1d39SFelix Homann 							cmask, val_type, name,
115925ee7ef8SFelix Homann 							&snd_usb_mixer_vol_tlv);
1160d5a0bf6cSDaniel Mack 			if (err < 0)
1161d5a0bf6cSDaniel Mack 				return err;
1162d5a0bf6cSDaniel Mack 		}
1163d5a0bf6cSDaniel Mack 	}
1164d5a0bf6cSDaniel Mack 
1165d5a0bf6cSDaniel Mack 	return 0;
1166d5a0bf6cSDaniel Mack }
1167d5a0bf6cSDaniel Mack 
1168d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface * mixer)1169d34bf148SFelix Homann static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
1170d34bf148SFelix Homann {
1171d34bf148SFelix Homann 	static const char name[] = "Effect Volume";
1172d34bf148SFelix Homann 	const unsigned int id = 6;
1173d34bf148SFelix Homann 	const int val_type = USB_MIXER_U8;
1174d34bf148SFelix Homann 	const unsigned int control = 2;
1175d34bf148SFelix Homann 	const unsigned int cmask = 0;
1176d34bf148SFelix Homann 
1177d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1178d34bf148SFelix Homann 					name, snd_usb_mixer_vol_tlv);
1179d34bf148SFelix Homann }
1180d34bf148SFelix Homann 
1181d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface * mixer)1182d34bf148SFelix Homann static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
1183d34bf148SFelix Homann {
1184d34bf148SFelix Homann 	static const char name[] = "Effect Duration";
1185d34bf148SFelix Homann 	const unsigned int id = 6;
1186d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1187d34bf148SFelix Homann 	const unsigned int control = 3;
1188d34bf148SFelix Homann 	const unsigned int cmask = 0;
1189d34bf148SFelix Homann 
1190d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1191d34bf148SFelix Homann 					name, snd_usb_mixer_vol_tlv);
1192d34bf148SFelix Homann }
1193d34bf148SFelix Homann 
1194d34bf148SFelix Homann /* This control needs a volume quirk, see mixer.c */
snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface * mixer)1195d34bf148SFelix Homann static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
1196d34bf148SFelix Homann {
1197d34bf148SFelix Homann 	static const char name[] = "Effect Feedback Volume";
1198d34bf148SFelix Homann 	const unsigned int id = 6;
1199d34bf148SFelix Homann 	const int val_type = USB_MIXER_U8;
1200d34bf148SFelix Homann 	const unsigned int control = 4;
1201d34bf148SFelix Homann 	const unsigned int cmask = 0;
1202d34bf148SFelix Homann 
1203d34bf148SFelix Homann 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
1204d34bf148SFelix Homann 					name, NULL);
1205d34bf148SFelix Homann }
1206d34bf148SFelix Homann 
snd_ftu_create_effect_return_ctls(struct usb_mixer_interface * mixer)1207d34bf148SFelix Homann static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
1208d34bf148SFelix Homann {
1209d34bf148SFelix Homann 	unsigned int cmask;
1210d34bf148SFelix Homann 	int err, ch;
1211d34bf148SFelix Homann 	char name[48];
1212d34bf148SFelix Homann 
1213d34bf148SFelix Homann 	const unsigned int id = 7;
1214d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1215d34bf148SFelix Homann 	const unsigned int control = 7;
1216d34bf148SFelix Homann 
1217d34bf148SFelix Homann 	for (ch = 0; ch < 4; ++ch) {
1218d34bf148SFelix Homann 		cmask = 1 << ch;
1219d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1220d34bf148SFelix Homann 			"Effect Return %d Volume", ch + 1);
1221d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control,
1222d34bf148SFelix Homann 						cmask, val_type, name,
1223d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1224d34bf148SFelix Homann 		if (err < 0)
1225d34bf148SFelix Homann 			return err;
1226d34bf148SFelix Homann 	}
1227d34bf148SFelix Homann 
1228d34bf148SFelix Homann 	return 0;
1229d34bf148SFelix Homann }
1230d34bf148SFelix Homann 
snd_ftu_create_effect_send_ctls(struct usb_mixer_interface * mixer)1231d34bf148SFelix Homann static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
1232d34bf148SFelix Homann {
1233d34bf148SFelix Homann 	unsigned int  cmask;
1234d34bf148SFelix Homann 	int err, ch;
1235d34bf148SFelix Homann 	char name[48];
1236d34bf148SFelix Homann 
1237d34bf148SFelix Homann 	const unsigned int id = 5;
1238d34bf148SFelix Homann 	const int val_type = USB_MIXER_S16;
1239d34bf148SFelix Homann 	const unsigned int control = 9;
1240d34bf148SFelix Homann 
1241d34bf148SFelix Homann 	for (ch = 0; ch < 8; ++ch) {
1242d34bf148SFelix Homann 		cmask = 1 << ch;
1243d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1244d34bf148SFelix Homann 			"Effect Send AIn%d Volume", ch + 1);
1245d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
1246d34bf148SFelix Homann 						val_type, name,
1247d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1248d34bf148SFelix Homann 		if (err < 0)
1249d34bf148SFelix Homann 			return err;
1250d34bf148SFelix Homann 	}
1251d34bf148SFelix Homann 	for (ch = 8; ch < 16; ++ch) {
1252d34bf148SFelix Homann 		cmask = 1 << ch;
1253d34bf148SFelix Homann 		snprintf(name, sizeof(name),
1254d34bf148SFelix Homann 			"Effect Send DIn%d Volume", ch - 7);
1255d34bf148SFelix Homann 		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
1256d34bf148SFelix Homann 						val_type, name,
1257d34bf148SFelix Homann 						snd_usb_mixer_vol_tlv);
1258d34bf148SFelix Homann 		if (err < 0)
1259d34bf148SFelix Homann 			return err;
1260d34bf148SFelix Homann 	}
1261d34bf148SFelix Homann 	return 0;
1262d34bf148SFelix Homann }
1263d34bf148SFelix Homann 
snd_ftu_create_mixer(struct usb_mixer_interface * mixer)1264cfe8f97cSFelix Homann static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
12657536c301SMark Hills {
12668a4d1d39SFelix Homann 	int err;
12677536c301SMark Hills 
1268cfe8f97cSFelix Homann 	err = snd_ftu_create_volume_ctls(mixer);
12698a4d1d39SFelix Homann 	if (err < 0)
12708a4d1d39SFelix Homann 		return err;
12717536c301SMark Hills 
1272d847ce0eSEldad Zack 	err = snd_ftu_create_effect_switch(mixer, 1, 6);
1273d34bf148SFelix Homann 	if (err < 0)
1274d34bf148SFelix Homann 		return err;
1275d847ce0eSEldad Zack 
1276d34bf148SFelix Homann 	err = snd_ftu_create_effect_volume_ctl(mixer);
1277d34bf148SFelix Homann 	if (err < 0)
1278d34bf148SFelix Homann 		return err;
1279d34bf148SFelix Homann 
1280d34bf148SFelix Homann 	err = snd_ftu_create_effect_duration_ctl(mixer);
1281d34bf148SFelix Homann 	if (err < 0)
1282d34bf148SFelix Homann 		return err;
1283d34bf148SFelix Homann 
1284d34bf148SFelix Homann 	err = snd_ftu_create_effect_feedback_ctl(mixer);
1285d34bf148SFelix Homann 	if (err < 0)
1286d34bf148SFelix Homann 		return err;
1287d34bf148SFelix Homann 
1288d34bf148SFelix Homann 	err = snd_ftu_create_effect_return_ctls(mixer);
1289d34bf148SFelix Homann 	if (err < 0)
1290d34bf148SFelix Homann 		return err;
1291d34bf148SFelix Homann 
1292d34bf148SFelix Homann 	err = snd_ftu_create_effect_send_ctls(mixer);
1293d34bf148SFelix Homann 	if (err < 0)
1294d34bf148SFelix Homann 		return err;
1295d34bf148SFelix Homann 
12968a4d1d39SFelix Homann 	return 0;
12977536c301SMark Hills }
12987536c301SMark Hills 
snd_emuusb_set_samplerate(struct snd_usb_audio * chip,unsigned char samplerate_id)12997b1eda22SDaniel Mack void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
13007b1eda22SDaniel Mack 			       unsigned char samplerate_id)
13017b1eda22SDaniel Mack {
13027b1eda22SDaniel Mack 	struct usb_mixer_interface *mixer;
13037b1eda22SDaniel Mack 	struct usb_mixer_elem_info *cval;
13046de3c9e3STakashi Iwai 	int unitid = 12; /* SampleRate ExtensionUnit ID */
13057b1eda22SDaniel Mack 
13067b1eda22SDaniel Mack 	list_for_each_entry(mixer, &chip->mixer_list, list) {
13076de3c9e3STakashi Iwai 		if (mixer->id_elems[unitid]) {
13088c558076STakashi Iwai 			cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
13097b1eda22SDaniel Mack 			snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
13107b1eda22SDaniel Mack 						    cval->control << 8,
13117b1eda22SDaniel Mack 						    samplerate_id);
13127b1eda22SDaniel Mack 			snd_usb_mixer_notify_id(mixer, unitid);
13137b1eda22SDaniel Mack 			break;
13147b1eda22SDaniel Mack 		}
13157b1eda22SDaniel Mack 	}
13166de3c9e3STakashi Iwai }
13177b1eda22SDaniel Mack 
1318e9a25e04SMatt Gruskin /* M-Audio Fast Track C400/C600 */
1319e9a25e04SMatt Gruskin /* C400/C600 volume controls, this control needs a volume quirk, see mixer.c */
snd_c400_create_vol_ctls(struct usb_mixer_interface * mixer)132009d8e3a7SEldad Zack static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
132109d8e3a7SEldad Zack {
132209d8e3a7SEldad Zack 	char name[64];
132309d8e3a7SEldad Zack 	unsigned int cmask, offset;
132409d8e3a7SEldad Zack 	int out, chan, err;
1325e9a25e04SMatt Gruskin 	int num_outs = 0;
1326e9a25e04SMatt Gruskin 	int num_ins = 0;
132709d8e3a7SEldad Zack 
132809d8e3a7SEldad Zack 	const unsigned int id = 0x40;
132909d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
133009d8e3a7SEldad Zack 	const int control = 1;
133109d8e3a7SEldad Zack 
1332e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1333e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1334e9a25e04SMatt Gruskin 		num_outs = 6;
1335e9a25e04SMatt Gruskin 		num_ins = 4;
1336e9a25e04SMatt Gruskin 		break;
1337e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1338e9a25e04SMatt Gruskin 		num_outs = 8;
1339e9a25e04SMatt Gruskin 		num_ins = 6;
1340e9a25e04SMatt Gruskin 		break;
1341e9a25e04SMatt Gruskin 	}
1342e9a25e04SMatt Gruskin 
1343e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs + num_ins; chan++) {
1344e9a25e04SMatt Gruskin 		for (out = 0; out < num_outs; out++) {
1345e9a25e04SMatt Gruskin 			if (chan < num_outs) {
134609d8e3a7SEldad Zack 				snprintf(name, sizeof(name),
134709d8e3a7SEldad Zack 					"PCM%d-Out%d Playback Volume",
134809d8e3a7SEldad Zack 					chan + 1, out + 1);
134909d8e3a7SEldad Zack 			} else {
135009d8e3a7SEldad Zack 				snprintf(name, sizeof(name),
135109d8e3a7SEldad Zack 					"In%d-Out%d Playback Volume",
1352e9a25e04SMatt Gruskin 					chan - num_outs + 1, out + 1);
135309d8e3a7SEldad Zack 			}
135409d8e3a7SEldad Zack 
135509d8e3a7SEldad Zack 			cmask = (out == 0) ? 0 : 1 << (out - 1);
1356e9a25e04SMatt Gruskin 			offset = chan * num_outs;
135709d8e3a7SEldad Zack 			err = snd_create_std_mono_ctl_offset(mixer, id, control,
135809d8e3a7SEldad Zack 						cmask, val_type, offset, name,
135909d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
136009d8e3a7SEldad Zack 			if (err < 0)
136109d8e3a7SEldad Zack 				return err;
136209d8e3a7SEldad Zack 		}
136309d8e3a7SEldad Zack 	}
136409d8e3a7SEldad Zack 
136509d8e3a7SEldad Zack 	return 0;
136609d8e3a7SEldad Zack }
136709d8e3a7SEldad Zack 
136809d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
snd_c400_create_effect_volume_ctl(struct usb_mixer_interface * mixer)136909d8e3a7SEldad Zack static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
137009d8e3a7SEldad Zack {
137109d8e3a7SEldad Zack 	static const char name[] = "Effect Volume";
137209d8e3a7SEldad Zack 	const unsigned int id = 0x43;
137309d8e3a7SEldad Zack 	const int val_type = USB_MIXER_U8;
137409d8e3a7SEldad Zack 	const unsigned int control = 3;
137509d8e3a7SEldad Zack 	const unsigned int cmask = 0;
137609d8e3a7SEldad Zack 
137709d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
137809d8e3a7SEldad Zack 					name, snd_usb_mixer_vol_tlv);
137909d8e3a7SEldad Zack }
138009d8e3a7SEldad Zack 
138109d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
snd_c400_create_effect_duration_ctl(struct usb_mixer_interface * mixer)138209d8e3a7SEldad Zack static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
138309d8e3a7SEldad Zack {
138409d8e3a7SEldad Zack 	static const char name[] = "Effect Duration";
138509d8e3a7SEldad Zack 	const unsigned int id = 0x43;
138609d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
138709d8e3a7SEldad Zack 	const unsigned int control = 4;
138809d8e3a7SEldad Zack 	const unsigned int cmask = 0;
138909d8e3a7SEldad Zack 
139009d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
139109d8e3a7SEldad Zack 					name, snd_usb_mixer_vol_tlv);
139209d8e3a7SEldad Zack }
139309d8e3a7SEldad Zack 
139409d8e3a7SEldad Zack /* This control needs a volume quirk, see mixer.c */
snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface * mixer)139509d8e3a7SEldad Zack static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
139609d8e3a7SEldad Zack {
139709d8e3a7SEldad Zack 	static const char name[] = "Effect Feedback Volume";
139809d8e3a7SEldad Zack 	const unsigned int id = 0x43;
139909d8e3a7SEldad Zack 	const int val_type = USB_MIXER_U8;
140009d8e3a7SEldad Zack 	const unsigned int control = 5;
140109d8e3a7SEldad Zack 	const unsigned int cmask = 0;
140209d8e3a7SEldad Zack 
140309d8e3a7SEldad Zack 	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
140409d8e3a7SEldad Zack 					name, NULL);
140509d8e3a7SEldad Zack }
140609d8e3a7SEldad Zack 
snd_c400_create_effect_vol_ctls(struct usb_mixer_interface * mixer)140709d8e3a7SEldad Zack static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
140809d8e3a7SEldad Zack {
140909d8e3a7SEldad Zack 	char name[64];
141009d8e3a7SEldad Zack 	unsigned int cmask;
141109d8e3a7SEldad Zack 	int chan, err;
1412e9a25e04SMatt Gruskin 	int num_outs = 0;
1413e9a25e04SMatt Gruskin 	int num_ins = 0;
141409d8e3a7SEldad Zack 
141509d8e3a7SEldad Zack 	const unsigned int id = 0x42;
141609d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
141709d8e3a7SEldad Zack 	const int control = 1;
141809d8e3a7SEldad Zack 
1419e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1420e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1421e9a25e04SMatt Gruskin 		num_outs = 6;
1422e9a25e04SMatt Gruskin 		num_ins = 4;
1423e9a25e04SMatt Gruskin 		break;
1424e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1425e9a25e04SMatt Gruskin 		num_outs = 8;
1426e9a25e04SMatt Gruskin 		num_ins = 6;
1427e9a25e04SMatt Gruskin 		break;
1428e9a25e04SMatt Gruskin 	}
1429e9a25e04SMatt Gruskin 
1430e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs + num_ins; chan++) {
1431e9a25e04SMatt Gruskin 		if (chan < num_outs) {
143209d8e3a7SEldad Zack 			snprintf(name, sizeof(name),
143309d8e3a7SEldad Zack 				"Effect Send DOut%d",
143409d8e3a7SEldad Zack 				chan + 1);
143509d8e3a7SEldad Zack 		} else {
143609d8e3a7SEldad Zack 			snprintf(name, sizeof(name),
143709d8e3a7SEldad Zack 				"Effect Send AIn%d",
1438e9a25e04SMatt Gruskin 				chan - num_outs + 1);
143909d8e3a7SEldad Zack 		}
144009d8e3a7SEldad Zack 
144109d8e3a7SEldad Zack 		cmask = (chan == 0) ? 0 : 1 << (chan - 1);
144209d8e3a7SEldad Zack 		err = snd_create_std_mono_ctl(mixer, id, control,
144309d8e3a7SEldad Zack 						cmask, val_type, name,
144409d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
144509d8e3a7SEldad Zack 		if (err < 0)
144609d8e3a7SEldad Zack 			return err;
144709d8e3a7SEldad Zack 	}
144809d8e3a7SEldad Zack 
144909d8e3a7SEldad Zack 	return 0;
145009d8e3a7SEldad Zack }
145109d8e3a7SEldad Zack 
snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface * mixer)145209d8e3a7SEldad Zack static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer)
145309d8e3a7SEldad Zack {
145409d8e3a7SEldad Zack 	char name[64];
145509d8e3a7SEldad Zack 	unsigned int cmask;
145609d8e3a7SEldad Zack 	int chan, err;
1457e9a25e04SMatt Gruskin 	int num_outs = 0;
1458e9a25e04SMatt Gruskin 	int offset = 0;
145909d8e3a7SEldad Zack 
146009d8e3a7SEldad Zack 	const unsigned int id = 0x40;
146109d8e3a7SEldad Zack 	const int val_type = USB_MIXER_S16;
146209d8e3a7SEldad Zack 	const int control = 1;
146309d8e3a7SEldad Zack 
1464e9a25e04SMatt Gruskin 	switch (mixer->chip->usb_id) {
1465e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2030):
1466e9a25e04SMatt Gruskin 		num_outs = 6;
1467e9a25e04SMatt Gruskin 		offset = 0x3c;
1468e9a25e04SMatt Gruskin 		/* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */
1469e9a25e04SMatt Gruskin 		break;
1470e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031):
1471e9a25e04SMatt Gruskin 		num_outs = 8;
1472e9a25e04SMatt Gruskin 		offset = 0x70;
1473e9a25e04SMatt Gruskin 		/* { 0x70, 0x79, 0x72, 0x7b, 0x74, 0x7d, 0x76, 0x7f } */
1474e9a25e04SMatt Gruskin 		break;
1475e9a25e04SMatt Gruskin 	}
1476e9a25e04SMatt Gruskin 
1477e9a25e04SMatt Gruskin 	for (chan = 0; chan < num_outs; chan++) {
147809d8e3a7SEldad Zack 		snprintf(name, sizeof(name),
147909d8e3a7SEldad Zack 			"Effect Return %d",
148009d8e3a7SEldad Zack 			chan + 1);
148109d8e3a7SEldad Zack 
1482e9a25e04SMatt Gruskin 		cmask = (chan == 0) ? 0 :
1483e9a25e04SMatt Gruskin 			1 << (chan + (chan % 2) * num_outs - 1);
148409d8e3a7SEldad Zack 		err = snd_create_std_mono_ctl_offset(mixer, id, control,
148509d8e3a7SEldad Zack 						cmask, val_type, offset, name,
148609d8e3a7SEldad Zack 						&snd_usb_mixer_vol_tlv);
148709d8e3a7SEldad Zack 		if (err < 0)
148809d8e3a7SEldad Zack 			return err;
148909d8e3a7SEldad Zack 	}
149009d8e3a7SEldad Zack 
149109d8e3a7SEldad Zack 	return 0;
149209d8e3a7SEldad Zack }
149309d8e3a7SEldad Zack 
snd_c400_create_mixer(struct usb_mixer_interface * mixer)149409d8e3a7SEldad Zack static int snd_c400_create_mixer(struct usb_mixer_interface *mixer)
149509d8e3a7SEldad Zack {
149609d8e3a7SEldad Zack 	int err;
149709d8e3a7SEldad Zack 
149809d8e3a7SEldad Zack 	err = snd_c400_create_vol_ctls(mixer);
149909d8e3a7SEldad Zack 	if (err < 0)
150009d8e3a7SEldad Zack 		return err;
150109d8e3a7SEldad Zack 
150209d8e3a7SEldad Zack 	err = snd_c400_create_effect_vol_ctls(mixer);
150309d8e3a7SEldad Zack 	if (err < 0)
150409d8e3a7SEldad Zack 		return err;
150509d8e3a7SEldad Zack 
150609d8e3a7SEldad Zack 	err = snd_c400_create_effect_ret_vol_ctls(mixer);
150709d8e3a7SEldad Zack 	if (err < 0)
150809d8e3a7SEldad Zack 		return err;
150909d8e3a7SEldad Zack 
151009d8e3a7SEldad Zack 	err = snd_ftu_create_effect_switch(mixer, 2, 0x43);
151109d8e3a7SEldad Zack 	if (err < 0)
151209d8e3a7SEldad Zack 		return err;
151309d8e3a7SEldad Zack 
151409d8e3a7SEldad Zack 	err = snd_c400_create_effect_volume_ctl(mixer);
151509d8e3a7SEldad Zack 	if (err < 0)
151609d8e3a7SEldad Zack 		return err;
151709d8e3a7SEldad Zack 
151809d8e3a7SEldad Zack 	err = snd_c400_create_effect_duration_ctl(mixer);
151909d8e3a7SEldad Zack 	if (err < 0)
152009d8e3a7SEldad Zack 		return err;
152109d8e3a7SEldad Zack 
152209d8e3a7SEldad Zack 	err = snd_c400_create_effect_feedback_ctl(mixer);
152309d8e3a7SEldad Zack 	if (err < 0)
152409d8e3a7SEldad Zack 		return err;
152509d8e3a7SEldad Zack 
152609d8e3a7SEldad Zack 	return 0;
152709d8e3a7SEldad Zack }
152809d8e3a7SEldad Zack 
1529b71dad18SMark Hills /*
1530b71dad18SMark Hills  * The mixer units for Ebox-44 are corrupt, and even where they
1531b71dad18SMark Hills  * are valid they presents mono controls as L and R channels of
1532b71dad18SMark Hills  * stereo. So we provide a good mixer here.
1533b71dad18SMark Hills  */
1534a01df925STakashi Iwai static const struct std_mono_table ebox44_table[] = {
1535989b0138SMark Hills 	{
1536989b0138SMark Hills 		.unitid = 4,
1537989b0138SMark Hills 		.control = 1,
1538989b0138SMark Hills 		.cmask = 0x0,
1539989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1540989b0138SMark Hills 		.name = "Headphone Playback Switch"
1541989b0138SMark Hills 	},
1542989b0138SMark Hills 	{
1543989b0138SMark Hills 		.unitid = 4,
1544989b0138SMark Hills 		.control = 2,
1545989b0138SMark Hills 		.cmask = 0x1,
1546989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1547989b0138SMark Hills 		.name = "Headphone A Mix Playback Volume"
1548989b0138SMark Hills 	},
1549989b0138SMark Hills 	{
1550989b0138SMark Hills 		.unitid = 4,
1551989b0138SMark Hills 		.control = 2,
1552989b0138SMark Hills 		.cmask = 0x2,
1553989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1554989b0138SMark Hills 		.name = "Headphone B Mix Playback Volume"
1555989b0138SMark Hills 	},
1556b71dad18SMark Hills 
1557989b0138SMark Hills 	{
1558989b0138SMark Hills 		.unitid = 7,
1559989b0138SMark Hills 		.control = 1,
1560989b0138SMark Hills 		.cmask = 0x0,
1561989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1562989b0138SMark Hills 		.name = "Output Playback Switch"
1563989b0138SMark Hills 	},
1564989b0138SMark Hills 	{
1565989b0138SMark Hills 		.unitid = 7,
1566989b0138SMark Hills 		.control = 2,
1567989b0138SMark Hills 		.cmask = 0x1,
1568989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1569989b0138SMark Hills 		.name = "Output A Playback Volume"
1570989b0138SMark Hills 	},
1571989b0138SMark Hills 	{
1572989b0138SMark Hills 		.unitid = 7,
1573989b0138SMark Hills 		.control = 2,
1574989b0138SMark Hills 		.cmask = 0x2,
1575989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1576989b0138SMark Hills 		.name = "Output B Playback Volume"
1577989b0138SMark Hills 	},
1578b71dad18SMark Hills 
1579989b0138SMark Hills 	{
1580989b0138SMark Hills 		.unitid = 10,
1581989b0138SMark Hills 		.control = 1,
1582989b0138SMark Hills 		.cmask = 0x0,
1583989b0138SMark Hills 		.val_type = USB_MIXER_INV_BOOLEAN,
1584989b0138SMark Hills 		.name = "Input Capture Switch"
1585989b0138SMark Hills 	},
1586989b0138SMark Hills 	{
1587989b0138SMark Hills 		.unitid = 10,
1588989b0138SMark Hills 		.control = 2,
1589989b0138SMark Hills 		.cmask = 0x1,
1590989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1591989b0138SMark Hills 		.name = "Input A Capture Volume"
1592989b0138SMark Hills 	},
1593989b0138SMark Hills 	{
1594989b0138SMark Hills 		.unitid = 10,
1595989b0138SMark Hills 		.control = 2,
1596989b0138SMark Hills 		.cmask = 0x2,
1597989b0138SMark Hills 		.val_type = USB_MIXER_S16,
1598989b0138SMark Hills 		.name = "Input B Capture Volume"
1599989b0138SMark Hills 	},
1600b71dad18SMark Hills 
1601b71dad18SMark Hills 	{}
1602b71dad18SMark Hills };
1603b71dad18SMark Hills 
1604066624c6SPrzemek Rudy /* Audio Advantage Micro II findings:
1605066624c6SPrzemek Rudy  *
1606066624c6SPrzemek Rudy  * Mapping spdif AES bits to vendor register.bit:
1607066624c6SPrzemek Rudy  * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00
1608066624c6SPrzemek Rudy  * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01
1609066624c6SPrzemek Rudy  * AES2: [0 0 0 0 0 0 0 0]
1610066624c6SPrzemek Rudy  * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request
1611066624c6SPrzemek Rudy  *                           (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices
1612066624c6SPrzemek Rudy  *
1613066624c6SPrzemek Rudy  * power on values:
1614066624c6SPrzemek Rudy  * r2: 0x10
1615066624c6SPrzemek Rudy  * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set
1616066624c6SPrzemek Rudy  *           just after it to 0xa0, presumably it disables/mutes some analog
1617066624c6SPrzemek Rudy  *           parts when there is no audio.)
1618066624c6SPrzemek Rudy  * r9: 0x28
1619066624c6SPrzemek Rudy  *
1620066624c6SPrzemek Rudy  * Optical transmitter on/off:
1621066624c6SPrzemek Rudy  * vendor register.bit: 9.1
1622066624c6SPrzemek Rudy  * 0 - on (0x28 register value)
1623066624c6SPrzemek Rudy  * 1 - off (0x2a register value)
1624066624c6SPrzemek Rudy  *
1625066624c6SPrzemek Rudy  */
snd_microii_spdif_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)1626066624c6SPrzemek Rudy static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol,
1627066624c6SPrzemek Rudy 	struct snd_ctl_elem_info *uinfo)
1628066624c6SPrzemek Rudy {
1629066624c6SPrzemek Rudy 	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
1630066624c6SPrzemek Rudy 	uinfo->count = 1;
1631066624c6SPrzemek Rudy 	return 0;
1632066624c6SPrzemek Rudy }
1633066624c6SPrzemek Rudy 
snd_microii_spdif_default_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1634066624c6SPrzemek Rudy static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
1635066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1636066624c6SPrzemek Rudy {
1637288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1638288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1639066624c6SPrzemek Rudy 	int err;
1640066624c6SPrzemek Rudy 	struct usb_interface *iface;
1641066624c6SPrzemek Rudy 	struct usb_host_interface *alts;
1642066624c6SPrzemek Rudy 	unsigned int ep;
1643066624c6SPrzemek Rudy 	unsigned char data[3];
1644066624c6SPrzemek Rudy 	int rate;
1645066624c6SPrzemek Rudy 
164647ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
164747ab1545STakashi Iwai 	if (err < 0)
164847ab1545STakashi Iwai 		return err;
1649288673beSTakashi Iwai 
1650066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff;
1651066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff;
1652066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[2] = 0x00;
1653066624c6SPrzemek Rudy 
1654066624c6SPrzemek Rudy 	/* use known values for that card: interface#1 altsetting#1 */
1655288673beSTakashi Iwai 	iface = usb_ifnum_to_if(chip->dev, 1);
165659e1947cSXiyu Yang 	if (!iface || iface->num_altsetting < 2) {
165759e1947cSXiyu Yang 		err = -EINVAL;
165859e1947cSXiyu Yang 		goto end;
165959e1947cSXiyu Yang 	}
1660066624c6SPrzemek Rudy 	alts = &iface->altsetting[1];
166159e1947cSXiyu Yang 	if (get_iface_desc(alts)->bNumEndpoints < 1) {
166259e1947cSXiyu Yang 		err = -EINVAL;
166359e1947cSXiyu Yang 		goto end;
166459e1947cSXiyu Yang 	}
1665066624c6SPrzemek Rudy 	ep = get_endpoint(alts, 0)->bEndpointAddress;
1666066624c6SPrzemek Rudy 
1667288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1668288673beSTakashi Iwai 			usb_rcvctrlpipe(chip->dev, 0),
1669066624c6SPrzemek Rudy 			UAC_GET_CUR,
1670066624c6SPrzemek Rudy 			USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
1671066624c6SPrzemek Rudy 			UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
1672066624c6SPrzemek Rudy 			ep,
1673066624c6SPrzemek Rudy 			data,
1674066624c6SPrzemek Rudy 			sizeof(data));
1675066624c6SPrzemek Rudy 	if (err < 0)
1676066624c6SPrzemek Rudy 		goto end;
1677066624c6SPrzemek Rudy 
1678066624c6SPrzemek Rudy 	rate = data[0] | (data[1] << 8) | (data[2] << 16);
1679066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[3] = (rate == 48000) ?
1680066624c6SPrzemek Rudy 			IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100;
1681066624c6SPrzemek Rudy 
1682066624c6SPrzemek Rudy 	err = 0;
1683066624c6SPrzemek Rudy  end:
168447ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1685066624c6SPrzemek Rudy 	return err;
1686066624c6SPrzemek Rudy }
1687066624c6SPrzemek Rudy 
snd_microii_spdif_default_update(struct usb_mixer_elem_list * list)1688288673beSTakashi Iwai static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list)
1689066624c6SPrzemek Rudy {
1690288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1691288673beSTakashi Iwai 	unsigned int pval = list->kctl->private_value;
1692066624c6SPrzemek Rudy 	u8 reg;
1693288673beSTakashi Iwai 	int err;
1694066624c6SPrzemek Rudy 
169547ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
169647ab1545STakashi Iwai 	if (err < 0)
169747ab1545STakashi Iwai 		return err;
1698288673beSTakashi Iwai 
1699288673beSTakashi Iwai 	reg = ((pval >> 4) & 0xf0) | (pval & 0x0f);
1700288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1701288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1702066624c6SPrzemek Rudy 			UAC_SET_CUR,
1703066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1704066624c6SPrzemek Rudy 			reg,
1705066624c6SPrzemek Rudy 			2,
1706066624c6SPrzemek Rudy 			NULL,
1707066624c6SPrzemek Rudy 			0);
1708066624c6SPrzemek Rudy 	if (err < 0)
1709066624c6SPrzemek Rudy 		goto end;
1710066624c6SPrzemek Rudy 
1711288673beSTakashi Iwai 	reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20;
1712288673beSTakashi Iwai 	reg |= (pval >> 12) & 0x0f;
1713288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1714288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1715066624c6SPrzemek Rudy 			UAC_SET_CUR,
1716066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1717066624c6SPrzemek Rudy 			reg,
1718066624c6SPrzemek Rudy 			3,
1719066624c6SPrzemek Rudy 			NULL,
1720066624c6SPrzemek Rudy 			0);
1721066624c6SPrzemek Rudy 	if (err < 0)
1722066624c6SPrzemek Rudy 		goto end;
1723066624c6SPrzemek Rudy 
1724288673beSTakashi Iwai  end:
172547ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1726288673beSTakashi Iwai 	return err;
1727288673beSTakashi Iwai }
1728288673beSTakashi Iwai 
snd_microii_spdif_default_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1729288673beSTakashi Iwai static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol,
1730288673beSTakashi Iwai 	struct snd_ctl_elem_value *ucontrol)
1731288673beSTakashi Iwai {
1732288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1733288673beSTakashi Iwai 	unsigned int pval, pval_old;
1734288673beSTakashi Iwai 	int err;
1735288673beSTakashi Iwai 
1736288673beSTakashi Iwai 	pval = pval_old = kcontrol->private_value;
1737288673beSTakashi Iwai 	pval &= 0xfffff0f0;
1738288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8;
1739288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[0] & 0x0f);
1740288673beSTakashi Iwai 
1741288673beSTakashi Iwai 	pval &= 0xffff0fff;
1742288673beSTakashi Iwai 	pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8;
1743066624c6SPrzemek Rudy 
1744066624c6SPrzemek Rudy 	/* The frequency bits in AES3 cannot be set via register access. */
1745066624c6SPrzemek Rudy 
1746066624c6SPrzemek Rudy 	/* Silently ignore any bits from the request that cannot be set. */
1747066624c6SPrzemek Rudy 
1748288673beSTakashi Iwai 	if (pval == pval_old)
1749288673beSTakashi Iwai 		return 0;
1750288673beSTakashi Iwai 
1751288673beSTakashi Iwai 	kcontrol->private_value = pval;
1752288673beSTakashi Iwai 	err = snd_microii_spdif_default_update(list);
1753288673beSTakashi Iwai 	return err < 0 ? err : 1;
1754066624c6SPrzemek Rudy }
1755066624c6SPrzemek Rudy 
snd_microii_spdif_mask_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1756066624c6SPrzemek Rudy static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol,
1757066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1758066624c6SPrzemek Rudy {
1759066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[0] = 0x0f;
1760066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[1] = 0xff;
1761066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[2] = 0x00;
1762066624c6SPrzemek Rudy 	ucontrol->value.iec958.status[3] = 0x00;
1763066624c6SPrzemek Rudy 
1764066624c6SPrzemek Rudy 	return 0;
1765066624c6SPrzemek Rudy }
1766066624c6SPrzemek Rudy 
snd_microii_spdif_switch_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1767066624c6SPrzemek Rudy static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol,
1768066624c6SPrzemek Rudy 	struct snd_ctl_elem_value *ucontrol)
1769066624c6SPrzemek Rudy {
1770066624c6SPrzemek Rudy 	ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02);
1771066624c6SPrzemek Rudy 
1772066624c6SPrzemek Rudy 	return 0;
1773066624c6SPrzemek Rudy }
1774066624c6SPrzemek Rudy 
snd_microii_spdif_switch_update(struct usb_mixer_elem_list * list)1775288673beSTakashi Iwai static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list)
1776066624c6SPrzemek Rudy {
1777288673beSTakashi Iwai 	struct snd_usb_audio *chip = list->mixer->chip;
1778288673beSTakashi Iwai 	u8 reg = list->kctl->private_value;
1779066624c6SPrzemek Rudy 	int err;
1780066624c6SPrzemek Rudy 
178147ab1545STakashi Iwai 	err = snd_usb_lock_shutdown(chip);
178247ab1545STakashi Iwai 	if (err < 0)
178347ab1545STakashi Iwai 		return err;
1784288673beSTakashi Iwai 
1785288673beSTakashi Iwai 	err = snd_usb_ctl_msg(chip->dev,
1786288673beSTakashi Iwai 			usb_sndctrlpipe(chip->dev, 0),
1787066624c6SPrzemek Rudy 			UAC_SET_CUR,
1788066624c6SPrzemek Rudy 			USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1789066624c6SPrzemek Rudy 			reg,
1790066624c6SPrzemek Rudy 			9,
1791066624c6SPrzemek Rudy 			NULL,
1792066624c6SPrzemek Rudy 			0);
1793066624c6SPrzemek Rudy 
179447ab1545STakashi Iwai 	snd_usb_unlock_shutdown(chip);
1795288673beSTakashi Iwai 	return err;
1796066624c6SPrzemek Rudy }
1797066624c6SPrzemek Rudy 
snd_microii_spdif_switch_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1798288673beSTakashi Iwai static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol,
1799288673beSTakashi Iwai 	struct snd_ctl_elem_value *ucontrol)
1800288673beSTakashi Iwai {
1801288673beSTakashi Iwai 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1802288673beSTakashi Iwai 	u8 reg;
1803288673beSTakashi Iwai 	int err;
1804288673beSTakashi Iwai 
1805288673beSTakashi Iwai 	reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a;
1806288673beSTakashi Iwai 	if (reg != list->kctl->private_value)
1807288673beSTakashi Iwai 		return 0;
1808288673beSTakashi Iwai 
1809288673beSTakashi Iwai 	kcontrol->private_value = reg;
1810288673beSTakashi Iwai 	err = snd_microii_spdif_switch_update(list);
1811288673beSTakashi Iwai 	return err < 0 ? err : 1;
1812066624c6SPrzemek Rudy }
1813066624c6SPrzemek Rudy 
1814195727e8STakashi Iwai static const struct snd_kcontrol_new snd_microii_mixer_spdif[] = {
1815066624c6SPrzemek Rudy 	{
1816066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_PCM,
1817066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
1818066624c6SPrzemek Rudy 		.info =     snd_microii_spdif_info,
1819066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_default_get,
1820066624c6SPrzemek Rudy 		.put =      snd_microii_spdif_default_put,
1821066624c6SPrzemek Rudy 		.private_value = 0x00000100UL,/* reset value */
1822066624c6SPrzemek Rudy 	},
1823066624c6SPrzemek Rudy 	{
1824066624c6SPrzemek Rudy 		.access =   SNDRV_CTL_ELEM_ACCESS_READ,
1825066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_PCM,
1826066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
1827066624c6SPrzemek Rudy 		.info =     snd_microii_spdif_info,
1828066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_mask_get,
1829066624c6SPrzemek Rudy 	},
1830066624c6SPrzemek Rudy 	{
1831066624c6SPrzemek Rudy 		.iface =    SNDRV_CTL_ELEM_IFACE_MIXER,
1832066624c6SPrzemek Rudy 		.name =     SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
1833066624c6SPrzemek Rudy 		.info =     snd_ctl_boolean_mono_info,
1834066624c6SPrzemek Rudy 		.get =      snd_microii_spdif_switch_get,
1835066624c6SPrzemek Rudy 		.put =      snd_microii_spdif_switch_put,
1836066624c6SPrzemek Rudy 		.private_value = 0x00000028UL,/* reset value */
1837066624c6SPrzemek Rudy 	}
1838066624c6SPrzemek Rudy };
1839066624c6SPrzemek Rudy 
snd_microii_controls_create(struct usb_mixer_interface * mixer)1840066624c6SPrzemek Rudy static int snd_microii_controls_create(struct usb_mixer_interface *mixer)
1841066624c6SPrzemek Rudy {
1842066624c6SPrzemek Rudy 	int err, i;
1843ff40e0d4SPierre-Louis Bossart 	static const usb_mixer_elem_resume_func_t resume_funcs[] = {
1844288673beSTakashi Iwai 		snd_microii_spdif_default_update,
1845288673beSTakashi Iwai 		NULL,
1846288673beSTakashi Iwai 		snd_microii_spdif_switch_update
1847288673beSTakashi Iwai 	};
1848066624c6SPrzemek Rudy 
1849066624c6SPrzemek Rudy 	for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) {
1850288673beSTakashi Iwai 		err = add_single_ctl_with_resume(mixer, 0,
1851288673beSTakashi Iwai 						 resume_funcs[i],
1852288673beSTakashi Iwai 						 &snd_microii_mixer_spdif[i],
1853288673beSTakashi Iwai 						 NULL);
1854066624c6SPrzemek Rudy 		if (err < 0)
1855066624c6SPrzemek Rudy 			return err;
1856066624c6SPrzemek Rudy 	}
1857066624c6SPrzemek Rudy 
185818e4753fSMikulas Patocka 	return 0;
1859066624c6SPrzemek Rudy }
1860066624c6SPrzemek Rudy 
1861388fdb8fSIan Douglas Scott /* Creative Sound Blaster E1 */
1862388fdb8fSIan Douglas Scott 
snd_soundblaster_e1_switch_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1863388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_get(struct snd_kcontrol *kcontrol,
1864388fdb8fSIan Douglas Scott 					  struct snd_ctl_elem_value *ucontrol)
1865388fdb8fSIan Douglas Scott {
1866388fdb8fSIan Douglas Scott 	ucontrol->value.integer.value[0] = kcontrol->private_value;
1867388fdb8fSIan Douglas Scott 	return 0;
1868388fdb8fSIan Douglas Scott }
1869388fdb8fSIan Douglas Scott 
snd_soundblaster_e1_switch_update(struct usb_mixer_interface * mixer,unsigned char state)1870388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_update(struct usb_mixer_interface *mixer,
1871388fdb8fSIan Douglas Scott 					     unsigned char state)
1872388fdb8fSIan Douglas Scott {
1873388fdb8fSIan Douglas Scott 	struct snd_usb_audio *chip = mixer->chip;
1874388fdb8fSIan Douglas Scott 	int err;
1875388fdb8fSIan Douglas Scott 	unsigned char buff[2];
1876388fdb8fSIan Douglas Scott 
1877388fdb8fSIan Douglas Scott 	buff[0] = 0x02;
1878388fdb8fSIan Douglas Scott 	buff[1] = state ? 0x02 : 0x00;
1879388fdb8fSIan Douglas Scott 
1880388fdb8fSIan Douglas Scott 	err = snd_usb_lock_shutdown(chip);
1881388fdb8fSIan Douglas Scott 	if (err < 0)
1882388fdb8fSIan Douglas Scott 		return err;
1883388fdb8fSIan Douglas Scott 	err = snd_usb_ctl_msg(chip->dev,
1884388fdb8fSIan Douglas Scott 			usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT,
1885388fdb8fSIan Douglas Scott 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
1886388fdb8fSIan Douglas Scott 			0x0202, 3, buff, 2);
1887388fdb8fSIan Douglas Scott 	snd_usb_unlock_shutdown(chip);
1888388fdb8fSIan Douglas Scott 	return err;
1889388fdb8fSIan Douglas Scott }
1890388fdb8fSIan Douglas Scott 
snd_soundblaster_e1_switch_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1891388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
1892388fdb8fSIan Douglas Scott 					  struct snd_ctl_elem_value *ucontrol)
1893388fdb8fSIan Douglas Scott {
1894388fdb8fSIan Douglas Scott 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
1895388fdb8fSIan Douglas Scott 	unsigned char value = !!ucontrol->value.integer.value[0];
1896388fdb8fSIan Douglas Scott 	int err;
1897388fdb8fSIan Douglas Scott 
1898388fdb8fSIan Douglas Scott 	if (kcontrol->private_value == value)
1899388fdb8fSIan Douglas Scott 		return 0;
1900388fdb8fSIan Douglas Scott 	kcontrol->private_value = value;
1901388fdb8fSIan Douglas Scott 	err = snd_soundblaster_e1_switch_update(list->mixer, value);
1902388fdb8fSIan Douglas Scott 	return err < 0 ? err : 1;
1903388fdb8fSIan Douglas Scott }
1904388fdb8fSIan Douglas Scott 
snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list * list)1905388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list)
1906388fdb8fSIan Douglas Scott {
1907388fdb8fSIan Douglas Scott 	return snd_soundblaster_e1_switch_update(list->mixer,
1908388fdb8fSIan Douglas Scott 						 list->kctl->private_value);
1909388fdb8fSIan Douglas Scott }
1910388fdb8fSIan Douglas Scott 
snd_soundblaster_e1_switch_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)1911388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_info(struct snd_kcontrol *kcontrol,
1912388fdb8fSIan Douglas Scott 					   struct snd_ctl_elem_info *uinfo)
1913388fdb8fSIan Douglas Scott {
1914388fdb8fSIan Douglas Scott 	static const char *const texts[2] = {
1915388fdb8fSIan Douglas Scott 		"Mic", "Aux"
1916388fdb8fSIan Douglas Scott 	};
1917388fdb8fSIan Douglas Scott 
1918388fdb8fSIan Douglas Scott 	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
1919388fdb8fSIan Douglas Scott }
1920388fdb8fSIan Douglas Scott 
1921195727e8STakashi Iwai static const struct snd_kcontrol_new snd_soundblaster_e1_input_switch = {
1922388fdb8fSIan Douglas Scott 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1923388fdb8fSIan Douglas Scott 	.name = "Input Source",
1924388fdb8fSIan Douglas Scott 	.info = snd_soundblaster_e1_switch_info,
1925388fdb8fSIan Douglas Scott 	.get = snd_soundblaster_e1_switch_get,
1926388fdb8fSIan Douglas Scott 	.put = snd_soundblaster_e1_switch_put,
1927388fdb8fSIan Douglas Scott 	.private_value = 0,
1928388fdb8fSIan Douglas Scott };
1929388fdb8fSIan Douglas Scott 
snd_soundblaster_e1_switch_create(struct usb_mixer_interface * mixer)1930388fdb8fSIan Douglas Scott static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer)
1931388fdb8fSIan Douglas Scott {
1932388fdb8fSIan Douglas Scott 	return add_single_ctl_with_resume(mixer, 0,
1933388fdb8fSIan Douglas Scott 					  snd_soundblaster_e1_switch_resume,
1934388fdb8fSIan Douglas Scott 					  &snd_soundblaster_e1_input_switch,
1935388fdb8fSIan Douglas Scott 					  NULL);
1936388fdb8fSIan Douglas Scott }
1937388fdb8fSIan Douglas Scott 
19384b8ea38fSJan Schär /*
19394b8ea38fSJan Schär  * Dell WD15 dock jack detection
19404b8ea38fSJan Schär  *
19414b8ea38fSJan Schär  * The WD15 contains an ALC4020 USB audio controller and ALC3263 audio codec
19424b8ea38fSJan Schär  * from Realtek. It is a UAC 1 device, and UAC 1 does not support jack
19434b8ea38fSJan Schär  * detection. Instead, jack detection works by sending HD Audio commands over
19444b8ea38fSJan Schär  * vendor-type USB messages.
19454b8ea38fSJan Schär  */
19464b8ea38fSJan Schär 
19474b8ea38fSJan Schär #define HDA_VERB_CMD(V, N, D) (((N) << 20) | ((V) << 8) | (D))
19484b8ea38fSJan Schär 
19494b8ea38fSJan Schär #define REALTEK_HDA_VALUE 0x0038
19504b8ea38fSJan Schär 
19514b8ea38fSJan Schär #define REALTEK_HDA_SET		62
19522e57a335SJan Schär #define REALTEK_MANUAL_MODE	72
19534b8ea38fSJan Schär #define REALTEK_HDA_GET_OUT	88
19544b8ea38fSJan Schär #define REALTEK_HDA_GET_IN	89
19554b8ea38fSJan Schär 
19562e57a335SJan Schär #define REALTEK_AUDIO_FUNCTION_GROUP	0x01
19574b8ea38fSJan Schär #define REALTEK_LINE1			0x1a
19584b8ea38fSJan Schär #define REALTEK_VENDOR_REGISTERS	0x20
19594b8ea38fSJan Schär #define REALTEK_HP_OUT			0x21
19604b8ea38fSJan Schär 
19614b8ea38fSJan Schär #define REALTEK_CBJ_CTRL2 0x50
19624b8ea38fSJan Schär 
19634b8ea38fSJan Schär #define REALTEK_JACK_INTERRUPT_NODE 5
19644b8ea38fSJan Schär 
19654b8ea38fSJan Schär #define REALTEK_MIC_FLAG 0x100
19664b8ea38fSJan Schär 
realtek_hda_set(struct snd_usb_audio * chip,u32 cmd)19674b8ea38fSJan Schär static int realtek_hda_set(struct snd_usb_audio *chip, u32 cmd)
19684b8ea38fSJan Schär {
19694b8ea38fSJan Schär 	struct usb_device *dev = chip->dev;
197061c606a4SJan Schär 	__be32 buf = cpu_to_be32(cmd);
19714b8ea38fSJan Schär 
19724b8ea38fSJan Schär 	return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), REALTEK_HDA_SET,
19734b8ea38fSJan Schär 			       USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
19744b8ea38fSJan Schär 			       REALTEK_HDA_VALUE, 0, &buf, sizeof(buf));
19754b8ea38fSJan Schär }
19764b8ea38fSJan Schär 
realtek_hda_get(struct snd_usb_audio * chip,u32 cmd,u32 * value)19774b8ea38fSJan Schär static int realtek_hda_get(struct snd_usb_audio *chip, u32 cmd, u32 *value)
19784b8ea38fSJan Schär {
19794b8ea38fSJan Schär 	struct usb_device *dev = chip->dev;
19804b8ea38fSJan Schär 	int err;
198161c606a4SJan Schär 	__be32 buf = cpu_to_be32(cmd);
19824b8ea38fSJan Schär 
19834b8ea38fSJan Schär 	err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), REALTEK_HDA_GET_OUT,
19844b8ea38fSJan Schär 			      USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
19854b8ea38fSJan Schär 			      REALTEK_HDA_VALUE, 0, &buf, sizeof(buf));
19864b8ea38fSJan Schär 	if (err < 0)
19874b8ea38fSJan Schär 		return err;
19884b8ea38fSJan Schär 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), REALTEK_HDA_GET_IN,
19894b8ea38fSJan Schär 			      USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_IN,
19904b8ea38fSJan Schär 			      REALTEK_HDA_VALUE, 0, &buf, sizeof(buf));
19914b8ea38fSJan Schär 	if (err < 0)
19924b8ea38fSJan Schär 		return err;
19934b8ea38fSJan Schär 
19944b8ea38fSJan Schär 	*value = be32_to_cpu(buf);
19954b8ea38fSJan Schär 	return 0;
19964b8ea38fSJan Schär }
19974b8ea38fSJan Schär 
realtek_ctl_connector_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)19984b8ea38fSJan Schär static int realtek_ctl_connector_get(struct snd_kcontrol *kcontrol,
19994b8ea38fSJan Schär 				     struct snd_ctl_elem_value *ucontrol)
20004b8ea38fSJan Schär {
20014b8ea38fSJan Schär 	struct usb_mixer_elem_info *cval = kcontrol->private_data;
20024b8ea38fSJan Schär 	struct snd_usb_audio *chip = cval->head.mixer->chip;
20034b8ea38fSJan Schär 	u32 pv = kcontrol->private_value;
20044b8ea38fSJan Schär 	u32 node_id = pv & 0xff;
20054b8ea38fSJan Schär 	u32 sense;
20064b8ea38fSJan Schär 	u32 cbj_ctrl2;
20074b8ea38fSJan Schär 	bool presence;
20084b8ea38fSJan Schär 	int err;
20094b8ea38fSJan Schär 
20104b8ea38fSJan Schär 	err = snd_usb_lock_shutdown(chip);
20114b8ea38fSJan Schär 	if (err < 0)
20124b8ea38fSJan Schär 		return err;
20134b8ea38fSJan Schär 	err = realtek_hda_get(chip,
20144b8ea38fSJan Schär 			      HDA_VERB_CMD(AC_VERB_GET_PIN_SENSE, node_id, 0),
20154b8ea38fSJan Schär 			      &sense);
20164b8ea38fSJan Schär 	if (err < 0)
20174b8ea38fSJan Schär 		goto err;
20184b8ea38fSJan Schär 	if (pv & REALTEK_MIC_FLAG) {
20194b8ea38fSJan Schär 		err = realtek_hda_set(chip,
20204b8ea38fSJan Schär 				      HDA_VERB_CMD(AC_VERB_SET_COEF_INDEX,
20214b8ea38fSJan Schär 						   REALTEK_VENDOR_REGISTERS,
20224b8ea38fSJan Schär 						   REALTEK_CBJ_CTRL2));
20234b8ea38fSJan Schär 		if (err < 0)
20244b8ea38fSJan Schär 			goto err;
20254b8ea38fSJan Schär 		err = realtek_hda_get(chip,
20264b8ea38fSJan Schär 				      HDA_VERB_CMD(AC_VERB_GET_PROC_COEF,
20274b8ea38fSJan Schär 						   REALTEK_VENDOR_REGISTERS, 0),
20284b8ea38fSJan Schär 				      &cbj_ctrl2);
20294b8ea38fSJan Schär 		if (err < 0)
20304b8ea38fSJan Schär 			goto err;
20314b8ea38fSJan Schär 	}
20324b8ea38fSJan Schär err:
20334b8ea38fSJan Schär 	snd_usb_unlock_shutdown(chip);
20344b8ea38fSJan Schär 	if (err < 0)
20354b8ea38fSJan Schär 		return err;
20364b8ea38fSJan Schär 
20374b8ea38fSJan Schär 	presence = sense & AC_PINSENSE_PRESENCE;
20384b8ea38fSJan Schär 	if (pv & REALTEK_MIC_FLAG)
20394b8ea38fSJan Schär 		presence = presence && (cbj_ctrl2 & 0x0070) == 0x0070;
20404b8ea38fSJan Schär 	ucontrol->value.integer.value[0] = presence;
20414b8ea38fSJan Schär 	return 0;
20424b8ea38fSJan Schär }
20434b8ea38fSJan Schär 
20444b8ea38fSJan Schär static const struct snd_kcontrol_new realtek_connector_ctl_ro = {
20454b8ea38fSJan Schär 	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
20464b8ea38fSJan Schär 	.name = "", /* will be filled later manually */
20474b8ea38fSJan Schär 	.access = SNDRV_CTL_ELEM_ACCESS_READ,
20484b8ea38fSJan Schär 	.info = snd_ctl_boolean_mono_info,
20494b8ea38fSJan Schär 	.get = realtek_ctl_connector_get,
20504b8ea38fSJan Schär };
20514b8ea38fSJan Schär 
realtek_resume_jack(struct usb_mixer_elem_list * list)20524b8ea38fSJan Schär static int realtek_resume_jack(struct usb_mixer_elem_list *list)
20534b8ea38fSJan Schär {
20544b8ea38fSJan Schär 	snd_ctl_notify(list->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
20554b8ea38fSJan Schär 		       &list->kctl->id);
20564b8ea38fSJan Schär 	return 0;
20574b8ea38fSJan Schär }
20584b8ea38fSJan Schär 
realtek_add_jack(struct usb_mixer_interface * mixer,char * name,u32 val)20594b8ea38fSJan Schär static int realtek_add_jack(struct usb_mixer_interface *mixer,
20604b8ea38fSJan Schär 			    char *name, u32 val)
20614b8ea38fSJan Schär {
20624b8ea38fSJan Schär 	struct usb_mixer_elem_info *cval;
20634b8ea38fSJan Schär 	struct snd_kcontrol *kctl;
20644b8ea38fSJan Schär 
20654b8ea38fSJan Schär 	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
20664b8ea38fSJan Schär 	if (!cval)
20674b8ea38fSJan Schär 		return -ENOMEM;
20684b8ea38fSJan Schär 	snd_usb_mixer_elem_init_std(&cval->head, mixer,
20694b8ea38fSJan Schär 				    REALTEK_JACK_INTERRUPT_NODE);
20704b8ea38fSJan Schär 	cval->head.resume = realtek_resume_jack;
20714b8ea38fSJan Schär 	cval->val_type = USB_MIXER_BOOLEAN;
20724b8ea38fSJan Schär 	cval->channels = 1;
20734b8ea38fSJan Schär 	cval->min = 0;
20744b8ea38fSJan Schär 	cval->max = 1;
20754b8ea38fSJan Schär 	kctl = snd_ctl_new1(&realtek_connector_ctl_ro, cval);
20764b8ea38fSJan Schär 	if (!kctl) {
20774b8ea38fSJan Schär 		kfree(cval);
20784b8ea38fSJan Schär 		return -ENOMEM;
20794b8ea38fSJan Schär 	}
20804b8ea38fSJan Schär 	kctl->private_value = val;
20814b8ea38fSJan Schär 	strscpy(kctl->id.name, name, sizeof(kctl->id.name));
20824b8ea38fSJan Schär 	kctl->private_free = snd_usb_mixer_elem_free;
20834b8ea38fSJan Schär 	return snd_usb_mixer_add_control(&cval->head, kctl);
20844b8ea38fSJan Schär }
20854b8ea38fSJan Schär 
dell_dock_mixer_create(struct usb_mixer_interface * mixer)20864b8ea38fSJan Schär static int dell_dock_mixer_create(struct usb_mixer_interface *mixer)
20874b8ea38fSJan Schär {
20884b8ea38fSJan Schär 	int err;
20892e57a335SJan Schär 	struct usb_device *dev = mixer->chip->dev;
20902e57a335SJan Schär 
20912e57a335SJan Schär 	/* Power down the audio codec to avoid loud pops in the next step. */
20922e57a335SJan Schär 	realtek_hda_set(mixer->chip,
20932e57a335SJan Schär 			HDA_VERB_CMD(AC_VERB_SET_POWER_STATE,
20942e57a335SJan Schär 				     REALTEK_AUDIO_FUNCTION_GROUP,
20952e57a335SJan Schär 				     AC_PWRST_D3));
20962e57a335SJan Schär 
20972e57a335SJan Schär 	/*
20982e57a335SJan Schär 	 * Turn off 'manual mode' in case it was enabled. This removes the need
20992e57a335SJan Schär 	 * to power cycle the dock after it was attached to a Windows machine.
21002e57a335SJan Schär 	 */
21012e57a335SJan Schär 	snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), REALTEK_MANUAL_MODE,
21022e57a335SJan Schär 			USB_RECIP_DEVICE | USB_TYPE_VENDOR | USB_DIR_OUT,
21032e57a335SJan Schär 			0, 0, NULL, 0);
21044b8ea38fSJan Schär 
21054b8ea38fSJan Schär 	err = realtek_add_jack(mixer, "Line Out Jack", REALTEK_LINE1);
21064b8ea38fSJan Schär 	if (err < 0)
21074b8ea38fSJan Schär 		return err;
21084b8ea38fSJan Schär 	err = realtek_add_jack(mixer, "Headphone Jack", REALTEK_HP_OUT);
21094b8ea38fSJan Schär 	if (err < 0)
21104b8ea38fSJan Schär 		return err;
21114b8ea38fSJan Schär 	err = realtek_add_jack(mixer, "Headset Mic Jack",
21124b8ea38fSJan Schär 			       REALTEK_HP_OUT | REALTEK_MIC_FLAG);
21134b8ea38fSJan Schär 	if (err < 0)
21144b8ea38fSJan Schär 		return err;
21154b8ea38fSJan Schär 	return 0;
21164b8ea38fSJan Schär }
21174b8ea38fSJan Schär 
dell_dock_init_vol(struct snd_usb_audio * chip,int ch,int id)2118964af639STakashi Iwai static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id)
2119964af639STakashi Iwai {
2120964af639STakashi Iwai 	u16 buf = 0;
2121964af639STakashi Iwai 
2122964af639STakashi Iwai 	snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
2123964af639STakashi Iwai 			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
21242e57a335SJan Schär 			(UAC_FU_VOLUME << 8) | ch,
21252e57a335SJan Schär 			snd_usb_ctrl_intf(chip) | (id << 8),
2126964af639STakashi Iwai 			&buf, 2);
2127964af639STakashi Iwai }
2128964af639STakashi Iwai 
dell_dock_mixer_init(struct usb_mixer_interface * mixer)2129964af639STakashi Iwai static int dell_dock_mixer_init(struct usb_mixer_interface *mixer)
2130964af639STakashi Iwai {
2131964af639STakashi Iwai 	/* fix to 0dB playback volumes */
2132964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 1, 16);
2133964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 2, 16);
2134964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 1, 19);
2135964af639STakashi Iwai 	dell_dock_init_vol(mixer->chip, 2, 19);
2136964af639STakashi Iwai 	return 0;
2137964af639STakashi Iwai }
2138964af639STakashi Iwai 
2139d39f1d68SJussi Laako /* RME Class Compliant device quirks */
2140d39f1d68SJussi Laako 
2141d39f1d68SJussi Laako #define SND_RME_GET_STATUS1			23
2142d39f1d68SJussi Laako #define SND_RME_GET_CURRENT_FREQ		17
2143d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM_SHIFT		16
2144d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM_MASK			0x1f
2145d39f1d68SJussi Laako #define SND_RME_CLK_AES_SHIFT			8
2146d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_SHIFT			12
2147d39f1d68SJussi Laako #define SND_RME_CLK_AES_SPDIF_MASK		0xf
2148d39f1d68SJussi Laako #define SND_RME_CLK_SYNC_SHIFT			6
2149d39f1d68SJussi Laako #define SND_RME_CLK_SYNC_MASK			0x3
2150d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL_SHIFT		18
2151d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL_MASK		0x7
2152d39f1d68SJussi Laako #define SND_RME_CLK_SYSTEM(x) \
2153d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK)
2154d39f1d68SJussi Laako #define SND_RME_CLK_AES(x) \
2155d39f1d68SJussi Laako 	((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
2156d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF(x) \
2157d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK)
2158d39f1d68SJussi Laako #define SND_RME_CLK_SYNC(x) \
2159d39f1d68SJussi Laako 	((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK)
2160d39f1d68SJussi Laako #define SND_RME_CLK_FREQMUL(x) \
2161d39f1d68SJussi Laako 	((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK)
2162d39f1d68SJussi Laako #define SND_RME_CLK_AES_LOCK			0x1
2163d39f1d68SJussi Laako #define SND_RME_CLK_AES_SYNC			0x4
2164d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_LOCK			0x2
2165d39f1d68SJussi Laako #define SND_RME_CLK_SPDIF_SYNC			0x8
2166d39f1d68SJussi Laako #define SND_RME_SPDIF_IF_SHIFT			4
2167d39f1d68SJussi Laako #define SND_RME_SPDIF_FORMAT_SHIFT		5
2168d39f1d68SJussi Laako #define SND_RME_BINARY_MASK			0x1
2169d39f1d68SJussi Laako #define SND_RME_SPDIF_IF(x) \
2170d39f1d68SJussi Laako 	((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK)
2171d39f1d68SJussi Laako #define SND_RME_SPDIF_FORMAT(x) \
2172d39f1d68SJussi Laako 	((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK)
2173d39f1d68SJussi Laako 
2174d39f1d68SJussi Laako static const u32 snd_rme_rate_table[] = {
2175d39f1d68SJussi Laako 	32000, 44100, 48000, 50000,
2176d39f1d68SJussi Laako 	64000, 88200, 96000, 100000,
2177d39f1d68SJussi Laako 	128000, 176400, 192000, 200000,
2178d39f1d68SJussi Laako 	256000,	352800, 384000, 400000,
2179d39f1d68SJussi Laako 	512000, 705600, 768000, 800000
2180d39f1d68SJussi Laako };
2181d39f1d68SJussi Laako /* maximum number of items for AES and S/PDIF rates for above table */
2182d39f1d68SJussi Laako #define SND_RME_RATE_IDX_AES_SPDIF_NUM		12
2183d39f1d68SJussi Laako 
2184d39f1d68SJussi Laako enum snd_rme_domain {
2185d39f1d68SJussi Laako 	SND_RME_DOMAIN_SYSTEM,
2186d39f1d68SJussi Laako 	SND_RME_DOMAIN_AES,
2187d39f1d68SJussi Laako 	SND_RME_DOMAIN_SPDIF
2188d39f1d68SJussi Laako };
2189d39f1d68SJussi Laako 
2190d39f1d68SJussi Laako enum snd_rme_clock_status {
2191d39f1d68SJussi Laako 	SND_RME_CLOCK_NOLOCK,
2192d39f1d68SJussi Laako 	SND_RME_CLOCK_LOCK,
2193d39f1d68SJussi Laako 	SND_RME_CLOCK_SYNC
2194d39f1d68SJussi Laako };
2195d39f1d68SJussi Laako 
snd_rme_read_value(struct snd_usb_audio * chip,unsigned int item,u32 * value)2196d39f1d68SJussi Laako static int snd_rme_read_value(struct snd_usb_audio *chip,
2197d39f1d68SJussi Laako 			      unsigned int item,
2198d39f1d68SJussi Laako 			      u32 *value)
2199d39f1d68SJussi Laako {
2200d39f1d68SJussi Laako 	struct usb_device *dev = chip->dev;
2201d39f1d68SJussi Laako 	int err;
2202d39f1d68SJussi Laako 
2203d39f1d68SJussi Laako 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
2204d39f1d68SJussi Laako 			      item,
2205d39f1d68SJussi Laako 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2206d39f1d68SJussi Laako 			      0, 0,
2207d39f1d68SJussi Laako 			      value, sizeof(*value));
2208d39f1d68SJussi Laako 	if (err < 0)
2209d39f1d68SJussi Laako 		dev_err(&dev->dev,
2210d39f1d68SJussi Laako 			"unable to issue vendor read request %d (ret = %d)",
2211d39f1d68SJussi Laako 			item, err);
2212d39f1d68SJussi Laako 	return err;
2213d39f1d68SJussi Laako }
2214d39f1d68SJussi Laako 
snd_rme_get_status1(struct snd_kcontrol * kcontrol,u32 * status1)2215d39f1d68SJussi Laako static int snd_rme_get_status1(struct snd_kcontrol *kcontrol,
2216d39f1d68SJussi Laako 			       u32 *status1)
2217d39f1d68SJussi Laako {
2218d39f1d68SJussi Laako 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
2219d39f1d68SJussi Laako 	struct snd_usb_audio *chip = list->mixer->chip;
2220d39f1d68SJussi Laako 	int err;
2221d39f1d68SJussi Laako 
2222d39f1d68SJussi Laako 	err = snd_usb_lock_shutdown(chip);
2223d39f1d68SJussi Laako 	if (err < 0)
2224d39f1d68SJussi Laako 		return err;
2225d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, status1);
2226d39f1d68SJussi Laako 	snd_usb_unlock_shutdown(chip);
2227d39f1d68SJussi Laako 	return err;
2228d39f1d68SJussi Laako }
2229d39f1d68SJussi Laako 
snd_rme_rate_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2230d39f1d68SJussi Laako static int snd_rme_rate_get(struct snd_kcontrol *kcontrol,
2231d39f1d68SJussi Laako 			    struct snd_ctl_elem_value *ucontrol)
2232d39f1d68SJussi Laako {
2233d39f1d68SJussi Laako 	u32 status1;
2234d39f1d68SJussi Laako 	u32 rate = 0;
2235d39f1d68SJussi Laako 	int idx;
2236d39f1d68SJussi Laako 	int err;
2237d39f1d68SJussi Laako 
2238d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2239d39f1d68SJussi Laako 	if (err < 0)
2240d39f1d68SJussi Laako 		return err;
2241d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2242d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SYSTEM:
2243d39f1d68SJussi Laako 		idx = SND_RME_CLK_SYSTEM(status1);
2244d39f1d68SJussi Laako 		if (idx < ARRAY_SIZE(snd_rme_rate_table))
2245d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
2246d39f1d68SJussi Laako 		break;
2247d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:
2248d39f1d68SJussi Laako 		idx = SND_RME_CLK_AES(status1);
2249d39f1d68SJussi Laako 		if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
2250d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
2251d39f1d68SJussi Laako 		break;
2252d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:
2253d39f1d68SJussi Laako 		idx = SND_RME_CLK_SPDIF(status1);
2254d39f1d68SJussi Laako 		if (idx < SND_RME_RATE_IDX_AES_SPDIF_NUM)
2255d39f1d68SJussi Laako 			rate = snd_rme_rate_table[idx];
2256d39f1d68SJussi Laako 		break;
2257d39f1d68SJussi Laako 	default:
2258d39f1d68SJussi Laako 		return -EINVAL;
2259d39f1d68SJussi Laako 	}
2260d39f1d68SJussi Laako 	ucontrol->value.integer.value[0] = rate;
2261d39f1d68SJussi Laako 	return 0;
2262d39f1d68SJussi Laako }
2263d39f1d68SJussi Laako 
snd_rme_sync_state_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2264d39f1d68SJussi Laako static int snd_rme_sync_state_get(struct snd_kcontrol *kcontrol,
2265d39f1d68SJussi Laako 				  struct snd_ctl_elem_value *ucontrol)
2266d39f1d68SJussi Laako {
2267d39f1d68SJussi Laako 	u32 status1;
2268d39f1d68SJussi Laako 	int idx = SND_RME_CLOCK_NOLOCK;
2269d39f1d68SJussi Laako 	int err;
2270d39f1d68SJussi Laako 
2271d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2272d39f1d68SJussi Laako 	if (err < 0)
2273d39f1d68SJussi Laako 		return err;
2274d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2275d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:  /* AES */
2276d39f1d68SJussi Laako 		if (status1 & SND_RME_CLK_AES_SYNC)
2277d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_SYNC;
2278d39f1d68SJussi Laako 		else if (status1 & SND_RME_CLK_AES_LOCK)
2279d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_LOCK;
2280d39f1d68SJussi Laako 		break;
2281d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:  /* SPDIF */
2282d39f1d68SJussi Laako 		if (status1 & SND_RME_CLK_SPDIF_SYNC)
2283d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_SYNC;
2284d39f1d68SJussi Laako 		else if (status1 & SND_RME_CLK_SPDIF_LOCK)
2285d39f1d68SJussi Laako 			idx = SND_RME_CLOCK_LOCK;
2286d39f1d68SJussi Laako 		break;
2287d39f1d68SJussi Laako 	default:
2288d39f1d68SJussi Laako 		return -EINVAL;
2289d39f1d68SJussi Laako 	}
2290d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = idx;
2291d39f1d68SJussi Laako 	return 0;
2292d39f1d68SJussi Laako }
2293d39f1d68SJussi Laako 
snd_rme_spdif_if_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2294d39f1d68SJussi Laako static int snd_rme_spdif_if_get(struct snd_kcontrol *kcontrol,
2295d39f1d68SJussi Laako 				struct snd_ctl_elem_value *ucontrol)
2296d39f1d68SJussi Laako {
2297d39f1d68SJussi Laako 	u32 status1;
2298d39f1d68SJussi Laako 	int err;
2299d39f1d68SJussi Laako 
2300d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2301d39f1d68SJussi Laako 	if (err < 0)
2302d39f1d68SJussi Laako 		return err;
2303d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_IF(status1);
2304d39f1d68SJussi Laako 	return 0;
2305d39f1d68SJussi Laako }
2306d39f1d68SJussi Laako 
snd_rme_spdif_format_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2307d39f1d68SJussi Laako static int snd_rme_spdif_format_get(struct snd_kcontrol *kcontrol,
2308d39f1d68SJussi Laako 				    struct snd_ctl_elem_value *ucontrol)
2309d39f1d68SJussi Laako {
2310d39f1d68SJussi Laako 	u32 status1;
2311d39f1d68SJussi Laako 	int err;
2312d39f1d68SJussi Laako 
2313d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2314d39f1d68SJussi Laako 	if (err < 0)
2315d39f1d68SJussi Laako 		return err;
2316d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_SPDIF_FORMAT(status1);
2317d39f1d68SJussi Laako 	return 0;
2318d39f1d68SJussi Laako }
2319d39f1d68SJussi Laako 
snd_rme_sync_source_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2320d39f1d68SJussi Laako static int snd_rme_sync_source_get(struct snd_kcontrol *kcontrol,
2321d39f1d68SJussi Laako 				   struct snd_ctl_elem_value *ucontrol)
2322d39f1d68SJussi Laako {
2323d39f1d68SJussi Laako 	u32 status1;
2324d39f1d68SJussi Laako 	int err;
2325d39f1d68SJussi Laako 
2326d39f1d68SJussi Laako 	err = snd_rme_get_status1(kcontrol, &status1);
2327d39f1d68SJussi Laako 	if (err < 0)
2328d39f1d68SJussi Laako 		return err;
2329d39f1d68SJussi Laako 	ucontrol->value.enumerated.item[0] = SND_RME_CLK_SYNC(status1);
2330d39f1d68SJussi Laako 	return 0;
2331d39f1d68SJussi Laako }
2332d39f1d68SJussi Laako 
snd_rme_current_freq_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2333d39f1d68SJussi Laako static int snd_rme_current_freq_get(struct snd_kcontrol *kcontrol,
2334d39f1d68SJussi Laako 				    struct snd_ctl_elem_value *ucontrol)
2335d39f1d68SJussi Laako {
2336d39f1d68SJussi Laako 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
2337d39f1d68SJussi Laako 	struct snd_usb_audio *chip = list->mixer->chip;
2338d39f1d68SJussi Laako 	u32 status1;
2339d39f1d68SJussi Laako 	const u64 num = 104857600000000ULL;
2340d39f1d68SJussi Laako 	u32 den;
2341d39f1d68SJussi Laako 	unsigned int freq;
2342d39f1d68SJussi Laako 	int err;
2343d39f1d68SJussi Laako 
2344d39f1d68SJussi Laako 	err = snd_usb_lock_shutdown(chip);
2345d39f1d68SJussi Laako 	if (err < 0)
2346d39f1d68SJussi Laako 		return err;
2347d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_STATUS1, &status1);
2348d39f1d68SJussi Laako 	if (err < 0)
2349d39f1d68SJussi Laako 		goto end;
2350d39f1d68SJussi Laako 	err = snd_rme_read_value(chip, SND_RME_GET_CURRENT_FREQ, &den);
2351d39f1d68SJussi Laako 	if (err < 0)
2352d39f1d68SJussi Laako 		goto end;
2353d39f1d68SJussi Laako 	freq = (den == 0) ? 0 : div64_u64(num, den);
2354d39f1d68SJussi Laako 	freq <<= SND_RME_CLK_FREQMUL(status1);
2355d39f1d68SJussi Laako 	ucontrol->value.integer.value[0] = freq;
2356d39f1d68SJussi Laako 
2357d39f1d68SJussi Laako end:
2358d39f1d68SJussi Laako 	snd_usb_unlock_shutdown(chip);
2359d39f1d68SJussi Laako 	return err;
2360d39f1d68SJussi Laako }
2361d39f1d68SJussi Laako 
snd_rme_rate_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2362d39f1d68SJussi Laako static int snd_rme_rate_info(struct snd_kcontrol *kcontrol,
2363d39f1d68SJussi Laako 			     struct snd_ctl_elem_info *uinfo)
2364d39f1d68SJussi Laako {
2365d39f1d68SJussi Laako 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2366d39f1d68SJussi Laako 	uinfo->count = 1;
2367d39f1d68SJussi Laako 	switch (kcontrol->private_value) {
2368d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SYSTEM:
2369d39f1d68SJussi Laako 		uinfo->value.integer.min = 32000;
2370d39f1d68SJussi Laako 		uinfo->value.integer.max = 800000;
2371d39f1d68SJussi Laako 		break;
2372d39f1d68SJussi Laako 	case SND_RME_DOMAIN_AES:
2373d39f1d68SJussi Laako 	case SND_RME_DOMAIN_SPDIF:
2374d39f1d68SJussi Laako 	default:
2375d39f1d68SJussi Laako 		uinfo->value.integer.min = 0;
2376d39f1d68SJussi Laako 		uinfo->value.integer.max = 200000;
2377d39f1d68SJussi Laako 	}
2378d39f1d68SJussi Laako 	uinfo->value.integer.step = 0;
2379d39f1d68SJussi Laako 	return 0;
2380d39f1d68SJussi Laako }
2381d39f1d68SJussi Laako 
snd_rme_sync_state_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2382d39f1d68SJussi Laako static int snd_rme_sync_state_info(struct snd_kcontrol *kcontrol,
2383d39f1d68SJussi Laako 				   struct snd_ctl_elem_info *uinfo)
2384d39f1d68SJussi Laako {
2385d39f1d68SJussi Laako 	static const char *const sync_states[] = {
2386d39f1d68SJussi Laako 		"No Lock", "Lock", "Sync"
2387d39f1d68SJussi Laako 	};
2388d39f1d68SJussi Laako 
2389d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2390d39f1d68SJussi Laako 				 ARRAY_SIZE(sync_states), sync_states);
2391d39f1d68SJussi Laako }
2392d39f1d68SJussi Laako 
snd_rme_spdif_if_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2393d39f1d68SJussi Laako static int snd_rme_spdif_if_info(struct snd_kcontrol *kcontrol,
2394d39f1d68SJussi Laako 				 struct snd_ctl_elem_info *uinfo)
2395d39f1d68SJussi Laako {
2396d39f1d68SJussi Laako 	static const char *const spdif_if[] = {
2397d39f1d68SJussi Laako 		"Coaxial", "Optical"
2398d39f1d68SJussi Laako 	};
2399d39f1d68SJussi Laako 
2400d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2401d39f1d68SJussi Laako 				 ARRAY_SIZE(spdif_if), spdif_if);
2402d39f1d68SJussi Laako }
2403d39f1d68SJussi Laako 
snd_rme_spdif_format_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2404d39f1d68SJussi Laako static int snd_rme_spdif_format_info(struct snd_kcontrol *kcontrol,
2405d39f1d68SJussi Laako 				     struct snd_ctl_elem_info *uinfo)
2406d39f1d68SJussi Laako {
2407d39f1d68SJussi Laako 	static const char *const optical_type[] = {
2408d39f1d68SJussi Laako 		"Consumer", "Professional"
2409d39f1d68SJussi Laako 	};
2410d39f1d68SJussi Laako 
2411d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2412d39f1d68SJussi Laako 				 ARRAY_SIZE(optical_type), optical_type);
2413d39f1d68SJussi Laako }
2414d39f1d68SJussi Laako 
snd_rme_sync_source_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)2415d39f1d68SJussi Laako static int snd_rme_sync_source_info(struct snd_kcontrol *kcontrol,
2416d39f1d68SJussi Laako 				    struct snd_ctl_elem_info *uinfo)
2417d39f1d68SJussi Laako {
2418d39f1d68SJussi Laako 	static const char *const sync_sources[] = {
2419d39f1d68SJussi Laako 		"Internal", "AES", "SPDIF", "Internal"
2420d39f1d68SJussi Laako 	};
2421d39f1d68SJussi Laako 
2422d39f1d68SJussi Laako 	return snd_ctl_enum_info(uinfo, 1,
2423d39f1d68SJussi Laako 				 ARRAY_SIZE(sync_sources), sync_sources);
2424d39f1d68SJussi Laako }
2425d39f1d68SJussi Laako 
2426195727e8STakashi Iwai static const struct snd_kcontrol_new snd_rme_controls[] = {
2427d39f1d68SJussi Laako 	{
2428d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2429d39f1d68SJussi Laako 		.name = "AES Rate",
2430d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2431d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2432d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2433d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_AES
2434d39f1d68SJussi Laako 	},
2435d39f1d68SJussi Laako 	{
2436d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2437d39f1d68SJussi Laako 		.name = "AES Sync",
2438d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2439d39f1d68SJussi Laako 		.info = snd_rme_sync_state_info,
2440d39f1d68SJussi Laako 		.get = snd_rme_sync_state_get,
2441d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_AES
2442d39f1d68SJussi Laako 	},
2443d39f1d68SJussi Laako 	{
2444d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2445d39f1d68SJussi Laako 		.name = "SPDIF Rate",
2446d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2447d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2448d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2449d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SPDIF
2450d39f1d68SJussi Laako 	},
2451d39f1d68SJussi Laako 	{
2452d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2453d39f1d68SJussi Laako 		.name = "SPDIF Sync",
2454d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2455d39f1d68SJussi Laako 		.info = snd_rme_sync_state_info,
2456d39f1d68SJussi Laako 		.get = snd_rme_sync_state_get,
2457d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SPDIF
2458d39f1d68SJussi Laako 	},
2459d39f1d68SJussi Laako 	{
2460d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2461d39f1d68SJussi Laako 		.name = "SPDIF Interface",
2462d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2463d39f1d68SJussi Laako 		.info = snd_rme_spdif_if_info,
2464d39f1d68SJussi Laako 		.get = snd_rme_spdif_if_get,
2465d39f1d68SJussi Laako 	},
2466d39f1d68SJussi Laako 	{
2467d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2468d39f1d68SJussi Laako 		.name = "SPDIF Format",
2469d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2470d39f1d68SJussi Laako 		.info = snd_rme_spdif_format_info,
2471d39f1d68SJussi Laako 		.get = snd_rme_spdif_format_get,
2472d39f1d68SJussi Laako 	},
2473d39f1d68SJussi Laako 	{
2474d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2475d39f1d68SJussi Laako 		.name = "Sync Source",
2476d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2477d39f1d68SJussi Laako 		.info = snd_rme_sync_source_info,
2478d39f1d68SJussi Laako 		.get = snd_rme_sync_source_get
2479d39f1d68SJussi Laako 	},
2480d39f1d68SJussi Laako 	{
2481d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2482d39f1d68SJussi Laako 		.name = "System Rate",
2483d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2484d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2485d39f1d68SJussi Laako 		.get = snd_rme_rate_get,
2486d39f1d68SJussi Laako 		.private_value = SND_RME_DOMAIN_SYSTEM
2487d39f1d68SJussi Laako 	},
2488d39f1d68SJussi Laako 	{
2489d39f1d68SJussi Laako 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2490d39f1d68SJussi Laako 		.name = "Current Frequency",
2491d39f1d68SJussi Laako 		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
2492d39f1d68SJussi Laako 		.info = snd_rme_rate_info,
2493d39f1d68SJussi Laako 		.get = snd_rme_current_freq_get
2494d39f1d68SJussi Laako 	}
2495d39f1d68SJussi Laako };
2496d39f1d68SJussi Laako 
snd_rme_controls_create(struct usb_mixer_interface * mixer)2497d39f1d68SJussi Laako static int snd_rme_controls_create(struct usb_mixer_interface *mixer)
2498d39f1d68SJussi Laako {
2499d39f1d68SJussi Laako 	int err, i;
2500d39f1d68SJussi Laako 
2501d39f1d68SJussi Laako 	for (i = 0; i < ARRAY_SIZE(snd_rme_controls); ++i) {
2502d39f1d68SJussi Laako 		err = add_single_ctl_with_resume(mixer, 0,
2503d39f1d68SJussi Laako 						 NULL,
2504d39f1d68SJussi Laako 						 &snd_rme_controls[i],
2505d39f1d68SJussi Laako 						 NULL);
2506d39f1d68SJussi Laako 		if (err < 0)
2507d39f1d68SJussi Laako 			return err;
2508d39f1d68SJussi Laako 	}
2509d39f1d68SJussi Laako 
2510d39f1d68SJussi Laako 	return 0;
2511d39f1d68SJussi Laako }
2512d39f1d68SJussi Laako 
25133e8f3bd0SThomas Ebeling /*
25143e8f3bd0SThomas Ebeling  * RME Babyface Pro (FS)
25153e8f3bd0SThomas Ebeling  *
25163e8f3bd0SThomas Ebeling  * These devices exposes a couple of DSP functions via request to EP0.
25173e8f3bd0SThomas Ebeling  * Switches are available via control registers, while routing is controlled
25183e8f3bd0SThomas Ebeling  * by controlling the volume on each possible crossing point.
251947b4f5f5SThomas Ebeling  * Volume control is linear, from -inf (dec. 0) to +6dB (dec. 65536) with
25203e8f3bd0SThomas Ebeling  * 0dB being at dec. 32768.
25213e8f3bd0SThomas Ebeling  */
25223e8f3bd0SThomas Ebeling enum {
25233e8f3bd0SThomas Ebeling 	SND_BBFPRO_CTL_REG1 = 0,
25243e8f3bd0SThomas Ebeling 	SND_BBFPRO_CTL_REG2
25253e8f3bd0SThomas Ebeling };
25263e8f3bd0SThomas Ebeling 
25273e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG_MASK 1
25283e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_IDX_MASK 0xff
25293e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_IDX_SHIFT 1
25303e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_VAL_MASK 1
25313e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_VAL_SHIFT 9
25323e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_CLK_MASTER 0
25333e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_CLK_OPTICAL 1
25343e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_PRO 7
25353e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_EMPH 8
25363e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL 10
25373e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_48V_AN1 0
25383e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_48V_AN2 1
25393e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_SENS_IN3 2
25403e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_SENS_IN4 3
25413e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_PAD_AN1 4
25423e8f3bd0SThomas Ebeling #define SND_BBFPRO_CTL_REG2_PAD_AN2 5
25433e8f3bd0SThomas Ebeling 
25443e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_IDX_MASK 0x1ff
25453e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff
25463e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_SHIFT 9
25473e8f3bd0SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf
254847b4f5f5SThomas Ebeling #define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB
25493e8f3bd0SThomas Ebeling 
25503e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_CTL_REG1 0x10
25513e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_CTL_REG2 0x17
25523e8f3bd0SThomas Ebeling #define SND_BBFPRO_USBREQ_MIXER 0x12
25533e8f3bd0SThomas Ebeling 
snd_bbfpro_ctl_update(struct usb_mixer_interface * mixer,u8 reg,u8 index,u8 value)25543e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg,
25553e8f3bd0SThomas Ebeling 				 u8 index, u8 value)
25563e8f3bd0SThomas Ebeling {
25573e8f3bd0SThomas Ebeling 	int err;
25583e8f3bd0SThomas Ebeling 	u16 usb_req, usb_idx, usb_val;
25593e8f3bd0SThomas Ebeling 	struct snd_usb_audio *chip = mixer->chip;
25603e8f3bd0SThomas Ebeling 
25613e8f3bd0SThomas Ebeling 	err = snd_usb_lock_shutdown(chip);
25623e8f3bd0SThomas Ebeling 	if (err < 0)
25633e8f3bd0SThomas Ebeling 		return err;
25643e8f3bd0SThomas Ebeling 
25653e8f3bd0SThomas Ebeling 	if (reg == SND_BBFPRO_CTL_REG1) {
25663e8f3bd0SThomas Ebeling 		usb_req = SND_BBFPRO_USBREQ_CTL_REG1;
25673e8f3bd0SThomas Ebeling 		if (index == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
25683e8f3bd0SThomas Ebeling 			usb_idx = 3;
25693e8f3bd0SThomas Ebeling 			usb_val = value ? 3 : 0;
25703e8f3bd0SThomas Ebeling 		} else {
25713e8f3bd0SThomas Ebeling 			usb_idx = 1 << index;
25723e8f3bd0SThomas Ebeling 			usb_val = value ? usb_idx : 0;
25733e8f3bd0SThomas Ebeling 		}
25743e8f3bd0SThomas Ebeling 	} else {
25753e8f3bd0SThomas Ebeling 		usb_req = SND_BBFPRO_USBREQ_CTL_REG2;
25763e8f3bd0SThomas Ebeling 		usb_idx = 1 << index;
25773e8f3bd0SThomas Ebeling 		usb_val = value ? usb_idx : 0;
25783e8f3bd0SThomas Ebeling 	}
25793e8f3bd0SThomas Ebeling 
25803e8f3bd0SThomas Ebeling 	err = snd_usb_ctl_msg(chip->dev,
25813e8f3bd0SThomas Ebeling 			      usb_sndctrlpipe(chip->dev, 0), usb_req,
25823e8f3bd0SThomas Ebeling 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
2583f99e24a6SThomas Ebeling 			      usb_val, usb_idx, NULL, 0);
25843e8f3bd0SThomas Ebeling 
25853e8f3bd0SThomas Ebeling 	snd_usb_unlock_shutdown(chip);
25863e8f3bd0SThomas Ebeling 	return err;
25873e8f3bd0SThomas Ebeling }
25883e8f3bd0SThomas Ebeling 
snd_bbfpro_ctl_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)25893e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol,
25903e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
25913e8f3bd0SThomas Ebeling {
25923e8f3bd0SThomas Ebeling 	u8 reg, idx, val;
25933e8f3bd0SThomas Ebeling 	int pv;
25943e8f3bd0SThomas Ebeling 
25953e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
25963e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
25973e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
25983e8f3bd0SThomas Ebeling 	val = kcontrol->private_value >> SND_BBFPRO_CTL_VAL_SHIFT;
25993e8f3bd0SThomas Ebeling 
26003e8f3bd0SThomas Ebeling 	if ((reg == SND_BBFPRO_CTL_REG1 &&
26013e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
26023e8f3bd0SThomas Ebeling 	    (reg == SND_BBFPRO_CTL_REG2 &&
26033e8f3bd0SThomas Ebeling 	    (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
26043e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
26053e8f3bd0SThomas Ebeling 		ucontrol->value.enumerated.item[0] = val;
26063e8f3bd0SThomas Ebeling 	} else {
26073e8f3bd0SThomas Ebeling 		ucontrol->value.integer.value[0] = val;
26083e8f3bd0SThomas Ebeling 	}
26093e8f3bd0SThomas Ebeling 	return 0;
26103e8f3bd0SThomas Ebeling }
26113e8f3bd0SThomas Ebeling 
snd_bbfpro_ctl_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)26123e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_info(struct snd_kcontrol *kcontrol,
26133e8f3bd0SThomas Ebeling 			       struct snd_ctl_elem_info *uinfo)
26143e8f3bd0SThomas Ebeling {
26153e8f3bd0SThomas Ebeling 	u8 reg, idx;
26163e8f3bd0SThomas Ebeling 	int pv;
26173e8f3bd0SThomas Ebeling 
26183e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
26193e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
26203e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
26213e8f3bd0SThomas Ebeling 
26223e8f3bd0SThomas Ebeling 	if (reg == SND_BBFPRO_CTL_REG1 &&
26233e8f3bd0SThomas Ebeling 	    idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) {
26243e8f3bd0SThomas Ebeling 		static const char * const texts[2] = {
26253e8f3bd0SThomas Ebeling 			"AutoSync",
26263e8f3bd0SThomas Ebeling 			"Internal"
26273e8f3bd0SThomas Ebeling 		};
26283e8f3bd0SThomas Ebeling 		return snd_ctl_enum_info(uinfo, 1, 2, texts);
26293e8f3bd0SThomas Ebeling 	} else if (reg == SND_BBFPRO_CTL_REG2 &&
26303e8f3bd0SThomas Ebeling 		   (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
26313e8f3bd0SThomas Ebeling 		    idx == SND_BBFPRO_CTL_REG2_SENS_IN4)) {
26323e8f3bd0SThomas Ebeling 		static const char * const texts[2] = {
26333e8f3bd0SThomas Ebeling 			"-10dBV",
26343e8f3bd0SThomas Ebeling 			"+4dBu"
26353e8f3bd0SThomas Ebeling 		};
26363e8f3bd0SThomas Ebeling 		return snd_ctl_enum_info(uinfo, 1, 2, texts);
26373e8f3bd0SThomas Ebeling 	}
26383e8f3bd0SThomas Ebeling 
26393e8f3bd0SThomas Ebeling 	uinfo->count = 1;
26403e8f3bd0SThomas Ebeling 	uinfo->value.integer.min = 0;
26413e8f3bd0SThomas Ebeling 	uinfo->value.integer.max = 1;
26423e8f3bd0SThomas Ebeling 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
26433e8f3bd0SThomas Ebeling 	return 0;
26443e8f3bd0SThomas Ebeling }
26453e8f3bd0SThomas Ebeling 
snd_bbfpro_ctl_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)26463e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol,
26473e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
26483e8f3bd0SThomas Ebeling {
26493e8f3bd0SThomas Ebeling 	int err;
26503e8f3bd0SThomas Ebeling 	u8 reg, idx;
26513e8f3bd0SThomas Ebeling 	int old_value, pv, val;
26523e8f3bd0SThomas Ebeling 
26533e8f3bd0SThomas Ebeling 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
26543e8f3bd0SThomas Ebeling 	struct usb_mixer_interface *mixer = list->mixer;
26553e8f3bd0SThomas Ebeling 
26563e8f3bd0SThomas Ebeling 	pv = kcontrol->private_value;
26573e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
26583e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
26593e8f3bd0SThomas Ebeling 	old_value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
26603e8f3bd0SThomas Ebeling 
26613e8f3bd0SThomas Ebeling 	if ((reg == SND_BBFPRO_CTL_REG1 &&
26623e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) ||
26633e8f3bd0SThomas Ebeling 	    (reg == SND_BBFPRO_CTL_REG2 &&
26643e8f3bd0SThomas Ebeling 	    (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 ||
26653e8f3bd0SThomas Ebeling 	     idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) {
26663e8f3bd0SThomas Ebeling 		val = ucontrol->value.enumerated.item[0];
26673e8f3bd0SThomas Ebeling 	} else {
26683e8f3bd0SThomas Ebeling 		val = ucontrol->value.integer.value[0];
26693e8f3bd0SThomas Ebeling 	}
26703e8f3bd0SThomas Ebeling 
26713e8f3bd0SThomas Ebeling 	if (val > 1)
26723e8f3bd0SThomas Ebeling 		return -EINVAL;
26733e8f3bd0SThomas Ebeling 
26743e8f3bd0SThomas Ebeling 	if (val == old_value)
26753e8f3bd0SThomas Ebeling 		return 0;
26763e8f3bd0SThomas Ebeling 
26773e8f3bd0SThomas Ebeling 	kcontrol->private_value = reg
26783e8f3bd0SThomas Ebeling 		| ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT)
26793e8f3bd0SThomas Ebeling 		| ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT);
26803e8f3bd0SThomas Ebeling 
26813e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
26823e8f3bd0SThomas Ebeling 	return err < 0 ? err : 1;
26833e8f3bd0SThomas Ebeling }
26843e8f3bd0SThomas Ebeling 
snd_bbfpro_ctl_resume(struct usb_mixer_elem_list * list)26853e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
26863e8f3bd0SThomas Ebeling {
26873e8f3bd0SThomas Ebeling 	u8 reg, idx;
26883e8f3bd0SThomas Ebeling 	int value, pv;
26893e8f3bd0SThomas Ebeling 
26903e8f3bd0SThomas Ebeling 	pv = list->kctl->private_value;
26913e8f3bd0SThomas Ebeling 	reg = pv & SND_BBFPRO_CTL_REG_MASK;
26923e8f3bd0SThomas Ebeling 	idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK;
26933e8f3bd0SThomas Ebeling 	value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK;
26943e8f3bd0SThomas Ebeling 
26953e8f3bd0SThomas Ebeling 	return snd_bbfpro_ctl_update(list->mixer, reg, idx, value);
26963e8f3bd0SThomas Ebeling }
26973e8f3bd0SThomas Ebeling 
snd_bbfpro_vol_update(struct usb_mixer_interface * mixer,u16 index,u32 value)26983e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index,
26993e8f3bd0SThomas Ebeling 				 u32 value)
27003e8f3bd0SThomas Ebeling {
27013e8f3bd0SThomas Ebeling 	struct snd_usb_audio *chip = mixer->chip;
27023e8f3bd0SThomas Ebeling 	int err;
27033e8f3bd0SThomas Ebeling 	u16 idx;
27043e8f3bd0SThomas Ebeling 	u16 usb_idx, usb_val;
27053e8f3bd0SThomas Ebeling 	u32 v;
27063e8f3bd0SThomas Ebeling 
27073e8f3bd0SThomas Ebeling 	err = snd_usb_lock_shutdown(chip);
27083e8f3bd0SThomas Ebeling 	if (err < 0)
27093e8f3bd0SThomas Ebeling 		return err;
27103e8f3bd0SThomas Ebeling 
27113e8f3bd0SThomas Ebeling 	idx = index & SND_BBFPRO_MIXER_IDX_MASK;
27123e8f3bd0SThomas Ebeling 	// 18 bit linear volume, split so 2 bits end up in index.
27133e8f3bd0SThomas Ebeling 	v = value & SND_BBFPRO_MIXER_VAL_MASK;
27143e8f3bd0SThomas Ebeling 	usb_idx = idx | (v & 0x3) << 14;
27153e8f3bd0SThomas Ebeling 	usb_val = (v >> 2) & 0xffff;
27163e8f3bd0SThomas Ebeling 
27173e8f3bd0SThomas Ebeling 	err = snd_usb_ctl_msg(chip->dev,
27183e8f3bd0SThomas Ebeling 			      usb_sndctrlpipe(chip->dev, 0),
27193e8f3bd0SThomas Ebeling 			      SND_BBFPRO_USBREQ_MIXER,
27203e8f3bd0SThomas Ebeling 			      USB_DIR_OUT | USB_TYPE_VENDOR |
27213e8f3bd0SThomas Ebeling 			      USB_RECIP_DEVICE,
2722f99e24a6SThomas Ebeling 			      usb_val, usb_idx, NULL, 0);
27233e8f3bd0SThomas Ebeling 
27243e8f3bd0SThomas Ebeling 	snd_usb_unlock_shutdown(chip);
27253e8f3bd0SThomas Ebeling 	return err;
27263e8f3bd0SThomas Ebeling }
27273e8f3bd0SThomas Ebeling 
snd_bbfpro_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)27283e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol,
27293e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
27303e8f3bd0SThomas Ebeling {
27313e8f3bd0SThomas Ebeling 	ucontrol->value.integer.value[0] =
27323e8f3bd0SThomas Ebeling 		kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
27333e8f3bd0SThomas Ebeling 	return 0;
27343e8f3bd0SThomas Ebeling }
27353e8f3bd0SThomas Ebeling 
snd_bbfpro_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)27363e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_info(struct snd_kcontrol *kcontrol,
27373e8f3bd0SThomas Ebeling 			       struct snd_ctl_elem_info *uinfo)
27383e8f3bd0SThomas Ebeling {
27393e8f3bd0SThomas Ebeling 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
27403e8f3bd0SThomas Ebeling 	uinfo->count = 1;
27413e8f3bd0SThomas Ebeling 	uinfo->value.integer.min = SND_BBFPRO_MIXER_VAL_MIN;
27423e8f3bd0SThomas Ebeling 	uinfo->value.integer.max = SND_BBFPRO_MIXER_VAL_MAX;
27433e8f3bd0SThomas Ebeling 	return 0;
27443e8f3bd0SThomas Ebeling }
27453e8f3bd0SThomas Ebeling 
snd_bbfpro_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)27463e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol,
27473e8f3bd0SThomas Ebeling 			      struct snd_ctl_elem_value *ucontrol)
27483e8f3bd0SThomas Ebeling {
27493e8f3bd0SThomas Ebeling 	int err;
27503e8f3bd0SThomas Ebeling 	u16 idx;
27513e8f3bd0SThomas Ebeling 	u32 new_val, old_value, uvalue;
27523e8f3bd0SThomas Ebeling 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
27533e8f3bd0SThomas Ebeling 	struct usb_mixer_interface *mixer = list->mixer;
27543e8f3bd0SThomas Ebeling 
27553e8f3bd0SThomas Ebeling 	uvalue = ucontrol->value.integer.value[0];
27563e8f3bd0SThomas Ebeling 	idx = kcontrol->private_value & SND_BBFPRO_MIXER_IDX_MASK;
27573e8f3bd0SThomas Ebeling 	old_value = kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT;
27583e8f3bd0SThomas Ebeling 
27593e8f3bd0SThomas Ebeling 	if (uvalue > SND_BBFPRO_MIXER_VAL_MAX)
27603e8f3bd0SThomas Ebeling 		return -EINVAL;
27613e8f3bd0SThomas Ebeling 
27623e8f3bd0SThomas Ebeling 	if (uvalue == old_value)
27633e8f3bd0SThomas Ebeling 		return 0;
27643e8f3bd0SThomas Ebeling 
27653e8f3bd0SThomas Ebeling 	new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK;
27663e8f3bd0SThomas Ebeling 
27673e8f3bd0SThomas Ebeling 	kcontrol->private_value = idx
27683e8f3bd0SThomas Ebeling 		| (new_val << SND_BBFPRO_MIXER_VAL_SHIFT);
27693e8f3bd0SThomas Ebeling 
27703e8f3bd0SThomas Ebeling 	err = snd_bbfpro_vol_update(mixer, idx, new_val);
27713e8f3bd0SThomas Ebeling 	return err < 0 ? err : 1;
27723e8f3bd0SThomas Ebeling }
27733e8f3bd0SThomas Ebeling 
snd_bbfpro_vol_resume(struct usb_mixer_elem_list * list)27743e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list)
27753e8f3bd0SThomas Ebeling {
27763e8f3bd0SThomas Ebeling 	int pv = list->kctl->private_value;
27773e8f3bd0SThomas Ebeling 	u16 idx = pv & SND_BBFPRO_MIXER_IDX_MASK;
27783e8f3bd0SThomas Ebeling 	u32 val = (pv >> SND_BBFPRO_MIXER_VAL_SHIFT)
27793e8f3bd0SThomas Ebeling 		& SND_BBFPRO_MIXER_VAL_MASK;
27803e8f3bd0SThomas Ebeling 	return snd_bbfpro_vol_update(list->mixer, idx, val);
27813e8f3bd0SThomas Ebeling }
27823e8f3bd0SThomas Ebeling 
27833e8f3bd0SThomas Ebeling // Predfine elements
27843e8f3bd0SThomas Ebeling static const struct snd_kcontrol_new snd_bbfpro_ctl_control = {
27853e8f3bd0SThomas Ebeling 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
27863e8f3bd0SThomas Ebeling 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
27873e8f3bd0SThomas Ebeling 	.index = 0,
27883e8f3bd0SThomas Ebeling 	.info = snd_bbfpro_ctl_info,
27893e8f3bd0SThomas Ebeling 	.get = snd_bbfpro_ctl_get,
27903e8f3bd0SThomas Ebeling 	.put = snd_bbfpro_ctl_put
27913e8f3bd0SThomas Ebeling };
27923e8f3bd0SThomas Ebeling 
27933e8f3bd0SThomas Ebeling static const struct snd_kcontrol_new snd_bbfpro_vol_control = {
27943e8f3bd0SThomas Ebeling 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
27953e8f3bd0SThomas Ebeling 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
27963e8f3bd0SThomas Ebeling 	.index = 0,
27973e8f3bd0SThomas Ebeling 	.info = snd_bbfpro_vol_info,
27983e8f3bd0SThomas Ebeling 	.get = snd_bbfpro_vol_get,
27993e8f3bd0SThomas Ebeling 	.put = snd_bbfpro_vol_put
28003e8f3bd0SThomas Ebeling };
28013e8f3bd0SThomas Ebeling 
snd_bbfpro_ctl_add(struct usb_mixer_interface * mixer,u8 reg,u8 index,char * name)28023e8f3bd0SThomas Ebeling static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg,
28033e8f3bd0SThomas Ebeling 			      u8 index, char *name)
28043e8f3bd0SThomas Ebeling {
28053e8f3bd0SThomas Ebeling 	struct snd_kcontrol_new knew = snd_bbfpro_ctl_control;
28063e8f3bd0SThomas Ebeling 
28073e8f3bd0SThomas Ebeling 	knew.name = name;
28083e8f3bd0SThomas Ebeling 	knew.private_value = (reg & SND_BBFPRO_CTL_REG_MASK)
28093e8f3bd0SThomas Ebeling 		| ((index & SND_BBFPRO_CTL_IDX_MASK)
28103e8f3bd0SThomas Ebeling 			<< SND_BBFPRO_CTL_IDX_SHIFT);
28113e8f3bd0SThomas Ebeling 
28123e8f3bd0SThomas Ebeling 	return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume,
28133e8f3bd0SThomas Ebeling 		&knew, NULL);
28143e8f3bd0SThomas Ebeling }
28153e8f3bd0SThomas Ebeling 
snd_bbfpro_vol_add(struct usb_mixer_interface * mixer,u16 index,char * name)28163e8f3bd0SThomas Ebeling static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index,
28173e8f3bd0SThomas Ebeling 			      char *name)
28183e8f3bd0SThomas Ebeling {
28193e8f3bd0SThomas Ebeling 	struct snd_kcontrol_new knew = snd_bbfpro_vol_control;
28203e8f3bd0SThomas Ebeling 
28213e8f3bd0SThomas Ebeling 	knew.name = name;
28223e8f3bd0SThomas Ebeling 	knew.private_value = index & SND_BBFPRO_MIXER_IDX_MASK;
28233e8f3bd0SThomas Ebeling 
28243e8f3bd0SThomas Ebeling 	return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_vol_resume,
28253e8f3bd0SThomas Ebeling 		&knew, NULL);
28263e8f3bd0SThomas Ebeling }
28273e8f3bd0SThomas Ebeling 
snd_bbfpro_controls_create(struct usb_mixer_interface * mixer)28283e8f3bd0SThomas Ebeling static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
28293e8f3bd0SThomas Ebeling {
28303e8f3bd0SThomas Ebeling 	int err, i, o;
28313e8f3bd0SThomas Ebeling 	char name[48];
28323e8f3bd0SThomas Ebeling 
28333e8f3bd0SThomas Ebeling 	static const char * const input[] = {
28343e8f3bd0SThomas Ebeling 		"AN1", "AN2", "IN3", "IN4", "AS1", "AS2", "ADAT3",
28353e8f3bd0SThomas Ebeling 		"ADAT4", "ADAT5", "ADAT6", "ADAT7", "ADAT8"};
28363e8f3bd0SThomas Ebeling 
28373e8f3bd0SThomas Ebeling 	static const char * const output[] = {
28383e8f3bd0SThomas Ebeling 		"AN1", "AN2", "PH3", "PH4", "AS1", "AS2", "ADAT3", "ADAT4",
28393e8f3bd0SThomas Ebeling 		"ADAT5", "ADAT6", "ADAT7", "ADAT8"};
28403e8f3bd0SThomas Ebeling 
28413e8f3bd0SThomas Ebeling 	for (o = 0 ; o < 12 ; ++o) {
28423e8f3bd0SThomas Ebeling 		for (i = 0 ; i < 12 ; ++i) {
28433e8f3bd0SThomas Ebeling 			// Line routing
28443e8f3bd0SThomas Ebeling 			snprintf(name, sizeof(name),
28453e8f3bd0SThomas Ebeling 				 "%s-%s-%s Playback Volume",
28463e8f3bd0SThomas Ebeling 				 (i < 2 ? "Mic" : "Line"),
28473e8f3bd0SThomas Ebeling 				 input[i], output[o]);
28483e8f3bd0SThomas Ebeling 			err = snd_bbfpro_vol_add(mixer, (26 * o + i), name);
28493e8f3bd0SThomas Ebeling 			if (err < 0)
28503e8f3bd0SThomas Ebeling 				return err;
28513e8f3bd0SThomas Ebeling 
28523e8f3bd0SThomas Ebeling 			// PCM routing... yes, it is output remapping
28533e8f3bd0SThomas Ebeling 			snprintf(name, sizeof(name),
28543e8f3bd0SThomas Ebeling 				 "PCM-%s-%s Playback Volume",
28553e8f3bd0SThomas Ebeling 				 output[i], output[o]);
28563e8f3bd0SThomas Ebeling 			err = snd_bbfpro_vol_add(mixer, (26 * o + 12 + i),
28573e8f3bd0SThomas Ebeling 						 name);
28583e8f3bd0SThomas Ebeling 			if (err < 0)
28593e8f3bd0SThomas Ebeling 				return err;
28603e8f3bd0SThomas Ebeling 		}
28613e8f3bd0SThomas Ebeling 	}
28623e8f3bd0SThomas Ebeling 
28633e8f3bd0SThomas Ebeling 	// Control Reg 1
28643e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
28653e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_CLK_OPTICAL,
28663e8f3bd0SThomas Ebeling 				 "Sample Clock Source");
28673e8f3bd0SThomas Ebeling 	if (err < 0)
28683e8f3bd0SThomas Ebeling 		return err;
28693e8f3bd0SThomas Ebeling 
28703e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
28713e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_PRO,
28723e8f3bd0SThomas Ebeling 				 "IEC958 Pro Mask");
28733e8f3bd0SThomas Ebeling 	if (err < 0)
28743e8f3bd0SThomas Ebeling 		return err;
28753e8f3bd0SThomas Ebeling 
28763e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
28773e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_EMPH,
28783e8f3bd0SThomas Ebeling 				 "IEC958 Emphasis");
28793e8f3bd0SThomas Ebeling 	if (err < 0)
28803e8f3bd0SThomas Ebeling 		return err;
28813e8f3bd0SThomas Ebeling 
28823e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1,
28833e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL,
28843e8f3bd0SThomas Ebeling 				 "IEC958 Switch");
28853e8f3bd0SThomas Ebeling 	if (err < 0)
28863e8f3bd0SThomas Ebeling 		return err;
28873e8f3bd0SThomas Ebeling 
28883e8f3bd0SThomas Ebeling 	// Control Reg 2
28893e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
28903e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_48V_AN1,
28913e8f3bd0SThomas Ebeling 				 "Mic-AN1 48V");
28923e8f3bd0SThomas Ebeling 	if (err < 0)
28933e8f3bd0SThomas Ebeling 		return err;
28943e8f3bd0SThomas Ebeling 
28953e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
28963e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_48V_AN2,
28973e8f3bd0SThomas Ebeling 				 "Mic-AN2 48V");
28983e8f3bd0SThomas Ebeling 	if (err < 0)
28993e8f3bd0SThomas Ebeling 		return err;
29003e8f3bd0SThomas Ebeling 
29013e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
29023e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_SENS_IN3,
29033e8f3bd0SThomas Ebeling 				 "Line-IN3 Sens.");
29043e8f3bd0SThomas Ebeling 	if (err < 0)
29053e8f3bd0SThomas Ebeling 		return err;
29063e8f3bd0SThomas Ebeling 
29073e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
29083e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_SENS_IN4,
29093e8f3bd0SThomas Ebeling 				 "Line-IN4 Sens.");
29103e8f3bd0SThomas Ebeling 	if (err < 0)
29113e8f3bd0SThomas Ebeling 		return err;
29123e8f3bd0SThomas Ebeling 
29133e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
29143e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_PAD_AN1,
29153e8f3bd0SThomas Ebeling 				 "Mic-AN1 PAD");
29163e8f3bd0SThomas Ebeling 	if (err < 0)
29173e8f3bd0SThomas Ebeling 		return err;
29183e8f3bd0SThomas Ebeling 
29193e8f3bd0SThomas Ebeling 	err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2,
29203e8f3bd0SThomas Ebeling 				 SND_BBFPRO_CTL_REG2_PAD_AN2,
29213e8f3bd0SThomas Ebeling 				 "Mic-AN2 PAD");
29223e8f3bd0SThomas Ebeling 	if (err < 0)
29233e8f3bd0SThomas Ebeling 		return err;
29243e8f3bd0SThomas Ebeling 
29253e8f3bd0SThomas Ebeling 	return 0;
29263e8f3bd0SThomas Ebeling }
29273e8f3bd0SThomas Ebeling 
2928cdc01a15SFrantišek Kučera /*
2929a07df82cSOlivia Mackintosh  * Pioneer DJ DJM Mixers
2930cdc01a15SFrantišek Kučera  *
2931a07df82cSOlivia Mackintosh  * These devices generally have options for soft-switching the playback and
2932a07df82cSOlivia Mackintosh  * capture sources in addition to the recording level. Although different
2933a07df82cSOlivia Mackintosh  * devices have different configurations, there seems to be canonical values
2934a07df82cSOlivia Mackintosh  * for specific capture/playback types:  See the definitions of these below.
2935cdc01a15SFrantišek Kučera  *
2936a07df82cSOlivia Mackintosh  * The wValue is masked with the stereo channel number. e.g. Setting Ch2 to
2937a07df82cSOlivia Mackintosh  * capture phono would be 0x0203. Capture, playback and capture level have
2938a07df82cSOlivia Mackintosh  * different wIndexes.
2939cdc01a15SFrantišek Kučera  */
2940cdc01a15SFrantišek Kučera 
2941a07df82cSOlivia Mackintosh // Capture types
2942a07df82cSOlivia Mackintosh #define SND_DJM_CAP_LINE	0x00
2943a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CDLINE	0x01
2944fee03efcSFabian Lesniak #define SND_DJM_CAP_DIGITAL	0x02
2945a07df82cSOlivia Mackintosh #define SND_DJM_CAP_PHONO	0x03
2946a07df82cSOlivia Mackintosh #define SND_DJM_CAP_PFADER	0x06
2947a07df82cSOlivia Mackintosh #define SND_DJM_CAP_XFADERA	0x07
2948a07df82cSOlivia Mackintosh #define SND_DJM_CAP_XFADERB	0x08
2949a07df82cSOlivia Mackintosh #define SND_DJM_CAP_MIC		0x09
2950a07df82cSOlivia Mackintosh #define SND_DJM_CAP_AUX		0x0d
2951a07df82cSOlivia Mackintosh #define SND_DJM_CAP_RECOUT	0x0a
2952a07df82cSOlivia Mackintosh #define SND_DJM_CAP_NONE	0x0f
2953a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CH1PFADER	0x11
2954a07df82cSOlivia Mackintosh #define SND_DJM_CAP_CH2PFADER	0x12
2955fee03efcSFabian Lesniak #define SND_DJM_CAP_CH3PFADER	0x13
2956fee03efcSFabian Lesniak #define SND_DJM_CAP_CH4PFADER	0x14
2957cdc01a15SFrantišek Kučera 
2958a07df82cSOlivia Mackintosh // Playback types
2959a07df82cSOlivia Mackintosh #define SND_DJM_PB_CH1		0x00
2960a07df82cSOlivia Mackintosh #define SND_DJM_PB_CH2		0x01
2961a07df82cSOlivia Mackintosh #define SND_DJM_PB_AUX		0x04
2962cdc01a15SFrantišek Kučera 
2963a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_CAP	0x8002
2964a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_CAPLVL	0x8003
2965a07df82cSOlivia Mackintosh #define SND_DJM_WINDEX_PB	0x8016
2966cdc01a15SFrantišek Kučera 
2967a07df82cSOlivia Mackintosh // kcontrol->private_value layout
2968a07df82cSOlivia Mackintosh #define SND_DJM_VALUE_MASK	0x0000ffff
2969a07df82cSOlivia Mackintosh #define SND_DJM_GROUP_MASK	0x00ff0000
2970a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE_MASK	0xff000000
2971a07df82cSOlivia Mackintosh #define SND_DJM_GROUP_SHIFT	16
2972a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE_SHIFT	24
2973cdc01a15SFrantišek Kučera 
2974a07df82cSOlivia Mackintosh // device table index
29757687850bSNicolas MURE // used for the snd_djm_devices table, so please update accordingly
2976a07df82cSOlivia Mackintosh #define SND_DJM_250MK2_IDX	0x0
2977a07df82cSOlivia Mackintosh #define SND_DJM_750_IDX		0x1
29787687850bSNicolas MURE #define SND_DJM_850_IDX		0x2
29797687850bSNicolas MURE #define SND_DJM_900NXS2_IDX	0x3
29806d277881SWilliam Overton #define SND_DJM_750MK2_IDX	0x4
29815ae225bbSSarah Grant #define SND_DJM_450_IDX		0x5
2982cdc01a15SFrantišek Kučera 
2983cdc01a15SFrantišek Kučera 
2984a07df82cSOlivia Mackintosh #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \
2985cdc01a15SFrantišek Kučera 	.name = _name, \
2986a07df82cSOlivia Mackintosh 	.options = snd_djm_opts_##suffix, \
2987a07df82cSOlivia Mackintosh 	.noptions = ARRAY_SIZE(snd_djm_opts_##suffix), \
2988a07df82cSOlivia Mackintosh 	.default_value = _default_value, \
2989a07df82cSOlivia Mackintosh 	.wIndex = _windex }
2990cdc01a15SFrantišek Kučera 
2991a07df82cSOlivia Mackintosh #define SND_DJM_DEVICE(suffix) { \
2992a07df82cSOlivia Mackintosh 	.controls = snd_djm_ctls_##suffix, \
2993a07df82cSOlivia Mackintosh 	.ncontrols = ARRAY_SIZE(snd_djm_ctls_##suffix) }
2994a07df82cSOlivia Mackintosh 
2995a07df82cSOlivia Mackintosh 
2996a07df82cSOlivia Mackintosh struct snd_djm_device {
2997a07df82cSOlivia Mackintosh 	const char *name;
2998a07df82cSOlivia Mackintosh 	const struct snd_djm_ctl *controls;
2999a07df82cSOlivia Mackintosh 	size_t ncontrols;
3000cdc01a15SFrantišek Kučera };
3001cdc01a15SFrantišek Kučera 
3002a07df82cSOlivia Mackintosh struct snd_djm_ctl {
3003cdc01a15SFrantišek Kučera 	const char *name;
3004a07df82cSOlivia Mackintosh 	const u16 *options;
3005a07df82cSOlivia Mackintosh 	size_t noptions;
3006a07df82cSOlivia Mackintosh 	u16 default_value;
3007a07df82cSOlivia Mackintosh 	u16 wIndex;
3008a07df82cSOlivia Mackintosh };
3009cdc01a15SFrantišek Kučera 
snd_djm_get_label_caplevel(u16 wvalue)3010a07df82cSOlivia Mackintosh static const char *snd_djm_get_label_caplevel(u16 wvalue)
3011a07df82cSOlivia Mackintosh {
3012a07df82cSOlivia Mackintosh 	switch (wvalue) {
3013a07df82cSOlivia Mackintosh 	case 0x0000:	return "-19dB";
3014a07df82cSOlivia Mackintosh 	case 0x0100:	return "-15dB";
3015a07df82cSOlivia Mackintosh 	case 0x0200:	return "-10dB";
3016a07df82cSOlivia Mackintosh 	case 0x0300:	return "-5dB";
3017a07df82cSOlivia Mackintosh 	default:	return NULL;
3018a07df82cSOlivia Mackintosh 	}
3019a07df82cSOlivia Mackintosh };
3020a07df82cSOlivia Mackintosh 
snd_djm_get_label_cap_common(u16 wvalue)3021b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap_common(u16 wvalue)
3022a07df82cSOlivia Mackintosh {
3023a07df82cSOlivia Mackintosh 	switch (wvalue & 0x00ff) {
3024a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_LINE:		return "Control Tone LINE";
3025a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CDLINE:	return "Control Tone CD/LINE";
3026fee03efcSFabian Lesniak 	case SND_DJM_CAP_DIGITAL:	return "Control Tone DIGITAL";
3027a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_PHONO:		return "Control Tone PHONO";
3028a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_PFADER:	return "Post Fader";
3029a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_XFADERA:	return "Cross Fader A";
3030a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_XFADERB:	return "Cross Fader B";
3031a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_MIC:		return "Mic";
3032a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_RECOUT:	return "Rec Out";
3033a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_AUX:		return "Aux";
3034a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_NONE:		return "None";
3035a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CH1PFADER:	return "Post Fader Ch1";
3036a07df82cSOlivia Mackintosh 	case SND_DJM_CAP_CH2PFADER:	return "Post Fader Ch2";
3037fee03efcSFabian Lesniak 	case SND_DJM_CAP_CH3PFADER:	return "Post Fader Ch3";
3038fee03efcSFabian Lesniak 	case SND_DJM_CAP_CH4PFADER:	return "Post Fader Ch4";
3039a07df82cSOlivia Mackintosh 	default:			return NULL;
3040a07df82cSOlivia Mackintosh 	}
3041a07df82cSOlivia Mackintosh };
3042a07df82cSOlivia Mackintosh 
3043b8db8be8SNicolas MURE // The DJM-850 has different values for CD/LINE and LINE capture
3044b8db8be8SNicolas MURE // control options than the other DJM declared in this file.
snd_djm_get_label_cap_850(u16 wvalue)3045b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap_850(u16 wvalue)
3046b8db8be8SNicolas MURE {
3047b8db8be8SNicolas MURE 	switch (wvalue & 0x00ff) {
3048b8db8be8SNicolas MURE 	case 0x00:		return "Control Tone CD/LINE";
3049b8db8be8SNicolas MURE 	case 0x01:		return "Control Tone LINE";
3050b8db8be8SNicolas MURE 	default:		return snd_djm_get_label_cap_common(wvalue);
3051b8db8be8SNicolas MURE 	}
3052b8db8be8SNicolas MURE };
3053b8db8be8SNicolas MURE 
snd_djm_get_label_cap(u8 device_idx,u16 wvalue)3054b8db8be8SNicolas MURE static const char *snd_djm_get_label_cap(u8 device_idx, u16 wvalue)
3055b8db8be8SNicolas MURE {
3056b8db8be8SNicolas MURE 	switch (device_idx) {
3057b8db8be8SNicolas MURE 	case SND_DJM_850_IDX:		return snd_djm_get_label_cap_850(wvalue);
3058b8db8be8SNicolas MURE 	default:			return snd_djm_get_label_cap_common(wvalue);
3059b8db8be8SNicolas MURE 	}
3060b8db8be8SNicolas MURE };
3061b8db8be8SNicolas MURE 
snd_djm_get_label_pb(u16 wvalue)3062a07df82cSOlivia Mackintosh static const char *snd_djm_get_label_pb(u16 wvalue)
3063a07df82cSOlivia Mackintosh {
3064a07df82cSOlivia Mackintosh 	switch (wvalue & 0x00ff) {
3065a07df82cSOlivia Mackintosh 	case SND_DJM_PB_CH1:	return "Ch1";
3066a07df82cSOlivia Mackintosh 	case SND_DJM_PB_CH2:	return "Ch2";
3067a07df82cSOlivia Mackintosh 	case SND_DJM_PB_AUX:	return "Aux";
3068a07df82cSOlivia Mackintosh 	default:		return NULL;
3069a07df82cSOlivia Mackintosh 	}
3070a07df82cSOlivia Mackintosh };
3071a07df82cSOlivia Mackintosh 
snd_djm_get_label(u8 device_idx,u16 wvalue,u16 windex)3072b8db8be8SNicolas MURE static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex)
3073a07df82cSOlivia Mackintosh {
3074a07df82cSOlivia Mackintosh 	switch (windex) {
3075a07df82cSOlivia Mackintosh 	case SND_DJM_WINDEX_CAPLVL:	return snd_djm_get_label_caplevel(wvalue);
3076b8db8be8SNicolas MURE 	case SND_DJM_WINDEX_CAP:	return snd_djm_get_label_cap(device_idx, wvalue);
3077a07df82cSOlivia Mackintosh 	case SND_DJM_WINDEX_PB:		return snd_djm_get_label_pb(wvalue);
3078a07df82cSOlivia Mackintosh 	default:			return NULL;
3079a07df82cSOlivia Mackintosh 	}
3080a07df82cSOlivia Mackintosh };
3081a07df82cSOlivia Mackintosh 
30827687850bSNicolas MURE // common DJM capture level option values
3083a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_cap_level[] = {
3084a07df82cSOlivia Mackintosh 	0x0000, 0x0100, 0x0200, 0x0300 };
3085a07df82cSOlivia Mackintosh 
30867687850bSNicolas MURE 
30877687850bSNicolas MURE // DJM-250MK2
3088a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap1[] = {
3089a07df82cSOlivia Mackintosh 	0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a };
3090a07df82cSOlivia Mackintosh 
3091a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap2[] = {
3092a07df82cSOlivia Mackintosh 	0x0203, 0x0200, 0x0206, 0x0207, 0x0208, 0x0209, 0x020d, 0x020a };
3093a07df82cSOlivia Mackintosh 
3094a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_cap3[] = {
3095a07df82cSOlivia Mackintosh 	0x030a, 0x0311, 0x0312, 0x0307, 0x0308, 0x0309, 0x030d };
3096a07df82cSOlivia Mackintosh 
3097a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
3098a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
3099a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_250mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
3100a07df82cSOlivia Mackintosh 
3101a07df82cSOlivia Mackintosh static const struct snd_djm_ctl snd_djm_ctls_250mk2[] = {
3102a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
3103a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Input",   250mk2_cap1, 2, SND_DJM_WINDEX_CAP),
3104a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Input",   250mk2_cap2, 2, SND_DJM_WINDEX_CAP),
3105a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Input",   250mk2_cap3, 0, SND_DJM_WINDEX_CAP),
3106a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Output",   250mk2_pb1, 0, SND_DJM_WINDEX_PB),
3107a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Output",   250mk2_pb2, 1, SND_DJM_WINDEX_PB),
3108a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Output",   250mk2_pb3, 2, SND_DJM_WINDEX_PB)
3109a07df82cSOlivia Mackintosh };
3110a07df82cSOlivia Mackintosh 
3111a07df82cSOlivia Mackintosh 
31125ae225bbSSarah Grant // DJM-450
31135ae225bbSSarah Grant static const u16 snd_djm_opts_450_cap1[] = {
31145ae225bbSSarah Grant 	0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a };
31155ae225bbSSarah Grant 
31165ae225bbSSarah Grant static const u16 snd_djm_opts_450_cap2[] = {
31175ae225bbSSarah Grant 	0x0203, 0x0200, 0x0206, 0x0207, 0x0208, 0x0209, 0x020d, 0x020a };
31185ae225bbSSarah Grant 
31195ae225bbSSarah Grant static const u16 snd_djm_opts_450_cap3[] = {
31205ae225bbSSarah Grant 	0x030a, 0x0311, 0x0312, 0x0307, 0x0308, 0x0309, 0x030d };
31215ae225bbSSarah Grant 
31225ae225bbSSarah Grant static const u16 snd_djm_opts_450_pb1[] = { 0x0100, 0x0101, 0x0104 };
31235ae225bbSSarah Grant static const u16 snd_djm_opts_450_pb2[] = { 0x0200, 0x0201, 0x0204 };
31245ae225bbSSarah Grant static const u16 snd_djm_opts_450_pb3[] = { 0x0300, 0x0301, 0x0304 };
31255ae225bbSSarah Grant 
31265ae225bbSSarah Grant static const struct snd_djm_ctl snd_djm_ctls_450[] = {
31275ae225bbSSarah Grant 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
31285ae225bbSSarah Grant 	SND_DJM_CTL("Ch1 Input",   450_cap1, 2, SND_DJM_WINDEX_CAP),
31295ae225bbSSarah Grant 	SND_DJM_CTL("Ch2 Input",   450_cap2, 2, SND_DJM_WINDEX_CAP),
31305ae225bbSSarah Grant 	SND_DJM_CTL("Ch3 Input",   450_cap3, 0, SND_DJM_WINDEX_CAP),
31315ae225bbSSarah Grant 	SND_DJM_CTL("Ch1 Output",   450_pb1, 0, SND_DJM_WINDEX_PB),
31325ae225bbSSarah Grant 	SND_DJM_CTL("Ch2 Output",   450_pb2, 1, SND_DJM_WINDEX_PB),
31335ae225bbSSarah Grant 	SND_DJM_CTL("Ch3 Output",   450_pb3, 2, SND_DJM_WINDEX_PB)
31345ae225bbSSarah Grant };
31355ae225bbSSarah Grant 
31365ae225bbSSarah Grant 
3137a07df82cSOlivia Mackintosh // DJM-750
3138a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap1[] = {
3139a07df82cSOlivia Mackintosh 	0x0101, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
3140a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap2[] = {
3141a07df82cSOlivia Mackintosh 	0x0200, 0x0201, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020f };
3142a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap3[] = {
3143a07df82cSOlivia Mackintosh 	0x0300, 0x0301, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030f };
3144a07df82cSOlivia Mackintosh static const u16 snd_djm_opts_750_cap4[] = {
3145a07df82cSOlivia Mackintosh 	0x0401, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
3146a07df82cSOlivia Mackintosh 
3147a07df82cSOlivia Mackintosh static const struct snd_djm_ctl snd_djm_ctls_750[] = {
3148a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
3149a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch1 Input",   750_cap1, 2, SND_DJM_WINDEX_CAP),
3150a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch2 Input",   750_cap2, 2, SND_DJM_WINDEX_CAP),
3151a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch3 Input",   750_cap3, 0, SND_DJM_WINDEX_CAP),
3152a07df82cSOlivia Mackintosh 	SND_DJM_CTL("Ch4 Input",   750_cap4, 0, SND_DJM_WINDEX_CAP)
3153a07df82cSOlivia Mackintosh };
3154a07df82cSOlivia Mackintosh 
3155a07df82cSOlivia Mackintosh 
31567687850bSNicolas MURE // DJM-850
31577687850bSNicolas MURE static const u16 snd_djm_opts_850_cap1[] = {
31587687850bSNicolas MURE 	0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f };
31597687850bSNicolas MURE static const u16 snd_djm_opts_850_cap2[] = {
31607687850bSNicolas MURE 	0x0200, 0x0201, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a, 0x020f };
31617687850bSNicolas MURE static const u16 snd_djm_opts_850_cap3[] = {
31627687850bSNicolas MURE 	0x0300, 0x0301, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a, 0x030f };
31637687850bSNicolas MURE static const u16 snd_djm_opts_850_cap4[] = {
31647687850bSNicolas MURE 	0x0400, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040f };
31657687850bSNicolas MURE 
31667687850bSNicolas MURE static const struct snd_djm_ctl snd_djm_ctls_850[] = {
31677687850bSNicolas MURE 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
31687687850bSNicolas MURE 	SND_DJM_CTL("Ch1 Input",   850_cap1, 1, SND_DJM_WINDEX_CAP),
31697687850bSNicolas MURE 	SND_DJM_CTL("Ch2 Input",   850_cap2, 0, SND_DJM_WINDEX_CAP),
31707687850bSNicolas MURE 	SND_DJM_CTL("Ch3 Input",   850_cap3, 0, SND_DJM_WINDEX_CAP),
31717687850bSNicolas MURE 	SND_DJM_CTL("Ch4 Input",   850_cap4, 1, SND_DJM_WINDEX_CAP)
31727687850bSNicolas MURE };
31737687850bSNicolas MURE 
31747687850bSNicolas MURE 
3175fee03efcSFabian Lesniak // DJM-900NXS2
3176fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap1[] = {
3177fee03efcSFabian Lesniak 	0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
3178fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap2[] = {
3179fee03efcSFabian Lesniak 	0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
3180fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap3[] = {
3181fee03efcSFabian Lesniak 	0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
3182fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap4[] = {
3183fee03efcSFabian Lesniak 	0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
3184fee03efcSFabian Lesniak static const u16 snd_djm_opts_900nxs2_cap5[] = {
3185fee03efcSFabian Lesniak 	0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
3186fee03efcSFabian Lesniak 
3187fee03efcSFabian Lesniak static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = {
3188fee03efcSFabian Lesniak 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
3189fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch1 Input",   900nxs2_cap1, 2, SND_DJM_WINDEX_CAP),
3190fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch2 Input",   900nxs2_cap2, 2, SND_DJM_WINDEX_CAP),
3191fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch3 Input",   900nxs2_cap3, 2, SND_DJM_WINDEX_CAP),
3192fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch4 Input",   900nxs2_cap4, 2, SND_DJM_WINDEX_CAP),
3193fee03efcSFabian Lesniak 	SND_DJM_CTL("Ch5 Input",   900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
3194fee03efcSFabian Lesniak };
3195fee03efcSFabian Lesniak 
31966d277881SWilliam Overton // DJM-750MK2
31976d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap1[] = {
31986d277881SWilliam Overton 	0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
31996d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap2[] = {
32006d277881SWilliam Overton 	0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
32016d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap3[] = {
32026d277881SWilliam Overton 	0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
32036d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap4[] = {
32046d277881SWilliam Overton 	0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
32056d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_cap5[] = {
32066d277881SWilliam Overton 	0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
32076d277881SWilliam Overton 
32086d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
32096d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
32106d277881SWilliam Overton static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
32116d277881SWilliam Overton 
32126d277881SWilliam Overton 
32136d277881SWilliam Overton static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = {
32146d277881SWilliam Overton 	SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
32156d277881SWilliam Overton 	SND_DJM_CTL("Ch1 Input",   750mk2_cap1, 2, SND_DJM_WINDEX_CAP),
32166d277881SWilliam Overton 	SND_DJM_CTL("Ch2 Input",   750mk2_cap2, 2, SND_DJM_WINDEX_CAP),
32176d277881SWilliam Overton 	SND_DJM_CTL("Ch3 Input",   750mk2_cap3, 2, SND_DJM_WINDEX_CAP),
32186d277881SWilliam Overton 	SND_DJM_CTL("Ch4 Input",   750mk2_cap4, 2, SND_DJM_WINDEX_CAP),
32196d277881SWilliam Overton 	SND_DJM_CTL("Ch5 Input",   750mk2_cap5, 3, SND_DJM_WINDEX_CAP),
32206d277881SWilliam Overton 	SND_DJM_CTL("Ch1 Output",   750mk2_pb1, 0, SND_DJM_WINDEX_PB),
32216d277881SWilliam Overton 	SND_DJM_CTL("Ch2 Output",   750mk2_pb2, 1, SND_DJM_WINDEX_PB),
32226d277881SWilliam Overton 	SND_DJM_CTL("Ch3 Output",   750mk2_pb3, 2, SND_DJM_WINDEX_PB)
32236d277881SWilliam Overton };
32246d277881SWilliam Overton 
3225fee03efcSFabian Lesniak 
3226a07df82cSOlivia Mackintosh static const struct snd_djm_device snd_djm_devices[] = {
3227fb1af5beSGeraldo Nascimento 	[SND_DJM_250MK2_IDX] = SND_DJM_DEVICE(250mk2),
3228fb1af5beSGeraldo Nascimento 	[SND_DJM_750_IDX] = SND_DJM_DEVICE(750),
3229fb1af5beSGeraldo Nascimento 	[SND_DJM_850_IDX] = SND_DJM_DEVICE(850),
3230fb1af5beSGeraldo Nascimento 	[SND_DJM_900NXS2_IDX] = SND_DJM_DEVICE(900nxs2),
3231fb1af5beSGeraldo Nascimento 	[SND_DJM_750MK2_IDX] = SND_DJM_DEVICE(750mk2),
32325ae225bbSSarah Grant 	[SND_DJM_450_IDX] = SND_DJM_DEVICE(450),
3233a07df82cSOlivia Mackintosh };
3234a07df82cSOlivia Mackintosh 
3235a07df82cSOlivia Mackintosh 
snd_djm_controls_info(struct snd_kcontrol * kctl,struct snd_ctl_elem_info * info)3236a07df82cSOlivia Mackintosh static int snd_djm_controls_info(struct snd_kcontrol *kctl,
3237a07df82cSOlivia Mackintosh 				struct snd_ctl_elem_info *info)
3238a07df82cSOlivia Mackintosh {
3239a07df82cSOlivia Mackintosh 	unsigned long private_value = kctl->private_value;
3240a07df82cSOlivia Mackintosh 	u8 device_idx = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
3241a07df82cSOlivia Mackintosh 	u8 ctl_idx = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
3242a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
3243a07df82cSOlivia Mackintosh 	const char *name;
3244a07df82cSOlivia Mackintosh 	const struct snd_djm_ctl *ctl;
3245a07df82cSOlivia Mackintosh 	size_t noptions;
3246a07df82cSOlivia Mackintosh 
3247a07df82cSOlivia Mackintosh 	if (ctl_idx >= device->ncontrols)
3248cdc01a15SFrantišek Kučera 		return -EINVAL;
3249cdc01a15SFrantišek Kučera 
3250a07df82cSOlivia Mackintosh 	ctl = &device->controls[ctl_idx];
3251a07df82cSOlivia Mackintosh 	noptions = ctl->noptions;
3252a07df82cSOlivia Mackintosh 	if (info->value.enumerated.item >= noptions)
3253a07df82cSOlivia Mackintosh 		info->value.enumerated.item = noptions - 1;
3254a07df82cSOlivia Mackintosh 
3255b8db8be8SNicolas MURE 	name = snd_djm_get_label(device_idx,
3256b8db8be8SNicolas MURE 				ctl->options[info->value.enumerated.item],
3257a07df82cSOlivia Mackintosh 				ctl->wIndex);
3258a07df82cSOlivia Mackintosh 	if (!name)
3259a07df82cSOlivia Mackintosh 		return -EINVAL;
3260a07df82cSOlivia Mackintosh 
326175b1a8f9SJoe Perches 	strscpy(info->value.enumerated.name, name, sizeof(info->value.enumerated.name));
3262cdc01a15SFrantišek Kučera 	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
3263cdc01a15SFrantišek Kučera 	info->count = 1;
3264a07df82cSOlivia Mackintosh 	info->value.enumerated.items = noptions;
3265cdc01a15SFrantišek Kučera 	return 0;
3266cdc01a15SFrantišek Kučera }
3267cdc01a15SFrantišek Kučera 
snd_djm_controls_update(struct usb_mixer_interface * mixer,u8 device_idx,u8 group,u16 value)3268a07df82cSOlivia Mackintosh static int snd_djm_controls_update(struct usb_mixer_interface *mixer,
3269a07df82cSOlivia Mackintosh 				u8 device_idx, u8 group, u16 value)
3270cdc01a15SFrantišek Kučera {
3271cdc01a15SFrantišek Kučera 	int err;
3272a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
3273cdc01a15SFrantišek Kučera 
3274a07df82cSOlivia Mackintosh 	if ((group >= device->ncontrols) || value >= device->controls[group].noptions)
3275cdc01a15SFrantišek Kučera 		return -EINVAL;
3276cdc01a15SFrantišek Kučera 
3277cdc01a15SFrantišek Kučera 	err = snd_usb_lock_shutdown(mixer->chip);
3278cdc01a15SFrantišek Kučera 	if (err)
3279cdc01a15SFrantišek Kučera 		return err;
3280cdc01a15SFrantišek Kučera 
3281cdc01a15SFrantišek Kučera 	err = snd_usb_ctl_msg(
3282cdc01a15SFrantišek Kučera 		mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0),
3283cdc01a15SFrantišek Kučera 		USB_REQ_SET_FEATURE,
3284cdc01a15SFrantišek Kučera 		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
3285a07df82cSOlivia Mackintosh 		device->controls[group].options[value],
3286a07df82cSOlivia Mackintosh 		device->controls[group].wIndex,
3287cdc01a15SFrantišek Kučera 		NULL, 0);
3288cdc01a15SFrantišek Kučera 
3289cdc01a15SFrantišek Kučera 	snd_usb_unlock_shutdown(mixer->chip);
3290cdc01a15SFrantišek Kučera 	return err;
3291cdc01a15SFrantišek Kučera }
3292cdc01a15SFrantišek Kučera 
snd_djm_controls_get(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * elem)3293a07df82cSOlivia Mackintosh static int snd_djm_controls_get(struct snd_kcontrol *kctl,
3294a07df82cSOlivia Mackintosh 				struct snd_ctl_elem_value *elem)
3295cdc01a15SFrantišek Kučera {
3296a07df82cSOlivia Mackintosh 	elem->value.enumerated.item[0] = kctl->private_value & SND_DJM_VALUE_MASK;
3297cdc01a15SFrantišek Kučera 	return 0;
3298cdc01a15SFrantišek Kučera }
3299cdc01a15SFrantišek Kučera 
snd_djm_controls_put(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * elem)3300a07df82cSOlivia Mackintosh static int snd_djm_controls_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *elem)
3301cdc01a15SFrantišek Kučera {
3302cdc01a15SFrantišek Kučera 	struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
3303cdc01a15SFrantišek Kučera 	struct usb_mixer_interface *mixer = list->mixer;
3304cdc01a15SFrantišek Kučera 	unsigned long private_value = kctl->private_value;
3305a07df82cSOlivia Mackintosh 
3306a07df82cSOlivia Mackintosh 	u8 device = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
3307a07df82cSOlivia Mackintosh 	u8 group = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
3308cdc01a15SFrantišek Kučera 	u16 value = elem->value.enumerated.item[0];
3309cdc01a15SFrantišek Kučera 
331050b1affcSColin Ian King 	kctl->private_value = (((unsigned long)device << SND_DJM_DEVICE_SHIFT) |
3311a07df82cSOlivia Mackintosh 			      (group << SND_DJM_GROUP_SHIFT) |
3312a07df82cSOlivia Mackintosh 			      value);
3313cdc01a15SFrantišek Kučera 
3314a07df82cSOlivia Mackintosh 	return snd_djm_controls_update(mixer, device, group, value);
3315cdc01a15SFrantišek Kučera }
3316cdc01a15SFrantišek Kučera 
snd_djm_controls_resume(struct usb_mixer_elem_list * list)3317a07df82cSOlivia Mackintosh static int snd_djm_controls_resume(struct usb_mixer_elem_list *list)
3318cdc01a15SFrantišek Kučera {
3319cdc01a15SFrantišek Kučera 	unsigned long private_value = list->kctl->private_value;
3320a07df82cSOlivia Mackintosh 	u8 device = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT;
3321a07df82cSOlivia Mackintosh 	u8 group = (private_value & SND_DJM_GROUP_MASK) >> SND_DJM_GROUP_SHIFT;
3322a07df82cSOlivia Mackintosh 	u16 value = (private_value & SND_DJM_VALUE_MASK);
3323cdc01a15SFrantišek Kučera 
3324a07df82cSOlivia Mackintosh 	return snd_djm_controls_update(list->mixer, device, group, value);
3325cdc01a15SFrantišek Kučera }
3326cdc01a15SFrantišek Kučera 
snd_djm_controls_create(struct usb_mixer_interface * mixer,const u8 device_idx)3327a07df82cSOlivia Mackintosh static int snd_djm_controls_create(struct usb_mixer_interface *mixer,
3328a07df82cSOlivia Mackintosh 		const u8 device_idx)
3329cdc01a15SFrantišek Kučera {
3330cdc01a15SFrantišek Kučera 	int err, i;
3331a07df82cSOlivia Mackintosh 	u16 value;
3332a07df82cSOlivia Mackintosh 
3333a07df82cSOlivia Mackintosh 	const struct snd_djm_device *device = &snd_djm_devices[device_idx];
3334a07df82cSOlivia Mackintosh 
3335cdc01a15SFrantišek Kučera 	struct snd_kcontrol_new knew = {
3336cdc01a15SFrantišek Kučera 		.iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
3337cdc01a15SFrantišek Kučera 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
3338cdc01a15SFrantišek Kučera 		.index = 0,
3339a07df82cSOlivia Mackintosh 		.info = snd_djm_controls_info,
3340a07df82cSOlivia Mackintosh 		.get  = snd_djm_controls_get,
3341a07df82cSOlivia Mackintosh 		.put  = snd_djm_controls_put
3342cdc01a15SFrantišek Kučera 	};
3343cdc01a15SFrantišek Kučera 
3344a07df82cSOlivia Mackintosh 	for (i = 0; i < device->ncontrols; i++) {
3345a07df82cSOlivia Mackintosh 		value = device->controls[i].default_value;
3346a07df82cSOlivia Mackintosh 		knew.name = device->controls[i].name;
3347a07df82cSOlivia Mackintosh 		knew.private_value = (
334850b1affcSColin Ian King 			((unsigned long)device_idx << SND_DJM_DEVICE_SHIFT) |
3349a07df82cSOlivia Mackintosh 			(i << SND_DJM_GROUP_SHIFT) |
3350a07df82cSOlivia Mackintosh 			value);
3351a07df82cSOlivia Mackintosh 		err = snd_djm_controls_update(mixer, device_idx, i, value);
3352cdc01a15SFrantišek Kučera 		if (err)
3353cdc01a15SFrantišek Kučera 			return err;
3354a07df82cSOlivia Mackintosh 		err = add_single_ctl_with_resume(mixer, 0, snd_djm_controls_resume,
3355cdc01a15SFrantišek Kučera 						 &knew, NULL);
3356cdc01a15SFrantišek Kučera 		if (err)
3357cdc01a15SFrantišek Kučera 			return err;
3358cdc01a15SFrantišek Kučera 	}
3359cdc01a15SFrantišek Kučera 	return 0;
3360cdc01a15SFrantišek Kučera }
3361cdc01a15SFrantišek Kučera 
snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface * mixer)33627b1eda22SDaniel Mack int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
33637b1eda22SDaniel Mack {
33643347b26cSDaniel Mack 	int err = 0;
33657b1eda22SDaniel Mack 
3366f25ecf8fSTakashi Iwai 	err = snd_usb_soundblaster_remote_init(mixer);
3367f25ecf8fSTakashi Iwai 	if (err < 0)
33687b1eda22SDaniel Mack 		return err;
33697b1eda22SDaniel Mack 
33703347b26cSDaniel Mack 	switch (mixer->chip->usb_id) {
3371d2bb390aSDetlef Urban 	/* Tascam US-16x08 */
3372d2bb390aSDetlef Urban 	case USB_ID(0x0644, 0x8047):
3373d2bb390aSDetlef Urban 		err = snd_us16x08_controls_create(mixer);
3374d2bb390aSDetlef Urban 		break;
33753347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3020):
33763347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3040):
33773347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3042):
33787cdd8d73SMathieu Bouffard 	case USB_ID(0x041e, 0x30df):
33793347b26cSDaniel Mack 	case USB_ID(0x041e, 0x3048):
33803347b26cSDaniel Mack 		err = snd_audigy2nx_controls_create(mixer);
33813347b26cSDaniel Mack 		if (err < 0)
33823347b26cSDaniel Mack 			break;
33837449054aSTakashi Iwai 		snd_card_ro_proc_new(mixer->chip->card, "audigy2nx",
33847449054aSTakashi Iwai 				     mixer, snd_audigy2nx_proc_read);
33853347b26cSDaniel Mack 		break;
33867b1eda22SDaniel Mack 
338744832a71SVasily Khoruzhick 	/* EMU0204 */
338844832a71SVasily Khoruzhick 	case USB_ID(0x041e, 0x3f19):
338944832a71SVasily Khoruzhick 		err = snd_emu0204_controls_create(mixer);
339044832a71SVasily Khoruzhick 		break;
339144832a71SVasily Khoruzhick 
339209d8e3a7SEldad Zack 	case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
3393e9a25e04SMatt Gruskin 	case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
339409d8e3a7SEldad Zack 		err = snd_c400_create_mixer(mixer);
339509d8e3a7SEldad Zack 		break;
339609d8e3a7SEldad Zack 
3397d5a0bf6cSDaniel Mack 	case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
3398d5a0bf6cSDaniel Mack 	case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
3399cfe8f97cSFelix Homann 		err = snd_ftu_create_mixer(mixer);
3400d5a0bf6cSDaniel Mack 		break;
3401d5a0bf6cSDaniel Mack 
34021d31affbSDenis Washington 	case USB_ID(0x0b05, 0x1739): /* ASUS Xonar U1 */
34031d31affbSDenis Washington 	case USB_ID(0x0b05, 0x1743): /* ASUS Xonar U1 (2) */
34041d31affbSDenis Washington 	case USB_ID(0x0b05, 0x17a0): /* ASUS Xonar U3 */
34057b1eda22SDaniel Mack 		err = snd_xonar_u1_controls_create(mixer);
34063347b26cSDaniel Mack 		break;
34077b1eda22SDaniel Mack 
3408066624c6SPrzemek Rudy 	case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */
3409066624c6SPrzemek Rudy 		err = snd_microii_controls_create(mixer);
3410066624c6SPrzemek Rudy 		break;
3411066624c6SPrzemek Rudy 
3412d497a82fSDamien Zammit 	case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */
34137ac2246fSDamien Zammit 		err = snd_mbox1_controls_create(mixer);
3414d497a82fSDamien Zammit 		break;
3415d497a82fSDamien Zammit 
34163347b26cSDaniel Mack 	case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */
341754a8c500SDaniel Mack 		err = snd_nativeinstruments_create_mixer(mixer,
341854a8c500SDaniel Mack 				snd_nativeinstruments_ta6_mixers,
341954a8c500SDaniel Mack 				ARRAY_SIZE(snd_nativeinstruments_ta6_mixers));
34203347b26cSDaniel Mack 		break;
342154a8c500SDaniel Mack 
34223347b26cSDaniel Mack 	case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */
342354a8c500SDaniel Mack 		err = snd_nativeinstruments_create_mixer(mixer,
342454a8c500SDaniel Mack 				snd_nativeinstruments_ta10_mixers,
342554a8c500SDaniel Mack 				ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
34263347b26cSDaniel Mack 		break;
34277536c301SMark Hills 
34287536c301SMark Hills 	case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
3429b71dad18SMark Hills 		/* detection is disabled in mixer_maps.c */
3430b71dad18SMark Hills 		err = snd_create_std_mono_table(mixer, ebox44_table);
34317536c301SMark Hills 		break;
343276b188c4SChris J Arges 
343376b188c4SChris J Arges 	case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */
343476b188c4SChris J Arges 	case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */
343576b188c4SChris J Arges 	case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */
343676b188c4SChris J Arges 	case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */
343776b188c4SChris J Arges 	case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */
343876b188c4SChris J Arges 		err = snd_scarlett_controls_create(mixer);
343976b188c4SChris J Arges 		break;
3440388fdb8fSIan Douglas Scott 
34419e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */
34429e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */
34439e4d5c1bSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */
34442fa96277SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8211): /* Focusrite Scarlett Solo 3rd Gen */
34452fa96277SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8210): /* Focusrite Scarlett 2i2 3rd Gen */
34464be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8212): /* Focusrite Scarlett 4i4 3rd Gen */
34474be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
34484be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
34494be47798SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
34504ef92905SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8206): /* Focusrite Clarett 2Pre USB */
34514ef92905SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8207): /* Focusrite Clarett 4Pre USB */
3452a95f279eSGeoffrey D. Bennett 	case USB_ID(0x1235, 0x8208): /* Focusrite Clarett 8Pre USB */
345392088c48SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x820a): /* Focusrite Clarett+ 2Pre */
345492088c48SGeoffrey D. Bennett 	case USB_ID(0x1235, 0x820b): /* Focusrite Clarett+ 4Pre */
3455f71c70dfSChristian Colglazier 	case USB_ID(0x1235, 0x820c): /* Focusrite Clarett+ 8Pre */
3456771ad4dfSGeoffrey D. Bennett 		err = snd_scarlett2_init(mixer);
34579e4d5c1bSGeoffrey D. Bennett 		break;
34589e4d5c1bSGeoffrey D. Bennett 
3459388fdb8fSIan Douglas Scott 	case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
3460388fdb8fSIan Douglas Scott 		err = snd_soundblaster_e1_switch_create(mixer);
3461388fdb8fSIan Douglas Scott 		break;
3462964af639STakashi Iwai 	case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
34634b8ea38fSJan Schär 		err = dell_dock_mixer_create(mixer);
34644b8ea38fSJan Schär 		if (err < 0)
34654b8ea38fSJan Schär 			break;
3466964af639STakashi Iwai 		err = dell_dock_mixer_init(mixer);
3467964af639STakashi Iwai 		break;
3468d39f1d68SJussi Laako 
3469d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd2): /* RME ADI-2 Pro */
3470d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd3): /* RME ADI-2 DAC */
3471d39f1d68SJussi Laako 	case USB_ID(0x2a39, 0x3fd4): /* RME */
3472d39f1d68SJussi Laako 		err = snd_rme_controls_create(mixer);
3473d39f1d68SJussi Laako 		break;
34748dc5efe3SNick Kossifidis 
34751e583aefSTakashi Iwai 	case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */
34768dc5efe3SNick Kossifidis 		err = snd_sc1810_init_mixer(mixer);
34778dc5efe3SNick Kossifidis 		break;
34783e8f3bd0SThomas Ebeling 	case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */
34793e8f3bd0SThomas Ebeling 		err = snd_bbfpro_controls_create(mixer);
34803e8f3bd0SThomas Ebeling 		break;
3481cdc01a15SFrantišek Kučera 	case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */
3482a07df82cSOlivia Mackintosh 		err = snd_djm_controls_create(mixer, SND_DJM_250MK2_IDX);
3483a07df82cSOlivia Mackintosh 		break;
34845ae225bbSSarah Grant 	case USB_ID(0x2b73, 0x0013): /* Pioneer DJ DJM-450 */
34855ae225bbSSarah Grant 		err = snd_djm_controls_create(mixer, SND_DJM_450_IDX);
34865ae225bbSSarah Grant 		break;
3487a07df82cSOlivia Mackintosh 	case USB_ID(0x08e4, 0x017f): /* Pioneer DJ DJM-750 */
3488a07df82cSOlivia Mackintosh 		err = snd_djm_controls_create(mixer, SND_DJM_750_IDX);
3489cdc01a15SFrantišek Kučera 		break;
34906d277881SWilliam Overton 	case USB_ID(0x2b73, 0x001b): /* Pioneer DJ DJM-750MK2 */
34916d277881SWilliam Overton 		err = snd_djm_controls_create(mixer, SND_DJM_750MK2_IDX);
34926d277881SWilliam Overton 		break;
34937687850bSNicolas MURE 	case USB_ID(0x08e4, 0x0163): /* Pioneer DJ DJM-850 */
34947687850bSNicolas MURE 		err = snd_djm_controls_create(mixer, SND_DJM_850_IDX);
34957687850bSNicolas MURE 		break;
3496fee03efcSFabian Lesniak 	case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
3497fee03efcSFabian Lesniak 		err = snd_djm_controls_create(mixer, SND_DJM_900NXS2_IDX);
3498fee03efcSFabian Lesniak 		break;
349954a8c500SDaniel Mack 	}
350054a8c500SDaniel Mack 
35013347b26cSDaniel Mack 	return err;
35027b1eda22SDaniel Mack }
35037b1eda22SDaniel Mack 
snd_usb_mixer_resume_quirk(struct usb_mixer_interface * mixer)3504964af639STakashi Iwai void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer)
3505964af639STakashi Iwai {
3506964af639STakashi Iwai 	switch (mixer->chip->usb_id) {
3507964af639STakashi Iwai 	case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */
3508964af639STakashi Iwai 		dell_dock_mixer_init(mixer);
3509964af639STakashi Iwai 		break;
3510964af639STakashi Iwai 	}
3511964af639STakashi Iwai }
3512964af639STakashi Iwai 
snd_usb_mixer_rc_memory_change(struct usb_mixer_interface * mixer,int unitid)35137b1eda22SDaniel Mack void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
35147b1eda22SDaniel Mack 				    int unitid)
35157b1eda22SDaniel Mack {
35167b1eda22SDaniel Mack 	if (!mixer->rc_cfg)
35177b1eda22SDaniel Mack 		return;
35187b1eda22SDaniel Mack 	/* unit ids specific to Extigy/Audigy 2 NX: */
35197b1eda22SDaniel Mack 	switch (unitid) {
35207b1eda22SDaniel Mack 	case 0: /* remote control */
35217b1eda22SDaniel Mack 		mixer->rc_urb->dev = mixer->chip->dev;
35227b1eda22SDaniel Mack 		usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
35237b1eda22SDaniel Mack 		break;
35247b1eda22SDaniel Mack 	case 4: /* digital in jack */
35257b1eda22SDaniel Mack 	case 7: /* line in jacks */
35267b1eda22SDaniel Mack 	case 19: /* speaker out jacks */
35277b1eda22SDaniel Mack 	case 20: /* headphones out jack */
35287b1eda22SDaniel Mack 		break;
35297b1eda22SDaniel Mack 	/* live24ext: 4 = line-in jack */
35307b1eda22SDaniel Mack 	case 3:	/* hp-out jack (may actuate Mute) */
35317b1eda22SDaniel Mack 		if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
35327b1eda22SDaniel Mack 		    mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
35337b1eda22SDaniel Mack 			snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
35347b1eda22SDaniel Mack 		break;
35357b1eda22SDaniel Mack 	default:
35360ba41d91STakashi Iwai 		usb_audio_dbg(mixer->chip, "memory change in unknown unit %d\n", unitid);
35377b1eda22SDaniel Mack 		break;
35387b1eda22SDaniel Mack 	}
35397b1eda22SDaniel Mack }
35407b1eda22SDaniel Mack 
snd_dragonfly_quirk_db_scale(struct usb_mixer_interface * mixer,struct usb_mixer_elem_info * cval,struct snd_kcontrol * kctl)354142e3121dSAnssi Hannula static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
3542eb1a74b7SAnssi Hannula 					 struct usb_mixer_elem_info *cval,
354342e3121dSAnssi Hannula 					 struct snd_kcontrol *kctl)
354442e3121dSAnssi Hannula {
354542e3121dSAnssi Hannula 	/* Approximation using 10 ranges based on output measurement on hw v1.2.
354642e3121dSAnssi Hannula 	 * This seems close to the cubic mapping e.g. alsamixer uses. */
354742e3121dSAnssi Hannula 	static const DECLARE_TLV_DB_RANGE(scale,
354842e3121dSAnssi Hannula 		 0,  1, TLV_DB_MINMAX_ITEM(-5300, -4970),
354942e3121dSAnssi Hannula 		 2,  5, TLV_DB_MINMAX_ITEM(-4710, -4160),
355042e3121dSAnssi Hannula 		 6,  7, TLV_DB_MINMAX_ITEM(-3884, -3710),
355142e3121dSAnssi Hannula 		 8, 14, TLV_DB_MINMAX_ITEM(-3443, -2560),
355242e3121dSAnssi Hannula 		15, 16, TLV_DB_MINMAX_ITEM(-2475, -2324),
355342e3121dSAnssi Hannula 		17, 19, TLV_DB_MINMAX_ITEM(-2228, -2031),
355442e3121dSAnssi Hannula 		20, 26, TLV_DB_MINMAX_ITEM(-1910, -1393),
355542e3121dSAnssi Hannula 		27, 31, TLV_DB_MINMAX_ITEM(-1322, -1032),
355642e3121dSAnssi Hannula 		32, 40, TLV_DB_MINMAX_ITEM(-968, -490),
355742e3121dSAnssi Hannula 		41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
355842e3121dSAnssi Hannula 	);
355942e3121dSAnssi Hannula 
3560eb1a74b7SAnssi Hannula 	if (cval->min == 0 && cval->max == 50) {
3561eb1a74b7SAnssi Hannula 		usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n");
356242e3121dSAnssi Hannula 		kctl->tlv.p = scale;
356342e3121dSAnssi Hannula 		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
356442e3121dSAnssi Hannula 		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
3565eb1a74b7SAnssi Hannula 
3566eb1a74b7SAnssi Hannula 	} else if (cval->min == 0 && cval->max <= 1000) {
3567eb1a74b7SAnssi Hannula 		/* Some other clearly broken DragonFly variant.
3568eb1a74b7SAnssi Hannula 		 * At least a 0..53 variant (hw v1.0) exists.
3569eb1a74b7SAnssi Hannula 		 */
3570eb1a74b7SAnssi Hannula 		usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device");
3571eb1a74b7SAnssi Hannula 		kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
3572eb1a74b7SAnssi Hannula 	}
357342e3121dSAnssi Hannula }
357442e3121dSAnssi Hannula 
snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface * mixer,struct usb_mixer_elem_info * cval,int unitid,struct snd_kcontrol * kctl)357542e3121dSAnssi Hannula void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
357642e3121dSAnssi Hannula 				  struct usb_mixer_elem_info *cval, int unitid,
357742e3121dSAnssi Hannula 				  struct snd_kcontrol *kctl)
357842e3121dSAnssi Hannula {
357942e3121dSAnssi Hannula 	switch (mixer->chip->usb_id) {
358042e3121dSAnssi Hannula 	case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
3581eb1a74b7SAnssi Hannula 		if (unitid == 7 && cval->control == UAC_FU_VOLUME)
3582eb1a74b7SAnssi Hannula 			snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
358342e3121dSAnssi Hannula 		break;
35840f306ccaSLars-Peter Clausen 	/* lowest playback value is muted on some devices */
35850f306ccaSLars-Peter Clausen 	case USB_ID(0x0d8c, 0x000c): /* C-Media */
35860f306ccaSLars-Peter Clausen 	case USB_ID(0x0d8c, 0x0014): /* C-Media */
35870f306ccaSLars-Peter Clausen 	case USB_ID(0x19f7, 0x0003): /* RODE NT-USB */
35880f174b35STakashi Iwai 		if (strstr(kctl->id.name, "Playback"))
35890f174b35STakashi Iwai 			cval->min_mute = 1;
35900f174b35STakashi Iwai 		break;
359142e3121dSAnssi Hannula 	}
359242e3121dSAnssi Hannula }
359342e3121dSAnssi Hannula 
3594