* [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen
@ 2023-12-26 18:05 Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 01/20] ALSA: scarlett2: Convert meter levels from little-endian Geoffrey D. Bennett
` (20 more replies)
0 siblings, 21 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:05 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Hi Takashi,
This patch series adds support for the Focusrite Scarlett 4th Gen
interfaces. It builds on top of the two previous patch series I sent:
- https://lore.kernel.org/linux-sound/cover.1703001053.git.g@b4.vu/
("ALSA: scarlett2: Firmware Upgrade and Error Handling Improvements")
- https://lore.kernel.org/linux-sound/cover.1703444932.git.g@b4.vu/
("ALSA: scarlett2: Refactor in preparation for gen4")
I already sent patch #1 in this series below separately:
https://lore.kernel.org/linux-sound/ZYsBIE3DSKdi4YC%2F@m.b4.vu/
as it should go in to 6.7. I wasn't sure if I should include it again
here or not; sorry if I guessed wrong.
Patch #2 is a little cleanup I missed before sending the previous
series and would have gone there had I noticed.
Patches #3-18 add the new controls, etc. needed for the Gen 4 support.
Patch #19 adds the USB IDs and configuration data to enable support
for the new interfaces.
Patch #20 adds a last-minute new control that is only present on the
Solo Gen 4.
Regards,
Geoffrey.
Geoffrey D. Bennett (20):
ALSA: scarlett2: Convert meter levels from little-endian
ALSA: scarlett2: Remove repeated elem->head.mixer references
ALSA: scarlett2: Add support for air/phantom control on input 2
ALSA: scarlett2: Add support for Gen 4 style parameters
ALSA: scarlett2: Allow for controls with a "mute mode"
ALSA: scarlett2: Add support for Air Presence + Drive option
ALSA: scarlett2: Add support for software-controllable input gain
ALSA: scarlett2: Minor refactor MSD mode check
ALSA: scarlett2: Disable input controls while autogain is running
ALSA: scarlett2: Disable autogain during phantom power state change
ALSA: scarlett2: Add power status control
ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor
ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates
ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes
ALSA: scarlett2: Add support for DSP mux channels
ALSA: scarlett2: Rename DSP mux channels
ALSA: scarlett2: Add minimum firmware version check
ALSA: scarlett2: Add R/O headphone volume control
ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4
ALSA: scarlett2: Add PCM Input Switch for Solo Gen 4
include/uapi/sound/scarlett2.h | 4 +-
sound/usb/mixer_quirks.c | 3 +
sound/usb/mixer_scarlett2.c | 2339 +++++++++++++++++++++++++++++++-
3 files changed, 2279 insertions(+), 67 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 01/20] ALSA: scarlett2: Convert meter levels from little-endian
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
@ 2023-12-26 18:06 ` Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 02/20] ALSA: scarlett2: Remove repeated elem->head.mixer references Geoffrey D. Bennett
` (19 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Add missing conversion from little-endian data to CPU-endian in
scarlett2_usb_get_meter_levels().
Fixes: 3473185f31df ("ALSA: scarlett2: Remap Level Meter values")
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index e25f004e50e4..e5d8543f9686 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -2110,7 +2110,7 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer,
__le16 num_meters;
__le32 magic;
} __packed req;
- u32 resp[SCARLETT2_MAX_METERS];
+ __le32 resp[SCARLETT2_MAX_METERS];
int i, err;
req.pad = 0;
@@ -2123,7 +2123,7 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer,
/* copy, convert to u16 */
for (i = 0; i < num_meters; i++)
- levels[i] = resp[i];
+ levels[i] = le32_to_cpu(resp[i]);
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 02/20] ALSA: scarlett2: Remove repeated elem->head.mixer references
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 01/20] ALSA: scarlett2: Convert meter levels from little-endian Geoffrey D. Bennett
@ 2023-12-26 18:06 ` Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 03/20] ALSA: scarlett2: Add support for air/phantom control on input 2 Geoffrey D. Bennett
` (18 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Use a local variable *mixer rather than repeating elem->header.mixer
in scarlett2_direct_monitor_ctl_get() and scarlett2_meter_ctl_get().
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index e5d8543f9686..10260666811f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -3927,7 +3927,7 @@ static int scarlett2_direct_monitor_ctl_get(
{
struct usb_mixer_elem_info *elem = kctl->private_data;
struct usb_mixer_interface *mixer = elem->head.mixer;
- struct scarlett2_data *private = elem->head.mixer->private_data;
+ struct scarlett2_data *private = mixer->private_data;
int err = 0;
mutex_lock(&private->data_mutex);
@@ -4191,7 +4191,8 @@ static int scarlett2_meter_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;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
u8 *meter_level_map = private->meter_level_map;
u16 meter_levels[SCARLETT2_MAX_METERS];
int i, err;
@@ -4203,7 +4204,7 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
goto unlock;
}
- err = scarlett2_usb_get_meter_levels(elem->head.mixer, elem->channels,
+ err = scarlett2_usb_get_meter_levels(mixer, elem->channels,
meter_levels);
if (err < 0)
goto unlock;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 03/20] ALSA: scarlett2: Add support for air/phantom control on input 2
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 01/20] ALSA: scarlett2: Convert meter levels from little-endian Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 02/20] ALSA: scarlett2: Remove repeated elem->head.mixer references Geoffrey D. Bennett
@ 2023-12-26 18:06 ` Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 04/20] ALSA: scarlett2: Add support for Gen 4 style parameters Geoffrey D. Bennett
` (17 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
The Focusrite Scarlett Gen 4 Solo has Air and Phantom Power controls
on analogue input #2 (the Gen 3 Solo had these controls on analogue
input #1). Add air_input_first and phantom_first device info options
to cater for this.
These options are similar to the level_input_first option that was
added for the Gen 3 Solo, but these new options do not require
adjusting the index of the control.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 10260666811f..5c5198bb224a 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -632,9 +632,15 @@ struct scarlett2_device_info {
*/
u8 air_input_count;
+ /* the first input with an air control (0-based) */
+ u8 air_input_first;
+
/* the number of phantom (48V) software switchable controls */
u8 phantom_count;
+ /* the first input with phantom power control (0-based) */
+ u8 phantom_first;
+
/* the number of inputs each phantom switch controls */
u8 inputs_per_phantom;
@@ -3043,6 +3049,7 @@ static int scarlett2_phantom_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;
int index = elem->control;
int oval, val, err = 0;
@@ -3064,7 +3071,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH,
- index, val);
+ index + info->phantom_first, val);
if (err == 0)
err = 1;
@@ -3763,7 +3770,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input air controls */
for (i = 0; i < info->air_input_count; i++) {
- snprintf(s, sizeof(s), fmt, i + 1, "Air", "Switch");
+ snprintf(s, sizeof(s), fmt, i + 1 + info->air_input_first,
+ "Air", "Switch");
err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl,
i, 1, s, &private->air_ctls[i]);
if (err < 0)
@@ -3773,7 +3781,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input phantom controls */
if (info->inputs_per_phantom == 1) {
for (i = 0; i < info->phantom_count; i++) {
- scnprintf(s, sizeof(s), fmt, i + 1,
+ scnprintf(s, sizeof(s), fmt,
+ i + 1 + info->phantom_first,
"Phantom Power", "Switch");
err = scarlett2_add_new_ctl(
mixer, &scarlett2_phantom_ctl,
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 04/20] ALSA: scarlett2: Add support for Gen 4 style parameters
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (2 preceding siblings ...)
2023-12-26 18:06 ` [PATCH 03/20] ALSA: scarlett2: Add support for air/phantom control on input 2 Geoffrey D. Bennett
@ 2023-12-26 18:06 ` Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 05/20] ALSA: scarlett2: Allow for controls with a "mute mode" Geoffrey D. Bennett
` (16 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Writing Scarlett Gen 4 parameters differs from Gen 2 and Gen 3:
- the values are written into a shared write location
- the values are only byte-sized
- the read locations now extend beyond 0xFF
- a separate NVRAM save step is no longer required
This patch implements that alternate write style, triggered by setting
the config item size field to zero.
The write address is specified through a new config set field
gen4_write_addr.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 53 +++++++++++++++++++++++++++++++++----
1 file changed, 48 insertions(+), 5 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 5c5198bb224a..09cd09799588 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -320,16 +320,21 @@ enum {
};
/* Location, size, and activation command number for the configuration
- * parameters. Size is in bits and may be 1, 8, or 16.
+ * parameters. Size is in bits and may be 0, 1, 8, or 16.
+ *
+ * A size of 0 indicates that the parameter is a byte-sized Scarlett
+ * Gen 4 configuration which is written through the gen4_write_addr
+ * location (but still read through the given offset location).
*/
struct scarlett2_config {
- u8 offset;
+ u16 offset;
u8 size;
u8 activate;
};
struct scarlett2_config_set {
const struct scarlett2_notification *notifications;
+ u16 gen4_write_addr;
const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
};
@@ -1612,9 +1617,12 @@ static int scarlett2_usb_get_config(
if (!config_item->offset)
return -EFAULT;
+ /* Gen 4 style parameters are always 1 byte */
+ size = config_item->size ? config_item->size : 8;
+
/* For byte-sized parameters, retrieve directly into buf */
- if (config_item->size >= 8) {
- size = config_item->size / 8 * count;
+ if (size >= 8) {
+ size = size / 8 * count;
err = scarlett2_usb_get(mixer, config_item->offset, buf, size);
if (err < 0)
return err;
@@ -1684,8 +1692,9 @@ static int scarlett2_usb_set_config(
int config_item_num, int index, int value)
{
struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_config_set *config_set = private->config_set;
const struct scarlett2_config *config_item =
- &private->config_set->items[config_item_num];
+ &config_set->items[config_item_num];
int offset, size;
int err;
@@ -1695,6 +1704,36 @@ static int scarlett2_usb_set_config(
if (!config_item->offset)
return -EFAULT;
+ /* Gen 4 style writes are selected with size = 0;
+ * these are only byte-sized values written through a shared
+ * location, different to the read address
+ */
+ if (!config_item->size) {
+ if (!config_set->gen4_write_addr)
+ return -EFAULT;
+
+ /* Place index in gen4_write_addr + 1 */
+ err = scarlett2_usb_set_data(
+ mixer, config_set->gen4_write_addr + 1, 1, index);
+ if (err < 0)
+ return err;
+
+ /* Place value in gen4_write_addr */
+ err = scarlett2_usb_set_data(
+ mixer, config_set->gen4_write_addr, 1, value);
+ if (err < 0)
+ return err;
+
+ /* Request the interface do the write */
+ return scarlett2_usb_activate_config(
+ mixer, config_item->activate);
+ }
+
+ /* Not-Gen 4 style needs NVRAM save, supports
+ * bit-modification, and writing is done to the same place
+ * that the value can be read from
+ */
+
/* Cancel any pending NVRAM save */
cancel_delayed_work_sync(&private->work);
@@ -1736,6 +1775,10 @@ static int scarlett2_usb_set_config(
if (err < 0)
return err;
+ /* Gen 2 style writes to Gen 4 devices don't need saving */
+ if (config_set->gen4_write_addr)
+ return 0;
+
/* Schedule the change to be written to NVRAM */
if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE)
schedule_delayed_work(&private->work, msecs_to_jiffies(2000));
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 05/20] ALSA: scarlett2: Allow for controls with a "mute mode"
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (3 preceding siblings ...)
2023-12-26 18:06 ` [PATCH 04/20] ALSA: scarlett2: Add support for Gen 4 style parameters Geoffrey D. Bennett
@ 2023-12-26 18:06 ` Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 06/20] ALSA: scarlett2: Add support for Air Presence + Drive option Geoffrey D. Bennett
` (15 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Gen 2/3 interfaces would only use 0/1 values for input level and
phantom power switch controls. Gen 4 interfaces use the second bit to
indicate that the state should be changed (or is changing), and the
input is to be muted (or is muted) while that happens.
Add a "mute" flag to config items to enable this behaviour for the
level/phantom controls.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 09cd09799588..3098d9d38e3f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -325,11 +325,18 @@ enum {
* A size of 0 indicates that the parameter is a byte-sized Scarlett
* Gen 4 configuration which is written through the gen4_write_addr
* location (but still read through the given offset location).
+ *
+ * Some Gen 4 configuration parameters are written with 0x02 for a
+ * desired value of 0x01, and 0x03 for 0x00. These are indicated with
+ * mute set to 1. 0x02 and 0x03 are temporary values while the device
+ * makes the change and the channel and/or corresponding DSP channel
+ * output is muted.
*/
struct scarlett2_config {
u16 offset;
u8 size;
u8 activate;
+ u8 mute;
};
struct scarlett2_config_set {
@@ -2177,6 +2184,15 @@ static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer,
return 0;
}
+/* For config items with mute=1, xor bits 0 & 1 together to get the
+ * current/next state. This won't have any effect on values which are
+ * only ever 0/1.
+ */
+static uint8_t scarlett2_decode_muteable(uint8_t v)
+{
+ return (v ^ (v >> 1)) & 1;
+}
+
/*** Control Functions ***/
/* helper function to create a new control */
@@ -2798,7 +2814,8 @@ static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
if (err < 0)
goto unlock;
}
- ucontrol->value.enumerated.item[0] = private->level_switch[index];
+ ucontrol->value.enumerated.item[0] = scarlett2_decode_muteable(
+ private->level_switch[index]);
unlock:
mutex_unlock(&private->data_mutex);
@@ -2831,6 +2848,10 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
private->level_switch[index] = val;
+ /* To set the Gen 4 muteable controls, bit 1 gets set instead */
+ if (private->config_set->items[SCARLETT2_CONFIG_LEVEL_SWITCH].mute)
+ val = (!val) | 0x02;
+
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH,
index, val);
@@ -3078,8 +3099,8 @@ static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl,
if (err < 0)
goto unlock;
}
- ucontrol->value.integer.value[0] =
- private->phantom_switch[elem->control];
+ ucontrol->value.integer.value[0] = scarlett2_decode_muteable(
+ private->phantom_switch[elem->control]);
unlock:
mutex_unlock(&private->data_mutex);
@@ -3112,6 +3133,10 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
private->phantom_switch[index] = val;
+ /* To set the Gen 4 muteable controls, bit 1 gets set */
+ if (private->config_set->items[SCARLETT2_CONFIG_PHANTOM_SWITCH].mute)
+ val = (!val) | 0x02;
+
/* Send switch change to the device */
err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH,
index + info->phantom_first, val);
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 06/20] ALSA: scarlett2: Add support for Air Presence + Drive option
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (4 preceding siblings ...)
2023-12-26 18:06 ` [PATCH 05/20] ALSA: scarlett2: Allow for controls with a "mute mode" Geoffrey D. Bennett
@ 2023-12-26 18:06 ` Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 07/20] ALSA: scarlett2: Add support for software-controllable input gain Geoffrey D. Bennett
` (14 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:06 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Extend the existing "air" option support from Scarlett Gen 3, which
had two states (off/on), to accommodate Scarlett Gen 4's new state:
Presence + Drive.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 46 +++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 3098d9d38e3f..416973b8a6c4 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -647,6 +647,12 @@ struct scarlett2_device_info {
/* the first input with an air control (0-based) */
u8 air_input_first;
+ /* number of additional air options
+ * 0 for air presence only (Gen 3)
+ * 1 for air presence+drive (Gen 4)
+ */
+ u8 air_option;
+
/* the number of phantom (48V) software switchable controls */
u8 phantom_count;
@@ -3022,7 +3028,7 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
}
oval = private->air_switch[index];
- val = !!ucontrol->value.integer.value[0];
+ val = ucontrol->value.integer.value[0];
if (oval == val)
goto unlock;
@@ -3040,12 +3046,31 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
return err;
}
-static const struct snd_kcontrol_new scarlett2_air_ctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "",
- .info = snd_ctl_boolean_mono_info,
- .get = scarlett2_air_ctl_get,
- .put = scarlett2_air_ctl_put,
+static int scarlett2_air_with_drive_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[3] = {
+ "Off", "Presence", "Presence + Drive"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 3, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_air_ctl_get,
+ .put = scarlett2_air_ctl_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_air_with_drive_ctl_info,
+ .get = scarlett2_air_ctl_get,
+ .put = scarlett2_air_ctl_put,
+ }
};
/*** Phantom Switch Controls ***/
@@ -3839,9 +3864,10 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/* Add input air controls */
for (i = 0; i < info->air_input_count; i++) {
snprintf(s, sizeof(s), fmt, i + 1 + info->air_input_first,
- "Air", "Switch");
- err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl,
- i, 1, s, &private->air_ctls[i]);
+ "Air", info->air_option ? "Enum" : "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_air_ctl[info->air_option],
+ i, 1, s, &private->air_ctls[i]);
if (err < 0)
return err;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 07/20] ALSA: scarlett2: Add support for software-controllable input gain
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (5 preceding siblings ...)
2023-12-26 18:06 ` [PATCH 06/20] ALSA: scarlett2: Add support for Air Presence + Drive option Geoffrey D. Bennett
@ 2023-12-26 18:07 ` Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 08/20] ALSA: scarlett2: Minor refactor MSD mode check Geoffrey D. Bennett
` (13 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:07 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Some devices in the Scarlett Gen 4 series have support for
software-controllable input gain. Along with this comes a channel
select option, an auto-gain feature, "safe" mode, and linking two
channels into a stereo pair.
Mark the new scarlett2_notify_input_*() functions with __always_unused
until they get used when the Gen 4 notification callback function
arrays are added.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 796 +++++++++++++++++++++++++++++++++++-
1 file changed, 795 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 416973b8a6c4..a0212bda2b1a 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -164,6 +164,7 @@
/* some gui mixers can't handle negative ctl values */
#define SCARLETT2_VOLUME_BIAS 127
+#define SCARLETT2_GAIN_BIAS 70
/* mixer range from -80dB to +6dB in 0.5dB steps */
#define SCARLETT2_MIXER_MIN_DB -80
@@ -196,11 +197,12 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of analogue outputs */
#define SCARLETT2_ANALOGUE_MAX 10
-/* Maximum number of level and pad switches */
+/* Maximum number of various input controls */
#define SCARLETT2_LEVEL_SWITCH_MAX 2
#define SCARLETT2_PAD_SWITCH_MAX 8
#define SCARLETT2_AIR_SWITCH_MAX 8
#define SCARLETT2_PHANTOM_SWITCH_MAX 2
+#define SCARLETT2_INPUT_GAIN_MAX 2
/* Maximum number of inputs to the mixer */
#define SCARLETT2_INPUT_MIX_MAX 25
@@ -266,6 +268,16 @@ static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
"Mute Playback Switch", "Dim Playback Switch"
};
+/* Autogain Status Values */
+enum {
+ SCARLETT2_AUTOGAIN_STATUS_STOPPED,
+ SCARLETT2_AUTOGAIN_STATUS_RUNNING,
+ SCARLETT2_AUTOGAIN_STATUS_FAILED,
+ SCARLETT2_AUTOGAIN_STATUS_CANCELLED,
+ SCARLETT2_AUTOGAIN_STATUS_UNKNOWN,
+ SCARLETT2_AUTOGAIN_STATUS_COUNT
+};
+
/* Notification callback functions */
struct scarlett2_notification {
u32 mask;
@@ -316,6 +328,12 @@ enum {
SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH,
SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE,
SCARLETT2_CONFIG_TALKBACK_MAP,
+ SCARLETT2_CONFIG_AUTOGAIN_SWITCH,
+ SCARLETT2_CONFIG_AUTOGAIN_STATUS,
+ SCARLETT2_CONFIG_INPUT_GAIN,
+ SCARLETT2_CONFIG_SAFE_SWITCH,
+ SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
SCARLETT2_CONFIG_COUNT
};
@@ -662,6 +680,9 @@ struct scarlett2_device_info {
/* the number of inputs each phantom switch controls */
u8 inputs_per_phantom;
+ /* the number of inputs with software-controllable gain */
+ u8 gain_input_count;
+
/* the number of direct monitor options
* (0 = none, 1 = mono only, 2 = mono/stereo)
*/
@@ -722,6 +743,10 @@ struct scarlett2_data {
u8 input_pad_updated;
u8 input_air_updated;
u8 input_phantom_updated;
+ u8 input_select_updated;
+ u8 input_gain_updated;
+ u8 autogain_updated;
+ u8 input_safe_updated;
u8 monitor_other_updated;
u8 direct_monitor_updated;
u8 mux_updated;
@@ -737,6 +762,12 @@ struct scarlett2_data {
u8 air_switch[SCARLETT2_AIR_SWITCH_MAX];
u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX];
u8 phantom_persistence;
+ u8 input_select_switch;
+ u8 input_link_switch[SCARLETT2_INPUT_GAIN_MAX / 2];
+ u8 gain[SCARLETT2_INPUT_GAIN_MAX];
+ u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX];
+ u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+ u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX];
u8 direct_monitor_switch;
u8 speaker_switching_switch;
u8 talkback_switch;
@@ -754,6 +785,12 @@ struct scarlett2_data {
struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX];
struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX];
struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX];
+ struct snd_kcontrol *input_select_ctl;
+ struct snd_kcontrol *input_link_ctls[SCARLETT2_INPUT_GAIN_MAX / 2];
+ struct snd_kcontrol *input_gain_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
struct snd_kcontrol *direct_monitor_ctl;
struct snd_kcontrol *speaker_switching_ctl;
@@ -2352,6 +2389,610 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer)
0, 1, "Sync Status", &private->sync_ctl);
}
+/*** Autogain Switch and Status Controls ***/
+
+static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int err, i;
+ u8 raw_autogain_status[SCARLETT2_INPUT_GAIN_MAX];
+
+ private->autogain_updated = 0;
+
+ if (!info->gain_input_count)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH,
+ info->gain_input_count, private->autogain_switch);
+ if (err < 0)
+ return err;
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_AUTOGAIN_STATUS,
+ info->gain_input_count, raw_autogain_status);
+ if (err < 0)
+ return err;
+
+ /* Translate autogain_switch and raw_autogain_status into
+ * autogain_status
+ */
+ for (i = 0; i < info->gain_input_count; i++)
+ if (private->autogain_switch[i])
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_RUNNING;
+ else if (raw_autogain_status[i] == 0)
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_STOPPED;
+ else if (raw_autogain_status[i] >= 2 &&
+ raw_autogain_status[i] <= 5)
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_FAILED;
+ else if (raw_autogain_status[i] == 6)
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_CANCELLED;
+ else
+ private->autogain_status[i] =
+ SCARLETT2_AUTOGAIN_STATUS_UNKNOWN;
+
+ return 0;
+}
+
+static int scarlett2_autogain_switch_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->autogain_updated) {
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] =
+ private->autogain_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_status_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->autogain_updated) {
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] =
+ private->autogain_status[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_switch_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->autogain_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->autogain_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_AUTOGAIN_SWITCH, index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_autogain_status_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[SCARLETT2_AUTOGAIN_STATUS_COUNT] = {
+ "Stopped", "Running", "Failed", "Cancelled", "Unknown"
+ };
+
+ return snd_ctl_enum_info(
+ uinfo, 1, SCARLETT2_AUTOGAIN_STATUS_COUNT, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_autogain_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_autogain_switch_ctl_get,
+ .put = scarlett2_autogain_switch_ctl_put
+};
+
+static const struct snd_kcontrol_new scarlett2_autogain_status_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .name = "",
+ .info = scarlett2_autogain_status_ctl_info,
+ .get = scarlett2_autogain_status_ctl_get,
+};
+
+/*** Input Select Control ***/
+
+static int scarlett2_update_input_select(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int link_count = info->gain_input_count / 2;
+ int err;
+
+ private->input_select_updated = 0;
+
+ if (!link_count)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ 1, &private->input_select_switch);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
+ link_count, private->input_link_switch);
+ if (err < 0)
+ return err;
+
+ /* simplified because no model yet has link_count > 1 */
+ if (private->input_link_switch[0])
+ private->input_select_switch = 0;
+
+ return 0;
+}
+
+static int scarlett2_input_select_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_select_updated) {
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] = private->input_select_switch;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_select_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;
+ int max_val = private->input_link_switch[0] ? 0 : 1;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->input_select_switch;
+ val = ucontrol->value.integer.value[0];
+
+ if (val < 0)
+ val = 0;
+ else if (val > max_val)
+ val = max_val;
+
+ if (oval == val)
+ goto unlock;
+
+ private->input_select_switch = val;
+
+ /* Send switch change to the device if inputs not linked */
+ if (!private->input_link_switch[0])
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
+ 1, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_select_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 inputs = private->info->gain_input_count;
+ int i, j;
+ int err;
+ char **values = kcalloc(inputs, sizeof(char *), GFP_KERNEL);
+
+ if (!values)
+ return -ENOMEM;
+
+ mutex_lock(&private->data_mutex);
+
+ /* Loop through each input
+ * Linked inputs have one value for the pair
+ */
+ for (i = 0, j = 0; i < inputs; i++) {
+ if (private->input_link_switch[i / 2]) {
+ values[j++] = kasprintf(
+ GFP_KERNEL, "Input %d-%d", i + 1, i + 2);
+ i++;
+ } else {
+ values[j++] = kasprintf(
+ GFP_KERNEL, "Input %d", i + 1);
+ }
+ }
+
+ err = snd_ctl_enum_info(uinfo, 1, j,
+ (const char * const *)values);
+
+ mutex_unlock(&private->data_mutex);
+
+ for (i = 0; i < inputs; i++)
+ kfree(values[i]);
+ kfree(values);
+
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_input_select_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_input_select_ctl_info,
+ .get = scarlett2_input_select_ctl_get,
+ .put = scarlett2_input_select_ctl_put,
+};
+
+/*** Input Link Switch Controls ***/
+
+static int scarlett2_input_link_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_select_updated) {
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] =
+ private->input_link_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_link_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->input_link_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->input_link_switch[index] = val;
+
+ /* Notify of change in input select options available */
+ snd_ctl_notify(mixer->chip->card,
+ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_select_ctl->id);
+ private->input_select_updated = 1;
+
+ /* Send switch change to the device
+ * Link for channels 1-2 is at index 1
+ * No device yet has more than 2 channels linked
+ */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_INPUT_LINK_SWITCH, index + 1, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_input_link_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_input_link_ctl_get,
+ .put = scarlett2_input_link_ctl_put
+};
+
+/*** Input Gain Controls ***/
+
+static int scarlett2_update_input_gain(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_gain_updated = 0;
+
+ if (!info->gain_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_INPUT_GAIN,
+ info->gain_input_count, private->gain);
+}
+
+static int scarlett2_input_gain_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct usb_mixer_elem_info *elem = kctl->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = elem->channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SCARLETT2_GAIN_BIAS;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int scarlett2_input_gain_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_gain_updated) {
+ err = scarlett2_update_input_gain(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] =
+ private->gain[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_input_gain_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->gain[index];
+ val = ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->gain[index] = val;
+
+ /* Send gain change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_INPUT_GAIN,
+ index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_MINMAX(
+ db_scale_scarlett2_gain, -SCARLETT2_GAIN_BIAS * 100, 0
+);
+
+static const struct snd_kcontrol_new scarlett2_input_gain_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_input_gain_ctl_info,
+ .get = scarlett2_input_gain_ctl_get,
+ .put = scarlett2_input_gain_ctl_put,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett2_gain }
+};
+
+/*** Safe Controls ***/
+
+static int scarlett2_update_input_safe(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+
+ private->input_safe_updated = 0;
+
+ if (!info->gain_input_count)
+ return 0;
+
+ return scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
+ info->gain_input_count, private->safe_switch);
+}
+
+static int scarlett2_safe_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->input_safe_updated) {
+ err = scarlett2_update_input_safe(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] =
+ private->safe_switch[elem->control];
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_safe_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->safe_switch[index];
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->safe_switch[index] = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SAFE_SWITCH,
+ index, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_safe_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = snd_ctl_boolean_mono_info,
+ .get = scarlett2_safe_ctl_get,
+ .put = scarlett2_safe_ctl_put,
+};
+
/*** Analogue Line Out Volume Controls ***/
/* Update hardware volume controls after receiving notification that
@@ -3908,6 +4549,60 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
return err;
}
+ /* Add software-controllable input gain controls */
+ if (info->gain_input_count) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_select_ctl, 0, 1,
+ "Input Select Capture Enum",
+ &private->input_select_ctl);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < info->gain_input_count; i++) {
+ if (i % 2) {
+ snprintf(s, sizeof(s),
+ "Line In %d-%d Link Capture Switch",
+ i, i + 1);
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_link_ctl,
+ i / 2, 1, s,
+ &private->input_link_ctls[i / 2]);
+ if (err < 0)
+ return err;
+ }
+
+ snprintf(s, sizeof(s), fmt, i + 1,
+ "Gain", "Volume");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_input_gain_ctl,
+ i, 1, s, &private->input_gain_ctls[i]);
+ if (err < 0)
+ return err;
+
+ snprintf(s, sizeof(s), fmt, i + 1,
+ "Autogain", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_autogain_switch_ctl,
+ i, 1, s, &private->autogain_ctls[i]);
+ if (err < 0)
+ return err;
+
+ snprintf(s, sizeof(s), fmt, i + 1,
+ "Autogain Status", "Enum");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_autogain_status_ctl,
+ i, 1, s, &private->autogain_status_ctls[i]);
+
+ snprintf(s, sizeof(s), fmt, i + 1,
+ "Safe", "Switch");
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_safe_ctl,
+ i, 1, s, &private->safe_ctls[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+
return 0;
}
@@ -4838,6 +5533,22 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
if (err < 0)
return err;
+ err = scarlett2_update_input_select(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_gain(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_update_input_safe(mixer);
+ if (err < 0)
+ return err;
+
for (i = 0; i < private->num_mix_out; i++) {
err = scarlett2_usb_get_mix(mixer, i);
if (err < 0)
@@ -4970,6 +5681,89 @@ static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer)
scarlett2_notify_input_phantom(mixer);
}
+/* Notify on input select change */
+static __always_unused void scarlett2_notify_input_select(
+ 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;
+
+ if (!info->gain_input_count)
+ return;
+
+ private->input_select_updated = 1;
+
+ snd_ctl_notify(card,
+ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_select_ctl->id);
+
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->input_link_ctls[i]->id);
+}
+
+/* Notify on input gain change */
+static __always_unused void scarlett2_notify_input_gain(
+ 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;
+
+ if (!info->gain_input_count)
+ return;
+
+ private->input_gain_updated = 1;
+
+ for (i = 0; i < info->gain_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->input_gain_ctls[i]->id);
+}
+
+/* Notify on autogain change */
+static __always_unused void scarlett2_notify_autogain(
+ 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;
+
+ if (!info->gain_input_count)
+ return;
+
+ private->autogain_updated = 1;
+
+ for (i = 0; i < info->gain_input_count; i++) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->autogain_ctls[i]->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->autogain_status_ctls[i]->id);
+ }
+}
+
+/* Notify on input safe switch change */
+static __always_unused void scarlett2_notify_input_safe(
+ 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;
+
+ if (!info->gain_input_count)
+ return;
+
+ private->input_safe_updated = 1;
+
+ for (i = 0; i < info->gain_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->safe_ctls[i]->id);
+}
+
/* Notify on "monitor other" change (speaker switching, talkback) */
static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 08/20] ALSA: scarlett2: Minor refactor MSD mode check
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (6 preceding siblings ...)
2023-12-26 18:07 ` [PATCH 07/20] ALSA: scarlett2: Add support for software-controllable input gain Geoffrey D. Bennett
@ 2023-12-26 18:07 ` Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 09/20] ALSA: scarlett2: Disable input controls while autogain is running Geoffrey D. Bennett
` (12 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:07 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Create local variable for storing private data pointer in
snd_scarlett2_controls_create(). It's currently only used for checking
msd_switch, but it will be used again.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index a0212bda2b1a..ce2fb742dd97 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -5898,6 +5898,7 @@ static int snd_scarlett2_controls_create(
struct usb_mixer_interface *mixer,
const struct scarlett2_device_entry *entry)
{
+ struct scarlett2_data *private;
int err;
/* Initialise private data */
@@ -5905,6 +5906,8 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
+ private = mixer->private_data;
+
/* Send proprietary USB initialisation sequence */
err = scarlett2_usb_init(mixer);
if (err < 0)
@@ -5931,7 +5934,7 @@ static int snd_scarlett2_controls_create(
return err;
/* If MSD mode is enabled, don't create any other controls */
- if (((struct scarlett2_data *)mixer->private_data)->msd_switch)
+ if (private->msd_switch)
return 0;
/* Create the analogue output controls */
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 09/20] ALSA: scarlett2: Disable input controls while autogain is running
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (7 preceding siblings ...)
2023-12-26 18:07 ` [PATCH 08/20] ALSA: scarlett2: Minor refactor MSD mode check Geoffrey D. Bennett
@ 2023-12-26 18:07 ` Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 10/20] ALSA: scarlett2: Disable autogain during phantom power state change Geoffrey D. Bennett
` (11 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:07 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
While the autogain function is running, the other input controls
(select, link, gain, safe, level, air, and phantom) can't be modified.
Update those controls to be read-only during this time.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 298 +++++++++++++++++++++++++++++++++---
1 file changed, 273 insertions(+), 25 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index ce2fb742dd97..6c8398aa103f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -2391,6 +2391,30 @@ static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer)
/*** Autogain Switch and Status Controls ***/
+/* Set the access mode of a control to read-only (val = 0) or
+ * read-write (val = 1).
+ */
+static void scarlett2_set_ctl_access(struct snd_kcontrol *kctl, int val)
+{
+ if (val)
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+}
+
+/* Check if autogain is running on any input */
+static int scarlett2_autogain_is_running(struct scarlett2_data *private)
+{
+ int i;
+
+ for (i = 0; i < private->info->gain_input_count; i++)
+ if (private->autogain_status[i] ==
+ SCARLETT2_AUTOGAIN_STATUS_RUNNING)
+ return 1;
+
+ return 0;
+}
+
static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
@@ -2438,13 +2462,108 @@ static int scarlett2_update_autogain(struct usb_mixer_interface *mixer)
return 0;
}
+/* Update access mode for controls affected by autogain */
+static void scarlett2_autogain_update_access(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
+ int val = !scarlett2_autogain_is_running(private);
+ int i;
+
+ scarlett2_set_ctl_access(private->input_select_ctl, val);
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ scarlett2_set_ctl_access(private->input_link_ctls[i], val);
+ for (i = 0; i < info->gain_input_count; i++) {
+ scarlett2_set_ctl_access(private->input_gain_ctls[i], val);
+ scarlett2_set_ctl_access(private->safe_ctls[i], val);
+ }
+ for (i = 0; i < info->level_input_count; i++)
+ scarlett2_set_ctl_access(private->level_ctls[i], val);
+ for (i = 0; i < info->air_input_count; i++)
+ scarlett2_set_ctl_access(private->air_ctls[i], val);
+ for (i = 0; i < info->phantom_count; i++)
+ scarlett2_set_ctl_access(private->phantom_ctls[i], val);
+}
+
+/* Notify of access mode change for all controls read-only while
+ * autogain runs.
+ */
+static void scarlett2_autogain_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;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_select_ctl->id);
+ for (i = 0; i < info->gain_input_count / 2; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_link_ctls[i]->id);
+ for (i = 0; i < info->gain_input_count; i++) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->input_gain_ctls[i]->id);
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->safe_ctls[i]->id);
+ }
+ for (i = 0; i < info->level_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->level_ctls[i]->id);
+ for (i = 0; i < info->air_input_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->air_ctls[i]->id);
+ for (i = 0; i < info->phantom_count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO,
+ &private->phantom_ctls[i]->id);
+}
+
+/* Call scarlett2_update_autogain() and
+ * scarlett2_autogain_update_access() if autogain_updated is set.
+ */
+static int scarlett2_check_autogain_updated(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ if (!private->autogain_updated)
+ return 0;
+
+ err = scarlett2_update_autogain(mixer);
+ if (err < 0)
+ return err;
+
+ scarlett2_autogain_update_access(mixer);
+
+ return 0;
+}
+
+/* If autogain_updated is set when a *_ctl_put() function for a
+ * control that is meant to be read-only while autogain is running,
+ * update the autogain status and access mode of affected controls.
+ * Return -EPERM if autogain is running.
+ */
+static int scarlett2_check_put_during_autogain(
+ struct usb_mixer_interface *mixer)
+{
+ int err = scarlett2_check_autogain_updated(mixer);
+
+ if (err < 0)
+ return err;
+
+ if (scarlett2_autogain_is_running(mixer->private_data))
+ return -EPERM;
+
+ return 0;
+}
+
static int scarlett2_autogain_switch_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);
@@ -2453,11 +2572,10 @@ static int scarlett2_autogain_switch_ctl_get(
goto unlock;
}
- if (private->autogain_updated) {
- err = scarlett2_update_autogain(mixer);
- if (err < 0)
- goto unlock;
- }
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
ucontrol->value.enumerated.item[0] =
private->autogain_switch[elem->control];
@@ -2472,7 +2590,7 @@ static int scarlett2_autogain_status_ctl_get(
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);
@@ -2481,11 +2599,10 @@ static int scarlett2_autogain_status_ctl_get(
goto unlock;
}
- if (private->autogain_updated) {
- err = scarlett2_update_autogain(mixer);
- if (err < 0)
- goto unlock;
- }
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
ucontrol->value.enumerated.item[0] =
private->autogain_status[elem->control];
@@ -2525,6 +2642,9 @@ static int scarlett2_autogain_switch_ctl_put(
if (err == 0)
err = 1;
+ scarlett2_autogain_update_access(mixer);
+ scarlett2_autogain_notify_access(mixer);
+
unlock:
mutex_unlock(&private->data_mutex);
return err;
@@ -2624,7 +2744,7 @@ static int scarlett2_input_select_ctl_put(
struct usb_mixer_interface *mixer = elem->head.mixer;
struct scarlett2_data *private = mixer->private_data;
- int oval, val, err = 0;
+ int oval, val, err;
int max_val = private->input_link_switch[0] ? 0 : 1;
mutex_lock(&private->data_mutex);
@@ -2634,6 +2754,10 @@ static int scarlett2_input_select_ctl_put(
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->input_select_switch;
val = ucontrol->value.integer.value[0];
@@ -2677,6 +2801,15 @@ static int scarlett2_input_select_ctl_info(
mutex_lock(&private->data_mutex);
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
/* Loop through each input
* Linked inputs have one value for the pair
*/
@@ -2694,6 +2827,7 @@ static int scarlett2_input_select_ctl_info(
err = snd_ctl_enum_info(uinfo, 1, j,
(const char * const *)values);
+unlock:
mutex_unlock(&private->data_mutex);
for (i = 0; i < inputs; i++)
@@ -2713,6 +2847,35 @@ static const struct snd_kcontrol_new scarlett2_input_select_ctl = {
/*** Input Link Switch Controls ***/
+/* snd_ctl_boolean_mono_info() with autogain-updated check
+ * (for controls that are read-only while autogain is running)
+ */
+static int scarlett2_autogain_disables_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_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_input_link_ctl_get(
struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
{
@@ -2749,7 +2912,7 @@ static int scarlett2_input_link_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);
@@ -2758,6 +2921,10 @@ static int scarlett2_input_link_ctl_put(
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->input_link_switch[index];
val = !!ucontrol->value.integer.value[0];
@@ -2789,7 +2956,7 @@ static int scarlett2_input_link_ctl_put(
static const struct snd_kcontrol_new scarlett2_input_link_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
- .info = snd_ctl_boolean_mono_info,
+ .info = scarlett2_autogain_disables_ctl_info,
.get = scarlett2_input_link_ctl_get,
.put = scarlett2_input_link_ctl_put
};
@@ -2815,13 +2982,30 @@ static int scarlett2_input_gain_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = elem->channels;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = SCARLETT2_GAIN_BIAS;
uinfo->value.integer.step = 1;
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_input_gain_ctl_get(struct snd_kcontrol *kctl,
@@ -2860,7 +3044,7 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl,
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);
@@ -2869,6 +3053,10 @@ static int scarlett2_input_gain_ctl_put(struct snd_kcontrol *kctl,
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->gain[index];
val = ucontrol->value.integer.value[0];
@@ -2957,7 +3145,7 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl,
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);
@@ -2966,6 +3154,10 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl,
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->safe_switch[index];
val = !!ucontrol->value.integer.value[0];
@@ -2988,7 +3180,7 @@ static int scarlett2_safe_ctl_put(struct snd_kcontrol *kctl,
static const struct snd_kcontrol_new scarlett2_safe_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
- .info = snd_ctl_boolean_mono_info,
+ .info = scarlett2_autogain_disables_ctl_info,
.get = scarlett2_safe_ctl_get,
.put = scarlett2_safe_ctl_put,
};
@@ -3434,8 +3626,27 @@ static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl,
static const char *const values[2] = {
"Line", "Inst"
};
+ 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;
- return snd_ctl_enum_info(uinfo, 1, 2, values);
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ err = snd_ctl_enum_info(uinfo, 1, 2, values);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl,
@@ -3478,7 +3689,7 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
const struct scarlett2_device_info *info = private->info;
int index = elem->control + info->level_input_first;
- int oval, val, err = 0;
+ int oval, val, err;
mutex_lock(&private->data_mutex);
@@ -3487,6 +3698,10 @@ static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl,
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->level_switch[index];
val = !!ucontrol->value.enumerated.item[0];
@@ -3659,7 +3874,7 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
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);
@@ -3668,6 +3883,10 @@ static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl,
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->air_switch[index];
val = ucontrol->value.integer.value[0];
@@ -3693,8 +3912,27 @@ static int scarlett2_air_with_drive_ctl_info(
static const char *const values[3] = {
"Off", "Presence", "Presence + Drive"
};
+ 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;
- return snd_ctl_enum_info(uinfo, 1, 3, values);
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ err = scarlett2_check_autogain_updated(mixer);
+ if (err < 0)
+ goto unlock;
+
+ err = snd_ctl_enum_info(uinfo, 1, 3, values);
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static const struct snd_kcontrol_new scarlett2_air_ctl[2] = {
@@ -3782,7 +4020,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
const struct scarlett2_device_info *info = private->info;
int index = elem->control;
- int oval, val, err = 0;
+ int oval, val, err;
mutex_lock(&private->data_mutex);
@@ -3791,6 +4029,10 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
goto unlock;
}
+ err = scarlett2_check_put_during_autogain(mixer);
+ if (err < 0)
+ goto unlock;
+
oval = private->phantom_switch[index];
val = !!ucontrol->value.integer.value[0];
@@ -3817,7 +4059,7 @@ static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl,
static const struct snd_kcontrol_new scarlett2_phantom_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
- .info = snd_ctl_boolean_mono_info,
+ .info = scarlett2_autogain_disables_ctl_info,
.get = scarlett2_phantom_ctl_get,
.put = scarlett2_phantom_ctl_put,
};
@@ -5743,6 +5985,8 @@ static __always_unused void scarlett2_notify_autogain(
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->autogain_status_ctls[i]->id);
}
+
+ scarlett2_autogain_notify_access(mixer);
}
/* Notify on input safe switch change */
@@ -5987,6 +6231,10 @@ 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)
+ scarlett2_autogain_update_access(mixer);
+
/* Set up the interrupt polling */
err = scarlett2_init_notify(mixer);
if (err < 0)
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 10/20] ALSA: scarlett2: Disable autogain during phantom power state change
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (8 preceding siblings ...)
2023-12-26 18:07 ` [PATCH 09/20] ALSA: scarlett2: Disable input controls while autogain is running Geoffrey D. Bennett
@ 2023-12-26 18:07 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 11/20] ALSA: scarlett2: Add power status control Geoffrey D. Bennett
` (10 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:07 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
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 --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);
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 11/20] ALSA: scarlett2: Add power status control
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (9 preceding siblings ...)
2023-12-26 18:07 ` [PATCH 10/20] ALSA: scarlett2: Disable autogain during phantom power state change Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 12/20] ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor Geoffrey D. Bennett
` (9 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Add a control to retrieve the power status from the interface
(bus-powered, external-powered, or insufficient power).
Mark the new scarlett2_notify_power_status() function with
__always_unused until it gets used when the Gen 4 notification
callback function arrays are added.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 123 ++++++++++++++++++++++++++++++++++++
1 file changed, 123 insertions(+)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 2d6fd9faed31..f5f57c86cec8 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -278,6 +278,14 @@ enum {
SCARLETT2_AUTOGAIN_STATUS_COUNT
};
+/* Power Status Values */
+enum {
+ SCARLETT2_POWER_STATUS_EXT,
+ SCARLETT2_POWER_STATUS_BUS,
+ SCARLETT2_POWER_STATUS_FAIL,
+ SCARLETT2_POWER_STATUS_COUNT
+};
+
/* Notification callback functions */
struct scarlett2_notification {
u32 mask;
@@ -334,6 +342,8 @@ enum {
SCARLETT2_CONFIG_SAFE_SWITCH,
SCARLETT2_CONFIG_INPUT_SELECT_SWITCH,
SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
+ SCARLETT2_CONFIG_POWER_EXT,
+ SCARLETT2_CONFIG_POWER_STATUS,
SCARLETT2_CONFIG_COUNT
};
@@ -751,6 +761,7 @@ struct scarlett2_data {
u8 direct_monitor_updated;
u8 mux_updated;
u8 speaker_switching_switched;
+ u8 power_status_updated;
u8 sync;
u8 master_vol;
u8 vol[SCARLETT2_ANALOGUE_MAX];
@@ -774,6 +785,7 @@ struct scarlett2_data {
u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX];
u8 msd_switch;
u8 standalone_switch;
+ u8 power_status;
u8 meter_level_map[SCARLETT2_MAX_METERS];
struct snd_kcontrol *sync_ctl;
struct snd_kcontrol *master_vol_ctl;
@@ -795,6 +807,7 @@ struct scarlett2_data {
struct snd_kcontrol *direct_monitor_ctl;
struct snd_kcontrol *speaker_switching_ctl;
struct snd_kcontrol *talkback_ctl;
+ struct snd_kcontrol *power_status_ctl;
u8 mux[SCARLETT2_MUX_MAX];
u8 mix[SCARLETT2_MIX_MAX];
};
@@ -5526,6 +5539,91 @@ static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer)
0, 1, "Standalone Switch", NULL);
}
+/*** Power Status ***/
+
+static int scarlett2_update_power_status(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+ u8 power_ext;
+ u8 power_status;
+
+ private->power_status_updated = 0;
+
+ err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_EXT,
+ 1, &power_ext);
+ if (err < 0)
+ return err;
+
+ err = scarlett2_usb_get_config(mixer, SCARLETT2_CONFIG_POWER_STATUS,
+ 1, &power_status);
+ if (err < 0)
+ return err;
+
+ if (power_status > 1)
+ private->power_status = SCARLETT2_POWER_STATUS_FAIL;
+ else if (power_ext)
+ private->power_status = SCARLETT2_POWER_STATUS_EXT;
+ else
+ private->power_status = SCARLETT2_POWER_STATUS_BUS;
+
+ return 0;
+}
+
+static int scarlett2_power_status_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->power_status_updated) {
+ err = scarlett2_update_power_status(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] = private->power_status;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_power_status_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[3] = {
+ "External", "Bus", "Fail"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 3, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_power_status_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .name = "",
+ .info = scarlett2_power_status_ctl_info,
+ .get = scarlett2_power_status_ctl_get,
+};
+
+static int scarlett2_add_power_status_ctl(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ if (!scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_POWER_EXT))
+ return 0;
+
+ /* Add power status control */
+ return scarlett2_add_new_ctl(mixer, &scarlett2_power_status_ctl,
+ 0, 1, "Power Status Card Enum",
+ &private->power_status_ctl);
+}
+
/*** Cleanup/Suspend Callbacks ***/
static void scarlett2_private_free(struct usb_mixer_interface *mixer)
@@ -5817,6 +5915,13 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
return err;
}
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_POWER_EXT)) {
+ err = scarlett2_update_power_status(mixer);
+ if (err < 0)
+ return err;
+ }
+
err = scarlett2_update_sync(mixer);
if (err < 0)
return err;
@@ -6153,6 +6258,19 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer)
&private->direct_monitor_ctl->id);
}
+/* Notify on power change */
+static __always_unused void scarlett2_notify_power_status(
+ struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->power_status_updated = 1;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->power_status_ctl->id);
+}
+
/* Interrupt callback */
static void scarlett2_notify(struct urb *urb)
{
@@ -6330,6 +6448,11 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
+ /* Create the power status control */
+ err = scarlett2_add_power_status_ctl(mixer);
+ if (err < 0)
+ return err;
+
/* Set the access mode of controls disabled during
* autogain/phantom power switching.
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 12/20] ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (10 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 11/20] ALSA: scarlett2: Add power status control Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 13/20] ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates Geoffrey D. Bennett
` (8 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
The Scarlett 4th Gen small interfaces have a software-controllable
mixer like the large 2nd and 3rd Gen interfaces do. Pressing the
"Direct" button on the interface updates the mixer controls, which
this driver hasn't needed to deal with previously.
This commit stores the ALSA mixer controls, and adds a mix_updated
flag so that the controls can be updated when a notification is
received.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 50 +++++++++++++++++++++++++++++++------
1 file changed, 42 insertions(+), 8 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index f5f57c86cec8..391ecacbc768 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -760,6 +760,7 @@ struct scarlett2_data {
u8 monitor_other_updated;
u8 direct_monitor_updated;
u8 mux_updated;
+ u8 mix_updated;
u8 speaker_switching_switched;
u8 power_status_updated;
u8 sync;
@@ -804,6 +805,7 @@ struct scarlett2_data {
struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
+ struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX];
struct snd_kcontrol *direct_monitor_ctl;
struct snd_kcontrol *speaker_switching_ctl;
struct snd_kcontrol *talkback_ctl;
@@ -4960,6 +4962,22 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
/*** Mixer Volume Controls ***/
+static int scarlett2_update_mix(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int i, err;
+
+ private->mix_updated = 0;
+
+ for (i = 0; i < private->num_mix_out; i++) {
+ err = scarlett2_usb_get_mix(mixer, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 1;
+}
+
static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
@@ -4977,10 +4995,27 @@ static int scarlett2_mixer_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;
+ struct usb_mixer_interface *mixer = elem->head.mixer;
+ struct scarlett2_data *private = mixer->private_data;
+ int err = 0;
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->mix_updated) {
+ err = scarlett2_update_mix(mixer);
+ if (err < 0)
+ goto unlock;
+ }
ucontrol->value.integer.value[0] = private->mix[elem->control];
- return 0;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
}
static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl,
@@ -5048,7 +5083,8 @@ static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer)
"Mix %c Input %02d Playback Volume",
'A' + i, j + 1);
err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl,
- index, 1, s, NULL);
+ index, 1, s,
+ &private->mix_ctls[index]);
if (err < 0)
return err;
}
@@ -5993,11 +6029,9 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
if (err < 0)
return err;
- for (i = 0; i < private->num_mix_out; i++) {
- err = scarlett2_usb_get_mix(mixer, i);
- if (err < 0)
- return err;
- }
+ err = scarlett2_update_mix(mixer);
+ if (err < 0)
+ return err;
return scarlett2_usb_get_mux(mixer);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 13/20] ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (11 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 12/20] ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 14/20] ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes Geoffrey D. Bennett
` (7 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
When the Direct Monitor feature on the Scarlett 4th Gen Solo and 2i2
interfaces is used, the Mix A and B gains are updated by the
interface. This patch calls snd_ctl_notify() for the ALSA mix controls
when a Direct Monitor notification is received.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 391ecacbc768..50899e81e00e 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -6285,11 +6285,23 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
+ int count = private->num_mix_in * private->num_mix_out;
+ int i;
private->direct_monitor_updated = 1;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
&private->direct_monitor_ctl->id);
+
+ if (!scarlett2_has_mixer(private))
+ return;
+
+ private->mix_updated = 1;
+
+ /* Notify of change to the mix controls */
+ for (i = 0; i < count; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->mix_ctls[i]->id);
}
/* Notify on power change */
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 14/20] ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (12 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 13/20] ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 15/20] ALSA: scarlett2: Add support for DSP mux channels Geoffrey D. Bennett
` (6 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
The mixes used by Direct Monitor feature on the Scarlett 4th Gen Solo
and 2i2 interfaces are configurable. This patch adds ALSA controls for
the gains which are copied to the mixer when Direct Monitor is
enabled.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 145 +++++++++++++++++++++++++++++++++++-
1 file changed, 141 insertions(+), 4 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 50899e81e00e..1c6a493eb2ee 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -213,6 +213,13 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
/* Maximum number of mixer gain controls */
#define SCARLETT2_MIX_MAX (SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX)
+/* Maximum number of direct monitor mixer gain controls
+ * 1 (Solo) or 2 (2i2) direct monitor selections (Mono & Stereo)
+ * 2 Mix outputs (A/Left & B/Right)
+ * 4 Mix inputs
+ */
+#define SCARLETT2_MONITOR_MIX_MAX (2 * 2 * 4)
+
/* Maximum size of the data in the USB mux assignment message:
* 20 inputs, 20 outputs, 25 matrix inputs, 12 spare
*/
@@ -344,6 +351,7 @@ enum {
SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
SCARLETT2_CONFIG_POWER_EXT,
SCARLETT2_CONFIG_POWER_STATUS,
+ SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
SCARLETT2_CONFIG_COUNT
};
@@ -742,6 +750,7 @@ struct scarlett2_data {
u8 num_mix_in;
u8 num_mix_out;
u8 num_line_out;
+ u8 num_monitor_mix_ctls;
u32 firmware_version;
u8 flash_segment_nums[SCARLETT2_SEGMENT_ID_COUNT];
u8 flash_segment_blocks[SCARLETT2_SEGMENT_ID_COUNT];
@@ -812,6 +821,7 @@ struct scarlett2_data {
struct snd_kcontrol *power_status_ctl;
u8 mux[SCARLETT2_MUX_MAX];
u8 mix[SCARLETT2_MIX_MAX];
+ u8 monitor_mix[SCARLETT2_MONITOR_MIX_MAX];
};
/*** Model-specific data ***/
@@ -5108,6 +5118,28 @@ static int scarlett2_update_direct_monitor(struct usb_mixer_interface *mixer)
1, &private->direct_monitor_switch);
}
+static int scarlett2_update_monitor_mix(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err, i;
+ u16 mix_values[SCARLETT2_MONITOR_MIX_MAX];
+
+ if (!private->num_monitor_mix_ctls)
+ return 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ private->num_monitor_mix_ctls, mix_values);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < private->num_monitor_mix_ctls; i++)
+ private->monitor_mix[i] = scarlett2_mixer_value_to_db(
+ mix_values[i]);
+
+ return 0;
+}
+
static int scarlett2_direct_monitor_ctl_get(
struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
{
@@ -5201,11 +5233,70 @@ static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = {
}
};
-static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer)
+static int scarlett2_monitor_mix_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->monitor_mix[elem->control];
+
+ return 0;
+}
+
+static int scarlett2_monitor_mix_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;
+ int index = elem->control;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->monitor_mix[index];
+ val = clamp(ucontrol->value.integer.value[0],
+ 0L, (long)SCARLETT2_MIXER_MAX_VALUE);
+
+ if (oval == val)
+ goto unlock;
+
+ private->monitor_mix[index] = val;
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
+ index, scarlett2_mixer_values[val]);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static const struct snd_kcontrol_new scarlett2_monitor_mix_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_mixer_ctl_info,
+ .get = scarlett2_monitor_mix_ctl_get,
+ .put = scarlett2_monitor_mix_ctl_put,
+ .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */
+ .tlv = { .p = db_scale_scarlett2_mixer }
+};
+
+static int scarlett2_add_direct_monitor_ctls(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
const struct scarlett2_device_info *info = private->info;
const char *s;
+ int err, i, j, k, index;
if (!info->direct_monitor)
return 0;
@@ -5214,9 +5305,47 @@ static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer)
? "Direct Monitor Playback Switch"
: "Direct Monitor Playback Enum";
- return scarlett2_add_new_ctl(
+ err = scarlett2_add_new_ctl(
mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1],
0, 1, s, &private->direct_monitor_ctl);
+ if (err < 0)
+ return err;
+
+ if (!private->num_monitor_mix_ctls)
+ return 0;
+
+ /* 1 or 2 direct monitor selections (Mono & Stereo) */
+ for (i = 0, index = 0; i < info->direct_monitor; i++) {
+ const char * const format =
+ "Monitor %sMix %c Input %02d Playback Volume";
+ const char *mix_type;
+
+ if (info->direct_monitor == 1)
+ mix_type = "";
+ else if (i == 0)
+ mix_type = "1 ";
+ else
+ mix_type = "2 ";
+
+ /* 2 Mix outputs, A/Left & B/Right */
+ for (j = 0; j < 2; j++)
+
+ /* Mix inputs */
+ for (k = 0; k < private->num_mix_in; k++, index++) {
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ snprintf(name, sizeof(name), format,
+ mix_type, 'A' + j, k + 1);
+
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_monitor_mix_ctl,
+ index, 1, name, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
}
/*** Mux Source Selection Controls ***/
@@ -5708,6 +5837,10 @@ static void scarlett2_count_io(struct scarlett2_data *private)
/* Number of analogue line outputs */
private->num_line_out =
port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+
+ /* Number of monitor mix controls */
+ private->num_monitor_mix_ctls =
+ info->direct_monitor * 2 * private->num_mix_in;
}
/* Look through the interface descriptors for the Focusrite Control
@@ -5938,6 +6071,10 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
if (!scarlett2_has_mixer(private))
return 0;
+ err = scarlett2_update_monitor_mix(mixer);
+ if (err < 0)
+ return err;
+
err = scarlett2_update_monitor_other(mixer);
if (err < 0)
return err;
@@ -6474,8 +6611,8 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
- /* Create the direct monitor control */
- err = scarlett2_add_direct_monitor_ctl(mixer);
+ /* Create the direct monitor control(s) */
+ err = scarlett2_add_direct_monitor_ctls(mixer);
if (err < 0)
return err;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 15/20] ALSA: scarlett2: Add support for DSP mux channels
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (13 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 14/20] ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 16/20] ALSA: scarlett2: Rename " Geoffrey D. Bennett
` (5 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
The DSP mux channels in the Scarlett 4th Gen appear as
SCARLETT2_PORT_TYPE_MIX ports but do not have corresponding mixer
controls. Add a dsp_count option to the device info struct to exclude
those DSP channels from the num_mix_in/num_mix_out counts.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 1c6a493eb2ee..5ac31fea9831 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -706,6 +706,9 @@ struct scarlett2_device_info {
*/
u8 direct_monitor;
+ /* the number of DSP channels */
+ u8 dsp_count;
+
/* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected
* internally to the analogue 7/8 outputs
*/
@@ -5827,12 +5830,17 @@ static void scarlett2_count_io(struct scarlett2_data *private)
private->num_mux_srcs = srcs;
private->num_mux_dsts = dsts;
- /* Mixer inputs are mux outputs and vice versa */
+ /* Mixer inputs are mux outputs and vice versa.
+ * Scarlett Gen 4 DSP I/O uses SCARLETT2_PORT_TYPE_MIX but
+ * doesn't have mixer controls.
+ */
private->num_mix_in =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT];
+ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT] -
+ info->dsp_count;
private->num_mix_out =
- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN];
+ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN] -
+ info->dsp_count;
/* Number of analogue line outputs */
private->num_line_out =
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 16/20] ALSA: scarlett2: Rename DSP mux channels
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (14 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 15/20] ALSA: scarlett2: Add support for DSP mux channels Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 17/20] ALSA: scarlett2: Add minimum firmware version check Geoffrey D. Bennett
` (4 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
The DSP mux channels are of type SCARLETT2_PORT_TYPE_MIX so the
ALSA controls would refer to them "Mix X" and "Mixer Input X". This
patch fixes them to be called "DSP X" and "DSP Input X".
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 5ac31fea9831..5c44793fe345 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -583,6 +583,8 @@ struct scarlett2_port {
const char * const src_descr;
int src_num_offset;
const char * const dst_descr;
+ const char * const dsp_src_descr;
+ const char * const dsp_dst_descr;
};
static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = {
@@ -612,7 +614,9 @@ static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] =
.id = 0x300,
.src_descr = "Mix %c",
.src_num_offset = 'A',
- .dst_descr = "Mixer Input %02d Capture"
+ .dst_descr = "Mixer Input %02d Capture",
+ .dsp_src_descr = "DSP %d",
+ .dsp_dst_descr = "DSP Input %d Capture"
},
[SCARLETT2_PORT_TYPE_PCM] = {
.id = 0x600,
@@ -5378,8 +5382,16 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl,
const struct scarlett2_port *port =
&scarlett2_ports[port_type];
- sprintf(uinfo->value.enumerated.name,
- port->src_descr, item + port->src_num_offset);
+ if (port_type == SCARLETT2_PORT_TYPE_MIX &&
+ item >= private->num_mix_out)
+ sprintf(uinfo->value.enumerated.name,
+ port->dsp_src_descr,
+ item - private->num_mix_out + 1);
+ else
+ sprintf(uinfo->value.enumerated.name,
+ port->src_descr,
+ item + port->src_num_offset);
+
return 0;
}
item -= port_count[port_type][SCARLETT2_PORT_IN];
@@ -5472,10 +5484,18 @@ static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer)
channel++, i++) {
int err;
char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- const char *const descr =
- scarlett2_ports[port_type].dst_descr;
+ int channel_num = channel + 1;
+ const struct scarlett2_port *port =
+ &scarlett2_ports[port_type];
+ const char *descr = port->dst_descr;
- snprintf(s, sizeof(s) - 5, descr, channel + 1);
+ if (port_type == SCARLETT2_PORT_TYPE_MIX &&
+ channel >= private->num_mix_in) {
+ channel_num -= private->num_mix_in;
+ descr = port->dsp_dst_descr;
+ }
+
+ snprintf(s, sizeof(s) - 5, descr, channel_num);
strcat(s, " Enum");
err = scarlett2_add_new_ctl(mixer,
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 17/20] ALSA: scarlett2: Add minimum firmware version check
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (15 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 16/20] ALSA: scarlett2: Rename " Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 18/20] ALSA: scarlett2: Add R/O headphone volume control Geoffrey D. Bennett
` (3 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Early firmware for the Scarlett Gen 4 devices has sufficient
differences that it is better to enforce a minimum firmware version
than to try and work around those differences. Add a minimum firmware
version field to the device info struct, and display a message if the
firmware version is too old. Only create the Firmware Version and MSD
(optional) controls in this case.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 73 ++++++++++++++++++++++++++++++++++---
1 file changed, 67 insertions(+), 6 deletions(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 5c44793fe345..c21ea9fc38ab 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -660,6 +660,9 @@ struct scarlett2_device_info {
/* which set of configuration parameters the device uses */
const struct scarlett2_config_set *config_set;
+ /* minimum firmware version required */
+ u16 min_firmware_version;
+
/* support for main/alt speaker switching */
u8 has_speaker_switching;
@@ -2352,6 +2355,45 @@ static int scarlett2_add_firmware_version_ctl(
0, 0, "Firmware Version", NULL);
}
+/*** Minimum Firmware Version Control ***/
+
+static int scarlett2_min_firmware_version_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->info->min_firmware_version;
+
+ return 0;
+}
+
+static int scarlett2_min_firmware_version_ctl_info(
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new scarlett2_min_firmware_version_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .name = "",
+ .info = scarlett2_min_firmware_version_ctl_info,
+ .get = scarlett2_min_firmware_version_ctl_get
+};
+
+static int scarlett2_add_min_firmware_version_ctl(
+ struct usb_mixer_interface *mixer)
+{
+ return scarlett2_add_new_ctl(mixer, &scarlett2_min_firmware_version_ctl,
+ 0, 0, "Minimum Firmware Version", NULL);
+}
+
/*** Sync Control ***/
/* Update sync control after receiving notification that the status
@@ -6061,6 +6103,7 @@ static int scarlett2_get_flash_segment_nums(struct usb_mixer_interface *mixer)
static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
+ const struct scarlett2_device_info *info = private->info;
int err, i;
if (scarlett2_has_config_item(private, SCARLETT2_CONFIG_MSD_SWITCH)) {
@@ -6069,12 +6112,22 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
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;
}
+ if (private->firmware_version < info->min_firmware_version) {
+ usb_audio_err(mixer->chip,
+ "Focusrite %s firmware version %d is too old; "
+ "need %d",
+ private->series_name,
+ private->firmware_version,
+ info->min_firmware_version);
+ return 0;
+ }
+
+ /* no other controls are created if MSD mode is on */
+ if (private->msd_switch)
+ return 0;
+
err = scarlett2_update_input_level(mixer);
if (err < 0)
return err;
@@ -6595,6 +6648,11 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
+ /* Add minimum firmware version control */
+ err = scarlett2_add_min_firmware_version_ctl(mixer);
+ if (err < 0)
+ return err;
+
/* Read volume levels and controls from the interface */
err = scarlett2_read_configs(mixer);
if (err < 0)
@@ -6605,8 +6663,11 @@ static int snd_scarlett2_controls_create(
if (err < 0)
return err;
- /* If MSD mode is enabled, don't create any other controls */
- if (private->msd_switch)
+ /* If MSD mode is enabled, or if the firmware version is too
+ * old, don't create any other controls
+ */
+ if (private->msd_switch ||
+ private->firmware_version < private->info->min_firmware_version)
return 0;
/* Create the analogue output controls */
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 18/20] ALSA: scarlett2: Add R/O headphone volume control
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (16 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 17/20] ALSA: scarlett2: Add minimum firmware version check Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 19/20] ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4 Geoffrey D. Bennett
` (2 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
The Scarlett 4i4 Gen 4 adds a R/O headphone volume control in addition
to a R/O master volume control (which is already supported).
Mark the new scarlett2_notify_volume() function with __always_unused
until it gets used when the Gen 4 notification callback function arrays
are added.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 82 ++++++++++++++++++++++++++++++++++++-
1 file changed, 81 insertions(+), 1 deletion(-)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index c21ea9fc38ab..fea22ae9b0ba 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -332,6 +332,7 @@ enum {
SCARLETT2_CONFIG_MUTE_SWITCH,
SCARLETT2_CONFIG_SW_HW_SWITCH,
SCARLETT2_CONFIG_MASTER_VOLUME,
+ SCARLETT2_CONFIG_HEADPHONE_VOLUME,
SCARLETT2_CONFIG_LEVEL_SWITCH,
SCARLETT2_CONFIG_PAD_SWITCH,
SCARLETT2_CONFIG_MSD_SWITCH,
@@ -784,6 +785,7 @@ struct scarlett2_data {
u8 power_status_updated;
u8 sync;
u8 master_vol;
+ u8 headphone_vol;
u8 vol[SCARLETT2_ANALOGUE_MAX];
u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX];
u8 mute_switch[SCARLETT2_ANALOGUE_MAX];
@@ -809,6 +811,7 @@ struct scarlett2_data {
u8 meter_level_map[SCARLETT2_MAX_METERS];
struct snd_kcontrol *sync_ctl;
struct snd_kcontrol *master_vol_ctl;
+ struct snd_kcontrol *headphone_vol_ctl;
struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
struct snd_kcontrol *sw_hw_ctls[SCARLETT2_ANALOGUE_MAX];
struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX];
@@ -3324,6 +3327,18 @@ static int scarlett2_update_volumes(struct usb_mixer_interface *mixer)
private->vol[i] = private->master_vol;
}
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_HEADPHONE_VOLUME)) {
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_HEADPHONE_VOLUME,
+ 1, &vol);
+ if (err < 0)
+ return err;
+
+ private->headphone_vol = clamp(vol + SCARLETT2_VOLUME_BIAS,
+ 0, SCARLETT2_VOLUME_BIAS);
+ }
+
return 0;
}
@@ -3367,6 +3382,34 @@ static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl,
return err;
}
+static int scarlett2_headphone_volume_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;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ if (private->vol_updated) {
+ err = scarlett2_update_volumes(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.integer.value[0] = private->headphone_vol;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
static int line_out_remap(struct scarlett2_data *private, int index)
{
const struct scarlett2_device_info *info = private->info;
@@ -3456,6 +3499,17 @@ static const struct snd_kcontrol_new scarlett2_master_volume_ctl = {
.tlv = { .p = db_scale_scarlett2_volume }
};
+static const struct snd_kcontrol_new scarlett2_headphone_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "",
+ .info = scarlett2_volume_ctl_info,
+ .get = scarlett2_headphone_volume_ctl_get,
+ .private_value = 0, /* max value */
+ .tlv = { .p = db_scale_scarlett2_volume }
+};
+
static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -4806,6 +4860,18 @@ static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer)
return err;
}
+ /* Add R/O headphone volume control */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_HEADPHONE_VOLUME)) {
+ snprintf(s, sizeof(s), "Headphone Playback Volume");
+ err = scarlett2_add_new_ctl(mixer,
+ &scarlett2_headphone_volume_ctl,
+ 0, 1, s,
+ &private->headphone_vol_ctl);
+ if (err < 0)
+ return err;
+ }
+
/* Remaining controls are only applicable if the device
* has per-channel line-out volume controls.
*/
@@ -6265,7 +6331,7 @@ static void scarlett2_notify_sync(struct usb_mixer_interface *mixer)
&private->sync_ctl->id);
}
-/* Notify on monitor change */
+/* Notify on monitor change (Gen 2/3) */
static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
@@ -6286,6 +6352,20 @@ static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer)
&private->vol_ctls[i]->id);
}
+/* Notify on volume change (Gen 4) */
+static __always_unused void scarlett2_notify_volume(
+ struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->vol_updated = 1;
+
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->master_vol_ctl->id);
+ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->headphone_vol_ctl->id);
+}
+
/* Notify on dim/mute change */
static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 19/20] ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (17 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 18/20] ALSA: scarlett2: Add R/O headphone volume control Geoffrey D. Bennett
@ 2023-12-26 18:08 ` Geoffrey D. Bennett
2023-12-26 18:09 ` [PATCH 20/20] ALSA: scarlett2: Add PCM Input Switch for Solo " Geoffrey D. Bennett
2023-12-29 15:06 ` [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Takashi Iwai
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:08 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
Add new Focusrite Scarlett Gen 4 USB IDs, notification arrays, config
sets, and device info data.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
include/uapi/sound/scarlett2.h | 4 +-
sound/usb/mixer_quirks.c | 3 +
sound/usb/mixer_scarlett2.c | 361 +++++++++++++++++++++++++++++++--
3 files changed, 351 insertions(+), 17 deletions(-)
diff --git a/include/uapi/sound/scarlett2.h b/include/uapi/sound/scarlett2.h
index d0ff38ffa154..91467ab0ed70 100644
--- a/include/uapi/sound/scarlett2.h
+++ b/include/uapi/sound/scarlett2.h
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Focusrite Scarlett 2 Protocol Driver for ALSA
- * (including Scarlett 2nd Gen, 3rd Gen, Clarett USB, and Clarett+
- * series products)
+ * (including Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, and
+ * Clarett+ series products)
*
* Copyright (c) 2023 by Geoffrey D. Bennett <g at b4.vu>
*/
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 898bc3baca7b..6ba431d60890 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3420,6 +3420,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */
case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */
case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */
+ case USB_ID(0x1235, 0x8218): /* Focusrite Scarlett Solo 4th Gen */
+ case USB_ID(0x1235, 0x8219): /* Focusrite Scarlett 2i2 4th Gen */
+ case USB_ID(0x1235, 0x821a): /* Focusrite Scarlett 4i4 4th Gen */
case USB_ID(0x1235, 0x8206): /* Focusrite Clarett 2Pre USB */
case USB_ID(0x1235, 0x8207): /* Focusrite Clarett 4Pre USB */
case USB_ID(0x1235, 0x8208): /* Focusrite Clarett 8Pre USB */
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index fea22ae9b0ba..89848204ad42 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Focusrite Scarlett 2 Protocol Driver for ALSA
- * (including Scarlett 2nd Gen, 3rd Gen, Clarett USB, and Clarett+
- * series products)
+ * (including Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, and
+ * Clarett+ series products)
*
* Supported models:
* - 6i6/18i8/18i20 Gen 2
* - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3
+ * - Solo/2i2/4i4 Gen 4
* - Clarett 2Pre/4Pre/8Pre USB
* - Clarett+ 2Pre/4Pre/8Pre
*
@@ -68,6 +69,12 @@
*
* Support for Clarett 2Pre and 4Pre USB added in Oct 2023.
*
+ * Support for firmware updates added in Dec 2023.
+ *
+ * Support for Scarlett Solo/2i2/4i4 Gen 4 added in Dec 2023 (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
@@ -78,6 +85,8 @@
* controls
* - disable/enable MSD mode
* - disable/enable standalone mode
+ * - input gain, autogain, safe mode
+ * - direct monitor mixes
*
* <ditaa>
* /--------------\ 18chn 20chn /--------------\
@@ -130,7 +139,7 @@
* \--------------/
* </ditaa>
*
- * Gen 3 devices have a Mass Storage Device (MSD) mode where a small
+ * Gen 3/4 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:
@@ -302,9 +311,19 @@ struct scarlett2_notification {
static void scarlett2_notify_sync(struct usb_mixer_interface *mixer);
static void scarlett2_notify_dim_mute(struct usb_mixer_interface *mixer);
static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_volume(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_level(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_pad(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_air(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_phantom(struct usb_mixer_interface *mixer);
static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer);
static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer);
static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer);
/* Arrays of notification callback functions */
@@ -325,6 +344,48 @@ static const struct scarlett2_notification scarlett3a_notifications[] = {
{ 0, NULL }
};
+static const struct scarlett2_notification scarlett4_solo_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00400000, scarlett2_notify_input_air },
+ { 0x00800000, scarlett2_notify_direct_monitor },
+ { 0x01000000, scarlett2_notify_input_level },
+ { 0x02000000, scarlett2_notify_input_phantom },
+ { 0, NULL }
+};
+
+static const struct scarlett2_notification scarlett4_2i2_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00200000, scarlett2_notify_input_safe },
+ { 0x00400000, scarlett2_notify_autogain },
+ { 0x00800000, scarlett2_notify_input_air },
+ { 0x01000000, scarlett2_notify_direct_monitor },
+ { 0x02000000, scarlett2_notify_input_select },
+ { 0x04000000, scarlett2_notify_input_level },
+ { 0x08000000, scarlett2_notify_input_phantom },
+ { 0x10000000, NULL }, /* power status, ignored */
+ { 0x40000000, scarlett2_notify_input_gain },
+ { 0x80000000, NULL }, /* power status, ignored */
+ { 0, NULL }
+};
+
+static const struct scarlett2_notification scarlett4_4i4_notifications[] = {
+ { 0x00000001, NULL }, /* ack, gets ignored */
+ { 0x00000008, scarlett2_notify_sync },
+ { 0x00200000, scarlett2_notify_input_safe },
+ { 0x00400000, scarlett2_notify_autogain },
+ { 0x00800000, scarlett2_notify_input_air },
+ { 0x01000000, scarlett2_notify_input_select },
+ { 0x02000000, scarlett2_notify_input_level },
+ { 0x04000000, scarlett2_notify_input_phantom },
+ { 0x08000000, scarlett2_notify_power_status }, /* power external */
+ { 0x20000000, scarlett2_notify_input_gain },
+ { 0x40000000, scarlett2_notify_power_status }, /* power status */
+ { 0x80000000, scarlett2_notify_volume },
+ { 0, NULL }
+};
+
/* Configuration parameters that can be read and written */
enum {
SCARLETT2_CONFIG_DIM_MUTE,
@@ -543,6 +604,123 @@ static const struct scarlett2_config_set scarlett2_config_set_gen3c = {
}
};
+/* Solo Gen 4 */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
+ .notifications = scarlett4_solo_notifications,
+ .gen4_write_addr = 0xd8,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x47, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
+ .offset = 0x108, .activate = 12 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x46, .activate = 9, .mute = 1 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x3d, .activate = 10, .mute = 1 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x3e, .activate = 11 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
+ .offset = 0x232, .size = 16, .activate = 26 }
+ }
+};
+
+/* 2i2 Gen 4 */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2 = {
+ .notifications = scarlett4_2i2_notifications,
+ .gen4_write_addr = 0xfc,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x49, .size = 8, .activate = 4 }, // 0x41 ??
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
+ .offset = 0x14a, .activate = 16 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+ .offset = 0x135, .activate = 10 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+ .offset = 0x137 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x48, .activate = 11, .mute = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_GAIN] = {
+ .offset = 0x4b, .activate = 12 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x3c, .activate = 13, .mute = 1 },
+
+ [SCARLETT2_CONFIG_SAFE_SWITCH] = {
+ .offset = 0x147, .activate = 14 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x3e, .activate = 15 },
+
+ [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
+ .offset = 0x14b, .activate = 17 },
+
+ [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
+ .offset = 0x14e, .activate = 18 },
+
+ [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
+ .offset = 0x2a0, .size = 16, .activate = 36 }
+ }
+};
+
+/* 4i4 Gen 4 */
+static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
+ .notifications = scarlett4_4i4_notifications,
+ .gen4_write_addr = 0x130,
+ .items = {
+ [SCARLETT2_CONFIG_MSD_SWITCH] = {
+ .offset = 0x5c, .size = 8, .activate = 4 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
+ .offset = 0x13e, .activate = 10 },
+
+ [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
+ .offset = 0x140 },
+
+ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
+ .offset = 0x5a, .activate = 11, .mute = 1 },
+
+ [SCARLETT2_CONFIG_INPUT_GAIN] = {
+ .offset = 0x5e, .activate = 12 },
+
+ [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
+ .offset = 0x4e, .activate = 13, .mute = 1 },
+
+ [SCARLETT2_CONFIG_SAFE_SWITCH] = {
+ .offset = 0x150, .activate = 14 },
+
+ [SCARLETT2_CONFIG_AIR_SWITCH] = {
+ .offset = 0x50, .activate = 15 },
+
+ [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
+ .offset = 0x153, .activate = 16 },
+
+ [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
+ .offset = 0x156, .activate = 17 },
+
+ [SCARLETT2_CONFIG_MASTER_VOLUME] = {
+ .offset = 0x32, .size = 16 },
+
+ [SCARLETT2_CONFIG_HEADPHONE_VOLUME] = {
+ .offset = 0x3a, .size = 16 },
+
+ [SCARLETT2_CONFIG_POWER_EXT] = {
+ .offset = 0x168 },
+
+ [SCARLETT2_CONFIG_POWER_STATUS] = {
+ .offset = 0x66 }
+ }
+};
+
/* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */
static const struct scarlett2_config_set scarlett2_config_set_clarett = {
.notifications = scarlett2_notifications,
@@ -1274,6 +1452,160 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
}
};
+static const struct scarlett2_device_info solo_gen4_info = {
+ .config_set = &scarlett2_config_set_gen4_solo,
+ .min_firmware_version = 2115,
+
+ .level_input_count = 1,
+ .air_input_count = 1,
+ .air_input_first = 1,
+ .air_option = 1,
+ .phantom_count = 1,
+ .phantom_first = 1,
+ .inputs_per_phantom = 1,
+ .direct_monitor = 1,
+ .dsp_count = 2,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 2 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 8, 6 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 2, 4 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 6, 2 },
+ { 4, 2 },
+ { 8, 4 },
+ { 2, 2 },
+ { 0, 2 },
+ { 0, 0 }
+ }
+};
+
+static const struct scarlett2_device_info s2i2_gen4_info = {
+ .config_set = &scarlett2_config_set_gen4_2i2,
+ .min_firmware_version = 2115,
+
+ .level_input_count = 2,
+ .air_input_count = 2,
+ .air_option = 1,
+ .phantom_count = 1,
+ .inputs_per_phantom = 2,
+ .gain_input_count = 2,
+ .direct_monitor = 2,
+ .dsp_count = 2,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 2 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 6, 6 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 2, 4 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 4, 2 },
+ { SCARLETT2_PORT_TYPE_MIX, 2, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 4 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 2 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 6, 2 },
+ { 4, 2 },
+ { 8, 4 },
+ { 2, 2 },
+ { 0, 2 },
+ { 0, 0 }
+ }
+};
+
+static const struct scarlett2_device_info s4i4_gen4_info = {
+ .config_set = &scarlett2_config_set_gen4_4i4,
+ .min_firmware_version = 2089,
+
+ .level_input_count = 2,
+ .air_input_count = 2,
+ .air_option = 1,
+ .phantom_count = 2,
+ .inputs_per_phantom = 1,
+ .gain_input_count = 2,
+ .dsp_count = 2,
+
+ .port_count = {
+ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 },
+ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 6 },
+ [SCARLETT2_PORT_TYPE_MIX] = { 8, 12 },
+ [SCARLETT2_PORT_TYPE_PCM] = { 6, 6 },
+ },
+
+ .mux_assignment = { {
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ }, {
+ { SCARLETT2_PORT_TYPE_MIX, 10, 2 },
+ { SCARLETT2_PORT_TYPE_PCM, 0, 6 },
+ { SCARLETT2_PORT_TYPE_MIX, 0, 10 },
+ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 },
+ { 0, 0, 0 },
+ } },
+
+ .meter_map = {
+ { 16, 8 },
+ { 6, 10 },
+ { 0, 6 },
+ { 0, 0 }
+ }
+};
+
static const struct scarlett2_device_info clarett_2pre_info = {
.config_set = &scarlett2_config_set_clarett,
.level_input_count = 2,
@@ -1451,6 +1783,11 @@ static const struct scarlett2_device_entry scarlett2_devices[] = {
{ USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" },
{ USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" },
+ /* Supported Gen 4 devices */
+ { USB_ID(0x1235, 0x8218), &solo_gen4_info, "Scarlett Gen 4" },
+ { USB_ID(0x1235, 0x8219), &s2i2_gen4_info, "Scarlett Gen 4" },
+ { USB_ID(0x1235, 0x821a), &s4i4_gen4_info, "Scarlett Gen 4" },
+
/* Supported Clarett USB/Clarett+ devices */
{ USB_ID(0x1235, 0x8206), &clarett_2pre_info, "Clarett USB" },
{ USB_ID(0x1235, 0x8207), &clarett_4pre_info, "Clarett USB" },
@@ -6353,8 +6690,7 @@ static void scarlett2_notify_monitor(struct usb_mixer_interface *mixer)
}
/* Notify on volume change (Gen 4) */
-static __always_unused void scarlett2_notify_volume(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_volume(struct usb_mixer_interface *mixer)
{
struct scarlett2_data *private = mixer->private_data;
@@ -6460,8 +6796,7 @@ static void scarlett2_notify_input_other(struct usb_mixer_interface *mixer)
}
/* Notify on input select change */
-static __always_unused void scarlett2_notify_input_select(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_input_select(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
@@ -6483,8 +6818,7 @@ static __always_unused void scarlett2_notify_input_select(
}
/* Notify on input gain change */
-static __always_unused void scarlett2_notify_input_gain(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_input_gain(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
@@ -6502,8 +6836,7 @@ static __always_unused void scarlett2_notify_input_gain(
}
/* Notify on autogain change */
-static __always_unused void scarlett2_notify_autogain(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_autogain(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
@@ -6526,8 +6859,7 @@ static __always_unused void scarlett2_notify_autogain(
}
/* Notify on input safe switch change */
-static __always_unused void scarlett2_notify_input_safe(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
@@ -6603,8 +6935,7 @@ static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer)
}
/* Notify on power change */
-static __always_unused void scarlett2_notify_power_status(
- struct usb_mixer_interface *mixer)
+static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer)
{
struct snd_card *card = mixer->chip->card;
struct scarlett2_data *private = mixer->private_data;
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 20/20] ALSA: scarlett2: Add PCM Input Switch for Solo Gen 4
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (18 preceding siblings ...)
2023-12-26 18:08 ` [PATCH 19/20] ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4 Geoffrey D. Bennett
@ 2023-12-26 18:09 ` Geoffrey D. Bennett
2023-12-29 15:06 ` [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Takashi Iwai
20 siblings, 0 replies; 22+ messages in thread
From: Geoffrey D. Bennett @ 2023-12-26 18:09 UTC (permalink / raw)
To: Takashi Iwai; +Cc: Takashi Iwai, alsa-devel, linux-sound
When the Direct button on the Solo Gen 4 is held for 3 seconds, the
PCM 1 and 2 inputs are toggled between DSP Outputs 1 and 2, and Mixer
Outputs E and F. This patch adds the corresponding ALSA control.
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
---
sound/usb/mixer_scarlett2.c | 151 ++++++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 89848204ad42..0d0d3f1c061e 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -324,6 +324,8 @@ static void scarlett2_notify_input_safe(struct usb_mixer_interface *mixer);
static void scarlett2_notify_monitor_other(struct usb_mixer_interface *mixer);
static void scarlett2_notify_direct_monitor(struct usb_mixer_interface *mixer);
static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer);
+static void scarlett2_notify_pcm_input_switch(
+ struct usb_mixer_interface *mixer);
/* Arrays of notification callback functions */
@@ -351,6 +353,7 @@ static const struct scarlett2_notification scarlett4_solo_notifications[] = {
{ 0x00800000, scarlett2_notify_direct_monitor },
{ 0x01000000, scarlett2_notify_input_level },
{ 0x02000000, scarlett2_notify_input_phantom },
+ { 0x04000000, scarlett2_notify_pcm_input_switch },
{ 0, NULL }
};
@@ -413,6 +416,7 @@ enum {
SCARLETT2_CONFIG_INPUT_LINK_SWITCH,
SCARLETT2_CONFIG_POWER_EXT,
SCARLETT2_CONFIG_POWER_STATUS,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN,
SCARLETT2_CONFIG_COUNT
};
@@ -624,6 +628,9 @@ static const struct scarlett2_config_set scarlett2_config_set_gen4_solo = {
[SCARLETT2_CONFIG_AIR_SWITCH] = {
.offset = 0x3e, .activate = 11 },
+ [SCARLETT2_CONFIG_PCM_INPUT_SWITCH] = {
+ .offset = 0x206, .activate = 25 },
+
[SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
.offset = 0x232, .size = 16, .activate = 26 }
}
@@ -955,6 +962,7 @@ struct scarlett2_data {
u8 input_gain_updated;
u8 autogain_updated;
u8 input_safe_updated;
+ u8 pcm_input_switch_updated;
u8 monitor_other_updated;
u8 direct_monitor_updated;
u8 mux_updated;
@@ -979,6 +987,7 @@ struct scarlett2_data {
u8 autogain_switch[SCARLETT2_INPUT_GAIN_MAX];
u8 autogain_status[SCARLETT2_INPUT_GAIN_MAX];
u8 safe_switch[SCARLETT2_INPUT_GAIN_MAX];
+ u8 pcm_input_switch;
u8 direct_monitor_switch;
u8 speaker_switching_switch;
u8 talkback_switch;
@@ -1004,6 +1013,7 @@ struct scarlett2_data {
struct snd_kcontrol *autogain_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *autogain_status_ctls[SCARLETT2_INPUT_GAIN_MAX];
struct snd_kcontrol *safe_ctls[SCARLETT2_INPUT_GAIN_MAX];
+ struct snd_kcontrol *pcm_input_switch_ctl;
struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX];
struct snd_kcontrol *mix_ctls[SCARLETT2_MIX_MAX];
struct snd_kcontrol *direct_monitor_ctl;
@@ -3633,6 +3643,101 @@ static const struct snd_kcontrol_new scarlett2_safe_ctl = {
.put = scarlett2_safe_ctl_put,
};
+/*** PCM Input Control ***/
+
+static int scarlett2_update_pcm_input_switch(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_data *private = mixer->private_data;
+ int err;
+
+ private->pcm_input_switch_updated = 0;
+
+ err = scarlett2_usb_get_config(
+ mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
+ 1, &private->pcm_input_switch);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int scarlett2_pcm_input_switch_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 = elem->head.mixer->private_data;
+ int err = 0;
+
+ mutex_lock(&private->data_mutex);
+
+ if (private->pcm_input_switch_updated) {
+ err = scarlett2_update_pcm_input_switch(mixer);
+ if (err < 0)
+ goto unlock;
+ }
+ ucontrol->value.enumerated.item[0] = private->pcm_input_switch;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_pcm_input_switch_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);
+
+ if (private->hwdep_in_use) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ oval = private->pcm_input_switch;
+ val = !!ucontrol->value.integer.value[0];
+
+ if (oval == val)
+ goto unlock;
+
+ private->pcm_input_switch = val;
+
+ /* Send switch change to the device */
+ err = scarlett2_usb_set_config(
+ mixer, SCARLETT2_CONFIG_PCM_INPUT_SWITCH,
+ 0, val);
+ if (err == 0)
+ err = 1;
+
+unlock:
+ mutex_unlock(&private->data_mutex);
+ return err;
+}
+
+static int scarlett2_pcm_input_switch_ctl_info(
+ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
+{
+ static const char *const values[2] = {
+ "Direct", "Mixer"
+ };
+
+ return snd_ctl_enum_info(
+ uinfo, 1, 2, values);
+}
+
+static const struct snd_kcontrol_new scarlett2_pcm_input_switch_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "",
+ .info = scarlett2_pcm_input_switch_ctl_info,
+ .get = scarlett2_pcm_input_switch_ctl_get,
+ .put = scarlett2_pcm_input_switch_ctl_put
+};
+
/*** Analogue Line Out Volume Controls ***/
/* Update hardware volume controls after receiving notification that
@@ -5419,6 +5524,17 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer)
}
}
+ /* Add PCM Input Switch control */
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+ err = scarlett2_add_new_ctl(
+ mixer, &scarlett2_pcm_input_switch_ctl, 0, 1,
+ "PCM Input Capture Switch",
+ &private->pcm_input_switch_ctl);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -6650,6 +6766,13 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
if (err < 0)
return err;
+ if (scarlett2_has_config_item(private,
+ SCARLETT2_CONFIG_PCM_INPUT_SWITCH)) {
+ err = scarlett2_update_pcm_input_switch(mixer);
+ if (err < 0)
+ return err;
+ }
+
err = scarlett2_update_mix(mixer);
if (err < 0)
return err;
@@ -6946,6 +7069,34 @@ static void scarlett2_notify_power_status(struct usb_mixer_interface *mixer)
&private->power_status_ctl->id);
}
+/* Notify on mux change */
+static void scarlett2_notify_mux(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+ int i;
+
+ private->mux_updated = 1;
+
+ for (i = 0; i < private->num_mux_dsts; i++)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->mux_ctls[i]->id);
+}
+
+/* Notify on PCM input switch change */
+static void scarlett2_notify_pcm_input_switch(struct usb_mixer_interface *mixer)
+{
+ struct snd_card *card = mixer->chip->card;
+ struct scarlett2_data *private = mixer->private_data;
+
+ private->pcm_input_switch_updated = 1;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &private->pcm_input_switch_ctl->id);
+
+ scarlett2_notify_mux(mixer);
+}
+
/* Interrupt callback */
static void scarlett2_notify(struct urb *urb)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
` (19 preceding siblings ...)
2023-12-26 18:09 ` [PATCH 20/20] ALSA: scarlett2: Add PCM Input Switch for Solo " Geoffrey D. Bennett
@ 2023-12-29 15:06 ` Takashi Iwai
20 siblings, 0 replies; 22+ messages in thread
From: Takashi Iwai @ 2023-12-29 15:06 UTC (permalink / raw)
To: Geoffrey D. Bennett; +Cc: Takashi Iwai, alsa-devel, linux-sound
On Tue, 26 Dec 2023 19:05:43 +0100,
Geoffrey D. Bennett wrote:
>
> Hi Takashi,
>
> This patch series adds support for the Focusrite Scarlett 4th Gen
> interfaces. It builds on top of the two previous patch series I sent:
>
> - https://lore.kernel.org/linux-sound/cover.1703001053.git.g@b4.vu/
> ("ALSA: scarlett2: Firmware Upgrade and Error Handling Improvements")
>
> - https://lore.kernel.org/linux-sound/cover.1703444932.git.g@b4.vu/
> ("ALSA: scarlett2: Refactor in preparation for gen4")
>
> I already sent patch #1 in this series below separately:
> https://lore.kernel.org/linux-sound/ZYsBIE3DSKdi4YC%2F@m.b4.vu/
> as it should go in to 6.7. I wasn't sure if I should include it again
> here or not; sorry if I guessed wrong.
>
> Patch #2 is a little cleanup I missed before sending the previous
> series and would have gone there had I noticed.
>
> Patches #3-18 add the new controls, etc. needed for the Gen 4 support.
>
> Patch #19 adds the USB IDs and configuration data to enable support
> for the new interfaces.
>
> Patch #20 adds a last-minute new control that is only present on the
> Solo Gen 4.
>
> Regards,
> Geoffrey.
>
> Geoffrey D. Bennett (20):
> ALSA: scarlett2: Convert meter levels from little-endian
> ALSA: scarlett2: Remove repeated elem->head.mixer references
> ALSA: scarlett2: Add support for air/phantom control on input 2
> ALSA: scarlett2: Add support for Gen 4 style parameters
> ALSA: scarlett2: Allow for controls with a "mute mode"
> ALSA: scarlett2: Add support for Air Presence + Drive option
> ALSA: scarlett2: Add support for software-controllable input gain
> ALSA: scarlett2: Minor refactor MSD mode check
> ALSA: scarlett2: Disable input controls while autogain is running
> ALSA: scarlett2: Disable autogain during phantom power state change
> ALSA: scarlett2: Add power status control
> ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor
> ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates
> ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes
> ALSA: scarlett2: Add support for DSP mux channels
> ALSA: scarlett2: Rename DSP mux channels
> ALSA: scarlett2: Add minimum firmware version check
> ALSA: scarlett2: Add R/O headphone volume control
> ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4
> ALSA: scarlett2: Add PCM Input Switch for Solo Gen 4
Now applied to topic/scarlett2 branch (merged to for-next).
The first patch was already taken in for-linus branch for 6.7.
thanks,
Takashi
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2024-01-02 19:35 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-12-26 18:05 [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 01/20] ALSA: scarlett2: Convert meter levels from little-endian Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 02/20] ALSA: scarlett2: Remove repeated elem->head.mixer references Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 03/20] ALSA: scarlett2: Add support for air/phantom control on input 2 Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 04/20] ALSA: scarlett2: Add support for Gen 4 style parameters Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 05/20] ALSA: scarlett2: Allow for controls with a "mute mode" Geoffrey D. Bennett
2023-12-26 18:06 ` [PATCH 06/20] ALSA: scarlett2: Add support for Air Presence + Drive option Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 07/20] ALSA: scarlett2: Add support for software-controllable input gain Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 08/20] ALSA: scarlett2: Minor refactor MSD mode check Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 09/20] ALSA: scarlett2: Disable input controls while autogain is running Geoffrey D. Bennett
2023-12-26 18:07 ` [PATCH 10/20] ALSA: scarlett2: Disable autogain during phantom power state change Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 11/20] ALSA: scarlett2: Add power status control Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 12/20] ALSA: scarlett2: Store mix_ctls for Gen 4 Direct Monitor Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 13/20] ALSA: scarlett2: Handle Gen 4 Direct Monitor mix updates Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 14/20] ALSA: scarlett2: Add support for custom Gen 4 Direct Monitor mixes Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 15/20] ALSA: scarlett2: Add support for DSP mux channels Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 16/20] ALSA: scarlett2: Rename " Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 17/20] ALSA: scarlett2: Add minimum firmware version check Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 18/20] ALSA: scarlett2: Add R/O headphone volume control Geoffrey D. Bennett
2023-12-26 18:08 ` [PATCH 19/20] ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4 Geoffrey D. Bennett
2023-12-26 18:09 ` [PATCH 20/20] ALSA: scarlett2: Add PCM Input Switch for Solo " Geoffrey D. Bennett
2023-12-29 15:06 ` [PATCH 00/20] ALSA: scarlett2: Add support for Scarlett 4th Gen Takashi Iwai
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.