Re: [PATCH] ALSA: usb-audio: apply quirk for MOONDROP JU Jiu

From: Cryolitia PukNgae

Date: Fri Apr 10 2026 - 04:46:28 EST


Takashi Iwai <tiwai@xxxxxxx> 于2026年4月2日周四 21:00写道:
>
> 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.
>

Hi Takashi,

Thanks for trying this out.

I think the current quirk list is probably still too small to justify
a refactoring like this. For now, it may be better to keep the
existing code and revisit it later if more devices with similar quirks
show up.

Best regards,
Cryolitia PukNgae

>
> 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;
> }
> }
>