Commit 0c88f9db authored by Geoffrey D. Bennett's avatar Geoffrey D. Bennett Committed by Takashi Iwai
Browse files

ALSA: usb-audio: scarlett2: Add mute support



For each analogue output, in addition to the output volume (gain)
control, the hardware also has a mute control. Add ALSA mute controls
for each analogue output.

If the device has the line_out_hw_vol feature, then the mute control
is disabled along with the output volume control when the switch is
set to HW.

Signed-off-by: default avatarGeoffrey D. Bennett <g@b4.vu>
Link: https://lore.kernel.org/r/6fad82174b44633e46cfd96332a038de74d544f2.1624294591.git.g@b4.vu


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 06250c89
Loading
Loading
Loading
Loading
+145 −25
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@
 * This ALSA mixer gives access to:
 *  - input, output, mixer-matrix muxes
 *  - 18x10 mixer-matrix gain stages
 *  - gain/volume controls
 *  - gain/volume/mute controls
 *  - level meters
 *  - line/inst level and pad controls
 *
@@ -195,7 +195,11 @@ enum {
};

/* Dim/Mute buttons on the 18i20 */
#define SCARLETT2_DIM_MUTE_COUNT 2
enum {
	SCARLETT2_BUTTON_MUTE    = 0,
	SCARLETT2_BUTTON_DIM     = 1,
	SCARLETT2_DIM_MUTE_COUNT = 2,
};

static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
	"Mute", "Dim"
@@ -258,12 +262,14 @@ struct scarlett2_data {
	u8 master_vol;
	u8 vol[SCARLETT2_ANALOGUE_MAX];
	u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
	u8 mute_switch[SCARLETT2_ANALOGUE_MAX];
	u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX];
	u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX];
	u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT];
	struct snd_kcontrol *sync_ctl;
	struct snd_kcontrol *master_vol_ctl;
	struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
	struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX];
	struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT];
	u8 mux[SCARLETT2_MUX_MAX];
	u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX];
@@ -509,7 +515,8 @@ struct scarlett2_usb_volume_status {
	/* actual volume of output inc. dim (-18dB) */
	s16 hw_vol[SCARLETT2_ANALOGUE_MAX];

	u8 pad2[SCARLETT2_ANALOGUE_MAX];
	/* internal mute buttons */
	u8 mute_switch[SCARLETT2_ANALOGUE_MAX];

	/* sw (0) or hw (1) controlled */
	u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
@@ -524,10 +531,11 @@ struct scarlett2_usb_volume_status {
enum {
	SCARLETT2_CONFIG_DIM_MUTE = 0,
	SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1,
	SCARLETT2_CONFIG_SW_HW_SWITCH = 2,
	SCARLETT2_CONFIG_LEVEL_SWITCH = 3,
	SCARLETT2_CONFIG_PAD_SWITCH = 4,
	SCARLETT2_CONFIG_COUNT = 5
	SCARLETT2_CONFIG_MUTE_SWITCH = 2,
	SCARLETT2_CONFIG_SW_HW_SWITCH = 3,
	SCARLETT2_CONFIG_LEVEL_SWITCH = 4,
	SCARLETT2_CONFIG_PAD_SWITCH = 5,
	SCARLETT2_CONFIG_COUNT = 6
};

/* Location, size, and activation command number for the configuration
@@ -547,6 +555,9 @@ static const struct scarlett2_config
	[SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
		.offset = 0x34, .size = 2, .activate = 1 },

	[SCARLETT2_CONFIG_MUTE_SWITCH] = {
		.offset = 0x5c, .size = 1, .activate = 1 },

	[SCARLETT2_CONFIG_SW_HW_SWITCH] = {
		.offset = 0x66, .size = 1, .activate = 3 },

@@ -1197,6 +1208,7 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer)
	int num_line_out =
		ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
	int err, i;
	int mute;

	private->vol_updated = 0;

@@ -1208,15 +1220,18 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer)
		volume_status.master_vol + SCARLETT2_VOLUME_BIAS,
		0, SCARLETT2_VOLUME_BIAS);

	for (i = 0; i < num_line_out; i++) {
		if (private->vol_sw_hw_switch[i])
			private->vol[i] = private->master_vol;
	}

	if (info->line_out_hw_vol)
		for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
			private->dim_mute[i] = !!volume_status.dim_mute[i];

	mute = private->dim_mute[SCARLETT2_BUTTON_MUTE];

	for (i = 0; i < num_line_out; i++)
		if (private->vol_sw_hw_switch[i]) {
			private->vol[i] = private->master_vol;
			private->mute_switch[i] = mute;
		}

	return 0;
}

@@ -1321,6 +1336,55 @@ static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = {
	.tlv = { .p = db_scale_scarlett2_gain }
};

/*** Mute Switch Controls ***/

static int scarlett2_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;
	int index = elem->control;

	ucontrol->value.integer.value[0] = private->mute_switch[index];
	return 0;
}

static int scarlett2_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 oval, val, err = 0;

	mutex_lock(&private->data_mutex);

	oval = private->mute_switch[index];
	val = !!ucontrol->value.integer.value[0];

	if (oval == val)
		goto unlock;

	private->mute_switch[index] = val;

	/* Send mute change to the device */
	err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
				       index, val);

unlock:
	mutex_unlock(&private->data_mutex);
	return err;
}

static const struct snd_kcontrol_new scarlett2_mute_ctl = {
	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
	.name = "",
	.info = snd_ctl_boolean_mono_info,
	.get  = scarlett2_mute_ctl_get,
	.put  = scarlett2_mute_ctl_put,
};

/*** HW/SW Volume Switch Controls ***/

static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl,
@@ -1348,18 +1412,26 @@ static void scarlett2_vol_ctl_set_writable(struct usb_mixer_interface *mixer,
					   int index, int value)
{
	struct scarlett2_data *private = mixer->private_data;
	struct snd_card *card = mixer->chip->card;

	/* Set/Clear write bit */
	if (value)
	/* Set/Clear write bits */
	if (value) {
		private->vol_ctls[index]->vd[0].access |=
			SNDRV_CTL_ELEM_ACCESS_WRITE;
	else
		private->mute_ctls[index]->vd[0].access |=
			SNDRV_CTL_ELEM_ACCESS_WRITE;
	} else {
		private->vol_ctls[index]->vd[0].access &=
			~SNDRV_CTL_ELEM_ACCESS_WRITE;
		private->mute_ctls[index]->vd[0].access &=
			~SNDRV_CTL_ELEM_ACCESS_WRITE;
	}

	/* Notify of write bit change */
	snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO,
	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
		       &private->vol_ctls[index]->id);
	snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
		       &private->mute_ctls[index]->id);
}

static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
@@ -1387,8 +1459,9 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
	 */
	scarlett2_vol_ctl_set_writable(mixer, index, !val);

	/* Reset volume to master volume */
	/* Reset volume/mute to master volume/mute */
	private->vol[index] = private->master_vol;
	private->mute_switch[index] = private->dim_mute[SCARLETT2_BUTTON_MUTE];

	/* Set SW volume to current HW volume */
	err = scarlett2_usb_set_config(
@@ -1397,6 +1470,13 @@ static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl,
	if (err < 0)
		goto unlock;

	/* Set SW mute to current HW mute */
	err = scarlett2_usb_set_config(
		mixer, SCARLETT2_CONFIG_MUTE_SWITCH,
		index, private->dim_mute[SCARLETT2_BUTTON_MUTE]);
	if (err < 0)
		goto unlock;

	/* Send SW/HW switch change to the device */
	err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH,
				       index, val);
@@ -1554,9 +1634,13 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
	struct usb_mixer_elem_info *elem = kctl->private_data;
	struct usb_mixer_interface *mixer = elem->head.mixer;
	struct scarlett2_data *private = mixer->private_data;
	const struct scarlett2_device_info *info = private->info;
	const struct scarlett2_ports *ports = info->ports;
	int num_line_out =
		ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];

	int index = elem->control;
	int oval, val, err = 0;
	int oval, val, err = 0, i;

	mutex_lock(&private->data_mutex);

@@ -1574,6 +1658,15 @@ static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl,
	if (err == 0)
		err = 1;

	if (index == SCARLETT2_BUTTON_MUTE)
		for (i = 0; i < num_line_out; i++)
			if (private->vol_sw_hw_switch[i]) {
				private->mute_switch[i] = val;
				snd_ctl_notify(mixer->chip->card,
					       SNDRV_CTL_EVENT_MASK_INFO,
					       &private->mute_ctls[i]->id);
			}

unlock:
	mutex_unlock(&private->data_mutex);
	return err;
@@ -1627,7 +1720,20 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
		if (err < 0)
			return err;

		/* Make the fader read-only if the SW/HW switch is set to HW */
		/* Mute Switch */
		snprintf(s, sizeof(s),
			 "Line %02d Mute Playback Switch",
			 i + 1);
		err = scarlett2_add_new_ctl(mixer,
					    &scarlett2_mute_ctl,
					    i, 1, s,
					    &private->mute_ctls[i]);
		if (err < 0)
			return err;

		/* Make the fader and mute controls read-only if the
		 * SW/HW switch is set to HW
		 */
		if (private->vol_sw_hw_switch[i])
			scarlett2_vol_ctl_set_writable(mixer, i, 0);

@@ -2109,12 +2215,16 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
	if (err < 0)
		return err;

	if (info->line_out_hw_vol)
		for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
			private->dim_mute[i] = !!volume_status.dim_mute[i];

	private->master_vol = clamp(
		volume_status.master_vol + SCARLETT2_VOLUME_BIAS,
		0, SCARLETT2_VOLUME_BIAS);

	for (i = 0; i < num_line_out; i++) {
		int volume;
		int volume, mute;

		private->vol_sw_hw_switch[i] =
			info->line_out_hw_vol
@@ -2126,11 +2236,12 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
		volume = clamp(volume + SCARLETT2_VOLUME_BIAS,
			       0, SCARLETT2_VOLUME_BIAS);
		private->vol[i] = volume;
	}

	if (info->line_out_hw_vol)
		for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
			private->dim_mute[i] = !!volume_status.dim_mute[i];
		mute = private->vol_sw_hw_switch[i]
			 ? private->dim_mute[SCARLETT2_BUTTON_MUTE]
			 : volume_status.mute_switch[i];
		private->mute_switch[i] = mute;
	}

	for (i = 0; i < num_mixer_out; i++) {
		err = scarlett2_usb_get_mix(mixer, i);
@@ -2185,8 +2296,12 @@ static void scarlett2_notify_monitor(
static void scarlett2_notify_dim_mute(
	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;
	const struct scarlett2_ports *ports = info->ports;
	int num_line_out =
		ports[SCARLETT2_PORT_TYPE_ANALOGUE].num[SCARLETT2_PORT_OUT];
	int i;

	private->vol_updated = 1;
@@ -2195,8 +2310,13 @@ static void scarlett2_notify_dim_mute(
		return;

	for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++)
		snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
			       &private->dim_mute_ctls[i]->id);

	for (i = 0; i < num_line_out; i++)
		if (private->vol_sw_hw_switch[i])
			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
				       &private->mute_ctls[i]->id);
}

/* Interrupt callback */