From patchwork Tue Dec 26 18:06:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504927 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18B1F4FF64 for ; Tue, 26 Dec 2023 18:06:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="v6tRrik9" Received: by m.b4.vu (Postfix, from userid 1000) id F376C604B910; Wed, 27 Dec 2023 04:36:00 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu F376C604B910 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703613961; bh=YFhRmMuCfu+4qMgL34kvxnxeON91cCmUlE73zbH2pJ4=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=v6tRrik9igi6yrbY196GKE3z1cWGu3K3jRgD1JJGW7FL2mYWmTl4W85PhSIP5b2V/ W10WEvwD6EeoCCaEnAgCk0c1/D6G23wQuHl5fnS+qkAdmEBmMxBHTzmv1gbQ4xuOew In2dKHDG4Z8kreTJOOvEyjmY3Nxe0yL2jHf+p9zV2hMxQJ430JSh8UwcL95yq41lB8 kaI0PwehOW7+eXUnevp3DWk2Wt9PTS3DeIw749tftjjbhvRt7KLeVx+9tWIAl7zuIX ExiC0Vd66OCJSYbrTxBm/nJqo1GSxkG4kL6ZpwkwgFm1qRQBo1wwYZQwzby4L+d0v6 V6Cf1SvCN6I7Q== Date: Wed, 27 Dec 2023 04:36:00 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 01/20] ALSA: scarlett2: Convert meter levels from little-endian Message-ID: <754919540c274b6f7e15b3e26b6f7136bccce4a4.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add missing conversion from little-endian data to CPU-endian in scarlett2_usb_get_meter_levels(). Fixes: 3473185f31df ("ALSA: scarlett2: Remap Level Meter values") Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index e25f004e50e4..e5d8543f9686 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2110,7 +2110,7 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, __le16 num_meters; __le32 magic; } __packed req; - u32 resp[SCARLETT2_MAX_METERS]; + __le32 resp[SCARLETT2_MAX_METERS]; int i, err; req.pad = 0; @@ -2123,7 +2123,7 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, /* copy, convert to u16 */ for (i = 0; i < num_meters; i++) - levels[i] = resp[i]; + levels[i] = le32_to_cpu(resp[i]); return 0; } From patchwork Tue Dec 26 18:06:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504928 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DB7D34FF64 for ; Tue, 26 Dec 2023 18:06:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="V7tJabgZ" Received: by m.b4.vu (Postfix, from userid 1000) id 00CCC604B91E; Wed, 27 Dec 2023 04:36:13 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 00CCC604B91E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703613974; bh=Kn0IOVuTt5Mu54Isu5It4H+0HpxpwDHS0VehW1SSnAg=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=V7tJabgZyXTAA8tvMZLHn+uUz61HwAgvJaI6eTOWGeVDfkTVy0jgBgcbTgFwHmgJK ZPvGUIrRXYrBCX4cQ5SMoFH9+rmxdccuvGcUuXvhXojD3J7FVrMm50CgoyXYxAZDS1 y2OWhCcfJS+w/zeyzKaACQ9Xu2jXkZ778SMl23QfpqAk8NbY5B6pQJIu18NM/c+uZN qRiZGPzZ7YmpGgHusrl6ay5bNBp2Zx6av2XSx2+qsgUPP10Z4NgZQHRXsRoc3jjyFd dsaYTYKrhHpJhoTtLx8sSquOdovFM7dBi1ogMSRJLMMQLhGdQt8OcK7Hye6h+ZIsWS NvC48kxb/N+9g== Date: Wed, 27 Dec 2023 04:36:13 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 02/20] ALSA: scarlett2: Remove repeated elem->head.mixer references Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Use a local variable *mixer rather than repeating elem->header.mixer in scarlett2_direct_monitor_ctl_get() and scarlett2_meter_ctl_get(). Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index e5d8543f9686..10260666811f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -3927,7 +3927,7 @@ static int scarlett2_direct_monitor_ctl_get( { struct usb_mixer_elem_info *elem = kctl->private_data; struct usb_mixer_interface *mixer = elem->head.mixer; - struct scarlett2_data *private = elem->head.mixer->private_data; + struct scarlett2_data *private = mixer->private_data; int err = 0; mutex_lock(&private->data_mutex); @@ -4191,7 +4191,8 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; - struct scarlett2_data *private = elem->head.mixer->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; u8 *meter_level_map = private->meter_level_map; u16 meter_levels[SCARLETT2_MAX_METERS]; int i, err; @@ -4203,7 +4204,7 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl, goto unlock; } - err = scarlett2_usb_get_meter_levels(elem->head.mixer, elem->channels, + err = scarlett2_usb_get_meter_levels(mixer, elem->channels, meter_levels); if (err < 0) goto unlock; From patchwork Tue Dec 26 18:06:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504929 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 697BF50243 for ; Tue, 26 Dec 2023 18:06:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="Q5FlQIZ8" Received: by m.b4.vu (Postfix, from userid 1000) id 975E1604B6C8; Wed, 27 Dec 2023 04:36:21 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 975E1604B6C8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703613981; bh=lN5FvSLhixdXj8WejUQ2Nd9w02A6tBSzlHk0cBitcqE=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Q5FlQIZ8tFNkfz0bdHjSeaVONzvWA5tItf+/SjEGbUrl/bcfQWGcFIS0xTcoKmuez jrrvMk+ot0yL11jpRFf8sLeIHWSAoHgnGK3F0M5Lp8tP+p21CQqHcRP6fjJAC29DmH J9VNvU0oLxvuq8d6fa4qkKg15DePVbabvWOnfrDefVPdyND2c7ChG3pGW5PuTYK+ma 8wz9Pg0EGINEkRpEWFXznZ96/EI3JCUpWp68LMb5KHytJ1FNfj0pBp0R/zF/eHHGKf cAhhZVu/oYUFE658cfEnGa9bev9pa+tOkViomvO3GCJ9yQGleJwOvzIxTnp7mLM+iQ FXjVOWFhZC9sQ== Date: Wed, 27 Dec 2023 04:36:21 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 03/20] ALSA: scarlett2: Add support for air/phantom control on input 2 Message-ID: <19511f18895b8c094985a4a5691fbc1dc028c108.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The Focusrite Scarlett Gen 4 Solo has Air and Phantom Power controls on analogue input #2 (the Gen 3 Solo had these controls on analogue input #1). Add air_input_first and phantom_first device info options to cater for this. These options are similar to the level_input_first option that was added for the Gen 3 Solo, but these new options do not require adjusting the index of the control. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 10260666811f..5c5198bb224a 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -632,9 +632,15 @@ struct scarlett2_device_info { */ u8 air_input_count; + /* the first input with an air control (0-based) */ + u8 air_input_first; + /* the number of phantom (48V) software switchable controls */ u8 phantom_count; + /* the first input with phantom power control (0-based) */ + u8 phantom_first; + /* the number of inputs each phantom switch controls */ u8 inputs_per_phantom; @@ -3043,6 +3049,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, struct usb_mixer_elem_info *elem = kctl->private_data; struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; int index = elem->control; int oval, val, err = 0; @@ -3064,7 +3071,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, /* Send switch change to the device */ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, - index, val); + index + info->phantom_first, val); if (err == 0) err = 1; @@ -3763,7 +3770,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) /* Add input air controls */ for (i = 0; i < info->air_input_count; i++) { - snprintf(s, sizeof(s), fmt, i + 1, "Air", "Switch"); + snprintf(s, sizeof(s), fmt, i + 1 + info->air_input_first, + "Air", "Switch"); err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl, i, 1, s, &private->air_ctls[i]); if (err < 0) @@ -3773,7 +3781,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) /* Add input phantom controls */ if (info->inputs_per_phantom == 1) { for (i = 0; i < info->phantom_count; i++) { - scnprintf(s, sizeof(s), fmt, i + 1, + scnprintf(s, sizeof(s), fmt, + i + 1 + info->phantom_first, "Phantom Power", "Switch"); err = scarlett2_add_new_ctl( mixer, &scarlett2_phantom_ctl, From patchwork Tue Dec 26 18:06:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504930 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 24DA850243 for ; Tue, 26 Dec 2023 18:06:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="JaTOMP/V" Received: by m.b4.vu (Postfix, from userid 1000) id 60A9E604B6AC; Wed, 27 Dec 2023 04:36:29 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 60A9E604B6AC DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703613989; bh=2n4PkT4R2tqEkDzTK9RSQIELqe+5LZML+5kl5+FYu/8=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=JaTOMP/VRurhmjAyd3N8ZOeRr9Jkm+U4YLDSaOGsrqKTK9DcW/grcHKKnzIX7kwE7 C7ME1d9XBgyCxXsSzH3GybdhPDn1InaMI8QJ2rvNYdKGfBmPcyDH1NHBxdkUfrIx5J pSitor7KxqFpWNCmJJNpxBc0CxZjrzh7EgoDcDCLvKjM7lH0wb9dfDnnq3atpUcIlT mzDRPW5ugVIg08pJbRtRPNaatJAOus5D+M8leZ9CGqJaNQ8Ip+GQhSjLo6l7+UnmL5 NxESBNhcp2n6smJ7MyLwIkSQbnFGYJyLdk72FCH7YIuepfrM0sJ7ndpzXHOKq1TTY0 PS58QAUcR1Kjw== Date: Wed, 27 Dec 2023 04:36:29 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 04/20] ALSA: scarlett2: Add support for Gen 4 style parameters Message-ID: <1624e6d8a0c629c3bdfe53825b16e8b589724fc4.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Writing Scarlett Gen 4 parameters differs from Gen 2 and Gen 3: - the values are written into a shared write location - the values are only byte-sized - the read locations now extend beyond 0xFF - a separate NVRAM save step is no longer required This patch implements that alternate write style, triggered by setting the config item size field to zero. The write address is specified through a new config set field gen4_write_addr. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 53 +++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 5c5198bb224a..09cd09799588 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -320,16 +320,21 @@ enum { }; /* Location, size, and activation command number for the configuration - * parameters. Size is in bits and may be 1, 8, or 16. + * parameters. Size is in bits and may be 0, 1, 8, or 16. + * + * A size of 0 indicates that the parameter is a byte-sized Scarlett + * Gen 4 configuration which is written through the gen4_write_addr + * location (but still read through the given offset location). */ struct scarlett2_config { - u8 offset; + u16 offset; u8 size; u8 activate; }; struct scarlett2_config_set { const struct scarlett2_notification *notifications; + u16 gen4_write_addr; const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT]; }; @@ -1612,9 +1617,12 @@ static int scarlett2_usb_get_config( if (!config_item->offset) return -EFAULT; + /* Gen 4 style parameters are always 1 byte */ + size = config_item->size ? config_item->size : 8; + /* For byte-sized parameters, retrieve directly into buf */ - if (config_item->size >= 8) { - size = config_item->size / 8 * count; + if (size >= 8) { + size = size / 8 * count; err = scarlett2_usb_get(mixer, config_item->offset, buf, size); if (err < 0) return err; @@ -1684,8 +1692,9 @@ static int scarlett2_usb_set_config( int config_item_num, int index, int value) { struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_config_set *config_set = private->config_set; const struct scarlett2_config *config_item = - &private->config_set->items[config_item_num]; + &config_set->items[config_item_num]; int offset, size; int err; @@ -1695,6 +1704,36 @@ static int scarlett2_usb_set_config( if (!config_item->offset) return -EFAULT; + /* Gen 4 style writes are selected with size = 0; + * these are only byte-sized values written through a shared + * location, different to the read address + */ + if (!config_item->size) { + if (!config_set->gen4_write_addr) + return -EFAULT; + + /* Place index in gen4_write_addr + 1 */ + err = scarlett2_usb_set_data( + mixer, config_set->gen4_write_addr + 1, 1, index); + if (err < 0) + return err; + + /* Place value in gen4_write_addr */ + err = scarlett2_usb_set_data( + mixer, config_set->gen4_write_addr, 1, value); + if (err < 0) + return err; + + /* Request the interface do the write */ + return scarlett2_usb_activate_config( + mixer, config_item->activate); + } + + /* Not-Gen 4 style needs NVRAM save, supports + * bit-modification, and writing is done to the same place + * that the value can be read from + */ + /* Cancel any pending NVRAM save */ cancel_delayed_work_sync(&private->work); @@ -1736,6 +1775,10 @@ static int scarlett2_usb_set_config( if (err < 0) return err; + /* Gen 2 style writes to Gen 4 devices don't need saving */ + if (config_set->gen4_write_addr) + return 0; + /* Schedule the change to be written to NVRAM */ if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE) schedule_delayed_work(&private->work, msecs_to_jiffies(2000)); From patchwork Tue Dec 26 18:06:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504931 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 208A850243 for ; Tue, 26 Dec 2023 18:06:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="hEXpWMmP" Received: by m.b4.vu (Postfix, from userid 1000) id 89D44604B5C9; Wed, 27 Dec 2023 04:36:38 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 89D44604B5C9 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703613998; bh=xrL/UYbYgK8uJYOBPgoRIyT81pQgXYqY2qxjvjiSoKk=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=hEXpWMmPvA1OWaNrPGF2LtwtAZxiG6KD7LJyZnVxFUFOcd33KhEUkvZ9t2iySh5he G1BmIpkx81KnPCmpX4mvfvJTpyybOt4Fk25gJA5N3yy5FQMspkn0EYPhdYYDVIvqA/ 8pQbdzPsAcX9I9aXNUmobbBh8lZZ4SfahiuHew383h6/nm6NpatHWB5g9GZWoM9mGL J1JDw4Fvf78vg7S4M7I3ykFTDU27P5plQujCHZOgoMVzDLgfrr7gMSEMlSmq8a/hgH 1C0jHcMoZohCwsEU7rr14xuekfRt5paMI2wM7ljT+02if9zbO01Yf2U3FzzWA2F0je l2VbbzmtJfPSA== Date: Wed, 27 Dec 2023 04:36:38 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 05/20] ALSA: scarlett2: Allow for controls with a "mute mode" Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Gen 2/3 interfaces would only use 0/1 values for input level and phantom power switch controls. Gen 4 interfaces use the second bit to indicate that the state should be changed (or is changing), and the input is to be muted (or is muted) while that happens. Add a "mute" flag to config items to enable this behaviour for the level/phantom controls. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 09cd09799588..3098d9d38e3f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -325,11 +325,18 @@ enum { * A size of 0 indicates that the parameter is a byte-sized Scarlett * Gen 4 configuration which is written through the gen4_write_addr * location (but still read through the given offset location). + * + * Some Gen 4 configuration parameters are written with 0x02 for a + * desired value of 0x01, and 0x03 for 0x00. These are indicated with + * mute set to 1. 0x02 and 0x03 are temporary values while the device + * makes the change and the channel and/or corresponding DSP channel + * output is muted. */ struct scarlett2_config { u16 offset; u8 size; u8 activate; + u8 mute; }; struct scarlett2_config_set { @@ -2177,6 +2184,15 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, return 0; } +/* For config items with mute=1, xor bits 0 & 1 together to get the + * current/next state. This won't have any effect on values which are + * only ever 0/1. + */ +static uint8_t scarlett2_decode_muteable(uint8_t v) +{ + return (v ^ (v >> 1)) & 1; +} + /*** Control Functions ***/ /* helper function to create a new control */ @@ -2798,7 +2814,8 @@ static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl, if (err < 0) goto unlock; } - ucontrol->value.enumerated.item[0] = private->level_switch[index]; + ucontrol->value.enumerated.item[0] = scarlett2_decode_muteable( + private->level_switch[index]); unlock: mutex_unlock(&private->data_mutex); @@ -2831,6 +2848,10 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl, private->level_switch[index] = val; + /* To set the Gen 4 muteable controls, bit 1 gets set instead */ + if (private->config_set->items[SCARLETT2_CONFIG_LEVEL_SWITCH].mute) + val = (!val) | 0x02; + /* Send switch change to the device */ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, index, val); @@ -3078,8 +3099,8 @@ static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl, if (err < 0) goto unlock; } - ucontrol->value.integer.value[0] = - private->phantom_switch[elem->control]; + ucontrol->value.integer.value[0] = scarlett2_decode_muteable( + private->phantom_switch[elem->control]); unlock: mutex_unlock(&private->data_mutex); @@ -3112,6 +3133,10 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, private->phantom_switch[index] = val; + /* To set the Gen 4 muteable controls, bit 1 gets set */ + if (private->config_set->items[SCARLETT2_CONFIG_PHANTOM_SWITCH].mute) + val = (!val) | 0x02; + /* Send switch change to the device */ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, index + info->phantom_first, val); From patchwork Tue Dec 26 18:06:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504932 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 74D195024D for ; Tue, 26 Dec 2023 18:06:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="F0XPc0LW" Received: by m.b4.vu (Postfix, from userid 1000) id 7B2B2604B5B2; Wed, 27 Dec 2023 04:36:54 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 7B2B2604B5B2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614014; bh=R2DkRGirTwjcIg9rwRCxACsDe9EmGVSkoUJ2iFhhNhY=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=F0XPc0LW7yi/dqHKaPBCfIp8vno4+wBllw/nhl6zjWY8yrcEgM6N0tL1U4FdRxCCz bM4CGLfcXegDq/09S+umNatwZB0JrA6p63uW/S4/1X++wGVQlA5OypintJROw3+Ijn y6I2+W7lqFARdr6zRS0Y9Wu3kd8g9lBIXH/TE86ucmRYNZWkdDKVm2TBq2jIwExnhb 53VAY/e5ATT4ymhMPj2ynHcJk0hOGLFVLkJ92ekBiLjxWb6tFLqbSs5ET1bzumN8Ql afou5qgGiPHfsn1sa+jsIPEWYePjWl6F9Asa2eQuHs0dwqBn9qEFxDTKiX44V/FQRP 84Ww3tOWtbGIA== Date: Wed, 27 Dec 2023 04:36:54 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 06/20] ALSA: scarlett2: Add support for Air Presence + Drive option Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Extend the existing "air" option support from Scarlett Gen 3, which had two states (off/on), to accommodate Scarlett Gen 4's new state: Presence + Drive. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 46 +++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 3098d9d38e3f..416973b8a6c4 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -647,6 +647,12 @@ struct scarlett2_device_info { /* the first input with an air control (0-based) */ u8 air_input_first; + /* number of additional air options + * 0 for air presence only (Gen 3) + * 1 for air presence+drive (Gen 4) + */ + u8 air_option; + /* the number of phantom (48V) software switchable controls */ u8 phantom_count; @@ -3022,7 +3028,7 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl, } oval = private->air_switch[index]; - val = !!ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; if (oval == val) goto unlock; @@ -3040,12 +3046,31 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl, return err; } -static const struct snd_kcontrol_new scarlett2_air_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "", - .info = snd_ctl_boolean_mono_info, - .get = scarlett2_air_ctl_get, - .put = scarlett2_air_ctl_put, +static int scarlett2_air_with_drive_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[3] = { + "Off", "Presence", "Presence + Drive" + }; + + return snd_ctl_enum_info(uinfo, 1, 3, values); +} + +static const struct snd_kcontrol_new scarlett2_air_ctl[2] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_air_ctl_get, + .put = scarlett2_air_ctl_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_air_with_drive_ctl_info, + .get = scarlett2_air_ctl_get, + .put = scarlett2_air_ctl_put, + } }; /*** Phantom Switch Controls ***/ @@ -3839,9 +3864,10 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) /* Add input air controls */ for (i = 0; i < info->air_input_count; i++) { snprintf(s, sizeof(s), fmt, i + 1 + info->air_input_first, - "Air", "Switch"); - err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl, - i, 1, s, &private->air_ctls[i]); + "Air", info->air_option ? "Enum" : "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_air_ctl[info->air_option], + i, 1, s, &private->air_ctls[i]); if (err < 0) return err; } From patchwork Tue Dec 26 18:07:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504933 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D77925024C for ; Tue, 26 Dec 2023 18:07:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="ZHoX5WZJ" Received: by m.b4.vu (Postfix, from userid 1000) id 50DC5604B5B4; Wed, 27 Dec 2023 04:37:12 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 50DC5604B5B4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614032; bh=tgl8p/A9CoPnMovJ/OCePItblwB8Rm6G9qT5nai1CLo=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=ZHoX5WZJ2+h4qVQKxB7hlGhl90mtRf8yN/vQP8iwFGKoGPnVjlcd2IXu3mm82Mavn 4TBJRR8nXmD0f5QbDAiiG4LlUQsZIRfTGP0bUJVJqnteQdrDC4XuOzLCbD9mk4zWEw oMHOOoJUJTV1whkwrlmbJF9t6zb+0U71FhA16etR8SKVQkCwMt7/tKiCYomZagtmtW VNioUi/xoLQV3unVUd3TIJhrp2an96JvtpUkzhvpc3RWVVxhG+cu/ur0EH9FW9QZ7M UEmlTwVJBnG+Gmr1Py//N5NChccHfLWoGQu1mQYDwcDRfMG43a8s19PRVBrtAnv2PK xSjMJrXzpGx4g== Date: Wed, 27 Dec 2023 04:37:12 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 07/20] ALSA: scarlett2: Add support for software-controllable input gain Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Some devices in the Scarlett Gen 4 series have support for software-controllable input gain. Along with this comes a channel select option, an auto-gain feature, "safe" mode, and linking two channels into a stereo pair. Mark the new scarlett2_notify_input_*() functions with __always_unused until they get used when the Gen 4 notification callback function arrays are added. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 796 +++++++++++++++++++++++++++++++++++- 1 file changed, 795 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 416973b8a6c4..a0212bda2b1a 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -164,6 +164,7 @@ /* some gui mixers can't handle negative ctl values */ #define SCARLETT2_VOLUME_BIAS 127 +#define SCARLETT2_GAIN_BIAS 70 /* mixer range from -80dB to +6dB in 0.5dB steps */ #define SCARLETT2_MIXER_MIN_DB -80 @@ -196,11 +197,12 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { /* Maximum number of analogue outputs */ #define SCARLETT2_ANALOGUE_MAX 10 -/* Maximum number of level and pad switches */ +/* Maximum number of various input controls */ #define SCARLETT2_LEVEL_SWITCH_MAX 2 #define SCARLETT2_PAD_SWITCH_MAX 8 #define SCARLETT2_AIR_SWITCH_MAX 8 #define SCARLETT2_PHANTOM_SWITCH_MAX 2 +#define SCARLETT2_INPUT_GAIN_MAX 2 /* Maximum number of inputs to the mixer */ #define SCARLETT2_INPUT_MIX_MAX 25 @@ -266,6 +268,16 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { "Mute Playback Switch", "Dim Playback Switch" }; +/* Autogain Status Values */ +enum { + SCARLETT2_AUTOGAIN_STATUS_STOPPED, + SCARLETT2_AUTOGAIN_STATUS_RUNNING, + SCARLETT2_AUTOGAIN_STATUS_FAILED, + SCARLETT2_AUTOGAIN_STATUS_CANCELLED, + SCARLETT2_AUTOGAIN_STATUS_UNKNOWN, + SCARLETT2_AUTOGAIN_STATUS_COUNT +}; + /* Notification callback functions */ struct scarlett2_notification { u32 mask; @@ -316,6 +328,12 @@ enum { SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, SCARLETT2_CONFIG_TALKBACK_MAP, + SCARLETT2_CONFIG_AUTOGAIN_SWITCH, + SCARLETT2_CONFIG_AUTOGAIN_STATUS, + SCARLETT2_CONFIG_INPUT_GAIN, + SCARLETT2_CONFIG_SAFE_SWITCH, + SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, + SCARLETT2_CONFIG_INPUT_LINK_SWITCH, SCARLETT2_CONFIG_COUNT }; @@ -662,6 +680,9 @@ struct scarlett2_device_info { /* the number of inputs each phantom switch controls */ u8 inputs_per_phantom; + /* the number of inputs with software-controllable gain */ + u8 gain_input_count; + /* the number of direct monitor options * (0 = none, 1 = mono only, 2 = mono/stereo) */ @@ -722,6 +743,10 @@ struct scarlett2_data { u8 input_pad_updated; u8 input_air_updated; u8 input_phantom_updated; + u8 input_select_updated; + u8 input_gain_updated; + u8 autogain_updated; + u8 input_safe_updated; u8 monitor_other_updated; u8 direct_monitor_updated; u8 mux_updated; @@ -737,6 +762,12 @@ struct scarlett2_data { u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; u8 phantom_persistence; + u8 input_select_switch; + u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX / 2]; + u8 gain[SCARLETT2_INPUT_GAIN_MAX]; + u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX]; + u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX]; + u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX]; u8 direct_monitor_switch; u8 speaker_switching_switch; u8 talkback_switch; @@ -754,6 +785,12 @@ struct scarlett2_data { struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX]; struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; + struct snd_kcontrol *input_select_ctl; + struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2]; + struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX]; + struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX]; + struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX]; + struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; struct snd_kcontrol *direct_monitor_ctl; struct snd_kcontrol *speaker_switching_ctl; @@ -2352,6 +2389,610 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) 0, 1, "Sync Status", &private->sync_ctl); } +/*** Autogain Switch and Status Controls ***/ + +static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int err, i; + u8 raw_autogain_status[SCARLETT2_INPUT_GAIN_MAX]; + + private->autogain_updated = 0; + + if (!info->gain_input_count) + return 0; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH, + info->gain_input_count, private->autogain_switch); + if (err < 0) + return err; + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_AUTOGAIN_STATUS, + info->gain_input_count, raw_autogain_status); + if (err < 0) + return err; + + /* Translate autogain_switch and raw_autogain_status into + * autogain_status + */ + for (i = 0; i < info->gain_input_count; i++) + if (private->autogain_switch[i]) + private->autogain_status[i] = + SCARLETT2_AUTOGAIN_STATUS_RUNNING; + else if (raw_autogain_status[i] == 0) + private->autogain_status[i] = + SCARLETT2_AUTOGAIN_STATUS_STOPPED; + else if (raw_autogain_status[i] >= 2 && + raw_autogain_status[i] <= 5) + private->autogain_status[i] = + SCARLETT2_AUTOGAIN_STATUS_FAILED; + else if (raw_autogain_status[i] == 6) + private->autogain_status[i] = + SCARLETT2_AUTOGAIN_STATUS_CANCELLED; + else + private->autogain_status[i] = + SCARLETT2_AUTOGAIN_STATUS_UNKNOWN; + + return 0; +} + +static int scarlett2_autogain_switch_ctl_get( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->autogain_updated) { + err = scarlett2_update_autogain(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.enumerated.item[0] = + private->autogain_switch[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_autogain_status_ctl_get( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->autogain_updated) { + err = scarlett2_update_autogain(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.enumerated.item[0] = + private->autogain_status[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_autogain_switch_ctl_put( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->autogain_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->autogain_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH, index, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_autogain_status_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[SCARLETT2_AUTOGAIN_STATUS_COUNT] = { + "Stopped", "Running", "Failed", "Cancelled", "Unknown" + }; + + return snd_ctl_enum_info( + uinfo, 1, SCARLETT2_AUTOGAIN_STATUS_COUNT, values); +} + +static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_autogain_switch_ctl_get, + .put = scarlett2_autogain_switch_ctl_put +}; + +static const struct snd_kcontrol_new scarlett2_autogain_status_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "", + .info = scarlett2_autogain_status_ctl_info, + .get = scarlett2_autogain_status_ctl_get, +}; + +/*** Input Select Control ***/ + +static int scarlett2_update_input_select(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int link_count = info->gain_input_count / 2; + int err; + + private->input_select_updated = 0; + + if (!link_count) + return 0; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, + 1, &private->input_select_switch); + if (err < 0) + return err; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, + link_count, private->input_link_switch); + if (err < 0) + return err; + + /* simplified because no model yet has link_count > 1 */ + if (private->input_link_switch[0]) + private->input_select_switch = 0; + + return 0; +} + +static int scarlett2_input_select_ctl_get( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->input_select_updated) { + err = scarlett2_update_input_select(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.enumerated.item[0] = private->input_select_switch; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_input_select_ctl_put( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int oval, val, err = 0; + int max_val = private->input_link_switch[0] ? 0 : 1; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->input_select_switch; + val = ucontrol->value.integer.value[0]; + + if (val < 0) + val = 0; + else if (val > max_val) + val = max_val; + + if (oval == val) + goto unlock; + + private->input_select_switch = val; + + /* Send switch change to the device if inputs not linked */ + if (!private->input_link_switch[0]) + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, + 1, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_input_select_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int inputs = private->info->gain_input_count; + int i, j; + int err; + char **values = kcalloc(inputs, sizeof(char *), GFP_KERNEL); + + if (!values) + return -ENOMEM; + + mutex_lock(&private->data_mutex); + + /* Loop through each input + * Linked inputs have one value for the pair + */ + for (i = 0, j = 0; i < inputs; i++) { + if (private->input_link_switch[i / 2]) { + values[j++] = kasprintf( + GFP_KERNEL, "Input %d-%d", i + 1, i + 2); + i++; + } else { + values[j++] = kasprintf( + GFP_KERNEL, "Input %d", i + 1); + } + } + + err = snd_ctl_enum_info(uinfo, 1, j, + (const char * const *)values); + + mutex_unlock(&private->data_mutex); + + for (i = 0; i < inputs; i++) + kfree(values[i]); + kfree(values); + + return err; +} + +static const struct snd_kcontrol_new scarlett2_input_select_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_input_select_ctl_info, + .get = scarlett2_input_select_ctl_get, + .put = scarlett2_input_select_ctl_put, +}; + +/*** Input Link Switch Controls ***/ + +static int scarlett2_input_link_ctl_get( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->input_select_updated) { + err = scarlett2_update_input_select(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.enumerated.item[0] = + private->input_link_switch[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_input_link_ctl_put( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->input_link_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->input_link_switch[index] = val; + + /* Notify of change in input select options available */ + snd_ctl_notify(mixer->chip->card, + SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, + &private->input_select_ctl->id); + private->input_select_updated = 1; + + /* Send switch change to the device + * Link for channels 1-2 is at index 1 + * No device yet has more than 2 channels linked + */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index + 1, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_input_link_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_input_link_ctl_get, + .put = scarlett2_input_link_ctl_put +}; + +/*** Input Gain Controls ***/ + +static int scarlett2_update_input_gain(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + private->input_gain_updated = 0; + + if (!info->gain_input_count) + return 0; + + return scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_INPUT_GAIN, + info->gain_input_count, private->gain); +} + +static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SCARLETT2_GAIN_BIAS; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett2_input_gain_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->input_gain_updated) { + err = scarlett2_update_input_gain(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = + private->gain[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->gain[index]; + val = ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->gain[index] = val; + + /* Send gain change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_INPUT_GAIN, + index, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const DECLARE_TLV_DB_MINMAX( + db_scale_scarlett2_gain, -SCARLETT2_GAIN_BIAS * 100, 0 +); + +static const struct snd_kcontrol_new scarlett2_input_gain_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_input_gain_ctl_info, + .get = scarlett2_input_gain_ctl_get, + .put = scarlett2_input_gain_ctl_put, + .private_value = 0, /* max value */ + .tlv = { .p = db_scale_scarlett2_gain } +}; + +/*** Safe Controls ***/ + +static int scarlett2_update_input_safe(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + private->input_safe_updated = 0; + + if (!info->gain_input_count) + return 0; + + return scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_SAFE_SWITCH, + info->gain_input_count, private->safe_switch); +} + +static int scarlett2_safe_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->input_safe_updated) { + err = scarlett2_update_input_safe(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = + private->safe_switch[elem->control]; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int index = elem->control; + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->safe_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->safe_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SAFE_SWITCH, + index, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_safe_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_safe_ctl_get, + .put = scarlett2_safe_ctl_put, +}; + /*** Analogue Line Out Volume Controls ***/ /* Update hardware volume controls after receiving notification that @@ -3908,6 +4549,60 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) return err; } + /* Add software-controllable input gain controls */ + if (info->gain_input_count) { + err = scarlett2_add_new_ctl( + mixer, &scarlett2_input_select_ctl, 0, 1, + "Input Select Capture Enum", + &private->input_select_ctl); + if (err < 0) + return err; + + for (i = 0; i < info->gain_input_count; i++) { + if (i % 2) { + snprintf(s, sizeof(s), + "Line In %d-%d Link Capture Switch", + i, i + 1); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_input_link_ctl, + i / 2, 1, s, + &private->input_link_ctls[i / 2]); + if (err < 0) + return err; + } + + snprintf(s, sizeof(s), fmt, i + 1, + "Gain", "Volume"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_input_gain_ctl, + i, 1, s, &private->input_gain_ctls[i]); + if (err < 0) + return err; + + snprintf(s, sizeof(s), fmt, i + 1, + "Autogain", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_switch_ctl, + i, 1, s, &private->autogain_ctls[i]); + if (err < 0) + return err; + + snprintf(s, sizeof(s), fmt, i + 1, + "Autogain Status", "Enum"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_autogain_status_ctl, + i, 1, s, &private->autogain_status_ctls[i]); + + snprintf(s, sizeof(s), fmt, i + 1, + "Safe", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_safe_ctl, + i, 1, s, &private->safe_ctls[i]); + if (err < 0) + return err; + } + } + return 0; } @@ -4838,6 +5533,22 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; + err = scarlett2_update_input_select(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_gain(mixer); + if (err < 0) + return err; + + err = scarlett2_update_autogain(mixer); + if (err < 0) + return err; + + err = scarlett2_update_input_safe(mixer); + if (err < 0) + return err; + for (i = 0; i < private->num_mix_out; i++) { err = scarlett2_usb_get_mix(mixer, i); if (err < 0) @@ -4970,6 +5681,89 @@ static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer) scarlett2_notify_input_phantom(mixer); } +/* Notify on input select change */ +static __always_unused void scarlett2_notify_input_select( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + if (!info->gain_input_count) + return; + + private->input_select_updated = 1; + + snd_ctl_notify(card, + SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, + &private->input_select_ctl->id); + + for (i = 0; i < info->gain_input_count / 2; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->input_link_ctls[i]->id); +} + +/* Notify on input gain change */ +static __always_unused void scarlett2_notify_input_gain( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + if (!info->gain_input_count) + return; + + private->input_gain_updated = 1; + + for (i = 0; i < info->gain_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->input_gain_ctls[i]->id); +} + +/* Notify on autogain change */ +static __always_unused void scarlett2_notify_autogain( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + if (!info->gain_input_count) + return; + + private->autogain_updated = 1; + + for (i = 0; i < info->gain_input_count; i++) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->autogain_ctls[i]->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->autogain_status_ctls[i]->id); + } +} + +/* Notify on input safe switch change */ +static __always_unused void scarlett2_notify_input_safe( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + if (!info->gain_input_count) + return; + + private->input_safe_updated = 1; + + for (i = 0; i < info->gain_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->safe_ctls[i]->id); +} + /* Notify on "monitor other" change (speaker switching, talkback) */ static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer) { From patchwork Tue Dec 26 18:07:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504934 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9DDF35024C for ; Tue, 26 Dec 2023 18:07:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="OFov0hgU" Received: by m.b4.vu (Postfix, from userid 1000) id 04558604B4E1; Wed, 27 Dec 2023 04:37:19 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 04558604B4E1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614039; bh=t3CmkI2+zSRKf48+Nc9cOEFrCdT4+wnmU51Dd9LF+ME=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=OFov0hgUcLcQr+zjDPbSAtGj+O9IVzpIqPyHNLjQnCF0zha/4q6CF8s0sbic7n0F8 KVuUPO4Sm72SQkRdQrUwR7r1Bw9xbK2Alkn+EQ7nC6HhT/t9cKvW85IAVfsob8xscN IXOtvedfh6TTpmHUdKOdm6VhOVVpmpMi9e2ojVKsgMBiyw8T7OuL4qeVAf2v2kCM11 YZL5qW+I0mEZVqMIRZftYwX4dWH8JpK0cUhqXJRno9m5gVjN2/BPI6GSMg/aR1Tczm aLHoxeWjUD85idOtxb56Ld/F6kOpbvwelKV3PQzIyskS/W1wMMJdarJ/lENl9vcKz3 27yU5RUWsq2dQ== Date: Wed, 27 Dec 2023 04:37:18 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 08/20] ALSA: scarlett2: Minor refactor MSD mode check Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Create local variable for storing private data pointer in snd_scarlett2_controls_create(). It's currently only used for checking msd_switch, but it will be used again. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index a0212bda2b1a..ce2fb742dd97 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -5898,6 +5898,7 @@ static int snd_scarlett2_controls_create( struct usb_mixer_interface *mixer, const struct scarlett2_device_entry *entry) { + struct scarlett2_data *private; int err; /* Initialise private data */ @@ -5905,6 +5906,8 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + private = mixer->private_data; + /* Send proprietary USB initialisation sequence */ err = scarlett2_usb_init(mixer); if (err < 0) @@ -5931,7 +5934,7 @@ static int snd_scarlett2_controls_create( return err; /* If MSD mode is enabled, don't create any other controls */ - if (((struct scarlett2_data *)mixer->private_data)->msd_switch) + if (private->msd_switch) return 0; /* Create the analogue output controls */ From patchwork Tue Dec 26 18:07:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504935 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C3C65024C for ; Tue, 26 Dec 2023 18:07:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="Ij7hQnBD" Received: by m.b4.vu (Postfix, from userid 1000) id 36371604B4AC; Wed, 27 Dec 2023 04:37:36 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 36371604B4AC DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614056; bh=8NJQf1915TN+0iGVecbd6yaQpk4rW4i3IjXzcfMmkmQ=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Ij7hQnBDhmp1RJQesZnOBnRDQkeI+1RXjglmF5GSlECpNY8Zzj+W8BYbupCSs1L7f 3VmPrvjgmDi1vmaJsJOu1ec4liWOo/IelNTKc3GhuNZBI042R7hGMV9y1qLnppFk1d thx99var62Zn61ScjN0AHGWwKzi2sL7tfl6dulJW2uD2AbX2Res7A8aTg/aydIHnxT V8hXviu4ynO6/uant6Ip7SgK5HWleUlI6y6HvgoBTyDLvtFmbAmY/hmUYlBvtFVn1W c9M59fqkOFR5f6BnH0xRd6vsu22iYgumJQbtDzAAOvEXJ7riQUW92Vs4cuASZCGyHS k6tBCL9z94bYQ== Date: Wed, 27 Dec 2023 04:37:36 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 09/20] ALSA: scarlett2: Disable input controls while autogain is running Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: While the autogain function is running, the other input controls (select, link, gain, safe, level, air, and phantom) can't be modified. Update those controls to be read-only during this time. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 298 +++++++++++++++++++++++++++++++++--- 1 file changed, 273 insertions(+), 25 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index ce2fb742dd97..6c8398aa103f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2391,6 +2391,30 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) /*** Autogain Switch and Status Controls ***/ +/* Set the access mode of a control to read-only (val = 0) or + * read-write (val = 1). + */ +static void scarlett2_set_ctl_access(struct snd_kcontrol *kctl, int val) +{ + if (val) + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + else + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; +} + +/* Check if autogain is running on any input */ +static int scarlett2_autogain_is_running(struct scarlett2_data *private) +{ + int i; + + for (i = 0; i < private->info->gain_input_count; i++) + if (private->autogain_status[i] == + SCARLETT2_AUTOGAIN_STATUS_RUNNING) + return 1; + + return 0; +} + static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; @@ -2438,13 +2462,108 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer) return 0; } +/* Update access mode for controls affected by autogain */ +static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int val = !scarlett2_autogain_is_running(private); + int i; + + scarlett2_set_ctl_access(private->input_select_ctl, val); + for (i = 0; i < info->gain_input_count / 2; i++) + scarlett2_set_ctl_access(private->input_link_ctls[i], val); + for (i = 0; i < info->gain_input_count; i++) { + scarlett2_set_ctl_access(private->input_gain_ctls[i], val); + scarlett2_set_ctl_access(private->safe_ctls[i], val); + } + for (i = 0; i < info->level_input_count; i++) + scarlett2_set_ctl_access(private->level_ctls[i], val); + for (i = 0; i < info->air_input_count; i++) + scarlett2_set_ctl_access(private->air_ctls[i], val); + for (i = 0; i < info->phantom_count; i++) + scarlett2_set_ctl_access(private->phantom_ctls[i], val); +} + +/* Notify of access mode change for all controls read-only while + * autogain runs. + */ +static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->input_select_ctl->id); + for (i = 0; i < info->gain_input_count / 2; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->input_link_ctls[i]->id); + for (i = 0; i < info->gain_input_count; i++) { + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->input_gain_ctls[i]->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->safe_ctls[i]->id); + } + for (i = 0; i < info->level_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->level_ctls[i]->id); + for (i = 0; i < info->air_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->air_ctls[i]->id); + for (i = 0; i < info->phantom_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->phantom_ctls[i]->id); +} + +/* Call scarlett2_update_autogain() and + * scarlett2_autogain_update_access() if autogain_updated is set. + */ +static int scarlett2_check_autogain_updated( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + + if (!private->autogain_updated) + return 0; + + err = scarlett2_update_autogain(mixer); + if (err < 0) + return err; + + scarlett2_autogain_update_access(mixer); + + return 0; +} + +/* If autogain_updated is set when a *_ctl_put() function for a + * control that is meant to be read-only while autogain is running, + * update the autogain status and access mode of affected controls. + * Return -EPERM if autogain is running. + */ +static int scarlett2_check_put_during_autogain( + struct usb_mixer_interface *mixer) +{ + int err = scarlett2_check_autogain_updated(mixer); + + if (err < 0) + return err; + + if (scarlett2_autogain_is_running(mixer->private_data)) + return -EPERM; + + return 0; +} + static int scarlett2_autogain_switch_ctl_get( struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; - int err = 0; + int err; mutex_lock(&private->data_mutex); @@ -2453,11 +2572,10 @@ static int scarlett2_autogain_switch_ctl_get( goto unlock; } - if (private->autogain_updated) { - err = scarlett2_update_autogain(mixer); - if (err < 0) - goto unlock; - } + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + ucontrol->value.enumerated.item[0] = private->autogain_switch[elem->control]; @@ -2472,7 +2590,7 @@ static int scarlett2_autogain_status_ctl_get( struct usb_mixer_elem_info *elem = kctl->private_data; struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; - int err = 0; + int err; mutex_lock(&private->data_mutex); @@ -2481,11 +2599,10 @@ static int scarlett2_autogain_status_ctl_get( goto unlock; } - if (private->autogain_updated) { - err = scarlett2_update_autogain(mixer); - if (err < 0) - goto unlock; - } + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + ucontrol->value.enumerated.item[0] = private->autogain_status[elem->control]; @@ -2525,6 +2642,9 @@ static int scarlett2_autogain_switch_ctl_put( if (err == 0) err = 1; + scarlett2_autogain_update_access(mixer); + scarlett2_autogain_notify_access(mixer); + unlock: mutex_unlock(&private->data_mutex); return err; @@ -2624,7 +2744,7 @@ static int scarlett2_input_select_ctl_put( struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; - int oval, val, err = 0; + int oval, val, err; int max_val = private->input_link_switch[0] ? 0 : 1; mutex_lock(&private->data_mutex); @@ -2634,6 +2754,10 @@ static int scarlett2_input_select_ctl_put( goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->input_select_switch; val = ucontrol->value.integer.value[0]; @@ -2677,6 +2801,15 @@ static int scarlett2_input_select_ctl_info( mutex_lock(&private->data_mutex); + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + /* Loop through each input * Linked inputs have one value for the pair */ @@ -2694,6 +2827,7 @@ static int scarlett2_input_select_ctl_info( err = snd_ctl_enum_info(uinfo, 1, j, (const char * const *)values); +unlock: mutex_unlock(&private->data_mutex); for (i = 0; i < inputs; i++) @@ -2713,6 +2847,35 @@ static const struct snd_kcontrol_new scarlett2_input_select_ctl = { /*** Input Link Switch Controls ***/ +/* snd_ctl_boolean_mono_info() with autogain-updated check + * (for controls that are read-only while autogain is running) + */ +static int scarlett2_autogain_disables_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + + err = snd_ctl_boolean_mono_info(kctl, uinfo); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + static int scarlett2_input_link_ctl_get( struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -2749,7 +2912,7 @@ static int scarlett2_input_link_ctl_put( struct scarlett2_data *private = mixer->private_data; int index = elem->control; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -2758,6 +2921,10 @@ static int scarlett2_input_link_ctl_put( goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->input_link_switch[index]; val = !!ucontrol->value.integer.value[0]; @@ -2789,7 +2956,7 @@ static int scarlett2_input_link_ctl_put( static const struct snd_kcontrol_new scarlett2_input_link_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", - .info = snd_ctl_boolean_mono_info, + .info = scarlett2_autogain_disables_ctl_info, .get = scarlett2_input_link_ctl_get, .put = scarlett2_input_link_ctl_put }; @@ -2815,13 +2982,30 @@ static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = elem->channels; uinfo->value.integer.min = 0; uinfo->value.integer.max = SCARLETT2_GAIN_BIAS; uinfo->value.integer.step = 1; - return 0; + +unlock: + mutex_unlock(&private->data_mutex); + return err; } static int scarlett2_input_gain_ctl_get(struct snd_kcontrol *kctl, @@ -2860,7 +3044,7 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl, struct scarlett2_data *private = mixer->private_data; int index = elem->control; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -2869,6 +3053,10 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl, goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->gain[index]; val = ucontrol->value.integer.value[0]; @@ -2957,7 +3145,7 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl, struct scarlett2_data *private = mixer->private_data; int index = elem->control; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -2966,6 +3154,10 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl, goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->safe_switch[index]; val = !!ucontrol->value.integer.value[0]; @@ -2988,7 +3180,7 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl, static const struct snd_kcontrol_new scarlett2_safe_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", - .info = snd_ctl_boolean_mono_info, + .info = scarlett2_autogain_disables_ctl_info, .get = scarlett2_safe_ctl_get, .put = scarlett2_safe_ctl_put, }; @@ -3434,8 +3626,27 @@ static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl, static const char *const values[2] = { "Line", "Inst" }; + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err; - return snd_ctl_enum_info(uinfo, 1, 2, values); + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + + err = snd_ctl_enum_info(uinfo, 1, 2, values); + +unlock: + mutex_unlock(&private->data_mutex); + return err; } static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl, @@ -3478,7 +3689,7 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl, const struct scarlett2_device_info *info = private->info; int index = elem->control + info->level_input_first; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -3487,6 +3698,10 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl, goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->level_switch[index]; val = !!ucontrol->value.enumerated.item[0]; @@ -3659,7 +3874,7 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl, struct scarlett2_data *private = mixer->private_data; int index = elem->control; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -3668,6 +3883,10 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl, goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->air_switch[index]; val = ucontrol->value.integer.value[0]; @@ -3693,8 +3912,27 @@ static int scarlett2_air_with_drive_ctl_info( static const char *const values[3] = { "Off", "Presence", "Presence + Drive" }; + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err; - return snd_ctl_enum_info(uinfo, 1, 3, values); + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + err = scarlett2_check_autogain_updated(mixer); + if (err < 0) + goto unlock; + + err = snd_ctl_enum_info(uinfo, 1, 3, values); + +unlock: + mutex_unlock(&private->data_mutex); + return err; } static const struct snd_kcontrol_new scarlett2_air_ctl[2] = { @@ -3782,7 +4020,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, const struct scarlett2_device_info *info = private->info; int index = elem->control; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -3791,6 +4029,10 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, goto unlock; } + err = scarlett2_check_put_during_autogain(mixer); + if (err < 0) + goto unlock; + oval = private->phantom_switch[index]; val = !!ucontrol->value.integer.value[0]; @@ -3817,7 +4059,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, static const struct snd_kcontrol_new scarlett2_phantom_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", - .info = snd_ctl_boolean_mono_info, + .info = scarlett2_autogain_disables_ctl_info, .get = scarlett2_phantom_ctl_get, .put = scarlett2_phantom_ctl_put, }; @@ -5743,6 +5985,8 @@ static __always_unused void scarlett2_notify_autogain( snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->autogain_status_ctls[i]->id); } + + scarlett2_autogain_notify_access(mixer); } /* Notify on input safe switch change */ @@ -5987,6 +6231,10 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Set the access mode of controls disabled during autogain */ + if (private->info->gain_input_count) + scarlett2_autogain_update_access(mixer); + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0) From patchwork Tue Dec 26 18:07:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504936 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6CECB4F88B for ; Tue, 26 Dec 2023 18:07:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="e0kWFnsl" Received: by m.b4.vu (Postfix, from userid 1000) id 784AE604B414; Wed, 27 Dec 2023 04:37:56 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 784AE604B414 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614076; bh=mXe3fmuxi2Ese6J9so7/NZDB/q2dxpB/nCEjIWf0QkU=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=e0kWFnsliHHDqKc5UtpkqVjm9YXWZUc2WyzfhaD9nAdnV5RikDOMpiYq9FESHL0Jo MpzIosqGITyvdX7oN53lNrrSgjkMXQzf/mRKYSN+QWCOWTzlOy+KuEVS4rD4Ls5erp JWei2QdmiKyuO0dcFr1pshYpPCCbsDY2ho9Zob2xULbWbEy9bHRjkXIa1D+6l3J9JS dU3tNiNupZXMhvTSj+D6o5DWIYlQvTjrKXG70gE6BuOGCIP2Aq6yShr4psq7+OhDaW xeGn/Er62O9hlAnra52SMBnEZ/Sd7CsqUJbUknv10/gHt4pcH390hM4ax5PglYpkls zV47ExawRZzAg== Date: Wed, 27 Dec 2023 04:37:56 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 10/20] ALSA: scarlett2: Disable autogain during phantom power state change Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: When phantom power is enabled or disabled, the autogain control cannot be enabled until the interface has signalled that the change is complete and the input is unmuted. Update those controls to be read-only during this time. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 123 +++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 10 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 6c8398aa103f..2d6fd9faed31 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2391,6 +2391,10 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) /*** Autogain Switch and Status Controls ***/ +/* Forward declarations as phantom power and autogain can disable each other */ +static int scarlett2_check_input_phantom_updated(struct usb_mixer_interface *); +static int scarlett2_phantom_is_switching(struct scarlett2_data *, int); + /* Set the access mode of a control to read-only (val = 0) or * read-write (val = 1). */ @@ -2557,6 +2561,27 @@ static int scarlett2_check_put_during_autogain( return 0; } +static int scarlett2_autogain_switch_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err; + + mutex_lock(&private->data_mutex); + + err = scarlett2_check_input_phantom_updated(mixer); + if (err < 0) + goto unlock; + + err = snd_ctl_boolean_mono_info(kctl, uinfo); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + static int scarlett2_autogain_switch_ctl_get( struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -2619,7 +2644,7 @@ static int scarlett2_autogain_switch_ctl_put( struct scarlett2_data *private = mixer->private_data; int index = elem->control; - int oval, val, err = 0; + int oval, val, err; mutex_lock(&private->data_mutex); @@ -2628,6 +2653,15 @@ static int scarlett2_autogain_switch_ctl_put( goto unlock; } + err = scarlett2_check_input_phantom_updated(mixer); + if (err < 0) + goto unlock; + + if (scarlett2_phantom_is_switching(private, index)) { + err = -EPERM; + goto unlock; + } + oval = private->autogain_switch[index]; val = !!ucontrol->value.integer.value[0]; @@ -2664,7 +2698,7 @@ static int scarlett2_autogain_status_ctl_info( static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", - .info = snd_ctl_boolean_mono_info, + .info = scarlett2_autogain_switch_ctl_info, .get = scarlett2_autogain_switch_ctl_get, .put = scarlett2_autogain_switch_ctl_put }; @@ -3983,13 +4017,74 @@ static int scarlett2_update_input_phantom(struct usb_mixer_interface *mixer) return 0; } +/* Check if phantom power on the given input is currently changing state */ +static int scarlett2_phantom_is_switching( + struct scarlett2_data *private, int line_num) +{ + const struct scarlett2_device_info *info = private->info; + int index = line_num / info->inputs_per_phantom; + + return !!(private->phantom_switch[index] & 0x02); +} + +/* Update autogain controls' access mode when phantom power changes state */ +static void scarlett2_phantom_update_access(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + /* Disable autogain controls if phantom power is changing state */ + for (i = 0; i < info->gain_input_count; i++) { + int val = !scarlett2_phantom_is_switching(private, i); + + scarlett2_set_ctl_access(private->autogain_ctls[i], val); + } +} + +/* Notify of access mode change for autogain which can't be enabled + * while phantom power is changing. + */ +static void scarlett2_phantom_notify_access(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + int i; + + for (i = 0; i < info->gain_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->autogain_ctls[i]->id); +} + +/* Call scarlett2_update_input_phantom() and + * scarlett2_phantom_update_access() if input_phantom_updated is set. + */ +static int scarlett2_check_input_phantom_updated( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + + if (!private->input_phantom_updated) + return 0; + + err = scarlett2_update_input_phantom(mixer); + if (err < 0) + return err; + + scarlett2_phantom_update_access(mixer); + + return 0; +} + static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; - int err = 0; + int err; mutex_lock(&private->data_mutex); @@ -3998,11 +4093,10 @@ static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl, goto unlock; } - if (private->input_phantom_updated) { - err = scarlett2_update_input_phantom(mixer); - if (err < 0) - goto unlock; - } + err = scarlett2_check_input_phantom_updated(mixer); + if (err < 0) + goto unlock; + ucontrol->value.integer.value[0] = scarlett2_decode_muteable( private->phantom_switch[elem->control]); @@ -4051,6 +4145,9 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, if (err == 0) err = 1; + scarlett2_phantom_update_access(mixer); + scarlett2_phantom_notify_access(mixer); + unlock: mutex_unlock(&private->data_mutex); return err; @@ -5912,6 +6009,8 @@ static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer) for (i = 0; i < info->phantom_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->phantom_ctls[i]->id); + + scarlett2_phantom_notify_access(mixer); } /* Notify on "input other" change (level/pad/air/phantom) */ @@ -6231,9 +6330,13 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; - /* Set the access mode of controls disabled during autogain */ - if (private->info->gain_input_count) + /* Set the access mode of controls disabled during + * autogain/phantom power switching. + */ + if (private->info->gain_input_count) { scarlett2_autogain_update_access(mixer); + scarlett2_phantom_update_access(mixer); + } /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); From patchwork Tue Dec 26 18:08:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504937 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 90EB350252 for ; Tue, 26 Dec 2023 18:08:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="VRc/W37w" Received: by m.b4.vu (Postfix, from userid 1000) id 8F53E604B2CB; Wed, 27 Dec 2023 04:38:02 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 8F53E604B2CB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614082; bh=3X7/qAzdi4fbKUpXPbQG/zFS4/3eHBSTkcgx993TyrY=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=VRc/W37wm6L6SRQ8Qe1J/FLrxmqNmQPYdvEeLhiw/KdZBD/c6zJw2QSOy/qnG3bxW 3hpvv9K0s/pM6yPG4EeJ+cdx+Gh+Q1O1D2S4sCV0OrTo7+lggesELmZVaWAJJWnDTz IqSQ73U1ROR9xWuCOvlKCTq7g56bRsUHJ9PKHjl7Eb2G0+WaVR/8127MEF1TkI7ivl wGs0QBIU7Ml/G0hXuz8pUw3Qi2KEtgQ5qPprZah9+2sBZ9Bcwvfz6jGEOsBPACANzz KGX3omazkrYsFl/lZ6aafGPcRJWDPwXv3qCD39Nlnn92TZL5l5OYUd24EQxpNho4pc WhB01BPswnl4A== Date: Wed, 27 Dec 2023 04:38:02 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 11/20] ALSA: scarlett2: Add power status control Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add a control to retrieve the power status from the interface (bus-powered, external-powered, or insufficient power). Mark the new scarlett2_notify_power_status() function with __always_unused until it gets used when the Gen 4 notification callback function arrays are added. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 123 ++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 2d6fd9faed31..f5f57c86cec8 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -278,6 +278,14 @@ enum { SCARLETT2_AUTOGAIN_STATUS_COUNT }; +/* Power Status Values */ +enum { + SCARLETT2_POWER_STATUS_EXT, + SCARLETT2_POWER_STATUS_BUS, + SCARLETT2_POWER_STATUS_FAIL, + SCARLETT2_POWER_STATUS_COUNT +}; + /* Notification callback functions */ struct scarlett2_notification { u32 mask; @@ -334,6 +342,8 @@ enum { SCARLETT2_CONFIG_SAFE_SWITCH, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, + SCARLETT2_CONFIG_POWER_EXT, + SCARLETT2_CONFIG_POWER_STATUS, SCARLETT2_CONFIG_COUNT }; @@ -751,6 +761,7 @@ struct scarlett2_data { u8 direct_monitor_updated; u8 mux_updated; u8 speaker_switching_switched; + u8 power_status_updated; u8 sync; u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; @@ -774,6 +785,7 @@ struct scarlett2_data { u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX]; u8 msd_switch; u8 standalone_switch; + u8 power_status; u8 meter_level_map[SCARLETT2_MAX_METERS]; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -795,6 +807,7 @@ struct scarlett2_data { struct snd_kcontrol *direct_monitor_ctl; struct snd_kcontrol *speaker_switching_ctl; struct snd_kcontrol *talkback_ctl; + struct snd_kcontrol *power_status_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_MIX_MAX]; }; @@ -5526,6 +5539,91 @@ static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer) 0, 1, "Standalone Switch", NULL); } +/*** Power Status ***/ + +static int scarlett2_update_power_status(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + u8 power_ext; + u8 power_status; + + private->power_status_updated = 0; + + err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_EXT, + 1, &power_ext); + if (err < 0) + return err; + + err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_STATUS, + 1, &power_status); + if (err < 0) + return err; + + if (power_status > 1) + private->power_status = SCARLETT2_POWER_STATUS_FAIL; + else if (power_ext) + private->power_status = SCARLETT2_POWER_STATUS_EXT; + else + private->power_status = SCARLETT2_POWER_STATUS_BUS; + + return 0; +} + +static int scarlett2_power_status_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->power_status_updated) { + err = scarlett2_update_power_status(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = private->power_status; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_power_status_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[3] = { + "External", "Bus", "Fail" + }; + + return snd_ctl_enum_info(uinfo, 1, 3, values); +} + +static const struct snd_kcontrol_new scarlett2_power_status_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "", + .info = scarlett2_power_status_ctl_info, + .get = scarlett2_power_status_ctl_get, +}; + +static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (!scarlett2_has_config_item(private, + SCARLETT2_CONFIG_POWER_EXT)) + return 0; + + /* Add power status control */ + return scarlett2_add_new_ctl(mixer, &scarlett2_power_status_ctl, + 0, 1, "Power Status Card Enum", + &private->power_status_ctl); +} + /*** Cleanup/Suspend Callbacks ***/ static void scarlett2_private_free(struct usb_mixer_interface *mixer) @@ -5817,6 +5915,13 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return err; } + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_POWER_EXT)) { + err = scarlett2_update_power_status(mixer); + if (err < 0) + return err; + } + err = scarlett2_update_sync(mixer); if (err < 0) return err; @@ -6153,6 +6258,19 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer) &private->direct_monitor_ctl->id); } +/* Notify on power change */ +static __always_unused void scarlett2_notify_power_status( + struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + + private->power_status_updated = 1; + + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->power_status_ctl->id); +} + /* Interrupt callback */ static void scarlett2_notify(struct urb *urb) { @@ -6330,6 +6448,11 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Create the power status control */ + err = scarlett2_add_power_status_ctl(mixer); + if (err < 0) + return err; + /* Set the access mode of controls disabled during * autogain/phantom power switching. */ From patchwork Tue Dec 26 18:08:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504938 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0357750250 for ; Tue, 26 Dec 2023 18:08:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="tX6jv8oZ" Received: by m.b4.vu (Postfix, from userid 1000) id 4A2FB604B1CB; Wed, 27 Dec 2023 04:38:13 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 4A2FB604B1CB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614093; bh=HcWcSia0FzSOhF8ynmrBT8sOObpt9ImVvBE3r2KvBW8=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=tX6jv8oZ8yS2aNHKsH3M+R0WvO/hH9/aLiMgN9M8x4V/ZWiVXZ6YnrLcLATeErZRD 1AcwBhkPJtvz6mwBW4YqkcPkls5BQLPXKZ5h8pvfmMvWboIXC3m7CEjbRQc0IvtsYT CqQsM/UvxF6GqXhtxicYxj6EJroz3KPB2ttATNwyx6XELDZU4VIaTanOgJXhnfjWyq GBpbbQTCNJE4UYl7EH8Zk0AUd3WX7rgsh5DlqZniYEpEV4rTK0zRc3ITimSEXgjVGx gOcvUoBcUQd7MDysDANUJK0wPrQAL0c+tuCcBXLO7tP3wB5Yh2wgv/XEFK9ha7S1xW ypoayU4ch4acw== Date: Wed, 27 Dec 2023 04:38:13 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 12/20] ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor Message-ID: <3ba27c60230511b80b0fa75727551ea70f17d829.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The Scarlett 4th Gen small interfaces have a software-controllable mixer like the large 2nd and 3rd Gen interfaces do. Pressing the "Direct" button on the interface updates the mixer controls, which this driver hasn't needed to deal with previously. This commit stores the ALSA mixer controls, and adds a mix_updated flag so that the controls can be updated when a notification is received. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 50 +++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index f5f57c86cec8..391ecacbc768 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -760,6 +760,7 @@ struct scarlett2_data { u8 monitor_other_updated; u8 direct_monitor_updated; u8 mux_updated; + u8 mix_updated; u8 speaker_switching_switched; u8 power_status_updated; u8 sync; @@ -804,6 +805,7 @@ struct scarlett2_data { struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; + struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX]; struct snd_kcontrol *direct_monitor_ctl; struct snd_kcontrol *speaker_switching_ctl; struct snd_kcontrol *talkback_ctl; @@ -4960,6 +4962,22 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) /*** Mixer Volume Controls ***/ +static int scarlett2_update_mix(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int i, err; + + private->mix_updated = 0; + + for (i = 0; i < private->num_mix_out; i++) { + err = scarlett2_usb_get_mix(mixer, i); + if (err < 0) + return err; + } + + return 1; +} + static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -4977,10 +4995,27 @@ static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; - struct scarlett2_data *private = elem->head.mixer->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->mix_updated) { + err = scarlett2_update_mix(mixer); + if (err < 0) + goto unlock; + } ucontrol->value.integer.value[0] = private->mix[elem->control]; - return 0; + +unlock: + mutex_unlock(&private->data_mutex); + return err; } static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl, @@ -5048,7 +5083,8 @@ static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer) "Mix %c Input %02d Playback Volume", 'A' + i, j + 1); err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl, - index, 1, s, NULL); + index, 1, s, + &private->mix_ctls[index]); if (err < 0) return err; } @@ -5993,11 +6029,9 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; - for (i = 0; i < private->num_mix_out; i++) { - err = scarlett2_usb_get_mix(mixer, i); - if (err < 0) - return err; - } + err = scarlett2_update_mix(mixer); + if (err < 0) + return err; return scarlett2_usb_get_mux(mixer); } From patchwork Tue Dec 26 18:08:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504939 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B4ED50252 for ; Tue, 26 Dec 2023 18:08:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="J1lPHZOm" Received: by m.b4.vu (Postfix, from userid 1000) id 8648B604B1CF; Wed, 27 Dec 2023 04:38:20 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 8648B604B1CF DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614100; bh=/nCYWdi1vvF1IIWfuaJ62MNbSMcO4Sx2CAt6oLGyJ9s=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=J1lPHZOmkheMTys8trPG8VA22lqZ/twmF3VAOw81P2Rui0Tj/3xnc/kZ5YOLr39lQ n2eIblxRd7jKJYPtMiH6bWE+zdU7gSgLfMuEOgeLZRzOrv0AasF/A97FZAZip0E7Sc /8n9fP60PWAhZYvWPP/5OQ/EmZjZQ+5aVyNIcG2IorObEBZ/uVt9/iAza6eLk9SlnO Mxsl2kWmkoRBqwjv26UpwICp88UoJr5Q4d3WcPdlKYYD0tOgUxvFyyNj568YwSbF4a 9dX/4Pnc99DB2yUbgm0mgcPoEj/mE24SWSyWUTMYgJThGrMeVRm43sDbakesUZaUgE vkkbk+lStZzng== Date: Wed, 27 Dec 2023 04:38:20 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 13/20] ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates Message-ID: <713d032e343e0547212368919bef17d6fa1c9d29.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: When the Direct Monitor feature on the Scarlett 4th Gen Solo and 2i2 interfaces is used, the Mix A and B gains are updated by the interface. This patch calls snd_ctl_notify() for the ALSA mix controls when a Direct Monitor notification is received. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 391ecacbc768..50899e81e00e 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -6285,11 +6285,23 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; + int count = private->num_mix_in * private->num_mix_out; + int i; private->direct_monitor_updated = 1; snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->direct_monitor_ctl->id); + + if (!scarlett2_has_mixer(private)) + return; + + private->mix_updated = 1; + + /* Notify of change to the mix controls */ + for (i = 0; i < count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mix_ctls[i]->id); } /* Notify on power change */ From patchwork Tue Dec 26 18:08:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504940 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 311515025C for ; Tue, 26 Dec 2023 18:08:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="DGRMyPaq" Received: by m.b4.vu (Postfix, from userid 1000) id 400B7604B1D6; Wed, 27 Dec 2023 04:38:28 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 400B7604B1D6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614108; bh=v67oe/v5feaoTXm7fzhh8FSx5eyKkY0u+xb6x5H9PaQ=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=DGRMyPaqduCSIV2xzp0F4fhODqV+84G9I8NF1bk5XnReRuFCTqpyJ81k3R941Ob+d 92dNCGgyfEOGnPRX0p6b73od1Xh9H5b9K+KWHEBZc1P5Lnc2+WHBK/iP4yn5COsBL+ xusfjoPR/DGqc3fkP5Y+8scwbbsKErEjXNZiXFNCZgYfzckm6Hh9tmgvPvtgTqOrDX h7JN4bro7r9ROJ6GhwPHhQW1muK2CufjbyxdSu0aNJDydWm10lT+EH6YhY/I12Cyu1 Dzrnlm70CiXs97vwj3H35EpubYNZbebEzNvfoh0s7Bw9knOf2a4e59hcsu9X0c09Qc LLbIBlxVHHEuQ== Date: Wed, 27 Dec 2023 04:38:28 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 14/20] ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes Message-ID: <96282a805b45f04560e5923d170745363906b7f3.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The mixes used by Direct Monitor feature on the Scarlett 4th Gen Solo and 2i2 interfaces are configurable. This patch adds ALSA controls for the gains which are copied to the mixer when Direct Monitor is enabled. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 145 +++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 50899e81e00e..1c6a493eb2ee 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -213,6 +213,13 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { /* Maximum number of mixer gain controls */ #define SCARLETT2_MIX_MAX (SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX) +/* Maximum number of direct monitor mixer gain controls + * 1 (Solo) or 2 (2i2) direct monitor selections (Mono & Stereo) + * 2 Mix outputs (A/Left & B/Right) + * 4 Mix inputs + */ +#define SCARLETT2_MONITOR_MIX_MAX (2 * 2 * 4) + /* Maximum size of the data in the USB mux assignment message: * 20 inputs, 20 outputs, 25 matrix inputs, 12 spare */ @@ -344,6 +351,7 @@ enum { SCARLETT2_CONFIG_INPUT_LINK_SWITCH, SCARLETT2_CONFIG_POWER_EXT, SCARLETT2_CONFIG_POWER_STATUS, + SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, SCARLETT2_CONFIG_COUNT }; @@ -742,6 +750,7 @@ struct scarlett2_data { u8 num_mix_in; u8 num_mix_out; u8 num_line_out; + u8 num_monitor_mix_ctls; u32 firmware_version; u8 flash_segment_nums[SCARLETT2_SEGMENT_ID_COUNT]; u8 flash_segment_blocks[SCARLETT2_SEGMENT_ID_COUNT]; @@ -812,6 +821,7 @@ struct scarlett2_data { struct snd_kcontrol *power_status_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_MIX_MAX]; + u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX]; }; /*** Model-specific data ***/ @@ -5108,6 +5118,28 @@ static int scarlett2_update_direct_monitor(struct usb_mixer_interface *mixer) 1, &private->direct_monitor_switch); } +static int scarlett2_update_monitor_mix(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err, i; + u16 mix_values[SCARLETT2_MONITOR_MIX_MAX]; + + if (!private->num_monitor_mix_ctls) + return 0; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, + private->num_monitor_mix_ctls, mix_values); + if (err < 0) + return err; + + for (i = 0; i < private->num_monitor_mix_ctls; i++) + private->monitor_mix[i] = scarlett2_mixer_value_to_db( + mix_values[i]); + + return 0; +} + static int scarlett2_direct_monitor_ctl_get( struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -5201,11 +5233,70 @@ static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = { } }; -static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer) +static int scarlett2_monitor_mix_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_data *private = elem->head.mixer->private_data; + + ucontrol->value.integer.value[0] = private->monitor_mix[elem->control]; + + return 0; +} + +static int scarlett2_monitor_mix_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int oval, val, err = 0; + int index = elem->control; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->monitor_mix[index]; + val = clamp(ucontrol->value.integer.value[0], + 0L, (long)SCARLETT2_MIXER_MAX_VALUE); + + if (oval == val) + goto unlock; + + private->monitor_mix[index] = val; + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, + index, scarlett2_mixer_values[val]); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_monitor_mix_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_mixer_ctl_info, + .get = scarlett2_monitor_mix_ctl_get, + .put = scarlett2_monitor_mix_ctl_put, + .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */ + .tlv = { .p = db_scale_scarlett2_mixer } +}; + +static int scarlett2_add_direct_monitor_ctls(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; const char *s; + int err, i, j, k, index; if (!info->direct_monitor) return 0; @@ -5214,9 +5305,47 @@ static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer) ? "Direct Monitor Playback Switch" : "Direct Monitor Playback Enum"; - return scarlett2_add_new_ctl( + err = scarlett2_add_new_ctl( mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1], 0, 1, s, &private->direct_monitor_ctl); + if (err < 0) + return err; + + if (!private->num_monitor_mix_ctls) + return 0; + + /* 1 or 2 direct monitor selections (Mono & Stereo) */ + for (i = 0, index = 0; i < info->direct_monitor; i++) { + const char * const format = + "Monitor %sMix %c Input %02d Playback Volume"; + const char *mix_type; + + if (info->direct_monitor == 1) + mix_type = ""; + else if (i == 0) + mix_type = "1 "; + else + mix_type = "2 "; + + /* 2 Mix outputs, A/Left & B/Right */ + for (j = 0; j < 2; j++) + + /* Mix inputs */ + for (k = 0; k < private->num_mix_in; k++, index++) { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + snprintf(name, sizeof(name), format, + mix_type, 'A' + j, k + 1); + + err = scarlett2_add_new_ctl( + mixer, &scarlett2_monitor_mix_ctl, + index, 1, name, NULL); + if (err < 0) + return err; + } + } + + return 0; } /*** Mux Source Selection Controls ***/ @@ -5708,6 +5837,10 @@ static void scarlett2_count_io(struct scarlett2_data *private) /* Number of analogue line outputs */ private->num_line_out = port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; + + /* Number of monitor mix controls */ + private->num_monitor_mix_ctls = + info->direct_monitor * 2 * private->num_mix_in; } /* Look through the interface descriptors for the Focusrite Control @@ -5938,6 +6071,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (!scarlett2_has_mixer(private)) return 0; + err = scarlett2_update_monitor_mix(mixer); + if (err < 0) + return err; + err = scarlett2_update_monitor_other(mixer); if (err < 0) return err; @@ -6474,8 +6611,8 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; - /* Create the direct monitor control */ - err = scarlett2_add_direct_monitor_ctl(mixer); + /* Create the direct monitor control(s) */ + err = scarlett2_add_direct_monitor_ctls(mixer); if (err < 0) return err; From patchwork Tue Dec 26 18:08:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504941 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3A1545025C for ; Tue, 26 Dec 2023 18:08:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="Ojj7sBnB" Received: by m.b4.vu (Postfix, from userid 1000) id 240FC604B1E4; Wed, 27 Dec 2023 04:38:34 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 240FC604B1E4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614114; bh=Z1wypPWyRwmNOqYEnSJ9Ayq84v0l+X7IAUmsB3CMjQA=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Ojj7sBnBy9qGcmhYbcLah13c7i8VbTeVCiRB3mzkOid5K/EqJR8hjUJ3ttfgKQEYZ gmtThMd9OFoS0yJIka2/fu3Mcui96jJMEFXddCLdXCNbJfB6nwmEBvAfToaF7Ahdm0 ZlnGgJaLvdg856HwSjxCXAMvyTFNylspJSjomzjJLi+QvD6l2UN3ODZRUj2RKldRtI dHSkxSge/MgXqdphxIsVl/xXd85M5ltc8tM5Dbt9YIvsQC6e7/z3ConjBUJUQSY6ak q43S92WzwbepaI4z/9BeSKWvyC5C9Jd+nBOoxuLH4sJzElB6Hom277EhW4yVFLtfmZ 44DOCPyBsNNNw== Date: Wed, 27 Dec 2023 04:38:34 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 15/20] ALSA: scarlett2: Add support for DSP mux channels Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The DSP mux channels in the Scarlett 4th Gen appear as SCARLETT2_PORT_TYPE_MIX ports but do not have corresponding mixer controls. Add a dsp_count option to the device info struct to exclude those DSP channels from the num_mix_in/num_mix_out counts. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 1c6a493eb2ee..5ac31fea9831 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -706,6 +706,9 @@ struct scarlett2_device_info { */ u8 direct_monitor; + /* the number of DSP channels */ + u8 dsp_count; + /* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected * internally to the analogue 7/8 outputs */ @@ -5827,12 +5830,17 @@ static void scarlett2_count_io(struct scarlett2_data *private) private->num_mux_srcs = srcs; private->num_mux_dsts = dsts; - /* Mixer inputs are mux outputs and vice versa */ + /* Mixer inputs are mux outputs and vice versa. + * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but + * doesn't have mixer controls. + */ private->num_mix_in = - port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] - + info->dsp_count; private->num_mix_out = - port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] - + info->dsp_count; /* Number of analogue line outputs */ private->num_line_out = From patchwork Tue Dec 26 18:08:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504942 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CFD0251008 for ; Tue, 26 Dec 2023 18:08:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="gAtpDUno" Received: by m.b4.vu (Postfix, from userid 1000) id 42A80604B18D; Wed, 27 Dec 2023 04:38:38 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 42A80604B18D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614118; bh=BgaJ32GMB6RjoC46opjTtx1YNOtIScEK+d2ov2yAQbM=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=gAtpDUnolFLTEOP+uCRjzXyRlloigpHpMSBCe6nUW8wk1EcpQhnnzan7okONEWkHH hr00vEx6vh26kSPSG1bO146z4aCjUDFNmWZMBFAi/QaQqt3OgBl4iS0NS3obuB3kLd 5o0GOnOKsGLxUmt4LKD61g32QKoynVV3TQy2zWcFygTjzrHJZzMwyMVbN6QLg1ZQwF eLTNs0LELQGOwQxrPpVtcB8o3NGlj7fk5XEQBi0h7iNp9nS37HR6MEENT08yvkiRp/ DbE9L9TRMnciJ4kqtnR9DGGmGAB7JhuxziAEOf0MnRKhahea1SfyGPQNSg9v2XSQCK aY/jcPNpPXN8g== Date: Wed, 27 Dec 2023 04:38:38 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 16/20] ALSA: scarlett2: Rename DSP mux channels Message-ID: <2d91d0a74d5c7f6179e950bed2c80a4498d16649.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The DSP mux channels are of type SCARLETT2_PORT_TYPE_MIX so the ALSA controls would refer to them "Mix X" and "Mixer Input X". This patch fixes them to be called "DSP X" and "DSP Input X". Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 5ac31fea9831..5c44793fe345 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -583,6 +583,8 @@ struct scarlett2_port { const char * const src_descr; int src_num_offset; const char * const dst_descr; + const char * const dsp_src_descr; + const char * const dsp_dst_descr; }; static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = { @@ -612,7 +614,9 @@ static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = .id = 0x300, .src_descr = "Mix %c", .src_num_offset = 'A', - .dst_descr = "Mixer Input %02d Capture" + .dst_descr = "Mixer Input %02d Capture", + .dsp_src_descr = "DSP %d", + .dsp_dst_descr = "DSP Input %d Capture" }, [SCARLETT2_PORT_TYPE_PCM] = { .id = 0x600, @@ -5378,8 +5382,16 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, const struct scarlett2_port *port = &scarlett2_ports[port_type]; - sprintf(uinfo->value.enumerated.name, - port->src_descr, item + port->src_num_offset); + if (port_type == SCARLETT2_PORT_TYPE_MIX && + item >= private->num_mix_out) + sprintf(uinfo->value.enumerated.name, + port->dsp_src_descr, + item - private->num_mix_out + 1); + else + sprintf(uinfo->value.enumerated.name, + port->src_descr, + item + port->src_num_offset); + return 0; } item -= port_count[port_type][SCARLETT2_PORT_IN]; @@ -5472,10 +5484,18 @@ static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer) channel++, i++) { int err; char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - const char *const descr = - scarlett2_ports[port_type].dst_descr; + int channel_num = channel + 1; + const struct scarlett2_port *port = + &scarlett2_ports[port_type]; + const char *descr = port->dst_descr; - snprintf(s, sizeof(s) - 5, descr, channel + 1); + if (port_type == SCARLETT2_PORT_TYPE_MIX && + channel >= private->num_mix_in) { + channel_num -= private->num_mix_in; + descr = port->dsp_dst_descr; + } + + snprintf(s, sizeof(s) - 5, descr, channel_num); strcat(s, " Enum"); err = scarlett2_add_new_ctl(mixer, From patchwork Tue Dec 26 18:08:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504943 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C62851009 for ; Tue, 26 Dec 2023 18:08:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="Ea7l5UvV" Received: by m.b4.vu (Postfix, from userid 1000) id 60AB4604B15B; Wed, 27 Dec 2023 04:38:46 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 60AB4604B15B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614126; bh=2ns/TR+XHkn0m2vqqGKlwG1XloQdP9W7wfhuQrABEbo=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Ea7l5UvVVWZVlG5RRUnmO/7UFTKk8qYCCImUjh8PKXlgIT0vsXotVrRIXHyR4rHEL QOeziDYdHhzdHL+whMolQCiuOaEoXb2hQNatfWPKtOq3yDOnuUxzGZnyFOyEACEG/l sMv+jWpvmfOu2IvILlC2PvKDRzXLp644pSGmS7/n+eRNa042oHx2mOmG6cFZD3v5un u5d+N1rgzA5RNr2dYqiG2VEB3jf/aLZfS0Ib/Qu9SUB7Wf7dcw1IV4L6bL/lxzPsXG y+czB2egbkNt5B5JFINrrw1OTh6WJntxCU7g/Q5ulzNQ6Q/5Rsn4qCMlz4LDP16P+9 Y2JmHbvBhAuNQ== Date: Wed, 27 Dec 2023 04:38:46 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 17/20] ALSA: scarlett2: Add minimum firmware version check Message-ID: <5455a7e54bda81556066abd7f761b10e9c5f8a16.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Early firmware for the Scarlett Gen 4 devices has sufficient differences that it is better to enforce a minimum firmware version than to try and work around those differences. Add a minimum firmware version field to the device info struct, and display a message if the firmware version is too old. Only create the Firmware Version and MSD (optional) controls in this case. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 73 ++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 5c44793fe345..c21ea9fc38ab 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -660,6 +660,9 @@ struct scarlett2_device_info { /* which set of configuration parameters the device uses */ const struct scarlett2_config_set *config_set; + /* minimum firmware version required */ + u16 min_firmware_version; + /* support for main/alt speaker switching */ u8 has_speaker_switching; @@ -2352,6 +2355,45 @@ static int scarlett2_add_firmware_version_ctl( 0, 0, "Firmware Version", NULL); } +/*** Minimum Firmware Version Control ***/ + +static int scarlett2_min_firmware_version_ctl_get( + struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett2_data *private = elem->head.mixer->private_data; + + ucontrol->value.integer.value[0] = private->info->min_firmware_version; + + return 0; +} + +static int scarlett2_min_firmware_version_ctl_info( + struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + return 0; +} + +static const struct snd_kcontrol_new scarlett2_min_firmware_version_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "", + .info = scarlett2_min_firmware_version_ctl_info, + .get = scarlett2_min_firmware_version_ctl_get +}; + +static int scarlett2_add_min_firmware_version_ctl( + struct usb_mixer_interface *mixer) +{ + return scarlett2_add_new_ctl(mixer, &scarlett2_min_firmware_version_ctl, + 0, 0, "Minimum Firmware Version", NULL); +} + /*** Sync Control ***/ /* Update sync control after receiving notification that the status @@ -6061,6 +6103,7 @@ static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer) static int scarlett2_read_configs(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; int err, i; if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) { @@ -6069,12 +6112,22 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) 1, &private->msd_switch); if (err < 0) return err; - - /* no other controls are created if MSD mode is on */ - if (private->msd_switch) - return 0; } + if (private->firmware_version < info->min_firmware_version) { + usb_audio_err(mixer->chip, + "Focusrite %s firmware version %d is too old; " + "need %d", + private->series_name, + private->firmware_version, + info->min_firmware_version); + return 0; + } + + /* no other controls are created if MSD mode is on */ + if (private->msd_switch) + return 0; + err = scarlett2_update_input_level(mixer); if (err < 0) return err; @@ -6595,6 +6648,11 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; + /* Add minimum firmware version control */ + err = scarlett2_add_min_firmware_version_ctl(mixer); + if (err < 0) + return err; + /* Read volume levels and controls from the interface */ err = scarlett2_read_configs(mixer); if (err < 0) @@ -6605,8 +6663,11 @@ static int snd_scarlett2_controls_create( if (err < 0) return err; - /* If MSD mode is enabled, don't create any other controls */ - if (private->msd_switch) + /* If MSD mode is enabled, or if the firmware version is too + * old, don't create any other controls + */ + if (private->msd_switch || + private->firmware_version < private->info->min_firmware_version) return 0; /* Create the analogue output controls */ From patchwork Tue Dec 26 18:08:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504944 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6692051009 for ; Tue, 26 Dec 2023 18:08:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="edngYPMo" Received: by m.b4.vu (Postfix, from userid 1000) id 9AC1C604B10A; Wed, 27 Dec 2023 04:38:52 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu 9AC1C604B10A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614132; bh=c154WcOmzp/baeTyNKMo6GI+66AqUQCJXonGXdL1MMk=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=edngYPMogD7pjF7a4uBqxe9u19QNjZ2Rhu+Pst/zVgmAW0nh5Xt5X0mbxMwbOrtzH Gr5vOMsOfo3//+2APMdXq+yHF30I4njyys+wHlFqXx9RtR8TVmz7A9et/aYV+raRxw kMMyFKs/F2cJF8pmw/kjIFFEn9hduboL5BbA4Zbwz9SxRjR1YLZ0y305aUSUD4M7vX l5qrneoVlgDH/Y8HLp+9Tdcho0diuPc+xEA9ovMc9ElHqoqSKWUXIsomC2JSG6w6vD qOvHUbG6E5i7JmeThYEtqDWFBgMX8r6t8qsFVtVPNx6o0tOdkOYggoLGdZh18EZ2pW KY1s/6RNJp/Iw== Date: Wed, 27 Dec 2023 04:38:52 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 18/20] ALSA: scarlett2: Add R/O headphone volume control Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: The Scarlett 4i4 Gen 4 adds a R/O headphone volume control in addition to a R/O master volume control (which is already supported). Mark the new scarlett2_notify_volume() function with __always_unused until it gets used when the Gen 4 notification callback function arrays are added. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 82 ++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index c21ea9fc38ab..fea22ae9b0ba 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -332,6 +332,7 @@ enum { SCARLETT2_CONFIG_MUTE_SWITCH, SCARLETT2_CONFIG_SW_HW_SWITCH, SCARLETT2_CONFIG_MASTER_VOLUME, + SCARLETT2_CONFIG_HEADPHONE_VOLUME, SCARLETT2_CONFIG_LEVEL_SWITCH, SCARLETT2_CONFIG_PAD_SWITCH, SCARLETT2_CONFIG_MSD_SWITCH, @@ -784,6 +785,7 @@ struct scarlett2_data { u8 power_status_updated; u8 sync; u8 master_vol; + u8 headphone_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; u8 mute_switch[SCARLETT2_ANALOGUE_MAX]; @@ -809,6 +811,7 @@ struct scarlett2_data { u8 meter_level_map[SCARLETT2_MAX_METERS]; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; + struct snd_kcontrol *headphone_vol_ctl; struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; struct snd_kcontrol *sw_hw_ctls[SCARLETT2_ANALOGUE_MAX]; struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX]; @@ -3324,6 +3327,18 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) private->vol[i] = private->master_vol; } + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_HEADPHONE_VOLUME)) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_HEADPHONE_VOLUME, + 1, &vol); + if (err < 0) + return err; + + private->headphone_vol = clamp(vol + SCARLETT2_VOLUME_BIAS, + 0, SCARLETT2_VOLUME_BIAS); + } + return 0; } @@ -3367,6 +3382,34 @@ static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl, return err; } +static int scarlett2_headphone_volume_ctl_get( + struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + if (private->vol_updated) { + err = scarlett2_update_volumes(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.integer.value[0] = private->headphone_vol; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + static int line_out_remap(struct scarlett2_data *private, int index) { const struct scarlett2_device_info *info = private->info; @@ -3456,6 +3499,17 @@ static const struct snd_kcontrol_new scarlett2_master_volume_ctl = { .tlv = { .p = db_scale_scarlett2_volume } }; +static const struct snd_kcontrol_new scarlett2_headphone_volume_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett2_volume_ctl_info, + .get = scarlett2_headphone_volume_ctl_get, + .private_value = 0, /* max value */ + .tlv = { .p = db_scale_scarlett2_volume } +}; + static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -4806,6 +4860,18 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) return err; } + /* Add R/O headphone volume control */ + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_HEADPHONE_VOLUME)) { + snprintf(s, sizeof(s), "Headphone Playback Volume"); + err = scarlett2_add_new_ctl(mixer, + &scarlett2_headphone_volume_ctl, + 0, 1, s, + &private->headphone_vol_ctl); + if (err < 0) + return err; + } + /* Remaining controls are only applicable if the device * has per-channel line-out volume controls. */ @@ -6265,7 +6331,7 @@ static void scarlett2_notify_sync(struct usb_mixer_interface *mixer) &private->sync_ctl->id); } -/* Notify on monitor change */ +/* Notify on monitor change (Gen 2/3) */ static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; @@ -6286,6 +6352,20 @@ static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer) &private->vol_ctls[i]->id); } +/* Notify on volume change (Gen 4) */ +static __always_unused void scarlett2_notify_volume( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + private->vol_updated = 1; + + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->master_vol_ctl->id); + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->headphone_vol_ctl->id); +} + /* Notify on dim/mute change */ static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer) { From patchwork Tue Dec 26 18:08:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504945 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7B8FB51009 for ; Tue, 26 Dec 2023 18:09:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="Y+yqufER" Received: by m.b4.vu (Postfix, from userid 1000) id BA62B604B093; Wed, 27 Dec 2023 04:38:58 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu BA62B604B093 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614138; bh=baibqTw0pZ8/OAKDWbRg2H2HR2gGC/LPr6nJDl46+/s=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Y+yqufER4qoAwNIaBgPqGVrGm7mRbN7BM/m4GfwFQZOQGQsUnkIfp1H40ctgYzsWU 3l4htEMgYnE6HYVR9iU7Lg9C5uaW8cXRjO0MoAMx8p58HU+o8MbnfwuWCQ5G40qkYp FIU0CiEKkAsRViKuG+rPwNj53n4BF7hVv6b7hdIvNCqW49jzylWAAWNwmlKPWAFlkO l8EPrl5ZxvHtrHBQJNnYQWtCL587U5AQXSToXHgy9Qg1/vIwsktHtKr7KQAWTyNIaK IowIGUJyUcRiHOmzv5BK64t3FJ2uF22LgNBjopZFu5ECl/hRHu8KbWnADa2Chv1R/1 ZEutLZKpAbzwg== Date: Wed, 27 Dec 2023 04:38:58 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 19/20] ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4 Message-ID: References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Add new Focusrite Scarlett Gen 4 USB IDs, notification arrays, config sets, and device info data. Signed-off-by: Geoffrey D. Bennett --- include/uapi/sound/scarlett2.h | 4 +- sound/usb/mixer_quirks.c | 3 + sound/usb/mixer_scarlett2.c | 361 +++++++++++++++++++++++++++++++-- 3 files changed, 351 insertions(+), 17 deletions(-) diff --git a/include/uapi/sound/scarlett2.h b/include/uapi/sound/scarlett2.h index d0ff38ffa154..91467ab0ed70 100644 --- a/include/uapi/sound/scarlett2.h +++ b/include/uapi/sound/scarlett2.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * Focusrite Scarlett 2 Protocol Driver for ALSA - * (including Scarlett 2nd Gen, 3rd Gen, Clarett USB, and Clarett+ - * series products) + * (including Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, and + * Clarett+ series products) * * Copyright (c) 2023 by Geoffrey D. Bennett */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 898bc3baca7b..6ba431d60890 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3420,6 +3420,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */ case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */ case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */ + case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */ + case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */ + case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */ case USB_ID(0x1235, 0x8206): /* Focusrite Clarett 2Pre USB */ case USB_ID(0x1235, 0x8207): /* Focusrite Clarett 4Pre USB */ case USB_ID(0x1235, 0x8208): /* Focusrite Clarett 8Pre USB */ diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index fea22ae9b0ba..89848204ad42 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* * Focusrite Scarlett 2 Protocol Driver for ALSA - * (including Scarlett 2nd Gen, 3rd Gen, Clarett USB, and Clarett+ - * series products) + * (including Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, and + * Clarett+ series products) * * Supported models: * - 6i6/18i8/18i20 Gen 2 * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 + * - Solo/2i2/4i4 Gen 4 * - Clarett 2Pre/4Pre/8Pre USB * - Clarett+ 2Pre/4Pre/8Pre * @@ -68,6 +69,12 @@ * * Support for Clarett 2Pre and 4Pre USB added in Oct 2023. * + * Support for firmware updates added in Dec 2023. + * + * Support for Scarlett Solo/2i2/4i4 Gen 4 added in Dec 2023 (thanks + * to many LinuxMusicians people and to Focusrite for hardware + * donations). + * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes * - mixer-matrix gain stages @@ -78,6 +85,8 @@ * controls * - disable/enable MSD mode * - disable/enable standalone mode + * - input gain, autogain, safe mode + * - direct monitor mixes * * * /--------------\ 18chn 20chn /--------------\ @@ -130,7 +139,7 @@ * \--------------/ * * - * Gen 3 devices have a Mass Storage Device (MSD) mode where a small + * Gen 3/4 devices have a Mass Storage Device (MSD) mode where a small * disk with registration and driver download information is presented * to the host. To access the full functionality of the device without * proprietary software, MSD mode can be disabled by: @@ -302,9 +311,19 @@ struct scarlett2_notification { static void scarlett2_notify_sync(struct usb_mixer_interface *mixer); static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer); static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer); +static void scarlett2_notify_volume(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer); static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer); +static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer); +static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer); static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer); static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer); +static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer); /* Arrays of notification callback functions */ @@ -325,6 +344,48 @@ static const struct scarlett2_notification scarlett3a_notifications[] = { { 0, NULL } }; +static const struct scarlett2_notification scarlett4_solo_notifications[] = { + { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000008, scarlett2_notify_sync }, + { 0x00400000, scarlett2_notify_input_air }, + { 0x00800000, scarlett2_notify_direct_monitor }, + { 0x01000000, scarlett2_notify_input_level }, + { 0x02000000, scarlett2_notify_input_phantom }, + { 0, NULL } +}; + +static const struct scarlett2_notification scarlett4_2i2_notifications[] = { + { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000008, scarlett2_notify_sync }, + { 0x00200000, scarlett2_notify_input_safe }, + { 0x00400000, scarlett2_notify_autogain }, + { 0x00800000, scarlett2_notify_input_air }, + { 0x01000000, scarlett2_notify_direct_monitor }, + { 0x02000000, scarlett2_notify_input_select }, + { 0x04000000, scarlett2_notify_input_level }, + { 0x08000000, scarlett2_notify_input_phantom }, + { 0x10000000, NULL }, /* power status, ignored */ + { 0x40000000, scarlett2_notify_input_gain }, + { 0x80000000, NULL }, /* power status, ignored */ + { 0, NULL } +}; + +static const struct scarlett2_notification scarlett4_4i4_notifications[] = { + { 0x00000001, NULL }, /* ack, gets ignored */ + { 0x00000008, scarlett2_notify_sync }, + { 0x00200000, scarlett2_notify_input_safe }, + { 0x00400000, scarlett2_notify_autogain }, + { 0x00800000, scarlett2_notify_input_air }, + { 0x01000000, scarlett2_notify_input_select }, + { 0x02000000, scarlett2_notify_input_level }, + { 0x04000000, scarlett2_notify_input_phantom }, + { 0x08000000, scarlett2_notify_power_status }, /* power external */ + { 0x20000000, scarlett2_notify_input_gain }, + { 0x40000000, scarlett2_notify_power_status }, /* power status */ + { 0x80000000, scarlett2_notify_volume }, + { 0, NULL } +}; + /* Configuration parameters that can be read and written */ enum { SCARLETT2_CONFIG_DIM_MUTE, @@ -543,6 +604,123 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = { } }; +/* Solo Gen 4 */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { + .notifications = scarlett4_solo_notifications, + .gen4_write_addr = 0xd8, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x47, .size = 8, .activate = 4 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x108, .activate = 12 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x46, .activate = 9, .mute = 1 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x3d, .activate = 10, .mute = 1 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x3e, .activate = 11 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { + .offset = 0x232, .size = 16, .activate = 26 } + } +}; + +/* 2i2 Gen 4 */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = { + .notifications = scarlett4_2i2_notifications, + .gen4_write_addr = 0xfc, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ?? + + [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x14a, .activate = 16 }, + + [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { + .offset = 0x135, .activate = 10 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .offset = 0x137 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x48, .activate = 11, .mute = 1 }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x4b, .activate = 12 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x3c, .activate = 13, .mute = 1 }, + + [SCARLETT2_CONFIG_SAFE_SWITCH] = { + .offset = 0x147, .activate = 14 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x3e, .activate = 15 }, + + [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { + .offset = 0x14b, .activate = 17 }, + + [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { + .offset = 0x14e, .activate = 18 }, + + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { + .offset = 0x2a0, .size = 16, .activate = 36 } + } +}; + +/* 4i4 Gen 4 */ +static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = { + .notifications = scarlett4_4i4_notifications, + .gen4_write_addr = 0x130, + .items = { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x5c, .size = 8, .activate = 4 }, + + [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = { + .offset = 0x13e, .activate = 10 }, + + [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = { + .offset = 0x140 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x5a, .activate = 11, .mute = 1 }, + + [SCARLETT2_CONFIG_INPUT_GAIN] = { + .offset = 0x5e, .activate = 12 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x4e, .activate = 13, .mute = 1 }, + + [SCARLETT2_CONFIG_SAFE_SWITCH] = { + .offset = 0x150, .activate = 14 }, + + [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x50, .activate = 15 }, + + [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = { + .offset = 0x153, .activate = 16 }, + + [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = { + .offset = 0x156, .activate = 17 }, + + [SCARLETT2_CONFIG_MASTER_VOLUME] = { + .offset = 0x32, .size = 16 }, + + [SCARLETT2_CONFIG_HEADPHONE_VOLUME] = { + .offset = 0x3a, .size = 16 }, + + [SCARLETT2_CONFIG_POWER_EXT] = { + .offset = 0x168 }, + + [SCARLETT2_CONFIG_POWER_STATUS] = { + .offset = 0x66 } + } +}; + /* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */ static const struct scarlett2_config_set scarlett2_config_set_clarett = { .notifications = scarlett2_notifications, @@ -1274,6 +1452,160 @@ static const struct scarlett2_device_info s18i20_gen3_info = { } }; +static const struct scarlett2_device_info solo_gen4_info = { + .config_set = &scarlett2_config_set_gen4_solo, + .min_firmware_version = 2115, + + .level_input_count = 1, + .air_input_count = 1, + .air_input_first = 1, + .air_option = 1, + .phantom_count = 1, + .phantom_first = 1, + .inputs_per_phantom = 1, + .direct_monitor = 1, + .dsp_count = 2, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_MIX] = { 8, 6 }, + [SCARLETT2_PORT_TYPE_PCM] = { 2, 4 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_MIX, 4, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 2, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_MIX, 4, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 2, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_MIX, 4, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 2, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { 0, 0, 0 }, + } }, + + .meter_map = { + { 6, 2 }, + { 4, 2 }, + { 8, 4 }, + { 2, 2 }, + { 0, 2 }, + { 0, 0 } + } +}; + +static const struct scarlett2_device_info s2i2_gen4_info = { + .config_set = &scarlett2_config_set_gen4_2i2, + .min_firmware_version = 2115, + + .level_input_count = 2, + .air_input_count = 2, + .air_option = 1, + .phantom_count = 1, + .inputs_per_phantom = 2, + .gain_input_count = 2, + .direct_monitor = 2, + .dsp_count = 2, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_MIX] = { 6, 6 }, + [SCARLETT2_PORT_TYPE_PCM] = { 2, 4 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_MIX, 4, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 2, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_MIX, 4, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 2, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_MIX, 4, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 2, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { 0, 0, 0 }, + } }, + + .meter_map = { + { 6, 2 }, + { 4, 2 }, + { 8, 4 }, + { 2, 2 }, + { 0, 2 }, + { 0, 0 } + } +}; + +static const struct scarlett2_device_info s4i4_gen4_info = { + .config_set = &scarlett2_config_set_gen4_4i4, + .min_firmware_version = 2089, + + .level_input_count = 2, + .air_input_count = 2, + .air_option = 1, + .phantom_count = 2, + .inputs_per_phantom = 1, + .gain_input_count = 2, + .dsp_count = 2, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 6 }, + [SCARLETT2_PORT_TYPE_MIX] = { 8, 12 }, + [SCARLETT2_PORT_TYPE_PCM] = { 6, 6 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_MIX, 10, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_MIX, 10, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_MIX, 10, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { 0, 0, 0 }, + } }, + + .meter_map = { + { 16, 8 }, + { 6, 10 }, + { 0, 6 }, + { 0, 0 } + } +}; + static const struct scarlett2_device_info clarett_2pre_info = { .config_set = &scarlett2_config_set_clarett, .level_input_count = 2, @@ -1451,6 +1783,11 @@ static const struct scarlett2_device_entry scarlett2_devices[] = { { USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" }, { USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" }, + /* Supported Gen 4 devices */ + { USB_ID(0x1235, 0x8218), &solo_gen4_info, "Scarlett Gen 4" }, + { USB_ID(0x1235, 0x8219), &s2i2_gen4_info, "Scarlett Gen 4" }, + { USB_ID(0x1235, 0x821a), &s4i4_gen4_info, "Scarlett Gen 4" }, + /* Supported Clarett USB/Clarett+ devices */ { USB_ID(0x1235, 0x8206), &clarett_2pre_info, "Clarett USB" }, { USB_ID(0x1235, 0x8207), &clarett_4pre_info, "Clarett USB" }, @@ -6353,8 +6690,7 @@ static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer) } /* Notify on volume change (Gen 4) */ -static __always_unused void scarlett2_notify_volume( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_volume(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; @@ -6460,8 +6796,7 @@ static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer) } /* Notify on input select change */ -static __always_unused void scarlett2_notify_input_select( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; @@ -6483,8 +6818,7 @@ static __always_unused void scarlett2_notify_input_select( } /* Notify on input gain change */ -static __always_unused void scarlett2_notify_input_gain( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; @@ -6502,8 +6836,7 @@ static __always_unused void scarlett2_notify_input_gain( } /* Notify on autogain change */ -static __always_unused void scarlett2_notify_autogain( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; @@ -6526,8 +6859,7 @@ static __always_unused void scarlett2_notify_autogain( } /* Notify on input safe switch change */ -static __always_unused void scarlett2_notify_input_safe( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; @@ -6603,8 +6935,7 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer) } /* Notify on power change */ -static __always_unused void scarlett2_notify_power_status( - struct usb_mixer_interface *mixer) +static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer) { struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; From patchwork Tue Dec 26 18:09:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Geoffrey D. Bennett" X-Patchwork-Id: 13504946 Received: from m.b4.vu (m.b4.vu [203.16.231.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6929951009 for ; Tue, 26 Dec 2023 18:09:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=b4.vu Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=b4.vu Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=b4.vu header.i=@b4.vu header.b="Ng2ykSdE" Received: by m.b4.vu (Postfix, from userid 1000) id D8F80604B02C; Wed, 27 Dec 2023 04:39:04 +1030 (ACDT) DKIM-Filter: OpenDKIM Filter v2.11.0 m.b4.vu D8F80604B02C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=b4.vu; s=m1; t=1703614144; bh=dj857lFlWamQkw2gQsiF1QroDaJCRaEaKwYp0lxan9o=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=Ng2ykSdE2IW7fYn1uUUg7FYStgXKcTAm+o4VsrGvgzs6uVGfzy3mDOhMZ8RonmNBN tiC+yCZ24UJMdZeke1bakHWlEHsNA2LMz79PmRCxeXfrGzCIVTW+vSHGmuuHmfMcWW zuaHK3Tt7qJjMjRWdM/nL2jSx+nT3G30+B+vxMPJj9tYQrQOAC6Mk7SfAPPHYgZDsi ZsXTAS04+Q9sfKbZPcCBNVnWOLzgGIOVFSGW2UleXp6kwiu7f/teF7SXsn3/ToD34W eRWppQ+Yzsv5RLZED0fjU8J/aqojt13mujYf1RLyXggPZHRAvW7EExm5wsEUHRpCuI EOFg2DhRd+HIA== Date: Wed, 27 Dec 2023 04:39:04 +1030 From: "Geoffrey D. Bennett" To: Takashi Iwai Cc: Takashi Iwai , alsa-devel@alsa-project.org, linux-sound@vger.kernel.org Subject: [PATCH 20/20] ALSA: scarlett2: Add PCM Input Switch for Solo Gen 4 Message-ID: <8c67c6131c459588ac4edab11e1fbc40a8297328.1703612638.git.g@b4.vu> References: Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: When the Direct button on the Solo Gen 4 is held for 3 seconds, the PCM 1 and 2 inputs are toggled between DSP Outputs 1 and 2, and Mixer Outputs E and F. This patch adds the corresponding ALSA control. Signed-off-by: Geoffrey D. Bennett --- sound/usb/mixer_scarlett2.c | 151 ++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 89848204ad42..0d0d3f1c061e 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -324,6 +324,8 @@ static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer); static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer); static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer); static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer); +static void scarlett2_notify_pcm_input_switch( + struct usb_mixer_interface *mixer); /* Arrays of notification callback functions */ @@ -351,6 +353,7 @@ static const struct scarlett2_notification scarlett4_solo_notifications[] = { { 0x00800000, scarlett2_notify_direct_monitor }, { 0x01000000, scarlett2_notify_input_level }, { 0x02000000, scarlett2_notify_input_phantom }, + { 0x04000000, scarlett2_notify_pcm_input_switch }, { 0, NULL } }; @@ -413,6 +416,7 @@ enum { SCARLETT2_CONFIG_INPUT_LINK_SWITCH, SCARLETT2_CONFIG_POWER_EXT, SCARLETT2_CONFIG_POWER_STATUS, + SCARLETT2_CONFIG_PCM_INPUT_SWITCH, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN, SCARLETT2_CONFIG_COUNT }; @@ -624,6 +628,9 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = { [SCARLETT2_CONFIG_AIR_SWITCH] = { .offset = 0x3e, .activate = 11 }, + [SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = { + .offset = 0x206, .activate = 25 }, + [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = { .offset = 0x232, .size = 16, .activate = 26 } } @@ -955,6 +962,7 @@ struct scarlett2_data { u8 input_gain_updated; u8 autogain_updated; u8 input_safe_updated; + u8 pcm_input_switch_updated; u8 monitor_other_updated; u8 direct_monitor_updated; u8 mux_updated; @@ -979,6 +987,7 @@ struct scarlett2_data { u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX]; u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX]; u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX]; + u8 pcm_input_switch; u8 direct_monitor_switch; u8 speaker_switching_switch; u8 talkback_switch; @@ -1004,6 +1013,7 @@ struct scarlett2_data { struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX]; struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX]; + struct snd_kcontrol *pcm_input_switch_ctl; struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX]; struct snd_kcontrol *direct_monitor_ctl; @@ -3633,6 +3643,101 @@ static const struct snd_kcontrol_new scarlett2_safe_ctl = { .put = scarlett2_safe_ctl_put, }; +/*** PCM Input Control ***/ + +static int scarlett2_update_pcm_input_switch(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + int err; + + private->pcm_input_switch_updated = 0; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH, + 1, &private->pcm_input_switch); + if (err < 0) + return err; + + return 0; +} + +static int scarlett2_pcm_input_switch_ctl_get( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = elem->head.mixer->private_data; + int err = 0; + + mutex_lock(&private->data_mutex); + + if (private->pcm_input_switch_updated) { + err = scarlett2_update_pcm_input_switch(mixer); + if (err < 0) + goto unlock; + } + ucontrol->value.enumerated.item[0] = private->pcm_input_switch; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_pcm_input_switch_ctl_put( + struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct scarlett2_data *private = mixer->private_data; + + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + if (private->hwdep_in_use) { + err = -EBUSY; + goto unlock; + } + + oval = private->pcm_input_switch; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->pcm_input_switch = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH, + 0, val); + if (err == 0) + err = 1; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_pcm_input_switch_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[2] = { + "Direct", "Mixer" + }; + + return snd_ctl_enum_info( + uinfo, 1, 2, values); +} + +static const struct snd_kcontrol_new scarlett2_pcm_input_switch_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_pcm_input_switch_ctl_info, + .get = scarlett2_pcm_input_switch_ctl_get, + .put = scarlett2_pcm_input_switch_ctl_put +}; + /*** Analogue Line Out Volume Controls ***/ /* Update hardware volume controls after receiving notification that @@ -5419,6 +5524,17 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) } } + /* Add PCM Input Switch control */ + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { + err = scarlett2_add_new_ctl( + mixer, &scarlett2_pcm_input_switch_ctl, 0, 1, + "PCM Input Capture Switch", + &private->pcm_input_switch_ctl); + if (err < 0) + return err; + } + return 0; } @@ -6650,6 +6766,13 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err; + if (scarlett2_has_config_item(private, + SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) { + err = scarlett2_update_pcm_input_switch(mixer); + if (err < 0) + return err; + } + err = scarlett2_update_mix(mixer); if (err < 0) return err; @@ -6946,6 +7069,34 @@ static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer) &private->power_status_ctl->id); } +/* Notify on mux change */ +static void scarlett2_notify_mux(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + int i; + + private->mux_updated = 1; + + for (i = 0; i < private->num_mux_dsts; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mux_ctls[i]->id); +} + +/* Notify on PCM input switch change */ +static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + + private->pcm_input_switch_updated = 1; + + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->pcm_input_switch_ctl->id); + + scarlett2_notify_mux(mixer); +} + /* Interrupt callback */ static void scarlett2_notify(struct urb *urb) {