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" 369e4d5c1bSGeoffrey D. Bennett #include "mixer_scarlett_gen2.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 */ 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) */ 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 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 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 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 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 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*/ 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 */ 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 */ 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 */ 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 */ 3456265d1a90SGeoffrey D. Bennett err = snd_scarlett_gen2_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 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 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 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 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