From: Greg Kroah-Hartman Date: Wed, 1 Dec 2021 09:07:12 +0000 (+0100) Subject: 5.15-stable patches X-Git-Tag: v4.4.294~73 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bb79976b0f3698381005c4c3be0f3a8220475453;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: alsa-usb-audio-add-spinlock-to-stop_urbs.patch alsa-usb-audio-avoid-killing-in-flight-urbs-during-draining.patch alsa-usb-audio-check-available-frames-for-the-next-packet-size.patch alsa-usb-audio-disable-low-latency-mode-for-implicit-feedback-sync.patch alsa-usb-audio-disable-low-latency-playback-for-free-wheel-mode.patch alsa-usb-audio-don-t-start-stream-for-capture-at-prepare.patch alsa-usb-audio-fix-packet-size-calculation-regression.patch alsa-usb-audio-improved-lowlatency-playback-support.patch alsa-usb-audio-less-restriction-for-low-latency-playback-mode.patch alsa-usb-audio-rename-early_playback_start-flag-with-lowlatency_playback.patch alsa-usb-audio-restrict-rates-for-the-shared-clocks.patch alsa-usb-audio-switch-back-to-non-latency-mode-at-a-later-point.patch --- diff --git a/queue-5.15/alsa-usb-audio-add-spinlock-to-stop_urbs.patch b/queue-5.15/alsa-usb-audio-add-spinlock-to-stop_urbs.patch new file mode 100644 index 00000000000..70a4ef12131 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-add-spinlock-to-stop_urbs.patch @@ -0,0 +1,42 @@ +From 0ef74366bc150dda4f53c546dfa6e8f7c707e087 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:42 +0200 +Subject: ALSA: usb-audio: Add spinlock to stop_urbs() + +From: Takashi Iwai + +commit 0ef74366bc150dda4f53c546dfa6e8f7c707e087 upstream. + +In theory, stop_urbs() may be called concurrently. +Although we have the state check beforehand, it's safer to apply +ep->lock during the critical list head manipulations. + +Link: https://lore.kernel.org/r/20210929080844.11583-8-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/endpoint.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -927,6 +927,7 @@ void snd_usb_endpoint_sync_pending_stop( + static int stop_urbs(struct snd_usb_endpoint *ep, bool force) + { + unsigned int i; ++ unsigned long flags; + + if (!force && atomic_read(&ep->running)) + return -EBUSY; +@@ -934,9 +935,11 @@ static int stop_urbs(struct snd_usb_endp + if (!ep_state_update(ep, EP_STATE_RUNNING, EP_STATE_STOPPING)) + return 0; + ++ spin_lock_irqsave(&ep->lock, flags); + INIT_LIST_HEAD(&ep->ready_playback_urbs); + ep->next_packet_head = 0; + ep->next_packet_queued = 0; ++ spin_unlock_irqrestore(&ep->lock, flags); + + for (i = 0; i < ep->nurbs; i++) { + if (test_bit(i, &ep->active_mask)) { diff --git a/queue-5.15/alsa-usb-audio-avoid-killing-in-flight-urbs-during-draining.patch b/queue-5.15/alsa-usb-audio-avoid-killing-in-flight-urbs-during-draining.patch new file mode 100644 index 00000000000..81111a80786 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-avoid-killing-in-flight-urbs-during-draining.patch @@ -0,0 +1,175 @@ +From 813a17cab9b708bbb1e0db8902e19857b57196ec Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:44 +0200 +Subject: ALSA: usb-audio: Avoid killing in-flight URBs during draining + +From: Takashi Iwai + +commit 813a17cab9b708bbb1e0db8902e19857b57196ec upstream. + +While draining a stream, ALSA PCM core stops the stream by issuing +snd_pcm_stop() after all data has been sent out. And, at PCM trigger +stop, currently USB-audio driver kills the in-flight URBs explicitly, +then at sync-stop ops, sync with the finish of all remaining URBs. +This might result in a drop of the drained samples as most of +USB-audio devices / hosts allow relatively long in-flight samples (as +a sort of FIFO). + +For avoiding the trimming, this patch changes the stream-stop behavior +during PCM draining state. Under that condition, the pending URBs +won't be killed. The leftover in-flight URBs are caught by the +sync-stop operation that shall be performed after the trigger-stop +operation. + +Link: https://lore.kernel.org/r/20210929080844.11583-10-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/endpoint.c | 14 +++++++++----- + sound/usb/endpoint.h | 2 +- + sound/usb/pcm.c | 16 ++++++++-------- + 3 files changed, 18 insertions(+), 14 deletions(-) + +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -955,7 +955,7 @@ void snd_usb_endpoint_sync_pending_stop( + * + * This function moves the EP to STOPPING state if it's being RUNNING. + */ +-static int stop_urbs(struct snd_usb_endpoint *ep, bool force) ++static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) + { + unsigned int i; + unsigned long flags; +@@ -972,6 +972,9 @@ static int stop_urbs(struct snd_usb_endp + ep->next_packet_queued = 0; + spin_unlock_irqrestore(&ep->lock, flags); + ++ if (keep_pending) ++ return 0; ++ + for (i = 0; i < ep->nurbs; i++) { + if (test_bit(i, &ep->active_mask)) { + if (!test_and_set_bit(i, &ep->unlink_mask)) { +@@ -995,7 +998,7 @@ static int release_urbs(struct snd_usb_e + snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); + + /* stop and unlink urbs */ +- err = stop_urbs(ep, force); ++ err = stop_urbs(ep, force, false); + if (err) + return err; + +@@ -1527,7 +1530,7 @@ int snd_usb_endpoint_start(struct snd_us + return 0; + + __error: +- snd_usb_endpoint_stop(ep); ++ snd_usb_endpoint_stop(ep, false); + return -EPIPE; + } + +@@ -1535,6 +1538,7 @@ __error: + * snd_usb_endpoint_stop: stop an snd_usb_endpoint + * + * @ep: the endpoint to stop (may be NULL) ++ * @keep_pending: keep in-flight URBs + * + * A call to this function will decrement the running count of the endpoint. + * In case the last user has requested the endpoint stop, the URBs will +@@ -1545,7 +1549,7 @@ __error: + * The caller needs to synchronize the pending stop operation via + * snd_usb_endpoint_sync_pending_stop(). + */ +-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) ++void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending) + { + if (!ep) + return; +@@ -1560,7 +1564,7 @@ void snd_usb_endpoint_stop(struct snd_us + if (!atomic_dec_return(&ep->running)) { + if (ep->sync_source) + WRITE_ONCE(ep->sync_source->sync_sink, NULL); +- stop_urbs(ep, false); ++ stop_urbs(ep, false, keep_pending); + } + } + +--- a/sound/usb/endpoint.h ++++ b/sound/usb/endpoint.h +@@ -38,7 +38,7 @@ void snd_usb_endpoint_set_callback(struc + struct snd_usb_substream *data_subs); + + int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); ++void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending); + void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); + void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep); + int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -219,16 +219,16 @@ int snd_usb_init_pitch(struct snd_usb_au + return 0; + } + +-static bool stop_endpoints(struct snd_usb_substream *subs) ++static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending) + { + bool stopped = 0; + + if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { +- snd_usb_endpoint_stop(subs->sync_endpoint); ++ snd_usb_endpoint_stop(subs->sync_endpoint, keep_pending); + stopped = true; + } + if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { +- snd_usb_endpoint_stop(subs->data_endpoint); ++ snd_usb_endpoint_stop(subs->data_endpoint, keep_pending); + stopped = true; + } + return stopped; +@@ -261,7 +261,7 @@ static int start_endpoints(struct snd_us + return 0; + + error: +- stop_endpoints(subs); ++ stop_endpoints(subs, false); + return err; + } + +@@ -437,7 +437,7 @@ static int configure_endpoints(struct sn + + if (subs->data_endpoint->need_setup) { + /* stop any running stream beforehand */ +- if (stop_endpoints(subs)) ++ if (stop_endpoints(subs, false)) + sync_pending_stops(subs); + err = snd_usb_endpoint_configure(chip, subs->data_endpoint); + if (err < 0) +@@ -572,7 +572,7 @@ static int snd_usb_hw_free(struct snd_pc + subs->cur_audiofmt = NULL; + mutex_unlock(&chip->mutex); + if (!snd_usb_lock_shutdown(chip)) { +- if (stop_endpoints(subs)) ++ if (stop_endpoints(subs, false)) + sync_pending_stops(subs); + close_endpoints(chip, subs); + snd_usb_unlock_shutdown(chip); +@@ -1559,7 +1559,7 @@ static int snd_usb_substream_playback_tr + return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: +- stop_endpoints(subs); ++ stop_endpoints(subs, substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING); + snd_usb_endpoint_set_callback(subs->data_endpoint, + NULL, NULL, NULL); + subs->running = 0; +@@ -1607,7 +1607,7 @@ static int snd_usb_substream_capture_tri + return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: +- stop_endpoints(subs); ++ stop_endpoints(subs, false); + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_usb_endpoint_set_callback(subs->data_endpoint, diff --git a/queue-5.15/alsa-usb-audio-check-available-frames-for-the-next-packet-size.patch b/queue-5.15/alsa-usb-audio-check-available-frames-for-the-next-packet-size.patch new file mode 100644 index 00000000000..ef11d95a395 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-check-available-frames-for-the-next-packet-size.patch @@ -0,0 +1,153 @@ +From d215f63d49da9a8803af3e81acd6cad743686573 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:41 +0200 +Subject: ALSA: usb-audio: Check available frames for the next packet size + +From: Takashi Iwai + +commit d215f63d49da9a8803af3e81acd6cad743686573 upstream. + +This is yet more preparation for the upcoming changes. + +Extend snd_usb_endpoint_next_packet_size() to check the available +frames and return -EAGAIN if the next packet size is equal or exceeds +the given size. This will be needed for avoiding XRUN during the low +latency operation. + +As of this patch, avail=0 is passed, i.e. the check is skipped and no +behavior change. + +Link: https://lore.kernel.org/r/20210929080844.11583-7-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/endpoint.c | 51 ++++++++++++++++++++++++++++++++++++--------------- + sound/usb/endpoint.h | 3 ++- + sound/usb/pcm.c | 2 +- + 3 files changed, 39 insertions(+), 17 deletions(-) + +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -148,18 +148,23 @@ int snd_usb_endpoint_implicit_feedback_s + * This won't be used for implicit feedback which takes the packet size + * returned from the sync source + */ +-static int slave_next_packet_size(struct snd_usb_endpoint *ep) ++static int slave_next_packet_size(struct snd_usb_endpoint *ep, ++ unsigned int avail) + { + unsigned long flags; ++ unsigned int phase; + int ret; + + if (ep->fill_max) + return ep->maxframesize; + + spin_lock_irqsave(&ep->lock, flags); +- ep->phase = (ep->phase & 0xffff) +- + (ep->freqm << ep->datainterval); +- ret = min(ep->phase >> 16, ep->maxframesize); ++ phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval); ++ ret = min(phase >> 16, ep->maxframesize); ++ if (avail && ret >= avail) ++ ret = -EAGAIN; ++ else ++ ep->phase = phase; + spin_unlock_irqrestore(&ep->lock, flags); + + return ret; +@@ -169,20 +174,25 @@ static int slave_next_packet_size(struct + * Return the number of samples to be sent in the next packet + * for adaptive and synchronous endpoints + */ +-static int next_packet_size(struct snd_usb_endpoint *ep) ++static int next_packet_size(struct snd_usb_endpoint *ep, unsigned int avail) + { ++ unsigned int sample_accum; + int ret; + + if (ep->fill_max) + return ep->maxframesize; + +- ep->sample_accum += ep->sample_rem; +- if (ep->sample_accum >= ep->pps) { +- ep->sample_accum -= ep->pps; ++ sample_accum += ep->sample_rem; ++ if (sample_accum >= ep->pps) { ++ sample_accum -= ep->pps; + ret = ep->packsize[1]; + } else { + ret = ep->packsize[0]; + } ++ if (avail && ret >= avail) ++ ret = -EAGAIN; ++ else ++ ep->sample_accum = sample_accum; + + return ret; + } +@@ -190,16 +200,27 @@ static int next_packet_size(struct snd_u + /* + * snd_usb_endpoint_next_packet_size: Return the number of samples to be sent + * in the next packet ++ * ++ * If the size is equal or exceeds @avail, don't proceed but return -EAGAIN ++ * Exception: @avail = 0 for skipping the check. + */ + int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, +- struct snd_urb_ctx *ctx, int idx) ++ struct snd_urb_ctx *ctx, int idx, ++ unsigned int avail) + { +- if (ctx->packet_size[idx]) +- return ctx->packet_size[idx]; +- else if (ep->sync_source) +- return slave_next_packet_size(ep); ++ unsigned int packet; ++ ++ packet = ctx->packet_size[idx]; ++ if (packet) { ++ if (avail && packet >= avail) ++ return -EAGAIN; ++ return packet; ++ } ++ ++ if (ep->sync_source) ++ return slave_next_packet_size(ep, avail); + else +- return next_packet_size(ep); ++ return next_packet_size(ep, avail); + } + + static void call_retire_callback(struct snd_usb_endpoint *ep, +@@ -263,7 +284,7 @@ static void prepare_silent_urb(struct sn + unsigned int length; + int counts; + +- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); ++ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0); + length = counts * ep->stride; /* number of silent bytes */ + offset = offs * ep->stride + extra * i; + urb->iso_frame_desc[i].offset = offset; +--- a/sound/usb/endpoint.h ++++ b/sound/usb/endpoint.h +@@ -46,6 +46,7 @@ void snd_usb_endpoint_free_all(struct sn + + int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); + int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, +- struct snd_urb_ctx *ctx, int idx); ++ struct snd_urb_ctx *ctx, int idx, ++ unsigned int avail); + + #endif /* __USBAUDIO_ENDPOINT_H */ +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -1365,7 +1365,7 @@ static void prepare_playback_urb(struct + spin_lock_irqsave(&subs->lock, flags); + subs->frame_limit += ep->max_urb_frames; + for (i = 0; i < ctx->packets; i++) { +- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); ++ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0); + /* set up descriptor */ + urb->iso_frame_desc[i].offset = frames * stride; + urb->iso_frame_desc[i].length = counts * stride; diff --git a/queue-5.15/alsa-usb-audio-disable-low-latency-mode-for-implicit-feedback-sync.patch b/queue-5.15/alsa-usb-audio-disable-low-latency-mode-for-implicit-feedback-sync.patch new file mode 100644 index 00000000000..94910fc978a --- /dev/null +++ b/queue-5.15/alsa-usb-audio-disable-low-latency-mode-for-implicit-feedback-sync.patch @@ -0,0 +1,37 @@ +From bceee75387554f682638e719d1ea60125ea78cea Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:40 +0200 +Subject: ALSA: usb-audio: Disable low-latency mode for implicit feedback sync + +From: Takashi Iwai + +commit bceee75387554f682638e719d1ea60125ea78cea upstream. + +When a playback stream runs in the implicit feedback mode, its +operation is passive and won't start unless the capture packet is +received. This behavior contradicts with the low-latency playback +mode, and we should turn off lowlatency_playback flag accordingly. + +In theory, we may take the low-latency mode when the playback-first +quirk is set, but it still conflicts with the later operation with the +fixed packet numbers, so it's disabled all together for now. + +Link: https://lore.kernel.org/r/20210929080844.11583-6-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/pcm.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -595,6 +595,9 @@ static int lowlatency_playback_available + /* free-wheeling mode? (e.g. dmix) */ + if (runtime->stop_threshold > runtime->buffer_size) + return false; ++ /* implicit feedback mode has own operation mode */ ++ if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) ++ return false; + /* too short periods? */ + if (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes) + return false; diff --git a/queue-5.15/alsa-usb-audio-disable-low-latency-playback-for-free-wheel-mode.patch b/queue-5.15/alsa-usb-audio-disable-low-latency-playback-for-free-wheel-mode.patch new file mode 100644 index 00000000000..a4afe7712a8 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-disable-low-latency-playback-for-free-wheel-mode.patch @@ -0,0 +1,52 @@ +From e581f1cec4f899f788f6c9477f805b1d5fef25e2 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:39 +0200 +Subject: ALSA: usb-audio: Disable low-latency playback for free-wheel mode + +From: Takashi Iwai + +commit e581f1cec4f899f788f6c9477f805b1d5fef25e2 upstream. + +The free-wheel stream operation like dmix may not update the appl_ptr +appropriately, and it doesn't fit with the low-latency playback mode. +Disable the low-latency playback operation when the stream is set up +in such a mode. + +Link: https://lore.kernel.org/r/20210929080844.11583-5-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/pcm.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -582,7 +582,8 @@ static int snd_usb_hw_free(struct snd_pc + } + + /* check whether early start is needed for playback stream */ +-static int lowlatency_playback_available(struct snd_usb_substream *subs) ++static int lowlatency_playback_available(struct snd_pcm_runtime *runtime, ++ struct snd_usb_substream *subs) + { + struct snd_usb_audio *chip = subs->stream->chip; + +@@ -591,6 +592,9 @@ static int lowlatency_playback_available + /* disabled via module option? */ + if (!chip->lowlatency) + return false; ++ /* free-wheeling mode? (e.g. dmix) */ ++ if (runtime->stop_threshold > runtime->buffer_size) ++ return false; + /* too short periods? */ + if (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes) + return false; +@@ -630,7 +634,7 @@ static int snd_usb_pcm_prepare(struct sn + subs->period_elapsed_pending = 0; + runtime->delay = 0; + +- subs->lowlatency_playback = lowlatency_playback_available(subs); ++ subs->lowlatency_playback = lowlatency_playback_available(runtime, subs); + if (!subs->lowlatency_playback) + ret = start_endpoints(subs); + diff --git a/queue-5.15/alsa-usb-audio-don-t-start-stream-for-capture-at-prepare.patch b/queue-5.15/alsa-usb-audio-don-t-start-stream-for-capture-at-prepare.patch new file mode 100644 index 00000000000..b0102297f34 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-don-t-start-stream-for-capture-at-prepare.patch @@ -0,0 +1,32 @@ +From 83de8f83816e8e15227dac985163e3d433a2bf9d Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 19 Nov 2021 11:26:29 +0100 +Subject: ALSA: usb-audio: Don't start stream for capture at prepare + +From: Takashi Iwai + +commit 83de8f83816e8e15227dac985163e3d433a2bf9d upstream. + +The recent change made mistakenly the stream for capture started at +prepare stage. Add the stream direction check to avoid it. + +Fixes: 9c9a3b9da891 ("ALSA: usb-audio: Rename early_playback_start flag with lowlatency_playback") +Link: https://lore.kernel.org/r/20211119102629.7476-1-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/pcm.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -640,7 +640,8 @@ static int snd_usb_pcm_prepare(struct sn + runtime->delay = 0; + + subs->lowlatency_playback = lowlatency_playback_available(runtime, subs); +- if (!subs->lowlatency_playback) ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ !subs->lowlatency_playback) + ret = start_endpoints(subs); + + unlock: diff --git a/queue-5.15/alsa-usb-audio-fix-packet-size-calculation-regression.patch b/queue-5.15/alsa-usb-audio-fix-packet-size-calculation-regression.patch new file mode 100644 index 00000000000..f91b532253e --- /dev/null +++ b/queue-5.15/alsa-usb-audio-fix-packet-size-calculation-regression.patch @@ -0,0 +1,35 @@ +From 23939115be181bc5dbc33aa8471adcdbffa28910 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 1 Oct 2021 12:54:25 +0200 +Subject: ALSA: usb-audio: Fix packet size calculation regression + +From: Takashi Iwai + +commit 23939115be181bc5dbc33aa8471adcdbffa28910 upstream. + +The commit d215f63d49da ("ALSA: usb-audio: Check available frames for +the next packet size") introduced the available frame size check, but +the conversion forgot to initialize the temporary variable properly, +and it resulted in a bogus calculation. This patch fixes it. + +Fixes: d215f63d49da ("ALSA: usb-audio: Check available frames for the next packet size") +Reported-by: Colin Ian King +Link: https://lore.kernel.org/r/20211001104417.14291-1-colin.king@canonical.com +Link: https://lore.kernel.org/r/20211001105425.16191-1-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/endpoint.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -182,7 +182,7 @@ static int next_packet_size(struct snd_u + if (ep->fill_max) + return ep->maxframesize; + +- sample_accum += ep->sample_rem; ++ sample_accum = ep->sample_accum + ep->sample_rem; + if (sample_accum >= ep->pps) { + sample_accum -= ep->pps; + ret = ep->packsize[1]; diff --git a/queue-5.15/alsa-usb-audio-improved-lowlatency-playback-support.patch b/queue-5.15/alsa-usb-audio-improved-lowlatency-playback-support.patch new file mode 100644 index 00000000000..6e027b039bd --- /dev/null +++ b/queue-5.15/alsa-usb-audio-improved-lowlatency-playback-support.patch @@ -0,0 +1,582 @@ +From d5f871f89e21bb71827ea57bd484eedea85839a0 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:43 +0200 +Subject: ALSA: usb-audio: Improved lowlatency playback support + +From: Takashi Iwai + +commit d5f871f89e21bb71827ea57bd484eedea85839a0 upstream. + +This is another attempt to improve further the handling of playback +stream in the low latency mode. The latest workaround in commit +4267c5a8f313 ("ALSA: usb-audio: Work around for XRUN with low latency +playback") revealed that submitting URBs forcibly in advance may +trigger XRUN easily. In the classical mode, this problem was avoided +by practically delaying the submission of the actual data with the +pre-submissions of silent data before triggering the stream start. +But that is exactly what we want to avoid. + +Now, in this patch, instead of the previous workaround, we take a +similar approach as used in the implicit feedback mode. The URBs are +queued at the PCM trigger start like before, but we check whether the +buffer has been already filled enough before each submission, and +stop queuing if the data overcomes the threshold. The remaining URBs +are kept in the ready list, and they will be retrieved in the URB +complete callback of other (already queued) URBs. In the complete +callback, we try to fill the data and submit as much as possible +again. When there is no more available in-flight URBs that may handle +the pending data, we'll check in PCM ack callback and submit and +process URBs there in addition. In this way, the amount of in-flight +URBs may vary dynamically and flexibly depending on the available data +without hitting XRUN. + +The following things are changed to achieve the behavior above: + +* The endpoint prepare callback is changed to return an error code; + when there is no enough data available, it may return -EAGAIN. + Currently only prepare_playback_urb() returns the error. + + The evaluation of the available data is a bit messy here; we can't + check with snd_pcm_avail() at the point of prepare callback (as + runtime->status->hwptr hasn't been updated yet), hence we manually + estimate the appl_ptr and compare with the internal hwptr_done to + calculate the available frames. + +* snd_usb_endpoint_start() doesn't submit full URBs if the prepare + callback returns -EAGAIN, and puts the remaining URBs to the ready + list for the later submission. + +* snd_complete_urb() treats the URBs in the low-latency mode similarly + like the implicit feedback mode, and submissions are done in + (now exported) snd_usb_queue_pending_output_urbs(). + +* snd_usb_queue_pending_output_urbs() again checks the error value + from the prepare callback. If it's -EAGAIN for the normal stream + (i.e. not implicit feedback mode), we push it back to the ready list + again. + +* PCM ack callback is introduced for the playback stream, and it calls + snd_usb_queue_pending_output_urbs() if there is no in-flight URB + while the stream is running. This corresponds to the case where the + system needs the appl_ptr update for re-submitting a new URB. + +* snd_usb_queue_pending_output_urbs() and the prepare EP callback + receive in_stream_lock argument, which is a bool flag indicating the + call path from PCM ack. It's needed for avoiding the deadlock of + snd_pcm_period_elapsed() calls. + +* Set the new SNDRV_PCM_INFO_EXPLICIT_SYNC flag when the new + low-latency mode is deployed. This assures catching each applptr + update even in the mmap mode. + +Fixes: 4267c5a8f313 ("ALSA: usb-audio: Work around for XRUN with low latency playback") +Link: https://lore.kernel.org/r/20210929080844.11583-9-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/card.h | 6 +- + sound/usb/endpoint.c | 130 ++++++++++++++++++++++++++++++++++----------------- + sound/usb/endpoint.h | 7 +- + sound/usb/pcm.c | 102 +++++++++++++++++++++++++++++++--------- + 4 files changed, 177 insertions(+), 68 deletions(-) + +--- a/sound/usb/card.h ++++ b/sound/usb/card.h +@@ -74,8 +74,9 @@ struct snd_usb_endpoint { + + atomic_t state; /* running state */ + +- void (*prepare_data_urb) (struct snd_usb_substream *subs, +- struct urb *urb); ++ int (*prepare_data_urb) (struct snd_usb_substream *subs, ++ struct urb *urb, ++ bool in_stream_lock); + void (*retire_data_urb) (struct snd_usb_substream *subs, + struct urb *urb); + +@@ -94,7 +95,6 @@ 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 */ + atomic_t submitted_urbs; /* currently submitted urbs */ +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -307,8 +307,9 @@ static void prepare_silent_urb(struct sn + /* + * Prepare a PLAYBACK urb for submission to the bus. + */ +-static void prepare_outbound_urb(struct snd_usb_endpoint *ep, +- struct snd_urb_ctx *ctx) ++static int prepare_outbound_urb(struct snd_usb_endpoint *ep, ++ struct snd_urb_ctx *ctx, ++ bool in_stream_lock) + { + struct urb *urb = ctx->urb; + unsigned char *cp = urb->transfer_buffer; +@@ -320,9 +321,9 @@ static void prepare_outbound_urb(struct + case SND_USB_ENDPOINT_TYPE_DATA: + data_subs = READ_ONCE(ep->data_subs); + if (data_subs && ep->prepare_data_urb) +- ep->prepare_data_urb(data_subs, urb); +- else /* no data provider, so send silence */ +- prepare_silent_urb(ep, ctx); ++ return ep->prepare_data_urb(data_subs, urb, in_stream_lock); ++ /* no data provider, so send silence */ ++ prepare_silent_urb(ep, ctx); + break; + + case SND_USB_ENDPOINT_TYPE_SYNC: +@@ -351,13 +352,14 @@ static void prepare_outbound_urb(struct + + break; + } ++ return 0; + } + + /* + * Prepare a CAPTURE or SYNC urb for submission to the bus. + */ +-static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, +- struct snd_urb_ctx *urb_ctx) ++static int prepare_inbound_urb(struct snd_usb_endpoint *ep, ++ struct snd_urb_ctx *urb_ctx) + { + int i, offs; + struct urb *urb = urb_ctx->urb; +@@ -382,6 +384,7 @@ static inline void prepare_inbound_urb(s + urb->iso_frame_desc[0].offset = 0; + break; + } ++ return 0; + } + + /* notify an error as XRUN to the assigned PCM data substream */ +@@ -417,6 +420,16 @@ next_packet_fifo_dequeue(struct snd_usb_ + return p; + } + ++static void push_back_to_ready_list(struct snd_usb_endpoint *ep, ++ struct snd_urb_ctx *ctx) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ep->lock, flags); ++ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); ++ spin_unlock_irqrestore(&ep->lock, flags); ++} ++ + /* + * Send output urbs that have been prepared previously. URBs are dequeued + * from ep->ready_playback_urbs and in case there aren't any available +@@ -427,12 +440,14 @@ next_packet_fifo_dequeue(struct snd_usb_ + * is that host controllers don't guarantee the order in which they return + * inbound and outbound packets to their submitters. + * +- * This function is only used for implicit feedback endpoints. For endpoints +- * driven by dedicated sync endpoints, URBs are immediately re-submitted +- * from their completion handler. ++ * This function is used both for implicit feedback endpoints and in low- ++ * latency playback mode. + */ +-static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) ++void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, ++ bool in_stream_lock) + { ++ bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep); ++ + while (ep_state_running(ep)) { + + unsigned long flags; +@@ -441,14 +456,14 @@ static void queue_pending_output_urbs(st + int err, i; + + spin_lock_irqsave(&ep->lock, flags); +- if (ep->next_packet_queued > 0 && ++ if ((!implicit_fb || ep->next_packet_queued > 0) && + !list_empty(&ep->ready_playback_urbs)) { + /* take URB out of FIFO */ + ctx = list_first_entry(&ep->ready_playback_urbs, + struct snd_urb_ctx, ready_list); + list_del_init(&ctx->ready_list); +- +- packet = next_packet_fifo_dequeue(ep); ++ if (implicit_fb) ++ packet = next_packet_fifo_dequeue(ep); + } + spin_unlock_irqrestore(&ep->lock, flags); + +@@ -456,11 +471,24 @@ static void queue_pending_output_urbs(st + return; + + /* copy over the length information */ +- for (i = 0; i < packet->packets; i++) +- ctx->packet_size[i] = packet->packet_size[i]; ++ if (implicit_fb) { ++ for (i = 0; i < packet->packets; i++) ++ ctx->packet_size[i] = packet->packet_size[i]; ++ } + + /* call the data handler to fill in playback data */ +- prepare_outbound_urb(ep, ctx); ++ err = prepare_outbound_urb(ep, ctx, in_stream_lock); ++ /* can be stopped during prepare callback */ ++ if (unlikely(!ep_state_running(ep))) ++ break; ++ if (err < 0) { ++ /* push back to ready list again for -EAGAIN */ ++ if (err == -EAGAIN) ++ push_back_to_ready_list(ep, ctx); ++ else ++ notify_xrun(ep); ++ return; ++ } + + err = usb_submit_urb(ctx->urb, GFP_ATOMIC); + if (err < 0) { +@@ -483,7 +511,6 @@ static void snd_complete_urb(struct urb + { + struct snd_urb_ctx *ctx = urb->context; + struct snd_usb_endpoint *ep = ctx->ep; +- unsigned long flags; + int err; + + if (unlikely(urb->status == -ENOENT || /* unlinked */ +@@ -504,17 +531,20 @@ static void snd_complete_urb(struct urb + if (unlikely(!ep_state_running(ep))) + goto exit_clear; + +- if (snd_usb_endpoint_implicit_feedback_sink(ep)) { +- spin_lock_irqsave(&ep->lock, flags); +- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); ++ /* in low-latency and implicit-feedback modes, push back the ++ * URB to ready list at first, then process as much as possible ++ */ ++ if (ep->lowlatency_playback || ++ snd_usb_endpoint_implicit_feedback_sink(ep)) { ++ push_back_to_ready_list(ep, ctx); + clear_bit(ctx->index, &ep->active_mask); +- spin_unlock_irqrestore(&ep->lock, flags); +- queue_pending_output_urbs(ep); ++ snd_usb_queue_pending_output_urbs(ep, false); + atomic_dec(&ep->submitted_urbs); /* decrement at last */ + return; + } + +- prepare_outbound_urb(ep, ctx); ++ /* in non-lowlatency mode, no error handling for prepare */ ++ prepare_outbound_urb(ep, ctx, false); + /* can be stopped during prepare callback */ + if (unlikely(!ep_state_running(ep))) + goto exit_clear; +@@ -807,8 +837,9 @@ void snd_usb_endpoint_set_sync(struct sn + * Pass NULL to deactivate each callback. + */ + void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, +- void (*prepare)(struct snd_usb_substream *subs, +- struct urb *urb), ++ int (*prepare)(struct snd_usb_substream *subs, ++ struct urb *urb, ++ bool in_stream_lock), + void (*retire)(struct snd_usb_substream *subs, + struct urb *urb), + struct snd_usb_substream *data_subs) +@@ -1166,10 +1197,6 @@ static int data_ep_set_params(struct snd + 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: +@@ -1408,6 +1435,7 @@ int snd_usb_endpoint_get_clock_rate(stru + */ + int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) + { ++ bool is_playback = usb_pipeout(ep->pipe); + int err; + unsigned int i; + +@@ -1444,13 +1472,9 @@ int snd_usb_endpoint_start(struct snd_us + + if (snd_usb_endpoint_implicit_feedback_sink(ep) && + !(ep->chip->quirk_flags & QUIRK_FLAG_PLAYBACK_FIRST)) { +- for (i = 0; i < ep->nurbs; i++) { +- struct snd_urb_ctx *ctx = ep->urb + i; +- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); +- } +- + usb_audio_dbg(ep->chip, "No URB submission due to implicit fb sync\n"); +- return 0; ++ i = 0; ++ goto fill_rest; + } + + for (i = 0; i < ep->nurbs; i++) { +@@ -1459,10 +1483,18 @@ int snd_usb_endpoint_start(struct snd_us + if (snd_BUG_ON(!urb)) + goto __error; + +- if (usb_pipeout(ep->pipe)) { +- prepare_outbound_urb(ep, urb->context); +- } else { +- prepare_inbound_urb(ep, urb->context); ++ if (is_playback) ++ err = prepare_outbound_urb(ep, urb->context, true); ++ else ++ err = prepare_inbound_urb(ep, urb->context); ++ if (err < 0) { ++ /* stop filling at applptr */ ++ if (err == -EAGAIN) ++ break; ++ usb_audio_dbg(ep->chip, ++ "EP 0x%x: failed to prepare urb: %d\n", ++ ep->ep_num, err); ++ goto __error; + } + + err = usb_submit_urb(urb, GFP_ATOMIC); +@@ -1476,8 +1508,22 @@ int snd_usb_endpoint_start(struct snd_us + atomic_inc(&ep->submitted_urbs); + } + ++ if (!i) { ++ usb_audio_dbg(ep->chip, "XRUN at starting EP 0x%x\n", ++ ep->ep_num); ++ goto __error; ++ } ++ + usb_audio_dbg(ep->chip, "%d URBs submitted for EP 0x%x\n", +- ep->nurbs, ep->ep_num); ++ i, ep->ep_num); ++ ++ fill_rest: ++ /* put the remaining URBs to ready list */ ++ if (is_playback) { ++ for (; i < ep->nurbs; i++) ++ push_back_to_ready_list(ep, ep->urb + i); ++ } ++ + return 0; + + __error: +@@ -1629,7 +1675,7 @@ static void snd_usb_handle_sync_urb(stru + } + + spin_unlock_irqrestore(&ep->lock, flags); +- queue_pending_output_urbs(ep); ++ snd_usb_queue_pending_output_urbs(ep, false); + + return; + } +--- a/sound/usb/endpoint.h ++++ b/sound/usb/endpoint.h +@@ -30,8 +30,9 @@ void snd_usb_endpoint_set_sync(struct sn + struct snd_usb_endpoint *data_ep, + struct snd_usb_endpoint *sync_ep); + void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, +- void (*prepare)(struct snd_usb_substream *subs, +- struct urb *urb), ++ int (*prepare)(struct snd_usb_substream *subs, ++ struct urb *urb, ++ bool in_stream_lock), + void (*retire)(struct snd_usb_substream *subs, + struct urb *urb), + struct snd_usb_substream *data_subs); +@@ -48,5 +49,7 @@ int snd_usb_endpoint_implicit_feedback_s + int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx, int idx, + unsigned int avail); ++void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, ++ bool in_stream_lock); + + #endif /* __USBAUDIO_ENDPOINT_H */ +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -598,9 +598,6 @@ static int lowlatency_playback_available + /* implicit feedback mode has own operation mode */ + if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) + return false; +- /* too short periods? */ +- if (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes) +- return false; + return true; + } + +@@ -1095,6 +1092,10 @@ static int snd_usb_pcm_open(struct snd_p + int ret; + + runtime->hw = snd_usb_hardware; ++ /* need an explicit sync to catch applptr update in low-latency mode */ ++ if (direction == SNDRV_PCM_STREAM_PLAYBACK && ++ as->chip->lowlatency) ++ runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC; + runtime->private_data = subs; + subs->pcm_substream = substream; + /* runtime PM is also done there */ +@@ -1347,44 +1348,66 @@ static unsigned int copy_to_urb_quirk(st + return bytes; + } + +-static void prepare_playback_urb(struct snd_usb_substream *subs, +- struct urb *urb) ++static int prepare_playback_urb(struct snd_usb_substream *subs, ++ struct urb *urb, ++ bool in_stream_lock) + { + struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + struct snd_usb_endpoint *ep = subs->data_endpoint; + struct snd_urb_ctx *ctx = urb->context; +- unsigned int counts, frames, bytes; ++ unsigned int frames, bytes; ++ int counts; ++ unsigned int transfer_done, frame_limit, avail = 0; + int i, stride, period_elapsed = 0; + unsigned long flags; ++ int err = 0; + + stride = ep->stride; + + frames = 0; + ctx->queued = 0; + urb->number_of_packets = 0; ++ + spin_lock_irqsave(&subs->lock, flags); +- subs->frame_limit += ep->max_urb_frames; ++ frame_limit = subs->frame_limit + ep->max_urb_frames; ++ transfer_done = subs->transfer_done; ++ ++ if (subs->lowlatency_playback && ++ runtime->status->state != SNDRV_PCM_STATE_DRAINING) { ++ unsigned int hwptr = subs->hwptr_done / stride; ++ ++ /* calculate the byte offset-in-buffer of the appl_ptr */ ++ avail = (runtime->control->appl_ptr - runtime->hw_ptr_base) ++ % runtime->buffer_size; ++ if (avail <= hwptr) ++ avail += runtime->buffer_size; ++ avail -= hwptr; ++ } ++ + for (i = 0; i < ctx->packets; i++) { +- counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0); ++ counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail); ++ if (counts < 0) ++ break; + /* set up descriptor */ + urb->iso_frame_desc[i].offset = frames * stride; + urb->iso_frame_desc[i].length = counts * stride; + frames += counts; ++ avail -= counts; + urb->number_of_packets++; +- subs->transfer_done += counts; +- if (subs->transfer_done >= runtime->period_size) { +- subs->transfer_done -= runtime->period_size; +- subs->frame_limit = 0; ++ transfer_done += counts; ++ if (transfer_done >= runtime->period_size) { ++ transfer_done -= runtime->period_size; ++ frame_limit = 0; + period_elapsed = 1; + if (subs->fmt_type == UAC_FORMAT_TYPE_II) { +- if (subs->transfer_done > 0) { ++ if (transfer_done > 0) { + /* FIXME: fill-max mode is not + * supported yet */ +- frames -= subs->transfer_done; +- counts -= subs->transfer_done; ++ frames -= transfer_done; ++ counts -= transfer_done; + urb->iso_frame_desc[i].length = + counts * stride; +- subs->transfer_done = 0; ++ transfer_done = 0; + } + i++; + if (i < ctx->packets) { +@@ -1398,13 +1421,19 @@ static void prepare_playback_urb(struct + } + } + /* finish at the period boundary or after enough frames */ +- if ((period_elapsed || +- subs->transfer_done >= subs->frame_limit) && ++ if ((period_elapsed || transfer_done >= frame_limit) && + !snd_usb_endpoint_implicit_feedback_sink(ep)) + break; + } +- bytes = frames * stride; + ++ if (!frames) { ++ err = -EAGAIN; ++ goto unlock; ++ } ++ ++ bytes = frames * stride; ++ subs->transfer_done = transfer_done; ++ subs->frame_limit = frame_limit; + if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE && + subs->cur_audiofmt->dsd_dop)) { + fill_playback_urb_dsd_dop(subs, urb, bytes); +@@ -1434,10 +1463,19 @@ static void prepare_playback_urb(struct + subs->period_elapsed_pending = 1; + period_elapsed = 0; + } ++ ++ unlock: + spin_unlock_irqrestore(&subs->lock, flags); ++ if (err < 0) ++ return err; + urb->transfer_buffer_length = bytes; +- if (period_elapsed) +- snd_pcm_period_elapsed(subs->pcm_substream); ++ if (period_elapsed) { ++ if (in_stream_lock) ++ snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream); ++ else ++ snd_pcm_period_elapsed(subs->pcm_substream); ++ } ++ return 0; + } + + /* +@@ -1469,6 +1507,27 @@ static void retire_playback_urb(struct s + snd_pcm_period_elapsed(subs->pcm_substream); + } + ++/* PCM ack callback for the playback stream; ++ * this plays a role only when the stream is running in low-latency mode. ++ */ ++static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream) ++{ ++ struct snd_usb_substream *subs = substream->runtime->private_data; ++ struct snd_usb_endpoint *ep; ++ ++ if (!subs->lowlatency_playback || !subs->running) ++ return 0; ++ ep = subs->data_endpoint; ++ if (!ep) ++ return 0; ++ /* When no more in-flight URBs available, try to process the pending ++ * outputs here ++ */ ++ if (!ep->active_mask) ++ snd_usb_queue_pending_output_urbs(ep, true); ++ return 0; ++} ++ + static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, + int cmd) + { +@@ -1572,6 +1631,7 @@ static const struct snd_pcm_ops snd_usb_ + .trigger = snd_usb_substream_playback_trigger, + .sync_stop = snd_usb_pcm_sync_stop, + .pointer = snd_usb_pcm_pointer, ++ .ack = snd_usb_pcm_playback_ack, + }; + + static const struct snd_pcm_ops snd_usb_capture_ops = { diff --git a/queue-5.15/alsa-usb-audio-less-restriction-for-low-latency-playback-mode.patch b/queue-5.15/alsa-usb-audio-less-restriction-for-low-latency-playback-mode.patch new file mode 100644 index 00000000000..f6fe20b77de --- /dev/null +++ b/queue-5.15/alsa-usb-audio-less-restriction-for-low-latency-playback-mode.patch @@ -0,0 +1,38 @@ +From 53451b6da8271905941eb1eb369db152c4bd92f2 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 11 Oct 2021 12:36:50 +0200 +Subject: ALSA: usb-audio: Less restriction for low-latency playback mode + +From: Takashi Iwai + +commit 53451b6da8271905941eb1eb369db152c4bd92f2 upstream. + +The recent support for the improved low-latency playback mode applied +the SNDRV_PCM_INFO_EXPLICIT_SYNC flag for the target streams, but this +was a slight overkill. The use of the flag above disables effectively +both PCM status and control mmaps, while basically what we want to +track is only about the appl_ptr update. + +For less restriction, use a more proper flag, +SNDRV_PCM_INFO_SYNC_APPLPTR instead, which disables only the control +mmap. + +Fixes: d5f871f89e21 ("ALSA: usb-audio: Improved lowlatency playback support") +Link: https://lore.kernel.org/r/20211011103650.10182-1-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/pcm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -1095,7 +1095,7 @@ static int snd_usb_pcm_open(struct snd_p + /* need an explicit sync to catch applptr update in low-latency mode */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK && + as->chip->lowlatency) +- runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC; ++ runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR; + runtime->private_data = subs; + subs->pcm_substream = substream; + /* runtime PM is also done there */ diff --git a/queue-5.15/alsa-usb-audio-rename-early_playback_start-flag-with-lowlatency_playback.patch b/queue-5.15/alsa-usb-audio-rename-early_playback_start-flag-with-lowlatency_playback.patch new file mode 100644 index 00000000000..d06fa47fde9 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-rename-early_playback_start-flag-with-lowlatency_playback.patch @@ -0,0 +1,122 @@ +From 9c9a3b9da891cc70405a544da6855700eddcbb71 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:38 +0200 +Subject: ALSA: usb-audio: Rename early_playback_start flag with lowlatency_playback + +From: Takashi Iwai + +commit 9c9a3b9da891cc70405a544da6855700eddcbb71 upstream. + +This is a preparation patch for the upcoming low-latency improvement +changes. + +Rename early_playback_start flag with lowlatency_playback as it's more +intuitive. The new flag is basically a reverse meaning. + +Along with the rename, factor out the code to set the flag to a +function. This makes the complex condition checks simpler. + +Also, the same flag is introduced to snd_usb_endpoint, too, that is +carried from the snd_usb_substream flag. Currently the endpoint flag +isn't still referred, but will be used in later patches. + +Link: https://lore.kernel.org/r/20210929080844.11583-4-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/card.h | 3 ++- + sound/usb/endpoint.c | 4 ++++ + sound/usb/pcm.c | 29 ++++++++++++++++++++--------- + 3 files changed, 26 insertions(+), 10 deletions(-) + +--- a/sound/usb/card.h ++++ b/sound/usb/card.h +@@ -126,6 +126,7 @@ struct snd_usb_endpoint { + int skip_packets; /* quirks for devices to ignore the first n packets + in a stream */ + bool implicit_fb_sync; /* syncs with implicit feedback */ ++ bool lowlatency_playback; /* low-latency playback mode */ + bool need_setup; /* (re-)need for configure? */ + + /* for hw constraints */ +@@ -190,7 +191,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? */ ++ bool lowlatency_playback; /* low-latency playback mode */ + struct media_ctl *media_ctl; + }; + +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -794,6 +794,10 @@ void snd_usb_endpoint_set_callback(struc + { + ep->prepare_data_urb = prepare; + ep->retire_data_urb = retire; ++ if (data_subs) ++ ep->lowlatency_playback = data_subs->lowlatency_playback; ++ else ++ ep->lowlatency_playback = false; + WRITE_ONCE(ep->data_subs, data_subs); + } + +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -581,6 +581,22 @@ static int snd_usb_hw_free(struct snd_pc + return 0; + } + ++/* check whether early start is needed for playback stream */ ++static int lowlatency_playback_available(struct snd_usb_substream *subs) ++{ ++ struct snd_usb_audio *chip = subs->stream->chip; ++ ++ if (subs->direction == SNDRV_PCM_STREAM_CAPTURE) ++ return false; ++ /* disabled via module option? */ ++ if (!chip->lowlatency) ++ return false; ++ /* too short periods? */ ++ if (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes) ++ return false; ++ return true; ++} ++ + /* + * prepare callback + * +@@ -614,13 +630,8 @@ static int snd_usb_pcm_prepare(struct sn + 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 && +- (!chip->lowlatency || +- (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes)); +- +- if (subs->early_playback_start) ++ subs->lowlatency_playback = lowlatency_playback_available(subs); ++ if (!subs->lowlatency_playback) + ret = start_endpoints(subs); + + unlock: +@@ -1412,7 +1423,7 @@ static void prepare_playback_urb(struct + subs->trigger_tstamp_pending_update = false; + } + +- if (period_elapsed && !subs->running && !subs->early_playback_start) { ++ if (period_elapsed && !subs->running && subs->lowlatency_playback) { + subs->period_elapsed_pending = 1; + period_elapsed = 0; + } +@@ -1466,7 +1477,7 @@ static int snd_usb_substream_playback_tr + prepare_playback_urb, + retire_playback_urb, + subs); +- if (!subs->early_playback_start && ++ if (subs->lowlatency_playback && + cmd == SNDRV_PCM_TRIGGER_START) { + err = start_endpoints(subs); + if (err < 0) { diff --git a/queue-5.15/alsa-usb-audio-restrict-rates-for-the-shared-clocks.patch b/queue-5.15/alsa-usb-audio-restrict-rates-for-the-shared-clocks.patch new file mode 100644 index 00000000000..6782d145ede --- /dev/null +++ b/queue-5.15/alsa-usb-audio-restrict-rates-for-the-shared-clocks.patch @@ -0,0 +1,116 @@ +From 4e7cf1fbb34ecb472c073980458cbe413afd4d64 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 29 Sep 2021 10:08:36 +0200 +Subject: ALSA: usb-audio: Restrict rates for the shared clocks + +From: Takashi Iwai + +commit 4e7cf1fbb34ecb472c073980458cbe413afd4d64 upstream. + +When a single clock source is shared among several endpoints, we have +to keep the same rate on all active endpoints as long as the clock is +being used. For dealing with such a case, this patch adds one more +check in the hw params constraint for the rate to take the shared +clocks into account. The current rate is evaluated from the endpoint +list that applies the same clock source. + +BugLink: https://bugzilla.suse.com/show_bug.cgi?id=1190418 +Link: https://lore.kernel.org/r/20210929080844.11583-2-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/card.h | 1 + + sound/usb/endpoint.c | 21 +++++++++++++++++++++ + sound/usb/endpoint.h | 1 + + sound/usb/pcm.c | 9 +++++++++ + 4 files changed, 32 insertions(+) + +--- a/sound/usb/card.h ++++ b/sound/usb/card.h +@@ -137,6 +137,7 @@ struct snd_usb_endpoint { + unsigned int cur_period_frames; + unsigned int cur_period_bytes; + unsigned int cur_buffer_periods; ++ unsigned char cur_clock; + + spinlock_t lock; + struct list_head list; +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -726,6 +726,7 @@ snd_usb_endpoint_open(struct snd_usb_aud + ep->cur_period_frames = params_period_size(params); + ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes; + ep->cur_buffer_periods = params_periods(params); ++ ep->cur_clock = fp->clock; + + if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC) + endpoint_set_syncinterval(chip, ep); +@@ -837,6 +838,7 @@ void snd_usb_endpoint_close(struct snd_u + ep->altsetting = 0; + ep->cur_audiofmt = NULL; + ep->cur_rate = 0; ++ ep->cur_clock = 0; + ep->iface_ref = NULL; + usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num); + } +@@ -1344,6 +1346,25 @@ unlock: + return err; + } + ++/* get the current rate set to the given clock by any endpoint */ ++int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock) ++{ ++ struct snd_usb_endpoint *ep; ++ int rate = 0; ++ ++ if (!clock) ++ return 0; ++ mutex_lock(&chip->mutex); ++ list_for_each_entry(ep, &chip->ep_list, list) { ++ if (ep->cur_clock == clock && ep->cur_rate) { ++ rate = ep->cur_rate; ++ break; ++ } ++ } ++ mutex_unlock(&chip->mutex); ++ return rate; ++} ++ + /** + * snd_usb_endpoint_start: start an snd_usb_endpoint + * +--- a/sound/usb/endpoint.h ++++ b/sound/usb/endpoint.h +@@ -19,6 +19,7 @@ void snd_usb_endpoint_close(struct snd_u + struct snd_usb_endpoint *ep); + int snd_usb_endpoint_configure(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep); ++int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock); + + bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep, +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -734,6 +734,7 @@ static int hw_rule_rate(struct snd_pcm_h + struct snd_pcm_hw_rule *rule) + { + struct snd_usb_substream *subs = rule->private; ++ struct snd_usb_audio *chip = subs->stream->chip; + const struct audioformat *fp; + struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + unsigned int rmin, rmax, r; +@@ -745,6 +746,14 @@ static int hw_rule_rate(struct snd_pcm_h + list_for_each_entry(fp, &subs->fmt_list, list) { + if (!hw_check_valid_format(subs, params, fp)) + continue; ++ r = snd_usb_endpoint_get_clock_rate(chip, fp->clock); ++ if (r > 0) { ++ if (!snd_interval_test(it, r)) ++ continue; ++ rmin = min(rmin, r); ++ rmax = max(rmax, r); ++ continue; ++ } + if (fp->rate_table && fp->nr_rates) { + for (i = 0; i < fp->nr_rates; i++) { + r = fp->rate_table[i]; diff --git a/queue-5.15/alsa-usb-audio-switch-back-to-non-latency-mode-at-a-later-point.patch b/queue-5.15/alsa-usb-audio-switch-back-to-non-latency-mode-at-a-later-point.patch new file mode 100644 index 00000000000..ccba7f5f10f --- /dev/null +++ b/queue-5.15/alsa-usb-audio-switch-back-to-non-latency-mode-at-a-later-point.patch @@ -0,0 +1,64 @@ +From eee5d6f1356a016105a974fb176b491288439efa Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 19 Nov 2021 11:24:59 +0100 +Subject: ALSA: usb-audio: Switch back to non-latency mode at a later point + +From: Takashi Iwai + +commit eee5d6f1356a016105a974fb176b491288439efa upstream. + +The recent regression report revealed that the judgment of the +low-latency playback mode based on the runtime->stop_threshold cannot +work reliably at the prepare stage, as sw_params call may happen at +any time, and PCM dmix actually sets it up after the prepare call. +This ended up with the stall of the stream as PCM ack won't be issued +at all. + +For addressing this, check the free-wheeling mode again at the PCM +trigger right before starting the stream again, and allow switching to +the non-LL mode at a late stage. + +Fixes: d5f871f89e21 ("ALSA: usb-audio: Improved lowlatency playback support") +Reported-and-tested-by: Kirill A. Shutemov +Link: https://lore.kernel.org/r/20211117161855.m45mxcqszkfcetai@box.shutemov.name +Link: https://lore.kernel.org/r/20211119102459.7055-1-tiwai@suse.de +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/pcm.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -581,6 +581,12 @@ static int snd_usb_hw_free(struct snd_pc + return 0; + } + ++/* free-wheeling mode? (e.g. dmix) */ ++static int in_free_wheeling_mode(struct snd_pcm_runtime *runtime) ++{ ++ return runtime->stop_threshold > runtime->buffer_size; ++} ++ + /* check whether early start is needed for playback stream */ + static int lowlatency_playback_available(struct snd_pcm_runtime *runtime, + struct snd_usb_substream *subs) +@@ -592,8 +598,7 @@ static int lowlatency_playback_available + /* disabled via module option? */ + if (!chip->lowlatency) + return false; +- /* free-wheeling mode? (e.g. dmix) */ +- if (runtime->stop_threshold > runtime->buffer_size) ++ if (in_free_wheeling_mode(runtime)) + return false; + /* implicit feedback mode has own operation mode */ + if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) +@@ -1545,6 +1550,8 @@ static int snd_usb_substream_playback_tr + subs); + if (subs->lowlatency_playback && + cmd == SNDRV_PCM_TRIGGER_START) { ++ if (in_free_wheeling_mode(substream->runtime)) ++ subs->lowlatency_playback = false; + err = start_endpoints(subs); + if (err < 0) { + snd_usb_endpoint_set_callback(subs->data_endpoint, diff --git a/queue-5.15/series b/queue-5.15/series new file mode 100644 index 00000000000..4d1caa5db2d --- /dev/null +++ b/queue-5.15/series @@ -0,0 +1,12 @@ +alsa-usb-audio-restrict-rates-for-the-shared-clocks.patch +alsa-usb-audio-rename-early_playback_start-flag-with-lowlatency_playback.patch +alsa-usb-audio-disable-low-latency-playback-for-free-wheel-mode.patch +alsa-usb-audio-disable-low-latency-mode-for-implicit-feedback-sync.patch +alsa-usb-audio-check-available-frames-for-the-next-packet-size.patch +alsa-usb-audio-add-spinlock-to-stop_urbs.patch +alsa-usb-audio-improved-lowlatency-playback-support.patch +alsa-usb-audio-avoid-killing-in-flight-urbs-during-draining.patch +alsa-usb-audio-fix-packet-size-calculation-regression.patch +alsa-usb-audio-less-restriction-for-low-latency-playback-mode.patch +alsa-usb-audio-switch-back-to-non-latency-mode-at-a-later-point.patch +alsa-usb-audio-don-t-start-stream-for-capture-at-prepare.patch