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