ALSA: scarlett2: Add Vocaster speaker/headphone mute controls

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>
This commit is contained in:
Geoffrey D. Bennett 2025-09-05 01:23:51 +09:30 committed by Takashi Iwai
parent 400f60b274
commit cbd676874e
1 changed files with 96 additions and 1 deletions

View 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.
*/