diff mbox series

[SRU,N,1/2] ALSA: scarlett2: Move initialisation code lower in the source

Message ID 20240924133157.15168-2-roxana.nicolescu@canonical.com
State New
Headers show
Series Fix alsa scarlett2 driver in 6.8 | expand

Commit Message

Roxana Nicolescu Sept. 24, 2024, 1:31 p.m. UTC
From: "Geoffrey D. Bennett" <g@b4.vu>

BugLink: https://bugs.launchpad.net/bugs/2076402

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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Message-ID: <0922071cb8be99a2394705de27b917d1e4e46f3f.1710264833.git.g@b4.vu>
(cherry picked from commit 4074f8d23278e9e32aabe9dba60f6dddaf68e6ef)
Signed-off-by: Roxana Nicolescu <roxana.nicolescu@canonical.com>
---
 sound/usb/mixer_scarlett2.c | 1187 ++++++++++++++++++-----------------
 1 file changed, 595 insertions(+), 592 deletions(-)
diff mbox series

Patch

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,798 +6383,801 @@  static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
 				     &private->power_status_ctl);
 }
 
-/*** Cleanup/Suspend Callbacks ***/
+/*** Notification Handlers ***/
 
-static void scarlett2_private_free(struct usb_mixer_interface *mixer)
+/* Notify on sync change */
+static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
 {
 	struct scarlett2_data *private = mixer->private_data;
 
-	cancel_delayed_work_sync(&private->work);
-	kfree(private);
-	mixer->private_data = NULL;
+	private->sync_updated = 1;
+
+	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->sync_ctl->id);
 }
 
-static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
+/* Notify on monitor change (Gen 2/3) */
+static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer)
 {
+	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
+	int i;
 
-	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;
+	if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
+		return;
 
-	/* 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->vol_updated = 1;
 
-	private->num_mux_srcs = srcs;
-	private->num_mux_dsts = dsts;
+	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->master_vol_ctl->id);
 
-	/* 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;
+	for (i = 0; i < private->num_line_out; i++)
+		if (private->vol_sw_hw_switch[line_out_remap(private, i)])
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &private->vol_ctls[i]->id);
+}
 
-	private->num_mix_out =
-		port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
-			info->dsp_count;
+/* Notify on volume change (Gen 4) */
+static void scarlett2_notify_volume(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
 
-	/* Number of analogue line outputs */
-	private->num_line_out =
-		port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+	private->vol_updated = 1;
 
-	/* Number of monitor mix controls */
-	private->num_monitor_mix_ctls =
-		info->direct_monitor * 2 * private->num_mix_in;
+	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->master_vol_ctl->id);
+	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->headphone_vol_ctl->id);
 }
 
-/* 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)
+/* Notify on dim/mute change */
+static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer)
 {
-	struct usb_host_config *config = dev->actconfig;
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
 	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 (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
+		return;
 
-		if (desc->bInterfaceClass != 255)
-			continue;
+	private->dim_mute_updated = 1;
 
-		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;
-	}
+	for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->dim_mute_ctls[i]->id);
 
-	return -EINVAL;
+	for (i = 0; i < private->num_line_out; i++)
+		if (private->vol_sw_hw_switch[line_out_remap(private, i)])
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &private->mute_ctls[i]->id);
 }
 
-/* Initialise private data */
-static int scarlett2_init_private(struct usb_mixer_interface *mixer,
-				  const struct scarlett2_device_entry *entry)
+/* Notify on input level switch change */
+static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer)
 {
-	struct scarlett2_data *private =
-		kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL);
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int i;
 
-	if (!private)
-		return -ENOMEM;
+	private->input_level_updated = 1;
 
-	mutex_init(&private->usb_mutex);
-	mutex_init(&private->data_mutex);
-	INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
+	for (i = 0; i < info->level_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->level_ctls[i]->id);
+}
 
-	mixer->private_data = private;
-	mixer->private_free = scarlett2_private_free;
-	mixer->private_suspend = scarlett2_private_suspend;
+/* Notify on input pad switch change */
+static void scarlett2_notify_input_pad(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->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;
+	private->input_pad_updated = 1;
 
-	return scarlett2_find_fc_interface(mixer->chip->dev, private);
+	for (i = 0; i < info->pad_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->pad_ctls[i]->id);
 }
 
-/* Cargo cult proprietary initialisation sequence */
-static int scarlett2_usb_init(struct usb_mixer_interface *mixer)
+/* Notify on input air switch change */
+static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
 {
-	struct usb_device *dev = mixer->chip->dev;
+	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
-	u8 step0_buf[24];
-	u8 step2_buf[84];
-	int err;
+	const struct scarlett2_device_info *info = private->info;
+	int i;
 
-	if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
-		return -EINVAL;
+	private->input_air_updated = 1;
 
-	/* step 0 */
-	err = scarlett2_usb_rx(dev, private->bInterfaceNumber,
-			       SCARLETT2_USB_CMD_INIT,
-			       step0_buf, sizeof(step0_buf));
-	if (err < 0)
-		return err;
+	for (i = 0; i < info->air_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->air_ctls[i]->id);
+}
 
-	/* step 1 */
-	private->scarlett2_seq = 1;
-	err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0);
-	if (err < 0)
-		return err;
+/* Notify on input phantom switch change */
+static void scarlett2_notify_input_phantom(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;
 
-	/* 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;
+	private->input_phantom_updated = 1;
 
-	/* 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);
+	for (i = 0; i < info->phantom_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->phantom_ctls[i]->id);
 
-	return 0;
+	scarlett2_phantom_notify_access(mixer);
 }
 
-/* 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)
+/* Notify on "input other" change (level/pad/air/phantom) */
+static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer)
+{
+	scarlett2_notify_input_level(mixer);
+	scarlett2_notify_input_pad(mixer);
+	scarlett2_notify_input_air(mixer);
+	scarlett2_notify_input_phantom(mixer);
+}
+
+/* Notify on input select change */
+static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
 {
+	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
-	int err, count, i;
+	const struct scarlett2_device_info *info = private->info;
+	int i;
 
-	struct {
-		__le32 size;
-		__le32 count;
-		u8 unknown[8];
-	} __packed flash_info;
+	if (!info->gain_input_count)
+		return;
 
-	struct {
-		__le32 size;
-		__le32 flags;
-		char name[16];
-	} __packed segment_info;
+	private->input_select_updated = 1;
 
-	err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_FLASH,
-			    NULL, 0,
-			    &flash_info, sizeof(flash_info));
-	if (err < 0)
-		return err;
+	snd_ctl_notify(card,
+		       SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+		       &private->input_select_ctl->id);
 
-	count = le32_to_cpu(flash_info.count);
+	for (i = 0; i < info->gain_input_count / 2; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->input_link_ctls[i]->id);
+}
 
-	/* 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;
+/* Notify on input gain change */
+static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer)
+{
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int i;
 
-		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;
-	}
+	if (!info->gain_input_count)
+		return;
 
-	/* 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;
-	}
+	private->input_gain_updated = 1;
 
-	return 0;
+	for (i = 0; i < info->gain_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->input_gain_ctls[i]->id);
 }
 
-/* Read configuration from the interface on start */
-static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
+/* Notify on autogain change */
+static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer)
 {
+	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
 	const struct scarlett2_device_info *info = private->info;
-	int 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;
+	int i;
 
-	err = scarlett2_update_input_air(mixer);
-	if (err < 0)
-		return err;
+	if (!info->gain_input_count)
+		return;
 
-	err = scarlett2_update_input_phantom(mixer);
-	if (err < 0)
-		return err;
+	private->autogain_updated = 1;
 
-	err = scarlett2_update_direct_monitor(mixer);
-	if (err < 0)
-		return err;
+	for (i = 0; i < info->gain_input_count; i++) {
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->autogain_ctls[i]->id);
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->autogain_status_ctls[i]->id);
+	}
 
-	/* the rest of the configuration is for devices with a mixer */
-	if (!scarlett2_has_mixer(private))
-		return 0;
+	scarlett2_autogain_notify_access(mixer);
+}
 
-	err = scarlett2_update_monitor_mix(mixer);
-	if (err < 0)
-		return err;
+/* Notify on input safe switch change */
+static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer)
+{
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int i;
 
-	err = scarlett2_update_monitor_other(mixer);
-	if (err < 0)
-		return err;
+	if (!info->gain_input_count)
+		return;
 
-	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;
-	}
+	private->input_safe_updated = 1;
 
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_POWER_EXT)) {
-		err = scarlett2_update_power_status(mixer);
-		if (err < 0)
-			return err;
-	}
+	for (i = 0; i < info->gain_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->safe_ctls[i]->id);
+}
 
-	err = scarlett2_update_sync(mixer);
-	if (err < 0)
-		return err;
+/* Notify on "monitor other" change (speaker switching, talkback) */
+static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer)
+{
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
 
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
-		s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
+	private->monitor_other_updated = 1;
 
-		/* 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;
+	if (info->has_speaker_switching)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->speaker_switching_ctl->id);
 
-		for (i = 0; i < private->num_line_out; i++)
-			private->vol[i] = clamp(
-				sw_vol[i] + SCARLETT2_VOLUME_BIAS,
-				0, SCARLETT2_VOLUME_BIAS);
+	if (info->has_talkback)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->talkback_ctl->id);
 
-		/* 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;
+	/* if speaker switching was recently enabled or disabled,
+	 * invalidate the dim/mute and mux enum controls
+	 */
+	if (private->speaker_switching_switched) {
+		int i;
 
-		for (i = 0; i < private->num_line_out; i++)
-			private->mute_switch[i] =
-				!!private->mute_switch[i];
+		scarlett2_notify_dim_mute(mixer);
 
-		/* 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;
+		private->speaker_switching_switched = 0;
+		private->mux_updated = 1;
 
-			for (i = 0; i < private->num_line_out; i++)
-				private->vol_sw_hw_switch[i] =
-					!!private->vol_sw_hw_switch[i];
-		}
+		for (i = 0; i < private->num_mux_dsts; i++)
+			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+				       &private->mux_ctls[i]->id);
 	}
+}
 
-	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;
+/* Notify on direct monitor switch change */
+static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer)
+{
+	struct snd_card *card = mixer->chip->card;
+	struct scarlett2_data *private = mixer->private_data;
+	int count = private->num_mix_in * private->num_mix_out;
+	int i;
 
-	err = scarlett2_update_autogain(mixer);
-	if (err < 0)
-		return err;
+	private->direct_monitor_updated = 1;
 
-	err = scarlett2_update_input_safe(mixer);
-	if (err < 0)
-		return err;
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->direct_monitor_ctl->id);
 
-	if (scarlett2_has_config_item(private,
-				      SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
-		err = scarlett2_update_pcm_input_switch(mixer);
-		if (err < 0)
-			return err;
-	}
+	if (!scarlett2_has_mixer(private))
+		return;
 
-	err = scarlett2_update_mix(mixer);
-	if (err < 0)
-		return err;
+	private->mix_updated = 1;
 
-	return scarlett2_usb_get_mux(mixer);
+	/* Notify of change to the mix controls */
+	for (i = 0; i < count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->mix_ctls[i]->id);
 }
 
-/* Notify on sync change */
-static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
+/* Notify on power change */
+static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer)
 {
+	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
 
-	private->sync_updated = 1;
+	private->power_status_updated = 1;
 
-	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->sync_ctl->id);
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->power_status_ctl->id);
 }
 
-/* Notify on monitor change (Gen 2/3) */
-static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer)
+/* Notify on mux change */
+static void scarlett2_notify_mux(struct usb_mixer_interface *mixer)
 {
 	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
 	int i;
 
-	if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
-		return;
-
-	private->vol_updated = 1;
-
-	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->master_vol_ctl->id);
+	private->mux_updated = 1;
 
-	for (i = 0; i < private->num_line_out; i++)
-		if (private->vol_sw_hw_switch[line_out_remap(private, i)])
-			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-				       &private->vol_ctls[i]->id);
+	for (i = 0; i < private->num_mux_dsts; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &private->mux_ctls[i]->id);
 }
 
-/* Notify on volume change (Gen 4) */
-static void scarlett2_notify_volume(struct usb_mixer_interface *mixer)
+/* Notify on PCM input switch change */
+static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
 {
+	struct snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
 
-	private->vol_updated = 1;
+	private->pcm_input_switch_updated = 1;
 
-	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->master_vol_ctl->id);
-	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->headphone_vol_ctl->id);
+	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+		       &private->pcm_input_switch_ctl->id);
+
+	scarlett2_notify_mux(mixer);
 }
 
-/* Notify on dim/mute change */
-static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer)
+/* Interrupt callback */
+static void scarlett2_notify(struct urb *urb)
 {
-	struct snd_card *card = mixer->chip->card;
+	struct usb_mixer_interface *mixer = urb->context;
+	int len = urb->actual_length;
+	int ustatus = urb->status;
+	u32 data;
 	struct scarlett2_data *private = mixer->private_data;
-	int i;
+	const struct scarlett2_notification *notifications =
+		private->config_set->notifications;
 
-	if (!scarlett2_has_config_item(private, SCARLETT2_CONFIG_SW_HW_SWITCH))
-		return;
+	if (ustatus != 0 || len != 8)
+		goto requeue;
 
-	private->dim_mute_updated = 1;
+	data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
 
-	for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->dim_mute_ctls[i]->id);
+	while (data && notifications->mask) {
+		if (data & notifications->mask) {
+			data &= ~notifications->mask;
+			if (notifications->func)
+				notifications->func(mixer);
+		}
+		notifications++;
+	}
 
-	for (i = 0; i < private->num_line_out; i++)
-		if (private->vol_sw_hw_switch[line_out_remap(private, i)])
-			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-				       &private->mute_ctls[i]->id);
+	if (data)
+		usb_audio_warn(mixer->chip,
+			       "%s: Unhandled notification: 0x%08x\n",
+			       __func__, data);
+
+requeue:
+	if (ustatus != -ENOENT &&
+	    ustatus != -ECONNRESET &&
+	    ustatus != -ESHUTDOWN) {
+		urb->dev = mixer->chip->dev;
+		usb_submit_urb(urb, GFP_ATOMIC);
+	}
 }
 
-/* Notify on input level switch change */
-static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer)
+/*** Cleanup/Suspend Callbacks ***/
+
+static void scarlett2_private_free(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_level_updated = 1;
 
-	for (i = 0; i < info->level_input_count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->level_ctls[i]->id);
+	cancel_delayed_work_sync(&private->work);
+	kfree(private);
+	mixer->private_data = NULL;
 }
 
-/* Notify on input pad switch change */
-static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer)
+static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
 {
-	struct snd_card *card = mixer->chip->card;
 	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;
-	int i;
+	const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
+	int port_type, srcs = 0, dsts = 0;
 
-	private->input_pad_updated = 1;
+	/* 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];
+	}
 
-	for (i = 0; i < info->pad_input_count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->pad_ctls[i]->id);
+	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;
 }
 
-/* Notify on input air switch change */
-static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer)
+/* 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 snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
+	struct usb_host_config *config = dev->actconfig;
 	int i;
 
-	private->input_air_updated = 1;
+	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;
 
-	for (i = 0; i < info->air_input_count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->air_ctls[i]->id);
+		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;
 }
 
-/* Notify on input phantom switch change */
-static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer)
+/* Initialise private data */
+static int scarlett2_init_private(struct usb_mixer_interface *mixer,
+				  const struct scarlett2_device_entry *entry)
 {
-	struct snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
-	int i;
+	struct scarlett2_data *private =
+		kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL);
 
-	private->input_phantom_updated = 1;
+	if (!private)
+		return -ENOMEM;
 
-	for (i = 0; i < info->phantom_count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->phantom_ctls[i]->id);
+	mutex_init(&private->usb_mutex);
+	mutex_init(&private->data_mutex);
+	INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
 
-	scarlett2_phantom_notify_access(mixer);
-}
+	mixer->private_data = private;
+	mixer->private_free = scarlett2_private_free;
+	mixer->private_suspend = scarlett2_private_suspend;
 
-/* Notify on "input other" change (level/pad/air/phantom) */
-static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer)
-{
-	scarlett2_notify_input_level(mixer);
-	scarlett2_notify_input_pad(mixer);
-	scarlett2_notify_input_air(mixer);
-	scarlett2_notify_input_phantom(mixer);
+	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);
 }
 
-/* Notify on input select change */
-static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
+/* Submit a URB to receive notifications from the device */
+static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
 {
-	struct snd_card *card = mixer->chip->card;
+	struct usb_device *dev = mixer->chip->dev;
 	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
-	int i;
+	unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress);
+	void *transfer_buffer;
 
-	if (!info->gain_input_count)
-		return;
+	if (mixer->urb) {
+		usb_audio_err(mixer->chip,
+			      "%s: mixer urb already in use!\n", __func__);
+		return 0;
+	}
 
-	private->input_select_updated = 1;
+	if (usb_pipe_type_check(dev, pipe))
+		return -EINVAL;
 
-	snd_ctl_notify(card,
-		       SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
-		       &private->input_select_ctl->id);
+	mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!mixer->urb)
+		return -ENOMEM;
 
-	for (i = 0; i < info->gain_input_count / 2; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->input_link_ctls[i]->id);
+	transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL);
+	if (!transfer_buffer)
+		return -ENOMEM;
+
+	usb_fill_int_urb(mixer->urb, dev, pipe,
+			 transfer_buffer, private->wMaxPacketSize,
+			 scarlett2_notify, mixer, private->bInterval);
+
+	return usb_submit_urb(mixer->urb, GFP_KERNEL);
 }
 
-/* Notify on input gain change */
-static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer)
+/* Cargo cult proprietary initialisation sequence */
+static int scarlett2_usb_init(struct usb_mixer_interface *mixer)
 {
-	struct snd_card *card = mixer->chip->card;
+	struct usb_device *dev = mixer->chip->dev;
 	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
-	int i;
+	u8 step0_buf[24];
+	u8 step2_buf[84];
+	int err;
 
-	if (!info->gain_input_count)
-		return;
+	if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
+		return -EINVAL;
 
-	private->input_gain_updated = 1;
+	/* step 0 */
+	err = scarlett2_usb_rx(dev, private->bInterfaceNumber,
+			       SCARLETT2_USB_CMD_INIT,
+			       step0_buf, sizeof(step0_buf));
+	if (err < 0)
+		return err;
 
-	for (i = 0; i < info->gain_input_count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->input_gain_ctls[i]->id);
+	/* 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;
 }
 
-/* Notify on autogain change */
-static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer)
+/* 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 snd_card *card = mixer->chip->card;
 	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
-	int i;
+	int err, count, i;
 
-	if (!info->gain_input_count)
-		return;
+	struct {
+		__le32 size;
+		__le32 count;
+		u8 unknown[8];
+	} __packed flash_info;
 
-	private->autogain_updated = 1;
+	struct {
+		__le32 size;
+		__le32 flags;
+		char name[16];
+	} __packed segment_info;
 
-	for (i = 0; i < info->gain_input_count; i++) {
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->autogain_ctls[i]->id);
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->autogain_status_ctls[i]->id);
+	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;
 	}
 
-	scarlett2_autogain_notify_access(mixer);
-}
+	for (i = 0; i < count; i++) {
+		__le32 segment_num_req = cpu_to_le32(i);
+		int flash_segment_id;
 
-/* Notify on input safe switch change */
-static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer)
-{
-	struct snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_device_info *info = private->info;
-	int i;
+		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 (!info->gain_input_count)
-		return;
+		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->input_safe_updated = 1;
+		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;
+	}
 
-	for (i = 0; i < info->gain_input_count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->safe_ctls[i]->id);
+	/* 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;
 }
 
-/* Notify on "monitor other" change (speaker switching, talkback) */
-static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer)
+/* Read configuration from the interface on start */
+static int scarlett2_read_configs(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 err, i;
 
-	private->monitor_other_updated = 1;
-
-	if (info->has_speaker_switching)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->speaker_switching_ctl->id);
-
-	if (info->has_talkback)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->talkback_ctl->id);
-
-	/* if speaker switching was recently enabled or disabled,
-	 * invalidate the dim/mute and mux enum controls
-	 */
-	if (private->speaker_switching_switched) {
-		int i;
-
-		scarlett2_notify_dim_mute(mixer);
-
-		private->speaker_switching_switched = 0;
-		private->mux_updated = 1;
-
-		for (i = 0; i < private->num_mux_dsts; i++)
-			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-				       &private->mux_ctls[i]->id);
+	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;
 	}
-}
 
-/* Notify on direct monitor switch change */
-static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer)
-{
-	struct snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
-	int count = private->num_mix_in * private->num_mix_out;
-	int i;
+	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;
+	}
 
-	private->direct_monitor_updated = 1;
+	/* no other controls are created if MSD mode is on */
+	if (private->msd_switch)
+		return 0;
 
-	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->direct_monitor_ctl->id);
+	err = scarlett2_update_input_level(mixer);
+	if (err < 0)
+		return err;
 
-	if (!scarlett2_has_mixer(private))
-		return;
+	err = scarlett2_update_input_pad(mixer);
+	if (err < 0)
+		return err;
 
-	private->mix_updated = 1;
+	err = scarlett2_update_input_air(mixer);
+	if (err < 0)
+		return err;
 
-	/* Notify of change to the mix controls */
-	for (i = 0; i < count; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->mix_ctls[i]->id);
-}
+	err = scarlett2_update_input_phantom(mixer);
+	if (err < 0)
+		return err;
 
-/* Notify on power change */
-static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer)
-{
-	struct snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
+	err = scarlett2_update_direct_monitor(mixer);
+	if (err < 0)
+		return err;
 
-	private->power_status_updated = 1;
+	/* the rest of the configuration is for devices with a mixer */
+	if (!scarlett2_has_mixer(private))
+		return 0;
 
-	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->power_status_ctl->id);
-}
+	err = scarlett2_update_monitor_mix(mixer);
+	if (err < 0)
+		return err;
 
-/* Notify on mux change */
-static void scarlett2_notify_mux(struct usb_mixer_interface *mixer)
-{
-	struct snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
-	int i;
+	err = scarlett2_update_monitor_other(mixer);
+	if (err < 0)
+		return err;
 
-	private->mux_updated = 1;
+	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;
+	}
 
-	for (i = 0; i < private->num_mux_dsts; i++)
-		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-			       &private->mux_ctls[i]->id);
-}
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_POWER_EXT)) {
+		err = scarlett2_update_power_status(mixer);
+		if (err < 0)
+			return err;
+	}
 
-/* Notify on PCM input switch change */
-static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
-{
-	struct snd_card *card = mixer->chip->card;
-	struct scarlett2_data *private = mixer->private_data;
+	err = scarlett2_update_sync(mixer);
+	if (err < 0)
+		return err;
 
-	private->pcm_input_switch_updated = 1;
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_LINE_OUT_VOLUME)) {
+		s16 sw_vol[SCARLETT2_ANALOGUE_MAX];
 
-	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
-		       &private->pcm_input_switch_ctl->id);
+		/* 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;
 
-	scarlett2_notify_mux(mixer);
-}
+		for (i = 0; i < private->num_line_out; i++)
+			private->vol[i] = clamp(
+				sw_vol[i] + SCARLETT2_VOLUME_BIAS,
+				0, SCARLETT2_VOLUME_BIAS);
 
-/* Interrupt callback */
-static void scarlett2_notify(struct urb *urb)
-{
-	struct usb_mixer_interface *mixer = urb->context;
-	int len = urb->actual_length;
-	int ustatus = urb->status;
-	u32 data;
-	struct scarlett2_data *private = mixer->private_data;
-	const struct scarlett2_notification *notifications =
-		private->config_set->notifications;
+		/* 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;
 
-	if (ustatus != 0 || len != 8)
-		goto requeue;
+		for (i = 0; i < private->num_line_out; i++)
+			private->mute_switch[i] =
+				!!private->mute_switch[i];
 
-	data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
+		/* 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;
 
-	while (data && notifications->mask) {
-		if (data & notifications->mask) {
-			data &= ~notifications->mask;
-			if (notifications->func)
-				notifications->func(mixer);
+			for (i = 0; i < private->num_line_out; i++)
+				private->vol_sw_hw_switch[i] =
+					!!private->vol_sw_hw_switch[i];
 		}
-		notifications++;
 	}
 
-	if (data)
-		usb_audio_warn(mixer->chip,
-			       "%s: Unhandled notification: 0x%08x\n",
-			       __func__, data);
+	err = scarlett2_update_volumes(mixer);
+	if (err < 0)
+		return err;
 
-requeue:
-	if (ustatus != -ENOENT &&
-	    ustatus != -ECONNRESET &&
-	    ustatus != -ESHUTDOWN) {
-		urb->dev = mixer->chip->dev;
-		usb_submit_urb(urb, GFP_ATOMIC);
-	}
-}
+	err = scarlett2_update_dim_mute(mixer);
+	if (err < 0)
+		return err;
 
-static int scarlett2_init_notify(struct usb_mixer_interface *mixer)
-{
-	struct usb_device *dev = mixer->chip->dev;
-	struct scarlett2_data *private = mixer->private_data;
-	unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress);
-	void *transfer_buffer;
+	err = scarlett2_update_input_select(mixer);
+	if (err < 0)
+		return err;
 
-	if (mixer->urb) {
-		usb_audio_err(mixer->chip,
-			      "%s: mixer urb already in use!\n", __func__);
-		return 0;
-	}
+	err = scarlett2_update_input_gain(mixer);
+	if (err < 0)
+		return err;
 
-	if (usb_pipe_type_check(dev, pipe))
-		return -EINVAL;
+	err = scarlett2_update_autogain(mixer);
+	if (err < 0)
+		return err;
 
-	mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!mixer->urb)
-		return -ENOMEM;
+	err = scarlett2_update_input_safe(mixer);
+	if (err < 0)
+		return err;
 
-	transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL);
-	if (!transfer_buffer)
-		return -ENOMEM;
+	if (scarlett2_has_config_item(private,
+				      SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+		err = scarlett2_update_pcm_input_switch(mixer);
+		if (err < 0)
+			return err;
+	}
 
-	usb_fill_int_urb(mixer->urb, dev, pipe,
-			 transfer_buffer, private->wMaxPacketSize,
-			 scarlett2_notify, mixer, private->bInterval);
+	err = scarlett2_update_mix(mixer);
+	if (err < 0)
+		return err;
 
-	return usb_submit_urb(mixer->urb, GFP_KERNEL);
+	return scarlett2_usb_get_mux(mixer);
 }
 
 static const struct scarlett2_device_entry *get_scarlett2_device_entry(