--- /dev/null
+From 0ef74366bc150dda4f53c546dfa6e8f7c707e087 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:42 +0200
+Subject: ALSA: usb-audio: Add spinlock to stop_urbs()
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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)) {
--- /dev/null
+From 813a17cab9b708bbb1e0db8902e19857b57196ec Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:44 +0200
+Subject: ALSA: usb-audio: Avoid killing in-flight URBs during draining
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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,
--- /dev/null
+From d215f63d49da9a8803af3e81acd6cad743686573 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:41 +0200
+Subject: ALSA: usb-audio: Check available frames for the next packet size
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
--- /dev/null
+From bceee75387554f682638e719d1ea60125ea78cea Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:40 +0200
+Subject: ALSA: usb-audio: Disable low-latency mode for implicit feedback sync
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
--- /dev/null
+From e581f1cec4f899f788f6c9477f805b1d5fef25e2 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:39 +0200
+Subject: ALSA: usb-audio: Disable low-latency playback for free-wheel mode
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
+
--- /dev/null
+From 83de8f83816e8e15227dac985163e3d433a2bf9d Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Fri, 19 Nov 2021 11:26:29 +0100
+Subject: ALSA: usb-audio: Don't start stream for capture at prepare
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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:
--- /dev/null
+From 23939115be181bc5dbc33aa8471adcdbffa28910 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Fri, 1 Oct 2021 12:54:25 +0200
+Subject: ALSA: usb-audio: Fix packet size calculation regression
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <colin.king@canonical.com>
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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];
--- /dev/null
+From d5f871f89e21bb71827ea57bd484eedea85839a0 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:43 +0200
+Subject: ALSA: usb-audio: Improved lowlatency playback support
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 = {
--- /dev/null
+From 53451b6da8271905941eb1eb369db152c4bd92f2 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Mon, 11 Oct 2021 12:36:50 +0200
+Subject: ALSA: usb-audio: Less restriction for low-latency playback mode
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 */
--- /dev/null
+From 9c9a3b9da891cc70405a544da6855700eddcbb71 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:38 +0200
+Subject: ALSA: usb-audio: Rename early_playback_start flag with lowlatency_playback
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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) {
--- /dev/null
+From 4e7cf1fbb34ecb472c073980458cbe413afd4d64 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Wed, 29 Sep 2021 10:08:36 +0200
+Subject: ALSA: usb-audio: Restrict rates for the shared clocks
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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];
--- /dev/null
+From eee5d6f1356a016105a974fb176b491288439efa Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+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 <tiwai@suse.de>
+
+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 <kirill.shutemov@linux.intel.com>
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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,
--- /dev/null
+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