Commit 6cfd839a authored by Jorge Sanjuan's avatar Jorge Sanjuan Committed by Takashi Iwai

ALSA: usb-audio: UAC3. Add support for mixer unit.

This adds support for the MIXER UNIT in UAC3. All the information
is obtained from the (HIGH CAPABILITY) Cluster's header. We don't
read the rest of the logical cluster to obtain the channel config
as that wont make any difference in the current mixer behaviour.

The name of the mixer unit is not yet requested as there is not
support for the UAC3 Class Specific String requests.

Tested in an UAC3 device working as a HEADSET with a basic mixer
unit (same as the one in the BADD spec) with no controls.
Signed-off-by: default avatarJorge Sanjuan <jorge.sanjuan@codethink.co.uk>
Reviewed-by: default avatarRuslan Bilovol <ruslan.bilovol@gmail.com>
Tested-by: default avatarRuslan Bilovol <ruslan.bilovol@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 8def12d9
...@@ -285,9 +285,22 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor ...@@ -285,9 +285,22 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor
static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc, static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc,
int protocol) int protocol)
{ {
return (protocol == UAC_VERSION_1) ? switch (protocol) {
&desc->baSourceID[desc->bNrInPins + 4] : case UAC_VERSION_1:
&desc->baSourceID[desc->bNrInPins + 6]; return &desc->baSourceID[desc->bNrInPins + 4];
case UAC_VERSION_2:
return &desc->baSourceID[desc->bNrInPins + 6];
case UAC_VERSION_3:
return &desc->baSourceID[desc->bNrInPins + 2];
default:
return NULL;
}
}
static inline __u16 uac3_mixer_unit_wClusterDescrID(struct uac_mixer_unit_descriptor *desc)
{
return (desc->baSourceID[desc->bNrInPins + 1] << 8) |
desc->baSourceID[desc->bNrInPins];
} }
static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc) static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
......
...@@ -716,6 +716,66 @@ static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iter ...@@ -716,6 +716,66 @@ static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iter
return 0; return 0;
} }
/*
* Get logical cluster information for UAC3 devices.
*/
static int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id)
{
struct uac3_cluster_header_descriptor c_header;
int err;
err = snd_usb_ctl_msg(state->chip->dev,
usb_rcvctrlpipe(state->chip->dev, 0),
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
cluster_id,
snd_usb_ctrl_intf(state->chip),
&c_header, sizeof(c_header));
if (err < 0)
goto error;
if (err != sizeof(c_header)) {
err = -EIO;
goto error;
}
return c_header.bNrChannels;
error:
usb_audio_err(state->chip, "cannot request logical cluster ID: %d (err: %d)\n", cluster_id, err);
return err;
}
/*
* Get number of channels for a Mixer Unit.
*/
static int uac_mixer_unit_get_channels(struct mixer_build *state,
struct uac_mixer_unit_descriptor *desc)
{
int mu_channels;
if (desc->bLength < 11)
return -EINVAL;
if (!desc->bNrInPins)
return -EINVAL;
switch (state->mixer->protocol) {
case UAC_VERSION_1:
case UAC_VERSION_2:
default:
mu_channels = uac_mixer_unit_bNrChannels(desc);
break;
case UAC_VERSION_3:
mu_channels = get_cluster_channels_v3(state,
uac3_mixer_unit_wClusterDescrID(desc));
break;
}
if (!mu_channels)
return -EINVAL;
return mu_channels;
}
/* /*
* parse the source unit recursively until it reaches to a terminal * parse the source unit recursively until it reaches to a terminal
* or a branched unit. * or a branched unit.
...@@ -863,6 +923,18 @@ static int check_input_term(struct mixer_build *state, int id, ...@@ -863,6 +923,18 @@ static int check_input_term(struct mixer_build *state, int id,
term->name = le16_to_cpu(d->wClockSourceStr); term->name = le16_to_cpu(d->wClockSourceStr);
return 0; return 0;
} }
case UAC3_MIXER_UNIT: {
struct uac_mixer_unit_descriptor *d = p1;
err = uac_mixer_unit_get_channels(state, d);
if (err < 0)
return err;
term->channels = err;
term->type = d->bDescriptorSubtype << 16; /* virtual type */
return 0;
}
default: default:
return -ENODEV; return -ENODEV;
} }
...@@ -1826,11 +1898,10 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, ...@@ -1826,11 +1898,10 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
*/ */
static void build_mixer_unit_ctl(struct mixer_build *state, static void build_mixer_unit_ctl(struct mixer_build *state,
struct uac_mixer_unit_descriptor *desc, struct uac_mixer_unit_descriptor *desc,
int in_pin, int in_ch, int unitid, int in_pin, int in_ch, int num_outs,
struct usb_audio_term *iterm) int unitid, struct usb_audio_term *iterm)
{ {
struct usb_mixer_elem_info *cval; struct usb_mixer_elem_info *cval;
unsigned int num_outs = uac_mixer_unit_bNrChannels(desc);
unsigned int i, len; unsigned int i, len;
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
const struct usbmix_name_map *map; const struct usbmix_name_map *map;
...@@ -1907,14 +1978,17 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, ...@@ -1907,14 +1978,17 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
int input_pins, num_ins, num_outs; int input_pins, num_ins, num_outs;
int pin, ich, err; int pin, ich, err;
if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || err = uac_mixer_unit_get_channels(state, desc);
!(num_outs = uac_mixer_unit_bNrChannels(desc))) { if (err < 0) {
usb_audio_err(state->chip, usb_audio_err(state->chip,
"invalid MIXER UNIT descriptor %d\n", "invalid MIXER UNIT descriptor %d\n",
unitid); unitid);
return -EINVAL; return err;
} }
num_outs = err;
input_pins = desc->bNrInPins;
num_ins = 0; num_ins = 0;
ich = 0; ich = 0;
for (pin = 0; pin < input_pins; pin++) { for (pin = 0; pin < input_pins; pin++) {
...@@ -1941,7 +2015,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, ...@@ -1941,7 +2015,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
} }
} }
if (ich_has_controls) if (ich_has_controls)
build_mixer_unit_ctl(state, desc, pin, ich, build_mixer_unit_ctl(state, desc, pin, ich, num_outs,
unitid, &iterm); unitid, &iterm);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment