Newer
Older
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* USB Audio Driver for ALSA
*
* Quirks and vendor-specific extensions for mixer interfaces
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
*
* Many codes borrowed from audio.c by
* Alan Cox (alan@lxorguk.ukuu.org.uk)
* Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* Audio Advantage Micro II support added by:
* Przemek Rudy (prudy1@o2.pl)
#include <linux/bitfield.h>
#include <linux/hid.h>
#include <linux/math64.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/hda_verbs.h>
#include <sound/hwdep.h>
#include <sound/info.h>
#include <sound/tlv.h>
#include "mixer_scarlett.h"
#include "mixer_scarlett2.h"
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
struct std_mono_table {
unsigned int unitid, control, cmask;
int val_type;
const char *name;
snd_kcontrol_tlv_rw_t *tlv_callback;
};
/* This function allows for the creation of standard UAC controls.
* See the quirks for M-Audio FTUs or Ebox-44.
* If you don't want to set a TLV callback pass NULL.
*
* Since there doesn't seem to be a devices that needs a multichannel
* version, we keep it mono for simplicity.
*/
static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer,
unsigned int unitid,
unsigned int control,
unsigned int cmask,
int val_type,
const char *name,
snd_kcontrol_tlv_rw_t *tlv_callback)
{
struct usb_mixer_elem_info *cval;
struct snd_kcontrol *kctl;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return -ENOMEM;
snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
cval->val_type = val_type;
cval->channels = 1;
cval->control = control;
cval->cmask = cmask;
/* get_min_max() is called only for integer volumes later,
* so provide a short-cut for booleans */
cval->min = 0;
cval->max = 1;
cval->res = 0;
cval->dBmin = 0;
cval->dBmax = 0;
/* Create control */
kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
if (!kctl) {
kfree(cval);
return -ENOMEM;
}
/* Set name */
snprintf(kctl->id.name, sizeof(kctl->id.name), name);
kctl->private_free = snd_usb_mixer_elem_free;
/* set TLV */
if (tlv_callback) {
kctl->tlv.c = tlv_callback;
kctl->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
/* Add control to mixer */
return snd_usb_mixer_add_control(&cval->head, kctl);
}
static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
unsigned int unitid,
unsigned int control,
unsigned int cmask,
int val_type,
const char *name,
snd_kcontrol_tlv_rw_t *tlv_callback)
{
return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask,
val_type, 0 /* Offset */, name, tlv_callback);
}
/*
* Create a set of standard UAC controls from a table
*/
static int snd_create_std_mono_table(struct usb_mixer_interface *mixer,
{
int err;
while (t->name != NULL) {
err = snd_create_std_mono_ctl(mixer, t->unitid, t->control,
t->cmask, t->val_type, t->name, t->tlv_callback);
if (err < 0)
return err;
t++;
}
return 0;
}
static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer,
int id,
usb_mixer_elem_resume_func_t resume,
const struct snd_kcontrol_new *knew,
struct usb_mixer_elem_list **listp)
{
struct usb_mixer_elem_list *list;
struct snd_kcontrol *kctl;
list = kzalloc(sizeof(*list), GFP_KERNEL);
if (!list)
return -ENOMEM;
if (listp)
*listp = list;
list->mixer = mixer;
list->id = id;
list->resume = resume;
kctl = snd_ctl_new1(knew, list);
if (!kctl) {
kfree(list);
return -ENOMEM;
}
kctl->private_free = snd_usb_mixer_elem_free;
/* don't use snd_usb_mixer_add_control() here, this is a special list element */
return snd_usb_mixer_add_list(list, kctl, false);
/*
* Sound Blaster remote control configuration
*
* format of remote control data:
* Extigy: xx 00
* Audigy 2 NX: 06 80 xx 00 00 00
* Live! 24-bit: 06 80 xx yy 22 83
*/
static const struct rc_config {
u32 usb_id;
u8 offset;
u8 length;
u8 packet_length;
u8 min_packet_length; /* minimum accepted length of the URB result */
u8 mute_mixer_id;
u32 mute_code;
} rc_configs[] = {
{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1, 18, 0x0013 }, /* Extigy */
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
{ USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3237), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
{ USB_ID(0x041e, 0x3263), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
};
static void snd_usb_soundblaster_remote_complete(struct urb *urb)
{
struct usb_mixer_interface *mixer = urb->context;
const struct rc_config *rc = mixer->rc_cfg;
u32 code;
if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
return;
code = mixer->rc_buffer[rc->offset];
if (rc->length == 2)
code |= mixer->rc_buffer[rc->offset + 1] << 8;
/* the Mute button actually changes the mixer control */
if (code == rc->mute_code)
snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
mixer->rc_code = code;
wmb();
wake_up(&mixer->rc_waitq);
}
static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
long count, loff_t *offset)
{
struct usb_mixer_interface *mixer = hw->private_data;
int err;
u32 rc_code;
if (count != 1 && count != 4)
return -EINVAL;
err = wait_event_interruptible(mixer->rc_waitq,
(rc_code = xchg(&mixer->rc_code, 0)) != 0);
if (err == 0) {
if (count == 1)
err = put_user(rc_code, buf);
else
err = put_user(rc_code, (u32 __user *)buf);
}
return err < 0 ? err : count;
}
static __poll_t snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
poll_table *wait)
{
struct usb_mixer_interface *mixer = hw->private_data;
poll_wait(file, &mixer->rc_waitq, wait);
return mixer->rc_code ? EPOLLIN | EPOLLRDNORM : 0;
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
}
static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
{
struct snd_hwdep *hwdep;
int err, len, i;
for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
if (rc_configs[i].usb_id == mixer->chip->usb_id)
break;
if (i >= ARRAY_SIZE(rc_configs))
return 0;
mixer->rc_cfg = &rc_configs[i];
len = mixer->rc_cfg->packet_length;
init_waitqueue_head(&mixer->rc_waitq);
err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
if (err < 0)
return err;
snprintf(hwdep->name, sizeof(hwdep->name),
"%s remote control", mixer->chip->card->shortname);
hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
hwdep->private_data = mixer;
hwdep->ops.read = snd_usb_sbrc_hwdep_read;
hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
hwdep->exclusive = 1;
mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!mixer->rc_urb)
return -ENOMEM;
mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
if (!mixer->rc_setup_packet) {
usb_free_urb(mixer->rc_urb);
mixer->rc_urb = NULL;
return -ENOMEM;
}
mixer->rc_setup_packet->bRequestType =
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
mixer->rc_setup_packet->wValue = cpu_to_le16(0);
mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
mixer->rc_setup_packet->wLength = cpu_to_le16(len);
usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0),
(u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
snd_usb_soundblaster_remote_complete, mixer);
return 0;
}
#define snd_audigy2nx_led_info snd_ctl_boolean_mono_info
static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = kcontrol->private_value >> 8;
static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer,
int value, int index)
struct snd_usb_audio *chip = mixer->chip;
int err;
err = snd_usb_lock_shutdown(chip);
if (err < 0)
return err;
if (chip->usb_id == USB_ID(0x041e, 0x3042))
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
!value, 0, NULL, 0);
/* USB X-Fi S51 Pro */
if (chip->usb_id == USB_ID(0x041e, 0x30df))
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
!value, 0, NULL, 0);
else
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x24,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
value, index + 2, NULL, 0);
snd_usb_unlock_shutdown(chip);
static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
int index = kcontrol->private_value & 0xff;
unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
int err;
if (value > 1)
return -EINVAL;
if (value == old_value)
return 0;
kcontrol->private_value = (value << 8) | index;
err = snd_audigy2nx_led_update(mixer, value, index);
return err < 0 ? err : 1;
}
static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
{
int priv_value = list->kctl->private_value;
return snd_audigy2nx_led_update(list->mixer, priv_value >> 8,
priv_value & 0xff);
}
/* name and private_value are set dynamically */
static const struct snd_kcontrol_new snd_audigy2nx_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_audigy2nx_led_info,
.get = snd_audigy2nx_led_get,
.put = snd_audigy2nx_led_put,
};
static const char * const snd_audigy2nx_led_names[] = {
"CMSS LED Switch",
"Power LED Switch",
"Dolby Digital LED Switch",
};
static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
{
int i, err;
for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) {
struct snd_kcontrol_new knew;
/* USB X-Fi S51 doesn't have a CMSS LED */
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
continue;
/* USB X-Fi S51 Pro doesn't have one either */
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
continue;
if (i > 1 && /* Live24ext has 2 LEDs only */
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
break;
knew = snd_audigy2nx_control;
knew.name = snd_audigy2nx_led_names[i];
knew.private_value = (1 << 8) | i; /* LED on as default */
err = add_single_ctl_with_resume(mixer, 0,
snd_audigy2nx_led_resume,
&knew, NULL);
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
if (err < 0)
return err;
}
return 0;
}
static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
static const struct sb_jack {
int unitid;
const char *name;
} jacks_audigy2nx[] = {
{4, "dig in "},
{7, "line in"},
{19, "spk out"},
{20, "hph out"},
{-1, NULL}
}, jacks_live24ext[] = {
{4, "line in"}, /* &1=Line, &2=Mic*/
{3, "hph out"}, /* headphones */
{0, "RC "}, /* last command, 6 bytes see rc_config above */
{-1, NULL}
};
const struct sb_jack *jacks;
struct usb_mixer_interface *mixer = entry->private_data;
int i, err;
u8 buf[3];
snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
jacks = jacks_audigy2nx;
else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
jacks = jacks_live24ext;
else
return;
for (i = 0; jacks[i].name; ++i) {
snd_iprintf(buffer, "%s: ", jacks[i].name);
err = snd_usb_lock_shutdown(mixer->chip);
if (err < 0)
return;
err = snd_usb_ctl_msg(mixer->chip->dev,
usb_rcvctrlpipe(mixer->chip->dev, 0),
UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0,
jacks[i].unitid << 8, buf, 3);
snd_usb_unlock_shutdown(mixer->chip);
if (err == 3 && (buf[0] == 3 || buf[0] == 6))
snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
else
snd_iprintf(buffer, "?\n");
}
}
/* EMU0204 */
static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[2] = {"1/2", "3/4"};
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.enumerated.item[0] = kcontrol->private_value;
return 0;
}
static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer,
int value)
struct snd_usb_audio *chip = mixer->chip;
int err;
unsigned char buf[2];
err = snd_usb_lock_shutdown(chip);
if (err < 0)
return err;
buf[0] = 0x01;
buf[1] = value ? 0x02 : 0x01;
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
0x0400, 0x0e00, buf, 2);
snd_usb_unlock_shutdown(chip);
return err;
}
static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
unsigned int value = ucontrol->value.enumerated.item[0];
int err;
if (value > 1)
return -EINVAL;
if (value == kcontrol->private_value)
return 0;
kcontrol->private_value = value;
err = snd_emu0204_ch_switch_update(mixer, value);
return err < 0 ? err : 1;
static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
{
return snd_emu0204_ch_switch_update(list->mixer,
list->kctl->private_value);
}
static const struct snd_kcontrol_new snd_emu0204_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Front Jack Channels",
.info = snd_emu0204_ch_switch_info,
.get = snd_emu0204_ch_switch_get,
.put = snd_emu0204_ch_switch_put,
.private_value = 0,
};
static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer)
{
return add_single_ctl_with_resume(mixer, 0,
snd_emu0204_ch_switch_resume,
&snd_emu0204_control, NULL);
/* ASUS Xonar U1 / U3 controls */
static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02);
static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer,
unsigned char status)
{
struct snd_usb_audio *chip = mixer->chip;
int err;
err = snd_usb_lock_shutdown(chip);
if (err < 0)
return err;
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x08,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
50, 0, &status, 1);
snd_usb_unlock_shutdown(chip);
static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
old_status = kcontrol->private_value;
if (ucontrol->value.integer.value[0])
new_status = old_status | 0x02;
else
new_status = old_status & ~0x02;
if (new_status == old_status)
return 0;
kcontrol->private_value = new_status;
err = snd_xonar_u1_switch_update(list->mixer, new_status);
return err < 0 ? err : 1;
}
static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
{
return snd_xonar_u1_switch_update(list->mixer,
list->kctl->private_value);
static const struct snd_kcontrol_new snd_xonar_u1_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = snd_xonar_u1_switch_get,
.put = snd_xonar_u1_switch_put,
};
static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
{
return add_single_ctl_with_resume(mixer, 0,
snd_xonar_u1_switch_resume,
&snd_xonar_u1_output_switch, NULL);
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
/* Digidesign Mbox 1 helper functions */
static int snd_mbox1_is_spdif_synced(struct snd_usb_audio *chip)
{
unsigned char buff[3];
int err;
int is_spdif_synced;
/* Read clock source */
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0), 0x81,
USB_DIR_IN |
USB_TYPE_CLASS |
USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
if (err < 0)
return err;
/* spdif sync: buff is all zeroes */
is_spdif_synced = !(buff[0] | buff[1] | buff[2]);
return is_spdif_synced;
}
static int snd_mbox1_set_clk_source(struct snd_usb_audio *chip, int rate_or_zero)
{
/* 2 possibilities: Internal -> expects sample rate
* S/PDIF sync -> expects rate = 0
*/
unsigned char buff[3];
buff[0] = (rate_or_zero >> 0) & 0xff;
buff[1] = (rate_or_zero >> 8) & 0xff;
buff[2] = (rate_or_zero >> 16) & 0xff;
/* Set clock source */
return snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x1,
USB_TYPE_CLASS |
USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3);
}
static int snd_mbox1_is_spdif_input(struct snd_usb_audio *chip)
{
/* Hardware gives 2 possibilities: ANALOG Source -> 0x01
* S/PDIF Source -> 0x02
*/
int err;
unsigned char source[1];
/* Read input source */
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0), 0x81,
USB_DIR_IN |
USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0x00, 0x500, source, 1);
if (err < 0)
return err;
return (source[0] == 2);
}
static int snd_mbox1_set_input_source(struct snd_usb_audio *chip, int is_spdif)
{
/* NB: Setting the input source to S/PDIF resets the clock source to S/PDIF
* Hardware expects 2 possibilities: ANALOG Source -> 0x01
* S/PDIF Source -> 0x02
*/
unsigned char buff[1];
buff[0] = (is_spdif & 1) + 1;
/* Set input source */
return snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), 0x1,
USB_TYPE_CLASS |
USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1);
}
/* Digidesign Mbox 1 clock source switch (internal/spdif) */
static int snd_mbox1_clk_switch_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct snd_usb_audio *chip = list->mixer->chip;
int err;
err = snd_usb_lock_shutdown(chip);
if (err < 0)
goto err;
err = snd_mbox1_is_spdif_synced(chip);
if (err < 0)
goto err;
kctl->private_value = err;
err = 0;
ucontrol->value.enumerated.item[0] = kctl->private_value;
err:
snd_usb_unlock_shutdown(chip);
return err;
}
static int snd_mbox1_clk_switch_update(struct usb_mixer_interface *mixer, int is_spdif_sync)
struct snd_usb_audio *chip = mixer->chip;
int err;
err = snd_usb_lock_shutdown(chip);
if (err < 0)
return err;
err = snd_mbox1_is_spdif_input(chip);
if (err < 0)
goto err;
err = snd_mbox1_is_spdif_synced(chip);
if (err < 0)
goto err;
/* FIXME: hardcoded sample rate */
err = snd_mbox1_set_clk_source(chip, is_spdif_sync ? 0 : 48000);
if (err < 0)
goto err;
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
err = snd_mbox1_is_spdif_synced(chip);
err:
snd_usb_unlock_shutdown(chip);
return err;
}
static int snd_mbox1_clk_switch_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *mixer = list->mixer;
int err;
bool cur_val, new_val;
cur_val = kctl->private_value;
new_val = ucontrol->value.enumerated.item[0];
if (cur_val == new_val)
return 0;
kctl->private_value = new_val;
err = snd_mbox1_clk_switch_update(mixer, new_val);
return err < 0 ? err : 1;
}
static int snd_mbox1_clk_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[2] = {
"Internal",
"S/PDIF"
};
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int snd_mbox1_clk_switch_resume(struct usb_mixer_elem_list *list)
{
return snd_mbox1_clk_switch_update(list->mixer, list->kctl->private_value);
}
/* Digidesign Mbox 1 input source switch (analog/spdif) */
static int snd_mbox1_src_switch_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.enumerated.item[0] = kctl->private_value;
return 0;
}
static int snd_mbox1_src_switch_update(struct usb_mixer_interface *mixer, int is_spdif_input)
{
struct snd_usb_audio *chip = mixer->chip;
int err;
err = snd_usb_lock_shutdown(chip);
if (err < 0)
return err;
err = snd_mbox1_is_spdif_input(chip);
if (err < 0)
goto err;
err = snd_mbox1_set_input_source(chip, is_spdif_input);
if (err < 0)
goto err;
err = snd_mbox1_is_spdif_input(chip);
if (err < 0)
goto err;
err = snd_mbox1_is_spdif_synced(chip);
err:
snd_usb_unlock_shutdown(chip);
return err;
}
static int snd_mbox1_src_switch_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct usb_mixer_interface *mixer = list->mixer;
int err;
bool cur_val, new_val;
cur_val = kctl->private_value;
new_val = ucontrol->value.enumerated.item[0];
if (cur_val == new_val)
return 0;
kctl->private_value = new_val;
err = snd_mbox1_src_switch_update(mixer, new_val);
return err < 0 ? err : 1;
}
static int snd_mbox1_src_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *const texts[2] = {
"S/PDIF"
};
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int snd_mbox1_src_switch_resume(struct usb_mixer_elem_list *list)
return snd_mbox1_src_switch_update(list->mixer, list->kctl->private_value);
static const struct snd_kcontrol_new snd_mbox1_clk_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Clock Source",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_mbox1_clk_switch_info,
.get = snd_mbox1_clk_switch_get,
.put = snd_mbox1_clk_switch_put,
.private_value = 0
};
static const struct snd_kcontrol_new snd_mbox1_src_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Source",
.index = 1,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_mbox1_src_switch_info,
.get = snd_mbox1_src_switch_get,
.put = snd_mbox1_src_switch_put,
.private_value = 0
};
static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer)
int err;
err = add_single_ctl_with_resume(mixer, 0,
snd_mbox1_clk_switch_resume,
&snd_mbox1_clk_switch, NULL);
if (err < 0)
return err;
return add_single_ctl_with_resume(mixer, 1,
snd_mbox1_src_switch_resume,
&snd_mbox1_src_switch, NULL);
}
/* Native Instruments device quirks */
#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex))
static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl)
{
struct usb_device *dev = mixer->chip->dev;
unsigned int pval = kctl->private_value;
u8 value;
int err;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
(pval >> 16) & 0xff,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0, pval & 0xffff, &value, 1);
if (err < 0) {
"unable to issue vendor read request (ret = %d)", err);
return err;
kctl->private_value |= ((unsigned int)value << 24);
return 0;
}
static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = kcontrol->private_value >> 24;
return 0;
}
static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list)
{
struct snd_usb_audio *chip = list->mixer->chip;
unsigned int pval = list->kctl->private_value;
int err;
err = snd_usb_lock_shutdown(chip);
if (err < 0)
return err;
err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
(pval >> 16) & 0xff,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
pval >> 24, pval & 0xffff, NULL, 0, 1000);
snd_usb_unlock_shutdown(chip);
return err;
}
static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
u8 oldval = (kcontrol->private_value >> 24) & 0xff;
u8 newval = ucontrol->value.integer.value[0];
int err;
if (oldval == newval)
return 0;
kcontrol->private_value &= ~(0xff << 24);
kcontrol->private_value |= (unsigned int)newval << 24;
err = snd_ni_update_cur_val(list);
return err < 0 ? err : 1;
static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
{
.name = "Direct Thru Channel A",
.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
},
{
.name = "Direct Thru Channel B",
.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
},
{
.name = "Phono Input Channel A",
.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
},
{
.name = "Phono Input Channel B",
.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
},
};
static const struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = {
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
.name = "Direct Thru Channel A",
.private_value = _MAKE_NI_CONTROL(0x01, 0x03),
},
{
.name = "Direct Thru Channel B",
.private_value = _MAKE_NI_CONTROL(0x01, 0x05),
},
{
.name = "Direct Thru Channel C",
.private_value = _MAKE_NI_CONTROL(0x01, 0x07),
},
{
.name = "Direct Thru Channel D",
.private_value = _MAKE_NI_CONTROL(0x01, 0x09),
},
{
.name = "Phono Input Channel A",
.private_value = _MAKE_NI_CONTROL(0x02, 0x03),
},
{
.name = "Phono Input Channel B",
.private_value = _MAKE_NI_CONTROL(0x02, 0x05),
},
{
.name = "Phono Input Channel C",
.private_value = _MAKE_NI_CONTROL(0x02, 0x07),
},
{
.name = "Phono Input Channel D",
.private_value = _MAKE_NI_CONTROL(0x02, 0x09),
},
};
static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
const struct snd_kcontrol_new *kc,
unsigned int count)
{
int i, err = 0;
struct snd_kcontrol_new template = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.get = snd_nativeinstruments_control_get,
.put = snd_nativeinstruments_control_put,
.info = snd_ctl_boolean_mono_info,