All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster
@ 2024-03-12 18:28 Geoffrey D. Bennett
  2024-03-12 18:33 ` [PATCH 01/14] ALSA: scarlett2: Move initialisation code lower in the source Geoffrey D. Bennett
                   ` (15 more replies)
  0 siblings, 16 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:28 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Hi Takashi,

This patch series contains:

1-2: Fix to wait for an ACK from the device after sending a command,
before attempting to read the response. This didn't seem to be
necessary previously, but with 4th Gen devices it seems to help with a
rare issue where the device stops responding.

3: Support for reading flash segments from the device.

4-11: Preparation for Vocaster support.

12: Vocaster One and Two support.

13: Add autogain target controls for 4th Gen and Vocaster.

14: Add Bluetooth volume control for Vocaster Two.

Thanks,
Geoffrey.

Geoffrey D. Bennett (14):
  ALSA: scarlett2: Move initialisation code lower in the source
  ALSA: scarlett2: Implement handling of the ACK notification
  ALSA: scarlett2: Add support for reading from flash
  ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr
  ALSA: scarlett2: Add pbuf field to struct scarlett2_config
  ALSA: scarlett2: Add support for config items with size = 32
  ALSA: scarlett2: Add additional input configuration parameters
  ALSA: scarlett2: Define the maximum preamp input gain per-config-set
  ALSA: scarlett2: Define autogain status texts per-config-set
  ALSA: scarlett2: Add input mute controls
  ALSA: scarlett2: Add DSP controls
  ALSA: scarlett2: Add support for Focusrite Vocaster One and Two
  ALSA: scarlett2: Add autogain target controls
  ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two

 sound/usb/mixer_quirks.c    |    2 +
 sound/usb/mixer_scarlett2.c | 2757 ++++++++++++++++++++++++++++-------
 2 files changed, 2235 insertions(+), 524 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 01/14] ALSA: scarlett2: Move initialisation code lower in the source
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
@ 2024-03-12 18:33 ` Geoffrey D. Bennett
  2024-03-12 18:34 ` [PATCH 02/14] ALSA: scarlett2: Implement handling of the ACK notification Geoffrey D. Bennett
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:33 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

So that more forward declarations won't be required when we add
handling of the ACK notification, move the initialisation functions to
after the notification functions.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 815 ++++++++++++++++++------------------
 1 file changed, 409 insertions(+), 406 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index bd114be537d7..8390b646c0ae 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -6383,412 +6383,7 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
 				     &private->power_status_ctl);
 }
 
-/*** Cleanup/Suspend Callbacks ***/
-
-static void scarlett2_private_free(struct usb_mixer_interface *mixer)
-{
-	struct scarlett2_data *private = mixer->private_data;
-
-	cancel_delayed_work_sync(&private->work);
-	kfree(private);
-	mixer->private_data = NULL;
-}
-
-static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
-{
-	struct scarlett2_data *private = mixer->private_data;
-
-	if (cancel_delayed_work_sync(&private->work))
-		scarlett2_config_save(private->mixer);
-}
-
-/*** Initialisation ***/
-
-static void scarlett2_count_io(struct scarlett2_data *private)
-{
-	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;
-
-	/* Count the number of mux sources and destinations */
-	for (port_type = 0;
-	     port_type < SCARLETT2_PORT_TYPE_COUNT;
-	     port_type++) {
-		srcs += port_count[port_type][SCARLETT2_PORT_IN];
-		dsts += port_count[port_type][SCARLETT2_PORT_OUT];
-	}
-
-	private->num_mux_srcs = srcs;
-	private->num_mux_dsts = dsts;
-
-	/* Mixer inputs are mux outputs and vice versa.
-	 * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but
-	 * doesn't have mixer controls.
-	 */
-	private->num_mix_in =
-		port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] -
-			info->dsp_count;
-
-	private->num_mix_out =
-		port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
-			info->dsp_count;
-
-	/* Number of analogue line outputs */
-	private->num_line_out =
-		port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
-
-	/* Number of monitor mix controls */
-	private->num_monitor_mix_ctls =
-		info->direct_monitor * 2 * private->num_mix_in;
-}
-
-/* Look through the interface descriptors for the Focusrite Control
- * interface (bInterfaceClass = 255 Vendor Specific Class) and set
- * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
- * in private
- */
-static int scarlett2_find_fc_interface(struct usb_device *dev,
-				       struct scarlett2_data *private)
-{
-	struct usb_host_config *config = dev->actconfig;
-	int i;
-
-	for (i = 0; i < config->desc.bNumInterfaces; i++) {
-		struct usb_interface *intf = config->interface[i];
-		struct usb_interface_descriptor *desc =
-			&intf->altsetting[0].desc;
-		struct usb_endpoint_descriptor *epd;
-
-		if (desc->bInterfaceClass != 255)
-			continue;
-
-		epd = get_endpoint(intf->altsetting, 0);
-		private->bInterfaceNumber = desc->bInterfaceNumber;
-		private->bEndpointAddress = epd->bEndpointAddress &
-			USB_ENDPOINT_NUMBER_MASK;
-		private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
-		private->bInterval = epd->bInterval;
-		return 0;
-	}
-
-	return -EINVAL;
-}
-
-/* Initialise private data */
-static int scarlett2_init_private(struct usb_mixer_interface *mixer,
-				  const struct scarlett2_device_entry *entry)
-{
-	struct scarlett2_data *private =
-		kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL);
-
-	if (!private)
-		return -ENOMEM;
-
-	mutex_init(&private->usb_mutex);
-	mutex_init(&private->data_mutex);
-	INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
-
-	mixer->private_data = private;
-	mixer->private_free = scarlett2_private_free;
-	mixer->private_suspend = scarlett2_private_suspend;
-
-	private->info = entry->info;
-	private->config_set = entry->info->config_set;
-	private->series_name = entry->series_name;
-	scarlett2_count_io(private);
-	private->scarlett2_seq = 0;
-	private->mixer = mixer;
-
-	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 step0_buf[24];
-	u8 step2_buf[84];
-	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,
-			       step0_buf, sizeof(step0_buf));
-	if (err < 0)
-		return err;
-
-	/* 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;
-	err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2,
-			    NULL, 0,
-			    step2_buf, sizeof(step2_buf));
-	if (err < 0)
-		return err;
-
-	/* extract 4-byte firmware version from step2_buf[8] */
-	private->firmware_version = le32_to_cpu(*(__le32 *)(step2_buf + 8));
-	usb_audio_info(mixer->chip,
-		       "Firmware version %d\n",
-		       private->firmware_version);
-
-	return 0;
-}
-
-/* Get the flash segment numbers for the App_Settings and App_Upgrade
- * segments and put them in the private data
- */
-static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer)
-{
-	struct scarlett2_data *private = mixer->private_data;
-	int err, count, i;
-
-	struct {
-		__le32 size;
-		__le32 count;
-		u8 unknown[8];
-	} __packed flash_info;
-
-	struct {
-		__le32 size;
-		__le32 flags;
-		char name[16];
-	} __packed segment_info;
-
-	err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH,
-			    NULL, 0,
-			    &flash_info, sizeof(flash_info));
-	if (err < 0)
-		return err;
-
-	count = le32_to_cpu(flash_info.count);
-
-	/* sanity check count */
-	if (count < SCARLETT2_SEGMENT_NUM_MIN ||
-	    count > SCARLETT2_SEGMENT_NUM_MAX + 1) {
-		usb_audio_err(mixer->chip,
-			      "invalid flash segment count: %d\n", count);
-		return -EINVAL;
-	}
-
-	for (i = 0; i < count; i++) {
-		__le32 segment_num_req = cpu_to_le32(i);
-		int flash_segment_id;
-
-		err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT,
-				    &segment_num_req, sizeof(segment_num_req),
-				    &segment_info, sizeof(segment_info));
-		if (err < 0) {
-			usb_audio_err(mixer->chip,
-				"failed to get flash segment info %d: %d\n",
-				i, err);
-			return err;
-		}
-
-		if (!strncmp(segment_info.name,
-			     SCARLETT2_SEGMENT_SETTINGS_NAME, 16))
-			flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS;
-		else if (!strncmp(segment_info.name,
-				  SCARLETT2_SEGMENT_FIRMWARE_NAME, 16))
-			flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE;
-		else
-			continue;
-
-		private->flash_segment_nums[flash_segment_id] = i;
-		private->flash_segment_blocks[flash_segment_id] =
-			le32_to_cpu(segment_info.size) /
-				SCARLETT2_FLASH_BLOCK_SIZE;
-	}
-
-	/* segment 0 is App_Gold and we never want to touch that, so
-	 * use 0 as the "not-found" value
-	 */
-	if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) {
-		usb_audio_err(mixer->chip,
-			      "failed to find flash segment %s\n",
-			      SCARLETT2_SEGMENT_SETTINGS_NAME);
-		return -EINVAL;
-	}
-	if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) {
-		usb_audio_err(mixer->chip,
-			      "failed to find flash segment %s\n",
-			      SCARLETT2_SEGMENT_FIRMWARE_NAME);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/* Read configuration from the interface on start */
-static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
-{
-	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
-	int err, i;
-
-	if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) {
-		err = scarlett2_usb_get_config(
-			mixer, SCARLETT2_CONFIG_MSD_SWITCH,
-			1, &private->msd_switch);
-		if (err < 0)
-			return err;
-	}
-
-	if (private->firmware_version < info->min_firmware_version) {
-		usb_audio_err(mixer->chip,
-			      "Focusrite %s firmware version %d is too old; "
-			      "need %d",
-			      private->series_name,
-			      private->firmware_version,
-			      info->min_firmware_version);
-		return 0;
-	}
-
-	/* no other controls are created if MSD mode is on */
-	if (private->msd_switch)
-		return 0;
-
-	err = scarlett2_update_input_level(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_input_pad(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_input_air(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_input_phantom(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_direct_monitor(mixer);
-	if (err < 0)
-		return err;
-
-	/* the rest of the configuration is for devices with a mixer */
-	if (!scarlett2_has_mixer(private))
-		return 0;
-
-	err = scarlett2_update_monitor_mix(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_monitor_other(mixer);
-	if (err < 0)
-		return err;
-
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_STANDALONE_SWITCH)) {
-		err = scarlett2_usb_get_config(
-			mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
-			1, &private->standalone_switch);
-		if (err < 0)
-			return err;
-	}
-
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_POWER_EXT)) {
-		err = scarlett2_update_power_status(mixer);
-		if (err < 0)
-			return err;
-	}
-
-	err = scarlett2_update_sync(mixer);
-	if (err < 0)
-		return err;
-
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
-		s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
-
-		/* read SW line out volume */
-		err = scarlett2_usb_get_config(
-			mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
-			private->num_line_out, &sw_vol);
-		if (err < 0)
-			return err;
-
-		for (i = 0; i < private->num_line_out; i++)
-			private->vol[i] = clamp(
-				sw_vol[i] + SCARLETT2_VOLUME_BIAS,
-				0, SCARLETT2_VOLUME_BIAS);
-
-		/* read SW mute */
-		err = scarlett2_usb_get_config(
-			mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
-			private->num_line_out, &private->mute_switch);
-		if (err < 0)
-			return err;
-
-		for (i = 0; i < private->num_line_out; i++)
-			private->mute_switch[i] =
-				!!private->mute_switch[i];
-
-		/* read SW/HW switches */
-		if (scarlett2_has_config_item(private,
-					      SCARLETT2_CONFIG_SW_HW_SWITCH)) {
-			err = scarlett2_usb_get_config(
-				mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
-				private->num_line_out,
-				&private->vol_sw_hw_switch);
-			if (err < 0)
-				return err;
-
-			for (i = 0; i < private->num_line_out; i++)
-				private->vol_sw_hw_switch[i] =
-					!!private->vol_sw_hw_switch[i];
-		}
-	}
-
-	err = scarlett2_update_volumes(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_dim_mute(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_input_select(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_input_gain(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_autogain(mixer);
-	if (err < 0)
-		return err;
-
-	err = scarlett2_update_input_safe(mixer);
-	if (err < 0)
-		return err;
-
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
-		err = scarlett2_update_pcm_input_switch(mixer);
-		if (err < 0)
-			return err;
-	}
-
-	err = scarlett2_update_mix(mixer);
-	if (err < 0)
-		return err;
-
-	return scarlett2_usb_get_mux(mixer);
-}
+/*** Notification Handlers ***/
 
 /* Notify on sync change */
 static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
@@ -7146,6 +6741,126 @@ static void scarlett2_notify(struct urb *urb)
 	}
 }
 
+/*** Cleanup/Suspend Callbacks ***/
+
+static void scarlett2_private_free(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+
+	cancel_delayed_work_sync(&private->work);
+	kfree(private);
+	mixer->private_data = NULL;
+}
+
+static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+
+	if (cancel_delayed_work_sync(&private->work))
+		scarlett2_config_save(private->mixer);
+}
+
+/*** Initialisation ***/
+
+static void scarlett2_count_io(struct scarlett2_data *private)
+{
+	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;
+
+	/* Count the number of mux sources and destinations */
+	for (port_type = 0;
+	     port_type < SCARLETT2_PORT_TYPE_COUNT;
+	     port_type++) {
+		srcs += port_count[port_type][SCARLETT2_PORT_IN];
+		dsts += port_count[port_type][SCARLETT2_PORT_OUT];
+	}
+
+	private->num_mux_srcs = srcs;
+	private->num_mux_dsts = dsts;
+
+	/* Mixer inputs are mux outputs and vice versa.
+	 * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but
+	 * doesn't have mixer controls.
+	 */
+	private->num_mix_in =
+		port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] -
+			info->dsp_count;
+
+	private->num_mix_out =
+		port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
+			info->dsp_count;
+
+	/* Number of analogue line outputs */
+	private->num_line_out =
+		port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+
+	/* Number of monitor mix controls */
+	private->num_monitor_mix_ctls =
+		info->direct_monitor * 2 * private->num_mix_in;
+}
+
+/* Look through the interface descriptors for the Focusrite Control
+ * interface (bInterfaceClass = 255 Vendor Specific Class) and set
+ * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
+ * in private
+ */
+static int scarlett2_find_fc_interface(struct usb_device *dev,
+				       struct scarlett2_data *private)
+{
+	struct usb_host_config *config = dev->actconfig;
+	int i;
+
+	for (i = 0; i < config->desc.bNumInterfaces; i++) {
+		struct usb_interface *intf = config->interface[i];
+		struct usb_interface_descriptor *desc =
+			&intf->altsetting[0].desc;
+		struct usb_endpoint_descriptor *epd;
+
+		if (desc->bInterfaceClass != 255)
+			continue;
+
+		epd = get_endpoint(intf->altsetting, 0);
+		private->bInterfaceNumber = desc->bInterfaceNumber;
+		private->bEndpointAddress = epd->bEndpointAddress &
+			USB_ENDPOINT_NUMBER_MASK;
+		private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
+		private->bInterval = epd->bInterval;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* Initialise private data */
+static int scarlett2_init_private(struct usb_mixer_interface *mixer,
+				  const struct scarlett2_device_entry *entry)
+{
+	struct scarlett2_data *private =
+		kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL);
+
+	if (!private)
+		return -ENOMEM;
+
+	mutex_init(&private->usb_mutex);
+	mutex_init(&private->data_mutex);
+	INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
+
+	mixer->private_data = private;
+	mixer->private_free = scarlett2_private_free;
+	mixer->private_suspend = scarlett2_private_suspend;
+
+	private->info = entry->info;
+	private->config_set = entry->info->config_set;
+	private->series_name = entry->series_name;
+	scarlett2_count_io(private);
+	private->scarlett2_seq = 0;
+	private->mixer = mixer;
+
+	return scarlett2_find_fc_interface(mixer->chip->dev, private);
+}
+
+/* Submit a URB to receive notifications from the device */
 static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
 {
 	struct usb_device *dev = mixer->chip->dev;
@@ -7177,6 +6892,294 @@ static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
 	return usb_submit_urb(mixer->urb, GFP_KERNEL);
 }
 
+/* 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 step0_buf[24];
+	u8 step2_buf[84];
+	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,
+			       step0_buf, sizeof(step0_buf));
+	if (err < 0)
+		return err;
+
+	/* 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;
+	err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_2,
+			    NULL, 0,
+			    step2_buf, sizeof(step2_buf));
+	if (err < 0)
+		return err;
+
+	/* extract 4-byte firmware version from step2_buf[8] */
+	private->firmware_version = le32_to_cpu(*(__le32 *)(step2_buf + 8));
+	usb_audio_info(mixer->chip,
+		       "Firmware version %d\n",
+		       private->firmware_version);
+
+	return 0;
+}
+
+/* Get the flash segment numbers for the App_Settings and App_Upgrade
+ * segments and put them in the private data
+ */
+static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	int err, count, i;
+
+	struct {
+		__le32 size;
+		__le32 count;
+		u8 unknown[8];
+	} __packed flash_info;
+
+	struct {
+		__le32 size;
+		__le32 flags;
+		char name[16];
+	} __packed segment_info;
+
+	err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH,
+			    NULL, 0,
+			    &flash_info, sizeof(flash_info));
+	if (err < 0)
+		return err;
+
+	count = le32_to_cpu(flash_info.count);
+
+	/* sanity check count */
+	if (count < SCARLETT2_SEGMENT_NUM_MIN ||
+	    count > SCARLETT2_SEGMENT_NUM_MAX + 1) {
+		usb_audio_err(mixer->chip,
+			      "invalid flash segment count: %d\n", count);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		__le32 segment_num_req = cpu_to_le32(i);
+		int flash_segment_id;
+
+		err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_SEGMENT,
+				    &segment_num_req, sizeof(segment_num_req),
+				    &segment_info, sizeof(segment_info));
+		if (err < 0) {
+			usb_audio_err(mixer->chip,
+				"failed to get flash segment info %d: %d\n",
+				i, err);
+			return err;
+		}
+
+		if (!strncmp(segment_info.name,
+			     SCARLETT2_SEGMENT_SETTINGS_NAME, 16))
+			flash_segment_id = SCARLETT2_SEGMENT_ID_SETTINGS;
+		else if (!strncmp(segment_info.name,
+				  SCARLETT2_SEGMENT_FIRMWARE_NAME, 16))
+			flash_segment_id = SCARLETT2_SEGMENT_ID_FIRMWARE;
+		else
+			continue;
+
+		private->flash_segment_nums[flash_segment_id] = i;
+		private->flash_segment_blocks[flash_segment_id] =
+			le32_to_cpu(segment_info.size) /
+				SCARLETT2_FLASH_BLOCK_SIZE;
+	}
+
+	/* segment 0 is App_Gold and we never want to touch that, so
+	 * use 0 as the "not-found" value
+	 */
+	if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_SETTINGS]) {
+		usb_audio_err(mixer->chip,
+			      "failed to find flash segment %s\n",
+			      SCARLETT2_SEGMENT_SETTINGS_NAME);
+		return -EINVAL;
+	}
+	if (!private->flash_segment_nums[SCARLETT2_SEGMENT_ID_FIRMWARE]) {
+		usb_audio_err(mixer->chip,
+			      "failed to find flash segment %s\n",
+			      SCARLETT2_SEGMENT_FIRMWARE_NAME);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Read configuration from the interface on start */
+static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int err, i;
+
+	if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) {
+		err = scarlett2_usb_get_config(
+			mixer, SCARLETT2_CONFIG_MSD_SWITCH,
+			1, &private->msd_switch);
+		if (err < 0)
+			return err;
+	}
+
+	if (private->firmware_version < info->min_firmware_version) {
+		usb_audio_err(mixer->chip,
+			      "Focusrite %s firmware version %d is too old; "
+			      "need %d",
+			      private->series_name,
+			      private->firmware_version,
+			      info->min_firmware_version);
+		return 0;
+	}
+
+	/* no other controls are created if MSD mode is on */
+	if (private->msd_switch)
+		return 0;
+
+	err = scarlett2_update_input_level(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_input_pad(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_input_air(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_input_phantom(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_direct_monitor(mixer);
+	if (err < 0)
+		return err;
+
+	/* the rest of the configuration is for devices with a mixer */
+	if (!scarlett2_has_mixer(private))
+		return 0;
+
+	err = scarlett2_update_monitor_mix(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_monitor_other(mixer);
+	if (err < 0)
+		return err;
+
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_STANDALONE_SWITCH)) {
+		err = scarlett2_usb_get_config(
+			mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH,
+			1, &private->standalone_switch);
+		if (err < 0)
+			return err;
+	}
+
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_POWER_EXT)) {
+		err = scarlett2_update_power_status(mixer);
+		if (err < 0)
+			return err;
+	}
+
+	err = scarlett2_update_sync(mixer);
+	if (err < 0)
+		return err;
+
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
+		s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
+
+		/* read SW line out volume */
+		err = scarlett2_usb_get_config(
+			mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME,
+			private->num_line_out, &sw_vol);
+		if (err < 0)
+			return err;
+
+		for (i = 0; i < private->num_line_out; i++)
+			private->vol[i] = clamp(
+				sw_vol[i] + SCARLETT2_VOLUME_BIAS,
+				0, SCARLETT2_VOLUME_BIAS);
+
+		/* read SW mute */
+		err = scarlett2_usb_get_config(
+			mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
+			private->num_line_out, &private->mute_switch);
+		if (err < 0)
+			return err;
+
+		for (i = 0; i < private->num_line_out; i++)
+			private->mute_switch[i] =
+				!!private->mute_switch[i];
+
+		/* read SW/HW switches */
+		if (scarlett2_has_config_item(private,
+					      SCARLETT2_CONFIG_SW_HW_SWITCH)) {
+			err = scarlett2_usb_get_config(
+				mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
+				private->num_line_out,
+				&private->vol_sw_hw_switch);
+			if (err < 0)
+				return err;
+
+			for (i = 0; i < private->num_line_out; i++)
+				private->vol_sw_hw_switch[i] =
+					!!private->vol_sw_hw_switch[i];
+		}
+	}
+
+	err = scarlett2_update_volumes(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_dim_mute(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_input_select(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_input_gain(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_autogain(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_input_safe(mixer);
+	if (err < 0)
+		return err;
+
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+		err = scarlett2_update_pcm_input_switch(mixer);
+		if (err < 0)
+			return err;
+	}
+
+	err = scarlett2_update_mix(mixer);
+	if (err < 0)
+		return err;
+
+	return scarlett2_usb_get_mux(mixer);
+}
+
 static const struct scarlett2_device_entry *get_scarlett2_device_entry(
 	struct usb_mixer_interface *mixer)
 {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 02/14] ALSA: scarlett2: Implement handling of the ACK notification
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
  2024-03-12 18:33 ` [PATCH 01/14] ALSA: scarlett2: Move initialisation code lower in the source Geoffrey D. Bennett
@ 2024-03-12 18:34 ` Geoffrey D. Bennett
  2024-03-12 18:34 ` [PATCH 03/14] ALSA: scarlett2: Add support for reading from flash Geoffrey D. Bennett
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:34 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

After scarlett2_usb() sends a command, it seems that we should wait
for an ACK before attempting to read the response. Not doing that
didn't seem necessary previously but seems to be causing occasional
issues with 4th Gen devices.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 70 ++++++++++++++++++++++++++++++++-----
 1 file changed, 61 insertions(+), 9 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 8390b646c0ae..02c488c80b7e 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -321,6 +321,7 @@ struct scarlett2_notification {
 	void (*func)(struct usb_mixer_interface *mixer);
 };
 
+static void scarlett2_notify_ack(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_sync(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer);
@@ -343,7 +344,7 @@ static void scarlett2_notify_pcm_input_switch(
 /* Arrays of notification callback functions */
 
 static const struct scarlett2_notification scarlett2_notifications[] = {
-	{ 0x00000001, NULL }, /* ack, gets ignored */
+	{ 0x00000001, scarlett2_notify_ack },
 	{ 0x00000008, scarlett2_notify_sync },
 	{ 0x00200000, scarlett2_notify_dim_mute },
 	{ 0x00400000, scarlett2_notify_monitor },
@@ -353,14 +354,14 @@ static const struct scarlett2_notification scarlett2_notifications[] = {
 };
 
 static const struct scarlett2_notification scarlett3a_notifications[] = {
-	{ 0x00000001, NULL }, /* ack, gets ignored */
+	{ 0x00000001, scarlett2_notify_ack },
 	{ 0x00800000, scarlett2_notify_input_other },
 	{ 0x01000000, scarlett2_notify_direct_monitor },
 	{ 0, NULL }
 };
 
 static const struct scarlett2_notification scarlett4_solo_notifications[] = {
-	{ 0x00000001, NULL }, /* ack, gets ignored */
+	{ 0x00000001, scarlett2_notify_ack },
 	{ 0x00000008, scarlett2_notify_sync },
 	{ 0x00400000, scarlett2_notify_input_air },
 	{ 0x00800000, scarlett2_notify_direct_monitor },
@@ -371,7 +372,7 @@ static const struct scarlett2_notification scarlett4_solo_notifications[] = {
 };
 
 static const struct scarlett2_notification scarlett4_2i2_notifications[] = {
-	{ 0x00000001, NULL }, /* ack, gets ignored */
+	{ 0x00000001, scarlett2_notify_ack },
 	{ 0x00000008, scarlett2_notify_sync },
 	{ 0x00200000, scarlett2_notify_input_safe },
 	{ 0x00400000, scarlett2_notify_autogain },
@@ -387,7 +388,7 @@ static const struct scarlett2_notification scarlett4_2i2_notifications[] = {
 };
 
 static const struct scarlett2_notification scarlett4_4i4_notifications[] = {
-	{ 0x00000001, NULL }, /* ack, gets ignored */
+	{ 0x00000001, scarlett2_notify_ack },
 	{ 0x00000008, scarlett2_notify_sync },
 	{ 0x00200000, scarlett2_notify_input_safe },
 	{ 0x00400000, scarlett2_notify_autogain },
@@ -942,7 +943,9 @@ struct scarlett2_device_info {
 struct scarlett2_data {
 	struct usb_mixer_interface *mixer;
 	struct mutex usb_mutex; /* prevent sending concurrent USB requests */
+	struct completion cmd_done;
 	struct mutex data_mutex; /* lock access to this data */
+	u8 running;
 	u8 hwdep_in_use;
 	u8 selected_flash_segment_id;
 	u8 flash_write_state;
@@ -1960,6 +1963,17 @@ static int scarlett2_usb(
 		goto unlock;
 	}
 
+	if (!wait_for_completion_timeout(&private->cmd_done,
+					 msecs_to_jiffies(1000))) {
+		usb_audio_err(
+			mixer->chip,
+			"%s USB request timed out, cmd %x\n",
+			private->series_name, cmd);
+
+		err = -ETIMEDOUT;
+		goto unlock;
+	}
+
 	/* send a second message to get the response */
 
 	err = scarlett2_usb_rx(dev, private->bInterfaceNumber,
@@ -6702,6 +6716,18 @@ static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
 	scarlett2_notify_mux(mixer);
 }
 
+/* Handle acknowledgement that a command was received; let
+ * scarlett2_usb() know that it can proceed
+ */
+static void scarlett2_notify_ack(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+
+	/* if running == 0, ignore ACKs */
+	if (private->running)
+		complete(&private->cmd_done);
+}
+
 /* Interrupt callback */
 static void scarlett2_notify(struct urb *urb)
 {
@@ -6718,6 +6744,12 @@ static void scarlett2_notify(struct urb *urb)
 
 	data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
 
+	/* Ignore notifications except ACK during initialisation.
+	 * ACK is 0x00000001 on every device.
+	 */
+	if (private->running < 2)
+		data &= 1;
+
 	while (data && notifications->mask) {
 		if (data & notifications->mask) {
 			data &= ~notifications->mask;
@@ -6738,6 +6770,8 @@ static void scarlett2_notify(struct urb *urb)
 	    ustatus != -ESHUTDOWN) {
 		urb->dev = mixer->chip->dev;
 		usb_submit_urb(urb, GFP_ATOMIC);
+	} else {
+		complete(&private->cmd_done);
 	}
 }
 
@@ -6889,6 +6923,8 @@ static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
 			 transfer_buffer, private->wMaxPacketSize,
 			 scarlett2_notify, mixer, private->bInterval);
 
+	init_completion(&private->cmd_done);
+
 	return usb_submit_urb(mixer->urb, GFP_KERNEL);
 }
 
@@ -6911,6 +6947,24 @@ static int scarlett2_usb_init(struct usb_mixer_interface *mixer)
 	if (err < 0)
 		return err;
 
+	/* Set up the interrupt polling for notifications.
+	 * When running is:
+	 * 0: all notifications are ignored
+	 * 1: only ACKs are handled
+	 * 2: all notifications are handled
+	 */
+	err = scarlett2_init_notify(mixer);
+	if (err < 0)
+		return err;
+
+	/* sleep for a moment in case of an outstanding ACK */
+	msleep(20);
+
+	/* start handling ACKs, but no other notifications until the
+	 * ALSA controls have been created
+	 */
+	private->running = 1;
+
 	/* step 1 */
 	private->scarlett2_seq = 1;
 	err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0);
@@ -7308,10 +7362,8 @@ static int snd_scarlett2_controls_create(
 		scarlett2_phantom_update_access(mixer);
 	}
 
-	/* Set up the interrupt polling */
-	err = scarlett2_init_notify(mixer);
-	if (err < 0)
-		return err;
+	/* Start handling all notifications */
+	private->running = 2;
 
 	return 0;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 03/14] ALSA: scarlett2: Add support for reading from flash
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
  2024-03-12 18:33 ` [PATCH 01/14] ALSA: scarlett2: Move initialisation code lower in the source Geoffrey D. Bennett
  2024-03-12 18:34 ` [PATCH 02/14] ALSA: scarlett2: Implement handling of the ACK notification Geoffrey D. Bennett
@ 2024-03-12 18:34 ` Geoffrey D. Bennett
  2024-03-12 18:34 ` [PATCH 04/14] ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr Geoffrey D. Bennett
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:34 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Add hwdep read op so flash segments can be read.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 88 +++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 4 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 02c488c80b7e..981ec48a811a 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -1859,6 +1859,7 @@ static int scarlett2_get_port_start_num(
 #define SCARLETT2_USB_ERASE_SEGMENT 0x00004002
 #define SCARLETT2_USB_GET_ERASE     0x00004003
 #define SCARLETT2_USB_WRITE_SEGMENT 0x00004004
+#define SCARLETT2_USB_READ_SEGMENT  0x00004005
 #define SCARLETT2_USB_GET_SYNC      0x00006004
 #define SCARLETT2_USB_GET_DATA      0x00800000
 #define SCARLETT2_USB_SET_DATA      0x00800001
@@ -1869,7 +1870,7 @@ static int scarlett2_get_port_start_num(
 #define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
 
 #define SCARLETT2_FLASH_BLOCK_SIZE 4096
-#define SCARLETT2_FLASH_WRITE_MAX 1024
+#define SCARLETT2_FLASH_RW_MAX 1024
 #define SCARLETT2_SEGMENT_NUM_MIN 1
 #define SCARLETT2_SEGMENT_NUM_MAX 4
 
@@ -7452,7 +7453,7 @@ static int scarlett2_reboot(struct usb_mixer_interface *mixer)
 	return scarlett2_usb(mixer, SCARLETT2_USB_REBOOT, NULL, 0, NULL, 0);
 }
 
-/* Select a flash segment for erasing (and possibly writing to) */
+/* Select a flash segment for reading/erasing/writing */
 static int scarlett2_ioctl_select_flash_segment(
 	struct usb_mixer_interface *mixer,
 	unsigned long arg)
@@ -7633,6 +7634,84 @@ static int scarlett2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
 	}
 }
 
+static long scarlett2_hwdep_read(struct snd_hwdep *hw,
+				 char __user *buf,
+				 long count, loff_t *offset)
+{
+	struct usb_mixer_interface *mixer = hw->private_data;
+	struct scarlett2_data *private = mixer->private_data;
+	int segment_id, segment_num, err;
+	int flash_size;
+
+	/* SCARLETT2_USB_READ_SEGMENT request data */
+	struct {
+		__le32 segment_num;
+		__le32 offset;
+		__le32 len;
+	} __packed req;
+
+	u8 *resp;
+
+	/* Flash segment must first be selected */
+	if (private->flash_write_state != SCARLETT2_FLASH_WRITE_STATE_SELECTED)
+		return -EINVAL;
+
+	/* Get the selected flash segment number */
+	segment_id = private->selected_flash_segment_id;
+	if (segment_id < 0 || segment_id >= SCARLETT2_SEGMENT_ID_COUNT)
+		return -EINVAL;
+
+	segment_num = private->flash_segment_nums[segment_id];
+	if (segment_num < 0 ||
+	    segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+		return -EFAULT;
+
+	/* Validate the offset and count */
+	if (count < 0 || *offset < 0)
+		return -EINVAL;
+
+	/* Reached EOF? */
+	flash_size = private->flash_segment_blocks[segment_id] *
+		     SCARLETT2_FLASH_BLOCK_SIZE;
+	if (!count || *offset >= flash_size)
+		return 0;
+
+	/* Limit the numbers of bytes read to SCARLETT2_FLASH_RW_MAX */
+	if (count > SCARLETT2_FLASH_RW_MAX)
+		count = SCARLETT2_FLASH_RW_MAX;
+
+	/* Limit read to EOF */
+	if (*offset + count >= flash_size)
+		count = flash_size - *offset;
+
+	/* Create and send the request */
+	req.segment_num = cpu_to_le32(segment_num);
+	req.offset = cpu_to_le32(*offset);
+	req.len = cpu_to_le32(count);
+
+	resp = kzalloc(count, GFP_KERNEL);
+	if (!resp)
+		return -ENOMEM;
+
+	err = scarlett2_usb(mixer, SCARLETT2_USB_READ_SEGMENT,
+			    &req, sizeof(req), resp, count);
+	if (err < 0)
+		goto error;
+
+	/* Copy the response to userspace */
+	if (copy_to_user(buf, resp, count)) {
+		err = -EFAULT;
+		goto error;
+	}
+
+	*offset += count;
+	err = count;
+
+error:
+	kfree(resp);
+	return err;
+}
+
 static long scarlett2_hwdep_write(struct snd_hwdep *hw,
 				  const char __user *buf,
 				  long count, loff_t *offset)
@@ -7651,7 +7730,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
 	} __packed *req;
 
 	/* Calculate the maximum permitted in data[] */
-	const size_t max_data_size = SCARLETT2_FLASH_WRITE_MAX -
+	const size_t max_data_size = SCARLETT2_FLASH_RW_MAX -
 				     offsetof(typeof(*req), data);
 
 	/* If erasing, wait for it to complete */
@@ -7688,7 +7767,7 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
 	if (!count)
 		return 0;
 
-	/* Limit the *req size to SCARLETT2_FLASH_WRITE_MAX */
+	/* Limit the *req size to SCARLETT2_FLASH_RW_MAX */
 	if (count > max_data_size)
 		count = max_data_size;
 
@@ -7749,6 +7828,7 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer)
 	hw->exclusive = 1;
 	hw->ops.open = scarlett2_hwdep_open;
 	hw->ops.ioctl = scarlett2_hwdep_ioctl;
+	hw->ops.read = scarlett2_hwdep_read;
 	hw->ops.write = scarlett2_hwdep_write;
 	hw->ops.release = scarlett2_hwdep_release;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 04/14] ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (2 preceding siblings ...)
  2024-03-12 18:34 ` [PATCH 03/14] ALSA: scarlett2: Add support for reading from flash Geoffrey D. Bennett
@ 2024-03-12 18:34 ` Geoffrey D. Bennett
  2024-03-12 18:35 ` [PATCH 05/14] ALSA: scarlett2: Add pbuf field to struct scarlett2_config Geoffrey D. Bennett
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:34 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

The location pointed to by gen4_write_addr and gen4_write_addr + 1 is
officially known as the parameter buffer. Update the code to match.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 37 ++++++++++++++++++-------------------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 981ec48a811a..6031bfd98137 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -438,9 +438,9 @@ enum {
 /* Location, size, and activation command number for the configuration
  * parameters. Size is in bits and may be 0, 1, 8, or 16.
  *
- * A size of 0 indicates that the parameter is a byte-sized Scarlett
- * Gen 4 configuration which is written through the gen4_write_addr
- * location (but still read through the given offset location).
+ * A size of 0 indicates that the parameter is a byte-sized
+ * configuration which is set through the parameter buffer (but still
+ * read through the given offset location).
  *
  * Some Gen 4 configuration parameters are written with 0x02 for a
  * desired value of 0x01, and 0x03 for 0x00. These are indicated with
@@ -457,7 +457,7 @@ struct scarlett2_config {
 
 struct scarlett2_config_set {
 	const struct scarlett2_notification *notifications;
-	u16 gen4_write_addr;
+	u16 param_buf_addr;
 	const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
 };
 
@@ -625,7 +625,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = {
 /* Solo Gen 4 */
 static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
 	.notifications = scarlett4_solo_notifications,
-	.gen4_write_addr = 0xd8,
+	.param_buf_addr = 0xd8,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x47, .size = 8, .activate = 4 },
@@ -653,7 +653,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
 /* 2i2 Gen 4 */
 static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 	.notifications = scarlett4_2i2_notifications,
-	.gen4_write_addr = 0xfc,
+	.param_buf_addr = 0xfc,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ??
@@ -696,7 +696,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 /* 4i4 Gen 4 */
 static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
 	.notifications = scarlett4_4i4_notifications,
-	.gen4_write_addr = 0x130,
+	.param_buf_addr = 0x130,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x5c, .size = 8, .activate = 4 },
@@ -2080,7 +2080,7 @@ static int scarlett2_usb_get_config(
 	if (!config_item->offset)
 		return -EFAULT;
 
-	/* Gen 4 style parameters are always 1 byte */
+	/* Writes to the parameter buffer are always 1 byte */
 	size = config_item->size ? config_item->size : 8;
 
 	/* For byte-sized parameters, retrieve directly into buf */
@@ -2167,23 +2167,23 @@ static int scarlett2_usb_set_config(
 	if (!config_item->offset)
 		return -EFAULT;
 
-	/* Gen 4 style writes are selected with size = 0;
+	/* Writes via the parameter buffer are selected with size = 0;
 	 * these are only byte-sized values written through a shared
 	 * location, different to the read address
 	 */
 	if (!config_item->size) {
-		if (!config_set->gen4_write_addr)
+		if (!config_set->param_buf_addr)
 			return -EFAULT;
 
-		/* Place index in gen4_write_addr + 1 */
+		/* Place index in param_buf_addr + 1 */
 		err = scarlett2_usb_set_data(
-			mixer, config_set->gen4_write_addr + 1, 1, index);
+			mixer, config_set->param_buf_addr + 1, 1, index);
 		if (err < 0)
 			return err;
 
-		/* Place value in gen4_write_addr */
+		/* Place value in param_buf_addr */
 		err = scarlett2_usb_set_data(
-			mixer, config_set->gen4_write_addr, 1, value);
+			mixer, config_set->param_buf_addr, 1, value);
 		if (err < 0)
 			return err;
 
@@ -2192,9 +2192,8 @@ static int scarlett2_usb_set_config(
 			mixer, config_item->activate);
 	}
 
-	/* Not-Gen 4 style needs NVRAM save, supports
-	 * bit-modification, and writing is done to the same place
-	 * that the value can be read from
+	/* Direct writes (not via the parameter buffer) need NVRAM
+	 * save and support bit-modification
 	 */
 
 	/* Cancel any pending NVRAM save */
@@ -2238,8 +2237,8 @@ static int scarlett2_usb_set_config(
 	if (err < 0)
 		return err;
 
-	/* Gen 2 style writes to Gen 4 devices don't need saving */
-	if (config_set->gen4_write_addr)
+	/* Writes via the parameter buffer don't need a separate save step */
+	if (config_set->param_buf_addr)
 		return 0;
 
 	/* Schedule the change to be written to NVRAM */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 05/14] ALSA: scarlett2: Add pbuf field to struct scarlett2_config
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (3 preceding siblings ...)
  2024-03-12 18:34 ` [PATCH 04/14] ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr Geoffrey D. Bennett
@ 2024-03-12 18:35 ` Geoffrey D. Bennett
  2024-03-12 18:35 ` [PATCH 06/14] ALSA: scarlett2: Add support for config items with size = 32 Geoffrey D. Bennett
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

scarlett2_usb_set_config() was using size = 0 as a signal to use the
parameter buffer. Replace that with an explicit indication (pbuf = 1),
as the upcoming Vocaster support has a config item written via the
parameter buffer with size = 1 rather than the implicit size of 8.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 85 ++++++++++++++++++++-----------------
 1 file changed, 46 insertions(+), 39 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 6031bfd98137..8d08f71bd4d6 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -436,11 +436,12 @@ enum {
 };
 
 /* Location, size, and activation command number for the configuration
- * parameters. Size is in bits and may be 0, 1, 8, or 16.
+ * parameters. Size is in bits and may be 1, 8, or 16.
  *
- * A size of 0 indicates that the parameter is a byte-sized
- * configuration which is set through the parameter buffer (but still
- * read through the given offset location).
+ * Vocaster and 4th Gen devices have a parameter buffer to set certain
+ * configuration parameters. When pbuf is set, rather than writing to
+ * the given offset, the channel and value are written to the
+ * parameter buffer and the activate command is sent to the device.
  *
  * Some Gen 4 configuration parameters are written with 0x02 for a
  * desired value of 0x01, and 0x03 for 0x00. These are indicated with
@@ -452,6 +453,7 @@ struct scarlett2_config {
 	u16 offset;
 	u8 size;
 	u8 activate;
+	u8 pbuf;
 	u8 mute;
 };
 
@@ -631,19 +633,21 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
 			.offset = 0x47, .size = 8, .activate = 4 },
 
 		[SCARLETT2_CONFIG_DIRECT_MONITOR] = {
-			.offset = 0x108, .activate = 12 },
+			.offset = 0x108, .size = 8, .activate = 12, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
-			.offset = 0x46, .activate = 9, .mute = 1 },
+			.offset = 0x46, .size = 8, .activate = 9, .pbuf = 1,
+			.mute = 1 },
 
 		[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
-			.offset = 0x3d, .activate = 10, .mute = 1 },
+			.offset = 0x3d, .size = 8, .activate = 10, .pbuf = 1,
+			.mute = 1 },
 
 		[SCARLETT2_CONFIG_AIR_SWITCH] = {
-			.offset = 0x3e, .activate = 11 },
+			.offset = 0x3e, .size = 8, .activate = 11, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = {
-			.offset = 0x206, .activate = 25 },
+			.offset = 0x206, .size = 8, .activate = 25, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
 			.offset = 0x232, .size = 16, .activate = 26 }
@@ -656,37 +660,39 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 	.param_buf_addr = 0xfc,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
-			.offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ??
+			.offset = 0x49, .size = 8, .activate = 4 },
 
 		[SCARLETT2_CONFIG_DIRECT_MONITOR] = {
-			.offset = 0x14a, .activate = 16 },
+			.offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
-			.offset = 0x135, .activate = 10 },
+			.offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
-			.offset = 0x137 },
+			.offset = 0x137, .size = 8 },
 
 		[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
-			.offset = 0x48, .activate = 11, .mute = 1 },
+			.offset = 0x48, .size = 8, .activate = 11, .pbuf = 1,
+			.mute = 1 },
 
 		[SCARLETT2_CONFIG_INPUT_GAIN] = {
-			.offset = 0x4b, .activate = 12 },
+			.offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
-			.offset = 0x3c, .activate = 13, .mute = 1 },
+			.offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1,
+			.mute = 1 },
 
 		[SCARLETT2_CONFIG_SAFE_SWITCH] = {
-			.offset = 0x147, .activate = 14 },
+			.offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_AIR_SWITCH] = {
-			.offset = 0x3e, .activate = 15 },
+			.offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
-			.offset = 0x14b, .activate = 17 },
+			.offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
-			.offset = 0x14e, .activate = 18 },
+			.offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
 			.offset = 0x2a0, .size = 16, .activate = 36 }
@@ -702,31 +708,33 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
 			.offset = 0x5c, .size = 8, .activate = 4 },
 
 		[SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
-			.offset = 0x13e, .activate = 10 },
+			.offset = 0x13e, .size = 8, .activate = 10, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
-			.offset = 0x140 },
+			.offset = 0x140, .size = 8 },
 
 		[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
-			.offset = 0x5a, .activate = 11, .mute = 1 },
+			.offset = 0x5a, .size = 8, .activate = 11, .pbuf = 1,
+			.mute = 1 },
 
 		[SCARLETT2_CONFIG_INPUT_GAIN] = {
-			.offset = 0x5e, .activate = 12 },
+			.offset = 0x5e, .size = 8, .activate = 12, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
-			.offset = 0x4e, .activate = 13, .mute = 1 },
+			.offset = 0x4e, .size = 8, .activate = 13, .pbuf = 1,
+			.mute = 1 },
 
 		[SCARLETT2_CONFIG_SAFE_SWITCH] = {
-			.offset = 0x150, .activate = 14 },
+			.offset = 0x150, .size = 8, .activate = 14, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_AIR_SWITCH] = {
-			.offset = 0x50, .activate = 15 },
+			.offset = 0x50, .size = 8, .activate = 15, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
-			.offset = 0x153, .activate = 16 },
+			.offset = 0x153, .size = 8, .activate = 16, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
-			.offset = 0x156, .activate = 17 },
+			.offset = 0x156, .size = 8, .activate = 17, .pbuf = 1 },
 
 		[SCARLETT2_CONFIG_MASTER_VOLUME] = {
 			.offset = 0x32, .size = 16 },
@@ -735,10 +743,10 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
 			.offset = 0x3a, .size = 16 },
 
 		[SCARLETT2_CONFIG_POWER_EXT] = {
-			.offset = 0x168 },
+			.offset = 0x168, .size = 8 },
 
 		[SCARLETT2_CONFIG_POWER_LOW] = {
-			.offset = 0x16d }
+			.offset = 0x16d, .size = 8 }
 	}
 };
 
@@ -2167,11 +2175,8 @@ static int scarlett2_usb_set_config(
 	if (!config_item->offset)
 		return -EFAULT;
 
-	/* Writes via the parameter buffer are selected with size = 0;
-	 * these are only byte-sized values written through a shared
-	 * location, different to the read address
-	 */
-	if (!config_item->size) {
+	/* Write via the parameter buffer? */
+	if (config_item->pbuf) {
 		if (!config_set->param_buf_addr)
 			return -EFAULT;
 
@@ -2187,7 +2192,7 @@ static int scarlett2_usb_set_config(
 		if (err < 0)
 			return err;
 
-		/* Request the interface do the write */
+		/* Activate the write through the parameter buffer */
 		return scarlett2_usb_activate_config(
 			mixer, config_item->activate);
 	}
@@ -2227,7 +2232,7 @@ static int scarlett2_usb_set_config(
 		value = tmp;
 	}
 
-	/* Send the configuration parameter data */
+	/* Write the new value */
 	err = scarlett2_usb_set_data(mixer, offset, size, value);
 	if (err < 0)
 		return err;
@@ -2237,7 +2242,9 @@ static int scarlett2_usb_set_config(
 	if (err < 0)
 		return err;
 
-	/* Writes via the parameter buffer don't need a separate save step */
+	/* Interfaces with parameter buffer writes don't need a
+	 * separate save step
+	 */
 	if (config_set->param_buf_addr)
 		return 0;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 06/14] ALSA: scarlett2: Add support for config items with size = 32
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (4 preceding siblings ...)
  2024-03-12 18:35 ` [PATCH 05/14] ALSA: scarlett2: Add pbuf field to struct scarlett2_config Geoffrey D. Bennett
@ 2024-03-12 18:35 ` Geoffrey D. Bennett
  2024-03-12 18:35 ` [PATCH 07/14] ALSA: scarlett2: Add additional input configuration parameters Geoffrey D. Bennett
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Update scarlett2_usb_get_config() to support 32-bit values which are
needed by the upcoming Vocaster support.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 8d08f71bd4d6..f2bbf90680f8 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -436,7 +436,7 @@ enum {
 };
 
 /* Location, size, and activation command number for the configuration
- * parameters. Size is in bits and may be 1, 8, or 16.
+ * parameters. Size is in bits and may be 1, 8, 16, or 32.
  *
  * Vocaster and 4th Gen devices have a parameter buffer to set certain
  * configuration parameters. When pbuf is set, rather than writing to
@@ -2102,6 +2102,11 @@ static int scarlett2_usb_get_config(
 
 			for (i = 0; i < count; i++, buf_16++)
 				*buf_16 = le16_to_cpu(*(__le16 *)buf_16);
+		} else if (size == 4) {
+			u32 *buf_32 = buf;
+
+			for (i = 0; i < count; i++, buf_32++)
+				*buf_32 = le32_to_cpu(*(__le32 *)buf_32);
 		}
 		return 0;
 	}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 07/14] ALSA: scarlett2: Add additional input configuration parameters
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (5 preceding siblings ...)
  2024-03-12 18:35 ` [PATCH 06/14] ALSA: scarlett2: Add support for config items with size = 32 Geoffrey D. Bennett
@ 2024-03-12 18:35 ` Geoffrey D. Bennett
  2024-03-12 18:35 ` [PATCH 08/14] ALSA: scarlett2: Define the maximum preamp input gain per-config-set Geoffrey D. Bennett
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

The 4th Gen Scarlett interfaces added software-controllable input gain
along with channel select, channel link, auto-gain, and "safe" mode.
Vocaster has software-controllable input gain and auto-gain but not
channel select, channel link, or safe mode.

Add a device info field safe_input_count to indicate how many channels
have a safe mode control, and use the presence of the input select and
input link switch configuration parameters to determine if those
controls should be created.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 140 +++++++++++++++++++++---------------
 1 file changed, 82 insertions(+), 58 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index f2bbf90680f8..4289661b453f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -916,6 +916,9 @@ struct scarlett2_device_info {
 	/* the number of inputs with software-controllable gain */
 	u8 gain_input_count;
 
+	/* the number of inputs with safe mode */
+	u8 safe_input_count;
+
 	/* the number of direct monitor options
 	 * (0 = none, 1 = mono only, 2 = mono/stereo)
 	 */
@@ -1550,6 +1553,7 @@ static const struct scarlett2_device_info s2i2_gen4_info = {
 	.phantom_count = 1,
 	.inputs_per_phantom = 2,
 	.gain_input_count = 2,
+	.safe_input_count = 2,
 	.direct_monitor = 2,
 	.dsp_count = 2,
 
@@ -1603,6 +1607,7 @@ static const struct scarlett2_device_info s4i4_gen4_info = {
 	.phantom_count = 2,
 	.inputs_per_phantom = 1,
 	.gain_input_count = 2,
+	.safe_input_count = 2,
 	.dsp_count = 2,
 
 	.port_count = {
@@ -2937,13 +2942,18 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
 	int val = !scarlett2_autogain_is_running(private);
 	int i;
 
-	scarlett2_set_ctl_access(private->input_select_ctl, val);
-	for (i = 0; i < info->gain_input_count / 2; i++)
-		scarlett2_set_ctl_access(private->input_link_ctls[i], val);
-	for (i = 0; i < info->gain_input_count; i++) {
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_INPUT_SELECT_SWITCH))
+		scarlett2_set_ctl_access(private->input_select_ctl, val);
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_INPUT_LINK_SWITCH))
+		for (i = 0; i < info->gain_input_count / 2; i++)
+			scarlett2_set_ctl_access(private->input_link_ctls[i],
+						 val);
+	for (i = 0; i < info->gain_input_count; i++)
 		scarlett2_set_ctl_access(private->input_gain_ctls[i], val);
+	for (i = 0; i < info->safe_input_count; i++)
 		scarlett2_set_ctl_access(private->safe_ctls[i], val);
-	}
 	for (i = 0; i < info->level_input_count; i++)
 		scarlett2_set_ctl_access(private->level_ctls[i], val);
 	for (i = 0; i < info->air_input_count; i++)
@@ -2962,17 +2972,21 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
 	const struct scarlett2_device_info *info = private->info;
 	int i;
 
-	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
-		       &private->input_select_ctl->id);
-	for (i = 0; i < info->gain_input_count / 2; i++)
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_INPUT_SELECT_SWITCH))
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
-			       &private->input_link_ctls[i]->id);
-	for (i = 0; i < info->gain_input_count; i++) {
+			       &private->input_select_ctl->id);
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_INPUT_LINK_SWITCH))
+		for (i = 0; i < info->gain_input_count / 2; i++)
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+				       &private->input_link_ctls[i]->id);
+	for (i = 0; i < info->gain_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->input_gain_ctls[i]->id);
+	for (i = 0; i < info->safe_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->safe_ctls[i]->id);
-	}
 	for (i = 0; i < info->level_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->level_ctls[i]->id);
@@ -3183,7 +3197,9 @@ static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
 
 	private->input_select_updated = 0;
 
-	if (!link_count)
+	if (!scarlett2_has_config_item(private,
+				       SCARLETT2_CONFIG_INPUT_SELECT_SWITCH) ||
+	    !link_count)
 		return 0;
 
 	err = scarlett2_usb_get_config(
@@ -3596,12 +3612,12 @@ static int scarlett2_update_input_safe(struct usb_mixer_interface *mixer)
 
 	private->input_safe_updated = 0;
 
-	if (!info->gain_input_count)
+	if (!info->safe_input_count)
 		return 0;
 
 	return scarlett2_usb_get_config(
 		mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
-		info->gain_input_count, private->safe_switch);
+		info->safe_input_count, private->safe_switch);
 }
 
 static int scarlett2_safe_ctl_get(struct snd_kcontrol *kctl,
@@ -5507,60 +5523,67 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
 			return err;
 	}
 
-	/* Add software-controllable input gain controls */
-	if (info->gain_input_count) {
+	/* Add input select/link controls */
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_INPUT_SELECT_SWITCH)) {
 		err = scarlett2_add_new_ctl(
 			mixer, &scarlett2_input_select_ctl, 0, 1,
 			"Input Select Capture Enum",
 			&private->input_select_ctl);
 		if (err < 0)
 			return err;
+	}
 
-		for (i = 0; i < info->gain_input_count; i++) {
-			if (i % 2) {
-				scnprintf(s, sizeof(s),
-					  "Line In %d-%d Link Capture Switch",
-					  i, i + 1);
-				err = scarlett2_add_new_ctl(
-					mixer, &scarlett2_input_link_ctl,
-					i / 2, 1, s,
-					&private->input_link_ctls[i / 2]);
-				if (err < 0)
-					return err;
-			}
-
-			scnprintf(s, sizeof(s), fmt, i + 1,
-				  "Gain", "Volume");
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_INPUT_LINK_SWITCH)) {
+		for (i = 0; i < info->gain_input_count / 2; i++) {
+			scnprintf(s, sizeof(s),
+				  "Line In %d-%d Link Capture Switch",
+				  (i * 2) + 1, (i * 2) + 2);
 			err = scarlett2_add_new_ctl(
-				mixer, &scarlett2_input_gain_ctl,
-				i, 1, s, &private->input_gain_ctls[i]);
-			if (err < 0)
-				return err;
-
-			scnprintf(s, sizeof(s), fmt, i + 1,
-				  "Autogain", "Switch");
-			err = scarlett2_add_new_ctl(
-				mixer, &scarlett2_autogain_switch_ctl,
-				i, 1, s, &private->autogain_ctls[i]);
-			if (err < 0)
-				return err;
-
-			scnprintf(s, sizeof(s), fmt, i + 1,
-				  "Autogain Status", "Enum");
-			err = scarlett2_add_new_ctl(
-				mixer, &scarlett2_autogain_status_ctl,
-				i, 1, s, &private->autogain_status_ctls[i]);
-
-			scnprintf(s, sizeof(s), fmt, i + 1,
-				  "Safe", "Switch");
-			err = scarlett2_add_new_ctl(
-				mixer, &scarlett2_safe_ctl,
-				i, 1, s, &private->safe_ctls[i]);
+				mixer, &scarlett2_input_link_ctl,
+				i, 1, s, &private->input_link_ctls[i]);
 			if (err < 0)
 				return err;
 		}
 	}
 
+	/* Add software-controllable input gain controls */
+	for (i = 0; i < info->gain_input_count; i++) {
+		scnprintf(s, sizeof(s), fmt, i + 1,
+			  "Gain", "Volume");
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_input_gain_ctl,
+			i, 1, s, &private->input_gain_ctls[i]);
+		if (err < 0)
+			return err;
+
+		scnprintf(s, sizeof(s), fmt, i + 1,
+			  "Autogain", "Switch");
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_autogain_switch_ctl,
+			i, 1, s, &private->autogain_ctls[i]);
+		if (err < 0)
+			return err;
+
+		scnprintf(s, sizeof(s), fmt, i + 1,
+			  "Autogain Status", "Enum");
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_autogain_status_ctl,
+			i, 1, s, &private->autogain_status_ctls[i]);
+	}
+
+	/* Add safe-mode input switch controls */
+	for (i = 0; i < info->safe_input_count; i++) {
+		scnprintf(s, sizeof(s), fmt, i + 1,
+			  "Safe", "Switch");
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_safe_ctl,
+			i, 1, s, &private->safe_ctls[i]);
+		if (err < 0)
+			return err;
+	}
+
 	/* Add PCM Input Switch control */
 	if (scarlett2_has_config_item(private,
 				      SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
@@ -6557,7 +6580,8 @@ static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
 	const struct scarlett2_device_info *info = private->info;
 	int i;
 
-	if (!info->gain_input_count)
+	if (!scarlett2_has_config_item(private,
+				       SCARLETT2_CONFIG_INPUT_SELECT_SWITCH))
 		return;
 
 	private->input_select_updated = 1;
@@ -6620,12 +6644,12 @@ static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer)
 	const struct scarlett2_device_info *info = private->info;
 	int i;
 
-	if (!info->gain_input_count)
+	if (!info->safe_input_count)
 		return;
 
 	private->input_safe_updated = 1;
 
-	for (i = 0; i < info->gain_input_count; i++)
+	for (i = 0; i < info->safe_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
 			       &private->safe_ctls[i]->id);
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 08/14] ALSA: scarlett2: Define the maximum preamp input gain per-config-set
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (6 preceding siblings ...)
  2024-03-12 18:35 ` [PATCH 07/14] ALSA: scarlett2: Add additional input configuration parameters Geoffrey D. Bennett
@ 2024-03-12 18:35 ` Geoffrey D. Bennett
  2024-03-12 18:36 ` [PATCH 09/14] ALSA: scarlett2: Define autogain status texts per-config-set Geoffrey D. Bennett
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Remove the #define SCARLETT2_MAX_GAIN_DB and replace with a
per-config-set TLV as the Vocaster has a maximum gain of 70dB vs the
4th Gen 69dB.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 4289661b453f..a891e92048b2 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -174,11 +174,10 @@
 /* some gui mixers can't handle negative ctl values */
 #define SCARLETT2_VOLUME_BIAS 127
 
-/* maximum preamp input gain and value
- * values are from 0 to 70, preamp gain is from 0 to 69 dB
+/* maximum preamp input gain value
+ * (the corresponding value in dB is per-device)
  */
 #define SCARLETT2_MAX_GAIN_VALUE 70
-#define SCARLETT2_MAX_GAIN_DB 69
 
 /* mixer range from -80dB to +6dB in 0.5dB steps */
 #define SCARLETT2_MIXER_MIN_DB -80
@@ -460,9 +459,15 @@ struct scarlett2_config {
 struct scarlett2_config_set {
 	const struct scarlett2_notification *notifications;
 	u16 param_buf_addr;
+	const unsigned int *input_gain_tlv;
 	const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
 };
 
+/* Input gain TLV dB ranges */
+static const DECLARE_TLV_DB_MINMAX(
+	db_scale_gen4_gain, 0, 69 * 100
+);
+
 /* Gen 2 devices without SW/HW volume switch: 6i6, 18i8 */
 
 static const struct scarlett2_config_set scarlett2_config_set_gen2a = {
@@ -658,6 +663,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
 static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 	.notifications = scarlett4_2i2_notifications,
 	.param_buf_addr = 0xfc,
+	.input_gain_tlv = db_scale_gen4_gain,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x49, .size = 8, .activate = 4 },
@@ -703,6 +709,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
 	.notifications = scarlett4_4i4_notifications,
 	.param_buf_addr = 0x130,
+	.input_gain_tlv = db_scale_gen4_gain,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x5c, .size = 8, .activate = 4 },
@@ -3587,10 +3594,6 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl,
 	return err;
 }
 
-static const DECLARE_TLV_DB_MINMAX(
-	db_scale_scarlett2_gain, 0, SCARLETT2_MAX_GAIN_DB * 100
-);
-
 static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -3600,7 +3603,6 @@ static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
 	.get  = scarlett2_input_gain_ctl_get,
 	.put  = scarlett2_input_gain_ctl_put,
 	.private_value = 0, /* max value */
-	.tlv = { .p = db_scale_scarlett2_gain }
 };
 
 /*** Safe Controls ***/
@@ -5557,6 +5559,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
 			i, 1, s, &private->input_gain_ctls[i]);
 		if (err < 0)
 			return err;
+		private->input_gain_ctls[i]->tlv.p =
+			private->config_set->input_gain_tlv;
 
 		scnprintf(s, sizeof(s), fmt, i + 1,
 			  "Autogain", "Switch");
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 09/14] ALSA: scarlett2: Define autogain status texts per-config-set
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (7 preceding siblings ...)
  2024-03-12 18:35 ` [PATCH 08/14] ALSA: scarlett2: Define the maximum preamp input gain per-config-set Geoffrey D. Bennett
@ 2024-03-12 18:36 ` Geoffrey D. Bennett
  2024-03-12 18:36 ` [PATCH 10/14] ALSA: scarlett2: Add input mute controls Geoffrey D. Bennett
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:36 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

The autogain status texts are different for Vocaster vs. Scarlett 4th
Gen, so make them configurable per-config-set.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index a891e92048b2..0962277947bf 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -294,7 +294,7 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
  * If autogain_switch is set, autogain_status is set to 0 (Running).
  * The other status values are from the raw_autogain_status value + 1.
  */
-static const char *const scarlett2_autogain_status_texts[] = {
+static const char *const scarlett2_autogain_status_gen4[] = {
 	"Running",
 	"Success",
 	"SuccessDRover",
@@ -303,7 +303,8 @@ static const char *const scarlett2_autogain_status_texts[] = {
 	"FailMaxGainLimit",
 	"FailClipped",
 	"Cancelled",
-	"Invalid"
+	"Invalid",
+	NULL
 };
 
 /* Power Status Values */
@@ -460,6 +461,7 @@ struct scarlett2_config_set {
 	const struct scarlett2_notification *notifications;
 	u16 param_buf_addr;
 	const unsigned int *input_gain_tlv;
+	const char *const *autogain_status_texts;
 	const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
 };
 
@@ -664,6 +666,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 	.notifications = scarlett4_2i2_notifications,
 	.param_buf_addr = 0xfc,
 	.input_gain_tlv = db_scale_gen4_gain,
+	.autogain_status_texts = scarlett2_autogain_status_gen4,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x49, .size = 8, .activate = 4 },
@@ -710,6 +713,7 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
 	.notifications = scarlett4_4i4_notifications,
 	.param_buf_addr = 0x130,
 	.input_gain_tlv = db_scale_gen4_gain,
+	.autogain_status_texts = scarlett2_autogain_status_gen4,
 	.items = {
 		[SCARLETT2_CONFIG_MSD_SWITCH] = {
 			.offset = 0x5c, .size = 8, .activate = 4 },
@@ -981,6 +985,7 @@ struct scarlett2_data {
 	u8 num_mix_out;
 	u8 num_line_out;
 	u8 num_monitor_mix_ctls;
+	u8 num_autogain_status_texts;
 	u32 firmware_version;
 	u8 flash_segment_nums[SCARLETT2_SEGMENT_ID_COUNT];
 	u8 flash_segment_blocks[SCARLETT2_SEGMENT_ID_COUNT];
@@ -2931,12 +2936,12 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
 		if (private->autogain_switch[i])
 			private->autogain_status[i] = 0;
 		else if (raw_autogain_status[i] <
-				ARRAY_SIZE(scarlett2_autogain_status_texts) - 1)
+				private->num_autogain_status_texts - 1)
 			private->autogain_status[i] =
 				raw_autogain_status[i] + 1;
 		else
 			private->autogain_status[i] =
-				ARRAY_SIZE(scarlett2_autogain_status_texts) - 1;
+				private->num_autogain_status_texts - 1;
 
 	return 0;
 }
@@ -3171,10 +3176,13 @@ static int scarlett2_autogain_switch_ctl_put(
 static int scarlett2_autogain_status_ctl_info(
 	struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
 {
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct scarlett2_data *private = elem->head.mixer->private_data;
+
 	return snd_ctl_enum_info(
 		uinfo, 1,
-		ARRAY_SIZE(scarlett2_autogain_status_texts),
-		scarlett2_autogain_status_texts);
+		private->num_autogain_status_texts,
+		private->config_set->autogain_status_texts);
 }
 
 static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = {
@@ -6839,8 +6847,9 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
 static void scarlett2_count_io(struct scarlett2_data *private)
 {
 	const struct scarlett2_device_info *info = private->info;
+	const struct scarlett2_config_set *config_set = info->config_set;
 	const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
-	int port_type, srcs = 0, dsts = 0;
+	int port_type, srcs = 0, dsts = 0, i;
 
 	/* Count the number of mux sources and destinations */
 	for (port_type = 0;
@@ -6872,6 +6881,15 @@ static void scarlett2_count_io(struct scarlett2_data *private)
 	/* Number of monitor mix controls */
 	private->num_monitor_mix_ctls =
 		info->direct_monitor * 2 * private->num_mix_in;
+
+	/* Number of autogain status texts */
+	if (config_set->autogain_status_texts) {
+		const char * const *texts = config_set->autogain_status_texts;
+
+		for (i = 0; texts[i]; i++)
+			;
+		private->num_autogain_status_texts = i;
+	}
 }
 
 /* Look through the interface descriptors for the Focusrite Control
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 10/14] ALSA: scarlett2: Add input mute controls
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (8 preceding siblings ...)
  2024-03-12 18:36 ` [PATCH 09/14] ALSA: scarlett2: Define autogain status texts per-config-set Geoffrey D. Bennett
@ 2024-03-12 18:36 ` Geoffrey D. Bennett
  2024-03-12 18:37 ` [PATCH 11/14] ALSA: scarlett2: Add DSP controls Geoffrey D. Bennett
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:36 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Add controls for the input mute switches that the Vocaster interfaces
have. Mark scarlett2_notify_input_mute() as __always_unused until it
gets used when the Vocaster callback function array is added.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 142 +++++++++++++++++++++++++++++++++++-
 1 file changed, 141 insertions(+), 1 deletion(-)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 0962277947bf..17fadfddc240 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -85,7 +85,7 @@
  *    controls
  *  - disable/enable MSD mode
  *  - disable/enable standalone mode
- *  - input gain, autogain, safe mode
+ *  - input mute, gain, autogain, safe mode
  *  - direct monitor mixes
  *
  * <ditaa>
@@ -214,6 +214,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_INPUT_MUTE_SWITCH_MAX 2
 #define SCARLETT2_PHANTOM_SWITCH_MAX 2
 #define SCARLETT2_INPUT_GAIN_MAX 2
 
@@ -329,6 +330,7 @@ static void scarlett2_notify_volume(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer);
@@ -415,6 +417,7 @@ enum {
 	SCARLETT2_CONFIG_PAD_SWITCH,
 	SCARLETT2_CONFIG_MSD_SWITCH,
 	SCARLETT2_CONFIG_AIR_SWITCH,
+	SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
 	SCARLETT2_CONFIG_STANDALONE_SWITCH,
 	SCARLETT2_CONFIG_PHANTOM_SWITCH,
 	SCARLETT2_CONFIG_PHANTOM_PERSISTENCE,
@@ -915,6 +918,11 @@ struct scarlett2_device_info {
 	 */
 	u8 air_option;
 
+	/* the number of analogue inputs with a software switchable
+	 * mute control
+	 */
+	u8 mute_input_count;
+
 	/* the number of phantom (48V) software switchable controls */
 	u8 phantom_count;
 
@@ -996,6 +1004,7 @@ struct scarlett2_data {
 	u8 input_level_updated;
 	u8 input_pad_updated;
 	u8 input_air_updated;
+	u8 input_mute_updated;
 	u8 input_phantom_updated;
 	u8 input_select_updated;
 	u8 input_gain_updated;
@@ -1018,6 +1027,7 @@ 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 input_mute_switch[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
 	u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX];
 	u8 phantom_persistence;
 	u8 input_select_switch;
@@ -1045,6 +1055,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 *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
 	struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX];
 	struct snd_kcontrol *input_select_ctl;
 	struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2];
@@ -2970,6 +2981,8 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
 		scarlett2_set_ctl_access(private->level_ctls[i], val);
 	for (i = 0; i < info->air_input_count; i++)
 		scarlett2_set_ctl_access(private->air_ctls[i], val);
+	for (i = 0; i < info->mute_input_count; i++)
+		scarlett2_set_ctl_access(private->input_mute_ctls[i], val);
 	for (i = 0; i < info->phantom_count; i++)
 		scarlett2_set_ctl_access(private->phantom_ctls[i], val);
 }
@@ -3005,6 +3018,9 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
 	for (i = 0; i < info->air_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->air_ctls[i]->id);
+	for (i = 0; i < info->mute_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+			       &private->input_mute_ctls[i]->id);
 	for (i = 0; i < info->phantom_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->phantom_ctls[i]->id);
@@ -4619,6 +4635,100 @@ static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
 	}
 };
 
+/*** Input Mute Switch Controls ***/
+
+static int scarlett2_update_input_mute(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+
+	private->input_mute_updated = 0;
+
+	if (!info->mute_input_count)
+		return 0;
+
+	return scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
+		info->mute_input_count, private->input_mute_switch);
+}
+
+static int scarlett2_input_mute_ctl_get(struct snd_kcontrol *kctl,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int err = 0;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	if (private->input_mute_updated) {
+		err = scarlett2_update_input_mute(mixer);
+		if (err < 0)
+			goto unlock;
+	}
+	ucontrol->value.integer.value[0] =
+		private->input_mute_switch[elem->control];
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_input_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;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_put_during_autogain(mixer);
+	if (err < 0)
+		goto unlock;
+
+	oval = private->input_mute_switch[index];
+	val = ucontrol->value.integer.value[0];
+
+	if (oval == val)
+		goto unlock;
+
+	private->input_mute_switch[index] = val;
+
+	/* Send switch change to the device */
+	err = scarlett2_usb_set_config(
+		mixer, SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
+			index, val);
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_input_mute_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "",
+	.info = scarlett2_autogain_disables_ctl_info,
+	.get  = scarlett2_input_mute_ctl_get,
+	.put  = scarlett2_input_mute_ctl_put,
+};
+
 /*** Phantom Switch Controls ***/
 
 static int scarlett2_update_input_phantom(struct usb_mixer_interface *mixer)
@@ -5497,6 +5607,16 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
 			return err;
 	}
 
+	/* Add input mute controls */
+	for (i = 0; i < info->mute_input_count; i++) {
+		scnprintf(s, sizeof(s), fmt, i + 1, "Mute", "Switch");
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_input_mute_ctl,
+			i, 1, s, &private->input_mute_ctls[i]);
+		if (err < 0)
+			return err;
+	}
+
 	/* Add input phantom controls */
 	if (info->inputs_per_phantom == 1) {
 		for (i = 0; i < info->phantom_count; i++) {
@@ -6558,6 +6678,22 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
 			       &private->air_ctls[i]->id);
 }
 
+/* Notify on input mute switch change */
+static __always_unused void scarlett2_notify_input_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;
+	int i;
+
+	private->input_mute_updated = 1;
+
+	for (i = 0; i < info->mute_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->input_mute_ctls[i]->id);
+}
+
 /* Notify on input phantom switch change */
 static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer)
 {
@@ -7171,6 +7307,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
 	if (err < 0)
 		return err;
 
+	err = scarlett2_update_input_mute(mixer);
+	if (err < 0)
+		return err;
+
 	err = scarlett2_update_input_phantom(mixer);
 	if (err < 0)
 		return err;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 11/14] ALSA: scarlett2: Add DSP controls
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (9 preceding siblings ...)
  2024-03-12 18:36 ` [PATCH 10/14] ALSA: scarlett2: Add input mute controls Geoffrey D. Bennett
@ 2024-03-12 18:37 ` Geoffrey D. Bennett
  2024-03-12 18:37 ` [PATCH 12/14] ALSA: scarlett2: Add support for Focusrite Vocaster One and Two Geoffrey D. Bennett
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:37 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Add filter and compressor DSP controls for the Vocaster interfaces.
Mark scarlett2_notify_input_dsp() as __always_unused until it gets
used when the Vocaster callback function array is added.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 855 ++++++++++++++++++++++++++++++++++++
 1 file changed, 855 insertions(+)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 17fadfddc240..77bfaadb99b8 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -87,6 +87,7 @@
  *  - disable/enable standalone mode
  *  - input mute, gain, autogain, safe mode
  *  - direct monitor mixes
+ *  - compressor and EQ
  *
  * <ditaa>
  *    /--------------\    18chn            20chn     /--------------\
@@ -214,6 +215,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_DSP_SWITCH_MAX 2
 #define SCARLETT2_INPUT_MUTE_SWITCH_MAX 2
 #define SCARLETT2_PHANTOM_SWITCH_MAX 2
 #define SCARLETT2_INPUT_GAIN_MAX 2
@@ -245,6 +247,59 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
 /* Maximum number of meters (sum of output port counts) */
 #define SCARLETT2_MAX_METERS 65
 
+/* Compressor parameter data
+ *
+ * The compressor parameters are 32-bit fixed point values with 24
+ * bits of fraction. Integer values are sufficient for the parameters
+ * except for ratio which we can set in 0.5:1 steps.
+ */
+struct compressor_param {
+	const char          *name;
+	snd_ctl_elem_type_t  type;
+	s32                  min;
+	s32                  max;
+	int                  scale_bits;
+};
+
+/* The available compressor parameters on the Vocaster:
+ * - Enable: Off, On
+ * - Threshold: -40dB to 0dB
+ * - Ratio: 1:1 to 50:1 in 0.5:1 steps
+ * - Knee Width: 0dB to 10dB
+ * - Attack: 30ms to 127ms
+ * - Release: 30ms to 127ms
+ * - Makeup Gain: 0dB to 24dB
+ */
+static const struct compressor_param compressor_params[] = {
+	{ "Enable",      SNDRV_CTL_ELEM_TYPE_BOOLEAN,   0,   1,  0 },
+	{ "Threshold",   SNDRV_CTL_ELEM_TYPE_INTEGER, -40,   0, 24 },
+	{ "Ratio",       SNDRV_CTL_ELEM_TYPE_INTEGER,   2, 100, 23 },
+	{ "Knee Width",  SNDRV_CTL_ELEM_TYPE_INTEGER,   0,  10, 24 },
+	{ "Attack",      SNDRV_CTL_ELEM_TYPE_INTEGER,  30, 127, 24 },
+	{ "Release",     SNDRV_CTL_ELEM_TYPE_INTEGER,  30, 127, 24 },
+	{ "Makeup Gain", SNDRV_CTL_ELEM_TYPE_INTEGER,   0,  24, 24 },
+};
+
+#define SCARLETT2_COMPRESSOR_PARAM_COUNT ARRAY_SIZE(compressor_params)
+#define SCARLETT2_COMPRESSOR_CTLS_MAX \
+	(SCARLETT2_COMPRESSOR_PARAM_COUNT * SCARLETT2_DSP_SWITCH_MAX)
+
+/* Maximum number of filter controls */
+#define SCARLETT2_PRECOMP_FLT_CTLS_MAX (2 * SCARLETT2_DSP_SWITCH_MAX)
+#define SCARLETT2_PEQ_FLT_CTLS_MAX (3 * SCARLETT2_DSP_SWITCH_MAX)
+
+/* Number of biquad filter coefficients */
+#define SCARLETT2_BIQUAD_COEFFS 5
+
+/* Maximum number of filter coefficient values */
+#define SCARLETT2_PRECOMP_FLT_VALUES_MAX \
+	(SCARLETT2_PRECOMP_FLT_CTLS_MAX * SCARLETT2_BIQUAD_COEFFS)
+#define SCARLETT2_PEQ_FLT_VALUES_MAX \
+	(SCARLETT2_PEQ_FLT_CTLS_MAX * SCARLETT2_BIQUAD_COEFFS)
+
+/* Maximum number of PEQ filter slots */
+#define SCARLETT2_PEQ_FLT_SLOTS_MAX 4
+
 /* Hardware port types:
  * - None (no input to mux)
  * - Analogue I/O
@@ -330,6 +385,7 @@ static void scarlett2_notify_volume(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_dsp(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer);
@@ -417,6 +473,12 @@ enum {
 	SCARLETT2_CONFIG_PAD_SWITCH,
 	SCARLETT2_CONFIG_MSD_SWITCH,
 	SCARLETT2_CONFIG_AIR_SWITCH,
+	SCARLETT2_CONFIG_DSP_SWITCH,
+	SCARLETT2_CONFIG_COMPRESSOR_PARAMS,
+	SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
+	SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
+	SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
+	SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
 	SCARLETT2_CONFIG_INPUT_MUTE_SWITCH,
 	SCARLETT2_CONFIG_STANDALONE_SWITCH,
 	SCARLETT2_CONFIG_PHANTOM_SWITCH,
@@ -918,6 +980,18 @@ struct scarlett2_device_info {
 	 */
 	u8 air_option;
 
+	/* the number of analogue inputs with DSP control */
+	u8 dsp_input_count;
+
+	/* number of pre-compressor filters */
+	u8 precomp_flt_count;
+
+	/* number of parametric EQ filters */
+	u8 peq_flt_count;
+
+	/* number of PEQ filters plus unused slots */
+	u8 peq_flt_total_count;
+
 	/* the number of analogue inputs with a software switchable
 	 * mute control
 	 */
@@ -1004,6 +1078,7 @@ struct scarlett2_data {
 	u8 input_level_updated;
 	u8 input_pad_updated;
 	u8 input_air_updated;
+	u8 input_dsp_updated;
 	u8 input_mute_updated;
 	u8 input_phantom_updated;
 	u8 input_select_updated;
@@ -1027,6 +1102,12 @@ 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 dsp_switch[SCARLETT2_DSP_SWITCH_MAX];
+	s32 compressor_values[SCARLETT2_COMPRESSOR_CTLS_MAX];
+	s32 precomp_flt_values[SCARLETT2_PRECOMP_FLT_VALUES_MAX];
+	s32 peq_flt_values[SCARLETT2_PEQ_FLT_VALUES_MAX];
+	u8 precomp_flt_switch[SCARLETT2_DSP_SWITCH_MAX];
+	u8 peq_flt_switch[SCARLETT2_DSP_SWITCH_MAX];
 	u8 input_mute_switch[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
 	u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX];
 	u8 phantom_persistence;
@@ -1055,6 +1136,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 *dsp_ctls[SCARLETT2_DSP_SWITCH_MAX];
 	struct snd_kcontrol *input_mute_ctls[SCARLETT2_INPUT_MUTE_SWITCH_MAX];
 	struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX];
 	struct snd_kcontrol *input_select_ctl;
@@ -1066,6 +1148,11 @@ struct scarlett2_data {
 	struct snd_kcontrol *pcm_input_switch_ctl;
 	struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
 	struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX];
+	struct snd_kcontrol *compressor_ctls[SCARLETT2_COMPRESSOR_CTLS_MAX];
+	struct snd_kcontrol *precomp_flt_ctls[SCARLETT2_PRECOMP_FLT_CTLS_MAX];
+	struct snd_kcontrol *peq_flt_ctls[SCARLETT2_PEQ_FLT_CTLS_MAX];
+	struct snd_kcontrol *precomp_flt_switch_ctls[SCARLETT2_DSP_SWITCH_MAX];
+	struct snd_kcontrol *peq_flt_switch_ctls[SCARLETT2_DSP_SWITCH_MAX];
 	struct snd_kcontrol *direct_monitor_ctl;
 	struct snd_kcontrol *speaker_switching_ctl;
 	struct snd_kcontrol *talkback_ctl;
@@ -2174,6 +2261,54 @@ static int scarlett2_usb_set_data(
 			     &req, sizeof(u32) * 2 + size, NULL, 0);
 }
 
+/* Send a SCARLETT2_USB_SET_DATA command with multiple values.
+ * offset: location in the device's data space
+ * size: size in bytes of each value (1, 2, 4)
+ * count: number of values
+ */
+static int scarlett2_usb_set_data_buf(
+	struct usb_mixer_interface *mixer,
+	int offset, int size, int count, void *buf)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	int bytes = size * count;
+	struct {
+		__le32 offset;
+		__le32 size;
+		u8 data[];
+	} __packed *req;
+	int err;
+	int buf_size = struct_size(req, data, bytes);
+
+	req = kmalloc(buf_size, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->offset = cpu_to_le32(offset);
+	req->size = cpu_to_le32(bytes);
+	if (size == 1) {
+		memcpy(req->data, buf, count);
+	} else if (size == 2) {
+		u16 *buf_16 = buf;
+		int i;
+
+		for (i = 0; i < count; i++)
+			((__le16 *)req->data)[i] = cpu_to_le16(buf_16[i]);
+	} else {
+		u32 *buf_32 = buf;
+		int i;
+
+		for (i = 0; i < count; i++)
+			((__le32 *)req->data)[i] = cpu_to_le32(buf_32[i]);
+	}
+
+	err = scarlett2_usb(private->mixer, SCARLETT2_USB_SET_DATA,
+			    req, buf_size, NULL, 0);
+
+	kfree(req);
+	return err;
+}
+
 /* Send a SCARLETT2_USB_DATA_CMD command.
  * Configuration changes require activation with this after they have
  * been uploaded by a previous SCARLETT2_USB_SET_DATA.
@@ -2288,6 +2423,47 @@ static int scarlett2_usb_set_config(
 	return 0;
 }
 
+/* Send USB messages to set a SCARLETT2_CONFIG_* parameter with
+ * multiple values
+ */
+static int scarlett2_usb_set_config_buf(
+	struct usb_mixer_interface *mixer,
+	int config_item_num, int index, int count, void *buf)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_config_set *config_set = private->config_set;
+	const struct scarlett2_config *config_item =
+		&config_set->items[config_item_num];
+	int offset, size;
+	int err;
+
+	/* Check that the configuration item is present in the
+	 * configuration set used by this device
+	 */
+	if (!config_item->offset)
+		return -EFAULT;
+
+	/* 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;
+
+	/* Bit updates not supported */
+	} else {
+		return -EFAULT;
+	}
+
+	/* Write the new values */
+	err = scarlett2_usb_set_data_buf(mixer, offset, size, count, buf);
+	if (err < 0)
+		return err;
+
+	/* Activate the change */
+	return scarlett2_usb_activate_config(mixer, config_item->activate);
+}
+
 /* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
 static void scarlett2_config_save(struct usb_mixer_interface *mixer)
 {
@@ -2985,6 +3161,8 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
 		scarlett2_set_ctl_access(private->input_mute_ctls[i], val);
 	for (i = 0; i < info->phantom_count; i++)
 		scarlett2_set_ctl_access(private->phantom_ctls[i], val);
+	for (i = 0; i < info->dsp_input_count; i++)
+		scarlett2_set_ctl_access(private->dsp_ctls[i], val);
 }
 
 /* Notify of access mode change for all controls read-only while
@@ -3018,6 +3196,9 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
 	for (i = 0; i < info->air_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->air_ctls[i]->id);
+	for (i = 0; i < info->dsp_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+			       &private->dsp_ctls[i]->id);
 	for (i = 0; i < info->mute_input_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->input_mute_ctls[i]->id);
@@ -4635,6 +4816,576 @@ static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
 	}
 };
 
+/*** DSP Switch Control ***/
+
+static int scarlett2_update_input_dsp(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+
+	private->input_dsp_updated = 0;
+
+	if (!info->dsp_input_count)
+		return 0;
+
+	return scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_DSP_SWITCH,
+		info->dsp_input_count, private->dsp_switch);
+}
+
+static int scarlett2_dsp_ctl_get(struct snd_kcontrol *kctl,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int err = 0;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	if (private->input_dsp_updated) {
+		err = scarlett2_update_input_dsp(mixer);
+		if (err < 0)
+			goto unlock;
+	}
+	ucontrol->value.integer.value[0] = private->dsp_switch[elem->control];
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_dsp_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;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_put_during_autogain(mixer);
+	if (err < 0)
+		goto unlock;
+
+	oval = private->dsp_switch[index];
+	val = ucontrol->value.integer.value[0];
+
+	if (oval == val)
+		goto unlock;
+
+	private->dsp_switch[index] = val;
+
+	/* Send switch change to the device */
+	err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DSP_SWITCH,
+				       index, val);
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_dsp_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "",
+	.info = scarlett2_autogain_disables_ctl_info,
+	.get  = scarlett2_dsp_ctl_get,
+	.put  = scarlett2_dsp_ctl_put,
+};
+
+/*** DSP Compressor Parameter Controls ***/
+
+static int scarlett2_update_compressor_values(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int err, i, j;
+
+	if (!info->dsp_input_count)
+		return 0;
+
+	err = scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS,
+		SCARLETT2_COMPRESSOR_PARAM_COUNT * info->dsp_input_count,
+		private->compressor_values);
+
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < SCARLETT2_COMPRESSOR_PARAM_COUNT; i++) {
+		const struct compressor_param *param = &compressor_params[i];
+
+		for (j = 0; j < info->dsp_input_count; j++) {
+			int idx = i + j * SCARLETT2_COMPRESSOR_PARAM_COUNT;
+			int val = private->compressor_values[idx];
+
+			val >>= param->scale_bits;
+			val = clamp(val, param->min, param->max);
+			private->compressor_values[idx] = val;
+		}
+	}
+
+	return 0;
+}
+
+static int scarlett2_compressor_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->compressor_values[elem->control];
+	return 0;
+}
+
+static int scarlett2_compressor_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 channel = index / SCARLETT2_COMPRESSOR_PARAM_COUNT;
+	int param_index = index % SCARLETT2_COMPRESSOR_PARAM_COUNT;
+	int oval, val, err;
+	s32 scaled_val;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_put_during_autogain(mixer);
+	if (err < 0)
+		goto unlock;
+
+	oval = private->compressor_values[index];
+	val = ucontrol->value.integer.value[0];
+	if (oval == val)
+		goto unlock;
+
+	private->compressor_values[index] = val;
+
+	const struct compressor_param *param = &compressor_params[param_index];
+
+	scaled_val = val << param->scale_bits;
+
+	/* Send change to the device */
+
+	/* The channel needs to be put in the parameter buffer index
+	 * field (param_buf_addr + 1); the value field isn't used in
+	 * this case.
+	 */
+	err = scarlett2_usb_set_data(
+		mixer, private->config_set->param_buf_addr + 1, 1, channel);
+	if (err < 0)
+		goto unlock;
+
+	err = scarlett2_usb_set_config(
+		mixer, SCARLETT2_CONFIG_COMPRESSOR_PARAMS, index, scaled_val);
+	if (err < 0)
+		goto unlock;
+
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_compressor_ctl_info(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	int control = elem->control % SCARLETT2_COMPRESSOR_PARAM_COUNT;
+
+	uinfo->type = compressor_params[control].type;
+	uinfo->count = 1;
+	uinfo->value.integer.min = compressor_params[control].min;
+	uinfo->value.integer.max = compressor_params[control].max;
+	uinfo->value.integer.step = 1;
+	return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_compressor_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "",
+	.info = scarlett2_compressor_ctl_info,
+	.get  = scarlett2_compressor_ctl_get,
+	.put  = scarlett2_compressor_ctl_put,
+};
+
+/*** DSP Pre-Compressor and PEQ Filter Controls ***/
+
+static int scarlett2_precomp_flt_switch_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->precomp_flt_switch[elem->control];
+
+	return 0;
+}
+
+static int scarlett2_peq_flt_switch_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->peq_flt_switch[elem->control];
+
+	return 0;
+}
+
+static int scarlett2_precomp_flt_switch_ctl_put(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int oval, val, err = 0;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	oval = private->precomp_flt_switch[elem->control];
+	val = ucontrol->value.integer.value[0];
+
+	if (oval == val)
+		goto unlock;
+
+	private->precomp_flt_switch[elem->control] = val;
+
+	/* Send change to the device */
+	err = scarlett2_usb_set_config(
+		mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
+		elem->control, val);
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_peq_flt_switch_ctl_put(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int oval, val, err = 0;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	oval = private->peq_flt_switch[elem->control];
+	val = ucontrol->value.integer.value[0];
+
+	if (oval == val)
+		goto unlock;
+
+	private->peq_flt_switch[elem->control] = val;
+
+	/* Send change to the device */
+	err = scarlett2_usb_set_config(
+		mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
+		elem->control, val);
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_precomp_flt_switch_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "",
+	.info = snd_ctl_boolean_mono_info,
+	.get  = scarlett2_precomp_flt_switch_ctl_get,
+	.put  = scarlett2_precomp_flt_switch_ctl_put,
+};
+
+static const struct snd_kcontrol_new scarlett2_peq_flt_switch_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "",
+	.info = snd_ctl_boolean_mono_info,
+	.get  = scarlett2_peq_flt_switch_ctl_get,
+	.put  = scarlett2_peq_flt_switch_ctl_put,
+};
+
+static int scarlett2_update_filter_values(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int err, i, j, k, src_idx, dst_idx;
+	s32 peq_flt_values[SCARLETT2_DSP_SWITCH_MAX *
+			   SCARLETT2_PEQ_FLT_SLOTS_MAX *
+			   SCARLETT2_BIQUAD_COEFFS];
+
+	if (!info->dsp_input_count)
+		return 0;
+
+	/* Get filter switch values */
+	err = scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH,
+		info->dsp_input_count, private->precomp_flt_switch);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_PEQ_FLT_SWITCH,
+		info->dsp_input_count * info->peq_flt_count,
+		private->peq_flt_switch);
+	if (err < 0)
+		return err;
+
+	/* Get pre-compressor filter values directly */
+	err = scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
+		info->dsp_input_count *
+			info->precomp_flt_count *
+			SCARLETT2_BIQUAD_COEFFS,
+		private->precomp_flt_values);
+
+	if (err < 0)
+		return err;
+
+	/* PEQ filter values need to be copied via buffer because of
+	 * padding after peq_flt_count up to peq_flt_total_count
+	 */
+	err = scarlett2_usb_get_config(
+		mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
+		info->dsp_input_count *
+			info->peq_flt_total_count *
+			SCARLETT2_BIQUAD_COEFFS,
+		peq_flt_values);
+
+	for (i = 0, dst_idx = 0; i < info->dsp_input_count; i++) {
+		src_idx = i *
+			  info->peq_flt_total_count *
+			  SCARLETT2_BIQUAD_COEFFS;
+		for (j = 0; j < info->peq_flt_count; j++)
+			for (k = 0;
+			     k < SCARLETT2_BIQUAD_COEFFS;
+			     k++, src_idx++, dst_idx++)
+				private->peq_flt_values[dst_idx] =
+					peq_flt_values[src_idx];
+	}
+
+	return 0;
+}
+
+static int scarlett2_precomp_flt_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 i, idx;
+
+	for (i = 0, idx = elem->control * SCARLETT2_BIQUAD_COEFFS;
+	     i < SCARLETT2_BIQUAD_COEFFS;
+	     i++, idx++)
+		ucontrol->value.integer.value[i] =
+			private->precomp_flt_values[idx];
+
+	return 0;
+}
+
+static int scarlett2_peq_flt_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 i, idx;
+
+	for (i = 0, idx = elem->control * SCARLETT2_BIQUAD_COEFFS;
+	     i < SCARLETT2_BIQUAD_COEFFS;
+	     i++, idx++)
+		ucontrol->value.integer.value[i] =
+			private->peq_flt_values[idx];
+
+	return 0;
+}
+
+static int scarlett2_precomp_flt_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 * SCARLETT2_BIQUAD_COEFFS;
+	int i, oval, val, err;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_put_during_autogain(mixer);
+	if (err < 0)
+		goto unlock;
+
+	/* Check if any of the values have changed; if not, return */
+	for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) {
+		oval = private->precomp_flt_values[index + i];
+		val = ucontrol->value.integer.value[i];
+		if (oval != val)
+			break;
+	}
+
+	if (i == SCARLETT2_BIQUAD_COEFFS)
+		goto unlock;
+
+	/* Update the values */
+	for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++)
+		private->precomp_flt_values[index + i] =
+			ucontrol->value.integer.value[i];
+
+	/* Send change to the device */
+	err = scarlett2_usb_set_data(
+		mixer, private->config_set->param_buf_addr, 1, index);
+	if (err < 0)
+		goto unlock;
+
+	err = scarlett2_usb_set_config_buf(
+		mixer, SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS,
+		index, SCARLETT2_BIQUAD_COEFFS,
+		&private->precomp_flt_values[index]);
+
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_peq_flt_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 struct scarlett2_device_info *info = private->info;
+
+	int src_index = elem->control * SCARLETT2_BIQUAD_COEFFS;
+	int dst_index = (
+		elem->control /
+		info->peq_flt_count *
+		info->peq_flt_total_count +
+		elem->control % info->peq_flt_count
+	) * SCARLETT2_BIQUAD_COEFFS;
+	int i, oval, val, err;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_put_during_autogain(mixer);
+	if (err < 0)
+		goto unlock;
+
+	/* Check if any of the values have changed; if not, return */
+	for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++) {
+		oval = private->peq_flt_values[src_index + i];
+		val = ucontrol->value.integer.value[i];
+		if (oval != val)
+			break;
+	}
+
+	if (i == SCARLETT2_BIQUAD_COEFFS)
+		goto unlock;
+
+	/* Update the values */
+	for (i = 0; i < SCARLETT2_BIQUAD_COEFFS; i++)
+		private->peq_flt_values[src_index + i] =
+			ucontrol->value.integer.value[i];
+
+	/* Send change to the device */
+	err = scarlett2_usb_set_data(
+		mixer, private->config_set->param_buf_addr, 1, dst_index);
+	if (err < 0)
+		goto unlock;
+
+	err = scarlett2_usb_set_config_buf(
+		mixer, SCARLETT2_CONFIG_PEQ_FLT_PARAMS,
+		dst_index, SCARLETT2_BIQUAD_COEFFS,
+		&private->peq_flt_values[src_index]);
+
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_flt_ctl_info(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = SCARLETT2_BIQUAD_COEFFS;
+	uinfo->value.integer.min = INT_MIN;
+	uinfo->value.integer.max = INT_MAX;
+	uinfo->value.integer.step = 1;
+	return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_precomp_flt_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "",
+	.info = scarlett2_flt_ctl_info,
+	.get  = scarlett2_precomp_flt_ctl_get,
+	.put  = scarlett2_precomp_flt_ctl_put,
+};
+
+static const struct snd_kcontrol_new scarlett2_peq_flt_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "",
+	.info = scarlett2_flt_ctl_info,
+	.get  = scarlett2_peq_flt_ctl_get,
+	.put  = scarlett2_peq_flt_ctl_put,
+};
+
 /*** Input Mute Switch Controls ***/
 
 static int scarlett2_update_input_mute(struct usb_mixer_interface *mixer)
@@ -5568,6 +6319,69 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
 
 /*** Create the analogue input controls ***/
 
+static int scarlett2_add_dsp_ctls(struct usb_mixer_interface *mixer, int i)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int j, err;
+	char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+	const char *compr_fmt = "Line In %d Compressor %s";
+	const char *flt_switch_fmt = "Line In %d %s Filter Enable";
+	const char *flt_fmt = "Line In %d %s Coefficients %d";
+
+	/* Add compressor controls */
+	for (j = 0; j < SCARLETT2_COMPRESSOR_PARAM_COUNT; j++) {
+		const struct compressor_param *param = &compressor_params[j];
+		int idx = i * SCARLETT2_COMPRESSOR_PARAM_COUNT + j;
+
+		scnprintf(s, sizeof(s), compr_fmt, i + 1, param->name);
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_compressor_ctl,
+			i * SCARLETT2_COMPRESSOR_PARAM_COUNT + j,
+			1, s, &private->compressor_ctls[idx]);
+		if (err < 0)
+			return err;
+	}
+
+	/* Add filter enable controls */
+	scnprintf(s, sizeof(s), flt_switch_fmt, i + 1, "Pre-Comp");
+	err = scarlett2_add_new_ctl(
+		mixer, &scarlett2_precomp_flt_switch_ctl,
+		i, 1, s, &private->precomp_flt_switch_ctls[i]);
+	if (err < 0)
+		return err;
+
+	scnprintf(s, sizeof(s), flt_switch_fmt, i + 1, "PEQ");
+	err = scarlett2_add_new_ctl(
+		mixer, &scarlett2_peq_flt_switch_ctl,
+		i, 1, s, &private->peq_flt_switch_ctls[i]);
+	if (err < 0)
+		return err;
+
+	/* Add filter coefficient controls */
+	for (j = 0; j < info->precomp_flt_count; j++) {
+		scnprintf(s, sizeof(s), flt_fmt, i + 1, "Pre-Comp", j + 1);
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_precomp_flt_ctl,
+			i * info->precomp_flt_count + j,
+			1, s, &private->precomp_flt_switch_ctls[j]);
+		if (err < 0)
+			return err;
+	}
+
+	for (j = 0; j < info->peq_flt_count; j++) {
+		scnprintf(s, sizeof(s), flt_fmt, i + 1, "PEQ", j + 1);
+		err = scarlett2_add_new_ctl(
+			mixer, &scarlett2_peq_flt_ctl,
+			i * info->peq_flt_count + j,
+			1, s, &private->peq_flt_switch_ctls[j]);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
 static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
 {
 	struct scarlett2_data *private = mixer->private_data;
@@ -5607,6 +6421,19 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
 			return err;
 	}
 
+	/* Add input DSP controls */
+	for (i = 0; i < info->dsp_input_count; i++) {
+		scnprintf(s, sizeof(s), fmt, i + 1, "DSP", "Switch");
+		err = scarlett2_add_new_ctl(mixer, &scarlett2_dsp_ctl,
+					    i, 1, s, &private->dsp_ctls[i]);
+		if (err < 0)
+			return err;
+
+		err = scarlett2_add_dsp_ctls(mixer, i);
+		if (err < 0)
+			return err;
+	}
+
 	/* Add input mute controls */
 	for (i = 0; i < info->mute_input_count; i++) {
 		scnprintf(s, sizeof(s), fmt, i + 1, "Mute", "Switch");
@@ -6678,6 +7505,22 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
 			       &private->air_ctls[i]->id);
 }
 
+/* Notify on input DSP switch change */
+static __always_unused void scarlett2_notify_input_dsp(
+	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_dsp_updated = 1;
+
+	for (i = 0; i < info->dsp_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->dsp_ctls[i]->id);
+}
+
 /* Notify on input mute switch change */
 static __always_unused void scarlett2_notify_input_mute(
 	struct usb_mixer_interface *mixer)
@@ -7307,6 +8150,18 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
 	if (err < 0)
 		return err;
 
+	err = scarlett2_update_input_dsp(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_compressor_values(mixer);
+	if (err < 0)
+		return err;
+
+	err = scarlett2_update_filter_values(mixer);
+	if (err < 0)
+		return err;
+
 	err = scarlett2_update_input_mute(mixer);
 	if (err < 0)
 		return err;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 12/14] ALSA: scarlett2: Add support for Focusrite Vocaster One and Two
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (10 preceding siblings ...)
  2024-03-12 18:37 ` [PATCH 11/14] ALSA: scarlett2: Add DSP controls Geoffrey D. Bennett
@ 2024-03-12 18:37 ` Geoffrey D. Bennett
  2024-03-12 18:37 ` [PATCH 13/14] ALSA: scarlett2: Add autogain target controls Geoffrey D. Bennett
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:37 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Add Focusrite Vocaster One and Two USB IDs, notification arrays,
config sets, and device info data.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_quirks.c    |   2 +
 sound/usb/mixer_scarlett2.c | 167 +++++++++++++++++++++++++++++++++++-
 2 files changed, 165 insertions(+), 4 deletions(-)

diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 065a4be0d771..212b5e6443d8 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3447,6 +3447,8 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
 	case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
 	case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
 	case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
+	case USB_ID(0x1235, 0x8216): /* Focusrite Vocaster One */
+	case USB_ID(0x1235, 0x8217): /* Focusrite Vocaster Two */
 	case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */
 	case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */
 	case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 77bfaadb99b8..43922e217503 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -363,6 +363,18 @@ static const char *const scarlett2_autogain_status_gen4[] = {
 	NULL
 };
 
+static const char *const scarlett2_autogain_status_vocaster[] = {
+	"Running",
+	"Success",
+	"FailPG",
+	"FailRange",
+	"WarnMaxCap",
+	"WarnMinCap",
+	"Cancelled",
+	"Invalid",
+	NULL
+};
+
 /* Power Status Values */
 enum {
 	SCARLETT2_POWER_STATUS_EXT,
@@ -418,6 +430,17 @@ static const struct scarlett2_notification scarlett3a_notifications[] = {
 	{ 0, NULL }
 };
 
+static const struct scarlett2_notification vocaster_notifications[] = {
+	{ 0x00000001, scarlett2_notify_ack },
+	{ 0x00000008, scarlett2_notify_sync },
+	{ 0x00200000, scarlett2_notify_input_mute },
+	{ 0x00400000, scarlett2_notify_autogain },
+	{ 0x04000000, scarlett2_notify_input_dsp },
+	{ 0x08000000, scarlett2_notify_input_gain },
+	{ 0x10000000, scarlett2_notify_input_phantom },
+	{ 0, NULL }
+};
+
 static const struct scarlett2_notification scarlett4_solo_notifications[] = {
 	{ 0x00000001, scarlett2_notify_ack },
 	{ 0x00000008, scarlett2_notify_sync },
@@ -531,6 +554,11 @@ struct scarlett2_config_set {
 };
 
 /* Input gain TLV dB ranges */
+
+static const DECLARE_TLV_DB_MINMAX(
+	db_scale_vocaster_gain, 0, 70 * 100
+);
+
 static const DECLARE_TLV_DB_MINMAX(
 	db_scale_gen4_gain, 0, 69 * 100
 );
@@ -696,6 +724,51 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = {
 	}
 };
 
+/* Vocaster */
+static const struct scarlett2_config_set scarlett2_config_set_vocaster = {
+	.notifications = vocaster_notifications,
+	.param_buf_addr = 0x1bc,
+	.input_gain_tlv = db_scale_vocaster_gain,
+	.autogain_status_texts = scarlett2_autogain_status_vocaster,
+	.items = {
+		[SCARLETT2_CONFIG_MSD_SWITCH] = {
+			.offset = 0x9d, .size = 8, .activate = 6 },
+
+		[SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+			.offset = 0x1c0, .size = 8, .activate = 19, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+			.offset = 0x1c2, .size = 8, },
+
+		[SCARLETT2_CONFIG_INPUT_GAIN] = {
+			.offset = 0x9f, .size = 8, .activate = 21, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+			.offset = 0x9c, .size = 1, .activate = 20, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_DSP_SWITCH] = {
+			.offset = 0x1c4, .size = 8, .activate = 22, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_COMPRESSOR_PARAMS] = {
+			.offset = 0x1c8, .size = 32, .activate = 23 },
+
+		[SCARLETT2_CONFIG_PRECOMP_FLT_SWITCH] = {
+			.offset = 0x7c, .size = 32, .activate = 27 },
+
+		[SCARLETT2_CONFIG_PRECOMP_FLT_PARAMS] = {
+			.offset = 0x200, .size = 32, .activate = 27 },
+
+		[SCARLETT2_CONFIG_PEQ_FLT_SWITCH] = {
+			.offset = 0x84, .size = 32, .activate = 27 },
+
+		[SCARLETT2_CONFIG_PEQ_FLT_PARAMS] = {
+			.offset = 0x250, .size = 32, .activate = 27 },
+
+		[SCARLETT2_CONFIG_INPUT_MUTE_SWITCH] = {
+			.offset = 0x1be, .size = 8, .activate = 17, .pbuf = 1 },
+	}
+};
+
 /* Solo Gen 4 */
 static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
 	.notifications = scarlett4_solo_notifications,
@@ -1599,6 +1672,90 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
 	}
 };
 
+static const struct scarlett2_device_info vocaster_one_info = {
+	.config_set = &scarlett2_config_set_vocaster,
+	.min_firmware_version = 1769,
+
+	.phantom_count = 1,
+	.inputs_per_phantom = 1,
+	.dsp_count = 1,
+	.dsp_input_count = 1,
+	.precomp_flt_count = 2,
+	.peq_flt_count = 3,
+	.peq_flt_total_count = 4,
+	.mute_input_count = 1,
+	.gain_input_count = 1,
+
+	.port_count = {
+		[SCARLETT2_PORT_TYPE_NONE]     = { 1,  0 },
+		[SCARLETT2_PORT_TYPE_ANALOGUE] = { 2,  4 },
+		[SCARLETT2_PORT_TYPE_MIX]      = { 9,  9 },
+		[SCARLETT2_PORT_TYPE_PCM]      = { 4, 10 },
+	},
+
+	.mux_assignment = { {
+		{ SCARLETT2_PORT_TYPE_MIX,      8, 1 },
+		{ SCARLETT2_PORT_TYPE_PCM,      5, 5 },
+		{ SCARLETT2_PORT_TYPE_MIX,      6, 2 },
+		{ SCARLETT2_PORT_TYPE_PCM,      0, 5 },
+		{ SCARLETT2_PORT_TYPE_MIX,      0, 6 },
+		{ SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 },
+		{ 0, 0, 0 },
+	} },
+
+	.meter_map = {
+		{ 12, 1 },
+		{ 18, 5 },
+		{ 10, 2 },
+		{ 13, 5 },
+		{  4, 6 },
+		{  0, 4 },
+		{  0, 0 }
+	}
+};
+
+static const struct scarlett2_device_info vocaster_two_info = {
+	.config_set = &scarlett2_config_set_vocaster,
+	.min_firmware_version = 1769,
+
+	.phantom_count = 2,
+	.inputs_per_phantom = 1,
+	.dsp_count = 2,
+	.dsp_input_count = 2,
+	.precomp_flt_count = 2,
+	.peq_flt_count = 3,
+	.peq_flt_total_count = 4,
+	.mute_input_count = 2,
+	.gain_input_count = 2,
+
+	.port_count = {
+		[SCARLETT2_PORT_TYPE_NONE]     = {  1,  0 },
+		[SCARLETT2_PORT_TYPE_ANALOGUE] = {  6,  6 },
+		[SCARLETT2_PORT_TYPE_MIX]      = { 12, 14 },
+		[SCARLETT2_PORT_TYPE_PCM]      = {  4, 14 },
+	},
+
+	.mux_assignment = { {
+		{ SCARLETT2_PORT_TYPE_MIX,      12,  2 },
+		{ SCARLETT2_PORT_TYPE_PCM,       6,  8 },
+		{ SCARLETT2_PORT_TYPE_MIX,      10,  2 },
+		{ SCARLETT2_PORT_TYPE_PCM,       0,  6 },
+		{ SCARLETT2_PORT_TYPE_MIX,       0, 10 },
+		{ SCARLETT2_PORT_TYPE_ANALOGUE,  0,  6 },
+		{ 0, 0, 0 },
+	} },
+
+	.meter_map = {
+		{ 18,  2 },
+		{ 26,  8 },
+		{ 16,  2 },
+		{ 20,  6 },
+		{  6, 10 },
+		{  0,  6 },
+		{  0,  0 }
+	}
+};
+
 static const struct scarlett2_device_info solo_gen4_info = {
 	.config_set = &scarlett2_config_set_gen4_solo,
 	.min_firmware_version = 2115,
@@ -1932,6 +2089,10 @@ static const struct scarlett2_device_entry scarlett2_devices[] = {
 	{ USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" },
 	{ USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" },
 
+	/* Supported Vocaster devices */
+	{ USB_ID(0x1235, 0x8216), &vocaster_one_info, "Vocaster" },
+	{ USB_ID(0x1235, 0x8217), &vocaster_two_info, "Vocaster" },
+
 	/* Supported Gen 4 devices */
 	{ USB_ID(0x1235, 0x8218), &solo_gen4_info, "Scarlett Gen 4" },
 	{ USB_ID(0x1235, 0x8219), &s2i2_gen4_info, "Scarlett Gen 4" },
@@ -7506,8 +7667,7 @@ static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
 }
 
 /* Notify on input DSP switch change */
-static __always_unused void scarlett2_notify_input_dsp(
-	struct usb_mixer_interface *mixer)
+static void scarlett2_notify_input_dsp(struct usb_mixer_interface *mixer)
 {
 	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
@@ -7522,8 +7682,7 @@ static __always_unused void scarlett2_notify_input_dsp(
 }
 
 /* Notify on input mute switch change */
-static __always_unused void scarlett2_notify_input_mute(
-	struct usb_mixer_interface *mixer)
+static void scarlett2_notify_input_mute(struct usb_mixer_interface *mixer)
 {
 	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 13/14] ALSA: scarlett2: Add autogain target controls
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (11 preceding siblings ...)
  2024-03-12 18:37 ` [PATCH 12/14] ALSA: scarlett2: Add support for Focusrite Vocaster One and Two Geoffrey D. Bennett
@ 2024-03-12 18:37 ` Geoffrey D. Bennett
  2024-03-12 18:38 ` [PATCH 14/14] ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two Geoffrey D. Bennett
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:37 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

The Scarlett 4th Gen and Vocaster interfaces allow the autogain target
dBFS value(s) to be configured. Add Mean and Peak Target controls for
4th Gen, and a Hot Target control for Vocaster.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 207 ++++++++++++++++++++++++++++++++++++
 1 file changed, 207 insertions(+)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 43922e217503..6040c4df356f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -332,6 +332,17 @@ enum {
 	SCARLETT2_DIM_MUTE_COUNT
 };
 
+/* Autogain target values */
+
+#define SCARLETT2_AG_TARGET_MIN (-30)
+
+enum {
+	SCARLETT2_AG_HOT_TARGET,
+	SCARLETT2_AG_MEAN_TARGET,
+	SCARLETT2_AG_PEAK_TARGET,
+	SCARLETT2_AG_TARGET_COUNT
+};
+
 /* Flash Write State */
 enum {
 	SCARLETT2_FLASH_WRITE_STATE_IDLE,
@@ -512,6 +523,9 @@ enum {
 	SCARLETT2_CONFIG_TALKBACK_MAP,
 	SCARLETT2_CONFIG_AUTOGAIN_SWITCH,
 	SCARLETT2_CONFIG_AUTOGAIN_STATUS,
+	SCARLETT2_CONFIG_AG_HOT_TARGET,
+	SCARLETT2_CONFIG_AG_MEAN_TARGET,
+	SCARLETT2_CONFIG_AG_PEAK_TARGET,
 	SCARLETT2_CONFIG_INPUT_GAIN,
 	SCARLETT2_CONFIG_SAFE_SWITCH,
 	SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
@@ -523,6 +537,18 @@ enum {
 	SCARLETT2_CONFIG_COUNT
 };
 
+/* Autogain target configuration parameters and names */
+
+static const int scarlett2_ag_target_configs[] = {
+	[SCARLETT2_AG_HOT_TARGET]  = SCARLETT2_CONFIG_AG_HOT_TARGET,
+	[SCARLETT2_AG_MEAN_TARGET] = SCARLETT2_CONFIG_AG_MEAN_TARGET,
+	[SCARLETT2_AG_PEAK_TARGET] = SCARLETT2_CONFIG_AG_PEAK_TARGET
+};
+
+static const char *const scarlett2_ag_target_names[] = {
+	"Hot", "Mean", "Peak"
+};
+
 /* Location, size, and activation command number for the configuration
  * parameters. Size is in bits and may be 1, 8, 16, or 32.
  *
@@ -740,6 +766,9 @@ static const struct scarlett2_config_set scarlett2_config_set_vocaster = {
 		[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
 			.offset = 0x1c2, .size = 8, },
 
+		[SCARLETT2_CONFIG_AG_HOT_TARGET] = {
+			.offset = 0xc1, .size = 8, .activate = 29, .pbuf = 1 },
+
 		[SCARLETT2_CONFIG_INPUT_GAIN] = {
 			.offset = 0x9f, .size = 8, .activate = 21, .pbuf = 1 },
 
@@ -818,6 +847,12 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
 		[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
 			.offset = 0x137, .size = 8 },
 
+		[SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
+			.offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
+			.offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 },
+
 		[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
 			.offset = 0x48, .size = 8, .activate = 11, .pbuf = 1,
 			.mute = 1 },
@@ -862,6 +897,12 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
 		[SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
 			.offset = 0x140, .size = 8 },
 
+		[SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
+			.offset = 0x13a, .size = 8, .activate = 23, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
+			.offset = 0x13b, .size = 8, .activate = 24, .pbuf = 1 },
+
 		[SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
 			.offset = 0x5a, .size = 8, .activate = 11, .pbuf = 1,
 			.mute = 1 },
@@ -1189,6 +1230,7 @@ struct scarlett2_data {
 	u8 gain[SCARLETT2_INPUT_GAIN_MAX];
 	u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX];
 	u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+	s8 ag_targets[SCARLETT2_AG_TARGET_COUNT];
 	u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX];
 	u8 pcm_input_switch;
 	u8 direct_monitor_switch;
@@ -1217,6 +1259,7 @@ struct scarlett2_data {
 	struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX];
 	struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX];
 	struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
+	struct snd_kcontrol *ag_target_ctls[SCARLETT2_AG_TARGET_COUNT];
 	struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX];
 	struct snd_kcontrol *pcm_input_switch_ctl;
 	struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
@@ -3253,6 +3296,7 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
 	const struct scarlett2_device_info *info = private->info;
 	int err, i;
 	u8 raw_autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+	s8 ag_target_values[SCARLETT2_AG_TARGET_COUNT];
 
 	private->autogain_updated = 0;
 
@@ -3291,6 +3335,21 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
 			private->autogain_status[i] =
 				private->num_autogain_status_texts - 1;
 
+
+	for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+		if (scarlett2_has_config_item(private,
+					      scarlett2_ag_target_configs[i])) {
+			err = scarlett2_usb_get_config(
+				mixer, scarlett2_ag_target_configs[i],
+				1, &ag_target_values[i]);
+			if (err < 0)
+				return err;
+		}
+
+	/* convert from negative dBFS as used by the device */
+	for (int i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+		private->ag_targets[i] = -ag_target_values[i];
+
 	return 0;
 }
 
@@ -3324,6 +3383,12 @@ static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
 		scarlett2_set_ctl_access(private->phantom_ctls[i], val);
 	for (i = 0; i < info->dsp_input_count; i++)
 		scarlett2_set_ctl_access(private->dsp_ctls[i], val);
+
+	for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+		if (scarlett2_has_config_item(private,
+					      scarlett2_ag_target_configs[i]))
+			scarlett2_set_ctl_access(
+				private->ag_target_ctls[i], val);
 }
 
 /* Notify of access mode change for all controls read-only while
@@ -3366,6 +3431,12 @@ static void scarlett2_autogain_notify_access(struct usb_mixer_interface *mixer)
 	for (i = 0; i < info->phantom_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
 			       &private->phantom_ctls[i]->id);
+
+	for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+		if (scarlett2_has_config_item(private,
+					      scarlett2_ag_target_configs[i]))
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+				       &private->ag_target_ctls[i]->id);
 }
 
 /* Call scarlett2_update_autogain() and
@@ -3559,6 +3630,122 @@ static const struct snd_kcontrol_new scarlett2_autogain_status_ctl = {
 	.get  = scarlett2_autogain_status_ctl_get,
 };
 
+/*** Autogain Target Controls ***/
+
+static int scarlett2_ag_target_ctl_info(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int err;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_autogain_updated(mixer);
+	if (err < 0)
+		goto unlock;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = SCARLETT2_AG_TARGET_MIN;
+	uinfo->value.integer.max = 0;
+	uinfo->value.integer.step = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_ag_target_ctl_get(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int err;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	if (private->autogain_updated) {
+		err = scarlett2_update_autogain(mixer);
+		if (err < 0)
+			goto unlock;
+	}
+
+	ucontrol->value.integer.value[0] = private->ag_targets[elem->control];
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_ag_target_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;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	err = scarlett2_check_put_during_autogain(mixer);
+	if (err < 0)
+		goto unlock;
+
+	oval = private->ag_targets[index];
+	val = clamp(ucontrol->value.integer.value[0],
+		    (long)SCARLETT2_AG_TARGET_MIN, 0L);
+
+	if (oval == val)
+		goto unlock;
+
+	private->ag_targets[index] = val;
+
+	/* Send new value to the device */
+	err = scarlett2_usb_set_config(
+		mixer, scarlett2_ag_target_configs[index], 1, -val);
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+	db_scale_ag_target, SCARLETT2_AG_TARGET_MIN * 100, 0
+);
+
+static const struct snd_kcontrol_new scarlett2_ag_target_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+	.name = "",
+	.info = scarlett2_ag_target_ctl_info,
+	.get  = scarlett2_ag_target_ctl_get,
+	.put  = scarlett2_ag_target_ctl_put,
+	.tlv = { .p = db_scale_ag_target }
+};
+
 /*** Input Select Control ***/
 
 static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
@@ -6693,6 +6880,20 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
 			i, 1, s, &private->autogain_status_ctls[i]);
 	}
 
+	/* Add autogain target controls */
+	for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+		if (scarlett2_has_config_item(private,
+					      scarlett2_ag_target_configs[i])) {
+
+			scnprintf(s, sizeof(s), "Autogain %s Target",
+				  scarlett2_ag_target_names[i]);
+			err = scarlett2_add_new_ctl(
+				mixer, &scarlett2_ag_target_ctl,
+				i, 1, s, &private->ag_target_ctls[i]);
+			if (err < 0)
+				return err;
+		}
+
 	/* Add safe-mode input switch controls */
 	for (i = 0; i < info->safe_input_count; i++) {
 		scnprintf(s, sizeof(s), fmt, i + 1,
@@ -7783,6 +7984,12 @@ static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer)
 			       &private->autogain_status_ctls[i]->id);
 	}
 
+	for (i = 0; i < SCARLETT2_AG_TARGET_COUNT; i++)
+		if (scarlett2_has_config_item(private,
+					      scarlett2_ag_target_configs[i]))
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+				       &private->ag_target_ctls[i]->id);
+
 	scarlett2_autogain_notify_access(mixer);
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 14/14] ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (12 preceding siblings ...)
  2024-03-12 18:37 ` [PATCH 13/14] ALSA: scarlett2: Add autogain target controls Geoffrey D. Bennett
@ 2024-03-12 18:38 ` Geoffrey D. Bennett
  2024-03-12 19:00 ` [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Takashi Iwai
  2024-04-18  6:35 ` Takashi Iwai
  15 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-12 18:38 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

The Vocaster Two has a Bluetooth module with a volume control. Add a
corresponding ALSA mixer control.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 156 ++++++++++++++++++++++++++++++++++++
 1 file changed, 156 insertions(+)

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 6040c4df356f..a2de31a0371b 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -88,6 +88,7 @@
  *  - input mute, gain, autogain, safe mode
  *  - direct monitor mixes
  *  - compressor and EQ
+ *  - Bluetooth volume
  *
  * <ditaa>
  *    /--------------\    18chn            20chn     /--------------\
@@ -180,6 +181,9 @@
  */
 #define SCARLETT2_MAX_GAIN_VALUE 70
 
+/* maximum Bluetooth volume value */
+#define SCARLETT2_MAX_BLUETOOTH_VOLUME 30
+
 /* mixer range from -80dB to +6dB in 0.5dB steps */
 #define SCARLETT2_MIXER_MIN_DB -80
 #define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2)
@@ -421,6 +425,7 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer);
 static void scarlett2_notify_pcm_input_switch(
 					struct usb_mixer_interface *mixer);
+static void scarlett2_notify_bluetooth(struct usb_mixer_interface *mixer);
 
 /* Arrays of notification callback functions */
 
@@ -449,6 +454,7 @@ static const struct scarlett2_notification vocaster_notifications[] = {
 	{ 0x04000000, scarlett2_notify_input_dsp },
 	{ 0x08000000, scarlett2_notify_input_gain },
 	{ 0x10000000, scarlett2_notify_input_phantom },
+	{ 0x20000000, scarlett2_notify_bluetooth },
 	{ 0, NULL }
 };
 
@@ -534,6 +540,7 @@ enum {
 	SCARLETT2_CONFIG_POWER_LOW,
 	SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
 	SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+	SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
 	SCARLETT2_CONFIG_COUNT
 };
 
@@ -795,6 +802,9 @@ static const struct scarlett2_config_set scarlett2_config_set_vocaster = {
 
 		[SCARLETT2_CONFIG_INPUT_MUTE_SWITCH] = {
 			.offset = 0x1be, .size = 8, .activate = 17, .pbuf = 1 },
+
+		[SCARLETT2_CONFIG_BLUETOOTH_VOLUME] = {
+			.offset = 0xbf, .size = 8, .activate = 28 },
 	}
 };
 
@@ -1134,6 +1144,9 @@ struct scarlett2_device_info {
 	/* the number of DSP channels */
 	u8 dsp_count;
 
+	/* has a Bluetooth module with volume control */
+	u8 has_bluetooth;
+
 	/* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected
 	 * internally to the analogue 7/8 outputs
 	 */
@@ -1206,6 +1219,7 @@ struct scarlett2_data {
 	u8 mix_updated;
 	u8 speaker_switching_switched;
 	u8 power_status_updated;
+	u8 bluetooth_updated;
 	u8 sync;
 	u8 master_vol;
 	u8 headphone_vol;
@@ -1240,6 +1254,7 @@ struct scarlett2_data {
 	u8 msd_switch;
 	u8 standalone_switch;
 	u8 power_status;
+	u8 bluetooth_volume;
 	u8 meter_level_map[SCARLETT2_MAX_METERS];
 	struct snd_kcontrol *sync_ctl;
 	struct snd_kcontrol *master_vol_ctl;
@@ -1273,6 +1288,7 @@ struct scarlett2_data {
 	struct snd_kcontrol *speaker_switching_ctl;
 	struct snd_kcontrol *talkback_ctl;
 	struct snd_kcontrol *power_status_ctl;
+	struct snd_kcontrol *bluetooth_volume_ctl;
 	u8 mux[SCARLETT2_MUX_MAX];
 	u8 mix[SCARLETT2_MIX_MAX];
 	u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX];
@@ -1770,6 +1786,7 @@ static const struct scarlett2_device_info vocaster_two_info = {
 	.peq_flt_total_count = 4,
 	.mute_input_count = 2,
 	.gain_input_count = 2,
+	.has_bluetooth = 1,
 
 	.port_count = {
 		[SCARLETT2_PORT_TYPE_NONE]     = {  1,  0 },
@@ -7753,6 +7770,121 @@ static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
 				     &private->power_status_ctl);
 }
 
+/*** Bluetooth Volume ***/
+
+static int scarlett2_update_bluetooth_volume(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	int err;
+
+	private->bluetooth_updated = 0;
+
+	if (!private->info->has_bluetooth)
+		return 0;
+
+	err = scarlett2_usb_get_config(mixer,
+				       SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+				       1, &private->bluetooth_volume);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int scarlett2_bluetooth_volume_ctl_get(struct snd_kcontrol *kctl,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int err = 0;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	if (private->bluetooth_updated) {
+		err = scarlett2_update_bluetooth_volume(mixer);
+		if (err < 0)
+			goto unlock;
+	}
+	ucontrol->value.integer.value[0] = private->bluetooth_volume;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_bluetooth_volume_ctl_put(struct snd_kcontrol *kctl,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct usb_mixer_elem_info *elem = kctl->private_data;
+	struct usb_mixer_interface *mixer = elem->head.mixer;
+	struct scarlett2_data *private = mixer->private_data;
+	int oval, val, err = 0;
+
+	mutex_lock(&private->data_mutex);
+
+	if (private->hwdep_in_use) {
+		err = -EBUSY;
+		goto unlock;
+	}
+
+	oval = private->bluetooth_volume;
+	val = clamp(ucontrol->value.integer.value[0],
+		    0L, (long)SCARLETT2_MAX_BLUETOOTH_VOLUME);
+
+	if (oval == val)
+		goto unlock;
+
+	private->bluetooth_volume = val;
+	err = scarlett2_usb_set_config(mixer,
+				       SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
+				       0, val);
+	if (err == 0)
+		err = 1;
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
+static int scarlett2_bluetooth_volume_ctl_info(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = SCARLETT2_MAX_BLUETOOTH_VOLUME;
+	uinfo->value.integer.step = 1;
+	return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_bluetooth_volume_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "",
+	.info = scarlett2_bluetooth_volume_ctl_info,
+	.get  = scarlett2_bluetooth_volume_ctl_get,
+	.put  = scarlett2_bluetooth_volume_ctl_put,
+};
+
+static int scarlett2_add_bluetooth_volume_ctl(
+	struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+
+	if (!private->info->has_bluetooth)
+		return 0;
+
+	/* Add Bluetooth volume control */
+	return scarlett2_add_new_ctl(mixer, &scarlett2_bluetooth_volume_ctl,
+				     0, 1, "Bluetooth Capture Volume",
+				     &private->bluetooth_volume_ctl);
+}
+
 /*** Notification Handlers ***/
 
 /* Notify on sync change */
@@ -8109,6 +8241,21 @@ static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
 	scarlett2_notify_mux(mixer);
 }
 
+/* Notify on Bluetooth change */
+static void scarlett2_notify_bluetooth(struct usb_mixer_interface *mixer)
+{
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
+
+	if (!private->info->has_bluetooth)
+		return;
+
+	private->bluetooth_updated = 1;
+
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->bluetooth_volume_ctl->id);
+}
+
 /* Handle acknowledgement that a command was received; let
  * scarlett2_usb() know that it can proceed
  */
@@ -8646,6 +8793,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
 			return err;
 	}
 
+	err = scarlett2_update_bluetooth_volume(mixer);
+	if (err < 0)
+		return err;
+
 	err = scarlett2_update_mix(mixer);
 	if (err < 0)
 		return err;
@@ -8773,6 +8924,11 @@ static int snd_scarlett2_controls_create(
 	if (err < 0)
 		return err;
 
+	/* Create the Bluetooth volume control */
+	err = scarlett2_add_bluetooth_volume_ctl(mixer);
+	if (err < 0)
+		return err;
+
 	/* Set the access mode of controls disabled during
 	 * autogain/phantom power switching.
 	 */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (13 preceding siblings ...)
  2024-03-12 18:38 ` [PATCH 14/14] ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two Geoffrey D. Bennett
@ 2024-03-12 19:00 ` Takashi Iwai
  2024-03-14 17:41   ` Geoffrey D. Bennett
  2024-04-18  6:35 ` Takashi Iwai
  15 siblings, 1 reply; 18+ messages in thread
From: Takashi Iwai @ 2024-03-12 19:00 UTC (permalink / raw)
  To: Geoffrey D. Bennett; +Cc: Takashi Iwai, Takashi Iwai, linux-sound

On Tue, 12 Mar 2024 19:28:52 +0100,
Geoffrey D. Bennett wrote:
> 
> Hi Takashi,
> 
> This patch series contains:
> 
> 1-2: Fix to wait for an ACK from the device after sending a command,
> before attempting to read the response. This didn't seem to be
> necessary previously, but with 4th Gen devices it seems to help with a
> rare issue where the device stops responding.
> 
> 3: Support for reading flash segments from the device.
> 
> 4-11: Preparation for Vocaster support.
> 
> 12: Vocaster One and Two support.
> 
> 13: Add autogain target controls for 4th Gen and Vocaster.
> 
> 14: Add Bluetooth volume control for Vocaster Two.

As the merge window was already opened, I postpone this series to
6.10.  If there is any fix that should be taken to 6.9, please submit
separately.


thanks,

Takashi

> 
> Thanks,
> Geoffrey.
> 
> Geoffrey D. Bennett (14):
>   ALSA: scarlett2: Move initialisation code lower in the source
>   ALSA: scarlett2: Implement handling of the ACK notification
>   ALSA: scarlett2: Add support for reading from flash
>   ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr
>   ALSA: scarlett2: Add pbuf field to struct scarlett2_config
>   ALSA: scarlett2: Add support for config items with size = 32
>   ALSA: scarlett2: Add additional input configuration parameters
>   ALSA: scarlett2: Define the maximum preamp input gain per-config-set
>   ALSA: scarlett2: Define autogain status texts per-config-set
>   ALSA: scarlett2: Add input mute controls
>   ALSA: scarlett2: Add DSP controls
>   ALSA: scarlett2: Add support for Focusrite Vocaster One and Two
>   ALSA: scarlett2: Add autogain target controls
>   ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two
> 
>  sound/usb/mixer_quirks.c    |    2 +
>  sound/usb/mixer_scarlett2.c | 2757 ++++++++++++++++++++++++++++-------
>  2 files changed, 2235 insertions(+), 524 deletions(-)
> 
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster
  2024-03-12 19:00 ` [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Takashi Iwai
@ 2024-03-14 17:41   ` Geoffrey D. Bennett
  0 siblings, 0 replies; 18+ messages in thread
From: Geoffrey D. Bennett @ 2024-03-14 17:41 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Takashi Iwai, linux-sound

Hi Takashi,

On Tue, Mar 12, 2024 at 08:00:42PM +0100, Takashi Iwai wrote:
> On Tue, 12 Mar 2024 19:28:52 +0100,
> Geoffrey D. Bennett wrote:
> > 
> > Hi Takashi,
> > 
> > This patch series contains:
> > 
> > 1-2: Fix to wait for an ACK from the device after sending a command,
> > before attempting to read the response. This didn't seem to be
> > necessary previously, but with 4th Gen devices it seems to help with a
> > rare issue where the device stops responding.
> > 
> > 3: Support for reading flash segments from the device.
> > 
> > 4-11: Preparation for Vocaster support.
> > 
> > 12: Vocaster One and Two support.
> > 
> > 13: Add autogain target controls for 4th Gen and Vocaster.
> > 
> > 14: Add Bluetooth volume control for Vocaster Two.
> 
> As the merge window was already opened, I postpone this series to
> 6.10.  If there is any fix that should be taken to 6.9, please submit
> separately.

Okay, thanks, I'll resubmit separately fixes for 6.9, and please don't
apply any of these for 6.10 yet; I'll submit a v2 later.

Thanks,
Geoffrey.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster
  2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
                   ` (14 preceding siblings ...)
  2024-03-12 19:00 ` [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Takashi Iwai
@ 2024-04-18  6:35 ` Takashi Iwai
  15 siblings, 0 replies; 18+ messages in thread
From: Takashi Iwai @ 2024-04-18  6:35 UTC (permalink / raw)
  To: Geoffrey D. Bennett; +Cc: Takashi Iwai, Takashi Iwai, linux-sound

On Tue, 12 Mar 2024 19:28:52 +0100,
Geoffrey D. Bennett wrote:
> 
> Hi Takashi,
> 
> This patch series contains:
> 
> 1-2: Fix to wait for an ACK from the device after sending a command,
> before attempting to read the response. This didn't seem to be
> necessary previously, but with 4th Gen devices it seems to help with a
> rare issue where the device stops responding.
> 
> 3: Support for reading flash segments from the device.
> 
> 4-11: Preparation for Vocaster support.
> 
> 12: Vocaster One and Two support.
> 
> 13: Add autogain target controls for 4th Gen and Vocaster.
> 
> 14: Add Bluetooth volume control for Vocaster Two.
> 
> Thanks,
> Geoffrey.
> 
> Geoffrey D. Bennett (14):
>   ALSA: scarlett2: Move initialisation code lower in the source
>   ALSA: scarlett2: Implement handling of the ACK notification
>   ALSA: scarlett2: Add support for reading from flash
>   ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr
>   ALSA: scarlett2: Add pbuf field to struct scarlett2_config
>   ALSA: scarlett2: Add support for config items with size = 32
>   ALSA: scarlett2: Add additional input configuration parameters
>   ALSA: scarlett2: Define the maximum preamp input gain per-config-set
>   ALSA: scarlett2: Define autogain status texts per-config-set
>   ALSA: scarlett2: Add input mute controls
>   ALSA: scarlett2: Add DSP controls
>   ALSA: scarlett2: Add support for Focusrite Vocaster One and Two
>   ALSA: scarlett2: Add autogain target controls
>   ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two

Now all merged to for-next branch.  Thanks.


Takashi

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2024-04-18  6:35 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-12 18:28 [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Geoffrey D. Bennett
2024-03-12 18:33 ` [PATCH 01/14] ALSA: scarlett2: Move initialisation code lower in the source Geoffrey D. Bennett
2024-03-12 18:34 ` [PATCH 02/14] ALSA: scarlett2: Implement handling of the ACK notification Geoffrey D. Bennett
2024-03-12 18:34 ` [PATCH 03/14] ALSA: scarlett2: Add support for reading from flash Geoffrey D. Bennett
2024-03-12 18:34 ` [PATCH 04/14] ALSA: scarlett2: Rename gen4_write_addr to param_buf_addr Geoffrey D. Bennett
2024-03-12 18:35 ` [PATCH 05/14] ALSA: scarlett2: Add pbuf field to struct scarlett2_config Geoffrey D. Bennett
2024-03-12 18:35 ` [PATCH 06/14] ALSA: scarlett2: Add support for config items with size = 32 Geoffrey D. Bennett
2024-03-12 18:35 ` [PATCH 07/14] ALSA: scarlett2: Add additional input configuration parameters Geoffrey D. Bennett
2024-03-12 18:35 ` [PATCH 08/14] ALSA: scarlett2: Define the maximum preamp input gain per-config-set Geoffrey D. Bennett
2024-03-12 18:36 ` [PATCH 09/14] ALSA: scarlett2: Define autogain status texts per-config-set Geoffrey D. Bennett
2024-03-12 18:36 ` [PATCH 10/14] ALSA: scarlett2: Add input mute controls Geoffrey D. Bennett
2024-03-12 18:37 ` [PATCH 11/14] ALSA: scarlett2: Add DSP controls Geoffrey D. Bennett
2024-03-12 18:37 ` [PATCH 12/14] ALSA: scarlett2: Add support for Focusrite Vocaster One and Two Geoffrey D. Bennett
2024-03-12 18:37 ` [PATCH 13/14] ALSA: scarlett2: Add autogain target controls Geoffrey D. Bennett
2024-03-12 18:38 ` [PATCH 14/14] ALSA: scarlett2: Add Bluetooth volume control for Vocaster Two Geoffrey D. Bennett
2024-03-12 19:00 ` [PATCH 00/14] ALSA: scarlett2: Add support for Vocaster Takashi Iwai
2024-03-14 17:41   ` Geoffrey D. Bennett
2024-04-18  6:35 ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.