* [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini
@ 2026-10-19 20:18 Rong Zhang
2026-10-19 20:18 ` [PATCH 1/4] ALSA: usb-audio: Unify dB scale override Rong Zhang
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Rong Zhang @ 2026-10-19 20:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: linux-sound, linux-kernel, Takashi Iwai, Rong Zhang
I recently got two devices with insane volume control, so I tried to
measure their volume mapping and add dB scale quirks for them.
PATCH 1 collects the code snippets for dB scale override into a helper
function, with unified info and warning messages, and also deconflicts
playback dB scale quirks of DragonFly.
PATCH 2 applies the MV-SILICON linear volume quirk only to the affected
mixers, instead of the whole device.
PATCH 3 adds dB scale quirk for an AB13X variant.
PATCH 4 adds dB scale quirk for the Xiaomi Speaker Mini.
Signed-off-by: Rong Zhang <i@rong.moe>
---
Rong Zhang (4):
ALSA: usb-audio: Unify dB scale override
ALSA: usb-audio: Apply MV-SILICON linear volume quirk per mixer
ALSA: usb-audio: Add dB scale quirk for an AB13X variant
ALSA: usb-audio: Add dB scale quirk for Xiaomi Speaker Mini
sound/usb/mixer_quirks.c | 285 ++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 245 insertions(+), 40 deletions(-)
---
base-commit: c03c1a14507dfaf8b856c2608f9ac216021fc222
change-id: 87da826d-b4-uac-ya-audio-e8c0421aa2cf
Thanks,
Rong
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/4] ALSA: usb-audio: Unify dB scale override
2026-10-19 20:18 [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini Rong Zhang
@ 2026-10-19 20:18 ` Rong Zhang
2026-10-19 20:18 ` [PATCH 2/4] ALSA: usb-audio: Apply MV-SILICON linear volume quirk per mixer Rong Zhang
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Rong Zhang @ 2026-10-19 20:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: linux-sound, linux-kernel, Takashi Iwai, Rong Zhang
Currently, both snd_dragonfly_quirk_db_scale() and
snd_usb_mixer_fu_quirk_linear_scale() override dB scale.
Collect the code snippets for dB scale override into a helper function,
with unified info and warning messages. Also deconflict playback dB
scale quirks of DragonFly.
Signed-off-by: Rong Zhang <i@rong.moe>
---
sound/usb/mixer_quirks.c | 107 ++++++++++++++++++++++++++++++++---------------
1 file changed, 73 insertions(+), 34 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index fc622eb95dc5..e2f7ca4252e2 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4646,6 +4646,72 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
}
}
+static void snd_usb_apply_db_scale(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl,
+ const unsigned int *scale, const char *desc)
+{
+ if (!scale) {
+ usb_audio_info(mixer->chip, "clearing %s dB scale on [%s] (%d/%d/%d)\n",
+ desc, kctl->id.name, cval->min, cval->max, cval->res);
+
+ kctl->tlv.p = NULL;
+ kctl->vd[0].access &= ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
+ return;
+ }
+
+ usb_audio_info(mixer->chip, "applying %s dB scale quirk on [%s] (%d/%d/%d)\n",
+ desc, kctl->id.name, cval->min, cval->max, cval->res);
+
+ if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ &&
+ !(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) {
+ usb_audio_warn(mixer->chip,
+ "%s dB scale quirk overrides the previously applied one on [%s]\n",
+ desc, kctl->id.name);
+ } else if (cval->min_mute) {
+ /*
+ * We are clearing SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ * resulting in min_mute becoming a no-op.
+ */
+ usb_audio_warn(mixer->chip, "%s dB scale quirk overrides MIN_MUTE on [%s]\n",
+ desc, kctl->id.name);
+ }
+
+ kctl->tlv.p = scale;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+}
+
+static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval,
+ struct snd_kcontrol *kctl)
+{
+ static const DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0);
+
+ snd_usb_apply_db_scale(mixer, cval, kctl, scale, "linear volume");
+}
+
+/* Respect quirks set by module parameters or quirk flags table. */
+static inline bool deconflict_playback_tlv_quirks(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
+{
+ const u64 playback_quirk_flags = (QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE |
+ QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL);
+
+ return (!(mixer->chip->quirk_flags & playback_quirk_flags) &&
+ strstr(kctl->id.name, "Playback"));
+}
+
+static inline bool deconflict_capture_tlv_quirks(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
+{
+ const u64 capture_quirk_flags = (QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE |
+ QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL);
+
+ return (!(mixer->chip->quirk_flags & capture_quirk_flags) &&
+ strstr(kctl->id.name, "Capture"));
+}
+
static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
@@ -4666,18 +4732,16 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
);
- if (cval->min == 0 && cval->max == 50) {
- usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n");
- kctl->tlv.p = scale;
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ if (!deconflict_playback_tlv_quirks(mixer, kctl))
+ return;
+ if (cval->min == 0 && cval->max == 50) {
+ snd_usb_apply_db_scale(mixer, cval, kctl, scale, "DragonFly");
} else if (cval->min == 0 && cval->max <= 1000) {
/* Some other clearly broken DragonFly variant.
* At least a 0..53 variant (hw v1.0) exists.
*/
- usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device");
- kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ snd_usb_apply_db_scale(mixer, cval, kctl, NULL, "DragonFly");
}
}
@@ -4745,25 +4809,6 @@ static void snd_fix_plt_name(struct snd_usb_audio *chip,
usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name);
}
-static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer,
- struct usb_mixer_elem_info *cval,
- struct snd_kcontrol *kctl)
-{
- static const DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0);
-
- if (cval->min_mute) {
- /*
- * We are clearing SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
- * resulting in min_mute being a no-op.
- */
- usb_audio_warn(mixer->chip, "LINEAR_VOL overrides MIN_MUTE\n");
- }
-
- kctl->tlv.p = scale;
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
-}
-
void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval, int unitid,
struct snd_kcontrol *kctl)
@@ -4796,18 +4841,12 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
}
if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL)
- if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Playback")) {
- usb_audio_info(mixer->chip,
- "applying playback linear volume quirk\n");
+ if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Playback"))
snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
- }
if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL)
- if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Capture")) {
- usb_audio_info(mixer->chip,
- "applying capture linear volume quirk\n");
+ if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Capture"))
snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
- }
/* ALSA-ify some Plantronics headset control names */
if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f &&
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/4] ALSA: usb-audio: Apply MV-SILICON linear volume quirk per mixer
2026-10-19 20:18 [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini Rong Zhang
2026-10-19 20:18 ` [PATCH 1/4] ALSA: usb-audio: Unify dB scale override Rong Zhang
@ 2026-10-19 20:18 ` Rong Zhang
2026-10-19 20:18 ` [PATCH 3/4] ALSA: usb-audio: Add dB scale quirk for an AB13X variant Rong Zhang
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Rong Zhang @ 2026-10-19 20:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: linux-sound, linux-kernel, Takashi Iwai, Rong Zhang
The snd_usb_mv_silicon_quirks() tries to apply both
MIXER_PLAYBACK_LINEAR_VOL and MIXER_CAPTURE_LINEAR_VOL on the device at
the same time, as long as one of the mixers fulfills val == 0/4096/1.
IOW, even if only one of them matches the control values, the linear
volume quirk applies to all mixers.
Apply the quirk by calling snd_usb_mixer_fu_quirk_linear_scale()
directly, so that the MV-SILICON quirk becomes mixer-wise. The existing
quirk flags are also checked to respect conflicting quirks.
Fixes: dfd4b0d46e77 ("ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices")
Signed-off-by: Rong Zhang <i@rong.moe>
---
sound/usb/mixer_quirks.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index e2f7ca4252e2..7e2cfb176348 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4753,13 +4753,11 @@ static void snd_usb_mv_silicon_quirks(struct usb_mixer_interface *mixer,
/* The final effects will be printed later. */
usb_audio_info(mixer->chip, "applying MV-SILICON quirks (0/4096/1 variant)\n");
- /* Respect MIN_MUTE set by module parameters. */
- if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE))
- mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL;
- if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE))
- mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL;
+ if (deconflict_playback_tlv_quirks(mixer, kctl) ||
+ deconflict_capture_tlv_quirks(mixer, kctl))
+ snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
} else {
- usb_audio_dbg(mixer->chip, "not applying MV-SILICON quirks on unknown variant");
+ usb_audio_dbg(mixer->chip, "not applying MV-SILICON quirks on unknown variant\n");
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/4] ALSA: usb-audio: Add dB scale quirk for an AB13X variant
2026-10-19 20:18 [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini Rong Zhang
2026-10-19 20:18 ` [PATCH 1/4] ALSA: usb-audio: Unify dB scale override Rong Zhang
2026-10-19 20:18 ` [PATCH 2/4] ALSA: usb-audio: Apply MV-SILICON linear volume quirk per mixer Rong Zhang
@ 2026-10-19 20:18 ` Rong Zhang
2026-10-19 20:18 ` [PATCH 4/4] ALSA: usb-audio: Add dB scale quirk for Xiaomi Speaker Mini Rong Zhang
2026-10-20 13:09 ` [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and " Takashi Iwai
4 siblings, 0 replies; 6+ messages in thread
From: Rong Zhang @ 2026-10-19 20:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: linux-sound, linux-kernel, Takashi Iwai, Rong Zhang
I recently got an AB13X variant with insane volume control, so I tried
to measure its volume mapping @ 1000Hz:
+--------+--------+-----------+---------+----------+--------+
| hex | dec | dec => dB | mVrms * | mV => dB | res |
+--------+--------+-----------+---------+----------+--------+
| 0xd300 | -11520 | -45.00 | | | |
| 0xdb5a | -9382 | -36.65 | 1.285 | -26.68 | 2138 # |
| 0xe2d0 | -7472 | -29.19 | 4.104 | -21.63 | 1910 # |
| 0xe7cc | -6196 | -24.20 | 13.693 | -16.40 | 1276 # |
| 0xeb8c | -5236 | -20.45 | 26.71 | -13.50 | 960 |
| 0xee8d | -4467 | -17.45 | 42.29 | -11.50 | 769 |
| 0xf10e | -3826 | -14.95 | 66.81 | -9.52 | 641 |
| 0xf335 | -3275 | -12.79 | 94.43 | -8.01 | 551 |
| 0xf517 | -2793 | -10.91 | 133.25 | -6.52 | 482 |
| 0xf6c5 | -2363 | -9.23 | 168.73 | -5.49 | 430 |
| 0xf847 | -1977 | -7.72 | 212.05 | -4.50 | 386 |
| 0xf9a7 | -1625 | -6.35 | 267.1 | -3.50 | 352 |
| 0xfae9 | -1303 | -5.09 | 336.3 | -2.50 | 322 |
| 0xfc13 | -1005 | -3.93 | 377.4 | -2.00 | 298 |
| 0xfd27 | -729 | -2.85 | 423.6 | -1.50 | 276 |
| 0xfe29 | -471 | -1.84 | 475.3 | -1.00 | 258 |
| 0xff69 | -151 | -0.59 | 533.1 | -0.50 | 320 |
| 0x0000 | 0 | 0.00 | 597.7 | 0.00 | 151 |
+--------+--------+-----------+---------+----------+--------+
Footnote *: measured through a power amplifier's loopback line out
to ensure high input impedance (see #). The raw voltage at 0x0000
without going through a power amplifier is 677mVrms.
Footnote #: low output power. Even the impedance of a multimeter is
too low, causing a massive voltage drop (-10dB!), which means that
they are inaudible when connected to earphones. Do not expose them.
The volume control mixer has uneven resolutions in reality. Setting any
value between [0xd300, 0xdb5a] results in the same audible volume as
0xdb5a, (0xdb5a, 0xe2d0] results in the same audible volume as 0xe2d0,
and so on. The read back value always acts like res == 1.
Carefully overrides cval->{min,max,res} so that volume steps with
0.5dB or 1.0dB resolution can be approximately represented togother.
With that, we can expose a 19-step TLV range mapping with 7 items.
The capture mixer is also quirky and requires linear volume.
The AB13X seems to have a lot of variants, probably because it's a OEM
product. I believe mine is one of them considering that its idVendor,
idProduct, Manufacturer and SerialNumber are the same as the original
AB13X. Only the Product string differs.
Both quirks are applied only when the mixer values match the seen ones,
so that they won't be accidentally applied on unaffected variants.
Quirky device sample:
usb 7-1: new full-speed USB device number 2 using xhci_hcd
usb 7-1: not running at top speed; connect to a high speed hub
usb 7-1: New USB device found, idVendor=001f, idProduct=0b21, bcdDevice= 1.00
usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 7-1: Product: YA USB Audio
usb 7-1: Manufacturer: Generic
usb 7-1: SerialNumber: 20210726905926
usb 7-1: From builtin table apply quirk flag IFACE_DELAY for device 001f:0b21
usb 7-1: From builtin table apply quirk flag FORCE_IFACE_RESET for device 001f:0b21
usb 7-1: Found last interface = 0
usb 7-1: 1:1: add audio endpoint 0x3
usb 7-1: Creating new data endpoint #3
usb 7-1: 1:1 Set sample rate 48000, clock 0
usb 7-1: 2:1: add audio endpoint 0x83
usb 7-1: Creating new data endpoint #83
usb 7-1: 2:1 Set sample rate 48000, clock 0
usb 7-1: [2] FU [PCM Playback Switch] ch = 1, val = 0/1/1
usb 7-1: [2] FU [PCM Playback Volume] ch = 1, val = -11520/0/1
usb 7-1: [5] FU [Mic Capture Switch] ch = 1, val = 0/1/1
usb 7-1: Warning! Unlikely big volume step count (=8191), linear volume or wrong cval->res?
usb 7-1: [5] FU [Mic Capture Volume] ch = 1, val = 0/8191/1
input: Generic YA USB Audio as /devices/pci0000:00/0000:00:08.3/0000:67:00.4/usb7/7-1/7-1:1.3/0003:001F:0B21.000F/input/input41
hid-generic 0003:001F:0B21.000F: input,hidraw12: USB HID v2.01 Device [Generic YA USB Audio] on usb-0000:67:00.4-1/input3
Signed-off-by: Rong Zhang <i@rong.moe>
---
sound/usb/mixer_quirks.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 7e2cfb176348..6cdd3ee94d66 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4682,6 +4682,26 @@ static void snd_usb_apply_db_scale(struct usb_mixer_interface *mixer,
kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
+static void snd_usb_apply_db_scale_val(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl,
+ const unsigned int *scale, const char *desc,
+ int min, int max, int res)
+{
+ snd_usb_apply_db_scale(mixer, cval, kctl, scale, desc);
+
+ usb_audio_info(mixer->chip,
+ "overriding [%s] val (%d/%d/%d => %d/%d/%d) to fit %s dB scale quirk\n",
+ kctl->id.name, cval->min, cval->max, cval->res, min, max, res, desc);
+
+ /*
+ * We are not fixing the cached values, as get_relative_value() will
+ * ensure userspace always sees compliant ones.
+ */
+ cval->min = min;
+ cval->max = max;
+ cval->res = res;
+}
+
static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
@@ -4745,6 +4765,81 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
}
}
+static void snd_ab13x_quirks(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval,
+ struct snd_kcontrol *kctl)
+{
+ /*
+ * Volume mapping based on voltage level measurement @ 1000Hz:
+ * +--------+--------+-----------+---------+----------+--------+
+ * | hex | dec | dec => dB | mVrms * | mV => dB | res |
+ * +--------+--------+-----------+---------+----------+--------+
+ * | 0xd300 | -11520 | -45.00 | | | |
+ * | 0xdb5a | -9382 | -36.65 | 1.285 | -26.68 | 2138 # |
+ * | 0xe2d0 | -7472 | -29.19 | 4.104 | -21.63 | 1910 # |
+ * | 0xe7cc | -6196 | -24.20 | 13.693 | -16.40 | 1276 # |
+ * | 0xeb8c | -5236 | -20.45 | 26.71 | -13.50 | 960 |
+ * | 0xee8d | -4467 | -17.45 | 42.29 | -11.50 | 769 |
+ * | 0xf10e | -3826 | -14.95 | 66.81 | -9.52 | 641 |
+ * | 0xf335 | -3275 | -12.79 | 94.43 | -8.01 | 551 |
+ * | 0xf517 | -2793 | -10.91 | 133.25 | -6.52 | 482 |
+ * | 0xf6c5 | -2363 | -9.23 | 168.73 | -5.49 | 430 |
+ * | 0xf847 | -1977 | -7.72 | 212.05 | -4.50 | 386 |
+ * | 0xf9a7 | -1625 | -6.35 | 267.1 | -3.50 | 352 |
+ * | 0xfae9 | -1303 | -5.09 | 336.3 | -2.50 | 322 |
+ * | 0xfc13 | -1005 | -3.93 | 377.4 | -2.00 | 298 |
+ * | 0xfd27 | -729 | -2.85 | 423.6 | -1.50 | 276 |
+ * | 0xfe29 | -471 | -1.84 | 475.3 | -1.00 | 258 |
+ * | 0xff69 | -151 | -0.59 | 533.1 | -0.50 | 320 |
+ * | 0x0000 | 0 | 0.00 | 597.7 | 0.00 | 151 |
+ * +--------+--------+-----------+---------+----------+--------+
+ *
+ * Footnote *: measured through a power amplifier's loopback line out
+ * to ensure high input impedance (see #). The raw voltage at 0x0000
+ * without going through a power amplifier is 677mVrms.
+ *
+ * Footnote #: low output power. Even the impedance of a multimeter is
+ * too low, causing a massive voltage drop (-10dB!), which means that
+ * they are inaudible when connected to earphones. Do not expose them.
+ *
+ * The volume control mixer has uneven resolutions in reality. Setting
+ * any value between [0xd300, 0xdb5a] results in the same audible volume
+ * as 0xdb5a, (0xdb5a, 0xe2d0] results in the same audible volume as
+ * 0xe2d0, and so on. The read back value always acts like res == 1.
+ */
+ static const DECLARE_TLV_DB_RANGE(scale,
+ 0, 0, TLV_DB_SCALE_ITEM(-1350, 0, 0), /* -5500 */
+ 1, 3, TLV_DB_SCALE_ITEM(-1150, 0, 0), /* -5200, -4900, -4600 */
+ 4, 5, TLV_DB_SCALE_ITEM( -952, 0, 0), /* -4300, -4000 */
+ 6, 7, TLV_DB_SCALE_ITEM( -801, 0, 0), /* -3700, -3400 */
+ 8, 9, TLV_DB_SCALE_ITEM( -652, 0, 0), /* -3100, -2800 */
+ 10, 13, TLV_DB_MINMAX_ITEM(-550, -250), /* -2500, -2200, -1900, -1600 */
+ 14, 18, TLV_DB_MINMAX_ITEM(-200, 0), /* -1300, -1000, -700, -400, -100 */
+ );
+
+ if (cval->min == -11520 && cval->max == 0 && cval->res == 1) {
+ if (!deconflict_playback_tlv_quirks(mixer, kctl))
+ return;
+
+ /*
+ * These values are carefully chosen so that volume steps with
+ * 0.5dB or 1.0dB resolution can be approximately represented
+ * togother.
+ *
+ * It's literally a miracle that these values can flawlessly
+ * align to 100.
+ */
+ snd_usb_apply_db_scale_val(mixer, cval, kctl, scale, "AB13X", -5500, -100, 300);
+ } else if (cval->min == 0 && cval->max == 8191 && cval->res == 1) {
+ /* The final effects will be printed later. */
+ usb_audio_info(mixer->chip, "applying AB13X Capture quirk (0/8191/1 variant)\n");
+
+ if (deconflict_capture_tlv_quirks(mixer, kctl))
+ snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
+ } else {
+ usb_audio_dbg(mixer->chip, "not applying AB13X quirk on unknown variant\n");
+ }
+}
+
static void snd_usb_mv_silicon_quirks(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
@@ -4812,6 +4907,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
struct snd_kcontrol *kctl)
{
switch (mixer->chip->usb_id) {
+ case USB_ID(0x001f, 0x0b21): /* AB13X Headset Adapter */
+ if (cval->control == UAC_FU_VOLUME)
+ snd_ab13x_quirks(mixer, cval, kctl);
+ break;
case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
if (unitid == 7 && cval->control == UAC_FU_VOLUME)
snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 4/4] ALSA: usb-audio: Add dB scale quirk for Xiaomi Speaker Mini
2026-10-19 20:18 [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini Rong Zhang
` (2 preceding siblings ...)
2026-10-19 20:18 ` [PATCH 3/4] ALSA: usb-audio: Add dB scale quirk for an AB13X variant Rong Zhang
@ 2026-10-19 20:18 ` Rong Zhang
2026-10-20 13:09 ` [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and " Takashi Iwai
4 siblings, 0 replies; 6+ messages in thread
From: Rong Zhang @ 2026-10-19 20:18 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: linux-sound, linux-kernel, Takashi Iwai, Rong Zhang
I recently brought an Xiaomi Speaker Mini (USB Product string, marketing
name: Xiaomi Bluetooth Speaker Mini), which is never advertised as UAC
capable.
By chance, I connected it to my PC, and a UAC device showed up.
Unsurprisingly, it's yet another device with insane volume control, so I
tried to measure its volume mapping @ 1000Hz with a microphone:
+--------+--------+-----------+-------------+--------+
| hex | dec | dec => dB | measured dB | res |
+--------+--------+-----------+-------------+--------+
| 0xd000 | -12288 | -48.00 | (mute) | |
| 0xdd00 | -8960 | -35.00 | -68.6 | 3328 # |
| 0xe540 | -6848 | -26.75 | -61.2 | 2112 |
| 0xea40 | -5568 | -21.75 | -57.0 | 1280 |
| 0xed80 | -4736 | -18.50 | -52.5 | 832 |
| 0xf0a0 | -3936 | -15.38 | -48.3 | 800 |
| 0xf2d0 | -3376 | -13.19 | -42.1 | 560 |
| 0xf4d0 | -2864 | -11.19 | -36.1 | 512 |
| 0xf6c0 | -2368 | -9.25 | -32.0 | 496 |
| 0xf840 | -1984 | -7.75 | -24.0 | 384 |
| 0xf9e0 | -1568 | -6.13 | -17.9 | 416 |
| 0xfb30 | -1232 | -4.81 | -13.9 | 336 |
| 0xfc80 | -896 | -3.50 | -10.0 | 336 |
| 0xfd90 | -624 | -2.44 | -6.0 | 272 |
| 0xfeb0 | -336 | -1.31 | -4.0 | 288 |
| 0xffb0 | -80 | -0.31 | -2.0 | 256 |
| 0x0000 | 0 | 0.00 | 0.0 | 80 |
+--------+--------+-----------+-------------+--------+
Footnote #: running out of the 10-item limit of TLV ranges. The
limitation is from alsa-lib, where MAX_TLV_RANGE_SIZE == 256 Bytes.
The volume control mixer declares res == 256, but actually acts like
res == 1 (i.e., accepts any value between min and max) and has uneven
resolutions in reality. Setting any value between (0xd000, 0xdd00]
results in the same audible volume as 0xdd00, and so on.
Carefully overrides cval->{min,max,res} so that volume steps with
2dB or 4dB resolution can be approximately represented togother.
With that, we can expose a 25-step TLV range mapping with 10 items.
The quirk is applied only when the mixer values match the seen ones,
so that it won't be accidentally applied to unseen variants.
Quirky device sample:
usb 3-1: new full-speed USB device number 14 using xhci_hcd
usb 3-1: New USB device found, idVendor=2717, idProduct=5096, bcdDevice= 1.00
usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 3-1: Product: Xiaomi Speaker Mini-****
usb 3-1: Manufacturer: bestechnic
usb 3-1: SerialNumber: 20160406.1
usb 3-1: Found last interface = 0
usb 3-1: 1:1: add audio endpoint 0x3
usb 3-1: Creating new data endpoint #3
usb 3-1: 1:1 Set sample rate 48000, clock 0
usb 3-1: [5] FU [PCM Playback Switch] ch = 1, val = 0/1/1
usb 3-1: cannot set ctl value: req = 0x4, wValue = 0x200, wIndex = 0x500, type = 4, data = 0x80/0x0
usb 3-1: [5] FU [PCM Playback Volume] ch = 1, val = -12288/0/256
input: bestechnic Xiaomi Speaker Mini-2421 as /devices/pci0000:00/0000:00:08.3/0000:67:00.0/usb3/3-1/3-1:1.2/0003:2717:5096.002A/input/input69
hid-generic 0003:2717:5096.002A: input,hidraw10: USB HID v1.11 Device [bestechnic Xiaomi Speaker Mini-2421] on usb-0000:67:00.0-1/input2
(**** is the last four digits of the device's serial number)
Signed-off-by: Rong Zhang <i@rong.moe>
---
sound/usb/mixer_quirks.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 6cdd3ee94d66..3534f6fad74b 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4840,6 +4840,71 @@ static void snd_ab13x_quirks(struct usb_mixer_interface *mixer, struct usb_mixer
}
}
+static void snd_xiaomi_speaker_mini_quirk_db_scale(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval,
+ struct snd_kcontrol *kctl)
+{
+ /*
+ * Volume mapping based on normalized microphone measurement @ 1000Hz:
+ * +--------+--------+-----------+-------------+--------+
+ * | hex | dec | dec => dB | measured dB | res |
+ * +--------+--------+-----------+-------------+--------+
+ * | 0xd000 | -12288 | -48.00 | (mute) | |
+ * | 0xdd00 | -8960 | -35.00 | -68.6 | 3328 # |
+ * | 0xe540 | -6848 | -26.75 | -61.2 | 2112 |
+ * | 0xea40 | -5568 | -21.75 | -57.0 | 1280 |
+ * | 0xed80 | -4736 | -18.50 | -52.5 | 832 |
+ * | 0xf0a0 | -3936 | -15.38 | -48.3 | 800 |
+ * | 0xf2d0 | -3376 | -13.19 | -42.1 | 560 |
+ * | 0xf4d0 | -2864 | -11.19 | -36.1 | 512 |
+ * | 0xf6c0 | -2368 | -9.25 | -32.0 | 496 |
+ * | 0xf840 | -1984 | -7.75 | -24.0 | 384 |
+ * | 0xf9e0 | -1568 | -6.13 | -17.9 | 416 |
+ * | 0xfb30 | -1232 | -4.81 | -13.9 | 336 |
+ * | 0xfc80 | -896 | -3.50 | -10.0 | 336 |
+ * | 0xfd90 | -624 | -2.44 | -6.0 | 272 |
+ * | 0xfeb0 | -336 | -1.31 | -4.0 | 288 |
+ * | 0xffb0 | -80 | -0.31 | -2.0 | 256 |
+ * | 0x0000 | 0 | 0.00 | 0.0 | 80 |
+ * +--------+--------+-----------+-------------+--------+
+ *
+ * The volume control mixer declares res == 256, but actually acts like
+ * res == 1 (i.e., accepts any value between min and max) and has uneven
+ * resolutions in reality. Setting any value between (0xd000, 0xdd00]
+ * results in the same audible volume as 0xdd00, and so on.
+ *
+ * Footnote #: running out of the 10-item limit of TLV ranges.
+ */
+ static const DECLARE_TLV_DB_RANGE(scale,
+ 0, 0, TLV_DB_SCALE_ITEM( -6120, 0, 0), /* -7104 */
+ 1, 5, TLV_DB_SCALE_ITEM( -5700, 0, 0), /* -6808, -6512, -6216, -5920, -5624 */
+ 6, 8, TLV_DB_SCALE_ITEM( -5250, 0, 0), /* -5328, -5032, -4736 */
+ 9, 10, TLV_DB_SCALE_ITEM( -4830, 0, 0), /* -4440, -4144 */
+ 11, 12, TLV_DB_SCALE_ITEM( -4210, 0, 0), /* -3848, -3552 */
+ 13, 14, TLV_DB_SCALE_ITEM( -3610, 0, 0), /* -3256, -2960 */
+ 15, 16, TLV_DB_SCALE_ITEM( -3200, 0, 0), /* -2664, -2368 */
+ 17, 18, TLV_DB_MINMAX_ITEM(-2400, -1790), /* -2072, -1776 */
+ 19, 21, TLV_DB_MINMAX_ITEM(-1390, -600), /* -1480, -1184, -888 */
+ 22, 24, TLV_DB_MINMAX_ITEM( -400, 0), /* -592, -296, 0 */
+ );
+
+ if (cval->min == -12288 && cval->max == 0 && cval->res == 256) {
+ if (!deconflict_playback_tlv_quirks(mixer, kctl))
+ return;
+
+ /*
+ * These values are carefully chosen so that volume steps with
+ * 2dB or 4dB resolution can be approximately represented
+ * togother.
+ */
+ snd_usb_apply_db_scale_val(mixer, cval, kctl, scale,
+ "Xiaomi Speaker Mini", -7104, 0, 296);
+ } else {
+ usb_audio_dbg(mixer->chip,
+ "not applying Xiaomi Speaker Mini quirk on unknown variant\n");
+ }
+}
+
static void snd_usb_mv_silicon_quirks(struct usb_mixer_interface *mixer,
struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
@@ -4915,6 +4980,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
if (unitid == 7 && cval->control == UAC_FU_VOLUME)
snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
break;
+ case USB_ID(0x2717, 0x5096): /* Xiaomi Speaker Mini */
+ if (cval->control == UAC_FU_VOLUME)
+ snd_xiaomi_speaker_mini_quirk_db_scale(mixer, cval, kctl);
+ break;
}
if (cval->control == UAC_FU_VOLUME &&
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini
2026-10-19 20:18 [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini Rong Zhang
` (3 preceding siblings ...)
2026-10-19 20:18 ` [PATCH 4/4] ALSA: usb-audio: Add dB scale quirk for Xiaomi Speaker Mini Rong Zhang
@ 2026-10-20 13:09 ` Takashi Iwai
4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2026-10-20 13:09 UTC (permalink / raw)
To: Rong Zhang
Cc: Jaroslav Kysela, Takashi Iwai, linux-sound, linux-kernel, Takashi Iwai
On Mon, 19 Oct 2026 22:18:21 +0200,
Rong Zhang wrote:
>
> I recently got two devices with insane volume control, so I tried to
> measure their volume mapping and add dB scale quirks for them.
>
> PATCH 1 collects the code snippets for dB scale override into a helper
> function, with unified info and warning messages, and also deconflicts
> playback dB scale quirks of DragonFly.
>
> PATCH 2 applies the MV-SILICON linear volume quirk only to the affected
> mixers, instead of the whole device.
>
> PATCH 3 adds dB scale quirk for an AB13X variant.
>
> PATCH 4 adds dB scale quirk for the Xiaomi Speaker Mini.
>
> Signed-off-by: Rong Zhang <i@rong.moe>
> ---
> Rong Zhang (4):
> ALSA: usb-audio: Unify dB scale override
> ALSA: usb-audio: Apply MV-SILICON linear volume quirk per mixer
> ALSA: usb-audio: Add dB scale quirk for an AB13X variant
> ALSA: usb-audio: Add dB scale quirk for Xiaomi Speaker Mini
JFYI, I postpone this for 7.5, as 7.4 merge window was already opened,
and the code change amount is slightly too big as a last-minute fix.
thanks,
Takashi
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-20 13:09 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-19 20:18 [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and Xiaomi Speaker Mini Rong Zhang
2026-10-19 20:18 ` [PATCH 1/4] ALSA: usb-audio: Unify dB scale override Rong Zhang
2026-10-19 20:18 ` [PATCH 2/4] ALSA: usb-audio: Apply MV-SILICON linear volume quirk per mixer Rong Zhang
2026-10-19 20:18 ` [PATCH 3/4] ALSA: usb-audio: Add dB scale quirk for an AB13X variant Rong Zhang
2026-10-19 20:18 ` [PATCH 4/4] ALSA: usb-audio: Add dB scale quirk for Xiaomi Speaker Mini Rong Zhang
2026-10-20 13:09 ` [PATCH 0/4] ALSA: usb-audio: Add dB scale quirks for AB13X and " Takashi Iwai
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®