]> www.infradead.org Git - users/hch/misc.git/commitdiff
ALSA: scarlett2: Add Vocaster speaker/headphone mute controls
authorGeoffrey D. Bennett <g@b4.vu>
Thu, 4 Sep 2025 15:53:51 +0000 (01:23 +0930)
committerTakashi Iwai <tiwai@suse.de>
Sat, 6 Sep 2025 18:02:52 +0000 (20:02 +0200)
Add support for the speaker and headphone mute controls on Focusrite
Vocaster interfaces. Unlike other Focusrite interfaces, these mute
controls are per-output, not per-channel.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
Link: https://patch.msgid.link/aLm2Dy6+O/eLTGVn@m.b4.vu
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/mixer_scarlett2.c

index e762d6110b1aba99641536b390ec79fc45fb0764..f2446bf3982c66a80a6ef0446fa606447ac32768 100644 (file)
@@ -10,8 +10,9 @@
  *   - Solo/2i2/4i4 Gen 4
  *   - Clarett 2Pre/4Pre/8Pre USB
  *   - Clarett+ 2Pre/4Pre/8Pre
+ *   - Vocaster One/Two
  *
- *   Copyright (c) 2018-2024 by Geoffrey D. Bennett <g at b4.vu>
+ *   Copyright (c) 2018-2025 by Geoffrey D. Bennett <g at b4.vu>
  *   Copyright (c) 2020-2021 by Vladimir Sadovnikov <sadko4u@gmail.com>
  *   Copyright (c) 2022 by Christian Colglazier <christian@cacolglazier.com>
  *
@@ -75,6 +76,9 @@
  * to many LinuxMusicians people and to Focusrite for hardware
  * donations).
  *
+ * Support for Vocaster One and Two added in Mar 2024 (thanks to many
+ * LinuxMusicians people and to Focusrite for hardware donations).
+ *
  * This ALSA mixer gives access to (model-dependent):
  *  - input, output, mixer-matrix muxes
  *  - mixer-matrix gain stages
@@ -364,6 +368,21 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
        "Mute Playback Switch", "Dim Playback Switch"
 };
 
+/* Vocaster One speaker/headphone mute names */
+static const char *const vocaster_one_sp_hp_mute_names[] = {
+       "Speaker Mute Playback Switch",
+       "Headphones Mute Playback Switch",
+       NULL
+};
+
+/* Vocaster Two speaker/headphone mute names */
+static const char *const vocaster_two_sp_hp_mute_names[] = {
+       "Speaker Mute Playback Switch",
+       "Headphones 1 Mute Playback Switch",
+       "Headphones 2 Mute Playback Switch",
+       NULL
+};
+
 /* The autogain_status is set based on the autogain_switch and
  * raw_autogain_status values.
  *
@@ -547,6 +566,7 @@ enum {
        SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
        SCARLETT2_CONFIG_BLUETOOTH_VOLUME,
        SCARLETT2_CONFIG_SPDIF_MODE,
+       SCARLETT2_CONFIG_SP_HP_MUTE,
        SCARLETT2_CONFIG_COUNT
 };
 
@@ -814,6 +834,9 @@ static const struct scarlett2_config_set scarlett2_config_set_vocaster = {
 
                [SCARLETT2_CONFIG_BLUETOOTH_VOLUME] = {
                        .offset = 0xbf, .size = 8, .activate = 28 },
+
+               [SCARLETT2_CONFIG_SP_HP_MUTE] = {
+                       .offset = 0xab, .size = 8, .activate = 10 },
        }
 };
 
@@ -1177,6 +1200,9 @@ struct scarlett2_device_info {
        /* additional description for the line out volume controls */
        const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
 
+       /* Vocaster speaker/headphone mute control names */
+       const char * const *sp_hp_mute_names;
+
        /* number of sources/destinations of each port type */
        const int port_count[SCARLETT2_PORT_TYPE_COUNT][SCARLETT2_PORT_DIRNS];
 
@@ -1249,6 +1275,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 sp_hp_mute;
        u8 air_switch[SCARLETT2_AIR_SWITCH_MAX];
        u8 dsp_switch[SCARLETT2_DSP_SWITCH_MAX];
        s32 compressor_values[SCARLETT2_COMPRESSOR_CTLS_MAX];
@@ -1791,6 +1818,7 @@ static const struct scarlett2_device_info vocaster_one_info = {
        .peq_flt_total_count = 4,
        .mute_input_count = 1,
        .gain_input_count = 1,
+       .sp_hp_mute_names = vocaster_one_sp_hp_mute_names,
 
        .port_count = {
                [SCARLETT2_PORT_TYPE_NONE]     = { 1,  0 },
@@ -1835,6 +1863,7 @@ static const struct scarlett2_device_info vocaster_two_info = {
        .mute_input_count = 2,
        .gain_input_count = 2,
        .has_bluetooth = 1,
+       .sp_hp_mute_names = vocaster_two_sp_hp_mute_names,
 
        .port_count = {
                [SCARLETT2_PORT_TYPE_NONE]     = {  1,  0 },
@@ -6293,6 +6322,61 @@ static const struct snd_kcontrol_new scarlett2_dim_mute_ctl = {
        .put  = scarlett2_dim_mute_ctl_put
 };
 
+/*** Vocaster Speaker/Headphone Mute Controls ***/
+
+static int scarlett2_sp_hp_mute_ctl_get(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *elem = kctl->private_data;
+       struct scarlett2_data *private = elem->head.mixer->private_data;
+
+       ucontrol->value.integer.value[0] =
+               !!(private->sp_hp_mute & (1 << elem->control));
+
+       return 0;
+}
+
+static int scarlett2_sp_hp_mute_ctl_put(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_elem_info *elem = kctl->private_data;
+       struct usb_mixer_interface *mixer = elem->head.mixer;
+       struct scarlett2_data *private = mixer->private_data;
+       int index = elem->control;
+       int val, err;
+
+       guard(mutex)(&private->data_mutex);
+
+       if (private->hwdep_in_use)
+               return -EBUSY;
+
+       val = private->sp_hp_mute;
+
+       if (ucontrol->value.integer.value[0])
+               val |= (1 << index);
+       else
+               val &= ~(1 << index);
+
+       if (val == private->sp_hp_mute)
+               return 0;
+
+       private->sp_hp_mute = val;
+
+       /* Send change to the device */
+       err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SP_HP_MUTE,
+                                      0, val);
+
+       return err < 0 ? err : 1;
+}
+
+static const struct snd_kcontrol_new scarlett2_sp_hp_mute_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "",
+       .info = snd_ctl_boolean_mono_info,
+       .get  = scarlett2_sp_hp_mute_ctl_get,
+       .put  = scarlett2_sp_hp_mute_ctl_put
+};
+
 /*** Create the analogue output controls ***/
 
 static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
@@ -6325,6 +6409,17 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
                        return err;
        }
 
+       /* Add Vocaster speaker/headphone mute controls */
+       if (private->info->sp_hp_mute_names)
+               for (i = 0; private->info->sp_hp_mute_names[i]; i++) {
+                       err = scarlett2_add_new_ctl(
+                               mixer, &scarlett2_sp_hp_mute_ctl,
+                               i, 1, private->info->sp_hp_mute_names[i],
+                               NULL);
+                       if (err < 0)
+                               return err;
+               }
+
        /* Remaining controls are only applicable if the device
         * has per-channel line-out volume controls.
         */