mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] ALSA: usb-audio: Add quirk for inverted sample rates on NUX NAI-24
@ 2026-09-20 12:26 Darren Chang
  0 siblings, 0 replies; only message in thread
From: Darren Chang @ 2026-09-20 12:26 UTC (permalink / raw)
  To: perex, tiwai; +Cc: corbet, skhan, rdunlap, linux-sound, linux-doc, linux-kernel

The NUX NAI-24 (USB 3703:2000) has a UAC2 clock source that reports
bmAttributes = 0x01 (internal fixed clock) and bmControls = 0x07, so the
driver treats its sample rate as programmable.  When the driver sends
SET_CUR(SAMPLING_FREQ_CONTROL), the firmware acknowledges the request and
then runs the clock on the opposite base-rate family: asking for 44100 Hz
makes the device run at 48000 Hz, and asking for 48000 Hz makes it run at
44100 Hz.

The result is playback that is about 8.8% fast, or 8.4% slow with heavy
static on the 48000 Hz family.  macOS and Windows ignore bmControls, treat
the clock as fixed and resample, so they are unaffected.

Work around it by sending the partner rate in SET_CUR for this device, so
the device runs at the requested rate.  A new QUIRK_FLAG_SWAP_RATES flag
controls this, applied through the quirk flags table.

Only the 44.1/48 kHz pair has been verified on hardware; the 88.2/96 kHz
and 176.4/192 kHz pairs are untested but follow the same pattern.

Testing: the equivalent change ran on the device as a locally built
module (44100 Hz PCM, device clock 44100 Hz, no xruns, correct tempo, no
static).  This upstream form compiles for sound/usb without warnings and
passes checkpatch, but has not been built in a full kernel tree or
load-tested.

Assisted-by: LLM
Signed-off-by: Darren Chang <darrenchangjr01@gmail.com>
---
 Documentation/sound/alsa-configuration.rst |  6 +++++
 sound/usb/clock.c                          | 27 ++++++++++++++++++++++
 sound/usb/quirks.c                         |  3 +++
 sound/usb/usbaudio.h                       |  6 +++++
 4 files changed, 42 insertions(+)

diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst
index e500ec84e..689eccf6a 100644
--- a/Documentation/sound/alsa-configuration.rst
+++ b/Documentation/sound/alsa-configuration.rst
@@ -2407,6 +2407,12 @@ quirk_flags
           the requested rate.  A device advertising a single rate is otherwise
           never sent the request at all, and some require it before streaming
           will start.
+        * bit 33: ``swap_rates``
+          The clock source applies a requested rate to the other base-rate
+          family, i.e. asking for 44100 makes the device run at 48000 and vice
+          versa (same for 88200 <-> 96000 and 176400 <-> 192000).  The driver
+          sends the partner rate in SET_CUR so the device ends up running at the
+          requested one.
 
 This module supports multiple devices, autoprobe and hotplugging.
 
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 34832183a..cc77eace7 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -549,6 +549,30 @@ static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface,
 	return le32_to_cpu(data);
 }
 
+/*
+ * Return the partner rate in the other base-rate family.
+ * See QUIRK_FLAG_SWAP_RATES.
+ */
+static unsigned int swap_base_rate(unsigned int rate)
+{
+	switch (rate) {
+	case 44100:
+		return 48000;
+	case 48000:
+		return 44100;
+	case 88200:
+		return 96000;
+	case 96000:
+		return 88200;
+	case 176400:
+		return 192000;
+	case 192000:
+		return 176400;
+	default:
+		return rate;
+	}
+}
+
 /*
  * Try to set the given sample rate:
  *
@@ -585,6 +609,9 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
 	if (!writeable)
 		return 0;
 
+	if (chip->quirk_flags & QUIRK_FLAG_SWAP_RATES)
+		rate = swap_base_rate(rate);
+
 	data = cpu_to_le32(rate);
 	err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC2_CS_CUR,
 			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c5870901a..03e3e5ae4 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2550,6 +2550,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
 		   QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
 	DEVICE_FLG(0x36f9, 0xc009, /* XIBERIA K03S */
 		   QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
+	DEVICE_FLG(0x3703, 0x2000, /* NUX NAI-24 */
+		   QUIRK_FLAG_SWAP_RATES),
 	DEVICE_FLG(0x3c20, 0x3d21, /* AB13X USB Audio */
 		   QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
 	DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
@@ -2675,6 +2677,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
 	QUIRK_STRING_ENTRY(MIXER_GET_CUR_OK),
 	QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP),
 	QUIRK_STRING_ENTRY(ALWAYS_SET_RATE),
+	QUIRK_STRING_ENTRY(SWAP_RATES),
 	NULL
 };
 
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 244364739..c6d59218d 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -258,6 +258,10 @@ extern bool snd_usb_skip_validation;
  *  Issue SET_CUR for the sample rate even when the clock already reports the
  *  requested rate.  A device advertising a single rate is otherwise never sent
  *  the request at all, and some require it before streaming will start.
+ * QUIRK_FLAG_SWAP_RATES
+ *  The device applies a requested sample rate to the other base-rate family
+ *  (44100 <-> 48000, 88200 <-> 96000, 176400 <-> 192000).  Send the partner
+ *  rate in SET_CUR so that the device runs at the requested rate.
  */
 
 enum {
@@ -294,6 +298,7 @@ enum {
 	QUIRK_TYPE_MIXER_GET_CUR_OK		= 30,
 	QUIRK_TYPE_PLAYBACK_URB_FIXUP		= 31,
 	QUIRK_TYPE_ALWAYS_SET_RATE		= 32,
+	QUIRK_TYPE_SWAP_RATES			= 33,
 /* Please also edit snd_usb_audio_quirk_flag_names and alsa-configuration.rst */
 };
 
@@ -332,5 +337,6 @@ enum {
 #define QUIRK_FLAG_MIXER_GET_CUR_OK		QUIRK_FLAG(MIXER_GET_CUR_OK)
 #define QUIRK_FLAG_PLAYBACK_URB_FIXUP		QUIRK_FLAG(PLAYBACK_URB_FIXUP)
 #define QUIRK_FLAG_ALWAYS_SET_RATE		QUIRK_FLAG(ALWAYS_SET_RATE)
+#define QUIRK_FLAG_SWAP_RATES			QUIRK_FLAG(SWAP_RATES)
 
 #endif /* __USBAUDIO_H */
-- 
2.55.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-20 12:27 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-20 12:26 [PATCH] ALSA: usb-audio: Add quirk for inverted sample rates on NUX NAI-24 Darren Chang

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®