aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/6fire/midi.c4
-rw-r--r--sound/usb/Makefile1
-rw-r--r--sound/usb/bcd2000/bcd2000.c4
-rw-r--r--sound/usb/caiaq/midi.c4
-rw-r--r--sound/usb/card.c1
-rw-r--r--sound/usb/endpoint.c40
-rw-r--r--sound/usb/endpoint.h2
-rw-r--r--sound/usb/hiface/pcm.c2
-rw-r--r--sound/usb/line6/driver.c49
-rw-r--r--sound/usb/line6/driver.h9
-rw-r--r--sound/usb/line6/midi.c4
-rw-r--r--sound/usb/line6/podhd.c26
-rw-r--r--sound/usb/midi.c4
-rw-r--r--sound/usb/mixer.c3
-rw-r--r--sound/usb/mixer_quirks.c5
-rw-r--r--sound/usb/mixer_us16x08.c1419
-rw-r--r--sound/usb/mixer_us16x08.h121
-rw-r--r--sound/usb/pcm.c41
-rw-r--r--sound/usb/quirks.c54
-rw-r--r--sound/usb/usx2y/us122l.c5
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c7
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c5
22 files changed, 1733 insertions, 77 deletions
diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c
index 3d410969553e..aa5adbb6eb5d 100644
--- a/sound/usb/6fire/midi.c
+++ b/sound/usb/6fire/midi.c
@@ -139,14 +139,14 @@ static void usb6fire_midi_in_trigger(
spin_unlock_irqrestore(&rt->in_lock, flags);
}
-static struct snd_rawmidi_ops out_ops = {
+static const struct snd_rawmidi_ops out_ops = {
.open = usb6fire_midi_out_open,
.close = usb6fire_midi_out_close,
.trigger = usb6fire_midi_out_trigger,
.drain = usb6fire_midi_out_drain
};
-static struct snd_rawmidi_ops in_ops = {
+static const struct snd_rawmidi_ops in_ops = {
.open = usb6fire_midi_in_open,
.close = usb6fire_midi_in_close,
.trigger = usb6fire_midi_in_trigger
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 2d2d122b069f..42cb33b94f6a 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -10,6 +10,7 @@ snd-usb-audio-objs := card.o \
mixer.o \
mixer_quirks.o \
mixer_scarlett.o \
+ mixer_us16x08.o \
pcm.o \
proc.o \
quirks.o \
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index d060dddcc52d..2ff9d578753a 100644
--- a/sound/usb/bcd2000/bcd2000.c
+++ b/sound/usb/bcd2000/bcd2000.c
@@ -252,13 +252,13 @@ static void bcd2000_input_complete(struct urb *urb)
__func__, ret);
}
-static struct snd_rawmidi_ops bcd2000_midi_output = {
+static const struct snd_rawmidi_ops bcd2000_midi_output = {
.open = bcd2000_midi_output_open,
.close = bcd2000_midi_output_close,
.trigger = bcd2000_midi_output_trigger,
};
-static struct snd_rawmidi_ops bcd2000_midi_input = {
+static const struct snd_rawmidi_ops bcd2000_midi_input = {
.open = bcd2000_midi_input_open,
.close = bcd2000_midi_input_close,
.trigger = bcd2000_midi_input_trigger,
diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c
index 2d7588461b33..f8e5b1b57c4f 100644
--- a/sound/usb/caiaq/midi.c
+++ b/sound/usb/caiaq/midi.c
@@ -102,14 +102,14 @@ static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *subs
}
-static struct snd_rawmidi_ops snd_usb_caiaq_midi_output =
+static const struct snd_rawmidi_ops snd_usb_caiaq_midi_output =
{
.open = snd_usb_caiaq_midi_output_open,
.close = snd_usb_caiaq_midi_output_close,
.trigger = snd_usb_caiaq_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_usb_caiaq_midi_input =
+static const struct snd_rawmidi_ops snd_usb_caiaq_midi_input =
{
.open = snd_usb_caiaq_midi_input_open,
.close = snd_usb_caiaq_midi_input_close,
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 2ddc034673a8..f36cb068dad3 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -206,7 +206,6 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
if (! snd_usb_parse_audio_interface(chip, interface)) {
usb_set_interface(dev, interface, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
- return -EINVAL;
}
return 0;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index c470251cea4b..c90607ebe155 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -384,6 +384,9 @@ static void snd_complete_urb(struct urb *urb)
if (unlikely(atomic_read(&ep->chip->shutdown)))
goto exit_clear;
+ if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+ goto exit_clear;
+
if (usb_pipeout(ep->pipe)) {
retire_outbound_urb(ep, ctx);
/* can be stopped during retire callback */
@@ -534,6 +537,11 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep)
alive, ep->ep_num);
clear_bit(EP_FLAG_STOPPING, &ep->flags);
+ ep->data_subs = NULL;
+ ep->sync_slave = NULL;
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+
return 0;
}
@@ -630,10 +638,24 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
ep->datainterval = fmt->datainterval;
ep->stride = frame_bits >> 3;
- ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
- /* assume max. frequency is 25% higher than nominal */
- ep->freqmax = ep->freqn + (ep->freqn >> 2);
+ switch (pcm_format) {
+ case SNDRV_PCM_FORMAT_U8:
+ ep->silence_value = 0x80;
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ case SNDRV_PCM_FORMAT_DSD_U16_BE:
+ case SNDRV_PCM_FORMAT_DSD_U32_BE:
+ ep->silence_value = 0x69;
+ break;
+ default:
+ ep->silence_value = 0;
+ }
+
+ /* assume max. frequency is 50% higher than nominal */
+ ep->freqmax = ep->freqn + (ep->freqn >> 1);
/* Round up freqmax to nearest integer in order to calculate maximum
* packet size, which must represent a whole number of frames.
* This is accomplished by adding 0x0.ffff before converting the
@@ -898,9 +920,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
- * @ep: the endpoint to start
- * @can_sleep: flag indicating whether the operation is executed in
- * non-atomic context
+ * @ep: the endpoint to start
*
* A call to this function will increment the use count of the endpoint.
* In case it is not already running, the URBs for this endpoint will be
@@ -910,7 +930,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
*
* Returns an error if the URB submission failed, 0 in all other cases.
*/
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
{
int err;
unsigned int i;
@@ -924,8 +944,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
/* just to be sure */
deactivate_urbs(ep, false);
- if (can_sleep)
- wait_clear_urbs(ep);
ep->active_mask = 0;
ep->unlink_mask = 0;
@@ -1006,10 +1024,6 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
if (--ep->use_count == 0) {
deactivate_urbs(ep, false);
- ep->data_subs = NULL;
- ep->sync_slave = NULL;
- ep->retire_data_urb = NULL;
- ep->prepare_data_urb = NULL;
set_bit(EP_FLAG_STOPPING, &ep->flags);
}
}
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 6428392d8f62..584f295d7c77 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,7 +18,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep);
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep);
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
index 2c44139b4041..33db205dd12b 100644
--- a/sound/usb/hiface/pcm.c
+++ b/sound/usb/hiface/pcm.c
@@ -445,6 +445,8 @@ static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
mutex_lock(&rt->stream_mutex);
+ hiface_pcm_stream_stop(rt);
+
sub->dma_off = 0;
sub->period_off = 0;
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 90009c0b3a92..0ff5a7d2e19f 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -492,42 +492,46 @@ static void line6_destruct(struct snd_card *card)
usb_put_dev(usbdev);
}
-/* get data from endpoint descriptor (see usb_maxpacket): */
-static void line6_get_interval(struct usb_line6 *line6)
+static void line6_get_usb_properties(struct usb_line6 *line6)
{
struct usb_device *usbdev = line6->usbdev;
const struct line6_properties *properties = line6->properties;
int pipe;
- struct usb_host_endpoint *ep;
+ struct usb_host_endpoint *ep = NULL;
- if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
- pipe =
- usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
- } else {
- pipe =
- usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+ if (properties->capabilities & LINE6_CAP_CONTROL) {
+ if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ pipe = usb_rcvintpipe(line6->usbdev,
+ line6->properties->ep_ctrl_r);
+ } else {
+ pipe = usb_rcvbulkpipe(line6->usbdev,
+ line6->properties->ep_ctrl_r);
+ }
+ ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
}
- ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
+ /* Control data transfer properties */
if (ep) {
line6->interval = ep->desc.bInterval;
- if (usbdev->speed == USB_SPEED_LOW) {
- line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
- line6->iso_buffers = USB_LOW_ISO_BUFFERS;
- } else {
- line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
- line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
- }
-
line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
} else {
- dev_err(line6->ifcdev,
- "endpoint not available, using fallback values");
+ if (properties->capabilities & LINE6_CAP_CONTROL) {
+ dev_err(line6->ifcdev,
+ "endpoint not available, using fallback values");
+ }
line6->interval = LINE6_FALLBACK_INTERVAL;
line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE;
}
-}
+ /* Isochronous transfer properties */
+ if (usbdev->speed == USB_SPEED_LOW) {
+ line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
+ line6->iso_buffers = USB_LOW_ISO_BUFFERS;
+ } else {
+ line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
+ line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
+ }
+}
/* Enable buffering of incoming messages, flush the buffer */
static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
@@ -754,8 +758,9 @@ int line6_probe(struct usb_interface *interface,
goto error;
}
+ line6_get_usb_properties(line6);
+
if (properties->capabilities & LINE6_CAP_CONTROL) {
- line6_get_interval(line6);
ret = line6_init_cap_control(line6);
if (ret < 0)
goto error;
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7e3a3aada222..a5c2e9ae5f17 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -98,10 +98,11 @@ struct line6_properties {
int altsetting;
- unsigned ep_ctrl_r;
- unsigned ep_ctrl_w;
- unsigned ep_audio_r;
- unsigned ep_audio_w;
+ unsigned int ctrl_if;
+ unsigned int ep_ctrl_r;
+ unsigned int ep_ctrl_w;
+ unsigned int ep_audio_r;
+ unsigned int ep_audio_w;
};
/* Capability bits */
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index d0fb2f205bd9..1d3a23b02d68 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -200,14 +200,14 @@ static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream,
line6->line6midi->substream_receive = NULL;
}
-static struct snd_rawmidi_ops line6_midi_output_ops = {
+static const struct snd_rawmidi_ops line6_midi_output_ops = {
.open = line6_midi_output_open,
.close = line6_midi_output_close,
.trigger = line6_midi_output_trigger,
.drain = line6_midi_output_drain,
};
-static struct snd_rawmidi_ops line6_midi_input_ops = {
+static const struct snd_rawmidi_ops line6_midi_input_ops = {
.open = line6_midi_input_open,
.close = line6_midi_input_close,
.trigger = line6_midi_input_trigger,
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 49cd4a65e390..6ab23e5aee71 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -153,6 +153,7 @@ static struct line6_pcm_properties podx3_pcm_properties = {
.rats = &podhd_ratden},
.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
};
+static struct usb_driver podhd_driver;
static void podhd_startup_start_workqueue(unsigned long data);
static void podhd_startup_workqueue(struct work_struct *work);
@@ -291,8 +292,14 @@ static void podhd_disconnect(struct usb_line6 *line6)
struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+ struct usb_interface *intf;
+
del_timer_sync(&pod->startup_timer);
cancel_work_sync(&pod->startup_work);
+
+ intf = usb_ifnum_to_if(line6->usbdev,
+ pod->line6.properties->ctrl_if);
+ usb_driver_release_interface(&podhd_driver, intf);
}
}
@@ -304,10 +311,27 @@ static int podhd_init(struct usb_line6 *line6,
{
int err;
struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
+ struct usb_interface *intf;
line6->disconnect = podhd_disconnect;
if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+ /* claim the data interface */
+ intf = usb_ifnum_to_if(line6->usbdev,
+ pod->line6.properties->ctrl_if);
+ if (!intf) {
+ dev_err(pod->line6.ifcdev, "interface %d not found\n",
+ pod->line6.properties->ctrl_if);
+ return -ENODEV;
+ }
+
+ err = usb_driver_claim_interface(&podhd_driver, intf, NULL);
+ if (err != 0) {
+ dev_err(pod->line6.ifcdev, "can't claim interface %d, error %d\n",
+ pod->line6.properties->ctrl_if, err);
+ return err;
+ }
+
/* create sysfs entries: */
err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
if (err < 0)
@@ -406,6 +430,7 @@ static const struct line6_properties podhd_properties_table[] = {
.altsetting = 1,
.ep_ctrl_r = 0x81,
.ep_ctrl_w = 0x01,
+ .ctrl_if = 1,
.ep_audio_r = 0x86,
.ep_audio_w = 0x02,
},
@@ -417,6 +442,7 @@ static const struct line6_properties podhd_properties_table[] = {
.altsetting = 1,
.ep_ctrl_r = 0x81,
.ep_ctrl_w = 0x01,
+ .ctrl_if = 1,
.ep_audio_r = 0x86,
.ep_audio_w = 0x02,
},
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 7ba92921bf28..6e763bc8d7db 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1234,14 +1234,14 @@ static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream,
clear_bit(substream->number, &umidi->input_triggered);
}
-static struct snd_rawmidi_ops snd_usbmidi_output_ops = {
+static const struct snd_rawmidi_ops snd_usbmidi_output_ops = {
.open = snd_usbmidi_output_open,
.close = snd_usbmidi_output_close,
.trigger = snd_usbmidi_output_trigger,
.drain = snd_usbmidi_output_drain,
};
-static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
+static const struct snd_rawmidi_ops snd_usbmidi_input_ops = {
.open = snd_usbmidi_input_open,
.close = snd_usbmidi_input_close,
.trigger = snd_usbmidi_input_trigger
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 2f8c388ef84f..4703caea56b2 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -932,9 +932,10 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
case USB_ID(0x046d, 0x0991):
+ case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */
/* Most audio usb devices lie about volume resolution.
* Most Logitech webcams have res = 384.
- * Proboly there is some logitech magic behind this number --fishor
+ * Probably there is some logitech magic behind this number --fishor
*/
if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
usb_audio_info(chip,
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 04991b009132..4fa0053a40af 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -43,6 +43,7 @@
#include "mixer.h"
#include "mixer_quirks.h"
#include "mixer_scarlett.h"
+#include "mixer_us16x08.h"
#include "helper.h"
extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
@@ -1729,6 +1730,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
return err;
switch (mixer->chip->usb_id) {
+ /* Tascam US-16x08 */
+ case USB_ID(0x0644, 0x8047):
+ err = snd_us16x08_controls_create(mixer);
+ break;
case USB_ID(0x041e, 0x3020):
case USB_ID(0x041e, 0x3040):
case USB_ID(0x041e, 0x3042):
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
new file mode 100644
index 000000000000..dc48eedea92e
--- /dev/null
+++ b/sound/usb/mixer_us16x08.c
@@ -0,0 +1,1419 @@
+/*
+ * Tascam US-16x08 ALSA driver
+ *
+ * Copyright (c) 2016 by Detlef Urban (onkel@paraair.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "helper.h"
+
+#include "mixer_us16x08.h"
+
+/* USB control message templates */
+static const char route_msg[] = {
+ 0x61,
+ 0x02,
+ 0x03, /* input from master (0x02) or input from computer bus (0x03) */
+ 0x62,
+ 0x02,
+ 0x01, /* input index (0x01/0x02 eq. left/right) or bus (0x01-0x08) */
+ 0x41,
+ 0x01,
+ 0x61,
+ 0x02,
+ 0x01,
+ 0x62,
+ 0x02,
+ 0x01, /* output index (0x01-0x08) */
+ 0x42,
+ 0x01,
+ 0x43,
+ 0x01,
+ 0x00,
+ 0x00
+};
+
+static const char mix_init_msg1[] = {
+ 0x71, 0x01, 0x00, 0x00
+};
+
+static const char mix_init_msg2[] = {
+ 0x62, 0x02, 0x00, 0x61, 0x02, 0x04, 0xb1, 0x01, 0x00, 0x00
+};
+
+static const char mix_msg_in[] = {
+ /* default message head, equal to all mixers */
+ 0x61, 0x02, 0x04, 0x62, 0x02, 0x01,
+ 0x81, /* 0x06: Controller ID */
+ 0x02, /* 0x07: */
+ 0x00, /* 0x08: Value of common mixer */
+ 0x00,
+ 0x00
+};
+
+static const char mix_msg_out[] = {
+ /* default message head, equal to all mixers */
+ 0x61, 0x02, 0x02, 0x62, 0x02, 0x01,
+ 0x81, /* 0x06: Controller ID */
+ 0x02, /* 0x07: */
+ 0x00, /* 0x08: Value of common mixer */
+ 0x00,
+ 0x00
+};
+
+static const char bypass_msg_out[] = {
+ 0x45,
+ 0x02,
+ 0x01, /* on/off flag */
+ 0x00,
+ 0x00
+};
+
+static const char bus_msg_out[] = {
+ 0x44,
+ 0x02,
+ 0x01, /* on/off flag */
+ 0x00,
+ 0x00
+};
+
+static const char comp_msg[] = {
+ /* default message head, equal to all mixers */
+ 0x61, 0x02, 0x04, 0x62, 0x02, 0x01,
+ 0x91,
+ 0x02,
+ 0xf0, /* 0x08: Threshold db (8) (e0 ... 00) (+-0dB -- -32dB) x-32 */
+ 0x92,
+ 0x02,
+ 0x0a, /* 0x0b: Ratio (0a,0b,0d,0f,11,14,19,1e,23,28,32,3c,50,a0,ff) */
+ 0x93,
+ 0x02,
+ 0x02, /* 0x0e: Attack (0x02 ... 0xc0) (2ms ... 200ms) */
+ 0x94,
+ 0x02,
+ 0x01, /* 0x11: Release (0x01 ... 0x64) (10ms ... 1000ms) x*10 */
+ 0x95,
+ 0x02,
+ 0x03, /* 0x14: gain (0 ... 20) (0dB .. 20dB) */
+ 0x96,
+ 0x02,
+ 0x01,
+ 0x97,
+ 0x02,
+ 0x01, /* 0x1a: main Comp switch (0 ... 1) (off ... on)) */
+ 0x00,
+ 0x00
+};
+
+static const char eqs_msq[] = {
+ /* default message head, equal to all mixers */
+ 0x61, 0x02, 0x04, 0x62, 0x02, 0x01,
+ 0x51, /* 0x06: Controller ID */
+ 0x02,
+ 0x04, /* 0x08: EQ set num (0x01..0x04) (LOW, LOWMID, HIGHMID, HIGH)) */
+ 0x52,
+ 0x02,
+ 0x0c, /* 0x0b: value dB (0 ... 12) (-12db .. +12db) x-6 */
+ 0x53,
+ 0x02,
+ 0x0f, /* 0x0e: value freq (32-47) (1.7kHz..18kHz) */
+ 0x54,
+ 0x02,
+ 0x02, /* 0x11: band width (0-6) (Q16-Q0.25) 2^x/4 (EQ xxMID only) */
+ 0x55,
+ 0x02,
+ 0x01, /* 0x14: main EQ switch (0 ... 1) (off ... on)) */
+ 0x00,
+ 0x00
+};
+
+/* compressor ratio map */
+static const char ratio_map[] = {
+ 0x0a, 0x0b, 0x0d, 0x0f, 0x11, 0x14, 0x19, 0x1e,
+ 0x23, 0x28, 0x32, 0x3c, 0x50, 0xa0, 0xff
+};
+
+/* route enumeration names */
+static const char *const route_names[] = {
+ "Master Left", "Master Right", "Output 1", "Output 2", "Output 3",
+ "Output 4", "Output 5", "Output 6", "Output 7", "Output 8",
+};
+
+static int snd_us16x08_recv_urb(struct snd_usb_audio *chip,
+ unsigned char *buf, int size)
+{
+
+ mutex_lock(&chip->mutex);
+ snd_usb_ctl_msg(chip->dev,
+ usb_rcvctrlpipe(chip->dev, 0),
+ SND_US16X08_URB_METER_REQUEST,
+ SND_US16X08_URB_METER_REQUESTTYPE, 0, 0, buf, size);
+ mutex_unlock(&chip->mutex);
+ return 0;
+}
+
+/* wrapper function to send prepared URB buffer to usb device. Return an error
+ * code if something went wrong
+ */
+static int snd_us16x08_send_urb(struct snd_usb_audio *chip, char *buf, int size)
+{
+ return snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
+ SND_US16X08_URB_REQUEST, SND_US16X08_URB_REQUESTTYPE,
+ 0, 0, buf, size);
+}
+
+static int snd_us16x08_route_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1, 10, route_names);
+}
+
+static int snd_us16x08_route_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ int index = ucontrol->id.index;
+
+ /* route has no bias */
+ ucontrol->value.enumerated.item[0] = elem->cache_val[index];
+
+ return 0;
+}
+
+static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ int index = ucontrol->id.index;
+ char buf[sizeof(route_msg)];
+ int val, val_org, err;
+
+ /* get the new value (no bias for routes) */
+ val = ucontrol->value.enumerated.item[0];
+
+ /* sanity check */
+ if (val < 0 || val > 9)
+ return -EINVAL;
+
+ /* prepare the message buffer from template */
+ memcpy(buf, route_msg, sizeof(route_msg));
+
+ if (val < 2) {
+ /* input comes from a master channel */
+ val_org = val;
+ buf[2] = 0x02;
+ } else {
+ /* input comes from a computer channel */
+ buf[2] = 0x03;
+ val_org = val - 2;
+ }
+
+ /* place new route selection in URB message */
+ buf[5] = (unsigned char) (val_org & 0x0f) + 1;
+ /* place route selector in URB message */
+ buf[13] = index + 1;
+
+ err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg));
+
+ if (err > 0) {
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err);
+ }
+
+ return err > 0 ? 1 : 0;
+}
+
+static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->count = 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.max = SND_US16X08_KCMAX(kcontrol);
+ uinfo->value.integer.min = SND_US16X08_KCMIN(kcontrol);
+ uinfo->value.integer.step = SND_US16X08_KCSTEP(kcontrol);
+ return 0;
+}
+
+static int snd_us16x08_master_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ int index = ucontrol->id.index;
+
+ ucontrol->value.integer.value[0] = elem->cache_val[index];
+
+ return 0;
+}
+
+static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ char buf[sizeof(mix_msg_out)];
+ int val, err;
+ int index = ucontrol->id.index;
+
+ /* new control value incl. bias*/
+ val = ucontrol->value.integer.value[0];
+
+ /* sanity check */
+ if (val < SND_US16X08_KCMIN(kcontrol)
+ || val > SND_US16X08_KCMAX(kcontrol))
+ return -EINVAL;
+
+ /* prepare the message buffer from template */
+ memcpy(buf, mix_msg_out, sizeof(mix_msg_out));
+
+ buf[8] = val - SND_US16X08_KCBIAS(kcontrol);
+ buf[6] = elem->head.id;
+
+ /* place channel selector in URB message */
+ buf[5] = index + 1;
+ err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out));
+
+ if (err > 0) {
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set master, err:%d\n", err);
+ }
+
+ return err > 0 ? 1 : 0;
+}
+
+static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ char buf[sizeof(mix_msg_out)];
+ int val, err = 0;
+
+ val = ucontrol->value.integer.value[0];
+
+ /* prepare the message buffer from template */
+ switch (elem->head.id) {
+ case SND_US16X08_ID_BYPASS:
+ memcpy(buf, bypass_msg_out, sizeof(bypass_msg_out));
+ buf[2] = val;
+ err = snd_us16x08_send_urb(chip, buf, sizeof(bypass_msg_out));
+ break;
+ case SND_US16X08_ID_BUSS_OUT:
+ memcpy(buf, bus_msg_out, sizeof(bus_msg_out));
+ buf[2] = val;
+ err = snd_us16x08_send_urb(chip, buf, sizeof(bus_msg_out));
+ break;
+ case SND_US16X08_ID_MUTE:
+ memcpy(buf, mix_msg_out, sizeof(mix_msg_out));
+ buf[8] = val;
+ buf[6] = elem->head.id;
+ buf[5] = 1;
+ err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out));
+ break;
+ }
+
+ if (err > 0) {
+ elem->cached |= 1;
+ elem->cache_val[0] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set buss param, err:%d\n", err);
+ }
+
+ return err > 0 ? 1 : 0;
+}
+
+static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+
+ switch (elem->head.id) {
+ case SND_US16X08_ID_BUSS_OUT:
+ ucontrol->value.integer.value[0] = elem->cache_val[0];
+ break;
+ case SND_US16X08_ID_BYPASS:
+ ucontrol->value.integer.value[0] = elem->cache_val[0];
+ break;
+ case SND_US16X08_ID_MUTE:
+ ucontrol->value.integer.value[0] = elem->cache_val[0];
+ break;
+ }
+
+ return 0;
+}
+
+/* gets a current mixer value from common store */
+static int snd_us16x08_channel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ int index = ucontrol->id.index;
+
+ ucontrol->value.integer.value[0] = elem->cache_val[index];
+
+ return 0;
+}
+
+static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ char buf[sizeof(mix_msg_in)];
+ int val, err;
+ int index = ucontrol->id.index;
+
+ val = ucontrol->value.integer.value[0];
+
+ /* sanity check */
+ if (val < SND_US16X08_KCMIN(kcontrol)
+ || val > SND_US16X08_KCMAX(kcontrol))
+ return -EINVAL;
+
+ /* prepare URB message from template */
+ memcpy(buf, mix_msg_in, sizeof(mix_msg_in));
+
+ /* add the bias to the new value */
+ buf[8] = val - SND_US16X08_KCBIAS(kcontrol);
+ buf[6] = elem->head.id;
+ buf[5] = index + 1;
+
+ err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in));
+
+ if (err > 0) {
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err);
+ }
+
+ return err > 0 ? 1 : 0;
+}
+
+static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->count = 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.max = SND_US16X08_KCMAX(kcontrol);
+ uinfo->value.integer.min = SND_US16X08_KCMIN(kcontrol);
+ uinfo->value.integer.step = SND_US16X08_KCSTEP(kcontrol);
+ return 0;
+}
+
+static int snd_us16x08_comp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_us16x08_comp_store *store = elem->private_data;
+ int index = ucontrol->id.index;
+ int val_idx = COMP_STORE_IDX(elem->head.id);
+
+ ucontrol->value.integer.value[0] = store->val[val_idx][index];
+
+ return 0;
+}
+
+static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ struct snd_us16x08_comp_store *store = elem->private_data;
+ int index = ucontrol->id.index;
+ char buf[sizeof(comp_msg)];
+ int val_idx, val;
+ int err;
+
+ val = ucontrol->value.integer.value[0];
+
+ /* sanity check */
+ if (val < SND_US16X08_KCMIN(kcontrol)
+ || val > SND_US16X08_KCMAX(kcontrol))
+ return -EINVAL;
+
+ /* new control value incl. bias*/
+ val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE;
+
+ store->val[val_idx][index] = ucontrol->value.integer.value[0];
+
+ /* prepare compressor URB message from template */
+ memcpy(buf, comp_msg, sizeof(comp_msg));
+
+ /* place comp values in message buffer watch bias! */
+ buf[8] = store->val[
+ COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index]
+ - SND_US16X08_COMP_THRESHOLD_BIAS;
+ buf[11] = ratio_map[store->val[
+ COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]];
+ buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]
+ + SND_US16X08_COMP_ATTACK_BIAS;
+ buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index]
+ + SND_US16X08_COMP_RELEASE_BIAS;
+ buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
+ buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index];
+
+ /* place channel selector in message buffer */
+ buf[5] = index + 1;
+
+ err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg));
+
+ if (err > 0) {
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err);
+ }
+
+ return 1;
+}
+
+static int snd_us16x08_eqswitch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int val;
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_us16x08_eq_store *store = elem->private_data;
+ int index = ucontrol->id.index;
+
+ /* get low switch from cache is enough, cause all bands are together */
+ val = store->val[EQ_STORE_BAND_IDX(elem->head.id)]
+ [EQ_STORE_PARAM_IDX(elem->head.id)][index];
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ struct snd_us16x08_eq_store *store = elem->private_data;
+ int index = ucontrol->id.index;
+ char buf[sizeof(eqs_msq)];
+ int val, err = 0;
+ int b_idx;
+
+ /* new control value incl. bias*/
+ val = ucontrol->value.integer.value[0] + SND_US16X08_KCBIAS(kcontrol);
+
+ /* prepare URB message from EQ template */
+ memcpy(buf, eqs_msq, sizeof(eqs_msq));
+
+ /* place channel index in URB message */
+ buf[5] = index + 1;
+ for (b_idx = 0; b_idx < SND_US16X08_ID_EQ_BAND_COUNT; b_idx++) {
+ /* all four EQ bands have to be enabled/disabled in once */
+ buf[20] = val;
+ buf[17] = store->val[b_idx][2][index];
+ buf[14] = store->val[b_idx][1][index];
+ buf[11] = store->val[b_idx][0][index];
+ buf[8] = b_idx + 1;
+ err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq));
+ if (err < 0)
+ break;
+ store->val[b_idx][3][index] = val;
+ msleep(15);
+ }
+
+ if (err > 0) {
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err);
+ }
+
+ return 1;
+}
+
+static int snd_us16x08_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int val;
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_us16x08_eq_store *store = elem->private_data;
+ int index = ucontrol->id.index;
+ int b_idx = EQ_STORE_BAND_IDX(elem->head.id) - 1;
+ int p_idx = EQ_STORE_PARAM_IDX(elem->head.id);
+
+ val = store->val[b_idx][p_idx][index];
+
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+
+static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ struct snd_us16x08_eq_store *store = elem->private_data;
+ int index = ucontrol->id.index;
+ char buf[sizeof(eqs_msq)];
+ int val, err;
+ int b_idx = EQ_STORE_BAND_IDX(elem->head.id) - 1;
+ int p_idx = EQ_STORE_PARAM_IDX(elem->head.id);
+
+ val = ucontrol->value.integer.value[0];
+
+ /* sanity check */
+ if (val < SND_US16X08_KCMIN(kcontrol)
+ || val > SND_US16X08_KCMAX(kcontrol))
+ return -EINVAL;
+
+ /* copy URB buffer from EQ template */
+ memcpy(buf, eqs_msq, sizeof(eqs_msq));
+
+ store->val[b_idx][p_idx][index] = val;
+ buf[20] = store->val[b_idx][3][index];
+ buf[17] = store->val[b_idx][2][index];
+ buf[14] = store->val[b_idx][1][index];
+ buf[11] = store->val[b_idx][0][index];
+
+ /* place channel index in URB buffer */
+ buf[5] = index + 1;
+
+ /* place EQ band in URB buffer */
+ buf[8] = b_idx + 1;
+
+ err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq));
+
+ if (err > 0) {
+ /* store new value in EQ band cache */
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ } else {
+ usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err);
+ }
+
+ return 1;
+}
+
+static int snd_us16x08_meter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->count = 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.max = 0x7FFF;
+ uinfo->value.integer.min = 0;
+
+ return 0;
+}
+
+/* calculate compressor index for reduction level request */
+static int snd_get_meter_comp_index(struct snd_us16x08_meter_store *store)
+{
+ int ret;
+
+ /* any channel active */
+ if (store->comp_active_index) {
+ /* check for stereo link */
+ if (store->comp_active_index & 0x20) {
+ /* reset comp_index to left channel*/
+ if (store->comp_index -
+ store->comp_active_index > 1)
+ store->comp_index =
+ store->comp_active_index;
+
+ ret = store->comp_index++ & 0x1F;
+ } else {
+ /* no stereo link */
+ ret = store->comp_active_index;
+ }
+ } else {
+ /* skip channels with no compressor active */
+ while (!store->comp_store->val[
+ COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)]
+ [store->comp_index - 1]
+ && store->comp_index <= SND_US16X08_MAX_CHANNELS) {
+ store->comp_index++;
+ }
+ ret = store->comp_index++;
+ if (store->comp_index > SND_US16X08_MAX_CHANNELS)
+ store->comp_index = 1;
+ }
+ return ret;
+}
+
+/* retrieve the meter level values from URB message */
+static void get_meter_levels_from_urb(int s,
+ struct snd_us16x08_meter_store *store,
+ u8 *meter_urb)
+{
+ int val = MUC2(meter_urb, s) + (MUC3(meter_urb, s) << 8);
+
+ if (MUA0(meter_urb, s) == 0x61 && MUA1(meter_urb, s) == 0x02 &&
+ MUA2(meter_urb, s) == 0x04 && MUB0(meter_urb, s) == 0x62) {
+ if (MUC0(meter_urb, s) == 0x72)
+ store->meter_level[MUB2(meter_urb, s) - 1] = val;
+ if (MUC0(meter_urb, s) == 0xb2)
+ store->comp_level[MUB2(meter_urb, s) - 1] = val;
+ }
+ if (MUA0(meter_urb, s) == 0x61 && MUA1(meter_urb, s) == 0x02 &&
+ MUA2(meter_urb, s) == 0x02 && MUB0(meter_urb, s) == 0x62)
+ store->master_level[MUB2(meter_urb, s) - 1] = val;
+}
+
+/* Function to retrieve current meter values from the device.
+ *
+ * The device needs to be polled for meter values with an initial
+ * requests. It will return with a sequence of different meter value
+ * packages. The first request (case 0:) initiate this meter response sequence.
+ * After the third response, an additional request can be placed,
+ * to retrieve compressor reduction level value for given channel. This round
+ * trip channel selector will skip all inactive compressors.
+ * A mixer can interrupt this round-trip by selecting one ore two (stereo-link)
+ * specific channels.
+ */
+static int snd_us16x08_meter_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i, set;
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_usb_audio *chip = elem->head.mixer->chip;
+ struct snd_us16x08_meter_store *store = elem->private_data;
+ u8 meter_urb[64];
+ char tmp[sizeof(mix_init_msg2)] = {0};
+
+ switch (kcontrol->private_value) {
+ case 0:
+ snd_us16x08_send_urb(chip, (char *)mix_init_msg1,
+ sizeof(mix_init_msg1));
+ snd_us16x08_recv_urb(chip, meter_urb,
+ sizeof(meter_urb));
+ kcontrol->private_value++;
+ break;
+ case 1:
+ snd_us16x08_recv_urb(chip, meter_urb,
+ sizeof(meter_urb));
+ kcontrol->private_value++;
+ break;
+ case 2:
+ snd_us16x08_recv_urb(chip, meter_urb,
+ sizeof(meter_urb));
+ kcontrol->private_value++;
+ break;
+ case 3:
+ memcpy(tmp, mix_init_msg2, sizeof(mix_init_msg2));
+ tmp[2] = snd_get_meter_comp_index(store);
+ snd_us16x08_send_urb(chip, tmp, sizeof(mix_init_msg2));
+ snd_us16x08_recv_urb(chip, meter_urb,
+ sizeof(meter_urb));
+ kcontrol->private_value = 0;
+ break;
+ }
+
+ for (set = 0; set < 6; set++)
+ get_meter_levels_from_urb(set, store, meter_urb);
+
+ for (i = 0; i < SND_US16X08_MAX_CHANNELS; i++) {
+ ucontrol->value.integer.value[i] =
+ store ? store->meter_level[i] : 0;
+ }
+
+ ucontrol->value.integer.value[i++] = store ? store->master_level[0] : 0;
+ ucontrol->value.integer.value[i++] = store ? store->master_level[1] : 0;
+
+ for (i = 2; i < SND_US16X08_MAX_CHANNELS + 2; i++)
+ ucontrol->value.integer.value[i + SND_US16X08_MAX_CHANNELS] =
+ store ? store->comp_level[i - 2] : 0;
+
+ return 1;
+}
+
+static int snd_us16x08_meter_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *elem = kcontrol->private_data;
+ struct snd_us16x08_meter_store *store = elem->private_data;
+ int val;
+
+ val = ucontrol->value.integer.value[0];
+
+ /* sanity check */
+ if (val < 0 || val >= SND_US16X08_MAX_CHANNELS)
+ return -EINVAL;
+
+ store->comp_active_index = val;
+ store->comp_index = val;
+
+ return 1;
+}
+
+static struct snd_kcontrol_new snd_us16x08_ch_boolean_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_switch_info,
+ .get = snd_us16x08_channel_get,
+ .put = snd_us16x08_channel_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1)
+};
+
+static struct snd_kcontrol_new snd_us16x08_ch_int_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_channel_get,
+ .put = snd_us16x08_channel_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 133)
+};
+
+static struct snd_kcontrol_new snd_us16x08_pan_int_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_channel_get,
+ .put = snd_us16x08_channel_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 255)
+};
+
+static struct snd_kcontrol_new snd_us16x08_master_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 1,
+ .info = snd_us16x08_master_info,
+ .get = snd_us16x08_master_get,
+ .put = snd_us16x08_master_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 133)
+};
+
+static struct snd_kcontrol_new snd_us16x08_route_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 8,
+ .info = snd_us16x08_route_info,
+ .get = snd_us16x08_route_get,
+ .put = snd_us16x08_route_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 9)
+};
+
+static struct snd_kcontrol_new snd_us16x08_bus_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 1,
+ .info = snd_us16x08_switch_info,
+ .get = snd_us16x08_bus_get,
+ .put = snd_us16x08_bus_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1)
+};
+
+static struct snd_kcontrol_new snd_us16x08_compswitch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_switch_info,
+ .get = snd_us16x08_comp_get,
+ .put = snd_us16x08_comp_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1)
+};
+
+static struct snd_kcontrol_new snd_us16x08_comp_threshold_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_comp_get,
+ .put = snd_us16x08_comp_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_COMP_THRESHOLD_BIAS, 1,
+ 0, 0x20)
+};
+
+static struct snd_kcontrol_new snd_us16x08_comp_ratio_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_comp_get,
+ .put = snd_us16x08_comp_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0,
+ sizeof(ratio_map) - 1), /*max*/
+};
+
+static struct snd_kcontrol_new snd_us16x08_comp_gain_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_comp_get,
+ .put = snd_us16x08_comp_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x14)
+};
+
+static struct snd_kcontrol_new snd_us16x08_comp_attack_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_comp_get,
+ .put = snd_us16x08_comp_put,
+ .private_value =
+ SND_US16X08_KCSET(SND_US16X08_COMP_ATTACK_BIAS, 1, 0, 0xc6),
+};
+
+static struct snd_kcontrol_new snd_us16x08_comp_release_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_comp_get,
+ .put = snd_us16x08_comp_put,
+ .private_value =
+ SND_US16X08_KCSET(SND_US16X08_COMP_RELEASE_BIAS, 1, 0, 0x63),
+};
+
+static struct snd_kcontrol_new snd_us16x08_eq_gain_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_eq_get,
+ .put = snd_us16x08_eq_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 24),
+};
+
+static struct snd_kcontrol_new snd_us16x08_eq_low_freq_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_eq_get,
+ .put = snd_us16x08_eq_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x1F),
+};
+
+static struct snd_kcontrol_new snd_us16x08_eq_mid_freq_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_eq_get,
+ .put = snd_us16x08_eq_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x3F)
+};
+
+static struct snd_kcontrol_new snd_us16x08_eq_mid_width_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_eq_get,
+ .put = snd_us16x08_eq_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x06)
+};
+
+static struct snd_kcontrol_new snd_us16x08_eq_high_freq_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_mix_info,
+ .get = snd_us16x08_eq_get,
+ .put = snd_us16x08_eq_put,
+ .private_value =
+ SND_US16X08_KCSET(SND_US16X08_EQ_HIGHFREQ_BIAS, 1, 0, 0x1F)
+};
+
+static struct snd_kcontrol_new snd_us16x08_eq_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 16,
+ .info = snd_us16x08_switch_info,
+ .get = snd_us16x08_eqswitch_get,
+ .put = snd_us16x08_eqswitch_put,
+ .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1)
+};
+
+static struct snd_kcontrol_new snd_us16x08_meter_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .count = 1,
+ .info = snd_us16x08_meter_info,
+ .get = snd_us16x08_meter_get,
+ .put = snd_us16x08_meter_put
+};
+
+/* control store preparation */
+
+/* setup compressor store and assign default value */
+static struct snd_us16x08_comp_store *snd_us16x08_create_comp_store(void)
+{
+ int i;
+ struct snd_us16x08_comp_store *tmp;
+
+ tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return NULL;
+
+ for (i = 0; i < SND_US16X08_MAX_CHANNELS; i++) {
+ tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][i]
+ = 0x20;
+ tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][i] = 0x00;
+ tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][i] = 0x00;
+ tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][i] = 0x00;
+ tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][i] = 0x00;
+ tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][i] = 0x00;
+ }
+ return tmp;
+}
+
+/* setup EQ store and assign default values */
+static struct snd_us16x08_eq_store *snd_us16x08_create_eq_store(void)
+{
+ int i, b_idx;
+ struct snd_us16x08_eq_store *tmp;
+
+ tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return NULL;
+
+ for (i = 0; i < SND_US16X08_MAX_CHANNELS; i++) {
+ for (b_idx = 0; b_idx < SND_US16X08_ID_EQ_BAND_COUNT; b_idx++) {
+ tmp->val[b_idx][0][i] = 0x0c;
+ tmp->val[b_idx][3][i] = 0x00;
+ switch (b_idx) {
+ case 0: /* EQ Low */
+ tmp->val[b_idx][1][i] = 0x05;
+ tmp->val[b_idx][2][i] = 0xff;
+ break;
+ case 1: /* EQ Mid low */
+ tmp->val[b_idx][1][i] = 0x0e;
+ tmp->val[b_idx][2][i] = 0x02;
+ break;
+ case 2: /* EQ Mid High */
+ tmp->val[b_idx][1][i] = 0x1b;
+ tmp->val[b_idx][2][i] = 0x02;
+ break;
+ case 3: /* EQ High */
+ tmp->val[b_idx][1][i] = 0x2f
+ - SND_US16X08_EQ_HIGHFREQ_BIAS;
+ tmp->val[b_idx][2][i] = 0xff;
+ break;
+ }
+ }
+ }
+ return tmp;
+}
+
+static struct snd_us16x08_meter_store *snd_us16x08_create_meter_store(void)
+{
+ struct snd_us16x08_meter_store *tmp;
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return NULL;
+ tmp->comp_index = 1;
+ tmp->comp_active_index = 0;
+ return tmp;
+}
+
+/* release elem->private_free as well; called only once for each *_store */
+static void elem_private_free(struct snd_kcontrol *kctl)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ if (elem)
+ kfree(elem->private_data);
+ kfree(elem);
+ kctl->private_data = NULL;
+}
+
+static int add_new_ctl(struct usb_mixer_interface *mixer,
+ const struct snd_kcontrol_new *ncontrol,
+ int index, int val_type, int channels,
+ const char *name, void *opt,
+ bool do_private_free,
+ struct usb_mixer_elem_info **elem_ret)
+{
+ struct snd_kcontrol *kctl;
+ struct usb_mixer_elem_info *elem;
+ int err;
+
+ usb_audio_dbg(mixer->chip, "us16x08 add mixer %s\n", name);
+
+ elem = kzalloc(sizeof(*elem), GFP_KERNEL);
+ if (!elem)
+ return -ENOMEM;
+
+ elem->head.mixer = mixer;
+ elem->head.resume = NULL;
+ elem->control = 0;
+ elem->idx_off = 0;
+ elem->head.id = index;
+ elem->val_type = val_type;
+ elem->channels = channels;
+ elem->private_data = opt;
+
+ kctl = snd_ctl_new1(ncontrol, elem);
+ if (!kctl) {
+ kfree(elem);
+ return -ENOMEM;
+ }
+
+ if (do_private_free)
+ kctl->private_free = elem_private_free;
+ else
+ kctl->private_free = snd_usb_mixer_elem_free;
+
+ strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
+
+ err = snd_usb_mixer_add_control(&elem->head, kctl);
+ if (err < 0)
+ return err;
+
+ if (elem_ret)
+ *elem_ret = elem;
+
+ return 0;
+}
+
+/* table of EQ controls */
+static const struct snd_us16x08_control_params eq_controls[] = {
+ { /* EQ switch */
+ .kcontrol_new = &snd_us16x08_eq_switch_ctl,
+ .control_id = SND_US16X08_ID_EQENABLE,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "EQ Switch",
+ },
+ { /* EQ low gain */
+ .kcontrol_new = &snd_us16x08_eq_gain_ctl,
+ .control_id = SND_US16X08_ID_EQLOWLEVEL,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ Low Volume",
+ },
+ { /* EQ low freq */
+ .kcontrol_new = &snd_us16x08_eq_low_freq_ctl,
+ .control_id = SND_US16X08_ID_EQLOWFREQ,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ Low Frequence",
+ },
+ { /* EQ mid low gain */
+ .kcontrol_new = &snd_us16x08_eq_gain_ctl,
+ .control_id = SND_US16X08_ID_EQLOWMIDLEVEL,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ MidLow Volume",
+ },
+ { /* EQ mid low freq */
+ .kcontrol_new = &snd_us16x08_eq_mid_freq_ctl,
+ .control_id = SND_US16X08_ID_EQLOWMIDFREQ,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ MidLow Frequence",
+ },
+ { /* EQ mid low Q */
+ .kcontrol_new = &snd_us16x08_eq_mid_width_ctl,
+ .control_id = SND_US16X08_ID_EQLOWMIDWIDTH,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ MidQLow Q",
+ },
+ { /* EQ mid high gain */
+ .kcontrol_new = &snd_us16x08_eq_gain_ctl,
+ .control_id = SND_US16X08_ID_EQHIGHMIDLEVEL,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ MidHigh Volume",
+ },
+ { /* EQ mid high freq */
+ .kcontrol_new = &snd_us16x08_eq_mid_freq_ctl,
+ .control_id = SND_US16X08_ID_EQHIGHMIDFREQ,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ MidHigh Frequence",
+ },
+ { /* EQ mid high Q */
+ .kcontrol_new = &snd_us16x08_eq_mid_width_ctl,
+ .control_id = SND_US16X08_ID_EQHIGHMIDWIDTH,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ MidHigh Q",
+ },
+ { /* EQ high gain */
+ .kcontrol_new = &snd_us16x08_eq_gain_ctl,
+ .control_id = SND_US16X08_ID_EQHIGHLEVEL,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ High Volume",
+ },
+ { /* EQ low freq */
+ .kcontrol_new = &snd_us16x08_eq_high_freq_ctl,
+ .control_id = SND_US16X08_ID_EQHIGHFREQ,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "EQ High Frequence",
+ },
+};
+
+/* table of compressor controls */
+static const struct snd_us16x08_control_params comp_controls[] = {
+ { /* Comp enable */
+ .kcontrol_new = &snd_us16x08_compswitch_ctl,
+ .control_id = SND_US16X08_ID_COMP_SWITCH,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "Compressor Switch",
+ },
+ { /* Comp threshold */
+ .kcontrol_new = &snd_us16x08_comp_threshold_ctl,
+ .control_id = SND_US16X08_ID_COMP_THRESHOLD,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Compressor Threshold Volume",
+ },
+ { /* Comp ratio */
+ .kcontrol_new = &snd_us16x08_comp_ratio_ctl,
+ .control_id = SND_US16X08_ID_COMP_RATIO,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Compressor Ratio",
+ },
+ { /* Comp attack */
+ .kcontrol_new = &snd_us16x08_comp_attack_ctl,
+ .control_id = SND_US16X08_ID_COMP_ATTACK,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Compressor Attack",
+ },
+ { /* Comp release */
+ .kcontrol_new = &snd_us16x08_comp_release_ctl,
+ .control_id = SND_US16X08_ID_COMP_RELEASE,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Compressor Release",
+ },
+ { /* Comp gain */
+ .kcontrol_new = &snd_us16x08_comp_gain_ctl,
+ .control_id = SND_US16X08_ID_COMP_GAIN,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Compressor Volume",
+ },
+};
+
+/* table of channel controls */
+static const struct snd_us16x08_control_params channel_controls[] = {
+ { /* Phase */
+ .kcontrol_new = &snd_us16x08_ch_boolean_ctl,
+ .control_id = SND_US16X08_ID_PHASE,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "Phase Switch",
+ .default_val = 0
+ },
+ { /* Fader */
+ .kcontrol_new = &snd_us16x08_ch_int_ctl,
+ .control_id = SND_US16X08_ID_FADER,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Line Volume",
+ .default_val = 127
+ },
+ { /* Mute */
+ .kcontrol_new = &snd_us16x08_ch_boolean_ctl,
+ .control_id = SND_US16X08_ID_MUTE,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "Mute Switch",
+ .default_val = 0
+ },
+ { /* Pan */
+ .kcontrol_new = &snd_us16x08_pan_int_ctl,
+ .control_id = SND_US16X08_ID_PAN,
+ .type = USB_MIXER_U16,
+ .num_channels = 16,
+ .name = "Pan Left-Right Volume",
+ .default_val = 127
+ },
+};
+
+/* table of master controls */
+static const struct snd_us16x08_control_params master_controls[] = {
+ { /* Master */
+ .kcontrol_new = &snd_us16x08_master_ctl,
+ .control_id = SND_US16X08_ID_FADER,
+ .type = USB_MIXER_U8,
+ .num_channels = 16,
+ .name = "Master Volume",
+ .default_val = 127
+ },
+ { /* Bypass */
+ .kcontrol_new = &snd_us16x08_bus_ctl,
+ .control_id = SND_US16X08_ID_BYPASS,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "DSP Bypass Switch",
+ .default_val = 0
+ },
+ { /* Buss out */
+ .kcontrol_new = &snd_us16x08_bus_ctl,
+ .control_id = SND_US16X08_ID_BUSS_OUT,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "Buss Out Switch",
+ .default_val = 0
+ },
+ { /* Master mute */
+ .kcontrol_new = &snd_us16x08_bus_ctl,
+ .control_id = SND_US16X08_ID_MUTE,
+ .type = USB_MIXER_BOOLEAN,
+ .num_channels = 16,
+ .name = "Master Mute Switch",
+ .default_val = 0
+ },
+
+};
+
+int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
+{
+ int i, j;
+ int err;
+ struct usb_mixer_elem_info *elem;
+ struct snd_us16x08_comp_store *comp_store;
+ struct snd_us16x08_meter_store *meter_store;
+ struct snd_us16x08_eq_store *eq_store;
+
+ /* just check for non-MIDI interface */
+ if (mixer->hostif->desc.bInterfaceNumber == 3) {
+
+ /* add routing control */
+ err = add_new_ctl(mixer, &snd_us16x08_route_ctl,
+ SND_US16X08_ID_ROUTE, USB_MIXER_U8, 8, "Line Out Route",
+ NULL, false, &elem);
+ if (err < 0) {
+ usb_audio_dbg(mixer->chip,
+ "Failed to create route control, err:%d\n",
+ err);
+ return err;
+ }
+ for (i = 0; i < 8; i++)
+ elem->cache_val[i] = i < 2 ? i : i + 2;
+ elem->cached = 0xff;
+
+ /* create compressor mixer elements */
+ comp_store = snd_us16x08_create_comp_store();
+ if (!comp_store)
+ return -ENOMEM;
+
+ /* add master controls */
+ for (i = 0; i < ARRAY_SIZE(master_controls); i++) {
+
+ err = add_new_ctl(mixer,
+ master_controls[i].kcontrol_new,
+ master_controls[i].control_id,
+ master_controls[i].type,
+ master_controls[i].num_channels,
+ master_controls[i].name,
+ comp_store,
+ i == 0, /* release comp_store only once */
+ &elem);
+ if (err < 0)
+ return err;
+ elem->cache_val[0] = master_controls[i].default_val;
+ elem->cached = 1;
+ }
+
+ /* add channel controls */
+ for (i = 0; i < ARRAY_SIZE(channel_controls); i++) {
+
+ err = add_new_ctl(mixer,
+ channel_controls[i].kcontrol_new,
+ channel_controls[i].control_id,
+ channel_controls[i].type,
+ channel_controls[i].num_channels,
+ channel_controls[i].name,
+ comp_store,
+ false, &elem);
+ if (err < 0)
+ return err;
+ for (j = 0; j < SND_US16X08_MAX_CHANNELS; j++) {
+ elem->cache_val[j] =
+ channel_controls[i].default_val;
+ }
+ elem->cached = 0xffff;
+ }
+
+ /* create eq store */
+ eq_store = snd_us16x08_create_eq_store();
+ if (!eq_store)
+ return -ENOMEM;
+
+ /* add EQ controls */
+ for (i = 0; i < ARRAY_SIZE(eq_controls); i++) {
+
+ err = add_new_ctl(mixer,
+ eq_controls[i].kcontrol_new,
+ eq_controls[i].control_id,
+ eq_controls[i].type,
+ eq_controls[i].num_channels,
+ eq_controls[i].name,
+ eq_store,
+ i == 0, /* release eq_store only once */
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
+ /* add compressor controls */
+ for (i = 0; i < ARRAY_SIZE(comp_controls); i++) {
+
+ err = add_new_ctl(mixer,
+ comp_controls[i].kcontrol_new,
+ comp_controls[i].control_id,
+ comp_controls[i].type,
+ comp_controls[i].num_channels,
+ comp_controls[i].name,
+ comp_store,
+ false, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ /* create meters store */
+ meter_store = snd_us16x08_create_meter_store();
+ if (!meter_store)
+ return -ENOMEM;
+
+ /* meter function 'get' must access to compressor store
+ * so place a reference here
+ */
+ meter_store->comp_store = comp_store;
+ err = add_new_ctl(mixer, &snd_us16x08_meter_ctl,
+ SND_US16X08_ID_METER, USB_MIXER_U16, 0, "Level Meter",
+ meter_store, true, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
diff --git a/sound/usb/mixer_us16x08.h b/sound/usb/mixer_us16x08.h
new file mode 100644
index 000000000000..a6312fb0f962
--- /dev/null
+++ b/sound/usb/mixer_us16x08.h
@@ -0,0 +1,121 @@
+#ifndef __USB_MIXER_US16X08_H
+#define __USB_MIXER_US16X08_H
+
+#define SND_US16X08_MAX_CHANNELS 16
+
+/* define some bias, cause some alsa-mixers wont work with
+ * negative ranges or if mixer-min != 0
+ */
+#define SND_US16X08_NO_BIAS 0
+#define SND_US16X08_FADER_BIAS 127
+#define SND_US16X08_EQ_HIGHFREQ_BIAS 0x20
+#define SND_US16X08_COMP_THRESHOLD_BIAS 0x20
+#define SND_US16X08_COMP_ATTACK_BIAS 2
+#define SND_US16X08_COMP_RELEASE_BIAS 1
+
+/* get macro for components of kcontrol private_value */
+#define SND_US16X08_KCBIAS(x) (((x)->private_value >> 24) & 0xff)
+#define SND_US16X08_KCSTEP(x) (((x)->private_value >> 16) & 0xff)
+#define SND_US16X08_KCMIN(x) (((x)->private_value >> 8) & 0xff)
+#define SND_US16X08_KCMAX(x) (((x)->private_value >> 0) & 0xff)
+/* set macro for kcontrol private_value */
+#define SND_US16X08_KCSET(bias, step, min, max) \
+ (((bias) << 24) | ((step) << 16) | ((min) << 8) | (max))
+
+/* the URB request/type to control Tascam mixers */
+#define SND_US16X08_URB_REQUEST 0x1D
+#define SND_US16X08_URB_REQUESTTYPE 0x40
+
+/* the URB params to retrieve meter ranges */
+#define SND_US16X08_URB_METER_REQUEST 0x1e
+#define SND_US16X08_URB_METER_REQUESTTYPE 0xc0
+
+#define MUA0(x, y) ((x)[(y) * 10 + 4])
+#define MUA1(x, y) ((x)[(y) * 10 + 5])
+#define MUA2(x, y) ((x)[(y) * 10 + 6])
+#define MUB0(x, y) ((x)[(y) * 10 + 7])
+#define MUB1(x, y) ((x)[(y) * 10 + 8])
+#define MUB2(x, y) ((x)[(y) * 10 + 9])
+#define MUC0(x, y) ((x)[(y) * 10 + 10])
+#define MUC1(x, y) ((x)[(y) * 10 + 11])
+#define MUC2(x, y) ((x)[(y) * 10 + 12])
+#define MUC3(x, y) ((x)[(y) * 10 + 13])
+
+/* Common Channel control IDs */
+#define SND_US16X08_ID_BYPASS 0x45
+#define SND_US16X08_ID_BUSS_OUT 0x44
+#define SND_US16X08_ID_PHASE 0x85
+#define SND_US16X08_ID_MUTE 0x83
+#define SND_US16X08_ID_FADER 0x81
+#define SND_US16X08_ID_PAN 0x82
+#define SND_US16X08_ID_METER 0xB1
+
+#define SND_US16X08_ID_EQ_BAND_COUNT 4
+#define SND_US16X08_ID_EQ_PARAM_COUNT 4
+
+/* EQ level IDs */
+#define SND_US16X08_ID_EQLOWLEVEL 0x01
+#define SND_US16X08_ID_EQLOWMIDLEVEL 0x02
+#define SND_US16X08_ID_EQHIGHMIDLEVEL 0x03
+#define SND_US16X08_ID_EQHIGHLEVEL 0x04
+
+/* EQ frequence IDs */
+#define SND_US16X08_ID_EQLOWFREQ 0x11
+#define SND_US16X08_ID_EQLOWMIDFREQ 0x12
+#define SND_US16X08_ID_EQHIGHMIDFREQ 0x13
+#define SND_US16X08_ID_EQHIGHFREQ 0x14
+
+/* EQ width IDs */
+#define SND_US16X08_ID_EQLOWMIDWIDTH 0x22
+#define SND_US16X08_ID_EQHIGHMIDWIDTH 0x23
+
+#define SND_US16X08_ID_EQENABLE 0x30
+
+#define EQ_STORE_BAND_IDX(x) ((x) & 0xf)
+#define EQ_STORE_PARAM_IDX(x) (((x) & 0xf0) >> 4)
+
+#define SND_US16X08_ID_ROUTE 0x00
+
+/* Compressor Ids */
+#define SND_US16X08_ID_COMP_BASE 0x32
+#define SND_US16X08_ID_COMP_THRESHOLD SND_US16X08_ID_COMP_BASE
+#define SND_US16X08_ID_COMP_RATIO (SND_US16X08_ID_COMP_BASE + 1)
+#define SND_US16X08_ID_COMP_ATTACK (SND_US16X08_ID_COMP_BASE + 2)
+#define SND_US16X08_ID_COMP_RELEASE (SND_US16X08_ID_COMP_BASE + 3)
+#define SND_US16X08_ID_COMP_GAIN (SND_US16X08_ID_COMP_BASE + 4)
+#define SND_US16X08_ID_COMP_SWITCH (SND_US16X08_ID_COMP_BASE + 5)
+#define SND_US16X08_ID_COMP_COUNT 6
+
+#define COMP_STORE_IDX(x) ((x) - SND_US16X08_ID_COMP_BASE)
+
+struct snd_us16x08_eq_store {
+ u8 val[SND_US16X08_ID_EQ_BAND_COUNT][SND_US16X08_ID_EQ_PARAM_COUNT]
+ [SND_US16X08_MAX_CHANNELS];
+};
+
+struct snd_us16x08_comp_store {
+ u8 val[SND_US16X08_ID_COMP_COUNT][SND_US16X08_MAX_CHANNELS];
+};
+
+struct snd_us16x08_meter_store {
+ int meter_level[SND_US16X08_MAX_CHANNELS];
+ int master_level[2]; /* level of meter for master output */
+ int comp_index; /* round trip channel selector */
+ int comp_active_index; /* channel select from user space mixer */
+ int comp_level[16]; /* compressor reduction level */
+ struct snd_us16x08_comp_store *comp_store;
+};
+
+struct snd_us16x08_control_params {
+ struct snd_kcontrol_new *kcontrol_new;
+ int control_id;
+ int type;
+ int num_channels;
+ const char *name;
+ int default_val;
+};
+
+#define snd_us16x08_switch_info snd_ctl_boolean_mono_info
+
+int snd_us16x08_controls_create(struct usb_mixer_interface *mixer);
+#endif /* __USB_MIXER_US16X08_H */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 44d178ee9177..9aa5b1855481 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -218,7 +218,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
}
}
-static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
+static int start_endpoints(struct snd_usb_substream *subs)
{
int err;
@@ -231,7 +231,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
dev_dbg(&subs->dev->dev, "Starting data EP @%p\n", ep);
ep->data_subs = subs;
- err = snd_usb_endpoint_start(ep, can_sleep);
+ err = snd_usb_endpoint_start(ep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
return err;
@@ -260,7 +260,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep);
ep->sync_slave = subs->data_endpoint;
- err = snd_usb_endpoint_start(ep, can_sleep);
+ err = snd_usb_endpoint_start(ep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
return err;
@@ -348,6 +348,16 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
alts = &iface->altsetting[1];
goto add_sync_ep;
+ case USB_ID(0x2466, 0x8003):
+ ep = 0x86;
+ iface = usb_ifnum_to_if(dev, 2);
+
+ if (!iface || iface->num_altsetting == 0)
+ return -EINVAL;
+
+ alts = &iface->altsetting[1];
+ goto add_sync_ep;
+
}
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
@@ -806,17 +816,18 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
if (ret < 0)
goto unlock;
- iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
- alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
- ret = snd_usb_init_sample_rate(subs->stream->chip,
- subs->cur_audiofmt->iface,
- alts,
- subs->cur_audiofmt,
- subs->cur_rate);
- if (ret < 0)
- goto unlock;
-
if (subs->need_setup_ep) {
+
+ iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
+ alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
+ ret = snd_usb_init_sample_rate(subs->stream->chip,
+ subs->cur_audiofmt->iface,
+ alts,
+ subs->cur_audiofmt,
+ subs->cur_rate);
+ if (ret < 0)
+ goto unlock;
+
ret = configure_endpoint(subs);
if (ret < 0)
goto unlock;
@@ -839,7 +850,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
- ret = start_endpoints(subs, true);
+ ret = start_endpoints(subs);
unlock:
snd_usb_unlock_shutdown(subs->stream->chip);
@@ -1655,7 +1666,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- err = start_endpoints(subs, false);
+ err = start_endpoints(subs);
if (err < 0)
return err;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2782155ae3ce..01eff6ce6401 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1135,6 +1135,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0x02F7): /* Plantronics BT-600 */
case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
@@ -1165,6 +1166,18 @@ static bool is_marantz_denon_dac(unsigned int id)
return false;
}
+/* TEAC UD-501/UD-503/NT-503 USB DACs need a vendor cmd to switch
+ * between PCM/DOP and native DSD mode
+ */
+static bool is_teac_50X_dac(unsigned int id)
+{
+ switch (id) {
+ case USB_ID(0x0644, 0x8043): /* TEAC UD-501/UD-503/NT-503 */
+ return true;
+ }
+ return false;
+}
+
int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt)
{
@@ -1192,6 +1205,26 @@ int snd_usb_select_mode_quirk(struct snd_usb_substream *subs,
break;
}
mdelay(20);
+ } else if (is_teac_50X_dac(subs->stream->chip->usb_id)) {
+ /* Vendor mode switch cmd is required. */
+ switch (fmt->altsetting) {
+ case 3: /* DSD mode (DSD_U32) requested */
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 1, 1, NULL, 0);
+ if (err < 0)
+ return err;
+ break;
+
+ case 2: /* PCM or DOP mode (S32) requested */
+ case 1: /* PCM mode (S16) requested */
+ err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+ 0, 1, NULL, 0);
+ if (err < 0)
+ return err;
+ break;
+ }
}
return 0;
}
@@ -1327,6 +1360,21 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
if (fp->altsetting == 3)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
+
+ /* Amanero Combo384 USB interface with native DSD support */
+ case USB_ID(0x16d0, 0x071a):
+ if (fp->altsetting == 2) {
+ switch (chip->dev->descriptor.bcdDevice) {
+ case 0x199:
+ return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+ case 0x19b:
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
+ default:
+ break;
+ }
+ }
+ break;
+
default:
break;
}
@@ -1337,5 +1385,11 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
}
+ /* TEAC devices with USB DAC functionality */
+ if (is_teac_50X_dac(chip->usb_id)) {
+ if (fp->altsetting == 3)
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
+ }
+
return 0;
}
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index cf5dc33f4a6d..cf45bf1f7ee0 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -137,13 +137,12 @@ static void usb_stream_hwdep_vm_open(struct vm_area_struct *area)
snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count));
}
-static int usb_stream_hwdep_vm_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static int usb_stream_hwdep_vm_fault(struct vm_fault *vmf)
{
unsigned long offset;
struct page *page;
void *vaddr;
- struct us122l *us122l = area->vm_private_data;
+ struct us122l *us122l = vmf->vma->vm_private_data;
struct usb_stream *s;
mutex_lock(&us122l->mutex);
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 0b34dbc8f302..605e1047c01d 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -31,19 +31,18 @@
#include "usbusx2y.h"
#include "usX2Yhwdep.h"
-static int snd_us428ctls_vm_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static int snd_us428ctls_vm_fault(struct vm_fault *vmf)
{
unsigned long offset;
struct page * page;
void *vaddr;
snd_printdd("ENTER, start %lXh, pgoff %ld\n",
- area->vm_start,
+ vmf->vma->vm_start,
vmf->pgoff);
offset = vmf->pgoff << PAGE_SHIFT;
- vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->us428ctls_sharedmem + offset;
+ vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset;
page = virt_to_page(vaddr);
get_page(page);
vmf->page = page;
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 90766a92e7fd..f95164b91152 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -652,14 +652,13 @@ static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
}
-static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
{
unsigned long offset;
void *vaddr;
offset = vmf->pgoff << PAGE_SHIFT;
- vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
+ vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
vmf->page = virt_to_page(vaddr);
get_page(vmf->page);
return 0;