[PATCH V3 00/17] Add Scarlett Gen 3 support
This patch series adds a fixed version of Scarlett Gen 3 support on top of the previous "Refactor Scarlett Gen 2 support" patches 1-15/31.
Two differences from the previous patches 16-31/31:
- Add patch from Takashi fixing scarlett2_add_new_ctl()
- Don't increase MAX_ID_ELEMS
I tested the above changes on the 18i20 Gen 3 and confirmed no crash with 439 controls and MAX_ID_ELEMS 256.
Geoffrey D. Bennett (16): ALSA: usb-audio: scarlett2: Add Gen 3 mixer support ALSA: usb-audio: scarlett2: Add support for "input-other" notify ALSA: usb-audio: scarlett2: Add Gen 3 MSD mode switch ALSA: usb-audio: scarlett2: Move get config above set config ALSA: usb-audio: scarlett2: Allow bit-level access to config ALSA: usb-audio: scarlett2: Add support for Solo and 2i2 Gen 3 ALSA: usb-audio: scarlett2: Add "air" switch support ALSA: usb-audio: scarlett2: Add phantom power switch support ALSA: usb-audio: scarlett2: Add direct monitor support ALSA: usb-audio: scarlett2: Label 18i8 Gen 3 line outputs correctly ALSA: usb-audio: scarlett2: Split up sw_hw_enum_ctl_put() ALSA: usb-audio: scarlett2: Add sw_hw_ctls and mux_ctls ALSA: usb-audio: scarlett2: Update mux controls to allow updates ALSA: usb-audio: scarlett2: Add speaker switching support ALSA: usb-audio: scarlett2: Update get_config to do endian conversion ALSA: usb-audio: scarlett2: Add support for the talkback feature
Takashi Iwai (1): ALSA: usb-audio: scarlett2: Fix wrong resume call
sound/usb/mixer.c | 3 + sound/usb/mixer.h | 1 + sound/usb/mixer_quirks.c | 6 + sound/usb/mixer_scarlett_gen2.c | 1804 ++++++++++++++++++++++++++++--- 4 files changed, 1637 insertions(+), 177 deletions(-)
From: Takashi Iwai tiwai@suse.de
The current way of the scarlett2 mixer code managing the usb_mixer_elem_info object is wrong in two ways: it passes its internal index to the head.id field, and the val_type field is uninitialized. This ended up with the wrong execution at the resume because a bogus unit id is passed wrongly. Also, in the later code extensions, we'll have more mixer elements, and passing the index will overflow the unit id size (of 256).
This patch corrects those issues. It introduces a new value type, USB_MIXER_BESPOKEN, which indicates a non-standard mixer element, and use this type for all scarlett2 mixer elements, as well as initializing the fixed unit id 0 for avoiding the overflow.
Tested-by: Geoffrey D. Bennett g@b4.vu Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.c | 3 +++ sound/usb/mixer.h | 1 + sound/usb/mixer_scarlett_gen2.c | 7 ++++++- 3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..0f578dabd094 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3605,6 +3605,9 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list) struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); int c, err, idx;
+ if (cval->val_type == USB_MIXER_BESPOKEN) + return 0; + if (cval->cmask) { idx = 0; for (c = 0; c < MAX_CHANNELS; c++) { diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index e5a01f17bf3c..ea41e7a1f7bf 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -55,6 +55,7 @@ enum { USB_MIXER_U16, USB_MIXER_S32, USB_MIXER_U32, + USB_MIXER_BESPOKEN, /* non-standard type */ };
typedef void (*usb_mixer_elem_dump_func_t)(struct snd_info_buffer *buffer, diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index dde008ea21d7..c4689c401d6e 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1136,10 +1136,15 @@ static int scarlett2_add_new_ctl(struct usb_mixer_interface *mixer, if (!elem) return -ENOMEM;
+ /* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code + * ignores them for resume and other operations. + * Also, the head.id field is set to 0, as we don't use this field. + */ elem->head.mixer = mixer; elem->control = index; - elem->head.id = index; + elem->head.id = 0; elem->channels = channels; + elem->val_type = USB_MIXER_BESPOKEN;
kctl = snd_ctl_new1(ncontrol, elem); if (!kctl) {
On Tue, 22 Jun 2021 19:00:49 +0200, Geoffrey D. Bennett wrote:
From: Takashi Iwai tiwai@suse.de
The current way of the scarlett2 mixer code managing the usb_mixer_elem_info object is wrong in two ways: it passes its internal index to the head.id field, and the val_type field is uninitialized. This ended up with the wrong execution at the resume because a bogus unit id is passed wrongly. Also, in the later code extensions, we'll have more mixer elements, and passing the index will overflow the unit id size (of 256).
This patch corrects those issues. It introduces a new value type, USB_MIXER_BESPOKEN, which indicates a non-standard mixer element, and use this type for all scarlett2 mixer elements, as well as initializing the fixed unit id 0 for avoiding the overflow.
Tested-by: Geoffrey D. Bennett g@b4.vu Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de
When submitting a patch, you have to your Signed-off-by line even if you are no author. Could you give it? Just reply with a proper SOB, then I'll fix manually.
thanks,
Takashi
On Tue, Jun 22, 2021 at 09:42:10PM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 19:00:49 +0200, Geoffrey D. Bennett wrote:
From: Takashi Iwai tiwai@suse.de
The current way of the scarlett2 mixer code managing the usb_mixer_elem_info object is wrong in two ways: it passes its internal index to the head.id field, and the val_type field is uninitialized. This ended up with the wrong execution at the resume because a bogus unit id is passed wrongly. Also, in the later code extensions, we'll have more mixer elements, and passing the index will overflow the unit id size (of 256).
This patch corrects those issues. It introduces a new value type, USB_MIXER_BESPOKEN, which indicates a non-standard mixer element, and use this type for all scarlett2 mixer elements, as well as initializing the fixed unit id 0 for avoiding the overflow.
Tested-by: Geoffrey D. Bennett g@b4.vu Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de
When submitting a patch, you have to your Signed-off-by line even if you are no author. Could you give it? Just reply with a proper SOB, then I'll fix manually.
Of course. You know how many times I've read Documentation/process/submitting-patches.rst ? Not enough for it to sink in, apparently :). Please append my SOB:
Signed-off-by: Geoffrey D. Bennett g@b4.vu
Thanks, Geoffrey.
On Wed, 23 Jun 2021 03:03:27 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:42:10PM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 19:00:49 +0200, Geoffrey D. Bennett wrote:
From: Takashi Iwai tiwai@suse.de
The current way of the scarlett2 mixer code managing the usb_mixer_elem_info object is wrong in two ways: it passes its internal index to the head.id field, and the val_type field is uninitialized. This ended up with the wrong execution at the resume because a bogus unit id is passed wrongly. Also, in the later code extensions, we'll have more mixer elements, and passing the index will overflow the unit id size (of 256).
This patch corrects those issues. It introduces a new value type, USB_MIXER_BESPOKEN, which indicates a non-standard mixer element, and use this type for all scarlett2 mixer elements, as well as initializing the fixed unit id 0 for avoiding the overflow.
Tested-by: Geoffrey D. Bennett g@b4.vu Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de
When submitting a patch, you have to your Signed-off-by line even if you are no author. Could you give it? Just reply with a proper SOB, then I'll fix manually.
Of course. You know how many times I've read Documentation/process/submitting-patches.rst ? Not enough for it to sink in, apparently :). Please append my SOB:
Signed-off-by: Geoffrey D. Bennett g@b4.vu
OK, now all patches have been merged.
Thanks!
Takashi
On Wednesday, 23 June 2021, 07:39:48 BST, Takashi Iwai tiwai@suse.de wrote:
Of course. You know how many times I've read Documentation/process/submitting-patches.rst ? Not enough for it to sink in, apparently :). Please append my SOB:
Signed-off-by: Geoffrey D. Bennett g@b4.vu
OK, now all patches have been merged.
To avoid this kind of problems, many years ago, I have had (see 'man git-config' for details):
git config format.signoff true
set in my local kernel dev repo clone. This way "git format-patch ..." would automatically add my sign-off for anything I "git format-patch" of . It is easier to remove the line if not needed, or just ignore the line if irrelevant, than to add the line every time. I did that to a few repos which explicitly requires signoff's. (wine is another one).
Back to this series of patches, I think it is appropriate to add Acked-by: Hin-Tak htl10@users.sourceforge.net
or 'Cc: ' as I was involved in packaging and adapting the patch series as for dkms / out-of-tree build for testing, so that I get e-mails, etc if it gets moved about / back-ported, though I was not involved in actually testing /using the patch series.
Regards, Hin-Tak
Add mixer support for the Focusrite Scarlett 4i4, 8i6, 18i8, and 18i20 Gen 3 devices.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 2 files changed, 245 insertions(+), 19 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 37ad77524c0b..df7492594e91 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3060,6 +3060,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */ case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */ case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */ + case USB_ID(0x1235, 0x8212): /* Focusrite Scarlett 4i4 3rd Gen */ + 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 */ err = snd_scarlett_gen2_init(mixer); break;
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index c4689c401d6e..d742c939eed0 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1,8 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Focusrite Scarlett 6i6/18i8/18i20 Gen 2 Driver for ALSA + * Focusrite Scarlett Gen 2/3 Driver for ALSA * - * Copyright (c) 2018-2019 by Geoffrey D. Bennett <g at b4.vu> + * Supported models: + * - 6i6/18i8/18i20 Gen 2 + * - 4i4/8i6/18i8/18i20 Gen 3 + * + * Copyright (c) 2018-2021 by Geoffrey D. Bennett <g at b4.vu> * * Based on the Scarlett (Gen 1) Driver for ALSA: * @@ -19,10 +23,6 @@ * David Henningsson <david.henningsson at canonical.com> */
-/* Mixer Interface for the Focusrite Scarlett 6i6/18i8/18i20 Gen 2 audio - * interface. Based on the Gen 1 driver and rewritten. - */ - /* The protocol was reverse engineered by looking at the communication * between Focusrite Control 2.3.4 and the Focusrite(R) Scarlett 18i20 * (firmware 1083) using usbmon in July-August 2018. @@ -32,13 +32,21 @@ * Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann * for providing usbmon output and testing). * + * Scarlett 4i4/8i6 Gen 3 support added in May 2020 (thanks to Laurent + * Debricon for donating a 4i4 and to Fredrik Unger for providing 8i6 + * usbmon output and testing). + * + * Scarlett 18i8/18i20 Gen 3 support added in June 2020 (thanks to + * Darren Jaeckel, Alex Sedlack, and Clovis Lunel for providing usbmon + * output, protocol traces and testing). + * * Support for loading mixer volume and mux configuration from the * interface during driver initialisation added in May 2021 (thanks to * Vladimir Sadovnikov for figuring out how). * * This ALSA mixer gives access to: * - input, output, mixer-matrix muxes - * - 18x10 mixer-matrix gain stages + * - mixer-matrix gain stages * - gain/volume/mute controls * - level meters * - line/inst level and pad controls @@ -148,21 +156,21 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of level and pad switches */ #define SCARLETT2_LEVEL_SWITCH_MAX 2 -#define SCARLETT2_PAD_SWITCH_MAX 4 +#define SCARLETT2_PAD_SWITCH_MAX 8
/* Maximum number of inputs to the mixer */ -#define SCARLETT2_INPUT_MIX_MAX 18 +#define SCARLETT2_INPUT_MIX_MAX 25
/* Maximum number of outputs from the mixer */ -#define SCARLETT2_OUTPUT_MIX_MAX 10 +#define SCARLETT2_OUTPUT_MIX_MAX 12
/* Maximum size of the data in the USB mux assignment message: - * 18 inputs, 20 outputs, 18 matrix inputs, 8 spare + * 20 inputs, 20 outputs, 25 matrix inputs, 12 spare */ -#define SCARLETT2_MUX_MAX 64 +#define SCARLETT2_MUX_MAX 77
/* Maximum number of meters (sum of output port counts) */ -#define SCARLETT2_MAX_METERS 56 +#define SCARLETT2_MAX_METERS 65
/* Hardware port types: * - None (no input to mux) @@ -256,7 +264,7 @@ static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = #define SCARLETT2_MUX_TABLES 3
/* Maximum number of entries in a mux table */ -#define SCARLETT2_MAX_MUX_ENTRIES 7 +#define SCARLETT2_MAX_MUX_ENTRIES 10
/* One entry within mux_assignment defines the port type and range of * ports to add to the set_mux message. The end of the list is marked @@ -475,12 +483,226 @@ static const struct scarlett2_device_info s18i20_gen2_info = { } }, };
+static const struct scarlett2_device_info s4i4_gen3_info = { + .usb_id = USB_ID(0x1235, 0x8212), + + .level_input_count = 2, + .pad_input_count = 2, + + .line_out_descrs = { + "Monitor L", + "Monitor R", + "Headphones L", + "Headphones R", + }, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 4 }, + [SCARLETT2_PORT_TYPE_MIX] = { 6, 8 }, + [SCARLETT2_PORT_TYPE_PCM] = { 4, 6 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, + { 0, 0, 0 }, + } }, +}; + +static const struct scarlett2_device_info s8i6_gen3_info = { + .usb_id = USB_ID(0x1235, 0x8213), + + .level_input_count = 2, + .pad_input_count = 2, + + .line_out_descrs = { + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", + }, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 6, 4 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_MIX] = { 8, 8 }, + [SCARLETT2_PORT_TYPE_PCM] = { 6, 10 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, + { 0, 0, 0 }, + } }, +}; + +static const struct scarlett2_device_info s18i8_gen3_info = { + .usb_id = USB_ID(0x1235, 0x8214), + + .line_out_hw_vol = 1, + .level_input_count = 2, + .pad_input_count = 2, + + .line_out_descrs = { + "Monitor L", + "Monitor R", + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", + "Alt Monitor L", + "Alt Monitor R", + }, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 8 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, + [SCARLETT2_PORT_TYPE_MIX] = { 10, 20 }, + [SCARLETT2_PORT_TYPE_PCM] = { 8, 20 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, + { SCARLETT2_PORT_TYPE_PCM, 12, 8 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 10, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, + { SCARLETT2_PORT_TYPE_PCM, 12, 4 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_PCM, 10, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, + { 0, 0, 0 }, + } }, +}; + +static const struct scarlett2_device_info s18i20_gen3_info = { + .usb_id = USB_ID(0x1235, 0x8215), + + .line_out_hw_vol = 1, + .level_input_count = 2, + .pad_input_count = 8, + + .line_out_descrs = { + "Monitor 1 L", + "Monitor 1 R", + "Monitor 2 L", + "Monitor 2 R", + NULL, + NULL, + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", + }, + + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 9, 10 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, + [SCARLETT2_PORT_TYPE_MIX] = { 12, 25 }, + [SCARLETT2_PORT_TYPE_PCM] = { 20, 20 }, + }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, + { SCARLETT2_PORT_TYPE_PCM, 10, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, + { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 25 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 12 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, + { SCARLETT2_PORT_TYPE_PCM, 10, 8 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, + { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 25 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 24 }, + { 0, 0, 0 }, + } }, +}; + static const struct scarlett2_device_info *scarlett2_devices[] = { /* Supported Gen 2 devices */ &s6i6_gen2_info, &s18i8_gen2_info, &s18i20_gen2_info,
+ /* Supported Gen 3 devices */ + &s4i4_gen3_info, + &s8i6_gen3_info, + &s18i8_gen3_info, + &s18i20_gen3_info, + /* End of list */ NULL }; @@ -674,7 +896,7 @@ static int scarlett2_usb( if (err != req_buf_size) { usb_audio_err( mixer->chip, - "Scarlett Gen 2 USB request result cmd %x was %d\n", + "Scarlett Gen 2/3 USB request result cmd %x was %d\n", cmd, err); err = -EINVAL; goto unlock; @@ -691,7 +913,7 @@ static int scarlett2_usb( if (err != resp_buf_size) { usb_audio_err( mixer->chip, - "Scarlett Gen 2 USB response result cmd %x was %d " + "Scarlett Gen 2/3 USB response result cmd %x was %d " "expected %d\n", cmd, err, resp_buf_size); err = -EINVAL; @@ -709,7 +931,7 @@ static int scarlett2_usb( resp->pad) { usb_audio_err( mixer->chip, - "Scarlett Gen 2 USB invalid response; " + "Scarlett Gen 2/3 USB invalid response; " "cmd tx/rx %d/%d seq %d/%d size %d/%d " "error %d pad %d\n", le32_to_cpu(req->cmd), le32_to_cpu(resp->cmd), @@ -2490,7 +2712,7 @@ int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer)
if (!(chip->setup & SCARLETT2_ENABLE)) { usb_audio_info(chip, - "Focusrite Scarlett Gen 2 Mixer Driver disabled; " + "Focusrite Scarlett Gen 2/3 Mixer Driver disabled; " "use options snd_usb_audio vid=0x%04x pid=0x%04x " "device_setup=1 to enable and report any issues " "to g@b4.vu", @@ -2500,7 +2722,7 @@ int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer) }
usb_audio_info(chip, - "Focusrite Scarlett Gen 2 Mixer Driver enabled pid=0x%04x", + "Focusrite Scarlett Gen 2/3 Mixer Driver enabled pid=0x%04x", USB_ID_PRODUCT(chip->usb_id));
err = snd_scarlett_gen2_controls_create(mixer);
Some models allow the level and pad settings to be controlled from the front-panel of the device. For these, the device will send an "input-other" notification to prompt the driver to re-read the status of those settings.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 99 ++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 26 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index d742c939eed0..06454d4e58bf 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -318,6 +318,7 @@ struct scarlett2_data { u16 scarlett2_seq; u8 sync_updated; u8 vol_updated; + u8 input_other_updated; u8 sync; u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; @@ -331,6 +332,8 @@ struct scarlett2_data { struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX]; struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT]; + struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; + struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; }; @@ -723,9 +726,10 @@ static int scarlett2_get_port_start_num( /*** USB Interactions ***/
/* Notifications from the interface */ -#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 -#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 -#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000 +#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 +#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 +#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000 +#define SCARLETT2_USB_NOTIFY_INPUT_OTHER 0x00800000
/* Commands for sending/receiving requests/responses */ #define SCARLETT2_USB_CMD_INIT 0 @@ -1745,6 +1749,32 @@ static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = {
/*** Line Level/Instrument Level Switch Controls ***/
+static int scarlett2_update_input_other(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + private->input_other_updated = 0; + + if (info->level_input_count) { + int err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, + info->level_input_count, private->level_switch); + if (err < 0) + return err; + } + + if (info->pad_input_count) { + int err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PAD_SWITCH, + info->pad_input_count, private->pad_switch); + if (err < 0) + return err; + } + + return 0; +} + static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -1759,10 +1789,16 @@ static int scarlett2_level_enum_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;
+ mutex_lock(&private->data_mutex); + if (private->input_other_updated) + scarlett2_update_input_other(mixer); ucontrol->value.enumerated.item[0] = private->level_switch[elem->control]; + mutex_unlock(&private->data_mutex); + return 0; }
@@ -1811,10 +1847,16 @@ static int scarlett2_pad_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;
+ mutex_lock(&private->data_mutex); + if (private->input_other_updated) + scarlett2_update_input_other(mixer); ucontrol->value.integer.value[0] = private->pad_switch[elem->control]; + mutex_unlock(&private->data_mutex); + return 0; }
@@ -2025,7 +2067,7 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) for (i = 0; i < info->level_input_count; i++) { snprintf(s, sizeof(s), fmt, i + 1, "Level", "Enum"); err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl, - i, 1, s, NULL); + i, 1, s, &private->level_ctls[i]); if (err < 0) return err; } @@ -2034,7 +2076,7 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) for (i = 0; i < info->pad_input_count; i++) { snprintf(s, sizeof(s), fmt, i + 1, "Pad", "Switch"); err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl, - i, 1, s, NULL); + i, 1, s, &private->pad_ctls[i]); if (err < 0) return err; } @@ -2446,25 +2488,9 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) struct scarlett2_usb_volume_status volume_status; int err, i;
- if (info->level_input_count) { - err = scarlett2_usb_get_config( - mixer, - SCARLETT2_CONFIG_LEVEL_SWITCH, - info->level_input_count, - private->level_switch); - if (err < 0) - return err; - } - - if (info->pad_input_count) { - err = scarlett2_usb_get_config( - mixer, - SCARLETT2_CONFIG_PAD_SWITCH, - info->pad_input_count, - private->pad_switch); - if (err < 0) - return err; - } + err = scarlett2_update_input_other(mixer); + if (err < 0) + return err;
err = scarlett2_update_sync(mixer); if (err < 0) @@ -2578,6 +2604,25 @@ static void scarlett2_notify_dim_mute( &private->mute_ctls[i]->id); }
+/* Notify on "input other" change (level/pad) */ +static void scarlett2_notify_input_other( + 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; + + private->input_other_updated = 1; + + for (i = 0; i < info->level_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->level_ctls[i]->id); + for (i = 0; i < info->pad_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->pad_ctls[i]->id); +} + /* Interrupt callback */ static void scarlett2_notify(struct urb *urb) { @@ -2596,6 +2641,8 @@ static void scarlett2_notify(struct urb *urb) scarlett2_notify_monitor(mixer); if (data & SCARLETT2_USB_NOTIFY_DIM_MUTE) scarlett2_notify_dim_mute(mixer); + if (data & SCARLETT2_USB_NOTIFY_INPUT_OTHER) + scarlett2_notify_input_other(mixer);
requeue: if (ustatus != -ENOENT &&
Add a control to disable the Gen 3 MSD mode so that the full functionality of the device is available. Don't create the other controls until MSD mode is disabled.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 120 +++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 06454d4e58bf..f35420369042 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -44,12 +44,13 @@ * interface during driver initialisation added in May 2021 (thanks to * Vladimir Sadovnikov for figuring out how). * - * This ALSA mixer gives access to: + * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes * - mixer-matrix gain stages * - gain/volume/mute controls * - level meters * - line/inst level and pad controls + * - disable/enable MSD mode * * <ditaa> * /--------------\ 18chn 20chn /--------------\ @@ -102,6 +103,14 @@ * --------------/ * </ditaa> * + * Gen 3 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: + * - holding down the 48V button for five seconds while powering on + * the device, or + * - using this driver and alsamixer to change the "MSD Mode" setting + * to Off and power-cycling the device */
#include <linux/slab.h> @@ -120,6 +129,9 @@ /* device_setup value to enable */ #define SCARLETT2_ENABLE 0x01
+/* device_setup value to allow turning MSD mode back on */ +#define SCARLETT2_MSD_ENABLE 0x02 + /* some gui mixers can't handle negative ctl values */ #define SCARLETT2_VOLUME_BIAS 127
@@ -279,6 +291,12 @@ struct scarlett2_mux_entry { struct scarlett2_device_info { u32 usb_id; /* USB device identifier */
+ /* Gen 3 devices have an internal MSD mode switch that needs + * to be disabled in order to access the full functionality of + * the device. + */ + u8 has_msd_mode; + /* line out hw volume is sw controlled */ u8 line_out_hw_vol;
@@ -327,6 +345,7 @@ struct scarlett2_data { u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; + u8 msd_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; @@ -489,6 +508,7 @@ static const struct scarlett2_device_info s18i20_gen2_info = { static const struct scarlett2_device_info s4i4_gen3_info = { .usb_id = USB_ID(0x1235, 0x8212),
+ .has_msd_mode = 1, .level_input_count = 2, .pad_input_count = 2,
@@ -530,6 +550,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = { static const struct scarlett2_device_info s8i6_gen3_info = { .usb_id = USB_ID(0x1235, 0x8213),
+ .has_msd_mode = 1, .level_input_count = 2, .pad_input_count = 2,
@@ -578,6 +599,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = { static const struct scarlett2_device_info s18i8_gen3_info = { .usb_id = USB_ID(0x1235, 0x8214),
+ .has_msd_mode = 1, .line_out_hw_vol = 1, .level_input_count = 2, .pad_input_count = 2, @@ -639,6 +661,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = { static const struct scarlett2_device_info s18i20_gen3_info = { .usb_id = USB_ID(0x1235, 0x8215),
+ .has_msd_mode = 1, .line_out_hw_vol = 1, .level_input_count = 2, .pad_input_count = 8, @@ -786,7 +809,8 @@ enum { SCARLETT2_CONFIG_SW_HW_SWITCH = 3, SCARLETT2_CONFIG_LEVEL_SWITCH = 4, SCARLETT2_CONFIG_PAD_SWITCH = 5, - SCARLETT2_CONFIG_COUNT = 6 + SCARLETT2_CONFIG_MSD_SWITCH = 6, + SCARLETT2_CONFIG_COUNT = 7 };
/* Location, size, and activation command number for the configuration @@ -817,6 +841,9 @@ static const struct scarlett2_config
[SCARLETT2_CONFIG_PAD_SWITCH] = { .offset = 0x84, .size = 1, .activate = 8 }, + + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x9d, .size = 1, .activate = 6 }, };
/* proprietary request/response format */ @@ -1016,7 +1043,8 @@ static int scarlett2_usb_set_config( return err;
/* Schedule the change to be written to NVRAM */ - schedule_delayed_work(&private->work, msecs_to_jiffies(2000)); + if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE) + schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
return 0; } @@ -2351,6 +2379,71 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) "Level Meter", NULL); }
+/*** MSD Controls ***/ + +static int scarlett2_msd_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->msd_switch; + return 0; +} + +static int scarlett2_msd_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); + + oval = private->msd_switch; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->msd_switch = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MSD_SWITCH, + 0, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_msd_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_msd_ctl_get, + .put = scarlett2_msd_ctl_put, +}; + +static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + if (!info->has_msd_mode) + return 0; + + /* If MSD mode is off, hide the switch by default */ + if (!private->msd_switch && !(mixer->chip->setup & SCARLETT2_MSD_ENABLE)) + return 0; + + /* Add MSD control */ + return scarlett2_add_new_ctl(mixer, &scarlett2_msd_ctl, + 0, 1, "MSD Mode", NULL); +} + /*** Cleanup/Suspend Callbacks ***/
static void scarlett2_private_free(struct usb_mixer_interface *mixer) @@ -2488,6 +2581,18 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) struct scarlett2_usb_volume_status volume_status; int err, i;
+ if (info->has_msd_mode) { + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_MSD_SWITCH, + 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; + } + err = scarlett2_update_input_other(mixer); if (err < 0) return err; @@ -2710,6 +2815,15 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ /* Create the MSD control */ + err = scarlett2_add_msd_ctl(mixer); + if (err < 0) + return err; + + /* If MSD mode is enabled, don't create any other controls */ + if (((struct scarlett2_data *)mixer->private_data)->msd_switch) + return 0; + /* Create the analogue output controls */ err = scarlett2_add_line_out_ctls(mixer); if (err < 0)
Move scarlett2_usb_get() and scarlett2_usb_get_config() above the functions relating to updating the configuration so that scarlett2_usb_set_config() can call scarlett2_usb_get() in a subsequent patch.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 56 ++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index f35420369042..7a5346c68603 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -985,6 +985,34 @@ static int scarlett2_usb( return err; }
+/* Send a USB message to get data; result placed in *buf */ +static int scarlett2_usb_get( + struct usb_mixer_interface *mixer, + int offset, void *buf, int size) +{ + struct { + __le32 offset; + __le32 size; + } __packed req; + + req.offset = cpu_to_le32(offset); + req.size = cpu_to_le32(size); + return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA, + &req, sizeof(req), buf, size); +} + +/* Send a USB message to get configuration parameters; result placed in *buf */ +static int scarlett2_usb_get_config( + struct usb_mixer_interface *mixer, + int config_item_num, int count, void *buf) +{ + const struct scarlett2_config *config_item = + &scarlett2_config_items[config_item_num]; + int size = config_item->size * count; + + return scarlett2_usb_get(mixer, config_item->offset, buf, size); +} + /* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ static void scarlett2_config_save(struct usb_mixer_interface *mixer) { @@ -1049,34 +1077,6 @@ static int scarlett2_usb_set_config( return 0; }
-/* Send a USB message to get data; result placed in *buf */ -static int scarlett2_usb_get( - struct usb_mixer_interface *mixer, - int offset, void *buf, int size) -{ - struct { - __le32 offset; - __le32 size; - } __packed req; - - req.offset = cpu_to_le32(offset); - req.size = cpu_to_le32(size); - return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA, - &req, sizeof(req), buf, size); -} - -/* Send a USB message to get configuration parameters; result placed in *buf */ -static int scarlett2_usb_get_config( - struct usb_mixer_interface *mixer, - int config_item_num, int count, void *buf) -{ - const struct scarlett2_config *config_item = - &scarlett2_config_items[config_item_num]; - int size = config_item->size * count; - - return scarlett2_usb_get(mixer, config_item->offset, buf, size); -} - /* Send a USB message to get sync status; result placed in *sync */ static int scarlett2_usb_get_sync_status( struct usb_mixer_interface *mixer,
Add support for accessing configuration values when multiple values are stored in one byte. Needed by the upcoming Solo and 2i2 Gen 3 support.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 68 ++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 13 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 7a5346c68603..08e7b687484e 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -814,7 +814,7 @@ enum { };
/* Location, size, and activation command number for the configuration - * parameters + * parameters. Size is in bits and may be 1, 8, or 16. */ struct scarlett2_config { u8 offset; @@ -825,25 +825,25 @@ struct scarlett2_config { static const struct scarlett2_config scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = { [SCARLETT2_CONFIG_DIM_MUTE] = { - .offset = 0x31, .size = 1, .activate = 2 }, + .offset = 0x31, .size = 8, .activate = 2 },
[SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { - .offset = 0x34, .size = 2, .activate = 1 }, + .offset = 0x34, .size = 16, .activate = 1 },
[SCARLETT2_CONFIG_MUTE_SWITCH] = { - .offset = 0x5c, .size = 1, .activate = 1 }, + .offset = 0x5c, .size = 8, .activate = 1 },
[SCARLETT2_CONFIG_SW_HW_SWITCH] = { - .offset = 0x66, .size = 1, .activate = 3 }, + .offset = 0x66, .size = 8, .activate = 3 },
[SCARLETT2_CONFIG_LEVEL_SWITCH] = { - .offset = 0x7c, .size = 1, .activate = 7 }, + .offset = 0x7c, .size = 8, .activate = 7 },
[SCARLETT2_CONFIG_PAD_SWITCH] = { - .offset = 0x84, .size = 1, .activate = 8 }, + .offset = 0x84, .size = 8, .activate = 8 },
[SCARLETT2_CONFIG_MSD_SWITCH] = { - .offset = 0x9d, .size = 1, .activate = 6 }, + .offset = 0x9d, .size = 8, .activate = 6 }, };
/* proprietary request/response format */ @@ -1008,9 +1008,25 @@ static int scarlett2_usb_get_config( { const struct scarlett2_config *config_item = &scarlett2_config_items[config_item_num]; - int size = config_item->size * count; + int size, err, i; + u8 value;
- return scarlett2_usb_get(mixer, config_item->offset, buf, size); + /* For byte-sized parameters, retrieve directly into buf */ + if (config_item->size >= 8) { + size = config_item->size / 8 * count; + return scarlett2_usb_get(mixer, config_item->offset, buf, size); + } + + /* For bit-sized parameters, retrieve into value */ + err = scarlett2_usb_get(mixer, config_item->offset, &value, 1); + if (err < 0) + return err; + + /* then unpack from value into buf[] */ + for (i = 0; i < 8 && i < count; i++, value >>= 1) + *(u8 *)buf++ = value & 1; + + return 0; }
/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ @@ -1047,18 +1063,44 @@ static int scarlett2_usb_set_config( __le32 value; } __packed req; __le32 req2; + int offset, size; int err; struct scarlett2_data *private = mixer->private_data;
/* Cancel any pending NVRAM save */ cancel_delayed_work_sync(&private->work);
+ /* Convert config_item->size in bits to size in bytes and + * calculate offset + */ + if (config_item->size >= 8) { + size = config_item->size / 8; + offset = config_item->offset + index * size; + + /* If updating a bit, retrieve the old value, set/clear the + * bit as needed, and update value + */ + } else { + u8 tmp; + + size = 1; + offset = config_item->offset; + + scarlett2_usb_get(mixer, offset, &tmp, 1); + if (value) + tmp |= (1 << index); + else + tmp &= ~(1 << index); + + value = tmp; + } + /* Send the configuration parameter data */ - req.offset = cpu_to_le32(config_item->offset + index * config_item->size); - req.bytes = cpu_to_le32(config_item->size); + req.offset = cpu_to_le32(offset); + req.bytes = cpu_to_le32(size); req.value = cpu_to_le32(value); err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA, - &req, sizeof(u32) * 2 + config_item->size, + &req, sizeof(u32) * 2 + size, NULL, 0); if (err < 0) return err;
Add initial support for the Focusrite Scarlett Solo and 2i2 devices: - They have no mixer - They don't support reporting sync status or levels - The configuration space is laid out differently to the other models - There is no level (line/inst) switch on input 1 of the Solo
Co-developed-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_quirks.c | 2 + sound/usb/mixer_scarlett_gen2.c | 94 ++++++++++++++++++++++++++++----- 2 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index df7492594e91..0a3cb8fd7d00 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3060,6 +3060,8 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x8203): /* Focusrite Scarlett 6i6 2nd Gen */ case USB_ID(0x1235, 0x8204): /* Focusrite Scarlett 18i8 2nd Gen */ case USB_ID(0x1235, 0x8201): /* Focusrite Scarlett 18i20 2nd Gen */ + case USB_ID(0x1235, 0x8211): /* Focusrite Scarlett Solo 3rd Gen */ + case USB_ID(0x1235, 0x8210): /* Focusrite Scarlett 2i2 3rd Gen */ case USB_ID(0x1235, 0x8212): /* Focusrite Scarlett 4i4 3rd Gen */ case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */ case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */ diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 08e7b687484e..0a56211a65ab 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -4,9 +4,10 @@ * * Supported models: * - 6i6/18i8/18i20 Gen 2 - * - 4i4/8i6/18i8/18i20 Gen 3 + * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 * * Copyright (c) 2018-2021 by Geoffrey D. Bennett <g at b4.vu> + * Copyright (c) 2020-2021 by Vladimir Sadovnikov sadko4u@gmail.com * * Based on the Scarlett (Gen 1) Driver for ALSA: * @@ -44,6 +45,9 @@ * interface during driver initialisation added in May 2021 (thanks to * Vladimir Sadovnikov for figuring out how). * + * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander + * Vorona for 2i2 protocol traces). + * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes * - mixer-matrix gain stages @@ -297,6 +301,11 @@ struct scarlett2_device_info { */ u8 has_msd_mode;
+ /* Gen 3 devices without a mixer have a different + * configuration set + */ + u8 has_mixer; + /* line out hw volume is sw controlled */ u8 line_out_hw_vol;
@@ -305,6 +314,9 @@ struct scarlett2_device_info { */ u8 level_input_count;
+ /* the first input with a level control (0-based) */ + u8 level_input_first; + /* the number of analogue inputs with a software switchable * 10dB pad control */ @@ -362,6 +374,7 @@ struct scarlett2_data { static const struct scarlett2_device_info s6i6_gen2_info = { .usb_id = USB_ID(0x1235, 0x8203),
+ .has_mixer = 1, .level_input_count = 2, .pad_input_count = 2,
@@ -407,6 +420,7 @@ static const struct scarlett2_device_info s6i6_gen2_info = { static const struct scarlett2_device_info s18i8_gen2_info = { .usb_id = USB_ID(0x1235, 0x8204),
+ .has_mixer = 1, .level_input_count = 2, .pad_input_count = 4,
@@ -455,6 +469,7 @@ static const struct scarlett2_device_info s18i8_gen2_info = { static const struct scarlett2_device_info s18i20_gen2_info = { .usb_id = USB_ID(0x1235, 0x8201),
+ .has_mixer = 1, .line_out_hw_vol = 1,
.line_out_descrs = { @@ -505,10 +520,26 @@ static const struct scarlett2_device_info s18i20_gen2_info = { } }, };
+static const struct scarlett2_device_info solo_gen3_info = { + .usb_id = USB_ID(0x1235, 0x8211), + + .has_msd_mode = 1, + .level_input_count = 1, + .level_input_first = 1, +}; + +static const struct scarlett2_device_info s2i2_gen3_info = { + .usb_id = USB_ID(0x1235, 0x8210), + + .has_msd_mode = 1, + .level_input_count = 2, +}; + static const struct scarlett2_device_info s4i4_gen3_info = { .usb_id = USB_ID(0x1235, 0x8212),
.has_msd_mode = 1, + .has_mixer = 1, .level_input_count = 2, .pad_input_count = 2,
@@ -551,6 +582,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = { .usb_id = USB_ID(0x1235, 0x8213),
.has_msd_mode = 1, + .has_mixer = 1, .level_input_count = 2, .pad_input_count = 2,
@@ -600,6 +632,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .usb_id = USB_ID(0x1235, 0x8214),
.has_msd_mode = 1, + .has_mixer = 1, .line_out_hw_vol = 1, .level_input_count = 2, .pad_input_count = 2, @@ -662,6 +695,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .usb_id = USB_ID(0x1235, 0x8215),
.has_msd_mode = 1, + .has_mixer = 1, .line_out_hw_vol = 1, .level_input_count = 2, .pad_input_count = 8, @@ -724,6 +758,8 @@ static const struct scarlett2_device_info *scarlett2_devices[] = { &s18i20_gen2_info,
/* Supported Gen 3 devices */ + &solo_gen3_info, + &s2i2_gen3_info, &s4i4_gen3_info, &s8i6_gen3_info, &s18i8_gen3_info, @@ -776,7 +812,7 @@ static int scarlett2_get_port_start_num( #define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31 #define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
-/* volume status is read together (matches scarlett2_config_items[]) */ +/* volume status is read together (matches scarlett2_config_items[1]) */ struct scarlett2_usb_volume_status { /* dim/mute buttons */ u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; @@ -822,8 +858,22 @@ struct scarlett2_config { u8 activate; };
+/* scarlett2_config_items[0] is for devices without a mixer + * scarlett2_config_items[1] is for devices with a mixer + */ static const struct scarlett2_config - scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = { + scarlett2_config_items[2][SCARLETT2_CONFIG_COUNT] = + +/* Devices without a mixer (Solo and 2i2 Gen 3) */ +{ { + [SCARLETT2_CONFIG_MSD_SWITCH] = { + .offset = 0x04, .size = 8, .activate = 6 }, + + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x08, .size = 1, .activate = 7 }, + +/* Devices with a mixer (Gen 2 and all other Gen 3) */ +}, { [SCARLETT2_CONFIG_DIM_MUTE] = { .offset = 0x31, .size = 8, .activate = 2 },
@@ -844,7 +894,7 @@ static const struct scarlett2_config
[SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x9d, .size = 8, .activate = 6 }, -}; +} };
/* proprietary request/response format */ struct scarlett2_usb_packet { @@ -1006,8 +1056,10 @@ static int scarlett2_usb_get_config( struct usb_mixer_interface *mixer, int config_item_num, int count, void *buf) { + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; const struct scarlett2_config *config_item = - &scarlett2_config_items[config_item_num]; + &scarlett2_config_items[info->has_mixer][config_item_num]; int size, err, i; u8 value;
@@ -1055,8 +1107,10 @@ static int scarlett2_usb_set_config( struct usb_mixer_interface *mixer, int config_item_num, int index, int value) { + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; const struct scarlett2_config *config_item = - &scarlett2_config_items[config_item_num]; + &scarlett2_config_items[info->has_mixer][config_item_num]; struct { __le32 offset; __le32 bytes; @@ -1065,7 +1119,6 @@ static int scarlett2_usb_set_config( __le32 req2; int offset, size; int err; - struct scarlett2_data *private = mixer->private_data;
/* Cancel any pending NVRAM save */ cancel_delayed_work_sync(&private->work); @@ -1511,6 +1564,10 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data;
+ /* devices without a mixer also don't support reporting sync status */ + if (!private->info->has_mixer) + return 0; + return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl, 0, 1, "Sync Status", &private->sync_ctl); } @@ -1829,7 +1886,8 @@ static int scarlett2_update_input_other(struct usb_mixer_interface *mixer) if (info->level_input_count) { int err = scarlett2_usb_get_config( mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, - info->level_input_count, private->level_switch); + info->level_input_count + info->level_input_first, + private->level_switch); if (err < 0) return err; } @@ -1861,12 +1919,14 @@ static int scarlett2_level_enum_ctl_get(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 + info->level_input_first;
mutex_lock(&private->data_mutex); if (private->input_other_updated) scarlett2_update_input_other(mixer); - ucontrol->value.enumerated.item[0] = - private->level_switch[elem->control]; + ucontrol->value.enumerated.item[0] = private->level_switch[index]; mutex_unlock(&private->data_mutex);
return 0; @@ -1878,8 +1938,9 @@ static int scarlett2_level_enum_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 index = elem->control + info->level_input_first; int oval, val, err = 0;
mutex_lock(&private->data_mutex); @@ -2135,7 +2196,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input level (line/inst) controls */ for (i = 0; i < info->level_input_count; i++) { - snprintf(s, sizeof(s), fmt, i + 1, "Level", "Enum"); + snprintf(s, sizeof(s), fmt, i + 1 + info->level_input_first, + "Level", "Enum"); err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl, i, 1, s, &private->level_ctls[i]); if (err < 0) @@ -2416,6 +2478,10 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data;
+ /* devices without a mixer also don't support reporting levels */ + if (!private->info->has_mixer) + return 0; + return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, 0, private->num_mux_dsts, "Level Meter", NULL); @@ -2639,6 +2705,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ /* the rest of the configuration is for devices with a mixer */ + if (!info->has_mixer) + return 0; + err = scarlett2_update_sync(mixer); if (err < 0) return err;
Some inputs on Gen 3 models have an "air" feature which can be enabled from the driver or (model-dependent) from the front panel. Add support for getting and setting the state of those switches.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 106 ++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 0a56211a65ab..696d9703436a 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -53,7 +53,7 @@ * - mixer-matrix gain stages * - gain/volume/mute controls * - level meters - * - line/inst level and pad controls + * - line/inst level, pad, and air controls * - disable/enable MSD mode * * <ditaa> @@ -173,6 +173,7 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { /* Maximum number of level and pad switches */ #define SCARLETT2_LEVEL_SWITCH_MAX 2 #define SCARLETT2_PAD_SWITCH_MAX 8 +#define SCARLETT2_AIR_SWITCH_MAX 8
/* Maximum number of inputs to the mixer */ #define SCARLETT2_INPUT_MIX_MAX 25 @@ -322,6 +323,11 @@ struct scarlett2_device_info { */ u8 pad_input_count;
+ /* the number of analogue inputs with a software switchable + * "air" control + */ + u8 air_input_count; + /* additional description for the line out volume controls */ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
@@ -357,6 +363,7 @@ struct scarlett2_data { u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; + u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; u8 msd_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -365,6 +372,7 @@ struct scarlett2_data { struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT]; struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; + struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX]; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; }; @@ -526,6 +534,7 @@ static const struct scarlett2_device_info solo_gen3_info = { .has_msd_mode = 1, .level_input_count = 1, .level_input_first = 1, + .air_input_count = 1, };
static const struct scarlett2_device_info s2i2_gen3_info = { @@ -533,6 +542,7 @@ static const struct scarlett2_device_info s2i2_gen3_info = {
.has_msd_mode = 1, .level_input_count = 2, + .air_input_count = 2, };
static const struct scarlett2_device_info s4i4_gen3_info = { @@ -542,6 +552,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = { .has_mixer = 1, .level_input_count = 2, .pad_input_count = 2, + .air_input_count = 2,
.line_out_descrs = { "Monitor L", @@ -585,6 +596,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = { .has_mixer = 1, .level_input_count = 2, .pad_input_count = 2, + .air_input_count = 2,
.line_out_descrs = { "Headphones 1 L", @@ -636,6 +648,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .line_out_hw_vol = 1, .level_input_count = 2, .pad_input_count = 2, + .air_input_count = 4,
.line_out_descrs = { "Monitor L", @@ -699,6 +712,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .line_out_hw_vol = 1, .level_input_count = 2, .pad_input_count = 8, + .air_input_count = 8,
.line_out_descrs = { "Monitor 1 L", @@ -846,7 +860,8 @@ enum { SCARLETT2_CONFIG_LEVEL_SWITCH = 4, SCARLETT2_CONFIG_PAD_SWITCH = 5, SCARLETT2_CONFIG_MSD_SWITCH = 6, - SCARLETT2_CONFIG_COUNT = 7 + SCARLETT2_CONFIG_AIR_SWITCH = 7, + SCARLETT2_CONFIG_COUNT = 8 };
/* Location, size, and activation command number for the configuration @@ -872,6 +887,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_LEVEL_SWITCH] = { .offset = 0x08, .size = 1, .activate = 7 },
+ [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x09, .size = 1, .activate = 8 }, + /* Devices with a mixer (Gen 2 and all other Gen 3) */ }, { [SCARLETT2_CONFIG_DIM_MUTE] = { @@ -892,6 +910,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_PAD_SWITCH] = { .offset = 0x84, .size = 8, .activate = 8 },
+ [SCARLETT2_CONFIG_AIR_SWITCH] = { + .offset = 0x8c, .size = 8, .activate = 8 }, + [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x9d, .size = 8, .activate = 6 }, } }; @@ -1100,9 +1121,7 @@ static void scarlett2_config_save_work(struct work_struct *work) scarlett2_config_save(private->mixer); }
-/* Send a USB message to set a configuration parameter (volume level, - * sw/hw volume switch, line/inst level switch, or pad switch) - */ +/* Send a USB message to set a SCARLETT2_CONFIG_* parameter */ static int scarlett2_usb_set_config( struct usb_mixer_interface *mixer, int config_item_num, int index, int value) @@ -1900,6 +1919,14 @@ static int scarlett2_update_input_other(struct usb_mixer_interface *mixer) return err; }
+ if (info->air_input_count) { + int err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_AIR_SWITCH, + info->air_input_count, private->air_switch); + if (err < 0) + return err; + } + return 0; }
@@ -2030,6 +2057,61 @@ static const struct snd_kcontrol_new scarlett2_pad_ctl = { .put = scarlett2_pad_ctl_put, };
+/*** Air Switch Controls ***/ + +static int scarlett2_air_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; + + mutex_lock(&private->data_mutex); + if (private->input_other_updated) + scarlett2_update_input_other(mixer); + ucontrol->value.integer.value[0] = private->air_switch[elem->control]; + mutex_unlock(&private->data_mutex); + + return 0; +} + +static int scarlett2_air_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); + + oval = private->air_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->air_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_AIR_SWITCH, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + 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, +}; + /*** Dim/Mute Controls ***/
static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, @@ -2213,6 +2295,15 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) return err; }
+ /* Add input air controls */ + for (i = 0; i < info->air_input_count; i++) { + snprintf(s, sizeof(s), fmt, i + 1, "Air", "Switch"); + err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl, + i, 1, s, &private->air_ctls[i]); + if (err < 0) + return err; + } + return 0; }
@@ -2821,7 +2912,7 @@ static void scarlett2_notify_dim_mute( &private->mute_ctls[i]->id); }
-/* Notify on "input other" change (level/pad) */ +/* Notify on "input other" change (level/pad/air) */ static void scarlett2_notify_input_other( struct usb_mixer_interface *mixer) { @@ -2838,6 +2929,9 @@ static void scarlett2_notify_input_other( for (i = 0; i < info->pad_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->pad_ctls[i]->id); + for (i = 0; i < info->air_input_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->air_ctls[i]->id); }
/* Interrupt callback */
Some inputs on Gen 3 models support software-selectable phantom power. Add support for getting and setting the state of those switches and the "Phantom Power Persistence" switch.
Co-developed-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 197 +++++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 696d9703436a..bc4f29cfb2f3 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -48,12 +48,15 @@ * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander * Vorona for 2i2 protocol traces). * + * Support for phantom power added in May 2021. + * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes * - mixer-matrix gain stages * - gain/volume/mute controls * - level meters * - line/inst level, pad, and air controls + * - phantom power controls * - disable/enable MSD mode * * <ditaa> @@ -174,6 +177,7 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { #define SCARLETT2_LEVEL_SWITCH_MAX 2 #define SCARLETT2_PAD_SWITCH_MAX 8 #define SCARLETT2_AIR_SWITCH_MAX 8 +#define SCARLETT2_PHANTOM_SWITCH_MAX 2
/* Maximum number of inputs to the mixer */ #define SCARLETT2_INPUT_MIX_MAX 25 @@ -328,6 +332,12 @@ struct scarlett2_device_info { */ u8 air_input_count;
+ /* the number of phantom (48V) software switchable controls */ + u8 phantom_count; + + /* the number of inputs each phantom switch controls */ + u8 inputs_per_phantom; + /* additional description for the line out volume controls */ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
@@ -364,6 +374,8 @@ struct scarlett2_data { u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; + u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; + u8 phantom_persistence; u8 msd_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -373,6 +385,7 @@ struct scarlett2_data { struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; 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]; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; }; @@ -535,6 +548,8 @@ static const struct scarlett2_device_info solo_gen3_info = { .level_input_count = 1, .level_input_first = 1, .air_input_count = 1, + .phantom_count = 1, + .inputs_per_phantom = 1, };
static const struct scarlett2_device_info s2i2_gen3_info = { @@ -543,6 +558,8 @@ static const struct scarlett2_device_info s2i2_gen3_info = { .has_msd_mode = 1, .level_input_count = 2, .air_input_count = 2, + .phantom_count = 1, + .inputs_per_phantom = 2, };
static const struct scarlett2_device_info s4i4_gen3_info = { @@ -553,6 +570,8 @@ static const struct scarlett2_device_info s4i4_gen3_info = { .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, + .phantom_count = 1, + .inputs_per_phantom = 2,
.line_out_descrs = { "Monitor L", @@ -597,6 +616,8 @@ static const struct scarlett2_device_info s8i6_gen3_info = { .level_input_count = 2, .pad_input_count = 2, .air_input_count = 2, + .phantom_count = 1, + .inputs_per_phantom = 2,
.line_out_descrs = { "Headphones 1 L", @@ -649,6 +670,8 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .level_input_count = 2, .pad_input_count = 2, .air_input_count = 4, + .phantom_count = 2, + .inputs_per_phantom = 2,
.line_out_descrs = { "Monitor L", @@ -713,6 +736,8 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .level_input_count = 2, .pad_input_count = 8, .air_input_count = 8, + .phantom_count = 2, + .inputs_per_phantom = 4,
.line_out_descrs = { "Monitor 1 L", @@ -861,7 +886,9 @@ enum { SCARLETT2_CONFIG_PAD_SWITCH = 5, SCARLETT2_CONFIG_MSD_SWITCH = 6, SCARLETT2_CONFIG_AIR_SWITCH = 7, - SCARLETT2_CONFIG_COUNT = 8 + SCARLETT2_CONFIG_PHANTOM_SWITCH = 8, + SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 9, + SCARLETT2_CONFIG_COUNT = 10 };
/* Location, size, and activation command number for the configuration @@ -884,6 +911,12 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x04, .size = 8, .activate = 6 },
+ [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { + .offset = 0x05, .size = 8, .activate = 6 }, + + [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x06, .size = 8, .activate = 3 }, + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { .offset = 0x08, .size = 1, .activate = 7 },
@@ -913,8 +946,14 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_AIR_SWITCH] = { .offset = 0x8c, .size = 8, .activate = 8 },
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { + .offset = 0x9c, .size = 1, .activate = 8 }, + [SCARLETT2_CONFIG_MSD_SWITCH] = { .offset = 0x9d, .size = 8, .activate = 6 }, + + [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { + .offset = 0x9e, .size = 8, .activate = 6 }, } };
/* proprietary request/response format */ @@ -1927,6 +1966,20 @@ static int scarlett2_update_input_other(struct usb_mixer_interface *mixer) return err; }
+ if (info->phantom_count) { + int err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, + info->phantom_count, private->phantom_switch); + if (err < 0) + return err; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, + 1, &private->phantom_persistence); + if (err < 0) + return err; + } + return 0; }
@@ -2112,6 +2165,111 @@ static const struct snd_kcontrol_new scarlett2_air_ctl = { .put = scarlett2_air_ctl_put, };
+/*** Phantom Switch Controls ***/ + +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; + + mutex_lock(&private->data_mutex); + if (private->input_other_updated) + scarlett2_update_input_other(mixer); + ucontrol->value.integer.value[0] = + private->phantom_switch[elem->control]; + mutex_unlock(&private->data_mutex); + + return 0; +} + +static int scarlett2_phantom_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); + + oval = private->phantom_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->phantom_switch[index] = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_phantom_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_phantom_ctl_get, + .put = scarlett2_phantom_ctl_put, +}; + +/*** Phantom Persistence Control ***/ + +static int scarlett2_phantom_persistence_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->phantom_persistence; + return 0; +} + +static int scarlett2_phantom_persistence_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); + + oval = private->phantom_persistence; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->phantom_persistence = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_phantom_persistence_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_phantom_persistence_ctl_get, + .put = scarlett2_phantom_persistence_ctl_put, +}; + /*** Dim/Mute Controls ***/
static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, @@ -2275,6 +2433,7 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) int err, i; char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; const char *fmt = "Line In %d %s Capture %s"; + const char *fmt2 = "Line In %d-%d %s Capture %s";
/* Add input level (line/inst) controls */ for (i = 0; i < info->level_input_count; i++) { @@ -2304,6 +2463,39 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) return err; }
+ /* Add input phantom controls */ + if (info->inputs_per_phantom == 1) { + for (i = 0; i < info->phantom_count; i++) { + snprintf(s, sizeof(s), fmt, i + 1, + "Phantom Power", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_phantom_ctl, + i, 1, s, &private->phantom_ctls[i]); + if (err < 0) + return err; + } + } else if (info->inputs_per_phantom > 1) { + for (i = 0; i < info->phantom_count; i++) { + int from = i * info->inputs_per_phantom + 1; + int to = (i + 1) * info->inputs_per_phantom; + + snprintf(s, sizeof(s), fmt2, from, to, + "Phantom Power", "Switch"); + err = scarlett2_add_new_ctl( + mixer, &scarlett2_phantom_ctl, + i, 1, s, &private->phantom_ctls[i]); + if (err < 0) + return err; + } + } + if (info->phantom_count) { + err = scarlett2_add_new_ctl( + mixer, &scarlett2_phantom_persistence_ctl, 0, 1, + "Phantom Power Persistence Capture Switch", NULL); + if (err < 0) + return err; + } + return 0; }
@@ -2932,6 +3124,9 @@ static void scarlett2_notify_input_other( for (i = 0; i < info->air_input_count; i++) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->air_ctls[i]->id); + for (i = 0; i < info->phantom_count; i++) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->phantom_ctls[i]->id); }
/* Interrupt callback */
The Solo and 2i2 devices don't have a mixer but they do have a "direct monitor" switch. Add support for getting and setting the state of this switch.
Co-developed-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 161 ++++++++++++++++++++++++++++++-- 1 file changed, 154 insertions(+), 7 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index bc4f29cfb2f3..2912854f64c1 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -48,7 +48,8 @@ * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander * Vorona for 2i2 protocol traces). * - * Support for phantom power added in May 2021. + * Support for phantom power and direct monitoring added in May-June + * 2021. * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes @@ -56,7 +57,7 @@ * - gain/volume/mute controls * - level meters * - line/inst level, pad, and air controls - * - phantom power controls + * - phantom power and direct monitor controls * - disable/enable MSD mode * * <ditaa> @@ -338,6 +339,11 @@ struct scarlett2_device_info { /* the number of inputs each phantom switch controls */ u8 inputs_per_phantom;
+ /* the number of direct monitor options + * (0 = none, 1 = mono only, 2 = mono/stereo) + */ + u8 direct_monitor; + /* additional description for the line out volume controls */ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
@@ -365,6 +371,7 @@ struct scarlett2_data { u8 sync_updated; u8 vol_updated; u8 input_other_updated; + u8 monitor_other_updated; u8 sync; u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; @@ -376,6 +383,7 @@ struct scarlett2_data { u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; u8 phantom_persistence; + u8 direct_monitor_switch; u8 msd_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -386,6 +394,7 @@ 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 *direct_monitor_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; }; @@ -550,6 +559,7 @@ static const struct scarlett2_device_info solo_gen3_info = { .air_input_count = 1, .phantom_count = 1, .inputs_per_phantom = 1, + .direct_monitor = 1, };
static const struct scarlett2_device_info s2i2_gen3_info = { @@ -560,6 +570,7 @@ static const struct scarlett2_device_info s2i2_gen3_info = { .air_input_count = 2, .phantom_count = 1, .inputs_per_phantom = 2, + .direct_monitor = 2, };
static const struct scarlett2_device_info s4i4_gen3_info = { @@ -824,10 +835,11 @@ static int scarlett2_get_port_start_num( /*** USB Interactions ***/
/* Notifications from the interface */ -#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 -#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 -#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000 -#define SCARLETT2_USB_NOTIFY_INPUT_OTHER 0x00800000 +#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 +#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 +#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000 +#define SCARLETT2_USB_NOTIFY_INPUT_OTHER 0x00800000 +#define SCARLETT2_USB_NOTIFY_MONITOR_OTHER 0x01000000
/* Commands for sending/receiving requests/responses */ #define SCARLETT2_USB_CMD_INIT 0 @@ -888,7 +900,8 @@ enum { SCARLETT2_CONFIG_AIR_SWITCH = 7, SCARLETT2_CONFIG_PHANTOM_SWITCH = 8, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 9, - SCARLETT2_CONFIG_COUNT = 10 + SCARLETT2_CONFIG_DIRECT_MONITOR = 10, + SCARLETT2_CONFIG_COUNT = 11 };
/* Location, size, and activation command number for the configuration @@ -917,6 +930,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { .offset = 0x06, .size = 8, .activate = 3 },
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = { + .offset = 0x07, .size = 8, .activate = 4 }, + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { .offset = 0x08, .size = 1, .activate = 7 },
@@ -2270,6 +2286,112 @@ static const struct snd_kcontrol_new scarlett2_phantom_persistence_ctl = { .put = scarlett2_phantom_persistence_ctl_put, };
+/*** Direct Monitor Control ***/ + +static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + private->monitor_other_updated = 0; + + if (info->direct_monitor) + return scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, + 1, &private->direct_monitor_switch); + + return 0; +} + +static int scarlett2_direct_monitor_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; + + mutex_lock(&private->data_mutex); + if (private->monitor_other_updated) + scarlett2_update_monitor_other(mixer); + ucontrol->value.enumerated.item[0] = private->direct_monitor_switch; + mutex_unlock(&private->data_mutex); + + return 0; +} + +static int scarlett2_direct_monitor_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); + + oval = private->direct_monitor_switch; + val = min(ucontrol->value.enumerated.item[0], 2U); + + if (oval == val) + goto unlock; + + private->direct_monitor_switch = val; + + /* Send switch change to the device */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static int scarlett2_direct_monitor_stereo_enum_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[3] = { + "Off", "Mono", "Stereo" + }; + + return snd_ctl_enum_info(uinfo, 1, 3, values); +} + +/* Direct Monitor for Solo is mono-only and only needs a boolean control + * Direct Monitor for 2i2 is selectable between Off/Mono/Stereo + */ +static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_direct_monitor_ctl_get, + .put = scarlett2_direct_monitor_ctl_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_direct_monitor_stereo_enum_ctl_info, + .get = scarlett2_direct_monitor_ctl_get, + .put = scarlett2_direct_monitor_ctl_put, + } +}; + +static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + if (!info->direct_monitor) + return 0; + + return scarlett2_add_new_ctl( + mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1], + 0, 1, "Direct Monitor Playback Switch", + &private->direct_monitor_ctl); +} + /*** Dim/Mute Controls ***/
static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, @@ -2988,6 +3110,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ err = scarlett2_update_monitor_other(mixer); + if (err < 0) + return err; + /* the rest of the configuration is for devices with a mixer */ if (!info->has_mixer) return 0; @@ -3129,6 +3255,20 @@ static void scarlett2_notify_input_other( &private->phantom_ctls[i]->id); }
+/* Notify on "monitor other" change (direct monitor) */ +static void scarlett2_notify_monitor_other( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + struct snd_card *card = mixer->chip->card; + + private->monitor_other_updated = 1; + + if (private->info->direct_monitor) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->direct_monitor_ctl->id); +} + /* Interrupt callback */ static void scarlett2_notify(struct urb *urb) { @@ -3149,6 +3289,8 @@ static void scarlett2_notify(struct urb *urb) scarlett2_notify_dim_mute(mixer); if (data & SCARLETT2_USB_NOTIFY_INPUT_OTHER) scarlett2_notify_input_other(mixer); + if (data & SCARLETT2_USB_NOTIFY_MONITOR_OTHER) + scarlett2_notify_monitor_other(mixer);
requeue: if (ustatus != -ENOENT && @@ -3255,6 +3397,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ /* Create the direct monitor control */ + err = scarlett2_add_direct_monitor_ctl(mixer); + if (err < 0) + return err; + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0)
The 18i8 Gen 3 analogue 7/8 outputs are identified as line 3/4 on the rear of the unit. Add support for remapping the channel numbers to match the labelling.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 82 ++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 23 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 2912854f64c1..59c9147c5cb5 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -344,6 +344,12 @@ struct scarlett2_device_info { */ u8 direct_monitor;
+ /* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected + * internally to the analogue 7/8 outputs + */ + u8 line_out_remap_enable; + u8 line_out_remap[SCARLETT2_ANALOGUE_MAX]; + /* additional description for the line out volume controls */ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
@@ -684,15 +690,18 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .phantom_count = 2, .inputs_per_phantom = 2,
+ .line_out_remap_enable = 1, + .line_out_remap = { 0, 1, 6, 7, 2, 3, 4, 5 }, + .line_out_descrs = { "Monitor L", "Monitor R", + "Alt Monitor L", + "Alt Monitor R", "Headphones 1 L", "Headphones 1 R", "Headphones 2 L", "Headphones 2 R", - "Alt Monitor L", - "Alt Monitor R", },
.port_count = { @@ -1716,13 +1725,22 @@ static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl, return 0; }
+static int line_out_remap(struct scarlett2_data *private, int index) +{ + const struct scarlett2_device_info *info = private->info; + + if (!info->line_out_remap_enable) + return index; + return info->line_out_remap[index]; +} + static int scarlett2_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 index = elem->control; + int index = line_out_remap(private, elem->control);
mutex_lock(&private->data_mutex); if (private->vol_updated) @@ -1739,7 +1757,7 @@ static int scarlett2_volume_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; - int index = elem->control; + int index = line_out_remap(private, elem->control); int oval, val, err = 0;
mutex_lock(&private->data_mutex); @@ -1795,7 +1813,7 @@ static int scarlett2_mute_ctl_get(struct snd_kcontrol *kctl, { struct usb_mixer_elem_info *elem = kctl->private_data; struct scarlett2_data *private = elem->head.mixer->private_data; - int index = elem->control; + int index = line_out_remap(private, elem->control);
ucontrol->value.integer.value[0] = private->mute_switch[index]; return 0; @@ -1807,7 +1825,7 @@ static int scarlett2_mute_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; - int index = elem->control; + int index = line_out_remap(private, elem->control); int oval, val, err = 0;
mutex_lock(&private->data_mutex); @@ -1854,9 +1872,9 @@ static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl, { struct usb_mixer_elem_info *elem = kctl->private_data; struct scarlett2_data *private = elem->head.mixer->private_data; + int index = line_out_remap(private, elem->control);
- ucontrol->value.enumerated.item[0] = - private->vol_sw_hw_switch[elem->control]; + ucontrol->value.enumerated.item[0] = private->vol_sw_hw_switch[index]; return 0; }
@@ -1892,8 +1910,8 @@ static int scarlett2_sw_hw_enum_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; - - int index = elem->control; + int ctl_index = elem->control; + int index = line_out_remap(private, ctl_index); int oval, val, err = 0;
mutex_lock(&private->data_mutex); @@ -1909,7 +1927,7 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, /* Change access mode to RO (hardware controlled volume) * or RW (software controlled volume) */ - scarlett2_vol_ctl_set_writable(mixer, index, !val); + scarlett2_vol_ctl_set_writable(mixer, ctl_index, !val);
/* Reset volume/mute to master volume/mute */ private->vol[index] = private->master_vol; @@ -2441,13 +2459,16 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl, err = 1;
if (index == SCARLETT2_BUTTON_MUTE) - for (i = 0; i < num_line_out; i++) - if (private->vol_sw_hw_switch[i]) { - private->mute_switch[i] = val; + for (i = 0; i < num_line_out; i++) { + int line_index = line_out_remap(private, i); + + if (private->vol_sw_hw_switch[line_index]) { + private->mute_switch[line_index] = val; snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, &private->mute_ctls[i]->id); } + }
unlock: mutex_unlock(&private->data_mutex); @@ -2486,6 +2507,7 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
/* Add volume controls */ for (i = 0; i < num_line_out; i++) { + int index = line_out_remap(private, i);
/* Fader */ if (info->line_out_descrs[i]) @@ -2516,7 +2538,7 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) /* Make the fader and mute controls read-only if the * SW/HW switch is set to HW */ - if (private->vol_sw_hw_switch[i]) + if (private->vol_sw_hw_switch[index]) scarlett2_vol_ctl_set_writable(mixer, i, 0);
/* SW/HW Switch */ @@ -2765,8 +2787,16 @@ static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl, { struct usb_mixer_elem_info *elem = kctl->private_data; struct scarlett2_data *private = elem->head.mixer->private_data; + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; + int line_out_count = + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; + int index = elem->control; + + if (index < line_out_count) + index = line_out_remap(private, index);
- ucontrol->value.enumerated.item[0] = private->mux[elem->control]; + ucontrol->value.enumerated.item[0] = private->mux[index]; return 0; }
@@ -2776,9 +2806,16 @@ static int scarlett2_mux_src_enum_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; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; + int line_out_count = + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; int index = elem->control; int oval, val, err = 0;
+ if (index < line_out_count) + index = line_out_remap(private, index); + mutex_lock(&private->data_mutex);
oval = private->mux[index]; @@ -3179,6 +3216,7 @@ static void scarlett2_notify_sync( static void scarlett2_notify_monitor( 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; const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; @@ -3195,12 +3233,10 @@ static void scarlett2_notify_monitor( snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &private->master_vol_ctl->id);
- for (i = 0; i < num_line_out; i++) { - if (!private->vol_sw_hw_switch[i]) - continue; - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &private->vol_ctls[i]->id); - } + for (i = 0; i < num_line_out; i++) + if (private->vol_sw_hw_switch[line_out_remap(private, i)]) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->vol_ctls[i]->id); }
/* Notify on dim/mute change */ @@ -3225,7 +3261,7 @@ static void scarlett2_notify_dim_mute( &private->dim_mute_ctls[i]->id);
for (i = 0; i < num_line_out; i++) - if (private->vol_sw_hw_switch[i]) + if (private->vol_sw_hw_switch[line_out_remap(private, i)]) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->mute_ctls[i]->id); }
Split part of scarlett2_sw_hw_enum_ctl_put() out into scarlett2_sw_hw_change() so that the code which actually makes the change is available in its own function. This will be used by the speaker switching support which needs to set the SW/HW switch to HW when speaker switching is enabled.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 46 ++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 18 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 59c9147c5cb5..37e35016db12 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1904,23 +1904,12 @@ static void scarlett2_vol_ctl_set_writable(struct usb_mixer_interface *mixer, &private->mute_ctls[index]->id); }
-static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) +static int scarlett2_sw_hw_change(struct usb_mixer_interface *mixer, + int ctl_index, int val) { - 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 ctl_index = elem->control; int index = line_out_remap(private, ctl_index); - int oval, val, err = 0; - - mutex_lock(&private->data_mutex); - - oval = private->vol_sw_hw_switch[index]; - val = !!ucontrol->value.enumerated.item[0]; - - if (oval == val) - goto unlock; + int err;
private->vol_sw_hw_switch[index] = val;
@@ -1938,18 +1927,39 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, index, private->master_vol - SCARLETT2_VOLUME_BIAS); if (err < 0) - goto unlock; + return err;
/* Set SW mute to current HW mute */ err = scarlett2_usb_set_config( mixer, SCARLETT2_CONFIG_MUTE_SWITCH, index, private->dim_mute[SCARLETT2_BUTTON_MUTE]); if (err < 0) - goto unlock; + return err;
/* Send SW/HW switch change to the device */ - err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, - index, val); + return scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, + index, val); +} + +static int scarlett2_sw_hw_enum_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 ctl_index = elem->control; + int index = line_out_remap(private, ctl_index); + int oval, val, err = 0; + + mutex_lock(&private->data_mutex); + + oval = private->vol_sw_hw_switch[index]; + val = !!ucontrol->value.enumerated.item[0]; + + if (oval == val) + goto unlock; + + err = scarlett2_sw_hw_change(mixer, ctl_index, val); if (err == 0) err = 1;
Save the struct snd_kcontrol pointers for the sw_hw and mux controls. This is in preparation for speaker switching support which needs to be able to update those controls.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 37e35016db12..b3e1cb943c3c 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -394,12 +394,14 @@ struct scarlett2_data { struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_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]; struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT]; struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; 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 *mux_ctls[SCARLETT2_MUX_MAX]; struct snd_kcontrol *direct_monitor_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; @@ -2558,7 +2560,8 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) i + 1); err = scarlett2_add_new_ctl(mixer, &scarlett2_sw_hw_enum_ctl, - i, 1, s, NULL); + i, 1, s, + &private->sw_hw_ctls[i]); if (err < 0) return err; } @@ -2876,7 +2879,8 @@ static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer)
err = scarlett2_add_new_ctl(mixer, &scarlett2_mux_src_enum_ctl, - i, 1, s, NULL); + i, 1, s, + &private->mux_ctls[i]); if (err < 0) return err; }
Enabling/disabling speaker switching will update the mux configuration. To prepare for this, add a private->mux_updated flag and update the scarlett2_mux_src_enum_ctl_get() callback to check it.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index b3e1cb943c3c..279196feb811 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -378,6 +378,7 @@ struct scarlett2_data { u8 vol_updated; u8 input_other_updated; u8 monitor_other_updated; + u8 mux_updated; u8 sync; u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; @@ -1446,6 +1447,8 @@ static int scarlett2_usb_get_mux(struct usb_mixer_interface *mixer)
__le32 data[SCARLETT2_MUX_MAX];
+ private->mux_updated = 0; + req.num = 0; req.count = cpu_to_le16(count);
@@ -2799,7 +2802,8 @@ static int scarlett2_mux_src_enum_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; const struct scarlett2_device_info *info = private->info; const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int line_out_count = @@ -2809,7 +2813,12 @@ static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl, if (index < line_out_count) index = line_out_remap(private, index);
+ mutex_lock(&private->data_mutex); + if (private->mux_updated) + scarlett2_usb_get_mux(mixer); ucontrol->value.enumerated.item[0] = private->mux[index]; + mutex_unlock(&private->data_mutex); + return 0; }
The 18i8 and 18i20 Gen 3 support "speaker switching". Add a Speaker Switch control which can be set to Off/Main/Alt.
When speaker switching is enabled or disabled, the interface may change the state of the Analog Outputs 3 and 4 routing and the global mute button, so use a flag private->speaker_switching_switched to note that those should be checked when the next "monitor other" notification is received.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 248 +++++++++++++++++++++++++++++++- 1 file changed, 241 insertions(+), 7 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 279196feb811..ffa2ee8d034c 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -48,8 +48,8 @@ * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander * Vorona for 2i2 protocol traces). * - * Support for phantom power and direct monitoring added in May-June - * 2021. + * Support for phantom power, direct monitoring, and speaker switching + * added in May-June 2021. * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes @@ -57,7 +57,7 @@ * - gain/volume/mute controls * - level meters * - line/inst level, pad, and air controls - * - phantom power and direct monitor controls + * - phantom power, direct monitor, and speaker switching controls * - disable/enable MSD mode * * <ditaa> @@ -315,6 +315,9 @@ struct scarlett2_device_info { /* line out hw volume is sw controlled */ u8 line_out_hw_vol;
+ /* support for main/alt speaker switching */ + u8 has_speaker_switching; + /* the number of analogue inputs with a software switchable * level control that can be set to line or instrument */ @@ -379,6 +382,7 @@ struct scarlett2_data { u8 input_other_updated; u8 monitor_other_updated; u8 mux_updated; + u8 speaker_switching_switched; u8 sync; u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; @@ -391,6 +395,7 @@ struct scarlett2_data { u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; u8 phantom_persistence; u8 direct_monitor_switch; + u8 speaker_switching_switch; u8 msd_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -404,6 +409,7 @@ struct scarlett2_data { struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; struct snd_kcontrol *direct_monitor_ctl; + struct snd_kcontrol *speaker_switching_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; }; @@ -687,6 +693,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = { .has_msd_mode = 1, .has_mixer = 1, .line_out_hw_vol = 1, + .has_speaker_switching = 1, .level_input_count = 2, .pad_input_count = 2, .air_input_count = 4, @@ -756,6 +763,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .has_msd_mode = 1, .has_mixer = 1, .line_out_hw_vol = 1, + .has_speaker_switching = 1, .level_input_count = 2, .pad_input_count = 8, .air_input_count = 8, @@ -913,7 +921,9 @@ enum { SCARLETT2_CONFIG_PHANTOM_SWITCH = 8, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 9, SCARLETT2_CONFIG_DIRECT_MONITOR = 10, - SCARLETT2_CONFIG_COUNT = 11 + SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 11, + SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 12, + SCARLETT2_CONFIG_COUNT = 13 };
/* Location, size, and activation command number for the configuration @@ -982,6 +992,12 @@ static const struct scarlett2_config
[SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { .offset = 0x9e, .size = 8, .activate = 6 }, + + [SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH] = { + .offset = 0x9f, .size = 1, .activate = 10 }, + + [SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE] = { + .offset = 0xa0, .size = 1, .activate = 10 }, } };
/* proprietary request/response format */ @@ -1862,6 +1878,18 @@ static const struct snd_kcontrol_new scarlett2_mute_ctl = {
/*** HW/SW Volume Switch Controls ***/
+static void scarlett2_sw_hw_ctl_ro(struct scarlett2_data *private, int index) +{ + private->sw_hw_ctls[index]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_WRITE; +} + +static void scarlett2_sw_hw_ctl_rw(struct scarlett2_data *private, int index) +{ + private->sw_hw_ctls[index]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_WRITE; +} + static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { @@ -2325,6 +2353,13 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; + int err; + + /* monitor_other_enable[0] enables speaker switching */ + u8 monitor_other_enable[2]; + + /* monitor_other_switch[0] activates the alternate speakers */ + u8 monitor_other_switch[2];
private->monitor_other_updated = 0;
@@ -2333,6 +2368,26 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, 1, &private->direct_monitor_switch);
+ if (!info->has_speaker_switching) + return 0; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, + 2, monitor_other_enable); + if (err < 0) + return err; + + err = scarlett2_usb_get_config( + mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, + 2, monitor_other_switch); + if (err < 0) + return err; + + if (!monitor_other_enable[0]) + private->speaker_switching_switch = 0; + else + private->speaker_switching_switch = monitor_other_switch[0] + 1; + return 0; }
@@ -2425,6 +2480,151 @@ static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer) &private->direct_monitor_ctl); }
+/*** Speaker Switching Control ***/ + +static int scarlett2_speaker_switch_enum_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[3] = { + "Off", "Main", "Alt" + }; + + return snd_ctl_enum_info(uinfo, 1, 3, values); +} + +static int scarlett2_speaker_switch_enum_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; + + mutex_lock(&private->data_mutex); + if (private->monitor_other_updated) + scarlett2_update_monitor_other(mixer); + ucontrol->value.enumerated.item[0] = private->speaker_switching_switch; + mutex_unlock(&private->data_mutex); + + return 0; +} + +/* when speaker switching gets enabled, switch the main/alt speakers + * to HW volume and disable those controls + */ +static void scarlett2_speaker_switch_enable(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + int i; + + for (i = 0; i < 4; i++) { + int index = line_out_remap(private, i); + + /* switch the main/alt speakers to HW volume */ + if (!private->vol_sw_hw_switch[index]) + scarlett2_sw_hw_change(private->mixer, i, 1); + + /* disable the line out SW/HW switch */ + scarlett2_sw_hw_ctl_ro(private, i); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->sw_hw_ctls[i]->id); + } + + /* when the next monitor-other notify comes in, update the mux + * configuration + */ + private->speaker_switching_switched = 1; +} + +/* when speaker switching gets disabled, reenable the hw/sw controls + * and invalidate the routing + */ +static void scarlett2_speaker_switch_disable(struct usb_mixer_interface *mixer) +{ + struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + int i; + + /* enable the line out SW/HW switch */ + for (i = 0; i < 4; i++) { + scarlett2_sw_hw_ctl_rw(private, i); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->sw_hw_ctls[i]->id); + } + + /* when the next monitor-other notify comes in, update the mux + * configuration + */ + private->speaker_switching_switched = 1; +} + +static int scarlett2_speaker_switch_enum_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); + + oval = private->speaker_switching_switch; + val = min(ucontrol->value.enumerated.item[0], 2U); + + if (oval == val) + goto unlock; + + private->speaker_switching_switch = val; + + /* enable/disable speaker switching */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, + 0, !!val); + if (err < 0) + goto unlock; + + /* if speaker switching is enabled, select main or alt */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, + 0, val == 2); + if (err < 0) + goto unlock; + + /* update controls if speaker switching gets enabled or disabled */ + if (!oval && val) + scarlett2_speaker_switch_enable(mixer); + else if (oval && !val) + scarlett2_speaker_switch_disable(mixer); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_speaker_switch_enum_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_speaker_switch_enum_ctl_info, + .get = scarlett2_speaker_switch_enum_ctl_get, + .put = scarlett2_speaker_switch_enum_ctl_put, +}; + +static int scarlett2_add_speaker_switch_ctl( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + + if (!info->has_speaker_switching) + return 0; + + return scarlett2_add_new_ctl( + mixer, &scarlett2_speaker_switch_enum_ctl, + 0, 1, "Speaker Switching Playback Enum", + &private->speaker_switching_ctl); +} + /*** Dim/Mute Controls ***/
static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, @@ -2567,6 +2767,12 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) &private->sw_hw_ctls[i]); if (err < 0) return err; + + /* Make the switch read-only if the line is + * involved in speaker switching + */ + if (private->speaker_switching_switch && i < 4) + scarlett2_sw_hw_ctl_ro(private, i); } }
@@ -3314,18 +3520,41 @@ static void scarlett2_notify_input_other( &private->phantom_ctls[i]->id); }
-/* Notify on "monitor other" change (direct monitor) */ +/* Notify on "monitor other" change (direct monitor, speaker switching) */ static void scarlett2_notify_monitor_other( struct usb_mixer_interface *mixer) { - struct scarlett2_data *private = mixer->private_data; struct snd_card *card = mixer->chip->card; + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info;
private->monitor_other_updated = 1;
- if (private->info->direct_monitor) + if (info->direct_monitor) { snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->direct_monitor_ctl->id); + return; + } + + if (info->has_speaker_switching) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->speaker_switching_ctl->id); + + /* if speaker switching was recently enabled or disabled, + * invalidate the dim/mute and mux enum controls + */ + if (private->speaker_switching_switched) { + int i; + + scarlett2_notify_dim_mute(mixer); + + private->speaker_switching_switched = 0; + 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); + } }
/* Interrupt callback */ @@ -3461,6 +3690,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ /* Create the speaker switching control */ + err = scarlett2_add_speaker_switch_ctl(mixer); + if (err < 0) + return err; + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0)
For configuration items with a size of 16, scarlett2_usb_get_config() was filling *buf with little-endian data. Update it to convert to CPU endian. This function is not currently used so affects nothing yet; will be used by the upcoming talkback feature.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index ffa2ee8d034c..f26ab6c39859 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1170,7 +1170,13 @@ static int scarlett2_usb_get_config( /* For byte-sized parameters, retrieve directly into buf */ if (config_item->size >= 8) { size = config_item->size / 8 * count; - return scarlett2_usb_get(mixer, config_item->offset, buf, size); + err = scarlett2_usb_get(mixer, config_item->offset, buf, size); + if (err < 0) + return err; + if (size == 2) + for (i = 0; i < count; i++, (u16 *)buf++) + *(u16 *)buf = le16_to_cpu(*(__le16 *)buf); + return 0; }
/* For bit-sized parameters, retrieve into value */
Add support for the talkback feature of the 18i20 Gen 3.
Co-developed-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 229 +++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 7 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index f26ab6c39859..fcba682cd422 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -48,8 +48,8 @@ * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander * Vorona for 2i2 protocol traces). * - * Support for phantom power, direct monitoring, and speaker switching - * added in May-June 2021. + * Support for phantom power, direct monitoring, speaker switching, + * and talkback added in May-June 2021. * * This ALSA mixer gives access to (model-dependent): * - input, output, mixer-matrix muxes @@ -57,7 +57,8 @@ * - gain/volume/mute controls * - level meters * - line/inst level, pad, and air controls - * - phantom power, direct monitor, and speaker switching controls + * - phantom power, direct monitor, speaker switching, and talkback + * controls * - disable/enable MSD mode * * <ditaa> @@ -318,6 +319,9 @@ struct scarlett2_device_info { /* support for main/alt speaker switching */ u8 has_speaker_switching;
+ /* support for talkback microphone */ + u8 has_talkback; + /* the number of analogue inputs with a software switchable * level control that can be set to line or instrument */ @@ -396,6 +400,8 @@ struct scarlett2_data { u8 phantom_persistence; u8 direct_monitor_switch; u8 speaker_switching_switch; + u8 talkback_switch; + u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX]; u8 msd_switch; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; @@ -410,6 +416,7 @@ struct scarlett2_data { struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; struct snd_kcontrol *direct_monitor_ctl; struct snd_kcontrol *speaker_switching_ctl; + struct snd_kcontrol *talkback_ctl; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; }; @@ -764,6 +771,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = { .has_mixer = 1, .line_out_hw_vol = 1, .has_speaker_switching = 1, + .has_talkback = 1, .level_input_count = 2, .pad_input_count = 8, .air_input_count = 8, @@ -923,7 +931,8 @@ enum { SCARLETT2_CONFIG_DIRECT_MONITOR = 10, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 11, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 12, - SCARLETT2_CONFIG_COUNT = 13 + SCARLETT2_CONFIG_TALKBACK_MAP = 13, + SCARLETT2_CONFIG_COUNT = 14 };
/* Location, size, and activation command number for the configuration @@ -998,6 +1007,9 @@ static const struct scarlett2_config
[SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE] = { .offset = 0xa0, .size = 1, .activate = 10 }, + + [SCARLETT2_CONFIG_TALKBACK_MAP] = { + .offset = 0xb0, .size = 16, .activate = 10 }, } };
/* proprietary request/response format */ @@ -2361,10 +2373,14 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) const struct scarlett2_device_info *info = private->info; int err;
- /* monitor_other_enable[0] enables speaker switching */ + /* monitor_other_enable[0] enables speaker switching + * monitor_other_enable[1] enables talkback + */ u8 monitor_other_enable[2];
- /* monitor_other_switch[0] activates the alternate speakers */ + /* monitor_other_switch[0] activates the alternate speakers + * monitor_other_switch[1] activates talkback + */ u8 monitor_other_switch[2];
private->monitor_other_updated = 0; @@ -2374,6 +2390,9 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, 1, &private->direct_monitor_switch);
+ /* if it doesn't do speaker switching then it also doesn't do + * talkback + */ if (!info->has_speaker_switching) return 0;
@@ -2394,6 +2413,26 @@ static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) else private->speaker_switching_switch = monitor_other_switch[0] + 1;
+ if (info->has_talkback) { + const int (*port_count)[SCARLETT2_PORT_DIRNS] = + info->port_count; + int num_mixes = + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; + u16 bitmap; + int i; + + if (!monitor_other_enable[1]) + private->talkback_switch = 0; + else + private->talkback_switch = monitor_other_switch[1] + 1; + + err = scarlett2_usb_get_config(mixer, + SCARLETT2_CONFIG_TALKBACK_MAP, + 1, &bitmap); + for (i = 0; i < num_mixes; i++, bitmap >>= 1) + private->talkback_map[i] = bitmap & 1; + } + return 0; }
@@ -2631,6 +2670,171 @@ static int scarlett2_add_speaker_switch_ctl( &private->speaker_switching_ctl); }
+/*** Talkback and Talkback Map Controls ***/ + +static int scarlett2_talkback_enum_ctl_info( + struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + static const char *const values[3] = { + "Disabled", "Off", "On" + }; + + return snd_ctl_enum_info(uinfo, 1, 3, values); +} + +static int scarlett2_talkback_enum_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; + + mutex_lock(&private->data_mutex); + if (private->monitor_other_updated) + scarlett2_update_monitor_other(mixer); + ucontrol->value.enumerated.item[0] = private->talkback_switch; + mutex_unlock(&private->data_mutex); + + return 0; +} + +static int scarlett2_talkback_enum_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); + + oval = private->talkback_switch; + val = min(ucontrol->value.enumerated.item[0], 2U); + + if (oval == val) + goto unlock; + + private->talkback_switch = val; + + /* enable/disable talkback */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, + 1, !!val); + if (err < 0) + goto unlock; + + /* if talkback is enabled, select main or alt */ + err = scarlett2_usb_set_config( + mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, + 1, val == 2); + if (err < 0) + goto unlock; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_talkback_enum_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett2_talkback_enum_ctl_info, + .get = scarlett2_talkback_enum_ctl_get, + .put = scarlett2_talkback_enum_ctl_put, +}; + +static int scarlett2_talkback_map_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 index = elem->control; + + ucontrol->value.integer.value[0] = private->talkback_map[index]; + + return 0; +} + +static int scarlett2_talkback_map_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; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = + private->info->port_count; + int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; + + int index = elem->control; + int oval, val, err = 0, i; + u16 bitmap = 0; + + mutex_lock(&private->data_mutex); + + oval = private->talkback_map[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->talkback_map[index] = val; + + for (i = 0; i < num_mixes; i++) + bitmap |= private->talkback_map[i] << i; + + /* Send updated bitmap to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_TALKBACK_MAP, + 0, bitmap); + if (err < 0) + goto unlock; + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_talkback_map_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_talkback_map_ctl_get, + .put = scarlett2_talkback_map_ctl_put, +}; + +static int scarlett2_add_talkback_ctls( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; + int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; + int err, i; + char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + if (!info->has_talkback) + return 0; + + err = scarlett2_add_new_ctl( + mixer, &scarlett2_talkback_enum_ctl, + 0, 1, "Talkback Playback Enum", + &private->talkback_ctl); + if (err < 0) + return err; + + for (i = 0; i < num_mixes; i++) { + snprintf(s, sizeof(s), + "Talkback Mix %c Playback Switch", i + 'A'); + err = scarlett2_add_new_ctl(mixer, &scarlett2_talkback_map_ctl, + i, 1, s, NULL); + if (err < 0) + return err; + } + + return 0; +} + /*** Dim/Mute Controls ***/
static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, @@ -3526,7 +3730,9 @@ static void scarlett2_notify_input_other( &private->phantom_ctls[i]->id); }
-/* Notify on "monitor other" change (direct monitor, speaker switching) */ +/* Notify on "monitor other" change (direct monitor, speaker + * switching, talkback) + */ static void scarlett2_notify_monitor_other( struct usb_mixer_interface *mixer) { @@ -3546,6 +3752,10 @@ static void scarlett2_notify_monitor_other( snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->speaker_switching_ctl->id);
+ if (info->has_talkback) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->talkback_ctl->id); + /* if speaker switching was recently enabled or disabled, * invalidate the dim/mute and mux enum controls */ @@ -3701,6 +3911,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ /* Create the talkback controls */ + err = scarlett2_add_talkback_ctls(mixer); + if (err < 0) + return err; + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0)
participants (3)
-
Geoffrey D. Bennett
-
Hin-Tak Leung
-
Takashi Iwai