diff mbox series

[10/20] ALSA: scarlett2: Disable autogain during phantom power state change

Message ID f49f7bf9358e1f20713d95d407d8d6a436859877.1703612638.git.g@b4.vu (mailing list archive)
State Accepted
Commit 882a2a36c41df96d0449c3751dceb86415b44a56
Headers show
Series ALSA: scarlett2: Add support for Scarlett 4th Gen | expand

Commit Message

Geoffrey D. Bennett Dec. 26, 2023, 6:07 p.m. UTC
When phantom power is enabled or disabled, the autogain control cannot
be enabled until the interface has signalled that the change is
complete and the input is unmuted. Update those controls to be
read-only during this time.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
 sound/usb/mixer_scarlett2.c | 123 +++++++++++++++++++++++++++++++++---
 1 file changed, 113 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 6c8398aa103f..2d6fd9faed31 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -2391,6 +2391,10 @@  static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer)
 
 /*** Autogain Switch and Status Controls ***/
 
+/* Forward declarations as phantom power and autogain can disable each other */
+static int scarlett2_check_input_phantom_updated(struct usb_mixer_interface *);
+static int scarlett2_phantom_is_switching(struct scarlett2_data *, int);
+
 /* Set the access mode of a control to read-only (val = 0) or
  * read-write (val = 1).
  */
@@ -2557,6 +2561,27 @@  static int scarlett2_check_put_during_autogain(
 	return 0;
 }
 
+static int scarlett2_autogain_switch_ctl_info(
+	struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+	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 err;
+
+	mutex_lock(&private->data_mutex);
+
+	err = scarlett2_check_input_phantom_updated(mixer);
+	if (err < 0)
+		goto unlock;
+
+	err = snd_ctl_boolean_mono_info(kctl, uinfo);
+
+unlock:
+	mutex_unlock(&private->data_mutex);
+	return err;
+}
+
 static int scarlett2_autogain_switch_ctl_get(
 	struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
 {
@@ -2619,7 +2644,7 @@  static int scarlett2_autogain_switch_ctl_put(
 	struct scarlett2_data *private = mixer->private_data;
 
 	int index = elem->control;
-	int oval, val, err = 0;
+	int oval, val, err;
 
 	mutex_lock(&private->data_mutex);
 
@@ -2628,6 +2653,15 @@  static int scarlett2_autogain_switch_ctl_put(
 		goto unlock;
 	}
 
+	err = scarlett2_check_input_phantom_updated(mixer);
+	if (err < 0)
+		goto unlock;
+
+	if (scarlett2_phantom_is_switching(private, index)) {
+		err = -EPERM;
+		goto unlock;
+	}
+
 	oval = private->autogain_switch[index];
 	val = !!ucontrol->value.integer.value[0];
 
@@ -2664,7 +2698,7 @@  static int scarlett2_autogain_status_ctl_info(
 static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "",
-	.info = snd_ctl_boolean_mono_info,
+	.info = scarlett2_autogain_switch_ctl_info,
 	.get  = scarlett2_autogain_switch_ctl_get,
 	.put  = scarlett2_autogain_switch_ctl_put
 };
@@ -3983,13 +4017,74 @@  static int scarlett2_update_input_phantom(struct usb_mixer_interface *mixer)
 	return 0;
 }
 
+/* Check if phantom power on the given input is currently changing state */
+static int scarlett2_phantom_is_switching(
+	struct scarlett2_data *private, int line_num)
+{
+	const struct scarlett2_device_info *info = private->info;
+	int index = line_num / info->inputs_per_phantom;
+
+	return !!(private->phantom_switch[index] & 0x02);
+}
+
+/* Update autogain controls' access mode when phantom power changes state */
+static void scarlett2_phantom_update_access(struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	const struct scarlett2_device_info *info = private->info;
+	int i;
+
+	/* Disable autogain controls if phantom power is changing state */
+	for (i = 0; i < info->gain_input_count; i++) {
+		int val = !scarlett2_phantom_is_switching(private, i);
+
+		scarlett2_set_ctl_access(private->autogain_ctls[i], val);
+	}
+}
+
+/* Notify of access mode change for autogain which can't be enabled
+ * while phantom power is changing.
+ */
+static void scarlett2_phantom_notify_access(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;
+
+	for (i = 0; i < info->gain_input_count; i++)
+		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+			       &private->autogain_ctls[i]->id);
+}
+
+/* Call scarlett2_update_input_phantom() and
+ * scarlett2_phantom_update_access() if input_phantom_updated is set.
+ */
+static int scarlett2_check_input_phantom_updated(
+	struct usb_mixer_interface *mixer)
+{
+	struct scarlett2_data *private = mixer->private_data;
+	int err;
+
+	if (!private->input_phantom_updated)
+		return 0;
+
+	err = scarlett2_update_input_phantom(mixer);
+	if (err < 0)
+		return err;
+
+	scarlett2_phantom_update_access(mixer);
+
+	return 0;
+}
+
 static int scarlett2_phantom_ctl_get(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 err = 0;
+	int err;
 
 	mutex_lock(&private->data_mutex);
 
@@ -3998,11 +4093,10 @@  static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl,
 		goto unlock;
 	}
 
-	if (private->input_phantom_updated) {
-		err = scarlett2_update_input_phantom(mixer);
-		if (err < 0)
-			goto unlock;
-	}
+	err = scarlett2_check_input_phantom_updated(mixer);
+	if (err < 0)
+		goto unlock;
+
 	ucontrol->value.integer.value[0] = scarlett2_decode_muteable(
 		private->phantom_switch[elem->control]);
 
@@ -4051,6 +4145,9 @@  static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
 	if (err == 0)
 		err = 1;
 
+	scarlett2_phantom_update_access(mixer);
+	scarlett2_phantom_notify_access(mixer);
+
 unlock:
 	mutex_unlock(&private->data_mutex);
 	return err;
@@ -5912,6 +6009,8 @@  static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer)
 	for (i = 0; i < info->phantom_count; i++)
 		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
 			       &private->phantom_ctls[i]->id);
+
+	scarlett2_phantom_notify_access(mixer);
 }
 
 /* Notify on "input other" change (level/pad/air/phantom) */
@@ -6231,9 +6330,13 @@  static int snd_scarlett2_controls_create(
 	if (err < 0)
 		return err;
 
-	/* Set the access mode of controls disabled during autogain */
-	if (private->info->gain_input_count)
+	/* Set the access mode of controls disabled during
+	 * autogain/phantom power switching.
+	 */
+	if (private->info->gain_input_count) {
 		scarlett2_autogain_update_access(mixer);
+		scarlett2_phantom_update_access(mixer);
+	}
 
 	/* Set up the interrupt polling */
 	err = scarlett2_init_notify(mixer);