]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 1 Dec 2021 09:07:12 +0000 (10:07 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 1 Dec 2021 09:07:12 +0000 (10:07 +0100)
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

13 files changed:
queue-5.15/alsa-usb-audio-add-spinlock-to-stop_urbs.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-avoid-killing-in-flight-urbs-during-draining.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-check-available-frames-for-the-next-packet-size.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-disable-low-latency-mode-for-implicit-feedback-sync.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-disable-low-latency-playback-for-free-wheel-mode.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-don-t-start-stream-for-capture-at-prepare.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-fix-packet-size-calculation-regression.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-improved-lowlatency-playback-support.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-less-restriction-for-low-latency-playback-mode.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-rename-early_playback_start-flag-with-lowlatency_playback.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-restrict-rates-for-the-shared-clocks.patch [new file with mode: 0644]
queue-5.15/alsa-usb-audio-switch-back-to-non-latency-mode-at-a-later-point.patch [new file with mode: 0644]
queue-5.15/series [new file with mode: 0644]

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 (file)
index 0000000..70a4ef1
--- /dev/null
@@ -0,0 +1,42 @@
+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)) {
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 (file)
index 0000000..81111a8
--- /dev/null
@@ -0,0 +1,175 @@
+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,
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 (file)
index 0000000..ef11d95
--- /dev/null
@@ -0,0 +1,153 @@
+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;
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 (file)
index 0000000..94910fc
--- /dev/null
@@ -0,0 +1,37 @@
+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;
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 (file)
index 0000000..a4afe77
--- /dev/null
@@ -0,0 +1,52 @@
+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);
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 (file)
index 0000000..b010229
--- /dev/null
@@ -0,0 +1,32 @@
+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:
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 (file)
index 0000000..f91b532
--- /dev/null
@@ -0,0 +1,35 @@
+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];
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 (file)
index 0000000..6e027b0
--- /dev/null
@@ -0,0 +1,582 @@
+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 = {
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 (file)
index 0000000..f6fe20b
--- /dev/null
@@ -0,0 +1,38 @@
+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 */
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 (file)
index 0000000..d06fa47
--- /dev/null
@@ -0,0 +1,122 @@
+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) {
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 (file)
index 0000000..6782d14
--- /dev/null
@@ -0,0 +1,116 @@
+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];
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 (file)
index 0000000..ccba7f5
--- /dev/null
@@ -0,0 +1,64 @@
+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,
diff --git a/queue-5.15/series b/queue-5.15/series
new file mode 100644 (file)
index 0000000..4d1caa5
--- /dev/null
@@ -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