[PATCH 2/2] ALSA: usb-audio: scarlett2: Read mux at init time
Geoffrey D. Bennett
g at b4.vu
Sun Jun 6 16:17:08 CEST 2021
Add support for retrieving the mux configuration from the hardware
when the driver is initialising. Previously the ALSA controls were
initialised to a default hard-coded state instead of being initialised
to match the hardware state.
Fixes: 9e4d5c1be21f ("ALSA: usb-audio: Scarlett Gen 2 mixer interface")
Suggested-by: Vladimir Sadovnikov <sadko4u at gmail.com>
Signed-off-by: Geoffrey D. Bennett <g at b4.vu>
---
sound/usb/mixer_scarlett_gen2.c | 170 ++++++++++++++++++++------------
1 file changed, 106 insertions(+), 64 deletions(-)
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c
index b0043906c77f..48cf3e1954e0 100644
--- a/sound/usb/mixer_scarlett_gen2.c
+++ b/sound/usb/mixer_scarlett_gen2.c
@@ -32,6 +32,10 @@
* Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann
* for providing usbmon output and testing).
*
+ * Support for loading mixer volume and mux configuration from the
+ * interface during driver initialisation added in May 2021 (thanks to
+ * Vladimir Sadovnikov for figuring out how).
+ *
* This ALSA mixer gives access to:
* - input, output, mixer-matrix muxes
* - 18x10 mixer-matrix gain stages
@@ -228,6 +232,7 @@ struct scarlett2_mixer_data {
struct delayed_work work;
const struct scarlett2_device_info *info;
int num_mux_srcs;
+ int num_mux_dsts;
u16 scarlett2_seq;
u8 vol_updated;
u8 master_vol;
@@ -468,6 +473,7 @@ static int scarlett2_get_port_start_num(const struct scarlett2_ports *ports,
#define SCARLETT2_USB_GET_METER_LEVELS 0x00001001
#define SCARLETT2_USB_GET_MIX 0x00002001
#define SCARLETT2_USB_SET_MIX 0x00002002
+#define SCARLETT2_USB_GET_MUX 0x00003001
#define SCARLETT2_USB_SET_MUX 0x00003002
#define SCARLETT2_USB_GET_DATA 0x00800000
#define SCARLETT2_USB_SET_DATA 0x00800001
@@ -877,6 +883,94 @@ static u32 scarlett2_mux_src_num_to_id(const struct scarlett2_ports *ports,
return 0;
}
+/* Convert a hardware ID to a port number index */
+static u32 scarlett2_mux_id_to_num(const struct scarlett2_ports *ports,
+ int direction,
+ u32 id)
+{
+ int port_type;
+ int port_num = 0;
+
+ for (port_type = 0;
+ port_type < SCARLETT2_PORT_TYPE_COUNT;
+ port_type++) {
+ struct scarlett2_ports port = ports[port_type];
+ int count = port.num[direction];
+
+ if (id >= port.id && id < port.id + count)
+ return port_num + id - port.id;
+ port_num += count;
+ }
+
+ /* Oops */
+ return -1;
+}
+
+/* Convert one mux entry from the interface and load into private->mux[] */
+static void scarlett2_usb_populate_mux(struct scarlett2_mixer_data *private,
+ u32 mux_entry)
+{
+ const struct scarlett2_device_info *info = private->info;
+ const struct scarlett2_ports *ports = info->ports;
+
+ int dst_idx, src_idx;
+
+ dst_idx = scarlett2_mux_id_to_num(ports, SCARLETT2_PORT_OUT,
+ mux_entry & 0xFFF);
+ if (dst_idx < 0)
+ return;
+
+ if (dst_idx >= private->num_mux_dsts) {
+ usb_audio_err(private->mixer->chip,
+ "BUG: scarlett2_mux_id_to_num(%06x, OUT): %d >= %d",
+ mux_entry, dst_idx, private->num_mux_dsts);
+ return;
+ }
+
+ src_idx = scarlett2_mux_id_to_num(ports, SCARLETT2_PORT_IN,
+ mux_entry >> 12);
+ if (src_idx < 0)
+ return;
+
+ if (src_idx >= private->num_mux_srcs) {
+ usb_audio_err(private->mixer->chip,
+ "BUG: scarlett2_mux_id_to_num(%06x, IN): %d >= %d",
+ mux_entry, src_idx, private->num_mux_srcs);
+ return;
+ }
+
+ private->mux[dst_idx] = src_idx;
+}
+
+/* Send USB message to get mux inputs and then populate private->mux[] */
+static int scarlett2_usb_get_mux(struct usb_mixer_interface *mixer)
+{
+ struct scarlett2_mixer_data *private = mixer->private_data;
+ int count = private->num_mux_dsts;
+ int err, i;
+
+ struct {
+ __le16 num;
+ __le16 count;
+ } __packed req;
+
+ __le32 data[SCARLETT2_MUX_MAX];
+
+ req.num = 0;
+ req.count = cpu_to_le16(count);
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_MUX,
+ &req, sizeof(req),
+ data, count * sizeof(u32));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < count; i++)
+ scarlett2_usb_populate_mux(private, le32_to_cpu(data[i]));
+
+ return 0;
+}
+
/* Send USB messages to set mux inputs */
static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer)
{
@@ -1783,72 +1877,23 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
/*** Initialisation ***/
-static int scarlett2_count_mux_srcs(const struct scarlett2_ports *ports)
+static void scarlett2_count_mux_io(struct scarlett2_mixer_data *private)
{
- int port_type, count = 0;
+ const struct scarlett2_ports *ports = private->info->ports;
+ int port_type, srcs = 0, dsts = 0;
for (port_type = 0;
port_type < SCARLETT2_PORT_TYPE_COUNT;
- port_type++)
- count += ports[port_type].num[SCARLETT2_PORT_IN];
-
- return count;
-}
-
-/* Default routing connects PCM outputs and inputs to Analogue,
- * S/PDIF, then ADAT
- */
-static void scarlett2_init_routing(u8 *mux,
- const struct scarlett2_ports *ports)
-{
- int i, input_num, input_count, port_type;
- int output_num, output_count, port_type_connect_num;
-
- static const int connect_order[] = {
- SCARLETT2_PORT_TYPE_ANALOGUE,
- SCARLETT2_PORT_TYPE_SPDIF,
- SCARLETT2_PORT_TYPE_ADAT,
- -1
- };
-
- /* Assign PCM inputs (routing outputs) */
- output_num = scarlett2_get_port_start_num(ports,
- SCARLETT2_PORT_OUT,
- SCARLETT2_PORT_TYPE_PCM);
- output_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_OUT];
-
- for (port_type = connect_order[port_type_connect_num = 0];
- port_type >= 0;
- port_type = connect_order[++port_type_connect_num]) {
- input_num = scarlett2_get_port_start_num(
- ports, SCARLETT2_PORT_IN, port_type);
- input_count = ports[port_type].num[SCARLETT2_PORT_IN];
- for (i = 0;
- i < input_count && output_count;
- i++, output_count--)
- mux[output_num++] = input_num++;
+ port_type++) {
+ srcs += ports[port_type].num[SCARLETT2_PORT_IN];
+ dsts += ports[port_type].num[SCARLETT2_PORT_OUT_44];
}
- /* Assign PCM outputs (routing inputs) */
- input_num = scarlett2_get_port_start_num(ports,
- SCARLETT2_PORT_IN,
- SCARLETT2_PORT_TYPE_PCM);
- input_count = ports[SCARLETT2_PORT_TYPE_PCM].num[SCARLETT2_PORT_IN];
-
- for (port_type = connect_order[port_type_connect_num = 0];
- port_type >= 0;
- port_type = connect_order[++port_type_connect_num]) {
- output_num = scarlett2_get_port_start_num(
- ports, SCARLETT2_PORT_OUT, port_type);
- output_count = ports[port_type].num[SCARLETT2_PORT_OUT];
- for (i = 0;
- i < output_count && input_count;
- i++, input_count--)
- mux[output_num++] = input_num++;
- }
+ private->num_mux_srcs = srcs;
+ private->num_mux_dsts = dsts;
}
-/* Initialise private data, routing, sequence number */
+/* Initialise private data and sequence number */
static int scarlett2_init_private(struct usb_mixer_interface *mixer,
const struct scarlett2_device_info *info)
{
@@ -1862,16 +1907,13 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer,
mutex_init(&private->data_mutex);
INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work);
private->info = info;
- private->num_mux_srcs = scarlett2_count_mux_srcs(info->ports);
+ scarlett2_count_mux_io(private);
private->scarlett2_seq = 0;
private->mixer = mixer;
mixer->private_data = private;
mixer->private_free = scarlett2_private_free;
mixer->private_suspend = scarlett2_private_suspend;
- /* Setup default routing */
- scarlett2_init_routing(private->mux, info->ports);
-
/* Initialise the sequence number used for the proprietary commands */
return scarlett2_usb(mixer, SCARLETT2_USB_INIT_SEQ, NULL, 0, NULL, 0);
}
@@ -1947,7 +1989,7 @@ static int scarlett2_read_configs(struct usb_mixer_interface *mixer)
return err;
}
- return 0;
+ return scarlett2_usb_get_mux(mixer);
}
/* Notify on volume change */
@@ -2055,7 +2097,7 @@ static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer,
{
int err;
- /* Initialise private data, routing, sequence number */
+ /* Initialise private data and sequence number */
err = scarlett2_init_private(mixer, info);
if (err < 0)
return err;
--
2.31.1
More information about the Alsa-devel
mailing list