Re: [PATCH 0/5] ALSA: usb-audio: Audient iD14 MkI vendor controls
From: Christian Ruppert
Date: Mon Oct 26 2026 - 14:46:26 EST
Hi Will,
On Mon, Oct 19, 2026 at 06:01:54PM -0500, Will Porter wrote:
> [...]
>
> sound for-next also has mixer_evo.c for the Audient EVO 4 (2708:0006).
> EVO and iD are separate Audient product lines. The iD14 MkI uses other
> units and a larger control set: a monitor controller, output routing, a
> 14 x 4 mixer and write-only controls. So this series keeps the iD line
> in its own file.
>
> [...]
I took a look at this question and it seems like we can share the majority
of code in the EVO driver by complementing it with the following features:
- variable width read/write
- caching of write-only values
- enum data type
Most of these changes should be trivial as the EVO driver controls are
also structured in tabular form and the driver was intended from the start
to be extensible to the evo8 and possibly other hardware. See below diff
for an example of how to support the first point (just to illustrate these
changes are minor, others should be of similar extent, not properly tested
so use with caution).
Adding those features should allow to simply add a new table (allocated at
probe time for WO caching) with entries for most id14 controls. The only
part which seems more problematic is probably FU12 which looks like it
requires some custom logic and would still need actual code.
Happy to support a common driver activity and rename everything to audient
if you think this makes sense in order to reduce code size but no problem
if you think this is too complex to manage, up to you.
Christian
diff --git a/sound/usb/mixer_evo.c b/sound/usb/mixer_evo.c
index c327b82c8d2d..92f3c6d108a9 100644
--- a/sound/usb/mixer_evo.c
+++ b/sound/usb/mixer_evo.c
@@ -31,9 +31,13 @@ enum snd_evo_type {
SND_EVO_TYPE_NCTYPES
};
+#define SND_EVO_CTRL_FLAGS_SIGNED (1L<<0)
+#define SND_EVO_CTRL_FLAGS_INVERT (1L<<1)
+
static const struct snd_evo_ctrl_type {
snd_ctl_elem_type_t type;
- bool invert;
+ unsigned long width;
+ unsigned long flags;
union {
int integer;
} min;
@@ -45,6 +49,7 @@ static const struct snd_evo_ctrl_type {
} snd_evo_ctypes[SND_EVO_TYPE_NCTYPES] = {
[SND_EVO_TYPE_MONITOR] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .width = 2,
.min.integer = 0,
.max.integer = 127,
.wValue = 0x0000,
@@ -52,7 +57,7 @@ static const struct snd_evo_ctrl_type {
},
[SND_EVO_TYPE_PHANTOM] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = false,
+ .width = 4,
.min.integer = 0,
.max.integer = 1,
.wValue = 0x0000,
@@ -60,7 +65,8 @@ static const struct snd_evo_ctrl_type {
},
[SND_EVO_TYPE_MUTE] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = true,
+ .width = 4,
+ .flags = SND_EVO_CTRL_FLAGS_INVERT,
.min.integer = 0,
.max.integer = 1,
.wValue = 0x0200,
@@ -68,7 +74,8 @@ static const struct snd_evo_ctrl_type {
},
[SND_EVO_TYPE_MUTE_OUT] = {
.type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
- .invert = true,
+ .width = 4,
+ .flags = SND_EVO_CTRL_FLAGS_INVERT,
.min.integer = 0,
.max.integer = 1,
.wValue = 0x0100,
@@ -76,6 +83,8 @@ static const struct snd_evo_ctrl_type {
},
[SND_EVO_TYPE_MIXER] = {
.type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .width = 2,
+ .flags = SND_EVO_CTRL_FLAGS_SIGNED,
.min.integer = -128 * 256,
.max.integer = 6 * 256,
.wValue = 0x0100,
@@ -280,24 +289,24 @@ static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
int i;
for (i = 0; i < hwchan; i++) {
- u32 buf;
+ u32 buf = 0;
int ret;
ret = snd_evo_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ &buf, ctype->width);
if (ret < 0)
return ret;
ctl_val->value.integer.value[i] = le32_to_cpu(buf) ? 1 : 0;
- if (ctype->invert)
+ if (ctype->flags & SND_EVO_CTRL_FLAGS_INVERT)
ctl_val->value.integer.value[i] ^= 0x1;
}
for (; i < nchan; i++)
- ctl_val->value.integer.value[i] = ctype->invert
- ? ctype->max.integer
- : ctype->min.integer;
+ ctl_val->value.integer.value[i]
+ = (ctype->flags & SND_EVO_CTRL_FLAGS_INVERT)
+ ? ctype->max.integer : ctype->min.integer;
return 0;
}
@@ -317,15 +326,15 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
int i;
for (i = 0; i < hwchan; i++) {
- u32 rbuf;
+ u32 rbuf = 0;
u32 buf = cpu_to_le32(ctl_val->value.integer.value[i] ? 1 : 0);
int ret;
- if (ctype->invert)
+ if (ctype->flags & SND_EVO_CTRL_FLAGS_INVERT)
buf ^= 0x1;
ret = snd_evo_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ &rbuf, ctype->width);
if (ret < 0)
return ret;
@@ -333,7 +342,7 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
changed = 1;
ret = snd_evo_set_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ &buf, ctype->width);
if (ret < 0)
return ret;
}
@@ -342,6 +351,19 @@ static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
return changed;
}
+static inline long buf2cpu(const struct snd_evo_ctrl_type *const ctype, u32 in)
+{
+ const u32 u = le32_to_cpu(in);
+
+ if (ctype->flags & SND_EVO_CTRL_FLAGS_SIGNED) {
+ const int sh = 32 - ctype->width * 8;
+ const s32 s = u << sh;
+
+ return s >> sh;
+ } else
+ return u;
+}
+
static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ctl_val)
{
@@ -357,16 +379,16 @@ static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
int i;
for (i = 0; i < hwchan; i++) {
- u16 buf;
- s16 val;
+ u32 buf = 0;
+ s32 val;
int ret;
ret = snd_evo_get_cur(mixer, value + i, index,
- &buf, sizeof(buf));
+ &buf, ctype->width);
if (ret < 0)
return ret;
- val = le16_to_cpu(buf);
+ val = buf2cpu(ctype, buf);
ctl_val->value.integer.value[i] = val;
}
@@ -391,16 +413,16 @@ static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
int i;
for (i = 0; i < hwchan; i++) {
- s16 rbuf;
- s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
+ s32 rbuf = 0;
+ s32 buf = cpu_to_le32(ctl_val->value.integer.value[i]);
int ret;
ret = snd_evo_get_cur(mixer, value + i, index,
- &rbuf, sizeof(rbuf));
+ &rbuf, ctype->width);
if (ret < 0)
return ret;
- if (rbuf != buf) {
+ if (buf2cpu(ctype, rbuf) != ctl_val->value.integer.value[i]) {
changed = 1;
ret = snd_evo_set_cur(mixer, value + i, index,
Attachment:
signature.asc
Description: PGP signature