From 4267c5a8f3133db0572cd9abee059b42cafbbdad Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Aug 2021 22:33:11 +0200 Subject: ALSA: usb-audio: Work around for XRUN with low latency playback The recent change for low latency playback works in most of test cases but it turned out still to hit errors on some use cases, most notably with JACK with small buffer sizes. This is because USB-audio driver fills up and submits full URBs at the beginning, while the URBs would return immediately and try to fill more -- that can easily trigger XRUN. It was more or less expected, but in the small buffer size, the problem became pretty obvious. Fixing this behavior properly would require the change of the fundamental driver design, so it's no trivial task, unfortunately. Instead, here we work around the problem just by switching back to the old method when the given configuration is too fragile with the low latency stream handling. As a threshold, we calculate the total buffer bytes in all plus one URBs, and check whether it's beyond the PCM buffer bytes. The one extra URB is needed because XRUN happens at the next submission after the first round. Fixes: 307cc9baac5c ("ALSA: usb-audio: Reduce latency at playback start, take#2") Cc: Link: https://lore.kernel.org/r/20210827203311.5987-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 2 ++ sound/usb/endpoint.c | 4 ++++ sound/usb/pcm.c | 13 +++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.h b/sound/usb/card.h index 6c0a052a28f9..5b19901f305a 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -94,6 +94,7 @@ struct snd_usb_endpoint { struct list_head ready_playback_urbs; /* playback URB FIFO for implicit fb */ unsigned int nurbs; /* # urbs */ + unsigned int nominal_queue_size; /* total buffer sizes in URBs */ unsigned long active_mask; /* bitmask of active urbs */ unsigned long unlink_mask; /* bitmask of unlinked urbs */ char *syncbuf; /* sync buffer for all sync URBs */ @@ -187,6 +188,7 @@ struct snd_usb_substream { } dsd_dop; bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */ + bool early_playback_start; /* early start needed for playback? */ struct media_ctl *media_ctl; }; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 8b1bec51c806..bf26c04cf471 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1126,6 +1126,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) INIT_LIST_HEAD(&u->ready_list); } + /* total buffer bytes of all URBs plus the next queue; + * referred in pcm.c + */ + ep->nominal_queue_size = maxsize * urb_packs * (ep->nurbs + 1); return 0; out_of_memory: diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 4e5031a68064..f5cbf61ac366 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -614,6 +614,14 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) subs->period_elapsed_pending = 0; runtime->delay = 0; + /* check whether early start is needed for playback stream */ + subs->early_playback_start = + subs->direction == SNDRV_PCM_STREAM_PLAYBACK && + subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes; + + if (subs->early_playback_start) + ret = start_endpoints(subs); + unlock: snd_usb_unlock_shutdown(chip); return ret; @@ -1394,7 +1402,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->trigger_tstamp_pending_update = false; } - if (period_elapsed && !subs->running) { + if (period_elapsed && !subs->running && !subs->early_playback_start) { subs->period_elapsed_pending = 1; period_elapsed = 0; } @@ -1448,7 +1456,8 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea prepare_playback_urb, retire_playback_urb, subs); - if (cmd == SNDRV_PCM_TRIGGER_START) { + if (!subs->early_playback_start && + cmd == SNDRV_PCM_TRIGGER_START) { err = start_endpoints(subs); if (err < 0) { snd_usb_endpoint_set_callback(subs->data_endpoint, -- cgit From 4801bee7d5a36c199b734a28cde5259183aff822 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 29 Aug 2021 09:38:30 +0200 Subject: ALSA: usb-audio: Add lowlatency module option For making user to switch back to the old playback mode, this patch adds a new module option 'lowlatency' to snd-usb-audio driver. When user face a regression due to the recent low-latency playback support, they can test easily by passing lowlatency=0 option without rebuilding the kernel. Fixes: 307cc9baac5c ("ALSA: usb-audio: Reduce latency at playback start, take#2") Link: https://lore.kernel.org/r/20210829073830.22686-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.c | 4 ++++ sound/usb/pcm.c | 3 ++- sound/usb/usbaudio.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index a1f8c3a026f5..6abfc9d079e7 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -68,6 +68,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; +static bool lowlatency = true; static char *quirk_alias[SNDRV_CARDS]; static char *delayed_register[SNDRV_CARDS]; static bool implicit_fb[SNDRV_CARDS]; @@ -92,6 +93,8 @@ MODULE_PARM_DESC(ignore_ctl_error, "Ignore errors from USB controller for mixer interfaces."); module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); +module_param(lowlatency, bool, 0444); +MODULE_PARM_DESC(lowlatency, "Enable low latency playback (default: yes)."); module_param_array(quirk_alias, charp, NULL, 0444); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); module_param_array(delayed_register, charp, NULL, 0444); @@ -599,6 +602,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, chip->setup = device_setup[idx]; chip->generic_implicit_fb = implicit_fb[idx]; chip->autoclock = autoclock; + chip->lowlatency = lowlatency; atomic_set(&chip->active, 1); /* avoid autopm during probing */ atomic_set(&chip->usage_count, 0); atomic_set(&chip->shutdown, 0); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index f5cbf61ac366..5dc9266180e3 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -617,7 +617,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) /* check whether early start is needed for playback stream */ subs->early_playback_start = subs->direction == SNDRV_PCM_STREAM_PLAYBACK && - subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes; + (!chip->lowlatency || + (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes)); if (subs->early_playback_start) ret = start_endpoints(subs); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 538831cbd925..8b70c9ea91b9 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -57,6 +57,7 @@ struct snd_usb_audio { bool generic_implicit_fb; /* from the 'implicit_fb' module param */ bool autoclock; /* from the 'autoclock' module param */ + bool lowlatency; /* from the 'lowlatency' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ struct media_device *media_dev; struct media_intf_devnode *ctl_intf_media_devnode; -- cgit