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

ALSA: usb-audio: scarlett2: Read mux at init time



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: 9e4d5c1b ("ALSA: usb-audio: Scarlett Gen 2 mixer interface")
Suggested-by: default avatarVladimir Sadovnikov <sadko4u@gmail.com>
Tested-by: default avatarMarkus Schroetter <project.m.schroetter@gmail.com>
Tested-by: default avatarAlex Fellows <alex.fellows@gmail.com>
Tested-by: default avatarDaniel Sales <daniel.sales.z@gmail.com>
Signed-off-by: default avatarGeoffrey D. Bennett <g@b4.vu>
Link: https://lore.kernel.org/r/15b17c60a2bca174bcddcec41c9419b746f21c1d.1623091570.git.g@b4.vu


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 2661f033
Loading
Loading
Loading
Loading
+106 −64
Original line number Diff line number Diff line
@@ -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;
	     port_type++) {
		srcs += ports[port_type].num[SCARLETT2_PORT_IN];
		dsts += ports[port_type].num[SCARLETT2_PORT_OUT_44];
	}

/* 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
	};
	private->num_mux_srcs = srcs;
	private->num_mux_dsts = dsts;
}

	/* 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++;
	}

	/* 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++;
	}
}

/* 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;