Re: [PATCH] ALSA: usb-audio: apply quirk for MOONDROP JU Jiu
From: Takashi Iwai
Date: Thu Apr 02 2026 - 09:13:34 EST
On Thu, 02 Apr 2026 12:48:45 +0200,
Takashi Iwai wrote:
>
> On Thu, 02 Apr 2026 07:36:57 +0200,
> Cryolitia PukNgae wrote:
> >
> > It(ID 31b2:0111 JU Jiu) reports a MIN value -12800 for volume control, but
> > will mute when setting it less than -10880.
> >
> > Thanks to my girlfriend Kagura for reporting this issue.
> >
> > Cc: Kagura <me@xxxxxxxxxxxxxxxx>
> > Cc: stable@xxxxxxxxxxxxxxx
> > Signed-off-by: Cryolitia PukNgae <cryolitia.pukngae@xxxxxxxxx>
>
> Applied to for-next branch now.
>
> > ---
> > Btw, is it a good idea for turn the volume_control_quirks from
> > switch-case to a table and sort it accroding to USB VID&PID?
>
> Yeah, this might be better, indeed.
>
> But the quirk isn't really straightforward for those, maybe we need a
> matching of USB id plus kctl id name string, then update the cval
> fields conditionally with flags. Let me cook later...
This doesn't look easy. A quick hack is like below, but it doesn't
reduce the code, ended up with more lines.
So, unless any other clever implementation is given, still not much
worth for it, as it seems.
Takashi
-- 8< --
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1070,11 +1070,107 @@ void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
* interface to ALSA control for feature/mixer units
*/
+#define VOL_QUIRK_MIN BIT(0)
+#define VOL_QUIRK_MAX BIT(1)
+#define VOL_QUIRK_RES BIT(2)
+
+struct mixer_volume_quirk {
+ unsigned int id;
+ const char *ctl_name;
+ unsigned int to_update;
+ int min, max, res;
+};
+
+#define QUIRK_ENTRY(vid, pid, name) \
+ .id = USB_ID(vid, pid), .ctl_name = (name)
+#define QUIRK_MIN(v) \
+ .to_update = VOL_QUIRK_MIN, .min = (v)
+#define QUIRK_RES(v) \
+ .to_update = VOL_QUIRK_RES, .res = (v)
+#define QUIRK_MINMAX(vmin, vmax) \
+ .to_update = VOL_QUIRK_MIN | VOL_QUIRK_MAX, .min = (vmin), .max = (vmax)
+#define QUIRK_MINMAXRES(vmin, vmax, vres) \
+ .to_update = VOL_QUIRK_MIN | VOL_QUIRK_MAX | VOL_QUIRK_RES, \
+ .min = (vmin), .max = (vmax), .res = (vres)
+
+static const struct mixer_volume_quirk mixer_volume_quirk_entries[] = {
+ /* M-Audio Fast Track C400 */
+ { QUIRK_ENTRY(0x0763, 0x2030, "Effect Duration"),
+ QUIRK_MINMAXRES(0x0000, 0xffff, 0x00e6) },
+ { QUIRK_ENTRY(0x0763, 0x2030, "Effect Volume"),
+ QUIRK_MINMAX(0x00, 0xff) },
+ { QUIRK_ENTRY(0x0763, 0x2030, "Effect Feedback Volume"),
+ QUIRK_MINMAX(0x00, 0xff) },
+ { QUIRK_ENTRY(0x0763, 0x2030, "Effect Return"),
+ QUIRK_MINMAXRES(0xb706, 0xff7b, 0x0073) },
+ { QUIRK_ENTRY(0x0763, 0x2030, "Playback Volume"),
+ QUIRK_MINMAXRES(0xb5fb, 0xfcfe, 0x0073) }, /* -73 dB = 0xb6ff */
+ { QUIRK_ENTRY(0x0763, 0x2030, "Effect Send"),
+ QUIRK_MINMAXRES(0xb5fb, 0xfcfe, 0x0073) }, /* -73 dB = 0xb6ff */
+
+ /* M-Audio Fast Track C600 */
+ { QUIRK_ENTRY(0x0763, 0x2031, "Effect Duration"),
+ QUIRK_MINMAXRES(0x0000, 0xffff, 0x00e6) },
+ { QUIRK_ENTRY(0x0763, 0x2031, "Effect Volume"),
+ QUIRK_MINMAX(0x00, 0xff) },
+ { QUIRK_ENTRY(0x0763, 0x2031, "Effect Feedback Volume"),
+ QUIRK_MINMAX(0x00, 0xff) },
+ { QUIRK_ENTRY(0x0763, 0x2031, "Effect Return"),
+ QUIRK_MINMAXRES(0xb706, 0xff7b, 0x0073) },
+ { QUIRK_ENTRY(0x0763, 0x2031, "Playback Volume"),
+ QUIRK_MINMAXRES(0xb5fb, 0xfcfe, 0x0073) }, /* -73 dB = 0xb6ff */
+ { QUIRK_ENTRY(0x0763, 0x2031, "Effect Send"),
+ QUIRK_MINMAXRES(0xb5fb, 0xfcfe, 0x0073) }, /* -73 dB = 0xb6ff */
+
+ /* M-Audio Fast Track Ultra 8R */
+ { QUIRK_ENTRY(0x0763, 0x2081, "Effect Duration"),
+ QUIRK_MINMAXRES(0x0000, 0x7f00, 0x0100) },
+ { QUIRK_ENTRY(0x0763, 0x2081, "Effect Volume"),
+ QUIRK_MINMAX(0x00, 0x7f) },
+ { QUIRK_ENTRY(0x0763, 0x2081, "Effect Feedback Volume"),
+ QUIRK_MINMAX(0x00, 0x7f) },
+
+ /* M-Audio Fast Track Ultra */
+ { QUIRK_ENTRY(0x0763, 0x2080, "Effect Duration"),
+ QUIRK_MINMAXRES(0x0000, 0x7f00, 0x0100) },
+ { QUIRK_ENTRY(0x0763, 0x2080, "Effect Volume"),
+ QUIRK_MINMAX(0x00, 0x7f) },
+ { QUIRK_ENTRY(0x0763, 0x2080, "Effect Feedback Volume"),
+ QUIRK_MINMAX(0x00, 0x7f) },
+
+ /* CM102-A+/102S+ */
+ { QUIRK_ENTRY(0x0d8c, 0x0103, "PCM Playback Volume"),
+ QUIRK_MIN(-256) },
+
+ /* MS LifeChat LX-3000 Headset */
+ { QUIRK_ENTRY(0x045e, 0x070f, "Speaker Playback Volume"),
+ QUIRK_RES(192) },
+
+ /* QuickCam E3500 */
+ { QUIRK_ENTRY(0x046d, 0x09a4, "Mic Capture Volume"),
+ QUIRK_MINMAXRES(6080, 8768, 192) },
+
+ /* MOONDROP Quark2 */
+ { QUIRK_ENTRY(0x3302, 0x12db, "PCM Playback Volume"),
+ QUIRK_MIN(-14208) }, /* Mute under it */
+
+ /* Huawei Technologies Co., Ltd. CM-Q3 */
+ { QUIRK_ENTRY(0x12d1, 0x3a07, "PCM Playback Volume"),
+ QUIRK_MIN(-11264) }, /* Mute under it */
+
+ /* MOONDROP JU Jiu */
+ { QUIRK_ENTRY(0x31b2, 0x0111, "PCM Playback Volume"),
+ QUIRK_MIN(-10880) }, /* Mute under it */
+
+ {} /* terminator */
+};
+
/* volume control quirks */
static void volume_control_quirks(struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
{
struct snd_usb_audio *chip = cval->head.mixer->chip;
+ const struct mixer_volume_quirk *q;
if (chip->quirk_flags & QUIRK_FLAG_MIC_RES_384) {
if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
@@ -1090,71 +1186,21 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
}
- switch (chip->usb_id) {
- case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
- case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
- if (strcmp(kctl->id.name, "Effect Duration") == 0) {
- cval->min = 0x0000;
- cval->max = 0xffff;
- cval->res = 0x00e6;
- break;
- }
- if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
- strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
- cval->min = 0x00;
- cval->max = 0xff;
- break;
- }
- if (strstr(kctl->id.name, "Effect Return") != NULL) {
- cval->min = 0xb706;
- cval->max = 0xff7b;
- cval->res = 0x0073;
- break;
- }
- if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
- (strstr(kctl->id.name, "Effect Send") != NULL)) {
- cval->min = 0xb5fb; /* -73 dB = 0xb6ff */
- cval->max = 0xfcfe;
- cval->res = 0x0073;
- }
- break;
-
- case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
- case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
- if (strcmp(kctl->id.name, "Effect Duration") == 0) {
- usb_audio_info(chip,
- "set quirk for FTU Effect Duration\n");
- cval->min = 0x0000;
- cval->max = 0x7f00;
- cval->res = 0x0100;
+ for (q = mixer_volume_quirk_entries; q->id; q++) {
+ if (q->id == chip->usb_id &&
+ !strcmp(q->ctl_name, kctl->id.name)) {
+ if (q->to_update & VOL_QUIRK_MIN)
+ cval->min = q->min;
+ if (q->to_update & VOL_QUIRK_MAX)
+ cval->max = q->max;
+ if (q->to_update & VOL_QUIRK_RES)
+ cval->res = q->res;
break;
}
- if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
- strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
- usb_audio_info(chip,
- "set quirks for FTU Effect Feedback/Volume\n");
- cval->min = 0x00;
- cval->max = 0x7f;
- break;
- }
- break;
-
- case USB_ID(0x0d8c, 0x0103):
- if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
- usb_audio_info(chip,
- "set volume quirk for CM102-A+/102S+\n");
- cval->min = -256;
- }
- break;
-
- case USB_ID(0x045e, 0x070f): /* MS LifeChat LX-3000 Headset */
- if (!strcmp(kctl->id.name, "Speaker Playback Volume")) {
- usb_audio_info(chip,
- "set volume quirk for MS LifeChat LX-3000\n");
- cval->res = 192;
- }
- break;
+ }
+ /* other exceptional cases */
+ switch (chip->usb_id) {
case USB_ID(0x0471, 0x0101):
case USB_ID(0x0471, 0x0104):
case USB_ID(0x0471, 0x0105):
@@ -1172,16 +1218,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
break;
- case USB_ID(0x046d, 0x09a4):
- if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
- usb_audio_info(chip,
- "set volume quirk for QuickCam E3500\n");
- cval->min = 6080;
- cval->max = 8768;
- cval->res = 192;
- }
- break;
-
case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */
if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
strstr(kctl->id.name, "Capture Volume") != NULL) {
@@ -1190,27 +1226,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
cval->res = 1;
}
break;
- case USB_ID(0x3302, 0x12db): /* MOONDROP Quark2 */
- if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
- usb_audio_info(chip,
- "set volume quirk for MOONDROP Quark2\n");
- cval->min = -14208; /* Mute under it */
- }
- break;
- case USB_ID(0x12d1, 0x3a07): /* Huawei Technologies Co., Ltd. CM-Q3 */
- if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
- usb_audio_info(chip,
- "set volume quirk for Huawei Technologies Co., Ltd. CM-Q3\n");
- cval->min = -11264; /* Mute under it */
- }
- break;
- case USB_ID(0x31b2, 0x0111): /* MOONDROP JU Jiu */
- if (!strcmp(kctl->id.name, "PCM Playback Volume")) {
- usb_audio_info(chip,
- "set volume quirk for MOONDROP JU Jiu\n");
- cval->min = -10880; /* Mute under it */
- }
- break;
}
}