summaryrefslogtreecommitdiffstats
path: root/sound/usb/mixer.c
diff options
context:
space:
mode:
authorRuslan Bilovol2018-03-21 01:03:59 +0100
committerTakashi Iwai2018-03-21 11:46:33 +0100
commit9a2fe9b801f585baccf8352d82839dcd54b300cf (patch)
tree333de5ebcac026ce1a18da1c693cb8e9603c9a54 /sound/usb/mixer.c
parentALSA: usb-audio: move audioformat quirks to quirks.c (diff)
downloadkernel-qcow2-linux-9a2fe9b801f585baccf8352d82839dcd54b300cf.tar.gz
kernel-qcow2-linux-9a2fe9b801f585baccf8352d82839dcd54b300cf.tar.xz
kernel-qcow2-linux-9a2fe9b801f585baccf8352d82839dcd54b300cf.zip
ALSA: usb: initial USB Audio Device Class 3.0 support
Recently released USB Audio Class 3.0 specification introduces many significant changes comparing to previous versions, like - new Power Domains, support for LPM/L1 - new Cluster descriptor - changed layout of all class-specific descriptors - new High Capability descriptors - New class-specific String descriptors - new and removed units - additional sources for interrupts - removed Type II Audio Data Formats - ... and many other things (check spec) It also provides backward compatibility through multiple configurations, as well as requires mandatory support for BADD (Basic Audio Device Definition) on each ADC3.0 compliant device This patch adds initial support of UAC3 specification that is enough for Generic I/O Profile (BAOF, BAIF) device support from BADD document. Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/mixer.c')
-rw-r--r--sound/usb/mixer.c337
1 files changed, 228 insertions, 109 deletions
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 06b22624ab7a..1c02f7373eda 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;
@@ -1423,7 +1474,7 @@ 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,
+ if (!uac_v2v3_control_is_readable(hdr->bmControls,
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
return 0;
@@ -1439,7 +1490,7 @@ 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,
+ if (uac_v2v3_control_is_writeable(hdr->bmControls,
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
else {
@@ -1502,7 +1553,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 +1570,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 */
@@ -1577,7 +1646,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
build_feature_ctl(state, _ftr, 0, i, &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;
@@ -1587,9 +1656,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
mask = snd_usb_combine_bytes(bmaControls +
csize * (j+1), csize);
- if (uac2_control_is_readable(mask, i)) {
+ if (uac_v2v3_control_is_readable(mask, i)) {
ch_bits |= (1 << j);
- if (!uac2_control_is_writeable(mask, i))
+ if (!uac_v2v3_control_is_writeable(mask, i))
ch_read_only |= (1 << j);
}
}
@@ -1610,9 +1679,9 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
if (ch_bits & 1)
build_feature_ctl(state, _ftr, ch_bits, i,
&iterm, unitid, ch_read_only);
- if (uac2_control_is_readable(master_bits, i))
+ if (uac_v2v3_control_is_readable(master_bits, i))
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
- !uac2_control_is_writeable(master_bits, i));
+ !uac_v2v3_control_is_writeable(master_bits, i));
}
}
@@ -2220,6 +2289,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 +2300,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 +2425,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 +2446,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 +2713,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 ||