From patchwork Thu Mar 22 21:39:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Chant X-Patchwork-Id: 10302369 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 810EE60386 for ; Thu, 22 Mar 2018 21:51:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6732328A6F for ; Thu, 22 Mar 2018 21:51:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6312328A6E; Thu, 22 Mar 2018 21:51:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, RCVD_IN_DNSWL_NONE, T_DKIM_INVALID autolearn=no version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EE7EE28A59 for ; Thu, 22 Mar 2018 21:51:14 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id F0A432673E1; Thu, 22 Mar 2018 22:40:46 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 3FAC0267396; Thu, 22 Mar 2018 22:40:42 +0100 (CET) Received: from mail-pl0-f66.google.com (mail-pl0-f66.google.com [209.85.160.66]) by alsa0.perex.cz (Postfix) with ESMTP id 2C16B267396 for ; Thu, 22 Mar 2018 22:40:40 +0100 (CET) Received: by mail-pl0-f66.google.com with SMTP id u11-v6so6180937plq.1 for ; Thu, 22 Mar 2018 14:40:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=3zC7YNcMHlb3zBacB9khmbqvX8fILA3vx0Ue0kwydn0=; b=XkamcJTRsLorrUq5gx3pUSKD2ya9+nJg0BGAEuN1i4NiuZPW4Y6euf3j2NnNTJCdi8 r0zUfOviwqqEG6vTaLx9G0IdsZKLAgQ8GGWovY28cRzUoKyctAzZ1wCnAuUqqDnW/3J6 VUvcPx0Eq+udOnSHD+PG13YDhJ5LW+oA7bkjLQX6uqEUK7w/Ly2NB8J23tRwNVkGltJm O/Q2KYSD+e4mQKvfZAzVJPDaFTAiZreTc5jEIbz+VUIFdFbDsVeGoPUq94AJyZOm1hy0 2eRSMi5MqMJBsAfWfmuiL7UV88jGycU9xxmMTSPvzOFPWfn7v8BKDpRSKY6WeKiV0g/v 6Ntg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=3zC7YNcMHlb3zBacB9khmbqvX8fILA3vx0Ue0kwydn0=; b=nfeOm7kzbGykUMaY5mMGYy0JcMqy6VGKQ97LLGKd7p/9sYPVRx8a7F9z7FSfmUkwbn v43NG+YScFDC6g/OvOimUkhyYAatzg/SLS4m/zPqYWFceTkhvc8g55LGev4QgekTY4L7 PFcFJa0gEYl9TVlZ6maxXRFOVeL6aqLWfkd+bWuOiHjc5dss0HOf29g87oM2YinU/F3f wRCG94MmiXuq9+r1+HCYnOVR+ojfymfqKnjPqxRsi/iuF/PX/dxv2jU7mHrhMiGu01yM FET8AbgNBK9EqXwhD1ftTqcOFNSWn0wEhw05C0FIpUsmKqup/EAfTilyxHflkka8WXX4 UsFQ== X-Gm-Message-State: AElRT7GYbacy8lxYCdT42dHY5elwVURcdzUUX/PvA2wDlFkc0La7D6Yt y3B2ELM/OHky3VHacH6JjN+JaG92GNM= X-Google-Smtp-Source: AG47ELt7iXvHLY48p93xTgItWlyC1cMD1Rr3N0LjNTxF7U7CuabJM3J32kXqwzSbIdj90zC9AxpJIQ== X-Received: by 2002:a17:902:983:: with SMTP id 3-v6mr27226517pln.278.1521754838836; Thu, 22 Mar 2018 14:40:38 -0700 (PDT) Received: from achant0.mtv.corp.google.com ([2620:0:1000:1612:b1:f71a:ab27:f6ec]) by smtp.gmail.com with ESMTPSA id 29sm16064956pfj.40.2018.03.22.14.40.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 22 Mar 2018 14:40:38 -0700 (PDT) From: Andrew Chant To: Takashi Iwai Date: Thu, 22 Mar 2018 14:39:56 -0700 Message-Id: <20180322213956.217933-3-achant@google.com> X-Mailer: git-send-email 2.17.0.rc0.231.g781580f067-goog In-Reply-To: <20180322213956.217933-1-achant@google.com> References: <20180322213956.217933-1-achant@google.com> Cc: alsa-devel@alsa-project.org, Andrew Chant , Ruslan Bilovol Subject: [alsa-devel] [PATCH 2/2] ALSA: usb-audio: UAC2 jack detection X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This implements UAC2 jack detection support, presenting jack status as a boolean read-only mono mixer. The presence of any channel in the UAC2_TE_CONNECTOR control for a terminal will result in the mixer saying the jack is connected. Mixer naming follows the convention in sound/core/ctljack.c, terminating the mixer with " Jack". For additional clues as to which jack is being presented, the name is prefixed with " - Input Jack" or " - Output Jack" depending on if it's an input or output terminal. This is required because terminal names are ambiguous between inputs and outputs and often duplicated - Bidirectional terminal types (0x400 -> 0x4FF) "... may be used separately for input only or output only. These types require two Terminal descriptors. Both have the same type." (quote from "USB Device Class Definition for Terminal Types") Since bidirectional terminal types are common for headphone adapters, this distinguishes between two otherwise identically-named jack controls. Tested with a UAC2 audio device with connector control capability. Signed-off-by: Andrew Chant --- sound/usb/mixer.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 3075ac50a391..6cb61307aefe 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1235,6 +1235,21 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, return changed; } +/* get the current value from a mixer element */ +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; + int val, err; + + err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); + if (err < 0) + return filter_error(cval, err); + val = (val != 0); + 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 */ @@ -1252,6 +1267,16 @@ static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = { .put = NULL, }; +/* A UAC connector mixer control */ +static struct snd_kcontrol_new usb_connector_ctl_ro = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .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 @@ -1464,6 +1489,55 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_add_control(&cval->head, kctl); } +static void get_connector_control_name(struct mixer_build *state, + struct usb_audio_term *term, + bool is_input, char *name, int name_size) +{ + int name_len = get_term_name(state, term, name, name_size, 0); + + if (name_len == 0) + strlcpy(name, "Unknown", name_size); + + /* + * sound/core/ctljack.c has a convention of naming jack controls + * by ending in " Jack". Make it slightly more useful by + * indicating Input or Output after the terminal name. + */ + if (is_input) + strlcat(name, " - Input Jack", name_size); + else + strlcat(name, " - Output Jack", name_size); +} + +/* Build a mixer control for a UAC connector control (jack-detect) */ +static void build_connector_control(struct mixer_build *state, + struct usb_audio_term *term, bool is_input) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *cval; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id); + cval->control = UAC2_TE_CONNECTOR; + cval->val_type = USB_MIXER_BOOLEAN; + cval->channels = term->channels; + cval->cmask = term->chconfig; + cval->min = 0; + cval->max = 1; + kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); + if (!kctl) { + usb_audio_err(state->chip, "cannot malloc kcontrol\n"); + kfree(cval); + return; + } + get_connector_control_name(state, term, is_input, kctl->id.name, + sizeof(kctl->id.name)); + kctl->private_free = snd_usb_mixer_elem_free; + snd_usb_mixer_add_control(&cval->head, kctl); +} + static int parse_clock_source_unit(struct mixer_build *state, int unitid, void *_ftr) { @@ -1770,6 +1844,23 @@ static void build_mixer_unit_ctl(struct mixer_build *state, snd_usb_mixer_add_control(&cval->head, kctl); } +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; + + check_input_term(state, d->bTerminalID, &iterm); + if (state->mixer->protocol == UAC_VERSION_2) { + /* Check for jack detection. */ + if (uac_v2v3_control_is_readable(d->bmControls, + UAC2_TE_CONNECTOR)) { + build_connector_control(state, &iterm, true); + } + } + return 0; +} + /* * parse a mixer unit */ @@ -2320,7 +2411,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { switch (p1[2]) { case UAC_INPUT_TERMINAL: - return 0; /* NOP */ + return parse_audio_input_terminal(state, unitid, p1); case UAC_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case UAC2_CLOCK_SOURCE: @@ -2463,6 +2554,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(desc->bmControls, + UAC2_TE_CONNECTOR)) { + build_connector_control(&state, &state.oterm, + false); + } } else { /* UAC_VERSION_3 */ struct uac3_output_terminal_descriptor *desc = p;