summaryrefslogtreecommitdiff
path: root/sound/usb/mixer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/mixer.c')
-rw-r--r--sound/usb/mixer.c108
1 files changed, 94 insertions, 14 deletions
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index c4abb3bca2ad..fb77847cbffc 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1328,6 +1328,51 @@ static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,
return 0;
}
+/* get the connectors status and report it as boolean type */
+static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_elem_info *cval = kcontrol->private_data;
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
+ int idx = 0, validx, ret, val;
+
+ validx = cval->control << 8 | 0;
+
+ ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
+ if (ret)
+ goto error;
+
+ idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
+ if (cval->head.mixer->protocol == UAC_VERSION_2) {
+ struct uac2_connectors_ctl_blk uac2_conn;
+
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx, idx, &uac2_conn, sizeof(uac2_conn));
+ val = !!uac2_conn.bNrChannels;
+ } else { /* UAC_VERSION_3 */
+ struct uac3_insertion_ctl_blk uac3_conn;
+
+ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR,
+ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+ validx, idx, &uac3_conn, sizeof(uac3_conn));
+ val = !!uac3_conn.bmConInserted;
+ }
+
+ snd_usb_unlock_shutdown(chip);
+
+ if (ret < 0) {
+error:
+ usb_audio_err(chip,
+ "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+ UAC_GET_CUR, validx, idx, cval->val_type);
+ return ret;
+ }
+
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
static struct snd_kcontrol_new usb_feature_unit_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later manually */
@@ -1358,6 +1403,15 @@ static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = {
.put = NULL,
};
+static const struct snd_kcontrol_new usb_connector_ctl_ro = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .name = "", /* will be filled later manually */
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ctl_boolean_mono_info,
+ .get = mixer_ctl_connector_get,
+ .put = NULL,
+};
+
/*
* This symbol is exported in order to allow the mixer quirks to
* hook up to the standard feature unit control mechanism
@@ -1626,17 +1680,25 @@ static void build_connector_control(struct mixer_build *state,
return;
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id);
/*
- * The first byte from reading the UAC2_TE_CONNECTOR control returns the
- * number of channels connected. This boolean ctl will simply report
- * if any channels are connected or not.
- * (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block)
+ * UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the
+ * number of channels connected.
+ *
+ * UAC3: The first byte specifies size of bitmap for the inserted controls. The
+ * following byte(s) specifies which connectors are inserted.
+ *
+ * This boolean ctl will simply report if any channels are connected
+ * or not.
*/
- cval->control = UAC2_TE_CONNECTOR;
+ if (state->mixer->protocol == UAC_VERSION_2)
+ cval->control = UAC2_TE_CONNECTOR;
+ else /* UAC_VERSION_3 */
+ cval->control = UAC3_TE_INSERTION;
+
cval->val_type = USB_MIXER_BOOLEAN;
cval->channels = 1; /* report true if any channel is connected */
cval->min = 0;
cval->max = 1;
- kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
+ kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
if (!kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
kfree(cval);
@@ -1954,16 +2016,28 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
void *raw_desc)
{
struct usb_audio_term iterm;
- struct uac2_input_terminal_descriptor *d = raw_desc;
+ unsigned int control, bmctls, term_id;
- check_input_term(state, d->bTerminalID, &iterm);
if (state->mixer->protocol == UAC_VERSION_2) {
- /* Check for jack detection. */
- if (uac_v2v3_control_is_readable(le16_to_cpu(d->bmControls),
- UAC2_TE_CONNECTOR)) {
- build_connector_control(state, &iterm, true);
- }
+ struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
+ control = UAC2_TE_CONNECTOR;
+ term_id = d_v2->bTerminalID;
+ bmctls = le16_to_cpu(d_v2->bmControls);
+ } else if (state->mixer->protocol == UAC_VERSION_3) {
+ struct uac3_input_terminal_descriptor *d_v3 = raw_desc;
+ control = UAC3_TE_INSERTION;
+ term_id = d_v3->bTerminalID;
+ bmctls = le32_to_cpu(d_v3->bmControls);
+ } else {
+ return 0; /* UAC1. No Insertion control */
}
+
+ check_input_term(state, term_id, &iterm);
+
+ /* Check for jack detection. */
+ if (uac_v2v3_control_is_readable(bmctls, control))
+ build_connector_control(state, &iterm, true);
+
return 0;
}
@@ -2554,7 +2628,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
} else { /* UAC_VERSION_3 */
switch (p1[2]) {
case UAC_INPUT_TERMINAL:
- return 0; /* NOP */
+ return parse_audio_input_terminal(state, unitid, p1);
case UAC3_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1);
case UAC3_CLOCK_SOURCE:
@@ -2932,6 +3006,12 @@ 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;
+
+ if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
+ UAC3_TE_INSERTION)) {
+ build_connector_control(&state, &state.oterm,
+ false);
+ }
}
}