[PATCH v3] ALSA: usb-audio: Fix boot-time audio stuttering for USB Audio device
Zhang Heng <[email protected]>
| Newsgroups | org.kernel.vger.linux-sound,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- sound/usb/endpoint.c | 7 ++++++- sound/usb/quirks.c | 3 +++ sound/usb/usbaudio.h | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) 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 40aa40fccb46..2b4e665564d2 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2422,6 +2422,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 */ @@ -2631,6 +2633,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 */ -- 2.25.1