From patchwork Sat Mar 24 02:25:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Chant X-Patchwork-Id: 10305853 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 60E3660349 for ; Sat, 24 Mar 2018 02:36:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5265528D48 for ; Sat, 24 Mar 2018 02:36:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4735D28F63; Sat, 24 Mar 2018 02:36:19 +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 7FE6828DD1 for ; Sat, 24 Mar 2018 02:36:18 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 8B5B1266EBE; Sat, 24 Mar 2018 03:26:01 +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 178D8266B67; Sat, 24 Mar 2018 03:25:57 +0100 (CET) Received: from mail-pf0-f195.google.com (mail-pf0-f195.google.com [209.85.192.195]) by alsa0.perex.cz (Postfix) with ESMTP id 427CB266B67 for ; Sat, 24 Mar 2018 03:25:54 +0100 (CET) Received: by mail-pf0-f195.google.com with SMTP id h69so152126pfe.13 for ; Fri, 23 Mar 2018 19:25:53 -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=FbtTdYIxxAc+2kOZV+EqpXCUaxI/5HY99mQLmQynaxk=; b=pXzl/Kf96wbXz0JMZI1Z2tvBUXDwEliu7HBIYDox14BTQF+mYWGkA7cOJkCCZSVXRz tSl/36UpKnlwvw3v4nmyrlbx7S6LnjzSXCA22yhI5HRuzfZ5oMSni01iIdERi2wYWfm6 qJuA9YdcTMVFRe3Y4HtU0B2s1VaJPB7+/qfmdGZL4C3L4QwWVSn/8gX3UdXotoAzuRKv mAWQUiE9t1ppVyCyslgjvGDSPJI/O1I3Z8pmJYd7xUm84qF1kzFPTeVQROhP5wDXkWWV b4+LCsyMWAUpIpWiSGxKAty1AqrFptLAwosprJL8TsxGXSyYw0wEElTNU7MRSaRFkTrB l7cA== 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=FbtTdYIxxAc+2kOZV+EqpXCUaxI/5HY99mQLmQynaxk=; b=PAVzUhD98U6hDKGpQVoztLNCXLGsRZW+ERn2woZEM8l0vEEx/NrGDC8wp6vKf5gxp5 NfP7aOXyLQofk4SUosue17/+uyBiJM5jjexn0JxZIOOAoTpcfDkqYPTSGnN/S1xhQOTl fKlqO+qImQigPGh3FGGWZK/TA/0JTvlqGbIYbH1nO5EPAiN180xnH0UqAqTnIfVWMQfP +LvU+sPLXDxDe6M4pm8YycAUAFBKfXmt/ZKhrACwMBt4CFqgRI7/1xeregxg3T2Ku/Ke 7PwrXGYGAe3Ol+kT0wM412MeVwcgH+mJSAg0bzZPhu6rgrpL4hwkFRA8Xg7JNFJitOR+ 7NyQ== X-Gm-Message-State: AElRT7Fk6gJ3EKysfNGbtYJPYj+9owdtlrGE1MZLWWJUin1f7z0XRdqE 5ckJBNbeBj3lOSTIAgZF1jCQpQ== X-Google-Smtp-Source: AG47ELsukFruPms3Q3Jl636XadU26qlnzPV25dAYH7CjAGL5KCDH24PeHL7XoVxxztdj2Mm+g0/3og== X-Received: by 10.99.114.86 with SMTP id c22mr22245169pgn.72.1521858352571; Fri, 23 Mar 2018 19:25:52 -0700 (PDT) Received: from achant0.mtv.corp.google.com ([2620:0:1000:1612:b1:f71a:ab27:f6ec]) by smtp.gmail.com with ESMTPSA id f6sm9624363pgo.54.2018.03.23.19.25.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 23 Mar 2018 19:25:51 -0700 (PDT) From: Andrew Chant To: Takashi Iwai Date: Fri, 23 Mar 2018 19:25:23 -0700 Message-Id: <20180324022524.227748-2-achant@google.com> X-Mailer: git-send-email 2.17.0.rc0.231.g781580f067-goog In-Reply-To: <20180324022524.227748-1-achant@google.com> References: <20180324022524.227748-1-achant@google.com> Cc: alsa-devel@alsa-project.org, Andrew Chant , Ruslan Bilovol , Benson Leung Subject: [alsa-devel] [PATCH v3 1/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 | 98 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 3075ac50a391..97ed2155205d 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_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 @@ -1464,6 +1489,54 @@ 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 = 1; /* report true if any channel is connected */ + 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 +1843,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 +2410,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 +2553,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;