* [PATCH 0/3] Add Audient EVO4 support
@ 2026-09-19 15:18 Christian Ruppert
2026-09-19 15:18 ` [PATCH 1/3] Fix Audient EVO4 master playback control name Christian Ruppert
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Christian Ruppert @ 2026-09-19 15:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, linux-kernel, linux-sound
Cc: Christian Ruppert, Will Porter, Peter Drabik, Neil Andrews
This patch set adds support for advanced mixer features of the Audient
EVO4 audio interface. The code is structured so that support for the
EVO8 (and potentially other Audient devices) should be trivial to add
but I don't have access to the hardware and no way of testing them so
they are not supported by this patch set.
The third patch in the series (Add Kconfig option for Audient EVO4 mixer
quirks) is optional. Other mixer quirks are compiled in by default and
don't provide Kconfig options but as this one adds a few hundred lines
of code I decided to provide this last patch as an option and leave the
choice if such an option should be added to the subsystem maintainers.
Christian Ruppert (3):
Fix Audient EVO4 master playback control name
Add Audient EVO4 mixer quirks
Add Kconfig option for Audient EVO4 mixer quirks
MAINTAINERS | 7 +
sound/usb/Kconfig | 10 +
sound/usb/Makefile | 2 +
sound/usb/mixer_evo.c | 505 +++++++++++++++++++++++++++++++++++++++
sound/usb/mixer_evo.h | 12 +
sound/usb/mixer_maps.c | 13 +
sound/usb/mixer_quirks.c | 9 +
7 files changed, 558 insertions(+)
create mode 100644 sound/usb/mixer_evo.c
create mode 100644 sound/usb/mixer_evo.h
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] Fix Audient EVO4 master playback control name
2026-09-19 15:18 [PATCH 0/3] Add Audient EVO4 support Christian Ruppert
@ 2026-09-19 15:18 ` Christian Ruppert
2026-09-19 15:18 ` [PATCH 2/3] Add Audient EVO4 mixer quirks Christian Ruppert
2026-09-19 15:18 ` [PATCH 3/3] Add Kconfig option for " Christian Ruppert
2 siblings, 0 replies; 4+ messages in thread
From: Christian Ruppert @ 2026-09-19 15:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, linux-kernel, linux-sound
Cc: Christian Ruppert, Will Porter, Peter Drabik, Neil Andrews
The Audient EVO4 master volume mixer channel enumerates as 'EVO4 '. Rename
it to 'Master' according to control-names.rst.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/mixer_maps.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 41454cb403d0..1a6ad776126b 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -505,6 +505,14 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = {
{}
};
+/* Audient EVO4: Rename FU 10 from 'EVO4 ' to 'Master' according to the
+ * ALSA naming convention
+ */
+static const struct usbmix_name_map audient_evo4_map[] = {
+ { 10, "Master Playback" },
+ {}
+};
+
/* Audient iD14 MkI and MkII: FU 12 sits on the monitor mixer branch but is
* traced through to the Speaker output terminal, so it is named "Speaker
* Playback Volume". On MkII it controls only 4 of 6 playback channels. MkI
@@ -632,6 +640,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x2708, 0x0002),
.map = audient_id14_map,
},
+ {
+ /* Audient EVO4 MkI */
+ .id = USB_ID(0x2708, 0x0006),
+ .map = audient_evo4_map,
+ },
{
/* Audient iD14 MkII */
.id = USB_ID(0x2708, 0x0008),
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 2/3] Add Audient EVO4 mixer quirks
2026-09-19 15:18 [PATCH 0/3] Add Audient EVO4 support Christian Ruppert
2026-09-19 15:18 ` [PATCH 1/3] Fix Audient EVO4 master playback control name Christian Ruppert
@ 2026-09-19 15:18 ` Christian Ruppert
2026-09-19 15:18 ` [PATCH 3/3] Add Kconfig option for " Christian Ruppert
2 siblings, 0 replies; 4+ messages in thread
From: Christian Ruppert @ 2026-09-19 15:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, linux-kernel, linux-sound
Cc: Christian Ruppert, Will Porter, Peter Drabik, Neil Andrews
The controls enumerated by the Audient EVO4 are incomplete. Add missing
controls for
* channel muting
* the cross-fader
* the recording mixer
* phantom power
The driver is structured so that it should be trivial to extend it to the
EVO8 (and potentially other Audient devices) but we need a kind soul who
has access to that hardware to correctly map the control names and test
everything.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
MAINTAINERS | 7 +
sound/usb/Makefile | 1 +
sound/usb/mixer_evo.c | 505 +++++++++++++++++++++++++++++++++++++++
sound/usb/mixer_evo.h | 12 +
sound/usb/mixer_quirks.c | 5 +
5 files changed, 530 insertions(+)
create mode 100644 sound/usb/mixer_evo.c
create mode 100644 sound/usb/mixer_evo.h
diff --git a/MAINTAINERS b/MAINTAINERS
index c2414447892c..5f8df442b1a5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4346,6 +4346,13 @@ F: drivers/net/ieee802154/at86rf230.h
F: drivers/net/ieee802154/atusb.c
F: drivers/net/ieee802154/atusb.h
+AUDIENT EVO MIXER DRIVER
+M: Christian Ruppert <arc@gmx.li>
+L: linux-sound@vger.kernel.org
+S: Maintained
+F: sound/usb/mixer_evo.c
+F: sound/usb/mixer_evo.h
+
AUDIT SUBSYSTEM
M: Paul Moore <paul@paul-moore.com>
M: Eric Paris <eparis@redhat.com>
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index e62794a87e73..2e842dcc73de 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,6 +11,7 @@ snd-usb-audio-y := card.o \
helper.o \
implicit.o \
mixer.o \
+ mixer_evo.o \
mixer_quirks.o \
mixer_scarlett.o \
mixer_scarlett2.o \
diff --git a/sound/usb/mixer_evo.c b/sound/usb/mixer_evo.c
new file mode 100644
index 000000000000..c327b82c8d2d
--- /dev/null
+++ b/sound/usb/mixer_evo.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Audient EVO driver for ALSA
+ * Copyright (C) 2026 Christian Ruppert <arc@gmx.li>
+ *
+ * Based on the work of Ivan Hazucha for audient-evo-py.
+ * https://github.com/vanzaho/audient-evo-py/
+ */
+
+
+#include <asm/byteorder.h>
+#include <linux/usb.h>
+#include <linux/usb/audio-v2.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "mixer_quirks.h"
+#include "helper.h"
+#include "mixer_evo.h"
+
+
+enum snd_evo_type {
+ SND_EVO_TYPE_MONITOR = 0,
+ SND_EVO_TYPE_PHANTOM,
+ SND_EVO_TYPE_MUTE,
+ SND_EVO_TYPE_MUTE_OUT,
+ SND_EVO_TYPE_MIXER,
+ SND_EVO_TYPE_NCTYPES
+};
+
+static const struct snd_evo_ctrl_type {
+ snd_ctl_elem_type_t type;
+ bool invert;
+ union {
+ int integer;
+ } min;
+ union {
+ int integer;
+ } max;
+ u16 wValue;
+ u16 wIndex;
+} snd_evo_ctypes[SND_EVO_TYPE_NCTYPES] = {
+ [SND_EVO_TYPE_MONITOR] = {
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .min.integer = 0,
+ .max.integer = 127,
+ .wValue = 0x0000,
+ .wIndex = 0x3800,
+ },
+ [SND_EVO_TYPE_PHANTOM] = {
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .invert = false,
+ .min.integer = 0,
+ .max.integer = 1,
+ .wValue = 0x0000,
+ .wIndex = 0x3A00,
+ },
+ [SND_EVO_TYPE_MUTE] = {
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .invert = true,
+ .min.integer = 0,
+ .max.integer = 1,
+ .wValue = 0x0200,
+ .wIndex = 0x3A00,
+ },
+ [SND_EVO_TYPE_MUTE_OUT] = {
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .invert = true,
+ .min.integer = 0,
+ .max.integer = 1,
+ .wValue = 0x0100,
+ .wIndex = 0x3B00,
+ },
+ [SND_EVO_TYPE_MIXER] = {
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .min.integer = -128 * 256,
+ .max.integer = 6 * 256,
+ .wValue = 0x0100,
+ .wIndex = 0x3C00,
+ },
+};
+
+#define SND_EVO_FLAGS_DIRECTION (1L<<0)
+#define SND_EVO_FLAGS_CAPTURE (1L<<0)
+#define SND_EVO_FLAGS_PLAYBACK (0L<<0)
+
+struct snd_evo_ctrl {
+ char *basename;
+ enum snd_evo_type type;
+ unsigned int channels;
+ unsigned int hwchannels;
+ unsigned int val_offs;
+ unsigned long flags;
+};
+
+static const struct snd_evo_ctrl evo4_controls[] = {
+ {
+ .basename = "Master",
+ .type = SND_EVO_TYPE_MUTE_OUT,
+ .channels = 1,
+ .hwchannels = 1,
+ .val_offs = 0,
+ .flags = SND_EVO_FLAGS_PLAYBACK,
+ },
+ {
+ .basename = "Mic",
+ .type = SND_EVO_TYPE_MUTE,
+ .channels = 4,
+ .hwchannels = 2,
+ .val_offs = 0,
+ .flags = SND_EVO_FLAGS_PLAYBACK,
+ },
+ {
+ .basename = "Monitor",
+ .type = SND_EVO_TYPE_MONITOR,
+ .channels = 1,
+ .hwchannels = 1,
+ .val_offs = 0,
+ .flags = SND_EVO_FLAGS_PLAYBACK,
+ },
+ {
+ .basename = "Mic 1",
+ .type = SND_EVO_TYPE_MIXER,
+ .channels = 2,
+ .hwchannels = 2,
+ .val_offs = 0,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Mic 2",
+ .type = SND_EVO_TYPE_MIXER,
+ .channels = 2,
+ .hwchannels = 2,
+ .val_offs = 2,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Master Left",
+ .type = SND_EVO_TYPE_MIXER,
+ .channels = 2,
+ .hwchannels = 2,
+ .val_offs = 4,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Master Right",
+ .type = SND_EVO_TYPE_MIXER,
+ .channels = 2,
+ .hwchannels = 2,
+ .val_offs = 6,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Loopback Left",
+ .type = SND_EVO_TYPE_MIXER,
+ .channels = 2,
+ .hwchannels = 2,
+ .val_offs = 8,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Loopback Right",
+ .type = SND_EVO_TYPE_MIXER,
+ .channels = 2,
+ .hwchannels = 2,
+ .val_offs = 10,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Mic 1 Phantom",
+ .type = SND_EVO_TYPE_PHANTOM,
+ .channels = 1,
+ .hwchannels = 1,
+ .val_offs = 0,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ {
+ .basename = "Mic 2 Phantom",
+ .type = SND_EVO_TYPE_PHANTOM,
+ .channels = 1,
+ .hwchannels = 1,
+ .val_offs = 1,
+ .flags = SND_EVO_FLAGS_CAPTURE,
+ },
+ { 0 }, /* sentinel */
+};
+
+static const struct evo_devinfo {
+ u32 usb_id;
+ const struct snd_evo_ctrl *controls;
+} evo_devinfo[] = {
+ {
+ .usb_id = USB_ID(USB_AUDIENT_VID, USB_EVO4_PID),
+ .controls = evo4_controls,
+ },
+ {
+ .usb_id = USB_ID(USB_AUDIENT_VID, USB_EVO4_PID),
+ .controls = NULL,
+ },
+ { 0 } /* sentinel */
+};
+
+
+static inline unsigned long snd_evo_kctl_priv(enum snd_evo_type type,
+ u8 valoffs, u8 hwchannels)
+{
+ unsigned long type_l = type & 0xFF;
+ unsigned long valoffs_l = valoffs & 0xFF;
+ unsigned long hwchannels_l = hwchannels & 0xFF;
+
+ return (hwchannels_l << 16) | (valoffs_l << 8) | type_l;
+}
+
+static __always_inline
+const struct snd_evo_ctrl_type *snd_evo_kctl_type(struct snd_kcontrol *kctl)
+{
+ return &snd_evo_ctypes[kctl->private_value & 0xFF];
+}
+
+static __always_inline u16 snd_evo_kctl_valoffs(struct snd_kcontrol *kctl)
+{
+ return (kctl->private_value >> 8) & 0xFF;
+}
+
+static __always_inline u16 snd_evo_kctl_hwchannels(struct snd_kcontrol *kctl)
+{
+ return (kctl->private_value >> 16) & 0xFF;
+}
+
+static inline u16 snd_evo_kctl_channels(struct snd_kcontrol *kctl)
+{
+ struct usb_mixer_elem_info *info = kctl->private_data;
+
+ return info->channels;
+}
+
+
+#define EVO_REQ_CUR (0x01)
+#define EVO_REQTYPE_SET (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+#define EVO_REQTYPE_GET (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+static inline int snd_evo_get_cur(struct usb_mixer_interface *mixer,
+ u16 value, u16 index,
+ void *buf, size_t len)
+{
+ struct usb_device *const dev = mixer->chip->dev;
+
+ return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
+ EVO_REQ_CUR, EVO_REQTYPE_GET,
+ value, index, buf, len);
+}
+
+static inline int snd_evo_set_cur(struct usb_mixer_interface *mixer,
+ u16 value, u16 index,
+ void *buf, size_t len)
+{
+ struct usb_device *const dev = mixer->chip->dev;
+
+ return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
+ EVO_REQ_CUR, EVO_REQTYPE_SET,
+ value, index, buf, len);
+}
+
+
+static int snd_ctl_evo_boolean_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
+{
+ struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = list->mixer;
+
+ const unsigned int nchan = snd_evo_kctl_channels(kctl);
+ const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
+ const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const u16 index = ctype->wIndex;
+
+ int i;
+
+ for (i = 0; i < hwchan; i++) {
+ u32 buf;
+ int ret;
+
+ ret = snd_evo_get_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ ctl_val->value.integer.value[i] = le32_to_cpu(buf) ? 1 : 0;
+
+ if (ctype->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;
+
+ return 0;
+}
+
+static int snd_ctl_evo_boolean_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
+{
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *mixer = list->mixer;
+
+ const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
+ const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const u16 index = ctype->wIndex;
+
+ int changed = 0;
+ int i;
+
+ for (i = 0; i < hwchan; i++) {
+ u32 rbuf;
+ u32 buf = cpu_to_le32(ctl_val->value.integer.value[i] ? 1 : 0);
+ int ret;
+
+ if (ctype->invert)
+ buf ^= 0x1;
+
+ ret = snd_evo_get_cur(mixer, value + i, index,
+ &rbuf, sizeof(rbuf));
+ if (ret < 0)
+ return ret;
+
+ if (rbuf != buf) {
+ changed = 1;
+
+ ret = snd_evo_set_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return changed;
+}
+
+static int snd_ctl_evo_integer_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
+{
+ struct usb_mixer_elem_list *const list = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *const mixer = list->mixer;
+
+ const unsigned int nchan = snd_evo_kctl_channels(kctl);
+ const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
+ const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const u16 index = ctype->wIndex;
+
+ int i;
+
+ for (i = 0; i < hwchan; i++) {
+ u16 buf;
+ s16 val;
+ int ret;
+
+ ret = snd_evo_get_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ val = le16_to_cpu(buf);
+ ctl_val->value.integer.value[i] = val;
+ }
+
+ for (; i < nchan; i++)
+ ctl_val->value.integer.value[i] = ctype->min.integer;
+
+ return 0;
+}
+
+static int snd_ctl_evo_integer_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ctl_val)
+{
+ struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
+ struct usb_mixer_interface *mixer = list->mixer;
+
+ const unsigned int hwchan = snd_evo_kctl_hwchannels(kctl);
+ const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+ const u16 value = ctype->wValue + snd_evo_kctl_valoffs(kctl);
+ const u16 index = ctype->wIndex;
+
+ int changed = 0;
+ int i;
+
+ for (i = 0; i < hwchan; i++) {
+ s16 rbuf;
+ s16 buf = cpu_to_le16(ctl_val->value.integer.value[i]);
+ int ret;
+
+ ret = snd_evo_get_cur(mixer, value + i, index,
+ &rbuf, sizeof(rbuf));
+ if (ret < 0)
+ return ret;
+
+ if (rbuf != buf) {
+ changed = 1;
+
+ ret = snd_evo_set_cur(mixer, value + i, index,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return changed;
+}
+
+static int snd_ctl_evo_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const struct snd_evo_ctrl_type *const ctype = snd_evo_kctl_type(kctl);
+
+ uinfo->type = ctype->type;
+ uinfo->access = SNDRV_CTL_ELEM_ACCESS_READWRITE
+ | SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ uinfo->count = snd_evo_kctl_channels(kctl);
+ uinfo->value.integer.min = ctype->min.integer;
+ uinfo->value.integer.max = ctype->max.integer;
+ return 0;
+}
+
+
+static int snd_evo_add_ctrl(struct usb_mixer_interface *mixer,
+ const struct snd_evo_ctrl *ctrl)
+{
+ const struct snd_evo_ctrl_type *ctype = &snd_evo_ctypes[ctrl->type];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct snd_kcontrol_new knew = { 0 };
+ struct snd_kcontrol *kctl;
+ struct usb_mixer_elem_info *elem;
+
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.name = name;
+ knew.info = snd_ctl_evo_info;
+ knew.private_value = snd_evo_kctl_priv(ctrl->type, ctrl->val_offs,
+ ctrl->hwchannels);
+
+ switch (ctype->type) {
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ knew.get = snd_ctl_evo_integer_get;
+ knew.put = snd_ctl_evo_integer_set;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ knew.get = snd_ctl_evo_boolean_get;
+ knew.put = snd_ctl_evo_boolean_set;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snprintf(name, sizeof(name), "%s %s %s", ctrl->basename,
+ ((ctrl->flags & SND_EVO_FLAGS_DIRECTION)
+ == SND_EVO_FLAGS_CAPTURE) ? "Capture" : "Playback",
+ (ctype->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN)
+ ? "Switch" : "Volume");
+
+ elem = kzalloc_obj(*elem);
+ if (!elem)
+ return -ENOMEM;
+
+ elem->head.mixer = mixer;
+ elem->channels = ctrl->channels;
+
+ kctl = snd_ctl_new1(&knew, elem);
+ if (!kctl) {
+ kfree(elem);
+ return -ENOMEM;
+ }
+
+ kctl->private_free = snd_usb_mixer_elem_free;
+
+ return snd_usb_mixer_add_control(&elem->head, kctl);
+}
+
+
+/* called from mixer_quirks.c */
+int snd_evo_controls_create(struct usb_mixer_interface *mixer)
+{
+ const struct evo_devinfo *info;
+ const struct snd_evo_ctrl *ctrl;
+
+ for (info = evo_devinfo; info->controls; info++)
+ if (info->usb_id == mixer->chip->usb_id)
+ break;
+
+ if (!info->controls)
+ return -ENODEV;
+
+ for (ctrl = info->controls; ctrl->basename; ctrl++) {
+ const int ret = snd_evo_add_ctrl(mixer, ctrl);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/sound/usb/mixer_evo.h b/sound/usb/mixer_evo.h
new file mode 100644
index 000000000000..7fe7e197185c
--- /dev/null
+++ b/sound/usb/mixer_evo.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USB_MIXER_EVO_H
+#define __USB_MIXER_EVO_H
+
+#include "mixer.h"
+
+#define USB_AUDIENT_VID (0x2708)
+#define USB_EVO4_PID (0x0006)
+
+int snd_evo_controls_create(struct usb_mixer_interface *mixer);
+
+#endif /* __USB_MIXER_EVO_H */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index fc622eb95dc5..1e266fdbbb8e 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -38,6 +38,7 @@
#include "mixer_scarlett2.h"
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
+#include "mixer_evo.h"
#include "helper.h"
#include "fcp.h"
@@ -4539,6 +4540,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_fcp_init(mixer);
break;
+ case USB_ID(USB_AUDIENT_VID, USB_EVO4_PID): /* Audient EVO 4 */
+ err = snd_evo_controls_create(mixer);
+ break;
+
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
break;
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 3/3] Add Kconfig option for Audient EVO4 mixer quirks
2026-09-19 15:18 [PATCH 0/3] Add Audient EVO4 support Christian Ruppert
2026-09-19 15:18 ` [PATCH 1/3] Fix Audient EVO4 master playback control name Christian Ruppert
2026-09-19 15:18 ` [PATCH 2/3] Add Audient EVO4 mixer quirks Christian Ruppert
@ 2026-09-19 15:18 ` Christian Ruppert
2 siblings, 0 replies; 4+ messages in thread
From: Christian Ruppert @ 2026-09-19 15:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, linux-kernel, linux-sound
Cc: Christian Ruppert, Will Porter, Peter Drabik, Neil Andrews
Add Kconfig option for the Audient EVO4 support. This is split out from
the evo4 driver so it can be optionally applied. Other mixer quirks don't
seem to have Kconfig options so I leave it to the upstream to decide what
to do with this.
Signed-off-by: Christian Ruppert <arc@gmx.li>
---
sound/usb/Kconfig | 10 ++++++++++
sound/usb/Makefile | 3 ++-
sound/usb/mixer_quirks.c | 4 ++++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index b4588915efa1..21039dd4ae3a 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -175,6 +175,16 @@ config SND_USB_HIFACE
To compile this driver as a module, choose M here: the module
will be called snd-usb-hiface.
+config SND_USB_EVO
+ bool "Audient EVO advanced mixer features"
+ help
+ Support advanced mixer features for the Audient EVO4 USB audio
+ interface such as
+ * the recording mixer
+ * the monitor crossfader
+ * phantom power control
+ * missing channel mute buttons
+
config SND_BCD2000
tristate "Behringer BCD2000 MIDI driver"
select SND_RAWMIDI
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 2e842dcc73de..f3664adeab85 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -11,7 +11,6 @@ snd-usb-audio-y := card.o \
helper.o \
implicit.o \
mixer.o \
- mixer_evo.o \
mixer_quirks.o \
mixer_scarlett.o \
mixer_scarlett2.o \
@@ -27,6 +26,8 @@ snd-usb-audio-y := card.o \
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_MIDI_V2) += midi2.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
+snd-usb-audio-$(CONFIG_SND_USB_EVO) += mixer_evo.o
+
snd-usbmidi-lib-y := midi.o
# Toplevel Module Dependency
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 1e266fdbbb8e..c32ccff40122 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -38,7 +38,9 @@
#include "mixer_scarlett2.h"
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
+#ifdef CONFIG_SND_USB_EVO
#include "mixer_evo.h"
+#endif
#include "helper.h"
#include "fcp.h"
@@ -4540,9 +4542,11 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_fcp_init(mixer);
break;
+#ifdef CONFIG_SND_USB_EVO
case USB_ID(USB_AUDIENT_VID, USB_EVO4_PID): /* Audient EVO 4 */
err = snd_evo_controls_create(mixer);
break;
+#endif
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-19 15:19 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-19 15:18 [PATCH 0/3] Add Audient EVO4 support Christian Ruppert
2026-09-19 15:18 ` [PATCH 1/3] Fix Audient EVO4 master playback control name Christian Ruppert
2026-09-19 15:18 ` [PATCH 2/3] Add Audient EVO4 mixer quirks Christian Ruppert
2026-09-19 15:18 ` [PATCH 3/3] Add Kconfig option for " Christian Ruppert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®