ALSA: scarlett2: Move initialisation code lower in the source
authorGeoffrey D. Bennett <g@b4.vu>
Tue, 12 Mar 2024 18:33:14 +0000 (05:03 +1030)
committerTakashi Iwai <tiwai@suse.de>
Thu, 18 Apr 2024 06:31:13 +0000 (08:31 +0200)
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>

sound/usb/mixer_scarlett2.c

index bd114be537d7aed32f75b955b5c9bd7db199cc2f..8390b646c0aef1c2e9c33d8a733f80913abb11d0 100644 (file)
@@ -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(