summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZhang Heng <zhangheng@kylinos.cn>2026-07-28 19:13:09 +0800
committerTakashi Iwai <tiwai@suse.de>2026-07-28 18:31:45 +0200
commitb7adaa94e336f3b062ab85131d299d09e6d7ff47 (patch)
tree1798e1696bf13e267aef052ef305f65a8ff4818c
parent46a6393e96b1a9a08fc53ee2ce9485238a06da13 (diff)
downloadlinux-next-b7adaa94e336f3b062ab85131d299d09e6d7ff47.tar.gz
linux-next-b7adaa94e336f3b062ab85131d299d09e6d7ff47.zip
ALSA: usb-audio: Fix boot-time audio stuttering for USB Audio device
This USB Audio device (0x1e0b:0xd01e) exhibits audio stuttering during boot when playing audio. Once the system is fully booted, playback is normal. The device reports its isochronous endpoints with the Asynchronous sync type (bmAttributes = 0x03), which causes the driver to calculate nurbs = min(max_urbs, ...) = 3, providing only ~16ms of buffering. During boot, the higher system scheduling jitter (e.g., from init scripts, device enumeration, and driver probing) can exceed this buffer depth, causing audible stuttering. This patch adds a device-specific quirk (QUIRK_FLAG_PLAYBACK_URB_FIXUP) that applies two changes for this device: 1. Forces nurbs to MAX_URBS (12), providing sufficient buffering 2. Sets URB_ISO_ASAP flag for more consistent xHCI scheduling Both changes are required together for stable boot-time playback: - The larger buffer absorbs scheduling jitter during boot - URB_ISO_ASAP ensures consistent URB submission timing, preventing the xHCI scheduler from introducing variable delays Test methodology: - Without patch: reboot and play audio → stuttering audible in all tests (reproduced consistently across multiple attempts) - With nurbs=8 only: occasional minor stuttering observed after multiple tests (insufficient buffer depth) - With full patch (nurbs=12 + URB_ISO_ASAP): reboot and play audio → no stuttering observed (tested in 10+ reboot cycles without reproducing the issue) Signed-off-by: Zhang Heng <zhangheng@kylinos.cn> Link: https://patch.msgid.link/20260728111309.1271834-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/usb/endpoint.c7
-rw-r--r--sound/usb/quirks.c3
-rw-r--r--sound/usb/usbaudio.h8
3 files changed, 17 insertions, 1 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 24cd7692bd01..54aeac7d087b 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -1232,7 +1232,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
/* try to use enough URBs to contain an entire ALSA buffer */
max_urbs = min((unsigned) MAX_URBS,
MAX_QUEUE * packs_per_ms / urb_packs);
- ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods);
+ if (chip->quirk_flags & QUIRK_FLAG_PLAYBACK_URB_FIXUP)
+ ep->nurbs = MAX_URBS;
+ else
+ ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods);
}
/* allocate and initialize data urbs */
@@ -1256,6 +1259,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
goto out_of_memory;
u->urb->pipe = ep->pipe;
u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ if (chip->quirk_flags & QUIRK_FLAG_PLAYBACK_URB_FIXUP)
+ u->urb->transfer_flags |= URB_ISO_ASAP;
u->urb->interval = 1 << ep->datainterval;
u->urb->context = u;
u->urb->complete = snd_complete_urb;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 41149561aa06..52dbbdb7f9a1 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2415,6 +2415,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
+ DEVICE_FLG(0x1e0b, 0xd01e, /* Generic USB Audio Device */
+ QUIRK_FLAG_PLAYBACK_URB_FIXUP),
DEVICE_FLG(0x1ff7, 0x0f81, /* SC13A Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */
@@ -2624,6 +2626,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY),
QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN),
+ QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP),
NULL
};
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index e26f9092417e..dc1d0c8c9c80 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -254,6 +254,12 @@ extern bool snd_usb_skip_validation;
* check being non-fatal and only disabling GET_CUR instead of the whole mixer.
* The current volume will then be provided by the internal cache that stores
* the last set volume
+ * QUIRK_FLAG_PLAYBACK_URB_FIXUP
+ * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS.
+ * This is needed for devices that exhibit boot-time audio stuttering due
+ * to insufficient buffer depth combined with xHCI scheduling variability.
+ * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling
+ * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling.
*/
enum {
@@ -288,6 +294,7 @@ enum {
QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28,
QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29,
QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30,
+ QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31,
/* Please also edit snd_usb_audio_quirk_flag_names */
};
@@ -324,5 +331,6 @@ enum {
#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
#define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY)
#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN)
+#define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP)
#endif /* __USBAUDIO_H */