summaryrefslogtreecommitdiff
path: root/sound/usb/mixer_scarlett2.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/mixer_scarlett2.c')
-rw-r--r--sound/usb/mixer_scarlett2.c188
1 files changed, 186 insertions, 2 deletions
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 601e310b7218..462a66d485c8 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -210,6 +210,9 @@ static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = {
*/
#define SCARLETT2_MUX_MAX 77
+/* Maximum number of sources (sum of input port counts) */
+#define SCARLETT2_MAX_SRCS 52
+
/* Maximum number of meters (sum of output port counts) */
#define SCARLETT2_MAX_METERS 65
@@ -328,6 +331,18 @@ struct scarlett2_mux_entry {
u8 count;
};
+/* Maximum number of entries in a mux table */
+#define SCARLETT2_MAX_METER_ENTRIES 9
+
+/* One entry within meter_assignment defines the range of mux outputs
+ * that consecutive meter entries are mapped to. The end of the list
+ * is marked with count == 0.
+ */
+struct scarlett2_meter_entry {
+ u8 start;
+ u8 count;
+};
+
struct scarlett2_device_info {
/* Gen 3 devices have an internal MSD mode switch that needs
* to be disabled in order to access the full functionality of
@@ -381,6 +396,7 @@ struct scarlett2_device_info {
*/
u8 line_out_remap_enable;
u8 line_out_remap[SCARLETT2_ANALOGUE_MAX];
+ u8 line_out_unmap[SCARLETT2_ANALOGUE_MAX];
/* additional description for the line out volume controls */
const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX];
@@ -391,6 +407,12 @@ struct scarlett2_device_info {
/* layout/order of the entries in the set_mux message */
struct scarlett2_mux_entry mux_assignment[SCARLETT2_MUX_TABLES]
[SCARLETT2_MAX_MUX_ENTRIES];
+
+ /* map from meter level order returned by
+ * SCARLETT2_USB_GET_METER to index into mux[] entries (same
+ * as the order returned by scarlett2_meter_ctl_get())
+ */
+ struct scarlett2_meter_entry meter_map[SCARLETT2_MAX_METER_ENTRIES];
};
struct scarlett2_data {
@@ -431,6 +453,7 @@ struct scarlett2_data {
u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX];
u8 msd_switch;
u8 standalone_switch;
+ u8 meter_level_map[SCARLETT2_MAX_METERS];
struct snd_kcontrol *sync_ctl;
struct snd_kcontrol *master_vol_ctl;
struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX];
@@ -493,6 +516,12 @@ static const struct scarlett2_device_info s6i6_gen2_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 8 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 24, 6 },
+ { 0, 24 },
+ { 0, 0 },
+ }
};
static const struct scarlett2_device_info s18i8_gen2_info = {
@@ -540,6 +569,12 @@ static const struct scarlett2_device_info s18i8_gen2_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 4 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 26, 18 },
+ { 0, 26 },
+ { 0, 0 },
+ }
};
static const struct scarlett2_device_info s18i20_gen2_info = {
@@ -592,6 +627,12 @@ static const struct scarlett2_device_info s18i20_gen2_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 6 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 38, 18 },
+ { 0, 38 },
+ { 0, 0 },
+ }
};
static const struct scarlett2_device_info solo_gen3_info = {
@@ -657,6 +698,12 @@ static const struct scarlett2_device_info s4i4_gen3_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 16 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 12, 6 },
+ { 0, 12 },
+ { 0, 0 },
+ }
};
static const struct scarlett2_device_info s8i6_gen3_info = {
@@ -708,6 +755,14 @@ static const struct scarlett2_device_info s8i6_gen3_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 18 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 14, 8 },
+ { 0, 6 },
+ { 22, 2 },
+ { 6, 8 },
+ { 0, 0 },
+ }
};
static const struct scarlett2_device_info s18i8_gen3_info = {
@@ -723,6 +778,7 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
.line_out_remap_enable = 1,
.line_out_remap = { 0, 1, 6, 7, 2, 3, 4, 5 },
+ .line_out_unmap = { 0, 1, 4, 5, 6, 7, 2, 3 },
.line_out_descrs = {
"Monitor L",
@@ -776,6 +832,18 @@ static const struct scarlett2_device_info s18i8_gen3_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 10 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 30, 10 },
+ { 42, 8 },
+ { 0, 2 },
+ { 6, 2 },
+ { 2, 4 },
+ { 8, 2 },
+ { 40, 2 },
+ { 10, 20 },
+ { 0, 0 }
+ }
};
static const struct scarlett2_device_info s18i20_gen3_info = {
@@ -839,6 +907,15 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 24 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 45, 8 },
+ { 55, 10 },
+ { 0, 20 },
+ { 53, 2 },
+ { 20, 25 },
+ { 0, 0 },
+ }
};
static const struct scarlett2_device_info clarett_2pre_info = {
@@ -881,6 +958,12 @@ static const struct scarlett2_device_info clarett_2pre_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 26 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 22, 12 },
+ { 0, 22 },
+ { 0, 0 }
+ }
};
static const struct scarlett2_device_info clarett_4pre_info = {
@@ -928,6 +1011,12 @@ static const struct scarlett2_device_info clarett_4pre_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 24 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 26, 18 },
+ { 0, 26 },
+ { 0, 0 }
+ }
};
static const struct scarlett2_device_info clarett_8pre_info = {
@@ -981,6 +1070,12 @@ static const struct scarlett2_device_info clarett_8pre_info = {
{ SCARLETT2_PORT_TYPE_NONE, 0, 22 },
{ 0, 0, 0 },
} },
+
+ .meter_map = {
+ { 38, 18 },
+ { 0, 38 },
+ { 0, 0 }
+ }
};
struct scarlett2_device_entry {
@@ -1688,6 +1783,79 @@ static void scarlett2_usb_populate_mux(struct scarlett2_data *private,
private->mux[dst_idx] = src_idx;
}
+/* Update the meter level map
+ *
+ * The meter level data from the interface (SCARLETT2_USB_GET_METER
+ * request) is returned in mux_assignment order, but to avoid exposing
+ * that to userspace, scarlett2_meter_ctl_get() rearranges the data
+ * into scarlett2_ports order using the meter_level_map[] array which
+ * is set up by this function.
+ *
+ * In addition, the meter level data values returned from the
+ * interface are invalid for destinations where:
+ *
+ * - the source is "Off"; therefore we set those values to zero (map
+ * value of 255)
+ *
+ * - the source is assigned to a previous (with respect to the
+ * mux_assignment order) destination; therefore we set those values
+ * to the value previously reported for that source
+ */
+static void scarlett2_update_meter_level_map(struct scarlett2_data *private)
+{
+ const struct scarlett2_device_info *info = private->info;
+ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
+ int line_out_count =
+ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT];
+ const struct scarlett2_meter_entry *entry;
+
+ /* sources already assigned to a destination
+ * value is 255 for None, otherwise the value of i
+ * (index into array returned by
+ * scarlett2_usb_get_meter_levels())
+ */
+ u8 seen_src[SCARLETT2_MAX_SRCS] = { 1 };
+ u8 seen_src_value[SCARLETT2_MAX_SRCS] = { 255 };
+
+ /* index in meter_map[] order */
+ int i = 0;
+
+ /* go through the meter_map[] entries */
+ for (entry = info->meter_map;
+ entry->count;
+ entry++) {
+
+ /* fill in each meter_level_map[] entry */
+ int j, mux_idx;
+
+ for (j = 0, mux_idx = entry->start;
+ j < entry->count;
+ i++, j++, mux_idx++) {
+
+ /* convert mux_idx using line_out_unmap[] */
+ int map_mux_idx = (
+ info->line_out_remap_enable &&
+ mux_idx < line_out_count
+ ) ? info->line_out_unmap[mux_idx]
+ : mux_idx;
+
+ /* check which source is connected, and if
+ * that source is already connected elsewhere,
+ * use that existing connection's destination
+ * for this meter entry instead
+ */
+ int mux_src = private->mux[mux_idx];
+
+ if (!seen_src[mux_src]) {
+ seen_src[mux_src] = 1;
+ seen_src_value[mux_src] = i;
+ }
+ private->meter_level_map[map_mux_idx] =
+ seen_src_value[mux_src];
+ }
+ }
+}
+
/* Send USB message to get mux inputs and then populate private->mux[] */
static int scarlett2_usb_get_mux(struct usb_mixer_interface *mixer)
{
@@ -1716,6 +1884,8 @@ static int scarlett2_usb_get_mux(struct usb_mixer_interface *mixer)
for (i = 0; i < count; i++)
scarlett2_usb_populate_mux(private, le32_to_cpu(data[i]));
+ scarlett2_update_meter_level_map(private);
+
return 0;
}
@@ -1782,6 +1952,8 @@ static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer)
return err;
}
+ scarlett2_update_meter_level_map(private);
+
return 0;
}
@@ -3619,6 +3791,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;
+ u8 *meter_level_map = private->meter_level_map;
u16 meter_levels[SCARLETT2_MAX_METERS];
int i, err;
@@ -3627,8 +3801,18 @@ static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl,
if (err < 0)
return err;
- for (i = 0; i < elem->channels; i++)
- ucontrol->value.integer.value[i] = meter_levels[i];
+ /* copy & translate from meter_levels[] using meter_level_map[] */
+ for (i = 0; i < elem->channels; i++) {
+ int idx = meter_level_map[i];
+ int value;
+
+ if (idx == 255)
+ value = 0;
+ else
+ value = meter_levels[idx];
+
+ ucontrol->value.integer.value[i] = value;
+ }
return 0;
}