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: 9e4d5c1be2
("ALSA: usb-audio: Scarlett Gen 2 mixer interface")
Suggested-by: Vladimir Sadovnikov <sadko4u@gmail.com>
Tested-by: Markus Schroetter <project.m.schroetter@gmail.com>
Tested-by: Alex Fellows <alex.fellows@gmail.com>
Tested-by: Daniel Sales <daniel.sales.z@gmail.com>
Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
Link: https://lore.kernel.org/r/15b17c60a2bca174bcddcec41c9419b746f21c1d.1623091570.git.g@b4.vu
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
2661f033c4
commit
d6f9afe947
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue