summaryrefslogtreecommitdiffstats
path: root/sound/usb/mixer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/mixer.c')
-rw-r--r--sound/usb/mixer.c398
1 files changed, 267 insertions, 131 deletions
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 06b22624ab7a..3075ac50a391 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -51,6 +51,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <linux/usb/audio-v3.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -189,7 +190,7 @@ static void *find_audio_control_unit(struct mixer_build *state,
USB_DT_CS_INTERFACE)) != NULL) {
if (hdr->bLength >= 4 &&
hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL &&
- hdr->bDescriptorSubtype <= UAC2_SAMPLE_RATE_CONVERTER &&
+ hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER &&
hdr->bUnitID == unit)
return hdr;
}
@@ -468,9 +469,10 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
validx += cval->idx_off;
+
if (cval->head.mixer->protocol == UAC_VERSION_1) {
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
- } else { /* UAC_VERSION_2 */
+ } else { /* UAC_VERSION_2/3 */
val_len = uac2_ctl_value_size(cval->val_type);
/* FIXME */
@@ -723,6 +725,7 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
static int check_input_term(struct mixer_build *state, int id,
struct usb_audio_term *term)
{
+ int protocol = state->mixer->protocol;
int err;
void *p1;
@@ -730,16 +733,104 @@ static int check_input_term(struct mixer_build *state, int id,
while ((p1 = find_audio_control_unit(state, id)) != NULL) {
unsigned char *hdr = p1;
term->id = id;
- switch (hdr[2]) {
- case UAC_INPUT_TERMINAL:
- if (state->mixer->protocol == UAC_VERSION_1) {
- struct uac_input_terminal_descriptor *d = p1;
- term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le16_to_cpu(d->wChannelConfig);
- term->name = d->iTerminal;
- } else { /* UAC_VERSION_2 */
- struct uac2_input_terminal_descriptor *d = p1;
+
+ if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+ switch (hdr[2]) {
+ case UAC_INPUT_TERMINAL:
+ if (protocol == UAC_VERSION_1) {
+ struct uac_input_terminal_descriptor *d = p1;
+
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le16_to_cpu(d->wChannelConfig);
+ term->name = d->iTerminal;
+ } else { /* UAC_VERSION_2 */
+ struct uac2_input_terminal_descriptor *d = p1;
+
+ /* call recursively to verify that the
+ * referenced clock entity is valid */
+ err = check_input_term(state, d->bCSourceID, term);
+ if (err < 0)
+ return err;
+
+ /* save input term properties after recursion,
+ * to ensure they are not overriden by the
+ * recursion calls */
+ term->id = id;
+ term->type = le16_to_cpu(d->wTerminalType);
+ term->channels = d->bNrChannels;
+ term->chconfig = le32_to_cpu(d->bmChannelConfig);
+ term->name = d->iTerminal;
+ }
+ return 0;
+ case UAC_FEATURE_UNIT: {
+ /* the header is the same for v1 and v2 */
+ struct uac_feature_unit_descriptor *d = p1;
+
+ id = d->bSourceID;
+ break; /* continue to parse */
+ }
+ case UAC_MIXER_UNIT: {
+ struct uac_mixer_unit_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->channels = uac_mixer_unit_bNrChannels(d);
+ term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol);
+ term->name = uac_mixer_unit_iMixer(d);
+ return 0;
+ }
+ case UAC_SELECTOR_UNIT:
+ case UAC2_CLOCK_SELECTOR: {
+ struct uac_selector_unit_descriptor *d = p1;
+ /* call recursively to retrieve the channel info */
+ err = check_input_term(state, d->baSourceID[0], term);
+ if (err < 0)
+ return err;
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->id = id;
+ term->name = uac_selector_unit_iSelector(d);
+ return 0;
+ }
+ case UAC1_PROCESSING_UNIT:
+ case UAC1_EXTENSION_UNIT:
+ /* UAC2_PROCESSING_UNIT_V2 */
+ /* UAC2_EFFECT_UNIT */
+ case UAC2_EXTENSION_UNIT_V2: {
+ struct uac_processing_unit_descriptor *d = p1;
+
+ if (protocol == UAC_VERSION_2 &&
+ hdr[2] == UAC2_EFFECT_UNIT) {
+ /* UAC2/UAC1 unit IDs overlap here in an
+ * uncompatible way. Ignore this unit for now.
+ */
+ return 0;
+ }
+
+ if (d->bNrInPins) {
+ id = d->baSourceID[0];
+ break; /* continue to parse */
+ }
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->channels = uac_processing_unit_bNrChannels(d);
+ term->chconfig = uac_processing_unit_wChannelConfig(d, protocol);
+ term->name = uac_processing_unit_iProcessing(d, protocol);
+ return 0;
+ }
+ case UAC2_CLOCK_SOURCE: {
+ struct uac_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->id = id;
+ term->name = d->iClockSource;
+ return 0;
+ }
+ default:
+ return -ENODEV;
+ }
+ } else { /* UAC_VERSION_3 */
+ switch (hdr[2]) {
+ case UAC_INPUT_TERMINAL: {
+ struct uac3_input_terminal_descriptor *d = p1;
/* call recursively to verify that the
* referenced clock entity is valid */
@@ -752,71 +843,31 @@ static int check_input_term(struct mixer_build *state, int id,
* recursion calls */
term->id = id;
term->type = le16_to_cpu(d->wTerminalType);
- term->channels = d->bNrChannels;
- term->chconfig = le32_to_cpu(d->bmChannelConfig);
- term->name = d->iTerminal;
- }
- return 0;
- case UAC_FEATURE_UNIT: {
- /* the header is the same for v1 and v2 */
- struct uac_feature_unit_descriptor *d = p1;
- id = d->bSourceID;
- break; /* continue to parse */
- }
- case UAC_MIXER_UNIT: {
- struct uac_mixer_unit_descriptor *d = p1;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->channels = uac_mixer_unit_bNrChannels(d);
- term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol);
- term->name = uac_mixer_unit_iMixer(d);
- return 0;
- }
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR: {
- struct uac_selector_unit_descriptor *d = p1;
- /* call recursively to retrieve the channel info */
- err = check_input_term(state, d->baSourceID[0], term);
- if (err < 0)
- return err;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->id = id;
- term->name = uac_selector_unit_iSelector(d);
- return 0;
- }
- case UAC1_PROCESSING_UNIT:
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 */
- /* UAC2_EFFECT_UNIT */
- case UAC2_EXTENSION_UNIT_V2: {
- struct uac_processing_unit_descriptor *d = p1;
-
- if (state->mixer->protocol == UAC_VERSION_2 &&
- hdr[2] == UAC2_EFFECT_UNIT) {
- /* UAC2/UAC1 unit IDs overlap here in an
- * uncompatible way. Ignore this unit for now.
- */
+
+ /* REVISIT: UAC3 IT doesn't have channels/cfg */
+ term->channels = 0;
+ term->chconfig = 0;
+
+ term->name = le16_to_cpu(d->wTerminalDescrStr);
return 0;
}
+ case UAC3_FEATURE_UNIT: {
+ struct uac3_feature_unit_descriptor *d = p1;
- if (d->bNrInPins) {
- id = d->baSourceID[0];
+ id = d->bSourceID;
break; /* continue to parse */
}
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->channels = uac_processing_unit_bNrChannels(d);
- term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
- term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
- return 0;
- }
- case UAC2_CLOCK_SOURCE: {
- struct uac_clock_source_descriptor *d = p1;
- term->type = d->bDescriptorSubtype << 16; /* virtual type */
- term->id = id;
- term->name = d->iClockSource;
- return 0;
- }
- default:
- return -ENODEV;
+ case UAC3_CLOCK_SOURCE: {
+ struct uac3_clock_source_descriptor *d = p1;
+
+ term->type = d->bDescriptorSubtype << 16; /* virtual type */
+ term->id = id;
+ term->name = le16_to_cpu(d->wClockSourceStr);
+ return 0;
+ }
+ default:
+ return -ENODEV;
+ }
}
}
return -ENODEV;
@@ -828,26 +879,27 @@ static int check_input_term(struct mixer_build *state, int id,
/* feature unit control information */
struct usb_feature_control_info {
+ int control;
const char *name;
int type; /* data type for uac1 */
int type_uac2; /* data type for uac2 if different from uac1, else -1 */
};
static struct usb_feature_control_info audio_feature_info[] = {
- { "Mute", USB_MIXER_INV_BOOLEAN, -1 },
- { "Volume", USB_MIXER_S16, -1 },
- { "Tone Control - Bass", USB_MIXER_S8, -1 },
- { "Tone Control - Mid", USB_MIXER_S8, -1 },
- { "Tone Control - Treble", USB_MIXER_S8, -1 },
- { "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemeted yet */
- { "Auto Gain Control", USB_MIXER_BOOLEAN, -1 },
- { "Delay Control", USB_MIXER_U16, USB_MIXER_U32 },
- { "Bass Boost", USB_MIXER_BOOLEAN, -1 },
- { "Loudness", USB_MIXER_BOOLEAN, -1 },
+ { UAC_FU_MUTE, "Mute", USB_MIXER_INV_BOOLEAN, -1 },
+ { UAC_FU_VOLUME, "Volume", USB_MIXER_S16, -1 },
+ { UAC_FU_BASS, "Tone Control - Bass", USB_MIXER_S8, -1 },
+ { UAC_FU_MID, "Tone Control - Mid", USB_MIXER_S8, -1 },
+ { UAC_FU_TREBLE, "Tone Control - Treble", USB_MIXER_S8, -1 },
+ { UAC_FU_GRAPHIC_EQUALIZER, "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemented yet */
+ { UAC_FU_AUTOMATIC_GAIN, "Auto Gain Control", USB_MIXER_BOOLEAN, -1 },
+ { UAC_FU_DELAY, "Delay Control", USB_MIXER_U16, USB_MIXER_U32 },
+ { UAC_FU_BASS_BOOST, "Bass Boost", USB_MIXER_BOOLEAN, -1 },
+ { UAC_FU_LOUDNESS, "Loudness", USB_MIXER_BOOLEAN, -1 },
/* UAC2 specific */
- { "Input Gain Control", USB_MIXER_S16, -1 },
- { "Input Gain Pad Control", USB_MIXER_S16, -1 },
- { "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
+ { UAC2_FU_INPUT_GAIN, "Input Gain Control", USB_MIXER_S16, -1 },
+ { UAC2_FU_INPUT_GAIN_PAD, "Input Gain Pad Control", USB_MIXER_S16, -1 },
+ { UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
};
/* private_free callback */
@@ -1242,6 +1294,17 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl,
strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name));
}
+static struct usb_feature_control_info *get_feature_control_info(int control)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(audio_feature_info); ++i) {
+ if (audio_feature_info[i].control == control)
+ return &audio_feature_info[i];
+ }
+ return NULL;
+}
+
static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
unsigned int ctl_mask, int control,
struct usb_audio_term *iterm, int unitid,
@@ -1257,8 +1320,6 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
const struct usbmix_name_map *map;
unsigned int range;
- control++; /* change from zero-based to 1-based value */
-
if (control == UAC_FU_GRAPHIC_EQUALIZER) {
/* FIXME: not supported yet */
return;
@@ -1274,7 +1335,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = control;
cval->cmask = ctl_mask;
- ctl_info = &audio_feature_info[control-1];
+
+ ctl_info = get_feature_control_info(control);
+ if (!ctl_info)
+ return;
if (state->mixer->protocol == UAC_VERSION_1)
cval->val_type = ctl_info->type;
else /* UAC_VERSION_2 */
@@ -1423,8 +1487,8 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
* The only property of this unit we are interested in is the
* clock source validity. If that isn't readable, just bail out.
*/
- if (!uac2_control_is_readable(hdr->bmControls,
- ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+ if (!uac_v2v3_control_is_readable(hdr->bmControls,
+ UAC2_CS_CONTROL_CLOCK_VALID))
return 0;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -1439,8 +1503,8 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
cval->val_type = USB_MIXER_BOOLEAN;
cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
- if (uac2_control_is_writeable(hdr->bmControls,
- ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+ if (uac_v2v3_control_is_writeable(hdr->bmControls,
+ UAC2_CS_CONTROL_CLOCK_VALID))
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
else {
cval->master_readonly = 1;
@@ -1502,7 +1566,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
- } else {
+ } else if (state->mixer->protocol == UAC_VERSION_2) {
struct uac2_feature_unit_descriptor *ftr = _ftr;
if (hdr->bLength < 6) {
usb_audio_err(state->chip,
@@ -1519,6 +1583,24 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
unitid);
return -EINVAL;
}
+ } else { /* UAC_VERSION_3 */
+ struct uac3_feature_unit_descriptor *ftr = _ftr;
+
+ if (hdr->bLength < 7) {
+ usb_audio_err(state->chip,
+ "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
+ unitid);
+ return -EINVAL;
+ }
+ csize = 4;
+ channels = (ftr->bLength - 7) / 4 - 1;
+ bmaControls = ftr->bmaControls;
+ if (hdr->bLength < 7 + csize) {
+ usb_audio_err(state->chip,
+ "unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
+ unitid);
+ return -EINVAL;
+ }
}
/* parse the source unit */
@@ -1556,6 +1638,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
/* check all control types */
for (i = 0; i < 10; i++) {
unsigned int ch_bits = 0;
+ int control = audio_feature_info[i].control;
+
for (j = 0; j < channels; j++) {
unsigned int mask;
@@ -1571,25 +1655,26 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
* (for ease of programming).
*/
if (ch_bits & 1)
- build_feature_ctl(state, _ftr, ch_bits, i,
+ build_feature_ctl(state, _ftr, ch_bits, control,
&iterm, unitid, 0);
if (master_bits & (1 << i))
- build_feature_ctl(state, _ftr, 0, i, &iterm,
- unitid, 0);
+ build_feature_ctl(state, _ftr, 0, control,
+ &iterm, unitid, 0);
}
- } else { /* UAC_VERSION_2 */
+ } else { /* UAC_VERSION_2/3 */
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
unsigned int ch_bits = 0;
unsigned int ch_read_only = 0;
+ int control = audio_feature_info[i].control;
for (j = 0; j < channels; j++) {
unsigned int mask;
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
- if (uac2_control_is_readable(mask, i)) {
+ if (uac_v2v3_control_is_readable(mask, control)) {
ch_bits |= (1 << j);
- if (!uac2_control_is_writeable(mask, i))
+ if (!uac_v2v3_control_is_writeable(mask, control))
ch_read_only |= (1 << j);
}
}
@@ -1608,11 +1693,12 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
* (for ease of programming).
*/
if (ch_bits & 1)
- build_feature_ctl(state, _ftr, ch_bits, i,
+ build_feature_ctl(state, _ftr, ch_bits, control,
&iterm, unitid, ch_read_only);
- if (uac2_control_is_readable(master_bits, i))
+ if (uac_v2v3_control_is_readable(master_bits, control))
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
- !uac2_control_is_writeable(master_bits, i));
+ !uac_v2v3_control_is_writeable(master_bits,
+ control));
}
}
@@ -2220,6 +2306,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
static int parse_audio_unit(struct mixer_build *state, int unitid)
{
unsigned char *p1;
+ int protocol = state->mixer->protocol;
if (test_and_set_bit(unitid, state->unitbitmap))
return 0; /* the unit already visited */
@@ -2230,36 +2317,61 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
return -EINVAL;
}
- switch (p1[2]) {
- case UAC_INPUT_TERMINAL:
- return 0; /* NOP */
- case UAC_MIXER_UNIT:
- return parse_audio_mixer_unit(state, unitid, p1);
- case UAC2_CLOCK_SOURCE:
- return parse_clock_source_unit(state, unitid, p1);
- case UAC_SELECTOR_UNIT:
- case UAC2_CLOCK_SELECTOR:
- return parse_audio_selector_unit(state, unitid, p1);
- case UAC_FEATURE_UNIT:
- return parse_audio_feature_unit(state, unitid, p1);
- case UAC1_PROCESSING_UNIT:
- /* UAC2_EFFECT_UNIT has the same value */
- if (state->mixer->protocol == UAC_VERSION_1)
- return parse_audio_processing_unit(state, unitid, p1);
- else
- return 0; /* FIXME - effect units not implemented yet */
- case UAC1_EXTENSION_UNIT:
- /* UAC2_PROCESSING_UNIT_V2 has the same value */
- if (state->mixer->protocol == UAC_VERSION_1)
+ if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
+ switch (p1[2]) {
+ case UAC_INPUT_TERMINAL:
+ return 0; /* NOP */
+ case UAC_MIXER_UNIT:
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case UAC2_CLOCK_SOURCE:
+ return parse_clock_source_unit(state, unitid, p1);
+ case UAC_SELECTOR_UNIT:
+ case UAC2_CLOCK_SELECTOR:
+ return parse_audio_selector_unit(state, unitid, p1);
+ case UAC_FEATURE_UNIT:
+ return parse_audio_feature_unit(state, unitid, p1);
+ case UAC1_PROCESSING_UNIT:
+ /* UAC2_EFFECT_UNIT has the same value */
+ if (protocol == UAC_VERSION_1)
+ return parse_audio_processing_unit(state, unitid, p1);
+ else
+ return 0; /* FIXME - effect units not implemented yet */
+ case UAC1_EXTENSION_UNIT:
+ /* UAC2_PROCESSING_UNIT_V2 has the same value */
+ if (protocol == UAC_VERSION_1)
+ return parse_audio_extension_unit(state, unitid, p1);
+ else /* UAC_VERSION_2 */
+ return parse_audio_processing_unit(state, unitid, p1);
+ case UAC2_EXTENSION_UNIT_V2:
return parse_audio_extension_unit(state, unitid, p1);
- else /* UAC_VERSION_2 */
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+ return -EINVAL;
+ }
+ } else { /* UAC_VERSION_3 */
+ switch (p1[2]) {
+ case UAC_INPUT_TERMINAL:
+ return 0; /* NOP */
+ case UAC3_MIXER_UNIT:
+ return parse_audio_mixer_unit(state, unitid, p1);
+ case UAC3_CLOCK_SOURCE:
+ return parse_clock_source_unit(state, unitid, p1);
+ case UAC3_CLOCK_SELECTOR:
+ return parse_audio_selector_unit(state, unitid, p1);
+ case UAC3_FEATURE_UNIT:
+ return parse_audio_feature_unit(state, unitid, p1);
+ case UAC3_EFFECT_UNIT:
+ return 0; /* FIXME - effect units not implemented yet */
+ case UAC3_PROCESSING_UNIT:
return parse_audio_processing_unit(state, unitid, p1);
- case UAC2_EXTENSION_UNIT_V2:
- return parse_audio_extension_unit(state, unitid, p1);
- default:
- usb_audio_err(state->chip,
- "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
- return -EINVAL;
+ case UAC3_EXTENSION_UNIT:
+ return parse_audio_extension_unit(state, unitid, p1);
+ default:
+ usb_audio_err(state->chip,
+ "unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+ return -EINVAL;
+ }
}
}
@@ -2330,7 +2442,7 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
err = parse_audio_unit(&state, desc->bSourceID);
if (err < 0 && err != -EINVAL)
return err;
- } else { /* UAC_VERSION_2 */
+ } else if (mixer->protocol == UAC_VERSION_2) {
struct uac2_output_terminal_descriptor *desc = p;
if (desc->bLength < sizeof(*desc))
@@ -2351,6 +2463,27 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
err = parse_audio_unit(&state, desc->bCSourceID);
if (err < 0 && err != -EINVAL)
return err;
+ } else { /* UAC_VERSION_3 */
+ struct uac3_output_terminal_descriptor *desc = p;
+
+ if (desc->bLength < sizeof(*desc))
+ continue; /* invalid descriptor? */
+ /* mark terminal ID as visited */
+ set_bit(desc->bTerminalID, state.unitbitmap);
+ state.oterm.id = desc->bTerminalID;
+ state.oterm.type = le16_to_cpu(desc->wTerminalType);
+ state.oterm.name = le16_to_cpu(desc->wTerminalDescrStr);
+ err = parse_audio_unit(&state, desc->bSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
+
+ /*
+ * For UAC3, use the same approach to also add the
+ * clock selectors
+ */
+ err = parse_audio_unit(&state, desc->bCSourceID);
+ if (err < 0 && err != -EINVAL)
+ return err;
}
}
@@ -2597,6 +2730,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
case UAC_VERSION_2:
mixer->protocol = UAC_VERSION_2;
break;
+ case UAC_VERSION_3:
+ mixer->protocol = UAC_VERSION_3;
+ break;
}
if ((err = snd_usb_mixer_controls(mixer)) < 0 ||