[PATCH 00/31] Refactor Scarlett Gen 2 support and add Scarlett Gen 3 support
This patch set broadly: - Refactors the Scarlett Gen 2 support to make the Gen 3 mixer support trivial to add - Fixes a couple of minor issues with the Gen 2 support (low priority for stable; the issues were not reported by any user) - Adds support for Gen 3 devices with and without a mixer - Adds support for the major features new with the Gen 3 devices
Geoffrey D. Bennett (31): ALSA: usb-audio: scarlett2: Add usb_tx/rx functions ALSA: usb-audio: scarlett2: Update initialisation sequence ALSA: usb-audio: scarlett2: Fix 6i6 Gen 2 line out descriptions ALSA: usb-audio: scarlett2: Always enable interrupt polling ALSA: usb-audio: scarlett2: Add "Sync Status" control ALSA: usb-audio: scarlett2: Merge common line in capture strings ALSA: usb-audio: scarlett2: Reformat scarlett2_config_items[] ALSA: usb-audio: scarlett2: Improve device info lookup ALSA: usb-audio: scarlett2: Move info lookup out of init function ALSA: usb-audio: scarlett2: Remove repeated device info comments ALSA: usb-audio: scarlett2: Add scarlett2_vol_ctl_write() helper ALSA: usb-audio: scarlett2: Add mute support ALSA: usb-audio: scarlett2: Allow arbitrary ordering of mux entries ALSA: usb-audio: scarlett2: Split struct scarlett2_ports ALSA: usb-audio: scarlett2: Fix Level Meter control 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
sound/usb/mixer.c | 2 +- sound/usb/mixer_quirks.c | 6 + sound/usb/mixer_scarlett_gen2.c | 2725 +++++++++++++++++++++++++------ 3 files changed, 2239 insertions(+), 494 deletions(-)
base-commit: 6c0a2078134aba6a77291554035304df9e16b85c
Pull out snd_usb_ctl_msg() calls from scarlett2_usb() and put into scarlett2_usb_tx() and scarlett2_usb_rx() functions.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 44 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 2e1937b072ee..6b77582d8e3d 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -453,8 +453,8 @@ static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports, #define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000
/* Commands for sending/receiving requests/responses */ -#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ 2 -#define SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP 3 +#define SCARLETT2_USB_CMD_REQ 2 +#define SCARLETT2_USB_CMD_RESP 3
#define SCARLETT2_USB_INIT_SEQ 0x00000000 #define SCARLETT2_USB_GET_METER 0x00001001 @@ -576,12 +576,31 @@ static void scarlett2_fill_request_header(struct scarlett2_data *private, req->pad = 0; }
+static int scarlett2_usb_tx(struct usb_device *dev, int interface, + void *buf, u16 size) +{ + return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), + SCARLETT2_USB_CMD_REQ, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0, interface, buf, size); +} + +static int scarlett2_usb_rx(struct usb_device *dev, int interface, + u32 usb_req, void *buf, u16 size) +{ + return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + usb_req, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, interface, buf, size); +} + /* Send a proprietary format request to the Scarlett interface */ static int scarlett2_usb( struct usb_mixer_interface *mixer, u32 cmd, void *req_data, u16 req_size, void *resp_data, u16 resp_size) { struct scarlett2_data *private = mixer->private_data; + struct usb_device *dev = mixer->chip->dev; u16 req_buf_size = sizeof(struct scarlett2_usb_packet) + req_size; u16 resp_buf_size = sizeof(struct scarlett2_usb_packet) + resp_size; struct scarlett2_usb_packet *req, *resp = NULL; @@ -608,14 +627,8 @@ static int scarlett2_usb( if (req_size) memcpy(req->data, req_data, req_size);
- err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), - SCARLETT2_USB_VENDOR_SPECIFIC_CMD_REQ, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - 0, - private->bInterfaceNumber, - req, - req_buf_size); + err = scarlett2_usb_tx(dev, private->bInterfaceNumber, + req, req_buf_size);
if (err != req_buf_size) { usb_audio_err( @@ -628,14 +641,9 @@ static int scarlett2_usb(
/* send a second message to get the response */
- err = snd_usb_ctl_msg(mixer->chip->dev, - usb_rcvctrlpipe(mixer->chip->dev, 0), - SCARLETT2_USB_VENDOR_SPECIFIC_CMD_RESP, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - 0, - private->bInterfaceNumber, - resp, - resp_buf_size); + err = scarlett2_usb_rx(dev, private->bInterfaceNumber, + SCARLETT2_USB_CMD_RESP, + resp, resp_buf_size);
/* validate the response */
The old initialisation code only works with Gen 2 devices. Replace it with an initialisation sequence that works on both Gen 2 and Gen 3 devices.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 56 ++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 6b77582d8e3d..ed89e28548c8 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -453,10 +453,12 @@ static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports, #define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000
/* Commands for sending/receiving requests/responses */ +#define SCARLETT2_USB_CMD_INIT 0 #define SCARLETT2_USB_CMD_REQ 2 #define SCARLETT2_USB_CMD_RESP 3
-#define SCARLETT2_USB_INIT_SEQ 0x00000000 +#define SCARLETT2_USB_INIT_1 0x00000000 +#define SCARLETT2_USB_INIT_2 0x00000002 #define SCARLETT2_USB_GET_METER 0x00001001 #define SCARLETT2_USB_GET_MIX 0x00002001 #define SCARLETT2_USB_SET_MIX 0x00002002 @@ -650,14 +652,19 @@ static int scarlett2_usb( if (err != resp_buf_size) { usb_audio_err( mixer->chip, - "Scarlett Gen 2 USB response result cmd %x was %d\n", - cmd, err); + "Scarlett Gen 2 USB response result cmd %x was %d " + "expected %d\n", + cmd, err, resp_buf_size); err = -EINVAL; goto unlock; }
+ /* cmd/seq/size should match except when initialising + * seq sent = 1, response = 0 + */ if (resp->cmd != req->cmd || - resp->seq != req->seq || + (resp->seq != req->seq && + (le16_to_cpu(req->seq) != 1 || resp->seq != 0)) || resp_size != le16_to_cpu(resp->size) || resp->error || resp->pad) { @@ -675,7 +682,7 @@ static int scarlett2_usb( goto unlock; }
- if (resp_size > 0) + if (resp_data && resp_size > 0) memcpy(resp_data, resp->data, resp_size);
unlock: @@ -1924,13 +1931,12 @@ static int scarlett2_find_fc_interface(struct usb_device *dev, return -EINVAL; }
-/* Initialise private data, sequence number, and get the USB data */ +/* Initialise private data */ static int scarlett2_init_private(struct usb_mixer_interface *mixer, const struct scarlett2_device_info *info) { struct scarlett2_data *private = kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); - int err;
if (!private) return -ENOMEM; @@ -1948,12 +1954,35 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, private->scarlett2_seq = 0; private->mixer = mixer;
- err = scarlett2_find_fc_interface(mixer->chip->dev, private); + return scarlett2_find_fc_interface(mixer->chip->dev, private); +} + +/* Cargo cult proprietary initialisation sequence */ +static int scarlett2_usb_init(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + struct scarlett2_data *private = mixer->private_data; + u8 buf[24]; + int err; + + if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; + + /* step 0 */ + err = scarlett2_usb_rx(dev, private->bInterfaceNumber, + SCARLETT2_USB_CMD_INIT, buf, sizeof(buf)); if (err < 0) return err;
- /* Initialise the sequence number used for the proprietary commands */ - return scarlett2_usb(mixer, SCARLETT2_USB_INIT_SEQ, NULL, 0, NULL, 0); + /* step 1 */ + private->scarlett2_seq = 1; + err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); + if (err < 0) + return err; + + /* step 2 */ + private->scarlett2_seq = 1; + return scarlett2_usb(mixer, SCARLETT2_USB_INIT_2, NULL, 0, NULL, 84); }
/* Read configuration from the interface on start */ @@ -2128,11 +2157,16 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer, { int err;
- /* Initialise private data, sequence number, and get the USB data */ + /* Initialise private data */ err = scarlett2_init_private(mixer, info); if (err < 0) return err;
+ /* Send proprietary USB initialisation sequence */ + err = scarlett2_usb_init(mixer); + if (err < 0) + return err; + /* Read volume levels and controls from the interface */ err = scarlett2_read_configs(mixer); if (err < 0)
There are two headphone outputs, and they map to the four analogue outputs.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index ed89e28548c8..0b1967d93486 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -263,10 +263,10 @@ static const struct scarlett2_device_info s6i6_gen2_info = { .pad_input_count = 2,
.line_out_descrs = { - "Monitor L", - "Monitor R", - "Headphones L", - "Headphones R", + "Headphones 1 L", + "Headphones 1 R", + "Headphones 2 L", + "Headphones 2 R", },
.ports = {
Always enable interrupt polling as every model has some sort of status to report.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 0b1967d93486..620f1e814f0d 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -2059,11 +2059,16 @@ static void scarlett2_notify_monitor( struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_ports *ports = private->info->ports; + const struct scarlett2_device_info *info = private->info; + const struct scarlett2_ports *ports = info->ports; int num_line_out = ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; int i;
+ /* if line_out_hw_vol is 0, there are no controls to update */ + if (!info->line_out_hw_vol) + return; + private->vol_updated = 1;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, @@ -2197,12 +2202,10 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer, if (err < 0) return err;
- /* Set up the interrupt polling if there are hardware buttons */ - if (info->line_out_hw_vol) { - err = scarlett2_init_notify(mixer); - if (err < 0) - return err; - } + /* Set up the interrupt polling */ + err = scarlett2_init_notify(mixer); + if (err < 0) + return err;
return 0; }
Add "Sync Status" control to display the sync locked/unlocked status.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 101 +++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 620f1e814f0d..4c2ae81f94a8 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -237,13 +237,16 @@ struct scarlett2_data { int num_mux_srcs; int num_mux_dsts; u16 scarlett2_seq; + u8 sync_updated; u8 vol_updated; + u8 sync; u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; + struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT]; @@ -448,7 +451,8 @@ static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports,
/*** USB Interactions ***/
-/* Interrupt flags for dim/mute button and monitor changes */ +/* Notifications from the interface */ +#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 #define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 #define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000
@@ -464,6 +468,7 @@ static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports, #define SCARLETT2_USB_SET_MIX 0x00002002 #define SCARLETT2_USB_GET_MUX 0x00003001 #define SCARLETT2_USB_SET_MUX 0x00003002 +#define SCARLETT2_USB_GET_SYNC 0x00006004 #define SCARLETT2_USB_GET_DATA 0x00800000 #define SCARLETT2_USB_SET_DATA 0x00800001 #define SCARLETT2_USB_DATA_CMD 0x00800002 @@ -784,6 +789,23 @@ static int scarlett2_usb_get_config( 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, + u8 *sync) +{ + __le32 data; + int err; + + err = scarlett2_usb(mixer, SCARLETT2_USB_GET_SYNC, + NULL, 0, &data, sizeof(data)); + if (err < 0) + return err; + + *sync = !!data; + return 0; +} + /* Send a USB message to get volume status; result placed in *buf */ static int scarlett2_usb_get_volume_status( struct usb_mixer_interface *mixer, @@ -1109,6 +1131,60 @@ static int scarlett2_add_new_ctl(struct usb_mixer_interface *mixer, return 0; }
+/*** Sync Control ***/ + +/* Update sync control after receiving notification that the status + * has changed + */ +static int scarlett2_update_sync(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + private->sync_updated = 0; + return scarlett2_usb_get_sync_status(mixer, &private->sync); +} + +static int scarlett2_sync_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + static const char *texts[2] = { + "Unlocked", "Locked" + }; + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} + +static int scarlett2_sync_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->sync_updated) + scarlett2_update_sync(mixer); + ucontrol->value.enumerated.item[0] = private->sync; + mutex_unlock(&private->data_mutex); + + return 0; +} + +static const struct snd_kcontrol_new scarlett2_sync_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "", + .info = scarlett2_sync_ctl_info, + .get = scarlett2_sync_ctl_get +}; + +static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl, + 0, 1, "Sync Status", &private->sync_ctl); +} + /*** Analogue Line Out Volume Controls ***/
/* Update hardware volume controls after receiving notification that @@ -2018,6 +2094,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return err; }
+ err = scarlett2_update_sync(mixer); + if (err < 0) + return err; + err = scarlett2_usb_get_volume_status(mixer, &volume_status); if (err < 0) return err; @@ -2054,6 +2134,18 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) return scarlett2_usb_get_mux(mixer); }
+/* Notify on sync change */ +static void scarlett2_notify_sync( + struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + private->sync_updated = 1; + + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->sync_ctl->id); +} + /* Notify on monitor change */ static void scarlett2_notify_monitor( struct usb_mixer_interface *mixer) @@ -2112,6 +2204,8 @@ static void scarlett2_notify(struct urb *urb) goto requeue;
data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); + if (data & SCARLETT2_USB_NOTIFY_SYNC) + scarlett2_notify_sync(mixer); if (data & SCARLETT2_USB_NOTIFY_MONITOR) scarlett2_notify_monitor(mixer); if (data & SCARLETT2_USB_NOTIFY_DIM_MUTE) @@ -2202,6 +2296,11 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer, if (err < 0) return err;
+ /* Create the sync control */ + err = scarlett2_add_sync_ctl(mixer); + if (err < 0) + return err; + /* Set up the interrupt polling */ err = scarlett2_init_notify(mixer); if (err < 0)
Use a common sprintf() format for the mixer element names generated in scarlett2_add_line_in_ctls() in preparation for more of them.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 4c2ae81f94a8..c401b7d56408 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1660,10 +1660,11 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) const struct scarlett2_device_info *info = private->info; int err, i; char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *fmt = "Line In %d %s Capture %s";
/* Add input level (line/inst) controls */ for (i = 0; i < info->level_input_count; i++) { - snprintf(s, sizeof(s), "Line In %d Level Capture Enum", i + 1); + snprintf(s, sizeof(s), fmt, i + 1, "Level", "Enum"); err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl, i, 1, s, NULL); if (err < 0) @@ -1672,7 +1673,7 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input pad controls */ for (i = 0; i < info->pad_input_count; i++) { - snprintf(s, sizeof(s), "Line In %d Pad Capture Switch", i + 1); + snprintf(s, sizeof(s), fmt, i + 1, "Pad", "Switch"); err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl, i, 1, s, NULL); if (err < 0)
Use designated initializers and merge lines in preparation for more configuration items coming soon.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 40 +++++++++------------------------ 1 file changed, 10 insertions(+), 30 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index c401b7d56408..4a36181e61ab 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -523,40 +523,20 @@ struct scarlett2_config {
static const struct scarlett2_config scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = { - /* Dim/Mute Buttons */ - { - .offset = 0x31, - .size = 1, - .activate = 2 - }, + [SCARLETT2_CONFIG_DIM_MUTE] = { + .offset = 0x31, .size = 1, .activate = 2 },
- /* Line Out Volume */ - { - .offset = 0x34, - .size = 2, - .activate = 1 - }, + [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { + .offset = 0x34, .size = 2, .activate = 1 },
- /* SW/HW Volume Switch */ - { - .offset = 0x66, - .size = 1, - .activate = 3 - }, + [SCARLETT2_CONFIG_SW_HW_SWITCH] = { + .offset = 0x66, .size = 1, .activate = 3 },
- /* Level Switch */ - { - .offset = 0x7c, - .size = 1, - .activate = 7 - }, + [SCARLETT2_CONFIG_LEVEL_SWITCH] = { + .offset = 0x7c, .size = 1, .activate = 7 },
- /* Pad Switch */ - { - .offset = 0x84, - .size = 1, - .activate = 8 - } + [SCARLETT2_CONFIG_PAD_SWITCH] = { + .offset = 0x84, .size = 1, .activate = 8 }, };
/* proprietary request/response format */
Add the USB device ID to the scarlett2_device_info struct so that the switch statement which finds the appropriate struct can be replaced with a loop that looks through an array of pointers to those structs.
Suggested-by: Vladimir Sadovnikov sadko4u@gmail.com Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 37 ++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 4a36181e61ab..481ebdd1a0df 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -217,6 +217,7 @@ struct scarlett2_ports { };
struct scarlett2_device_info { + u32 usb_id; /* USB device identifier */ u8 line_out_hw_vol; /* line out hw volume is sw controlled */ u8 level_input_count; /* inputs with level selectable */ u8 pad_input_count; /* inputs with pad selectable */ @@ -257,6 +258,8 @@ struct scarlett2_data { /*** Model-specific data ***/
static const struct scarlett2_device_info s6i6_gen2_info = { + .usb_id = USB_ID(0x1235, 0x8203), + /* The first two analogue inputs can be switched between line * and instrument levels. */ @@ -310,6 +313,8 @@ static const struct scarlett2_device_info s6i6_gen2_info = { };
static const struct scarlett2_device_info s18i8_gen2_info = { + .usb_id = USB_ID(0x1235, 0x8204), + /* The first two analogue inputs can be switched between line * and instrument levels. */ @@ -371,6 +376,8 @@ static const struct scarlett2_device_info s18i8_gen2_info = { };
static const struct scarlett2_device_info s18i20_gen2_info = { + .usb_id = USB_ID(0x1235, 0x8201), + /* The analogue line outputs on the 18i20 can be switched * between software and hardware volume control */ @@ -437,6 +444,16 @@ static const struct scarlett2_device_info s18i20_gen2_info = { }, };
+static const struct scarlett2_device_info *scarlett2_devices[] = { + /* Supported Gen 2 devices */ + &s6i6_gen2_info, + &s18i8_gen2_info, + &s18i20_gen2_info, + + /* End of list */ + NULL +}; + /* get the starting port index number for a given port type/direction */ static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports, int direction, int port_type) @@ -2293,26 +2310,18 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer, int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer) { struct snd_usb_audio *chip = mixer->chip; - const struct scarlett2_device_info *info; + const struct scarlett2_device_info **info = scarlett2_devices; int err;
/* only use UAC_VERSION_2 */ if (!mixer->protocol) return 0;
- switch (chip->usb_id) { - case USB_ID(0x1235, 0x8203): - info = &s6i6_gen2_info; - break; - case USB_ID(0x1235, 0x8204): - info = &s18i8_gen2_info; - break; - case USB_ID(0x1235, 0x8201): - info = &s18i20_gen2_info; - break; - default: /* device not (yet) supported */ + /* find device in scarlett2_devices */ + while (*info && (*info)->usb_id != chip->usb_id) + info++; + if (!*info) return -EINVAL; - }
if (!(chip->setup & SCARLETT2_ENABLE)) { usb_audio_info(chip, @@ -2329,7 +2338,7 @@ int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer) "Focusrite Scarlett Gen 2 Mixer Driver enabled pid=0x%04x", USB_ID_PRODUCT(chip->usb_id));
- err = snd_scarlett_gen2_controls_create(mixer, info); + err = snd_scarlett_gen2_controls_create(mixer, *info); if (err < 0) usb_audio_err(mixer->chip, "Error initialising Scarlett Mixer Driver: %d",
The info variable is not used by snd_scarlett_gen2_init() except to pass it to snd_scarlett_gen2_controls_create(), so move the lookup into that function.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 481ebdd1a0df..5cc4296944f5 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -2249,13 +2249,19 @@ static int scarlett2_init_notify(struct usb_mixer_interface *mixer) return usb_submit_urb(mixer->urb, GFP_KERNEL); }
-static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer, - const struct scarlett2_device_info *info) +static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) { + const struct scarlett2_device_info **info = scarlett2_devices; int err;
+ /* Find device in scarlett2_devices */ + while (*info && (*info)->usb_id != mixer->chip->usb_id) + info++; + if (!*info) + return -EINVAL; + /* Initialise private data */ - err = scarlett2_init_private(mixer, info); + err = scarlett2_init_private(mixer, *info); if (err < 0) return err;
@@ -2310,19 +2316,12 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer, int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer) { struct snd_usb_audio *chip = mixer->chip; - const struct scarlett2_device_info **info = scarlett2_devices; int err;
/* only use UAC_VERSION_2 */ if (!mixer->protocol) return 0;
- /* find device in scarlett2_devices */ - while (*info && (*info)->usb_id != chip->usb_id) - info++; - if (!*info) - return -EINVAL; - if (!(chip->setup & SCARLETT2_ENABLE)) { usb_audio_info(chip, "Focusrite Scarlett Gen 2 Mixer Driver disabled; " @@ -2338,7 +2337,7 @@ int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer) "Focusrite Scarlett Gen 2 Mixer Driver enabled pid=0x%04x", USB_ID_PRODUCT(chip->usb_id));
- err = snd_scarlett_gen2_controls_create(mixer, *info); + err = snd_scarlett_gen2_controls_create(mixer); if (err < 0) usb_audio_err(mixer->chip, "Error initialising Scarlett Mixer Driver: %d",
Document the fields of struct scarlett2_device_info in the definition of the struct, not in each instantiation.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 5cc4296944f5..ded99baa92de 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -218,10 +218,24 @@ struct scarlett2_ports {
struct scarlett2_device_info { u32 usb_id; /* USB device identifier */ - u8 line_out_hw_vol; /* line out hw volume is sw controlled */ - u8 level_input_count; /* inputs with level selectable */ - u8 pad_input_count; /* inputs with pad selectable */ + + /* line out hw volume is sw controlled */ + u8 line_out_hw_vol; + + /* the number of analogue inputs with a software switchable + * level control that can be set to line or instrument + */ + u8 level_input_count; + + /* the number of analogue inputs with a software switchable + * 10dB pad control + */ + u8 pad_input_count; + + /* additional description for the line out volume controls */ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX]; + + /* port count and type data */ struct scarlett2_ports ports[SCARLETT2_PORT_TYPE_COUNT]; };
@@ -260,12 +274,7 @@ struct scarlett2_data { static const struct scarlett2_device_info s6i6_gen2_info = { .usb_id = USB_ID(0x1235, 0x8203),
- /* The first two analogue inputs can be switched between line - * and instrument levels. - */ .level_input_count = 2, - - /* The first two analogue inputs have an optional pad. */ .pad_input_count = 2,
.line_out_descrs = { @@ -315,12 +324,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),
- /* The first two analogue inputs can be switched between line - * and instrument levels. - */ .level_input_count = 2, - - /* The first four analogue inputs have an optional pad. */ .pad_input_count = 4,
.line_out_descrs = { @@ -378,9 +382,6 @@ static const struct scarlett2_device_info s18i8_gen2_info = { static const struct scarlett2_device_info s18i20_gen2_info = { .usb_id = USB_ID(0x1235, 0x8201),
- /* The analogue line outputs on the 18i20 can be switched - * between software and hardware volume control - */ .line_out_hw_vol = 1,
.line_out_descrs = {
Add helper function for setting the read/write status of a volume control. This will simplify the upcoming mute control support.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index ded99baa92de..e156119a21e8 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1344,6 +1344,24 @@ static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl, return 0; }
+static void scarlett2_vol_ctl_set_writable(struct usb_mixer_interface *mixer, + int index, int value) +{ + struct scarlett2_data *private = mixer->private_data; + + /* Set/Clear write bit */ + if (value) + private->vol_ctls[index]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_WRITE; + else + private->vol_ctls[index]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_WRITE; + + /* Notify of write bit change */ + snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, + &private->vol_ctls[index]->id); +} + static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { @@ -1367,12 +1385,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) */ - if (val) - private->vol_ctls[index]->vd[0].access &= - ~SNDRV_CTL_ELEM_ACCESS_WRITE; - else - private->vol_ctls[index]->vd[0].access |= - SNDRV_CTL_ELEM_ACCESS_WRITE; + scarlett2_vol_ctl_set_writable(mixer, index, !val);
/* Reset volume to master volume */ private->vol[index] = private->master_vol; @@ -1384,10 +1397,6 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, if (err < 0) goto unlock;
- /* Notify of RO/RW change */ - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, - &private->vol_ctls[index]->id); - /* Send SW/HW switch change to the device */ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, index, val); @@ -1620,8 +1629,7 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
/* Make the fader read-only if the SW/HW switch is set to HW */ if (private->vol_sw_hw_switch[i]) - private->vol_ctls[i]->vd[0].access &= - ~SNDRV_CTL_ELEM_ACCESS_WRITE; + scarlett2_vol_ctl_set_writable(mixer, i, 0);
/* SW/HW Switch */ if (info->line_out_hw_vol) {
For each analogue output, in addition to the output volume (gain) control, the hardware also has a mute control. Add ALSA mute controls for each analogue output.
If the device has the line_out_hw_vol feature, then the mute control is disabled along with the output volume control when the switch is set to HW.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 170 +++++++++++++++++++++++++++----- 1 file changed, 145 insertions(+), 25 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index e156119a21e8..d30f15d580b5 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -39,7 +39,7 @@ * This ALSA mixer gives access to: * - input, output, mixer-matrix muxes * - 18x10 mixer-matrix gain stages - * - gain/volume controls + * - gain/volume/mute controls * - level meters * - line/inst level and pad controls * @@ -195,7 +195,11 @@ enum { };
/* Dim/Mute buttons on the 18i20 */ -#define SCARLETT2_DIM_MUTE_COUNT 2 +enum { + SCARLETT2_BUTTON_MUTE = 0, + SCARLETT2_BUTTON_DIM = 1, + SCARLETT2_DIM_MUTE_COUNT = 2, +};
static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { "Mute", "Dim" @@ -258,12 +262,14 @@ struct scarlett2_data { u8 master_vol; u8 vol[SCARLETT2_ANALOGUE_MAX]; u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; + u8 mute_switch[SCARLETT2_ANALOGUE_MAX]; u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; struct snd_kcontrol *sync_ctl; struct snd_kcontrol *master_vol_ctl; 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]; u8 mux[SCARLETT2_MUX_MAX]; u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; @@ -509,7 +515,8 @@ struct scarlett2_usb_volume_status { /* actual volume of output inc. dim (-18dB) */ s16 hw_vol[SCARLETT2_ANALOGUE_MAX];
- u8 pad2[SCARLETT2_ANALOGUE_MAX]; + /* internal mute buttons */ + u8 mute_switch[SCARLETT2_ANALOGUE_MAX];
/* sw (0) or hw (1) controlled */ u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; @@ -524,10 +531,11 @@ struct scarlett2_usb_volume_status { enum { SCARLETT2_CONFIG_DIM_MUTE = 0, SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1, - SCARLETT2_CONFIG_SW_HW_SWITCH = 2, - SCARLETT2_CONFIG_LEVEL_SWITCH = 3, - SCARLETT2_CONFIG_PAD_SWITCH = 4, - SCARLETT2_CONFIG_COUNT = 5 + SCARLETT2_CONFIG_MUTE_SWITCH = 2, + SCARLETT2_CONFIG_SW_HW_SWITCH = 3, + SCARLETT2_CONFIG_LEVEL_SWITCH = 4, + SCARLETT2_CONFIG_PAD_SWITCH = 5, + SCARLETT2_CONFIG_COUNT = 6 };
/* Location, size, and activation command number for the configuration @@ -547,6 +555,9 @@ static const struct scarlett2_config [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { .offset = 0x34, .size = 2, .activate = 1 },
+ [SCARLETT2_CONFIG_MUTE_SWITCH] = { + .offset = 0x5c, .size = 1, .activate = 1 }, + [SCARLETT2_CONFIG_SW_HW_SWITCH] = { .offset = 0x66, .size = 1, .activate = 3 },
@@ -1197,6 +1208,7 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) int num_line_out = ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; int err, i; + int mute;
private->vol_updated = 0;
@@ -1208,15 +1220,18 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) volume_status.master_vol + SCARLETT2_VOLUME_BIAS, 0, SCARLETT2_VOLUME_BIAS);
- for (i = 0; i < num_line_out; i++) { - if (private->vol_sw_hw_switch[i]) - private->vol[i] = private->master_vol; - } - if (info->line_out_hw_vol) for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) private->dim_mute[i] = !!volume_status.dim_mute[i];
+ mute = private->dim_mute[SCARLETT2_BUTTON_MUTE]; + + for (i = 0; i < num_line_out; i++) + if (private->vol_sw_hw_switch[i]) { + private->vol[i] = private->master_vol; + private->mute_switch[i] = mute; + } + return 0; }
@@ -1321,6 +1336,55 @@ static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = { .tlv = { .p = db_scale_scarlett2_gain } };
+/*** Mute Switch Controls ***/ + +static int scarlett2_mute_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; + int index = elem->control; + + ucontrol->value.integer.value[0] = private->mute_switch[index]; + return 0; +} + +static int scarlett2_mute_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->mute_switch[index]; + val = !!ucontrol->value.integer.value[0]; + + if (oval == val) + goto unlock; + + private->mute_switch[index] = val; + + /* Send mute change to the device */ + err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MUTE_SWITCH, + index, val); + +unlock: + mutex_unlock(&private->data_mutex); + return err; +} + +static const struct snd_kcontrol_new scarlett2_mute_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = snd_ctl_boolean_mono_info, + .get = scarlett2_mute_ctl_get, + .put = scarlett2_mute_ctl_put, +}; + /*** HW/SW Volume Switch Controls ***/
static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl, @@ -1348,18 +1412,26 @@ static void scarlett2_vol_ctl_set_writable(struct usb_mixer_interface *mixer, int index, int value) { struct scarlett2_data *private = mixer->private_data; + struct snd_card *card = mixer->chip->card;
- /* Set/Clear write bit */ - if (value) + /* Set/Clear write bits */ + if (value) { private->vol_ctls[index]->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - else + private->mute_ctls[index]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_WRITE; + } else { private->vol_ctls[index]->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; + private->mute_ctls[index]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_WRITE; + }
/* Notify of write bit change */ - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &private->vol_ctls[index]->id); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, + &private->mute_ctls[index]->id); }
static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, @@ -1387,8 +1459,9 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, */ scarlett2_vol_ctl_set_writable(mixer, index, !val);
- /* Reset volume to master volume */ + /* Reset volume/mute to master volume/mute */ private->vol[index] = private->master_vol; + private->mute_switch[index] = private->dim_mute[SCARLETT2_BUTTON_MUTE];
/* Set SW volume to current HW volume */ err = scarlett2_usb_set_config( @@ -1397,6 +1470,13 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, if (err < 0) goto unlock;
+ /* 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; + /* Send SW/HW switch change to the device */ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, index, val); @@ -1554,9 +1634,13 @@ static int scarlett2_dim_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; + const struct scarlett2_device_info *info = private->info; + const struct scarlett2_ports *ports = info->ports; + int num_line_out = + ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
int index = elem->control; - int oval, val, err = 0; + int oval, val, err = 0, i;
mutex_lock(&private->data_mutex);
@@ -1574,6 +1658,15 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl, if (err == 0) 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; + snd_ctl_notify(mixer->chip->card, + SNDRV_CTL_EVENT_MASK_INFO, + &private->mute_ctls[i]->id); + } + unlock: mutex_unlock(&private->data_mutex); return err; @@ -1627,7 +1720,20 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) if (err < 0) return err;
- /* Make the fader read-only if the SW/HW switch is set to HW */ + /* Mute Switch */ + snprintf(s, sizeof(s), + "Line %02d Mute Playback Switch", + i + 1); + err = scarlett2_add_new_ctl(mixer, + &scarlett2_mute_ctl, + i, 1, s, + &private->mute_ctls[i]); + if (err < 0) + return err; + + /* Make the fader and mute controls read-only if the + * SW/HW switch is set to HW + */ if (private->vol_sw_hw_switch[i]) scarlett2_vol_ctl_set_writable(mixer, i, 0);
@@ -2109,12 +2215,16 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) if (err < 0) return err;
+ if (info->line_out_hw_vol) + for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) + private->dim_mute[i] = !!volume_status.dim_mute[i]; + private->master_vol = clamp( volume_status.master_vol + SCARLETT2_VOLUME_BIAS, 0, SCARLETT2_VOLUME_BIAS);
for (i = 0; i < num_line_out; i++) { - int volume; + int volume, mute;
private->vol_sw_hw_switch[i] = info->line_out_hw_vol @@ -2126,11 +2236,12 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) volume = clamp(volume + SCARLETT2_VOLUME_BIAS, 0, SCARLETT2_VOLUME_BIAS); private->vol[i] = volume; - }
- if (info->line_out_hw_vol) - for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) - private->dim_mute[i] = !!volume_status.dim_mute[i]; + mute = private->vol_sw_hw_switch[i] + ? private->dim_mute[SCARLETT2_BUTTON_MUTE] + : volume_status.mute_switch[i]; + private->mute_switch[i] = mute; + }
for (i = 0; i < num_mixer_out; i++) { err = scarlett2_usb_get_mix(mixer, i); @@ -2185,8 +2296,12 @@ static void scarlett2_notify_monitor( static void scarlett2_notify_dim_mute( 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 struct scarlett2_ports *ports = info->ports; + int num_line_out = + ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; int i;
private->vol_updated = 1; @@ -2195,8 +2310,13 @@ static void scarlett2_notify_dim_mute( return;
for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) - snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &private->dim_mute_ctls[i]->id); + + for (i = 0; i < num_line_out; i++) + if (private->vol_sw_hw_switch[i]) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &private->mute_ctls[i]->id); }
/* Interrupt callback */
Some Gen 3 devices do not put all of the mux entries for the same port types together in order in the "set mux" message data. To prepare for this, replace the struct scarlett2_ports num[] array and the assignment_order[] array with mux_assignment[], a list of port types and ranges that is defined in the struct scarlett2_device_info.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 222 ++++++++++++++++++++++---------- 1 file changed, 154 insertions(+), 68 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index d30f15d580b5..b874c0c922d3 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -184,14 +184,11 @@ enum { SCARLETT2_PORT_TYPE_COUNT = 6, };
-/* Count of total I/O and number available at each sample rate */ +/* I/O count of each port type kept in struct scarlett2_ports */ enum { - SCARLETT2_PORT_IN = 0, - SCARLETT2_PORT_OUT = 1, - SCARLETT2_PORT_OUT_44 = 2, - SCARLETT2_PORT_OUT_88 = 3, - SCARLETT2_PORT_OUT_176 = 4, - SCARLETT2_PORT_DIRNS = 5, + SCARLETT2_PORT_IN = 0, + SCARLETT2_PORT_OUT = 1, + SCARLETT2_PORT_DIRNS = 2, };
/* Dim/Mute buttons on the 18i20 */ @@ -220,6 +217,24 @@ struct scarlett2_ports { const char * const dst_descr; };
+/* Number of mux tables: one for each band of sample rates + * (44.1/48kHz, 88.2/96kHz, and 176.4/176kHz) + */ +#define SCARLETT2_MUX_TABLES 3 + +/* Maximum number of entries in a mux table */ +#define SCARLETT2_MAX_MUX_ENTRIES 7 + +/* 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 + * with count == 0. + */ +struct scarlett2_mux_entry { + u8 port_type; + u8 start; + u8 count; +}; + struct scarlett2_device_info { u32 usb_id; /* USB device identifier */
@@ -241,6 +256,10 @@ struct scarlett2_device_info {
/* port count and type data */ struct scarlett2_ports ports[SCARLETT2_PORT_TYPE_COUNT]; + + /* layout/order of the entries in the set_mux message */ + struct scarlett2_mux_entry mux_assignment[SCARLETT2_MUX_TABLES] + [SCARLETT2_MAX_MUX_ENTRIES]; };
struct scarlett2_data { @@ -293,38 +312,61 @@ static const struct scarlett2_device_info s6i6_gen2_info = { .ports = { [SCARLETT2_PORT_TYPE_NONE] = { .id = 0x000, - .num = { 1, 0, 8, 8, 8 }, + .num = { 1, 0 }, .src_descr = "Off", }, [SCARLETT2_PORT_TYPE_ANALOGUE] = { .id = 0x080, - .num = { 4, 4, 4, 4, 4 }, + .num = { 4, 4 }, .src_descr = "Analogue %d", .src_num_offset = 1, .dst_descr = "Analogue Output %02d Playback" }, [SCARLETT2_PORT_TYPE_SPDIF] = { .id = 0x180, - .num = { 2, 2, 2, 2, 2 }, + .num = { 2, 2 }, .src_descr = "S/PDIF %d", .src_num_offset = 1, .dst_descr = "S/PDIF Output %d Playback" }, [SCARLETT2_PORT_TYPE_MIX] = { .id = 0x300, - .num = { 10, 18, 18, 18, 18 }, + .num = { 10, 18 }, .src_descr = "Mix %c", .src_num_offset = 65, .dst_descr = "Mixer Input %02d Capture" }, [SCARLETT2_PORT_TYPE_PCM] = { .id = 0x600, - .num = { 6, 6, 6, 6, 6 }, + .num = { 6, 6 }, .src_descr = "PCM %d", .src_num_offset = 1, .dst_descr = "PCM %02d Capture" }, }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + } }, };
static const struct scarlett2_device_info s18i8_gen2_info = { @@ -345,44 +387,67 @@ static const struct scarlett2_device_info s18i8_gen2_info = { .ports = { [SCARLETT2_PORT_TYPE_NONE] = { .id = 0x000, - .num = { 1, 0, 8, 8, 4 }, + .num = { 1, 0 }, .src_descr = "Off", }, [SCARLETT2_PORT_TYPE_ANALOGUE] = { .id = 0x080, - .num = { 8, 6, 6, 6, 6 }, + .num = { 8, 6 }, .src_descr = "Analogue %d", .src_num_offset = 1, .dst_descr = "Analogue Output %02d Playback" }, [SCARLETT2_PORT_TYPE_SPDIF] = { .id = 0x180, - .num = { 2, 2, 2, 2, 2 }, + .num = { 2, 2 }, .src_descr = "S/PDIF %d", .src_num_offset = 1, .dst_descr = "S/PDIF Output %d Playback" }, [SCARLETT2_PORT_TYPE_ADAT] = { .id = 0x200, - .num = { 8, 0, 0, 0, 0 }, + .num = { 8, 0 }, .src_descr = "ADAT %d", .src_num_offset = 1, }, [SCARLETT2_PORT_TYPE_MIX] = { .id = 0x300, - .num = { 10, 18, 18, 18, 18 }, + .num = { 10, 18 }, .src_descr = "Mix %c", .src_num_offset = 65, .dst_descr = "Mixer Input %02d Capture" }, [SCARLETT2_PORT_TYPE_PCM] = { .id = 0x600, - .num = { 8, 18, 18, 14, 10 }, + .num = { 8, 18 }, .src_descr = "PCM %d", .src_num_offset = 1, .dst_descr = "PCM %02d Capture" }, }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 4 }, + { 0, 0, 0 }, + } }, };
static const struct scarlett2_device_info s18i20_gen2_info = { @@ -406,12 +471,12 @@ static const struct scarlett2_device_info s18i20_gen2_info = { .ports = { [SCARLETT2_PORT_TYPE_NONE] = { .id = 0x000, - .num = { 1, 0, 8, 8, 6 }, + .num = { 1, 0 }, .src_descr = "Off", }, [SCARLETT2_PORT_TYPE_ANALOGUE] = { .id = 0x080, - .num = { 8, 10, 10, 10, 10 }, + .num = { 8, 10 }, .src_descr = "Analogue %d", .src_num_offset = 1, .dst_descr = "Analogue Output %02d Playback" @@ -422,33 +487,58 @@ static const struct scarlett2_device_info s18i20_gen2_info = { * assignment message anyway */ .id = 0x180, - .num = { 2, 2, 2, 2, 2 }, + .num = { 2, 2 }, .src_descr = "S/PDIF %d", .src_num_offset = 1, .dst_descr = "S/PDIF Output %d Playback" }, [SCARLETT2_PORT_TYPE_ADAT] = { .id = 0x200, - .num = { 8, 8, 8, 4, 0 }, + .num = { 8, 8 }, .src_descr = "ADAT %d", .src_num_offset = 1, .dst_descr = "ADAT Output %d Playback" }, [SCARLETT2_PORT_TYPE_MIX] = { .id = 0x300, - .num = { 10, 18, 18, 18, 18 }, + .num = { 10, 18 }, .src_descr = "Mix %c", .src_num_offset = 65, .dst_descr = "Mixer Input %02d Capture" }, [SCARLETT2_PORT_TYPE_PCM] = { .id = 0x600, - .num = { 20, 18, 18, 14, 10 }, + .num = { 20, 18 }, .src_descr = "PCM %d", .src_num_offset = 1, .dst_descr = "PCM %02d Capture" }, }, + + .mux_assignment = { { + { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_ADAT, 0, 4 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, + { 0, 0, 0 }, + }, { + { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, + { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, + { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, + { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, + { SCARLETT2_PORT_TYPE_NONE, 0, 6 }, + { 0, 0, 0 }, + } }, };
static const struct scarlett2_device_info *scarlett2_devices[] = { @@ -1009,16 +1099,7 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; const struct scarlett2_ports *ports = info->ports; - int rate, port_dir_rate; - - static const int assignment_order[SCARLETT2_PORT_TYPE_COUNT] = { - SCARLETT2_PORT_TYPE_PCM, - SCARLETT2_PORT_TYPE_ANALOGUE, - SCARLETT2_PORT_TYPE_SPDIF, - SCARLETT2_PORT_TYPE_ADAT, - SCARLETT2_PORT_TYPE_MIX, - SCARLETT2_PORT_TYPE_NONE, - }; + int table;
struct { __le16 pad; @@ -1028,39 +1109,44 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer)
req.pad = 0;
- /* mux settings for each rate */ - for (rate = 0, port_dir_rate = SCARLETT2_PORT_OUT_44; - port_dir_rate <= SCARLETT2_PORT_OUT_176; - rate++, port_dir_rate++) { - int order_num, i, err; - - req.num = cpu_to_le16(rate); - - for (order_num = 0, i = 0; - order_num < SCARLETT2_PORT_TYPE_COUNT; - order_num++) { - int port_type = assignment_order[order_num]; - int j = scarlett2_get_port_start_num(ports, - SCARLETT2_PORT_OUT, - port_type); - int port_id = ports[port_type].id; - int channel; - - for (channel = 0; - channel < ports[port_type].num[port_dir_rate]; - channel++, i++, j++) - /* lower 12 bits for the destination and - * next 12 bits for the source - */ - req.data[i] = !port_id - ? 0 - : cpu_to_le32( - port_id | - channel | - scarlett2_mux_src_num_to_id( - ports, private->mux[j] - ) << 12 - ); + /* set mux settings for each rate */ + for (table = 0; table < SCARLETT2_MUX_TABLES; table++) { + const struct scarlett2_mux_entry *entry; + + /* i counts over the output array */ + int i = 0, err; + + req.num = cpu_to_le16(table); + + /* loop through each entry */ + for (entry = info->mux_assignment[table]; + entry->count; + entry++) { + int j; + int port_type = entry->port_type; + int port_idx = entry->start; + int mux_idx = scarlett2_get_port_start_num(ports, + SCARLETT2_PORT_OUT, port_type) + port_idx; + int dst_id = ports[port_type].id + port_idx; + + /* Empty slots */ + if (!dst_id) { + for (j = 0; j < entry->count; j++) + req.data[i++] = 0; + continue; + } + + /* Non-empty mux slots use the lower 12 bits + * for the destination and next 12 bits for + * the source + */ + for (j = 0; j < entry->count; j++) { + int src_id = scarlett2_mux_src_num_to_id( + ports, private->mux[mux_idx++]); + req.data[i++] = cpu_to_le32(dst_id | + src_id << 12); + dst_id++; + } }
err = scarlett2_usb(mixer, SCARLETT2_USB_SET_MUX, @@ -2081,7 +2167,7 @@ static void scarlett2_count_mux_io(struct scarlett2_data *private) port_type < SCARLETT2_PORT_TYPE_COUNT; port_type++) { srcs += ports[port_type].num[SCARLETT2_PORT_IN]; - dsts += ports[port_type].num[SCARLETT2_PORT_OUT_44]; + dsts += ports[port_type].num[SCARLETT2_PORT_OUT]; }
private->num_mux_srcs = srcs;
The scarlett2_ports struct contains both generic (hardware IDs and descriptions) and model-specific (port count) data. Remove the generic data from the scarlett2_device_info struct so it is not repeated for every model.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 303 +++++++++++++------------------- 1 file changed, 124 insertions(+), 179 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index b874c0c922d3..7647b3428093 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -203,20 +203,55 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { };
/* Description of each hardware port type: - * - id: hardware ID for this port type - * - num: number of sources/destinations of this port type + * - id: hardware ID of this port type * - src_descr: printf format string for mux input selections * - src_num_offset: added to channel number for the fprintf * - dst_descr: printf format string for mixer controls */ -struct scarlett2_ports { +struct scarlett2_port { u16 id; - int num[SCARLETT2_PORT_DIRNS]; const char * const src_descr; int src_num_offset; const char * const dst_descr; };
+static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = { + [SCARLETT2_PORT_TYPE_NONE] = { + .id = 0x000, + .src_descr = "Off" + }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { + .id = 0x080, + .src_descr = "Analogue %d", + .src_num_offset = 1, + .dst_descr = "Analogue Output %02d Playback" + }, + [SCARLETT2_PORT_TYPE_SPDIF] = { + .id = 0x180, + .src_descr = "S/PDIF %d", + .src_num_offset = 1, + .dst_descr = "S/PDIF Output %d Playback" + }, + [SCARLETT2_PORT_TYPE_ADAT] = { + .id = 0x200, + .src_descr = "ADAT %d", + .src_num_offset = 1, + .dst_descr = "ADAT Output %d Playback" + }, + [SCARLETT2_PORT_TYPE_MIX] = { + .id = 0x300, + .src_descr = "Mix %c", + .src_num_offset = 'A', + .dst_descr = "Mixer Input %02d Capture" + }, + [SCARLETT2_PORT_TYPE_PCM] = { + .id = 0x600, + .src_descr = "PCM %d", + .src_num_offset = 1, + .dst_descr = "PCM %02d Capture" + }, +}; + /* Number of mux tables: one for each band of sample rates * (44.1/48kHz, 88.2/96kHz, and 176.4/176kHz) */ @@ -254,8 +289,8 @@ struct scarlett2_device_info { /* additional description for the line out volume controls */ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
- /* port count and type data */ - struct scarlett2_ports ports[SCARLETT2_PORT_TYPE_COUNT]; + /* number of sources/destinations of each port type */ + const int port_count[SCARLETT2_PORT_TYPE_COUNT][SCARLETT2_PORT_DIRNS];
/* layout/order of the entries in the set_mux message */ struct scarlett2_mux_entry mux_assignment[SCARLETT2_MUX_TABLES] @@ -309,40 +344,12 @@ static const struct scarlett2_device_info s6i6_gen2_info = { "Headphones 2 R", },
- .ports = { - [SCARLETT2_PORT_TYPE_NONE] = { - .id = 0x000, - .num = { 1, 0 }, - .src_descr = "Off", - }, - [SCARLETT2_PORT_TYPE_ANALOGUE] = { - .id = 0x080, - .num = { 4, 4 }, - .src_descr = "Analogue %d", - .src_num_offset = 1, - .dst_descr = "Analogue Output %02d Playback" - }, - [SCARLETT2_PORT_TYPE_SPDIF] = { - .id = 0x180, - .num = { 2, 2 }, - .src_descr = "S/PDIF %d", - .src_num_offset = 1, - .dst_descr = "S/PDIF Output %d Playback" - }, - [SCARLETT2_PORT_TYPE_MIX] = { - .id = 0x300, - .num = { 10, 18 }, - .src_descr = "Mix %c", - .src_num_offset = 65, - .dst_descr = "Mixer Input %02d Capture" - }, - [SCARLETT2_PORT_TYPE_PCM] = { - .id = 0x600, - .num = { 6, 6 }, - .src_descr = "PCM %d", - .src_num_offset = 1, - .dst_descr = "PCM %02d Capture" - }, + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 4 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, + [SCARLETT2_PORT_TYPE_PCM] = { 6, 6 }, },
.mux_assignment = { { @@ -384,46 +391,13 @@ static const struct scarlett2_device_info s18i8_gen2_info = { "Headphones 2 R", },
- .ports = { - [SCARLETT2_PORT_TYPE_NONE] = { - .id = 0x000, - .num = { 1, 0 }, - .src_descr = "Off", - }, - [SCARLETT2_PORT_TYPE_ANALOGUE] = { - .id = 0x080, - .num = { 8, 6 }, - .src_descr = "Analogue %d", - .src_num_offset = 1, - .dst_descr = "Analogue Output %02d Playback" - }, - [SCARLETT2_PORT_TYPE_SPDIF] = { - .id = 0x180, - .num = { 2, 2 }, - .src_descr = "S/PDIF %d", - .src_num_offset = 1, - .dst_descr = "S/PDIF Output %d Playback" - }, - [SCARLETT2_PORT_TYPE_ADAT] = { - .id = 0x200, - .num = { 8, 0 }, - .src_descr = "ADAT %d", - .src_num_offset = 1, - }, - [SCARLETT2_PORT_TYPE_MIX] = { - .id = 0x300, - .num = { 10, 18 }, - .src_descr = "Mix %c", - .src_num_offset = 65, - .dst_descr = "Mixer Input %02d Capture" - }, - [SCARLETT2_PORT_TYPE_PCM] = { - .id = 0x600, - .num = { 8, 18 }, - .src_descr = "PCM %d", - .src_num_offset = 1, - .dst_descr = "PCM %02d Capture" - }, + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 6 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, + [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, + [SCARLETT2_PORT_TYPE_PCM] = { 8, 18 }, },
.mux_assignment = { { @@ -468,51 +442,13 @@ static const struct scarlett2_device_info s18i20_gen2_info = { "Headphones 2 R", },
- .ports = { - [SCARLETT2_PORT_TYPE_NONE] = { - .id = 0x000, - .num = { 1, 0 }, - .src_descr = "Off", - }, - [SCARLETT2_PORT_TYPE_ANALOGUE] = { - .id = 0x080, - .num = { 8, 10 }, - .src_descr = "Analogue %d", - .src_num_offset = 1, - .dst_descr = "Analogue Output %02d Playback" - }, - [SCARLETT2_PORT_TYPE_SPDIF] = { - /* S/PDIF outputs aren't available at 192kHz - * but are included in the USB mux I/O - * assignment message anyway - */ - .id = 0x180, - .num = { 2, 2 }, - .src_descr = "S/PDIF %d", - .src_num_offset = 1, - .dst_descr = "S/PDIF Output %d Playback" - }, - [SCARLETT2_PORT_TYPE_ADAT] = { - .id = 0x200, - .num = { 8, 8 }, - .src_descr = "ADAT %d", - .src_num_offset = 1, - .dst_descr = "ADAT Output %d Playback" - }, - [SCARLETT2_PORT_TYPE_MIX] = { - .id = 0x300, - .num = { 10, 18 }, - .src_descr = "Mix %c", - .src_num_offset = 65, - .dst_descr = "Mixer Input %02d Capture" - }, - [SCARLETT2_PORT_TYPE_PCM] = { - .id = 0x600, - .num = { 20, 18 }, - .src_descr = "PCM %d", - .src_num_offset = 1, - .dst_descr = "PCM %02d Capture" - }, + .port_count = { + [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, + [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 10 }, + [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, + [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, + [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, + [SCARLETT2_PORT_TYPE_PCM] = { 20, 18 }, },
.mux_assignment = { { @@ -552,13 +488,14 @@ static const struct scarlett2_device_info *scarlett2_devices[] = { };
/* get the starting port index number for a given port type/direction */ -static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports, - int direction, int port_type) +static int scarlett2_get_port_start_num( + const int port_count[][SCARLETT2_PORT_DIRNS], + int direction, int port_type) { int i, num = 0;
for (i = 0; i < port_type; i++) - num += ports[i].num[direction]; + num += port_count[i][direction];
return num; } @@ -924,7 +861,7 @@ static int scarlett2_usb_get_mix(struct usb_mixer_interface *mixer, const struct scarlett2_device_info *info = private->info;
int num_mixer_in = - info->ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; + info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; int err, i, j, k;
struct { @@ -973,7 +910,7 @@ static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer,
int i, j; int num_mixer_in = - info->ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; + info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT];
req.mix_num = cpu_to_le16(mix_num);
@@ -987,18 +924,18 @@ static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer, NULL, 0); }
-/* Convert a port number index (per info->ports) to a hardware ID */ -static u32 scarlett2_mux_src_num_to_id(const struct scarlett2_ports *ports, - int num) +/* Convert a port number index (per info->port_count) to a hardware ID */ +static u32 scarlett2_mux_src_num_to_id( + const int port_count[][SCARLETT2_PORT_DIRNS], int num) { int port_type;
for (port_type = 0; port_type < SCARLETT2_PORT_TYPE_COUNT; port_type++) { - if (num < ports[port_type].num[SCARLETT2_PORT_IN]) - return ports[port_type].id | num; - num -= ports[port_type].num[SCARLETT2_PORT_IN]; + if (num < port_count[port_type][SCARLETT2_PORT_IN]) + return scarlett2_ports[port_type].id | num; + num -= port_count[port_type][SCARLETT2_PORT_IN]; }
/* Oops */ @@ -1006,9 +943,8 @@ static u32 scarlett2_mux_src_num_to_id(const struct scarlett2_ports *ports, }
/* Convert a hardware ID to a port number index */ -static u32 scarlett2_mux_id_to_num(const struct scarlett2_ports *ports, - int direction, - u32 id) +static u32 scarlett2_mux_id_to_num( + const int port_count[][SCARLETT2_PORT_DIRNS], int direction, u32 id) { int port_type; int port_num = 0; @@ -1016,11 +952,11 @@ static u32 scarlett2_mux_id_to_num(const struct scarlett2_ports *ports, for (port_type = 0; port_type < SCARLETT2_PORT_TYPE_COUNT; port_type++) { - struct scarlett2_ports port = ports[port_type]; - int count = port.num[direction]; + int base = scarlett2_ports[port_type].id; + int count = port_count[port_type][direction];
- if (id >= port.id && id < port.id + count) - return port_num + id - port.id; + if (id >= base && id < base + count) + return port_num + id - base; port_num += count; }
@@ -1033,11 +969,11 @@ static void scarlett2_usb_populate_mux(struct scarlett2_data *private, u32 mux_entry) { const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
int dst_idx, src_idx;
- dst_idx = scarlett2_mux_id_to_num(ports, SCARLETT2_PORT_OUT, + dst_idx = scarlett2_mux_id_to_num(port_count, SCARLETT2_PORT_OUT, mux_entry & 0xFFF); if (dst_idx < 0) return; @@ -1049,7 +985,7 @@ static void scarlett2_usb_populate_mux(struct scarlett2_data *private, return; }
- src_idx = scarlett2_mux_id_to_num(ports, SCARLETT2_PORT_IN, + src_idx = scarlett2_mux_id_to_num(port_count, SCARLETT2_PORT_IN, mux_entry >> 12); if (src_idx < 0) return; @@ -1098,7 +1034,7 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int table;
struct { @@ -1125,9 +1061,9 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) int j; int port_type = entry->port_type; int port_idx = entry->start; - int mux_idx = scarlett2_get_port_start_num(ports, + int mux_idx = scarlett2_get_port_start_num(port_count, SCARLETT2_PORT_OUT, port_type) + port_idx; - int dst_id = ports[port_type].id + port_idx; + int dst_id = scarlett2_ports[port_type].id + port_idx;
/* Empty slots */ if (!dst_id) { @@ -1142,7 +1078,7 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) */ for (j = 0; j < entry->count; j++) { int src_id = scarlett2_mux_src_num_to_id( - ports, private->mux[mux_idx++]); + port_count, private->mux[mux_idx++]); req.data[i++] = cpu_to_le32(dst_id | src_id << 12); dst_id++; @@ -1289,10 +1225,10 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; struct scarlett2_usb_volume_status volume_status; int num_line_out = - ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; int err, i; int mute;
@@ -1721,9 +1657,9 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl, struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int num_line_out = - ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
int index = elem->control; int oval, val, err = 0, i; @@ -1772,9 +1708,9 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int num_line_out = - ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; int err, i; char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
@@ -1913,7 +1849,7 @@ static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl, struct usb_mixer_interface *mixer = elem->head.mixer; struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int oval, val, num_mixer_in, mix_num, err = 0; int index = elem->control;
@@ -1921,7 +1857,7 @@ static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
oval = private->mix[index]; val = ucontrol->value.integer.value[0]; - num_mixer_in = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; + num_mixer_in = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; mix_num = index / num_mixer_in;
if (oval == val) @@ -1958,13 +1894,16 @@ static const struct snd_kcontrol_new scarlett2_mixer_ctl = { static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_ports *ports = private->info->ports; + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int err, i, j; int index; char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- int num_inputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_OUT]; - int num_outputs = ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_IN]; + int num_inputs = + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; + int num_outputs = + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
for (i = 0, index = 0; i < num_outputs; i++) for (j = 0; j < num_inputs; j++, index++) { @@ -1987,7 +1926,8 @@ static int scarlett2_mux_src_enum_ctl_info(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_ports *ports = private->info->ports; + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; unsigned int item = uinfo->value.enumerated.item; int items = private->num_mux_srcs; int port_type; @@ -2002,13 +1942,15 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, for (port_type = 0; port_type < SCARLETT2_PORT_TYPE_COUNT; port_type++) { - if (item < ports[port_type].num[SCARLETT2_PORT_IN]) { + if (item < port_count[port_type][SCARLETT2_PORT_IN]) { + const struct scarlett2_port *port = + &scarlett2_ports[port_type]; + sprintf(uinfo->value.enumerated.name, - ports[port_type].src_descr, - item + ports[port_type].src_num_offset); + port->src_descr, item + port->src_num_offset); return 0; } - item -= ports[port_type].num[SCARLETT2_PORT_IN]; + item -= port_count[port_type][SCARLETT2_PORT_IN]; }
return -EINVAL; @@ -2063,18 +2005,20 @@ static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = { static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; - const struct scarlett2_ports *ports = private->info->ports; + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int port_type, channel, i;
for (i = 0, port_type = 0; port_type < SCARLETT2_PORT_TYPE_COUNT; port_type++) { for (channel = 0; - channel < ports[port_type].num[SCARLETT2_PORT_OUT]; + channel < port_count[port_type][SCARLETT2_PORT_OUT]; channel++, i++) { int err; char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - const char *const descr = ports[port_type].dst_descr; + const char *const descr = + scarlett2_ports[port_type].dst_descr;
snprintf(s, sizeof(s) - 5, descr, channel + 1); strcat(s, " Enum"); @@ -2160,14 +2104,15 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
static void scarlett2_count_mux_io(struct scarlett2_data *private) { - const struct scarlett2_ports *ports = private->info->ports; + const struct scarlett2_device_info *info = private->info; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int port_type, srcs = 0, dsts = 0;
for (port_type = 0; port_type < SCARLETT2_PORT_TYPE_COUNT; port_type++) { - srcs += ports[port_type].num[SCARLETT2_PORT_IN]; - dsts += ports[port_type].num[SCARLETT2_PORT_OUT]; + srcs += port_count[port_type][SCARLETT2_PORT_IN]; + dsts += port_count[port_type][SCARLETT2_PORT_OUT]; }
private->num_mux_srcs = srcs; @@ -2265,11 +2210,11 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int num_line_out = - ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; int num_mixer_out = - ports[SCARLETT2_PORT_TYPE_MIX].num[SCARLETT2_PORT_IN]; + port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; struct scarlett2_usb_volume_status volume_status; int err, i;
@@ -2356,9 +2301,9 @@ static void scarlett2_notify_monitor( { struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int num_line_out = - ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; int i;
/* if line_out_hw_vol is 0, there are no controls to update */ @@ -2385,9 +2330,9 @@ static void scarlett2_notify_dim_mute( struct snd_card *card = mixer->chip->card; struct scarlett2_data *private = mixer->private_data; const struct scarlett2_device_info *info = private->info; - const struct scarlett2_ports *ports = info->ports; + const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; int num_line_out = - ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT]; + port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; int i;
private->vol_updated = 1;
The Level Meter control had a fixed number of channels and therefore only worked with the 18i20 Gen 2. Fix the control to contain the correct number of channels.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer_scarlett_gen2.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 7647b3428093..dde008ea21d7 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -161,10 +161,8 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { */ #define SCARLETT2_MUX_MAX 64
-/* Number of meters: - * 18 inputs, 20 outputs, 18 matrix inputs - */ -#define SCARLETT2_NUM_METERS 56 +/* Maximum number of meters (sum of output port counts) */ +#define SCARLETT2_MAX_METERS 56
/* Hardware port types: * - None (no input to mux) @@ -1097,26 +1095,26 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer)
/* Send USB message to get meter levels */ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, - u16 *levels) + u16 num_meters, u16 *levels) { struct { __le16 pad; __le16 num_meters; __le32 magic; } __packed req; - u32 resp[SCARLETT2_NUM_METERS]; + u32 resp[SCARLETT2_MAX_METERS]; int i, err;
req.pad = 0; - req.num_meters = cpu_to_le16(SCARLETT2_NUM_METERS); + req.num_meters = cpu_to_le16(num_meters); req.magic = cpu_to_le32(SCARLETT2_USB_METER_LEVELS_GET_MAGIC); err = scarlett2_usb(mixer, SCARLETT2_USB_GET_METER, - &req, sizeof(req), resp, sizeof(resp)); + &req, sizeof(req), resp, num_meters * sizeof(u32)); if (err < 0) return err;
/* copy, convert to u16 */ - for (i = 0; i < SCARLETT2_NUM_METERS; i++) + for (i = 0; i < num_meters; i++) levels[i] = resp[i];
return 0; @@ -2053,10 +2051,11 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_info *elem = kctl->private_data; - u16 meter_levels[SCARLETT2_NUM_METERS]; + u16 meter_levels[SCARLETT2_MAX_METERS]; int i, err;
- err = scarlett2_usb_get_meter_levels(elem->head.mixer, meter_levels); + err = scarlett2_usb_get_meter_levels(elem->head.mixer, elem->channels, + meter_levels); if (err < 0) return err;
@@ -2076,8 +2075,10 @@ static const struct snd_kcontrol_new scarlett2_meter_ctl = {
static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) { + struct scarlett2_data *private = mixer->private_data; + return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, - 0, SCARLETT2_NUM_METERS, + 0, private->num_mux_dsts, "Level Meter", NULL); }
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.c | 2 +- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 3 files changed, 246 insertions(+), 20 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..ba4aa1eacb04 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,7 +50,7 @@ #include "mixer_quirks.h" #include "power.h"
-#define MAX_ID_ELEMS 256 +#define MAX_ID_ELEMS 512
struct usb_audio_term { int id; 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 dde008ea21d7..c4bcccb5aecd 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), @@ -2485,7 +2707,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", @@ -2495,7 +2717,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);
On Mon, 21 Jun 2021 20:09:48 +0200, Geoffrey D. Bennett wrote:
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.c | 2 +- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 3 files changed, 246 insertions(+), 20 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..ba4aa1eacb04 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,7 +50,7 @@ #include "mixer_quirks.h" #include "power.h"
-#define MAX_ID_ELEMS 256 +#define MAX_ID_ELEMS 512
This change requires the explanation. Usually the unit id is a byte per definition, so it can't be over 256.
thanks,
Takashi
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
Let's assume we have a mute switch for each mixer gain setting. For the 18i20 device this will give: 12 inputs * 25 outputs = 300 mute switches.
So I think this constant should be increased rapidly up to 1024 or even to 2048.
Best, Vladimir
22.06.2021 10:00, Takashi Iwai пишет:
On Mon, 21 Jun 2021 20:09:48 +0200, Geoffrey D. Bennett wrote:
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.c | 2 +- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 3 files changed, 246 insertions(+), 20 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..ba4aa1eacb04 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,7 +50,7 @@ #include "mixer_quirks.h" #include "power.h"
-#define MAX_ID_ELEMS 256 +#define MAX_ID_ELEMS 512
This change requires the explanation. Usually the unit id is a byte per definition, so it can't be over 256.
thanks,
Takashi
Add mixer support for the Focusrite Scarlett 4i4, 8i6, 18i8, and 18i20 Gen 3 devices.
Change mixer.c MAX_ID_ELEMS from 256 to 512 as there are now more than 256 controls needed by the 18i20 Gen 3. The majority of these are the gain controls for the mixer matrix which has 25 inputs and 12 outputs, so 300 controls.
Signed-off-by: Geoffrey D. Bennett g@b4.vu --- sound/usb/mixer.c | 2 +- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 3 files changed, 246 insertions(+), 20 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..ba4aa1eacb04 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,7 +50,7 @@ #include "mixer_quirks.h" #include "power.h"
-#define MAX_ID_ELEMS 256 +#define MAX_ID_ELEMS 512
struct usb_audio_term { int id; 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 dde008ea21d7..c4bcccb5aecd 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), @@ -2485,7 +2707,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", @@ -2495,7 +2717,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);
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Takashi
Let's assume we have a mute switch for each mixer gain setting. For the 18i20 device this will give: 12 inputs * 25 outputs = 300 mute switches.
So I think this constant should be increased rapidly up to 1024 or even to 2048.
Best, Vladimir
22.06.2021 10:00, Takashi Iwai пишет:
On Mon, 21 Jun 2021 20:09:48 +0200, Geoffrey D. Bennett wrote:
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.c | 2 +- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 3 files changed, 246 insertions(+), 20 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..ba4aa1eacb04 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,7 +50,7 @@ #include "mixer_quirks.h" #include "power.h" -#define MAX_ID_ELEMS 256 +#define MAX_ID_ELEMS 512
This change requires the explanation. Usually the unit id is a byte per definition, so it can't be over 256.
thanks,
Takashi
On Tue, Jun 22, 2021 at 09:34:25AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Sorry I don't understand what's going on then. Am I calling snd_usb_mixer_add_control() wrong? Because when I called it more than MAX_ID_ELEMS times I got a buffer overflow in mixer->id_elems[] (from memory, I can confirm tonight).
snd_usb_create_mixer() has:
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), GFP_KERNEL);
snd_usb_mixer_add_control() called from mixer_scarlett_gen2.c ends up at snd_usb_mixer_add_list() which does:
list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list;
And list->id was going over MAX_ID_ELEMS.
On Tue, 22 Jun 2021 09:44:54 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:34:25AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Sorry I don't understand what's going on then. Am I calling snd_usb_mixer_add_control() wrong? Because when I called it more than MAX_ID_ELEMS times I got a buffer overflow in mixer->id_elems[] (from memory, I can confirm tonight).
snd_usb_create_mixer() has:
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), GFP_KERNEL);
snd_usb_mixer_add_control() called from mixer_scarlett_gen2.c ends up at snd_usb_mixer_add_list() which does:
list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list;
And list->id was going over MAX_ID_ELEMS.
Here, list->id is the *USB* mixer unit id, not the ALSA control id or whatever the internal index. The former should be a byte, hence it can't be over 256.
That said, the scarlett2 code calls the function in a wrong way. It has worked casually, so far, just because the core code doesn't use the unit id number for significant roles.
So, as a quick workaround, simply pass 0 or any fixed number under 256 to list->id (i.e. elem->head.id in scarlett2_add_new_ctl()). That's all, and the elements are chained in the linked list.
Takashi
On Tue, 22 Jun 2021 09:58:27 +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:44:54 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:34:25AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Sorry I don't understand what's going on then. Am I calling snd_usb_mixer_add_control() wrong? Because when I called it more than MAX_ID_ELEMS times I got a buffer overflow in mixer->id_elems[] (from memory, I can confirm tonight).
snd_usb_create_mixer() has:
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), GFP_KERNEL);
snd_usb_mixer_add_control() called from mixer_scarlett_gen2.c ends up at snd_usb_mixer_add_list() which does:
list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list;
And list->id was going over MAX_ID_ELEMS.
Here, list->id is the *USB* mixer unit id, not the ALSA control id or whatever the internal index. The former should be a byte, hence it can't be over 256.
That said, the scarlett2 code calls the function in a wrong way. It has worked casually, so far, just because the core code doesn't use the unit id number for significant roles.
... and looking again at the code, actually it does matter. The USB audio mixer code calls the resume for each mixer element, and the default resume code (default_mixer_resume()) is applied for a control with usb_mixer_elem_info.val_type == USB_MIXER_BOOLEAN and channels == 1. It seems that scarlett2 has some of them, and the resume procedure is applied wrongly with an invalid unit id.
We need to define a special mixer value type (e.g. USB_MIXER_VENDOR), and use this for the all scarlett2 mixer elements, in addition to the below:
So, as a quick workaround, simply pass 0 or any fixed number under 256 to list->id (i.e. elem->head.id in scarlett2_add_new_ctl()). That's all, and the elements are chained in the linked list.
Takashi
On Tue, Jun 22, 2021 at 09:58:27AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:44:54 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:34:25AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Sorry I don't understand what's going on then. Am I calling snd_usb_mixer_add_control() wrong? Because when I called it more than MAX_ID_ELEMS times I got a buffer overflow in mixer->id_elems[] (from memory, I can confirm tonight).
snd_usb_create_mixer() has:
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), GFP_KERNEL);
snd_usb_mixer_add_control() called from mixer_scarlett_gen2.c ends up at snd_usb_mixer_add_list() which does:
list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list;
And list->id was going over MAX_ID_ELEMS.
Here, list->id is the *USB* mixer unit id, not the ALSA control id or whatever the internal index. The former should be a byte, hence it can't be over 256.
That said, the scarlett2 code calls the function in a wrong way. It has worked casually, so far, just because the core code doesn't use the unit id number for significant roles.
So, as a quick workaround, simply pass 0 or any fixed number under 256 to list->id (i.e. elem->head.id in scarlett2_add_new_ctl()). That's all, and the elements are chained in the linked list.
Okay, I will fix that tonight.
Were patches 1-15 of this set of 31 acceptable? If so, I will send a new set with this fix and the remainder of the patches.
On Tue, 22 Jun 2021 10:18:39 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:58:27AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:44:54 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:34:25AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Sorry I don't understand what's going on then. Am I calling snd_usb_mixer_add_control() wrong? Because when I called it more than MAX_ID_ELEMS times I got a buffer overflow in mixer->id_elems[] (from memory, I can confirm tonight).
snd_usb_create_mixer() has:
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), GFP_KERNEL);
snd_usb_mixer_add_control() called from mixer_scarlett_gen2.c ends up at snd_usb_mixer_add_list() which does:
list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list;
And list->id was going over MAX_ID_ELEMS.
Here, list->id is the *USB* mixer unit id, not the ALSA control id or whatever the internal index. The former should be a byte, hence it can't be over 256.
That said, the scarlett2 code calls the function in a wrong way. It has worked casually, so far, just because the core code doesn't use the unit id number for significant roles.
So, as a quick workaround, simply pass 0 or any fixed number under 256 to list->id (i.e. elem->head.id in scarlett2_add_new_ctl()). That's all, and the elements are chained in the linked list.
Okay, I will fix that tonight.
Were patches 1-15 of this set of 31 acceptable? If so, I will send a new set with this fix and the remainder of the patches.
I don't see other issues through a quick glance.
And, the additional fix is below. If this works, please include this at first.
Takashi
-- 8< -- From: Takashi Iwai tiwai@suse.de Subject: [PATCH] ALSA: usb-audio: scarlett2: Fix wrong resume call
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.
Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/mixer.h | 1 + sound/usb/mixer_scarlett_gen2.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-)
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 2e1937b072ee..ed2f16a5fc87 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1070,10 +1070,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 10:34:54 +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 10:18:39 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:58:27AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:44:54 +0200, Geoffrey D. Bennett wrote:
On Tue, Jun 22, 2021 at 09:34:25AM +0200, Takashi Iwai wrote:
On Tue, 22 Jun 2021 09:07:20 +0200, Vladimir Sadovnikov wrote:
Hello Takashi!
Since Focusrite devices are too advanced in settings, the overall amount of 256 controls is not enough for these devices (like 18i20). I would like also to extend this constant up to 1024 or even more since adding support of software configuration of the device also can exceed the amount of 512 control elements.
This define isn't for the total number of mixer elements. Instead, it's just a size of the bitmap table that contains the head of the linked list for each unit id (in the sense of USB mixer spec). So the number of mixer elements is unlimited.
Sorry I don't understand what's going on then. Am I calling snd_usb_mixer_add_control() wrong? Because when I called it more than MAX_ID_ELEMS times I got a buffer overflow in mixer->id_elems[] (from memory, I can confirm tonight).
snd_usb_create_mixer() has:
mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), GFP_KERNEL);
snd_usb_mixer_add_control() called from mixer_scarlett_gen2.c ends up at snd_usb_mixer_add_list() which does:
list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list;
And list->id was going over MAX_ID_ELEMS.
Here, list->id is the *USB* mixer unit id, not the ALSA control id or whatever the internal index. The former should be a byte, hence it can't be over 256.
That said, the scarlett2 code calls the function in a wrong way. It has worked casually, so far, just because the core code doesn't use the unit id number for significant roles.
So, as a quick workaround, simply pass 0 or any fixed number under 256 to list->id (i.e. elem->head.id in scarlett2_add_new_ctl()). That's all, and the elements are chained in the linked list.
Okay, I will fix that tonight.
Were patches 1-15 of this set of 31 acceptable? If so, I will send a new set with this fix and the remainder of the patches.
I don't see other issues through a quick glance.
And, the additional fix is below. If this works, please include this at first.
It was missing one piece. The revised patch is below.
Takashi
-- 8< -- From: Takashi Iwai tiwai@suse.de Subject: [PATCH v2] ALSA: usb-audio: scarlett2: Fix wrong resume call
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.
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 2e1937b072ee..ed2f16a5fc87 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1070,10 +1070,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, Jun 22, 2021 at 09:00:19AM +0200, Takashi Iwai wrote:
On Mon, 21 Jun 2021 20:09:48 +0200, Geoffrey D. Bennett wrote:
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.c | 2 +- sound/usb/mixer_quirks.c | 4 + sound/usb/mixer_scarlett_gen2.c | 260 +++++++++++++++++++++++++++++--- 3 files changed, 246 insertions(+), 20 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 428d581f988f..ba4aa1eacb04 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,7 +50,7 @@ #include "mixer_quirks.h" #include "power.h"
-#define MAX_ID_ELEMS 256 +#define MAX_ID_ELEMS 512
This change requires the explanation. Usually the unit id is a byte per definition, so it can't be over 256.
Before making this change we were getting a buffer overflow in mixer->id_elems[] (snd_usb_mixer_add_list()) because more than 256 controls were being added for the 18i20 Gen 3 device. I will send a replacement patch with an updated comment.
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 c4bcccb5aecd..a4f92291b74f 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 @@ -1740,6 +1744,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) { @@ -1754,10 +1784,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; }
@@ -1806,10 +1842,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; }
@@ -2020,7 +2062,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; } @@ -2029,7 +2071,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; } @@ -2441,25 +2483,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) @@ -2573,6 +2599,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) { @@ -2591,6 +2636,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 a4f92291b74f..6995d692c6b2 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; } @@ -2346,6 +2374,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) @@ -2483,6 +2576,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; @@ -2705,6 +2810,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 6995d692c6b2..87a90cafb175 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 87a90cafb175..45b28585dacb 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 45b28585dacb..50a90693482f 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); @@ -1506,6 +1559,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); } @@ -1824,7 +1881,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; } @@ -1856,12 +1914,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; @@ -1873,8 +1933,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); @@ -2130,7 +2191,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) @@ -2411,6 +2473,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); @@ -2634,6 +2700,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 50a90693482f..a8c5a28f21c9 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) @@ -1895,6 +1914,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; }
@@ -2025,6 +2052,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, @@ -2208,6 +2290,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; }
@@ -2816,7 +2907,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) { @@ -2833,6 +2924,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 a8c5a28f21c9..87672e6b4505 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 */ @@ -1922,6 +1961,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; }
@@ -2107,6 +2160,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, @@ -2270,6 +2428,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++) { @@ -2299,6 +2458,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; }
@@ -2927,6 +3119,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 87672e6b4505..94de63847169 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 },
@@ -2265,6 +2281,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, @@ -2983,6 +3105,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; @@ -3124,6 +3250,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) { @@ -3144,6 +3284,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 && @@ -3250,6 +3392,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 94de63847169..03decb35d25e 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 = { @@ -1711,13 +1720,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) @@ -1734,7 +1752,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); @@ -1790,7 +1808,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; @@ -1802,7 +1820,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); @@ -1849,9 +1867,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; }
@@ -1887,8 +1905,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); @@ -1904,7 +1922,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; @@ -2436,13 +2454,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); @@ -2481,6 +2502,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]) @@ -2511,7 +2533,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 */ @@ -2760,8 +2782,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; }
@@ -2771,9 +2801,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]; @@ -3174,6 +3211,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; @@ -3190,12 +3228,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 */ @@ -3220,7 +3256,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 03decb35d25e..1b398a06d4b2 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1899,23 +1899,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;
@@ -1933,18 +1922,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 1b398a06d4b2..efa65bdb48c8 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]; @@ -2553,7 +2555,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; } @@ -2871,7 +2874,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 efa65bdb48c8..dce40d2e0ba6 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);
@@ -2794,7 +2797,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 = @@ -2804,7 +2808,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 dce40d2e0ba6..dc5fd045b2dc 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 */ @@ -1857,6 +1873,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) { @@ -2320,6 +2348,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;
@@ -2328,6 +2363,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; }
@@ -2420,6 +2475,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, @@ -2562,6 +2762,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); } }
@@ -3309,18 +3515,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 */ @@ -3456,6 +3685,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 dc5fd045b2dc..8fd850f6aa6e 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 8fd850f6aa6e..fcf78b06a519 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 */ @@ -2356,10 +2368,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; @@ -2369,6 +2385,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;
@@ -2389,6 +2408,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; }
@@ -2626,6 +2665,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, @@ -3521,7 +3725,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) { @@ -3541,6 +3747,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 */ @@ -3696,6 +3906,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
-
Takashi Iwai
-
Vladimir Sadovnikov