]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
ALSA: usb-audio: scarlett2: Add Gen 3 MSD mode switch
authorGeoffrey D. Bennett <g@b4.vu>
Tue, 22 Jun 2021 17:01:52 +0000 (02:31 +0930)
committerTakashi Iwai <tiwai@suse.de>
Tue, 22 Jun 2021 19:42:24 +0000 (21:42 +0200)
Add a control to disable the Gen 3 MSD mode so that the full
functionality of the device is available. Don't create the other
controls until MSD mode is disabled.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
Link: https://lore.kernel.org/r/1cb93bbe585f6b0a74f5dc27450bc87e1f3776dc.1624379707.git.g@b4.vu
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/mixer_scarlett_gen2.c

index 06454d4e58bfb90ca674bbaf8cf70a7cabf62151..f35420369042f36fd86797ef73f4a6041981a0f9 100644 (file)
  * interface during driver initialisation added in May 2021 (thanks to
  * Vladimir Sadovnikov for figuring out how).
  *
- * This ALSA mixer gives access to:
+ * This ALSA mixer gives access to (model-dependent):
  *  - input, output, mixer-matrix muxes
  *  - mixer-matrix gain stages
  *  - gain/volume/mute controls
  *  - level meters
  *  - line/inst level and pad controls
+ *  - disable/enable MSD mode
  *
  * <ditaa>
  *    /--------------\    18chn            20chn     /--------------\
  *  \--------------/
  * </ditaa>
  *
+ * Gen 3 devices have a Mass Storage Device (MSD) mode where a small
+ * disk with registration and driver download information is presented
+ * to the host. To access the full functionality of the device without
+ * proprietary software, MSD mode can be disabled by:
+ * - holding down the 48V button for five seconds while powering on
+ *   the device, or
+ * - using this driver and alsamixer to change the "MSD Mode" setting
+ *   to Off and power-cycling the device
  */
 
 #include <linux/slab.h>
 /* device_setup value to enable */
 #define SCARLETT2_ENABLE 0x01
 
+/* device_setup value to allow turning MSD mode back on */
+#define SCARLETT2_MSD_ENABLE 0x02
+
 /* some gui mixers can't handle negative ctl values */
 #define SCARLETT2_VOLUME_BIAS 127
 
@@ -279,6 +291,12 @@ struct scarlett2_mux_entry {
 struct scarlett2_device_info {
        u32 usb_id; /* USB device identifier */
 
+       /* Gen 3 devices have an internal MSD mode switch that needs
+        * to be disabled in order to access the full functionality of
+        * the device.
+        */
+       u8 has_msd_mode;
+
        /* line out hw volume is sw controlled */
        u8 line_out_hw_vol;
 
@@ -327,6 +345,7 @@ struct scarlett2_data {
        u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX];
        u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX];
        u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT];
+       u8 msd_switch;
        struct snd_kcontrol *sync_ctl;
        struct snd_kcontrol *master_vol_ctl;
        struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
@@ -489,6 +508,7 @@ static const struct scarlett2_device_info s18i20_gen2_info = {
 static const struct scarlett2_device_info s4i4_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8212),
 
+       .has_msd_mode = 1,
        .level_input_count = 2,
        .pad_input_count = 2,
 
@@ -530,6 +550,7 @@ static const struct scarlett2_device_info s4i4_gen3_info = {
 static const struct scarlett2_device_info s8i6_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8213),
 
+       .has_msd_mode = 1,
        .level_input_count = 2,
        .pad_input_count = 2,
 
@@ -578,6 +599,7 @@ static const struct scarlett2_device_info s8i6_gen3_info = {
 static const struct scarlett2_device_info s18i8_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8214),
 
+       .has_msd_mode = 1,
        .line_out_hw_vol = 1,
        .level_input_count = 2,
        .pad_input_count = 2,
@@ -639,6 +661,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
 static const struct scarlett2_device_info s18i20_gen3_info = {
        .usb_id = USB_ID(0x1235, 0x8215),
 
+       .has_msd_mode = 1,
        .line_out_hw_vol = 1,
        .level_input_count = 2,
        .pad_input_count = 8,
@@ -786,7 +809,8 @@ enum {
        SCARLETT2_CONFIG_SW_HW_SWITCH = 3,
        SCARLETT2_CONFIG_LEVEL_SWITCH = 4,
        SCARLETT2_CONFIG_PAD_SWITCH = 5,
-       SCARLETT2_CONFIG_COUNT = 6
+       SCARLETT2_CONFIG_MSD_SWITCH = 6,
+       SCARLETT2_CONFIG_COUNT = 7
 };
 
 /* Location, size, and activation command number for the configuration
@@ -817,6 +841,9 @@ static const struct scarlett2_config
 
        [SCARLETT2_CONFIG_PAD_SWITCH] = {
                .offset = 0x84, .size = 1, .activate = 8 },
+
+       [SCARLETT2_CONFIG_MSD_SWITCH] = {
+               .offset = 0x9d, .size = 1, .activate = 6 },
 };
 
 /* proprietary request/response format */
@@ -1016,7 +1043,8 @@ static int scarlett2_usb_set_config(
                return err;
 
        /* Schedule the change to be written to NVRAM */
-       schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
+       if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE)
+               schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
 
        return 0;
 }
@@ -2351,6 +2379,71 @@ static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer)
                                     "Level Meter", NULL);
 }
 
+/*** MSD Controls ***/
+
+static int scarlett2_msd_ctl_get(struct snd_kcontrol *kctl,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *elem = kctl->private_data;
+       struct scarlett2_data *private = elem->head.mixer->private_data;
+
+       ucontrol->value.integer.value[0] = private->msd_switch;
+       return 0;
+}
+
+static int scarlett2_msd_ctl_put(struct snd_kcontrol *kctl,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *elem = kctl->private_data;
+       struct usb_mixer_interface *mixer = elem->head.mixer;
+       struct scarlett2_data *private = mixer->private_data;
+
+       int oval, val, err = 0;
+
+       mutex_lock(&private->data_mutex);
+
+       oval = private->msd_switch;
+       val = !!ucontrol->value.integer.value[0];
+
+       if (oval == val)
+               goto unlock;
+
+       private->msd_switch = val;
+
+       /* Send switch change to the device */
+       err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MSD_SWITCH,
+                                      0, val);
+
+unlock:
+       mutex_unlock(&private->data_mutex);
+       return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_msd_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "",
+       .info = snd_ctl_boolean_mono_info,
+       .get  = scarlett2_msd_ctl_get,
+       .put  = scarlett2_msd_ctl_put,
+};
+
+static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer)
+{
+       struct scarlett2_data *private = mixer->private_data;
+       const struct scarlett2_device_info *info = private->info;
+
+       if (!info->has_msd_mode)
+               return 0;
+
+       /* If MSD mode is off, hide the switch by default */
+       if (!private->msd_switch && !(mixer->chip->setup & SCARLETT2_MSD_ENABLE))
+               return 0;
+
+       /* Add MSD control */
+       return scarlett2_add_new_ctl(mixer, &scarlett2_msd_ctl,
+                                    0, 1, "MSD Mode", NULL);
+}
+
 /*** Cleanup/Suspend Callbacks ***/
 
 static void scarlett2_private_free(struct usb_mixer_interface *mixer)
@@ -2488,6 +2581,18 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
        struct scarlett2_usb_volume_status volume_status;
        int err, i;
 
+       if (info->has_msd_mode) {
+               err = scarlett2_usb_get_config(
+                       mixer, SCARLETT2_CONFIG_MSD_SWITCH,
+                       1, &private->msd_switch);
+               if (err < 0)
+                       return err;
+
+               /* no other controls are created if MSD mode is on */
+               if (private->msd_switch)
+                       return 0;
+       }
+
        err = scarlett2_update_input_other(mixer);
        if (err < 0)
                return err;
@@ -2710,6 +2815,15 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer)
        if (err < 0)
                return err;
 
+       /* Create the MSD control */
+       err = scarlett2_add_msd_ctl(mixer);
+       if (err < 0)
+               return err;
+
+       /* If MSD mode is enabled, don't create any other controls */
+       if (((struct scarlett2_data *)mixer->private_data)->msd_switch)
+               return 0;
+
        /* Create the analogue output controls */
        err = scarlett2_add_line_out_ctls(mixer);
        if (err < 0)