162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/init.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/bitrev.h>
862306a36Sopenharmony_ci#include <linux/ratelimit.h>
962306a36Sopenharmony_ci#include <linux/usb.h>
1062306a36Sopenharmony_ci#include <linux/usb/audio.h>
1162306a36Sopenharmony_ci#include <linux/usb/audio-v2.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <sound/core.h>
1462306a36Sopenharmony_ci#include <sound/pcm.h>
1562306a36Sopenharmony_ci#include <sound/pcm_params.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "usbaudio.h"
1862306a36Sopenharmony_ci#include "card.h"
1962306a36Sopenharmony_ci#include "quirks.h"
2062306a36Sopenharmony_ci#include "endpoint.h"
2162306a36Sopenharmony_ci#include "helper.h"
2262306a36Sopenharmony_ci#include "pcm.h"
2362306a36Sopenharmony_ci#include "clock.h"
2462306a36Sopenharmony_ci#include "power.h"
2562306a36Sopenharmony_ci#include "media.h"
2662306a36Sopenharmony_ci#include "implicit.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define SUBSTREAM_FLAG_DATA_EP_STARTED	0
2962306a36Sopenharmony_ci#define SUBSTREAM_FLAG_SYNC_EP_STARTED	1
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* return the estimated delay based on USB frame counters */
3262306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
3362306a36Sopenharmony_ci					   struct snd_pcm_runtime *runtime)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	unsigned int current_frame_number;
3662306a36Sopenharmony_ci	unsigned int frame_diff;
3762306a36Sopenharmony_ci	int est_delay;
3862306a36Sopenharmony_ci	int queued;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
4162306a36Sopenharmony_ci		queued = bytes_to_frames(runtime, subs->inflight_bytes);
4262306a36Sopenharmony_ci		if (!queued)
4362306a36Sopenharmony_ci			return 0;
4462306a36Sopenharmony_ci	} else if (!subs->running) {
4562306a36Sopenharmony_ci		return 0;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	current_frame_number = usb_get_current_frame_number(subs->dev);
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * HCD implementations use different widths, use lower 8 bits.
5162306a36Sopenharmony_ci	 * The delay will be managed up to 256ms, which is more than
5262306a36Sopenharmony_ci	 * enough
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci	frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Approximation based on number of samples per USB frame (ms),
5762306a36Sopenharmony_ci	   some truncation for 44.1 but the estimate is good enough */
5862306a36Sopenharmony_ci	est_delay = frame_diff * runtime->rate / 1000;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
6162306a36Sopenharmony_ci		est_delay = queued - est_delay;
6262306a36Sopenharmony_ci		if (est_delay < 0)
6362306a36Sopenharmony_ci			est_delay = 0;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return est_delay;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * return the current pcm pointer.  just based on the hwptr_done value.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7562306a36Sopenharmony_ci	struct snd_usb_substream *subs = runtime->private_data;
7662306a36Sopenharmony_ci	unsigned int hwptr_done;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (atomic_read(&subs->stream->chip->shutdown))
7962306a36Sopenharmony_ci		return SNDRV_PCM_POS_XRUN;
8062306a36Sopenharmony_ci	spin_lock(&subs->lock);
8162306a36Sopenharmony_ci	hwptr_done = subs->hwptr_done;
8262306a36Sopenharmony_ci	runtime->delay = snd_usb_pcm_delay(subs, runtime);
8362306a36Sopenharmony_ci	spin_unlock(&subs->lock);
8462306a36Sopenharmony_ci	return bytes_to_frames(runtime, hwptr_done);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * find a matching audio format
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic const struct audioformat *
9162306a36Sopenharmony_cifind_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
9262306a36Sopenharmony_ci	    unsigned int rate, unsigned int channels, bool strict_match,
9362306a36Sopenharmony_ci	    struct snd_usb_substream *subs)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	const struct audioformat *fp;
9662306a36Sopenharmony_ci	const struct audioformat *found = NULL;
9762306a36Sopenharmony_ci	int cur_attr = 0, attr;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	list_for_each_entry(fp, fmt_list_head, list) {
10062306a36Sopenharmony_ci		if (strict_match) {
10162306a36Sopenharmony_ci			if (!(fp->formats & pcm_format_to_bits(format)))
10262306a36Sopenharmony_ci				continue;
10362306a36Sopenharmony_ci			if (fp->channels != channels)
10462306a36Sopenharmony_ci				continue;
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci		if (rate < fp->rate_min || rate > fp->rate_max)
10762306a36Sopenharmony_ci			continue;
10862306a36Sopenharmony_ci		if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
10962306a36Sopenharmony_ci			unsigned int i;
11062306a36Sopenharmony_ci			for (i = 0; i < fp->nr_rates; i++)
11162306a36Sopenharmony_ci				if (fp->rate_table[i] == rate)
11262306a36Sopenharmony_ci					break;
11362306a36Sopenharmony_ci			if (i >= fp->nr_rates)
11462306a36Sopenharmony_ci				continue;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci		attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
11762306a36Sopenharmony_ci		if (!found) {
11862306a36Sopenharmony_ci			found = fp;
11962306a36Sopenharmony_ci			cur_attr = attr;
12062306a36Sopenharmony_ci			continue;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci		/* avoid async out and adaptive in if the other method
12362306a36Sopenharmony_ci		 * supports the same format.
12462306a36Sopenharmony_ci		 * this is a workaround for the case like
12562306a36Sopenharmony_ci		 * M-audio audiophile USB.
12662306a36Sopenharmony_ci		 */
12762306a36Sopenharmony_ci		if (subs && attr != cur_attr) {
12862306a36Sopenharmony_ci			if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
12962306a36Sopenharmony_ci			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
13062306a36Sopenharmony_ci			    (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
13162306a36Sopenharmony_ci			     subs->direction == SNDRV_PCM_STREAM_CAPTURE))
13262306a36Sopenharmony_ci				continue;
13362306a36Sopenharmony_ci			if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
13462306a36Sopenharmony_ci			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
13562306a36Sopenharmony_ci			    (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
13662306a36Sopenharmony_ci			     subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
13762306a36Sopenharmony_ci				found = fp;
13862306a36Sopenharmony_ci				cur_attr = attr;
13962306a36Sopenharmony_ci				continue;
14062306a36Sopenharmony_ci			}
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci		/* find the format with the largest max. packet size */
14362306a36Sopenharmony_ci		if (fp->maxpacksize > found->maxpacksize) {
14462306a36Sopenharmony_ci			found = fp;
14562306a36Sopenharmony_ci			cur_attr = attr;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	return found;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic const struct audioformat *
15262306a36Sopenharmony_cifind_substream_format(struct snd_usb_substream *subs,
15362306a36Sopenharmony_ci		      const struct snd_pcm_hw_params *params)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return find_format(&subs->fmt_list, params_format(params),
15662306a36Sopenharmony_ci			   params_rate(params), params_channels(params),
15762306a36Sopenharmony_ci			   true, subs);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cibool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	const struct audioformat *fp;
16362306a36Sopenharmony_ci	struct snd_usb_audio *chip;
16462306a36Sopenharmony_ci	int rate = -1;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (!subs)
16762306a36Sopenharmony_ci		return false;
16862306a36Sopenharmony_ci	chip = subs->stream->chip;
16962306a36Sopenharmony_ci	if (!(chip->quirk_flags & QUIRK_FLAG_FIXED_RATE))
17062306a36Sopenharmony_ci		return false;
17162306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
17262306a36Sopenharmony_ci		if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
17362306a36Sopenharmony_ci			return false;
17462306a36Sopenharmony_ci		if (fp->nr_rates < 1)
17562306a36Sopenharmony_ci			continue;
17662306a36Sopenharmony_ci		if (fp->nr_rates > 1)
17762306a36Sopenharmony_ci			return false;
17862306a36Sopenharmony_ci		if (rate < 0) {
17962306a36Sopenharmony_ci			rate = fp->rate_table[0];
18062306a36Sopenharmony_ci			continue;
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci		if (rate != fp->rate_table[0])
18362306a36Sopenharmony_ci			return false;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	return true;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int init_pitch_v1(struct snd_usb_audio *chip, int ep)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct usb_device *dev = chip->dev;
19162306a36Sopenharmony_ci	unsigned char data[1];
19262306a36Sopenharmony_ci	int err;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	data[0] = 1;
19562306a36Sopenharmony_ci	err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
19662306a36Sopenharmony_ci			      USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
19762306a36Sopenharmony_ci			      UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
19862306a36Sopenharmony_ci			      data, sizeof(data));
19962306a36Sopenharmony_ci	return err;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int init_pitch_v2(struct snd_usb_audio *chip, int ep)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct usb_device *dev = chip->dev;
20562306a36Sopenharmony_ci	unsigned char data[1];
20662306a36Sopenharmony_ci	int err;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	data[0] = 1;
20962306a36Sopenharmony_ci	err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
21062306a36Sopenharmony_ci			      USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
21162306a36Sopenharmony_ci			      UAC2_EP_CS_PITCH << 8, 0,
21262306a36Sopenharmony_ci			      data, sizeof(data));
21362306a36Sopenharmony_ci	return err;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/*
21762306a36Sopenharmony_ci * initialize the pitch control and sample rate
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_ciint snd_usb_init_pitch(struct snd_usb_audio *chip,
22062306a36Sopenharmony_ci		       const struct audioformat *fmt)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	int err;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* if endpoint doesn't have pitch control, bail out */
22562306a36Sopenharmony_ci	if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
22662306a36Sopenharmony_ci		return 0;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	usb_audio_dbg(chip, "enable PITCH for EP 0x%x\n", fmt->endpoint);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	switch (fmt->protocol) {
23162306a36Sopenharmony_ci	case UAC_VERSION_1:
23262306a36Sopenharmony_ci		err = init_pitch_v1(chip, fmt->endpoint);
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci	case UAC_VERSION_2:
23562306a36Sopenharmony_ci		err = init_pitch_v2(chip, fmt->endpoint);
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	default:
23862306a36Sopenharmony_ci		return 0;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (err < 0) {
24262306a36Sopenharmony_ci		usb_audio_err(chip, "failed to enable PITCH for EP 0x%x\n",
24362306a36Sopenharmony_ci			      fmt->endpoint);
24462306a36Sopenharmony_ci		return err;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	bool stopped = 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
25562306a36Sopenharmony_ci		snd_usb_endpoint_stop(subs->sync_endpoint, keep_pending);
25662306a36Sopenharmony_ci		stopped = true;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
25962306a36Sopenharmony_ci		snd_usb_endpoint_stop(subs->data_endpoint, keep_pending);
26062306a36Sopenharmony_ci		stopped = true;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	return stopped;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int start_endpoints(struct snd_usb_substream *subs)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int err;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (!subs->data_endpoint)
27062306a36Sopenharmony_ci		return -EINVAL;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
27362306a36Sopenharmony_ci		err = snd_usb_endpoint_start(subs->data_endpoint);
27462306a36Sopenharmony_ci		if (err < 0) {
27562306a36Sopenharmony_ci			clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
27662306a36Sopenharmony_ci			goto error;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (subs->sync_endpoint &&
28162306a36Sopenharmony_ci	    !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
28262306a36Sopenharmony_ci		err = snd_usb_endpoint_start(subs->sync_endpoint);
28362306a36Sopenharmony_ci		if (err < 0) {
28462306a36Sopenharmony_ci			clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
28562306a36Sopenharmony_ci			goto error;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci error:
29262306a36Sopenharmony_ci	stop_endpoints(subs, false);
29362306a36Sopenharmony_ci	return err;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void sync_pending_stops(struct snd_usb_substream *subs)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint);
29962306a36Sopenharmony_ci	snd_usb_endpoint_sync_pending_stop(subs->data_endpoint);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/* PCM sync_stop callback */
30362306a36Sopenharmony_cistatic int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct snd_usb_substream *subs = substream->runtime->private_data;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	sync_pending_stops(subs);
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/* Set up sync endpoint */
31262306a36Sopenharmony_ciint snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip,
31362306a36Sopenharmony_ci				    struct audioformat *fmt)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct usb_device *dev = chip->dev;
31662306a36Sopenharmony_ci	struct usb_host_interface *alts;
31762306a36Sopenharmony_ci	struct usb_interface_descriptor *altsd;
31862306a36Sopenharmony_ci	unsigned int ep, attr, sync_attr;
31962306a36Sopenharmony_ci	bool is_playback;
32062306a36Sopenharmony_ci	int err;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (fmt->sync_ep)
32362306a36Sopenharmony_ci		return 0; /* already set up */
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting);
32662306a36Sopenharmony_ci	if (!alts)
32762306a36Sopenharmony_ci		return 0;
32862306a36Sopenharmony_ci	altsd = get_iface_desc(alts);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	err = snd_usb_parse_implicit_fb_quirk(chip, fmt, alts);
33162306a36Sopenharmony_ci	if (err > 0)
33262306a36Sopenharmony_ci		return 0; /* matched */
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/*
33562306a36Sopenharmony_ci	 * Generic sync EP handling
33662306a36Sopenharmony_ci	 */
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (fmt->ep_idx > 0 || altsd->bNumEndpoints < 2)
33962306a36Sopenharmony_ci		return 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	is_playback = !(get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN);
34262306a36Sopenharmony_ci	attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
34362306a36Sopenharmony_ci	if ((is_playback && (attr == USB_ENDPOINT_SYNC_SYNC ||
34462306a36Sopenharmony_ci			     attr == USB_ENDPOINT_SYNC_ADAPTIVE)) ||
34562306a36Sopenharmony_ci	    (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE))
34662306a36Sopenharmony_ci		return 0;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	sync_attr = get_endpoint(alts, 1)->bmAttributes;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/*
35162306a36Sopenharmony_ci	 * In case of illegal SYNC_NONE for OUT endpoint, we keep going to see
35262306a36Sopenharmony_ci	 * if we don't find a sync endpoint, as on M-Audio Transit. In case of
35362306a36Sopenharmony_ci	 * error fall back to SYNC mode and don't create sync endpoint
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* check sync-pipe endpoint */
35762306a36Sopenharmony_ci	/* ... and check descriptor size before accessing bSynchAddress
35862306a36Sopenharmony_ci	   because there is a version of the SB Audigy 2 NX firmware lacking
35962306a36Sopenharmony_ci	   the audio fields in the endpoint descriptors */
36062306a36Sopenharmony_ci	if ((sync_attr & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
36162306a36Sopenharmony_ci	    (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
36262306a36Sopenharmony_ci	     get_endpoint(alts, 1)->bSynchAddress != 0)) {
36362306a36Sopenharmony_ci		dev_err(&dev->dev,
36462306a36Sopenharmony_ci			"%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
36562306a36Sopenharmony_ci			   fmt->iface, fmt->altsetting,
36662306a36Sopenharmony_ci			   get_endpoint(alts, 1)->bmAttributes,
36762306a36Sopenharmony_ci			   get_endpoint(alts, 1)->bLength,
36862306a36Sopenharmony_ci			   get_endpoint(alts, 1)->bSynchAddress);
36962306a36Sopenharmony_ci		if (is_playback && attr == USB_ENDPOINT_SYNC_NONE)
37062306a36Sopenharmony_ci			return 0;
37162306a36Sopenharmony_ci		return -EINVAL;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	ep = get_endpoint(alts, 1)->bEndpointAddress;
37462306a36Sopenharmony_ci	if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
37562306a36Sopenharmony_ci	    get_endpoint(alts, 0)->bSynchAddress != 0 &&
37662306a36Sopenharmony_ci	    ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
37762306a36Sopenharmony_ci	     (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
37862306a36Sopenharmony_ci		dev_err(&dev->dev,
37962306a36Sopenharmony_ci			"%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
38062306a36Sopenharmony_ci			   fmt->iface, fmt->altsetting,
38162306a36Sopenharmony_ci			   is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
38262306a36Sopenharmony_ci		if (is_playback && attr == USB_ENDPOINT_SYNC_NONE)
38362306a36Sopenharmony_ci			return 0;
38462306a36Sopenharmony_ci		return -EINVAL;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	fmt->sync_ep = ep;
38862306a36Sopenharmony_ci	fmt->sync_iface = altsd->bInterfaceNumber;
38962306a36Sopenharmony_ci	fmt->sync_altsetting = altsd->bAlternateSetting;
39062306a36Sopenharmony_ci	fmt->sync_ep_idx = 1;
39162306a36Sopenharmony_ci	if ((sync_attr & USB_ENDPOINT_USAGE_MASK) == USB_ENDPOINT_USAGE_IMPLICIT_FB)
39262306a36Sopenharmony_ci		fmt->implicit_fb = 1;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	dev_dbg(&dev->dev, "%d:%d: found sync_ep=0x%x, iface=%d, alt=%d, implicit_fb=%d\n",
39562306a36Sopenharmony_ci		fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface,
39662306a36Sopenharmony_ci		fmt->sync_altsetting, fmt->implicit_fb);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int snd_usb_pcm_change_state(struct snd_usb_substream *subs, int state)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!subs->str_pd)
40662306a36Sopenharmony_ci		return 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	ret = snd_usb_power_domain_set(subs->stream->chip, subs->str_pd, state);
40962306a36Sopenharmony_ci	if (ret < 0) {
41062306a36Sopenharmony_ci		dev_err(&subs->dev->dev,
41162306a36Sopenharmony_ci			"Cannot change Power Domain ID: %d to state: %d. Err: %d\n",
41262306a36Sopenharmony_ci			subs->str_pd->pd_id, state, ret);
41362306a36Sopenharmony_ci		return ret;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return 0;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ciint snd_usb_pcm_suspend(struct snd_usb_stream *as)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	int ret;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ret = snd_usb_pcm_change_state(&as->substream[0], UAC3_PD_STATE_D2);
42462306a36Sopenharmony_ci	if (ret < 0)
42562306a36Sopenharmony_ci		return ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ret = snd_usb_pcm_change_state(&as->substream[1], UAC3_PD_STATE_D2);
42862306a36Sopenharmony_ci	if (ret < 0)
42962306a36Sopenharmony_ci		return ret;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciint snd_usb_pcm_resume(struct snd_usb_stream *as)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	int ret;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	ret = snd_usb_pcm_change_state(&as->substream[0], UAC3_PD_STATE_D1);
43962306a36Sopenharmony_ci	if (ret < 0)
44062306a36Sopenharmony_ci		return ret;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	ret = snd_usb_pcm_change_state(&as->substream[1], UAC3_PD_STATE_D1);
44362306a36Sopenharmony_ci	if (ret < 0)
44462306a36Sopenharmony_ci		return ret;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void close_endpoints(struct snd_usb_audio *chip,
45062306a36Sopenharmony_ci			    struct snd_usb_substream *subs)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	if (subs->data_endpoint) {
45362306a36Sopenharmony_ci		snd_usb_endpoint_set_sync(chip, subs->data_endpoint, NULL);
45462306a36Sopenharmony_ci		snd_usb_endpoint_close(chip, subs->data_endpoint);
45562306a36Sopenharmony_ci		subs->data_endpoint = NULL;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (subs->sync_endpoint) {
45962306a36Sopenharmony_ci		snd_usb_endpoint_close(chip, subs->sync_endpoint);
46062306a36Sopenharmony_ci		subs->sync_endpoint = NULL;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci/*
46562306a36Sopenharmony_ci * hw_params callback
46662306a36Sopenharmony_ci *
46762306a36Sopenharmony_ci * allocate a buffer and set the given audio format.
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * so far we use a physically linear buffer although packetize transfer
47062306a36Sopenharmony_ci * doesn't need a continuous area.
47162306a36Sopenharmony_ci * if sg buffer is supported on the later version of alsa, we'll follow
47262306a36Sopenharmony_ci * that.
47362306a36Sopenharmony_ci */
47462306a36Sopenharmony_cistatic int snd_usb_hw_params(struct snd_pcm_substream *substream,
47562306a36Sopenharmony_ci			     struct snd_pcm_hw_params *hw_params)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct snd_usb_substream *subs = substream->runtime->private_data;
47862306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
47962306a36Sopenharmony_ci	const struct audioformat *fmt;
48062306a36Sopenharmony_ci	const struct audioformat *sync_fmt;
48162306a36Sopenharmony_ci	bool fixed_rate, sync_fixed_rate;
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	ret = snd_media_start_pipeline(subs);
48562306a36Sopenharmony_ci	if (ret)
48662306a36Sopenharmony_ci		return ret;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	fixed_rate = snd_usb_pcm_has_fixed_rate(subs);
48962306a36Sopenharmony_ci	fmt = find_substream_format(subs, hw_params);
49062306a36Sopenharmony_ci	if (!fmt) {
49162306a36Sopenharmony_ci		usb_audio_dbg(chip,
49262306a36Sopenharmony_ci			      "cannot find format: format=%s, rate=%d, channels=%d\n",
49362306a36Sopenharmony_ci			      snd_pcm_format_name(params_format(hw_params)),
49462306a36Sopenharmony_ci			      params_rate(hw_params), params_channels(hw_params));
49562306a36Sopenharmony_ci		ret = -EINVAL;
49662306a36Sopenharmony_ci		goto stop_pipeline;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (fmt->implicit_fb) {
50062306a36Sopenharmony_ci		sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt,
50162306a36Sopenharmony_ci								hw_params,
50262306a36Sopenharmony_ci								!substream->stream,
50362306a36Sopenharmony_ci								&sync_fixed_rate);
50462306a36Sopenharmony_ci		if (!sync_fmt) {
50562306a36Sopenharmony_ci			usb_audio_dbg(chip,
50662306a36Sopenharmony_ci				      "cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n",
50762306a36Sopenharmony_ci				      fmt->sync_ep, fmt->sync_iface,
50862306a36Sopenharmony_ci				      fmt->sync_altsetting,
50962306a36Sopenharmony_ci				      snd_pcm_format_name(params_format(hw_params)),
51062306a36Sopenharmony_ci				      params_rate(hw_params), params_channels(hw_params));
51162306a36Sopenharmony_ci			ret = -EINVAL;
51262306a36Sopenharmony_ci			goto stop_pipeline;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci	} else {
51562306a36Sopenharmony_ci		sync_fmt = fmt;
51662306a36Sopenharmony_ci		sync_fixed_rate = fixed_rate;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ret = snd_usb_lock_shutdown(chip);
52062306a36Sopenharmony_ci	if (ret < 0)
52162306a36Sopenharmony_ci		goto stop_pipeline;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
52462306a36Sopenharmony_ci	if (ret < 0)
52562306a36Sopenharmony_ci		goto unlock;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (subs->data_endpoint) {
52862306a36Sopenharmony_ci		if (snd_usb_endpoint_compatible(chip, subs->data_endpoint,
52962306a36Sopenharmony_ci						fmt, hw_params))
53062306a36Sopenharmony_ci			goto unlock;
53162306a36Sopenharmony_ci		if (stop_endpoints(subs, false))
53262306a36Sopenharmony_ci			sync_pending_stops(subs);
53362306a36Sopenharmony_ci		close_endpoints(chip, subs);
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false, fixed_rate);
53762306a36Sopenharmony_ci	if (!subs->data_endpoint) {
53862306a36Sopenharmony_ci		ret = -EINVAL;
53962306a36Sopenharmony_ci		goto unlock;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (fmt->sync_ep) {
54362306a36Sopenharmony_ci		subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt,
54462306a36Sopenharmony_ci							    hw_params,
54562306a36Sopenharmony_ci							    fmt == sync_fmt,
54662306a36Sopenharmony_ci							    sync_fixed_rate);
54762306a36Sopenharmony_ci		if (!subs->sync_endpoint) {
54862306a36Sopenharmony_ci			ret = -EINVAL;
54962306a36Sopenharmony_ci			goto unlock;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		snd_usb_endpoint_set_sync(chip, subs->data_endpoint,
55362306a36Sopenharmony_ci					  subs->sync_endpoint);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
55762306a36Sopenharmony_ci	subs->cur_audiofmt = fmt;
55862306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (!subs->data_endpoint->need_setup)
56162306a36Sopenharmony_ci		goto unlock;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (subs->sync_endpoint) {
56462306a36Sopenharmony_ci		ret = snd_usb_endpoint_set_params(chip, subs->sync_endpoint);
56562306a36Sopenharmony_ci		if (ret < 0)
56662306a36Sopenharmony_ci			goto unlock;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	ret = snd_usb_endpoint_set_params(chip, subs->data_endpoint);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci unlock:
57262306a36Sopenharmony_ci	if (ret < 0)
57362306a36Sopenharmony_ci		close_endpoints(chip, subs);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	snd_usb_unlock_shutdown(chip);
57662306a36Sopenharmony_ci stop_pipeline:
57762306a36Sopenharmony_ci	if (ret < 0)
57862306a36Sopenharmony_ci		snd_media_stop_pipeline(subs);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return ret;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/*
58462306a36Sopenharmony_ci * hw_free callback
58562306a36Sopenharmony_ci *
58662306a36Sopenharmony_ci * reset the audio format and release the buffer
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_cistatic int snd_usb_hw_free(struct snd_pcm_substream *substream)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct snd_usb_substream *subs = substream->runtime->private_data;
59162306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	snd_media_stop_pipeline(subs);
59462306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
59562306a36Sopenharmony_ci	subs->cur_audiofmt = NULL;
59662306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
59762306a36Sopenharmony_ci	if (!snd_usb_lock_shutdown(chip)) {
59862306a36Sopenharmony_ci		if (stop_endpoints(subs, false))
59962306a36Sopenharmony_ci			sync_pending_stops(subs);
60062306a36Sopenharmony_ci		close_endpoints(chip, subs);
60162306a36Sopenharmony_ci		snd_usb_unlock_shutdown(chip);
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return 0;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/* free-wheeling mode? (e.g. dmix) */
60862306a36Sopenharmony_cistatic int in_free_wheeling_mode(struct snd_pcm_runtime *runtime)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	return runtime->stop_threshold > runtime->buffer_size;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/* check whether early start is needed for playback stream */
61462306a36Sopenharmony_cistatic int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
61562306a36Sopenharmony_ci					 struct snd_usb_substream *subs)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (subs->direction == SNDRV_PCM_STREAM_CAPTURE)
62062306a36Sopenharmony_ci		return false;
62162306a36Sopenharmony_ci	/* disabled via module option? */
62262306a36Sopenharmony_ci	if (!chip->lowlatency)
62362306a36Sopenharmony_ci		return false;
62462306a36Sopenharmony_ci	if (in_free_wheeling_mode(runtime))
62562306a36Sopenharmony_ci		return false;
62662306a36Sopenharmony_ci	/* implicit feedback mode has own operation mode */
62762306a36Sopenharmony_ci	if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
62862306a36Sopenharmony_ci		return false;
62962306a36Sopenharmony_ci	return true;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci/*
63362306a36Sopenharmony_ci * prepare callback
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * only a few subtle things...
63662306a36Sopenharmony_ci */
63762306a36Sopenharmony_cistatic int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
64062306a36Sopenharmony_ci	struct snd_usb_substream *subs = runtime->private_data;
64162306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
64262306a36Sopenharmony_ci	int retry = 0;
64362306a36Sopenharmony_ci	int ret;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ret = snd_usb_lock_shutdown(chip);
64662306a36Sopenharmony_ci	if (ret < 0)
64762306a36Sopenharmony_ci		return ret;
64862306a36Sopenharmony_ci	if (snd_BUG_ON(!subs->data_endpoint)) {
64962306a36Sopenharmony_ci		ret = -EIO;
65062306a36Sopenharmony_ci		goto unlock;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
65462306a36Sopenharmony_ci	if (ret < 0)
65562306a36Sopenharmony_ci		goto unlock;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci again:
65862306a36Sopenharmony_ci	if (subs->sync_endpoint) {
65962306a36Sopenharmony_ci		ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);
66062306a36Sopenharmony_ci		if (ret < 0)
66162306a36Sopenharmony_ci			goto unlock;
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	ret = snd_usb_endpoint_prepare(chip, subs->data_endpoint);
66562306a36Sopenharmony_ci	if (ret < 0)
66662306a36Sopenharmony_ci		goto unlock;
66762306a36Sopenharmony_ci	else if (ret > 0)
66862306a36Sopenharmony_ci		snd_usb_set_format_quirk(subs, subs->cur_audiofmt);
66962306a36Sopenharmony_ci	ret = 0;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* reset the pointer */
67262306a36Sopenharmony_ci	subs->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size);
67362306a36Sopenharmony_ci	subs->inflight_bytes = 0;
67462306a36Sopenharmony_ci	subs->hwptr_done = 0;
67562306a36Sopenharmony_ci	subs->transfer_done = 0;
67662306a36Sopenharmony_ci	subs->last_frame_number = 0;
67762306a36Sopenharmony_ci	subs->period_elapsed_pending = 0;
67862306a36Sopenharmony_ci	runtime->delay = 0;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
68162306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
68262306a36Sopenharmony_ci	    !subs->lowlatency_playback) {
68362306a36Sopenharmony_ci		ret = start_endpoints(subs);
68462306a36Sopenharmony_ci		/* if XRUN happens at starting streams (possibly with implicit
68562306a36Sopenharmony_ci		 * fb case), restart again, but only try once.
68662306a36Sopenharmony_ci		 */
68762306a36Sopenharmony_ci		if (ret == -EPIPE && !retry++) {
68862306a36Sopenharmony_ci			sync_pending_stops(subs);
68962306a36Sopenharmony_ci			goto again;
69062306a36Sopenharmony_ci		}
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci unlock:
69362306a36Sopenharmony_ci	snd_usb_unlock_shutdown(chip);
69462306a36Sopenharmony_ci	return ret;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci/*
69862306a36Sopenharmony_ci * h/w constraints
69962306a36Sopenharmony_ci */
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci#ifdef HW_CONST_DEBUG
70262306a36Sopenharmony_ci#define hwc_debug(fmt, args...) pr_debug(fmt, ##args)
70362306a36Sopenharmony_ci#else
70462306a36Sopenharmony_ci#define hwc_debug(fmt, args...) do { } while(0)
70562306a36Sopenharmony_ci#endif
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_usb_hardware =
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	.info =			SNDRV_PCM_INFO_MMAP |
71062306a36Sopenharmony_ci				SNDRV_PCM_INFO_MMAP_VALID |
71162306a36Sopenharmony_ci				SNDRV_PCM_INFO_BATCH |
71262306a36Sopenharmony_ci				SNDRV_PCM_INFO_INTERLEAVED |
71362306a36Sopenharmony_ci				SNDRV_PCM_INFO_BLOCK_TRANSFER |
71462306a36Sopenharmony_ci				SNDRV_PCM_INFO_PAUSE,
71562306a36Sopenharmony_ci	.channels_min =		1,
71662306a36Sopenharmony_ci	.channels_max =		256,
71762306a36Sopenharmony_ci	.buffer_bytes_max =	INT_MAX, /* limited by BUFFER_TIME later */
71862306a36Sopenharmony_ci	.period_bytes_min =	64,
71962306a36Sopenharmony_ci	.period_bytes_max =	INT_MAX, /* limited by PERIOD_TIME later */
72062306a36Sopenharmony_ci	.periods_min =		2,
72162306a36Sopenharmony_ci	.periods_max =		1024,
72262306a36Sopenharmony_ci};
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int hw_check_valid_format(struct snd_usb_substream *subs,
72562306a36Sopenharmony_ci				 struct snd_pcm_hw_params *params,
72662306a36Sopenharmony_ci				 const struct audioformat *fp)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
72962306a36Sopenharmony_ci	struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
73062306a36Sopenharmony_ci	struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
73162306a36Sopenharmony_ci	struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
73262306a36Sopenharmony_ci	struct snd_mask check_fmts;
73362306a36Sopenharmony_ci	unsigned int ptime;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* check the format */
73662306a36Sopenharmony_ci	snd_mask_none(&check_fmts);
73762306a36Sopenharmony_ci	check_fmts.bits[0] = (u32)fp->formats;
73862306a36Sopenharmony_ci	check_fmts.bits[1] = (u32)(fp->formats >> 32);
73962306a36Sopenharmony_ci	snd_mask_intersect(&check_fmts, fmts);
74062306a36Sopenharmony_ci	if (snd_mask_empty(&check_fmts)) {
74162306a36Sopenharmony_ci		hwc_debug("   > check: no supported format 0x%llx\n", fp->formats);
74262306a36Sopenharmony_ci		return 0;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci	/* check the channels */
74562306a36Sopenharmony_ci	if (fp->channels < ct->min || fp->channels > ct->max) {
74662306a36Sopenharmony_ci		hwc_debug("   > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);
74762306a36Sopenharmony_ci		return 0;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	/* check the rate is within the range */
75062306a36Sopenharmony_ci	if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {
75162306a36Sopenharmony_ci		hwc_debug("   > check: rate_min %d > max %d\n", fp->rate_min, it->max);
75262306a36Sopenharmony_ci		return 0;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci	if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {
75562306a36Sopenharmony_ci		hwc_debug("   > check: rate_max %d < min %d\n", fp->rate_max, it->min);
75662306a36Sopenharmony_ci		return 0;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci	/* check whether the period time is >= the data packet interval */
75962306a36Sopenharmony_ci	if (subs->speed != USB_SPEED_FULL) {
76062306a36Sopenharmony_ci		ptime = 125 * (1 << fp->datainterval);
76162306a36Sopenharmony_ci		if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
76262306a36Sopenharmony_ci			hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
76362306a36Sopenharmony_ci			return 0;
76462306a36Sopenharmony_ci		}
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	return 1;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int apply_hw_params_minmax(struct snd_interval *it, unsigned int rmin,
77062306a36Sopenharmony_ci				  unsigned int rmax)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	int changed;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (rmin > rmax) {
77562306a36Sopenharmony_ci		hwc_debug("  --> get empty\n");
77662306a36Sopenharmony_ci		it->empty = 1;
77762306a36Sopenharmony_ci		return -EINVAL;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	changed = 0;
78162306a36Sopenharmony_ci	if (it->min < rmin) {
78262306a36Sopenharmony_ci		it->min = rmin;
78362306a36Sopenharmony_ci		it->openmin = 0;
78462306a36Sopenharmony_ci		changed = 1;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci	if (it->max > rmax) {
78762306a36Sopenharmony_ci		it->max = rmax;
78862306a36Sopenharmony_ci		it->openmax = 0;
78962306a36Sopenharmony_ci		changed = 1;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci	if (snd_interval_checkempty(it)) {
79262306a36Sopenharmony_ci		it->empty = 1;
79362306a36Sopenharmony_ci		return -EINVAL;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci	hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
79662306a36Sopenharmony_ci	return changed;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci/* get the specified endpoint object that is being used by other streams
80062306a36Sopenharmony_ci * (i.e. the parameter is locked)
80162306a36Sopenharmony_ci */
80262306a36Sopenharmony_cistatic const struct snd_usb_endpoint *
80362306a36Sopenharmony_ciget_endpoint_in_use(struct snd_usb_audio *chip, int endpoint,
80462306a36Sopenharmony_ci		    const struct snd_usb_endpoint *ref_ep)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	const struct snd_usb_endpoint *ep;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	ep = snd_usb_get_endpoint(chip, endpoint);
80962306a36Sopenharmony_ci	if (ep && ep->cur_audiofmt && (ep != ref_ep || ep->opened > 1))
81062306a36Sopenharmony_ci		return ep;
81162306a36Sopenharmony_ci	return NULL;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int hw_rule_rate(struct snd_pcm_hw_params *params,
81562306a36Sopenharmony_ci			struct snd_pcm_hw_rule *rule)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct snd_usb_substream *subs = rule->private;
81862306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
81962306a36Sopenharmony_ci	const struct snd_usb_endpoint *ep;
82062306a36Sopenharmony_ci	const struct audioformat *fp;
82162306a36Sopenharmony_ci	struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
82262306a36Sopenharmony_ci	unsigned int rmin, rmax, r;
82362306a36Sopenharmony_ci	int i;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
82662306a36Sopenharmony_ci	rmin = UINT_MAX;
82762306a36Sopenharmony_ci	rmax = 0;
82862306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
82962306a36Sopenharmony_ci		if (!hw_check_valid_format(subs, params, fp))
83062306a36Sopenharmony_ci			continue;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		ep = get_endpoint_in_use(chip, fp->endpoint,
83362306a36Sopenharmony_ci					 subs->data_endpoint);
83462306a36Sopenharmony_ci		if (ep) {
83562306a36Sopenharmony_ci			hwc_debug("rate limit %d for ep#%x\n",
83662306a36Sopenharmony_ci				  ep->cur_rate, fp->endpoint);
83762306a36Sopenharmony_ci			rmin = min(rmin, ep->cur_rate);
83862306a36Sopenharmony_ci			rmax = max(rmax, ep->cur_rate);
83962306a36Sopenharmony_ci			continue;
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		if (fp->implicit_fb) {
84362306a36Sopenharmony_ci			ep = get_endpoint_in_use(chip, fp->sync_ep,
84462306a36Sopenharmony_ci						 subs->sync_endpoint);
84562306a36Sopenharmony_ci			if (ep) {
84662306a36Sopenharmony_ci				hwc_debug("rate limit %d for sync_ep#%x\n",
84762306a36Sopenharmony_ci					  ep->cur_rate, fp->sync_ep);
84862306a36Sopenharmony_ci				rmin = min(rmin, ep->cur_rate);
84962306a36Sopenharmony_ci				rmax = max(rmax, ep->cur_rate);
85062306a36Sopenharmony_ci				continue;
85162306a36Sopenharmony_ci			}
85262306a36Sopenharmony_ci		}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
85562306a36Sopenharmony_ci		if (r > 0) {
85662306a36Sopenharmony_ci			if (!snd_interval_test(it, r))
85762306a36Sopenharmony_ci				continue;
85862306a36Sopenharmony_ci			rmin = min(rmin, r);
85962306a36Sopenharmony_ci			rmax = max(rmax, r);
86062306a36Sopenharmony_ci			continue;
86162306a36Sopenharmony_ci		}
86262306a36Sopenharmony_ci		if (fp->rate_table && fp->nr_rates) {
86362306a36Sopenharmony_ci			for (i = 0; i < fp->nr_rates; i++) {
86462306a36Sopenharmony_ci				r = fp->rate_table[i];
86562306a36Sopenharmony_ci				if (!snd_interval_test(it, r))
86662306a36Sopenharmony_ci					continue;
86762306a36Sopenharmony_ci				rmin = min(rmin, r);
86862306a36Sopenharmony_ci				rmax = max(rmax, r);
86962306a36Sopenharmony_ci			}
87062306a36Sopenharmony_ci		} else {
87162306a36Sopenharmony_ci			rmin = min(rmin, fp->rate_min);
87262306a36Sopenharmony_ci			rmax = max(rmax, fp->rate_max);
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return apply_hw_params_minmax(it, rmin, rmax);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int hw_rule_channels(struct snd_pcm_hw_params *params,
88162306a36Sopenharmony_ci			    struct snd_pcm_hw_rule *rule)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct snd_usb_substream *subs = rule->private;
88462306a36Sopenharmony_ci	const struct audioformat *fp;
88562306a36Sopenharmony_ci	struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
88662306a36Sopenharmony_ci	unsigned int rmin, rmax;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
88962306a36Sopenharmony_ci	rmin = UINT_MAX;
89062306a36Sopenharmony_ci	rmax = 0;
89162306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
89262306a36Sopenharmony_ci		if (!hw_check_valid_format(subs, params, fp))
89362306a36Sopenharmony_ci			continue;
89462306a36Sopenharmony_ci		rmin = min(rmin, fp->channels);
89562306a36Sopenharmony_ci		rmax = max(rmax, fp->channels);
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return apply_hw_params_minmax(it, rmin, rmax);
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic int apply_hw_params_format_bits(struct snd_mask *fmt, u64 fbits)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	u32 oldbits[2];
90462306a36Sopenharmony_ci	int changed;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	oldbits[0] = fmt->bits[0];
90762306a36Sopenharmony_ci	oldbits[1] = fmt->bits[1];
90862306a36Sopenharmony_ci	fmt->bits[0] &= (u32)fbits;
90962306a36Sopenharmony_ci	fmt->bits[1] &= (u32)(fbits >> 32);
91062306a36Sopenharmony_ci	if (!fmt->bits[0] && !fmt->bits[1]) {
91162306a36Sopenharmony_ci		hwc_debug("  --> get empty\n");
91262306a36Sopenharmony_ci		return -EINVAL;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci	changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);
91562306a36Sopenharmony_ci	hwc_debug("  --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);
91662306a36Sopenharmony_ci	return changed;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int hw_rule_format(struct snd_pcm_hw_params *params,
92062306a36Sopenharmony_ci			  struct snd_pcm_hw_rule *rule)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct snd_usb_substream *subs = rule->private;
92362306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
92462306a36Sopenharmony_ci	const struct snd_usb_endpoint *ep;
92562306a36Sopenharmony_ci	const struct audioformat *fp;
92662306a36Sopenharmony_ci	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
92762306a36Sopenharmony_ci	u64 fbits;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
93062306a36Sopenharmony_ci	fbits = 0;
93162306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
93262306a36Sopenharmony_ci		if (!hw_check_valid_format(subs, params, fp))
93362306a36Sopenharmony_ci			continue;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		ep = get_endpoint_in_use(chip, fp->endpoint,
93662306a36Sopenharmony_ci					 subs->data_endpoint);
93762306a36Sopenharmony_ci		if (ep) {
93862306a36Sopenharmony_ci			hwc_debug("format limit %d for ep#%x\n",
93962306a36Sopenharmony_ci				  ep->cur_format, fp->endpoint);
94062306a36Sopenharmony_ci			fbits |= pcm_format_to_bits(ep->cur_format);
94162306a36Sopenharmony_ci			continue;
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		if (fp->implicit_fb) {
94562306a36Sopenharmony_ci			ep = get_endpoint_in_use(chip, fp->sync_ep,
94662306a36Sopenharmony_ci						 subs->sync_endpoint);
94762306a36Sopenharmony_ci			if (ep) {
94862306a36Sopenharmony_ci				hwc_debug("format limit %d for sync_ep#%x\n",
94962306a36Sopenharmony_ci					  ep->cur_format, fp->sync_ep);
95062306a36Sopenharmony_ci				fbits |= pcm_format_to_bits(ep->cur_format);
95162306a36Sopenharmony_ci				continue;
95262306a36Sopenharmony_ci			}
95362306a36Sopenharmony_ci		}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		fbits |= fp->formats;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	return apply_hw_params_format_bits(fmt, fbits);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic int hw_rule_period_time(struct snd_pcm_hw_params *params,
96162306a36Sopenharmony_ci			       struct snd_pcm_hw_rule *rule)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct snd_usb_substream *subs = rule->private;
96462306a36Sopenharmony_ci	const struct audioformat *fp;
96562306a36Sopenharmony_ci	struct snd_interval *it;
96662306a36Sopenharmony_ci	unsigned char min_datainterval;
96762306a36Sopenharmony_ci	unsigned int pmin;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
97062306a36Sopenharmony_ci	hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
97162306a36Sopenharmony_ci	min_datainterval = 0xff;
97262306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
97362306a36Sopenharmony_ci		if (!hw_check_valid_format(subs, params, fp))
97462306a36Sopenharmony_ci			continue;
97562306a36Sopenharmony_ci		min_datainterval = min(min_datainterval, fp->datainterval);
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci	if (min_datainterval == 0xff) {
97862306a36Sopenharmony_ci		hwc_debug("  --> get empty\n");
97962306a36Sopenharmony_ci		it->empty = 1;
98062306a36Sopenharmony_ci		return -EINVAL;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci	pmin = 125 * (1 << min_datainterval);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	return apply_hw_params_minmax(it, pmin, UINT_MAX);
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci/* additional hw constraints for implicit feedback mode */
98862306a36Sopenharmony_cistatic int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
98962306a36Sopenharmony_ci					   struct snd_pcm_hw_rule *rule)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	struct snd_usb_substream *subs = rule->private;
99262306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
99362306a36Sopenharmony_ci	const struct audioformat *fp;
99462306a36Sopenharmony_ci	const struct snd_usb_endpoint *ep;
99562306a36Sopenharmony_ci	struct snd_interval *it;
99662306a36Sopenharmony_ci	unsigned int rmin, rmax;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
99962306a36Sopenharmony_ci	hwc_debug("hw_rule_period_size: (%u,%u)\n", it->min, it->max);
100062306a36Sopenharmony_ci	rmin = UINT_MAX;
100162306a36Sopenharmony_ci	rmax = 0;
100262306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
100362306a36Sopenharmony_ci		if (!hw_check_valid_format(subs, params, fp))
100462306a36Sopenharmony_ci			continue;
100562306a36Sopenharmony_ci		ep = get_endpoint_in_use(chip, fp->endpoint,
100662306a36Sopenharmony_ci					 subs->data_endpoint);
100762306a36Sopenharmony_ci		if (ep) {
100862306a36Sopenharmony_ci			hwc_debug("period size limit %d for ep#%x\n",
100962306a36Sopenharmony_ci				  ep->cur_period_frames, fp->endpoint);
101062306a36Sopenharmony_ci			rmin = min(rmin, ep->cur_period_frames);
101162306a36Sopenharmony_ci			rmax = max(rmax, ep->cur_period_frames);
101262306a36Sopenharmony_ci			continue;
101362306a36Sopenharmony_ci		}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		if (fp->implicit_fb) {
101662306a36Sopenharmony_ci			ep = get_endpoint_in_use(chip, fp->sync_ep,
101762306a36Sopenharmony_ci						 subs->sync_endpoint);
101862306a36Sopenharmony_ci			if (ep) {
101962306a36Sopenharmony_ci				hwc_debug("period size limit %d for sync_ep#%x\n",
102062306a36Sopenharmony_ci					  ep->cur_period_frames, fp->sync_ep);
102162306a36Sopenharmony_ci				rmin = min(rmin, ep->cur_period_frames);
102262306a36Sopenharmony_ci				rmax = max(rmax, ep->cur_period_frames);
102362306a36Sopenharmony_ci				continue;
102462306a36Sopenharmony_ci			}
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (!rmax)
102962306a36Sopenharmony_ci		return 0; /* no limit by implicit fb */
103062306a36Sopenharmony_ci	return apply_hw_params_minmax(it, rmin, rmax);
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
103462306a36Sopenharmony_ci				       struct snd_pcm_hw_rule *rule)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct snd_usb_substream *subs = rule->private;
103762306a36Sopenharmony_ci	struct snd_usb_audio *chip = subs->stream->chip;
103862306a36Sopenharmony_ci	const struct audioformat *fp;
103962306a36Sopenharmony_ci	const struct snd_usb_endpoint *ep;
104062306a36Sopenharmony_ci	struct snd_interval *it;
104162306a36Sopenharmony_ci	unsigned int rmin, rmax;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
104462306a36Sopenharmony_ci	hwc_debug("hw_rule_periods: (%u,%u)\n", it->min, it->max);
104562306a36Sopenharmony_ci	rmin = UINT_MAX;
104662306a36Sopenharmony_ci	rmax = 0;
104762306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
104862306a36Sopenharmony_ci		if (!hw_check_valid_format(subs, params, fp))
104962306a36Sopenharmony_ci			continue;
105062306a36Sopenharmony_ci		ep = get_endpoint_in_use(chip, fp->endpoint,
105162306a36Sopenharmony_ci					 subs->data_endpoint);
105262306a36Sopenharmony_ci		if (ep) {
105362306a36Sopenharmony_ci			hwc_debug("periods limit %d for ep#%x\n",
105462306a36Sopenharmony_ci				  ep->cur_buffer_periods, fp->endpoint);
105562306a36Sopenharmony_ci			rmin = min(rmin, ep->cur_buffer_periods);
105662306a36Sopenharmony_ci			rmax = max(rmax, ep->cur_buffer_periods);
105762306a36Sopenharmony_ci			continue;
105862306a36Sopenharmony_ci		}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		if (fp->implicit_fb) {
106162306a36Sopenharmony_ci			ep = get_endpoint_in_use(chip, fp->sync_ep,
106262306a36Sopenharmony_ci						 subs->sync_endpoint);
106362306a36Sopenharmony_ci			if (ep) {
106462306a36Sopenharmony_ci				hwc_debug("periods limit %d for sync_ep#%x\n",
106562306a36Sopenharmony_ci					  ep->cur_buffer_periods, fp->sync_ep);
106662306a36Sopenharmony_ci				rmin = min(rmin, ep->cur_buffer_periods);
106762306a36Sopenharmony_ci				rmax = max(rmax, ep->cur_buffer_periods);
106862306a36Sopenharmony_ci				continue;
106962306a36Sopenharmony_ci			}
107062306a36Sopenharmony_ci		}
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (!rmax)
107462306a36Sopenharmony_ci		return 0; /* no limit by implicit fb */
107562306a36Sopenharmony_ci	return apply_hw_params_minmax(it, rmin, rmax);
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci/*
107962306a36Sopenharmony_ci * set up the runtime hardware information.
108062306a36Sopenharmony_ci */
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	const struct audioformat *fp;
108562306a36Sopenharmony_ci	unsigned int pt, ptmin;
108662306a36Sopenharmony_ci	int param_period_time_if_needed = -1;
108762306a36Sopenharmony_ci	int err;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	runtime->hw.formats = subs->formats;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	runtime->hw.rate_min = 0x7fffffff;
109262306a36Sopenharmony_ci	runtime->hw.rate_max = 0;
109362306a36Sopenharmony_ci	runtime->hw.channels_min = 256;
109462306a36Sopenharmony_ci	runtime->hw.channels_max = 0;
109562306a36Sopenharmony_ci	runtime->hw.rates = 0;
109662306a36Sopenharmony_ci	ptmin = UINT_MAX;
109762306a36Sopenharmony_ci	/* check min/max rates and channels */
109862306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
109962306a36Sopenharmony_ci		runtime->hw.rates |= fp->rates;
110062306a36Sopenharmony_ci		if (runtime->hw.rate_min > fp->rate_min)
110162306a36Sopenharmony_ci			runtime->hw.rate_min = fp->rate_min;
110262306a36Sopenharmony_ci		if (runtime->hw.rate_max < fp->rate_max)
110362306a36Sopenharmony_ci			runtime->hw.rate_max = fp->rate_max;
110462306a36Sopenharmony_ci		if (runtime->hw.channels_min > fp->channels)
110562306a36Sopenharmony_ci			runtime->hw.channels_min = fp->channels;
110662306a36Sopenharmony_ci		if (runtime->hw.channels_max < fp->channels)
110762306a36Sopenharmony_ci			runtime->hw.channels_max = fp->channels;
110862306a36Sopenharmony_ci		if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
110962306a36Sopenharmony_ci			/* FIXME: there might be more than one audio formats... */
111062306a36Sopenharmony_ci			runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
111162306a36Sopenharmony_ci				fp->frame_size;
111262306a36Sopenharmony_ci		}
111362306a36Sopenharmony_ci		pt = 125 * (1 << fp->datainterval);
111462306a36Sopenharmony_ci		ptmin = min(ptmin, pt);
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
111862306a36Sopenharmony_ci	if (subs->speed == USB_SPEED_FULL)
111962306a36Sopenharmony_ci		/* full speed devices have fixed data packet interval */
112062306a36Sopenharmony_ci		ptmin = 1000;
112162306a36Sopenharmony_ci	if (ptmin == 1000)
112262306a36Sopenharmony_ci		/* if period time doesn't go below 1 ms, no rules needed */
112362306a36Sopenharmony_ci		param_period_time_if_needed = -1;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
112662306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
112762306a36Sopenharmony_ci					   ptmin, UINT_MAX);
112862306a36Sopenharmony_ci	if (err < 0)
112962306a36Sopenharmony_ci		return err;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
113262306a36Sopenharmony_ci				  hw_rule_rate, subs,
113362306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE,
113462306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
113562306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
113662306a36Sopenharmony_ci				  param_period_time_if_needed,
113762306a36Sopenharmony_ci				  -1);
113862306a36Sopenharmony_ci	if (err < 0)
113962306a36Sopenharmony_ci		return err;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
114262306a36Sopenharmony_ci				  hw_rule_channels, subs,
114362306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
114462306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
114562306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE,
114662306a36Sopenharmony_ci				  param_period_time_if_needed,
114762306a36Sopenharmony_ci				  -1);
114862306a36Sopenharmony_ci	if (err < 0)
114962306a36Sopenharmony_ci		return err;
115062306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
115162306a36Sopenharmony_ci				  hw_rule_format, subs,
115262306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT,
115362306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE,
115462306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS,
115562306a36Sopenharmony_ci				  param_period_time_if_needed,
115662306a36Sopenharmony_ci				  -1);
115762306a36Sopenharmony_ci	if (err < 0)
115862306a36Sopenharmony_ci		return err;
115962306a36Sopenharmony_ci	if (param_period_time_if_needed >= 0) {
116062306a36Sopenharmony_ci		err = snd_pcm_hw_rule_add(runtime, 0,
116162306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_PERIOD_TIME,
116262306a36Sopenharmony_ci					  hw_rule_period_time, subs,
116362306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_FORMAT,
116462306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_CHANNELS,
116562306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_RATE,
116662306a36Sopenharmony_ci					  -1);
116762306a36Sopenharmony_ci		if (err < 0)
116862306a36Sopenharmony_ci			return err;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* set max period and buffer sizes for 1 and 2 seconds, respectively */
117262306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
117362306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
117462306a36Sopenharmony_ci					   0, 1000000);
117562306a36Sopenharmony_ci	if (err < 0)
117662306a36Sopenharmony_ci		return err;
117762306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
117862306a36Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_BUFFER_TIME,
117962306a36Sopenharmony_ci					   0, 2000000);
118062306a36Sopenharmony_ci	if (err < 0)
118162306a36Sopenharmony_ci		return err;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/* additional hw constraints for implicit fb */
118462306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
118562306a36Sopenharmony_ci				  hw_rule_period_size_implicit_fb, subs,
118662306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
118762306a36Sopenharmony_ci	if (err < 0)
118862306a36Sopenharmony_ci		return err;
118962306a36Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
119062306a36Sopenharmony_ci				  hw_rule_periods_implicit_fb, subs,
119162306a36Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIODS, -1);
119262306a36Sopenharmony_ci	if (err < 0)
119362306a36Sopenharmony_ci		return err;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	list_for_each_entry(fp, &subs->fmt_list, list) {
119662306a36Sopenharmony_ci		if (fp->implicit_fb) {
119762306a36Sopenharmony_ci			runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
119862306a36Sopenharmony_ci			break;
119962306a36Sopenharmony_ci		}
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	return 0;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic int snd_usb_pcm_open(struct snd_pcm_substream *substream)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	int direction = substream->stream;
120862306a36Sopenharmony_ci	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
120962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
121062306a36Sopenharmony_ci	struct snd_usb_substream *subs = &as->substream[direction];
121162306a36Sopenharmony_ci	int ret;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	runtime->hw = snd_usb_hardware;
121462306a36Sopenharmony_ci	/* need an explicit sync to catch applptr update in low-latency mode */
121562306a36Sopenharmony_ci	if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
121662306a36Sopenharmony_ci	    as->chip->lowlatency)
121762306a36Sopenharmony_ci		runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR;
121862306a36Sopenharmony_ci	runtime->private_data = subs;
121962306a36Sopenharmony_ci	subs->pcm_substream = substream;
122062306a36Sopenharmony_ci	/* runtime PM is also done there */
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* initialize DSD/DOP context */
122362306a36Sopenharmony_ci	subs->dsd_dop.byte_idx = 0;
122462306a36Sopenharmony_ci	subs->dsd_dop.channel = 0;
122562306a36Sopenharmony_ci	subs->dsd_dop.marker = 1;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	ret = setup_hw_info(runtime, subs);
122862306a36Sopenharmony_ci	if (ret < 0)
122962306a36Sopenharmony_ci		return ret;
123062306a36Sopenharmony_ci	ret = snd_usb_autoresume(subs->stream->chip);
123162306a36Sopenharmony_ci	if (ret < 0)
123262306a36Sopenharmony_ci		return ret;
123362306a36Sopenharmony_ci	ret = snd_media_stream_init(subs, as->pcm, direction);
123462306a36Sopenharmony_ci	if (ret < 0)
123562306a36Sopenharmony_ci		snd_usb_autosuspend(subs->stream->chip);
123662306a36Sopenharmony_ci	return ret;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic int snd_usb_pcm_close(struct snd_pcm_substream *substream)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	int direction = substream->stream;
124262306a36Sopenharmony_ci	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
124362306a36Sopenharmony_ci	struct snd_usb_substream *subs = &as->substream[direction];
124462306a36Sopenharmony_ci	int ret;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	snd_media_stop_pipeline(subs);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!snd_usb_lock_shutdown(subs->stream->chip)) {
124962306a36Sopenharmony_ci		ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1);
125062306a36Sopenharmony_ci		snd_usb_unlock_shutdown(subs->stream->chip);
125162306a36Sopenharmony_ci		if (ret < 0)
125262306a36Sopenharmony_ci			return ret;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	subs->pcm_substream = NULL;
125662306a36Sopenharmony_ci	snd_usb_autosuspend(subs->stream->chip);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return 0;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci/* Since a URB can handle only a single linear buffer, we must use double
126262306a36Sopenharmony_ci * buffering when the data to be transferred overflows the buffer boundary.
126362306a36Sopenharmony_ci * To avoid inconsistencies when updating hwptr_done, we use double buffering
126462306a36Sopenharmony_ci * for all URBs.
126562306a36Sopenharmony_ci */
126662306a36Sopenharmony_cistatic void retire_capture_urb(struct snd_usb_substream *subs,
126762306a36Sopenharmony_ci			       struct urb *urb)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
127062306a36Sopenharmony_ci	unsigned int stride, frames, bytes, oldptr;
127162306a36Sopenharmony_ci	int i, period_elapsed = 0;
127262306a36Sopenharmony_ci	unsigned long flags;
127362306a36Sopenharmony_ci	unsigned char *cp;
127462306a36Sopenharmony_ci	int current_frame_number;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	/* read frame number here, update pointer in critical section */
127762306a36Sopenharmony_ci	current_frame_number = usb_get_current_frame_number(subs->dev);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	stride = runtime->frame_bits >> 3;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
128262306a36Sopenharmony_ci		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset + subs->pkt_offset_adj;
128362306a36Sopenharmony_ci		if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
128462306a36Sopenharmony_ci			dev_dbg(&subs->dev->dev, "frame %d active: %d\n",
128562306a36Sopenharmony_ci				i, urb->iso_frame_desc[i].status);
128662306a36Sopenharmony_ci			// continue;
128762306a36Sopenharmony_ci		}
128862306a36Sopenharmony_ci		bytes = urb->iso_frame_desc[i].actual_length;
128962306a36Sopenharmony_ci		if (subs->stream_offset_adj > 0) {
129062306a36Sopenharmony_ci			unsigned int adj = min(subs->stream_offset_adj, bytes);
129162306a36Sopenharmony_ci			cp += adj;
129262306a36Sopenharmony_ci			bytes -= adj;
129362306a36Sopenharmony_ci			subs->stream_offset_adj -= adj;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci		frames = bytes / stride;
129662306a36Sopenharmony_ci		if (!subs->txfr_quirk)
129762306a36Sopenharmony_ci			bytes = frames * stride;
129862306a36Sopenharmony_ci		if (bytes % (runtime->sample_bits >> 3) != 0) {
129962306a36Sopenharmony_ci			int oldbytes = bytes;
130062306a36Sopenharmony_ci			bytes = frames * stride;
130162306a36Sopenharmony_ci			dev_warn_ratelimited(&subs->dev->dev,
130262306a36Sopenharmony_ci				 "Corrected urb data len. %d->%d\n",
130362306a36Sopenharmony_ci							oldbytes, bytes);
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci		/* update the current pointer */
130662306a36Sopenharmony_ci		spin_lock_irqsave(&subs->lock, flags);
130762306a36Sopenharmony_ci		oldptr = subs->hwptr_done;
130862306a36Sopenharmony_ci		subs->hwptr_done += bytes;
130962306a36Sopenharmony_ci		if (subs->hwptr_done >= subs->buffer_bytes)
131062306a36Sopenharmony_ci			subs->hwptr_done -= subs->buffer_bytes;
131162306a36Sopenharmony_ci		frames = (bytes + (oldptr % stride)) / stride;
131262306a36Sopenharmony_ci		subs->transfer_done += frames;
131362306a36Sopenharmony_ci		if (subs->transfer_done >= runtime->period_size) {
131462306a36Sopenharmony_ci			subs->transfer_done -= runtime->period_size;
131562306a36Sopenharmony_ci			period_elapsed = 1;
131662306a36Sopenharmony_ci		}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		/* realign last_frame_number */
131962306a36Sopenharmony_ci		subs->last_frame_number = current_frame_number;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		spin_unlock_irqrestore(&subs->lock, flags);
132262306a36Sopenharmony_ci		/* copy a data chunk */
132362306a36Sopenharmony_ci		if (oldptr + bytes > subs->buffer_bytes) {
132462306a36Sopenharmony_ci			unsigned int bytes1 = subs->buffer_bytes - oldptr;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci			memcpy(runtime->dma_area + oldptr, cp, bytes1);
132762306a36Sopenharmony_ci			memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
132862306a36Sopenharmony_ci		} else {
132962306a36Sopenharmony_ci			memcpy(runtime->dma_area + oldptr, cp, bytes);
133062306a36Sopenharmony_ci		}
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (period_elapsed)
133462306a36Sopenharmony_ci		snd_pcm_period_elapsed(subs->pcm_substream);
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_cistatic void urb_ctx_queue_advance(struct snd_usb_substream *subs,
133862306a36Sopenharmony_ci				  struct urb *urb, unsigned int bytes)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	struct snd_urb_ctx *ctx = urb->context;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	ctx->queued += bytes;
134362306a36Sopenharmony_ci	subs->inflight_bytes += bytes;
134462306a36Sopenharmony_ci	subs->hwptr_done += bytes;
134562306a36Sopenharmony_ci	if (subs->hwptr_done >= subs->buffer_bytes)
134662306a36Sopenharmony_ci		subs->hwptr_done -= subs->buffer_bytes;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
135062306a36Sopenharmony_ci					     struct urb *urb, unsigned int bytes)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
135362306a36Sopenharmony_ci	unsigned int dst_idx = 0;
135462306a36Sopenharmony_ci	unsigned int src_idx = subs->hwptr_done;
135562306a36Sopenharmony_ci	unsigned int wrap = subs->buffer_bytes;
135662306a36Sopenharmony_ci	u8 *dst = urb->transfer_buffer;
135762306a36Sopenharmony_ci	u8 *src = runtime->dma_area;
135862306a36Sopenharmony_ci	static const u8 marker[] = { 0x05, 0xfa };
135962306a36Sopenharmony_ci	unsigned int queued = 0;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/*
136262306a36Sopenharmony_ci	 * The DSP DOP format defines a way to transport DSD samples over
136362306a36Sopenharmony_ci	 * normal PCM data endpoints. It requires stuffing of marker bytes
136462306a36Sopenharmony_ci	 * (0x05 and 0xfa, alternating per sample frame), and then expects
136562306a36Sopenharmony_ci	 * 2 additional bytes of actual payload. The whole frame is stored
136662306a36Sopenharmony_ci	 * LSB.
136762306a36Sopenharmony_ci	 *
136862306a36Sopenharmony_ci	 * Hence, for a stereo transport, the buffer layout looks like this,
136962306a36Sopenharmony_ci	 * where L refers to left channel samples and R to right.
137062306a36Sopenharmony_ci	 *
137162306a36Sopenharmony_ci	 *   L1 L2 0x05   R1 R2 0x05   L3 L4 0xfa  R3 R4 0xfa
137262306a36Sopenharmony_ci	 *   L5 L6 0x05   R5 R6 0x05   L7 L8 0xfa  R7 R8 0xfa
137362306a36Sopenharmony_ci	 *   .....
137462306a36Sopenharmony_ci	 *
137562306a36Sopenharmony_ci	 */
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	while (bytes--) {
137862306a36Sopenharmony_ci		if (++subs->dsd_dop.byte_idx == 3) {
137962306a36Sopenharmony_ci			/* frame boundary? */
138062306a36Sopenharmony_ci			dst[dst_idx++] = marker[subs->dsd_dop.marker];
138162306a36Sopenharmony_ci			src_idx += 2;
138262306a36Sopenharmony_ci			subs->dsd_dop.byte_idx = 0;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci			if (++subs->dsd_dop.channel % runtime->channels == 0) {
138562306a36Sopenharmony_ci				/* alternate the marker */
138662306a36Sopenharmony_ci				subs->dsd_dop.marker++;
138762306a36Sopenharmony_ci				subs->dsd_dop.marker %= ARRAY_SIZE(marker);
138862306a36Sopenharmony_ci				subs->dsd_dop.channel = 0;
138962306a36Sopenharmony_ci			}
139062306a36Sopenharmony_ci		} else {
139162306a36Sopenharmony_ci			/* stuff the DSD payload */
139262306a36Sopenharmony_ci			int idx = (src_idx + subs->dsd_dop.byte_idx - 1) % wrap;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci			if (subs->cur_audiofmt->dsd_bitrev)
139562306a36Sopenharmony_ci				dst[dst_idx++] = bitrev8(src[idx]);
139662306a36Sopenharmony_ci			else
139762306a36Sopenharmony_ci				dst[dst_idx++] = src[idx];
139862306a36Sopenharmony_ci			queued++;
139962306a36Sopenharmony_ci		}
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	urb_ctx_queue_advance(subs, urb, queued);
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci/* copy bit-reversed bytes onto transfer buffer */
140662306a36Sopenharmony_cistatic void fill_playback_urb_dsd_bitrev(struct snd_usb_substream *subs,
140762306a36Sopenharmony_ci					 struct urb *urb, unsigned int bytes)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
141062306a36Sopenharmony_ci	const u8 *src = runtime->dma_area;
141162306a36Sopenharmony_ci	u8 *buf = urb->transfer_buffer;
141262306a36Sopenharmony_ci	int i, ofs = subs->hwptr_done;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	for (i = 0; i < bytes; i++) {
141562306a36Sopenharmony_ci		*buf++ = bitrev8(src[ofs]);
141662306a36Sopenharmony_ci		if (++ofs >= subs->buffer_bytes)
141762306a36Sopenharmony_ci			ofs = 0;
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	urb_ctx_queue_advance(subs, urb, bytes);
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
142462306a36Sopenharmony_ci			int offset, int stride, unsigned int bytes)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	if (subs->hwptr_done + bytes > subs->buffer_bytes) {
142962306a36Sopenharmony_ci		/* err, the transferred area goes over buffer boundary. */
143062306a36Sopenharmony_ci		unsigned int bytes1 = subs->buffer_bytes - subs->hwptr_done;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		memcpy(urb->transfer_buffer + offset,
143362306a36Sopenharmony_ci		       runtime->dma_area + subs->hwptr_done, bytes1);
143462306a36Sopenharmony_ci		memcpy(urb->transfer_buffer + offset + bytes1,
143562306a36Sopenharmony_ci		       runtime->dma_area, bytes - bytes1);
143662306a36Sopenharmony_ci	} else {
143762306a36Sopenharmony_ci		memcpy(urb->transfer_buffer + offset,
143862306a36Sopenharmony_ci		       runtime->dma_area + subs->hwptr_done, bytes);
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	urb_ctx_queue_advance(subs, urb, bytes);
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
144562306a36Sopenharmony_ci				      struct urb *urb, int stride,
144662306a36Sopenharmony_ci				      unsigned int bytes)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	__le32 packet_length;
144962306a36Sopenharmony_ci	int i;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/* Put __le32 length descriptor at start of each packet. */
145262306a36Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
145362306a36Sopenharmony_ci		unsigned int length = urb->iso_frame_desc[i].length;
145462306a36Sopenharmony_ci		unsigned int offset = urb->iso_frame_desc[i].offset;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci		packet_length = cpu_to_le32(length);
145762306a36Sopenharmony_ci		offset += i * sizeof(packet_length);
145862306a36Sopenharmony_ci		urb->iso_frame_desc[i].offset = offset;
145962306a36Sopenharmony_ci		urb->iso_frame_desc[i].length += sizeof(packet_length);
146062306a36Sopenharmony_ci		memcpy(urb->transfer_buffer + offset,
146162306a36Sopenharmony_ci		       &packet_length, sizeof(packet_length));
146262306a36Sopenharmony_ci		copy_to_urb(subs, urb, offset + sizeof(packet_length),
146362306a36Sopenharmony_ci			    stride, length);
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci	/* Adjust transfer size accordingly. */
146662306a36Sopenharmony_ci	bytes += urb->number_of_packets * sizeof(packet_length);
146762306a36Sopenharmony_ci	return bytes;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_cistatic int prepare_playback_urb(struct snd_usb_substream *subs,
147162306a36Sopenharmony_ci				struct urb *urb,
147262306a36Sopenharmony_ci				bool in_stream_lock)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
147562306a36Sopenharmony_ci	struct snd_usb_endpoint *ep = subs->data_endpoint;
147662306a36Sopenharmony_ci	struct snd_urb_ctx *ctx = urb->context;
147762306a36Sopenharmony_ci	unsigned int frames, bytes;
147862306a36Sopenharmony_ci	int counts;
147962306a36Sopenharmony_ci	unsigned int transfer_done, frame_limit, avail = 0;
148062306a36Sopenharmony_ci	int i, stride, period_elapsed = 0;
148162306a36Sopenharmony_ci	unsigned long flags;
148262306a36Sopenharmony_ci	int err = 0;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	stride = ep->stride;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	frames = 0;
148762306a36Sopenharmony_ci	ctx->queued = 0;
148862306a36Sopenharmony_ci	urb->number_of_packets = 0;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	spin_lock_irqsave(&subs->lock, flags);
149162306a36Sopenharmony_ci	frame_limit = subs->frame_limit + ep->max_urb_frames;
149262306a36Sopenharmony_ci	transfer_done = subs->transfer_done;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	if (subs->lowlatency_playback &&
149562306a36Sopenharmony_ci	    runtime->state != SNDRV_PCM_STATE_DRAINING) {
149662306a36Sopenharmony_ci		unsigned int hwptr = subs->hwptr_done / stride;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci		/* calculate the byte offset-in-buffer of the appl_ptr */
149962306a36Sopenharmony_ci		avail = (runtime->control->appl_ptr - runtime->hw_ptr_base)
150062306a36Sopenharmony_ci			% runtime->buffer_size;
150162306a36Sopenharmony_ci		if (avail <= hwptr)
150262306a36Sopenharmony_ci			avail += runtime->buffer_size;
150362306a36Sopenharmony_ci		avail -= hwptr;
150462306a36Sopenharmony_ci	}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	for (i = 0; i < ctx->packets; i++) {
150762306a36Sopenharmony_ci		counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
150862306a36Sopenharmony_ci		if (counts < 0)
150962306a36Sopenharmony_ci			break;
151062306a36Sopenharmony_ci		/* set up descriptor */
151162306a36Sopenharmony_ci		urb->iso_frame_desc[i].offset = frames * stride;
151262306a36Sopenharmony_ci		urb->iso_frame_desc[i].length = counts * stride;
151362306a36Sopenharmony_ci		frames += counts;
151462306a36Sopenharmony_ci		avail -= counts;
151562306a36Sopenharmony_ci		urb->number_of_packets++;
151662306a36Sopenharmony_ci		transfer_done += counts;
151762306a36Sopenharmony_ci		if (transfer_done >= runtime->period_size) {
151862306a36Sopenharmony_ci			transfer_done -= runtime->period_size;
151962306a36Sopenharmony_ci			frame_limit = 0;
152062306a36Sopenharmony_ci			period_elapsed = 1;
152162306a36Sopenharmony_ci			if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
152262306a36Sopenharmony_ci				if (transfer_done > 0) {
152362306a36Sopenharmony_ci					/* FIXME: fill-max mode is not
152462306a36Sopenharmony_ci					 * supported yet */
152562306a36Sopenharmony_ci					frames -= transfer_done;
152662306a36Sopenharmony_ci					counts -= transfer_done;
152762306a36Sopenharmony_ci					urb->iso_frame_desc[i].length =
152862306a36Sopenharmony_ci						counts * stride;
152962306a36Sopenharmony_ci					transfer_done = 0;
153062306a36Sopenharmony_ci				}
153162306a36Sopenharmony_ci				i++;
153262306a36Sopenharmony_ci				if (i < ctx->packets) {
153362306a36Sopenharmony_ci					/* add a transfer delimiter */
153462306a36Sopenharmony_ci					urb->iso_frame_desc[i].offset =
153562306a36Sopenharmony_ci						frames * stride;
153662306a36Sopenharmony_ci					urb->iso_frame_desc[i].length = 0;
153762306a36Sopenharmony_ci					urb->number_of_packets++;
153862306a36Sopenharmony_ci				}
153962306a36Sopenharmony_ci				break;
154062306a36Sopenharmony_ci			}
154162306a36Sopenharmony_ci		}
154262306a36Sopenharmony_ci		/* finish at the period boundary or after enough frames */
154362306a36Sopenharmony_ci		if ((period_elapsed || transfer_done >= frame_limit) &&
154462306a36Sopenharmony_ci		    !snd_usb_endpoint_implicit_feedback_sink(ep))
154562306a36Sopenharmony_ci			break;
154662306a36Sopenharmony_ci	}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (!frames) {
154962306a36Sopenharmony_ci		err = -EAGAIN;
155062306a36Sopenharmony_ci		goto unlock;
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	bytes = frames * stride;
155462306a36Sopenharmony_ci	subs->transfer_done = transfer_done;
155562306a36Sopenharmony_ci	subs->frame_limit = frame_limit;
155662306a36Sopenharmony_ci	if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
155762306a36Sopenharmony_ci		     subs->cur_audiofmt->dsd_dop)) {
155862306a36Sopenharmony_ci		fill_playback_urb_dsd_dop(subs, urb, bytes);
155962306a36Sopenharmony_ci	} else if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U8 &&
156062306a36Sopenharmony_ci			   subs->cur_audiofmt->dsd_bitrev)) {
156162306a36Sopenharmony_ci		fill_playback_urb_dsd_bitrev(subs, urb, bytes);
156262306a36Sopenharmony_ci	} else {
156362306a36Sopenharmony_ci		/* usual PCM */
156462306a36Sopenharmony_ci		if (!subs->tx_length_quirk)
156562306a36Sopenharmony_ci			copy_to_urb(subs, urb, 0, stride, bytes);
156662306a36Sopenharmony_ci		else
156762306a36Sopenharmony_ci			bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
156862306a36Sopenharmony_ci			/* bytes is now amount of outgoing data */
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	subs->last_frame_number = usb_get_current_frame_number(subs->dev);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (subs->trigger_tstamp_pending_update) {
157462306a36Sopenharmony_ci		/* this is the first actual URB submitted,
157562306a36Sopenharmony_ci		 * update trigger timestamp to reflect actual start time
157662306a36Sopenharmony_ci		 */
157762306a36Sopenharmony_ci		snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
157862306a36Sopenharmony_ci		subs->trigger_tstamp_pending_update = false;
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if (period_elapsed && !subs->running && subs->lowlatency_playback) {
158262306a36Sopenharmony_ci		subs->period_elapsed_pending = 1;
158362306a36Sopenharmony_ci		period_elapsed = 0;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci unlock:
158762306a36Sopenharmony_ci	spin_unlock_irqrestore(&subs->lock, flags);
158862306a36Sopenharmony_ci	if (err < 0)
158962306a36Sopenharmony_ci		return err;
159062306a36Sopenharmony_ci	urb->transfer_buffer_length = bytes;
159162306a36Sopenharmony_ci	if (period_elapsed) {
159262306a36Sopenharmony_ci		if (in_stream_lock)
159362306a36Sopenharmony_ci			snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream);
159462306a36Sopenharmony_ci		else
159562306a36Sopenharmony_ci			snd_pcm_period_elapsed(subs->pcm_substream);
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci	return 0;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci/*
160162306a36Sopenharmony_ci * process after playback data complete
160262306a36Sopenharmony_ci * - decrease the delay count again
160362306a36Sopenharmony_ci */
160462306a36Sopenharmony_cistatic void retire_playback_urb(struct snd_usb_substream *subs,
160562306a36Sopenharmony_ci			       struct urb *urb)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	unsigned long flags;
160862306a36Sopenharmony_ci	struct snd_urb_ctx *ctx = urb->context;
160962306a36Sopenharmony_ci	bool period_elapsed = false;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	spin_lock_irqsave(&subs->lock, flags);
161262306a36Sopenharmony_ci	if (ctx->queued) {
161362306a36Sopenharmony_ci		if (subs->inflight_bytes >= ctx->queued)
161462306a36Sopenharmony_ci			subs->inflight_bytes -= ctx->queued;
161562306a36Sopenharmony_ci		else
161662306a36Sopenharmony_ci			subs->inflight_bytes = 0;
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	subs->last_frame_number = usb_get_current_frame_number(subs->dev);
162062306a36Sopenharmony_ci	if (subs->running) {
162162306a36Sopenharmony_ci		period_elapsed = subs->period_elapsed_pending;
162262306a36Sopenharmony_ci		subs->period_elapsed_pending = 0;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci	spin_unlock_irqrestore(&subs->lock, flags);
162562306a36Sopenharmony_ci	if (period_elapsed)
162662306a36Sopenharmony_ci		snd_pcm_period_elapsed(subs->pcm_substream);
162762306a36Sopenharmony_ci}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci/* PCM ack callback for the playback stream;
163062306a36Sopenharmony_ci * this plays a role only when the stream is running in low-latency mode.
163162306a36Sopenharmony_ci */
163262306a36Sopenharmony_cistatic int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct snd_usb_substream *subs = substream->runtime->private_data;
163562306a36Sopenharmony_ci	struct snd_usb_endpoint *ep;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (!subs->lowlatency_playback || !subs->running)
163862306a36Sopenharmony_ci		return 0;
163962306a36Sopenharmony_ci	ep = subs->data_endpoint;
164062306a36Sopenharmony_ci	if (!ep)
164162306a36Sopenharmony_ci		return 0;
164262306a36Sopenharmony_ci	/* When no more in-flight URBs available, try to process the pending
164362306a36Sopenharmony_ci	 * outputs here
164462306a36Sopenharmony_ci	 */
164562306a36Sopenharmony_ci	if (!ep->active_mask)
164662306a36Sopenharmony_ci		return snd_usb_queue_pending_output_urbs(ep, true);
164762306a36Sopenharmony_ci	return 0;
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
165162306a36Sopenharmony_ci					      int cmd)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	struct snd_usb_substream *subs = substream->runtime->private_data;
165462306a36Sopenharmony_ci	int err;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	switch (cmd) {
165762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
165862306a36Sopenharmony_ci		subs->trigger_tstamp_pending_update = true;
165962306a36Sopenharmony_ci		fallthrough;
166062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
166162306a36Sopenharmony_ci		snd_usb_endpoint_set_callback(subs->data_endpoint,
166262306a36Sopenharmony_ci					      prepare_playback_urb,
166362306a36Sopenharmony_ci					      retire_playback_urb,
166462306a36Sopenharmony_ci					      subs);
166562306a36Sopenharmony_ci		if (subs->lowlatency_playback &&
166662306a36Sopenharmony_ci		    cmd == SNDRV_PCM_TRIGGER_START) {
166762306a36Sopenharmony_ci			if (in_free_wheeling_mode(substream->runtime))
166862306a36Sopenharmony_ci				subs->lowlatency_playback = false;
166962306a36Sopenharmony_ci			err = start_endpoints(subs);
167062306a36Sopenharmony_ci			if (err < 0) {
167162306a36Sopenharmony_ci				snd_usb_endpoint_set_callback(subs->data_endpoint,
167262306a36Sopenharmony_ci							      NULL, NULL, NULL);
167362306a36Sopenharmony_ci				return err;
167462306a36Sopenharmony_ci			}
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci		subs->running = 1;
167762306a36Sopenharmony_ci		dev_dbg(&subs->dev->dev, "%d:%d Start Playback PCM\n",
167862306a36Sopenharmony_ci			subs->cur_audiofmt->iface,
167962306a36Sopenharmony_ci			subs->cur_audiofmt->altsetting);
168062306a36Sopenharmony_ci		return 0;
168162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
168262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
168362306a36Sopenharmony_ci		stop_endpoints(subs, substream->runtime->state == SNDRV_PCM_STATE_DRAINING);
168462306a36Sopenharmony_ci		snd_usb_endpoint_set_callback(subs->data_endpoint,
168562306a36Sopenharmony_ci					      NULL, NULL, NULL);
168662306a36Sopenharmony_ci		subs->running = 0;
168762306a36Sopenharmony_ci		dev_dbg(&subs->dev->dev, "%d:%d Stop Playback PCM\n",
168862306a36Sopenharmony_ci			subs->cur_audiofmt->iface,
168962306a36Sopenharmony_ci			subs->cur_audiofmt->altsetting);
169062306a36Sopenharmony_ci		return 0;
169162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
169262306a36Sopenharmony_ci		/* keep retire_data_urb for delay calculation */
169362306a36Sopenharmony_ci		snd_usb_endpoint_set_callback(subs->data_endpoint,
169462306a36Sopenharmony_ci					      NULL,
169562306a36Sopenharmony_ci					      retire_playback_urb,
169662306a36Sopenharmony_ci					      subs);
169762306a36Sopenharmony_ci		subs->running = 0;
169862306a36Sopenharmony_ci		dev_dbg(&subs->dev->dev, "%d:%d Pause Playback PCM\n",
169962306a36Sopenharmony_ci			subs->cur_audiofmt->iface,
170062306a36Sopenharmony_ci			subs->cur_audiofmt->altsetting);
170162306a36Sopenharmony_ci		return 0;
170262306a36Sopenharmony_ci	}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	return -EINVAL;
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_cistatic int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream,
170862306a36Sopenharmony_ci					     int cmd)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	int err;
171162306a36Sopenharmony_ci	struct snd_usb_substream *subs = substream->runtime->private_data;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	switch (cmd) {
171462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
171562306a36Sopenharmony_ci		err = start_endpoints(subs);
171662306a36Sopenharmony_ci		if (err < 0)
171762306a36Sopenharmony_ci			return err;
171862306a36Sopenharmony_ci		fallthrough;
171962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
172062306a36Sopenharmony_ci		snd_usb_endpoint_set_callback(subs->data_endpoint,
172162306a36Sopenharmony_ci					      NULL, retire_capture_urb,
172262306a36Sopenharmony_ci					      subs);
172362306a36Sopenharmony_ci		subs->last_frame_number = usb_get_current_frame_number(subs->dev);
172462306a36Sopenharmony_ci		subs->running = 1;
172562306a36Sopenharmony_ci		dev_dbg(&subs->dev->dev, "%d:%d Start Capture PCM\n",
172662306a36Sopenharmony_ci			subs->cur_audiofmt->iface,
172762306a36Sopenharmony_ci			subs->cur_audiofmt->altsetting);
172862306a36Sopenharmony_ci		return 0;
172962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
173062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
173162306a36Sopenharmony_ci		stop_endpoints(subs, false);
173262306a36Sopenharmony_ci		fallthrough;
173362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
173462306a36Sopenharmony_ci		snd_usb_endpoint_set_callback(subs->data_endpoint,
173562306a36Sopenharmony_ci					      NULL, NULL, NULL);
173662306a36Sopenharmony_ci		subs->running = 0;
173762306a36Sopenharmony_ci		dev_dbg(&subs->dev->dev, "%d:%d Stop Capture PCM\n",
173862306a36Sopenharmony_ci			subs->cur_audiofmt->iface,
173962306a36Sopenharmony_ci			subs->cur_audiofmt->altsetting);
174062306a36Sopenharmony_ci		return 0;
174162306a36Sopenharmony_ci	}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	return -EINVAL;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_usb_playback_ops = {
174762306a36Sopenharmony_ci	.open =		snd_usb_pcm_open,
174862306a36Sopenharmony_ci	.close =	snd_usb_pcm_close,
174962306a36Sopenharmony_ci	.hw_params =	snd_usb_hw_params,
175062306a36Sopenharmony_ci	.hw_free =	snd_usb_hw_free,
175162306a36Sopenharmony_ci	.prepare =	snd_usb_pcm_prepare,
175262306a36Sopenharmony_ci	.trigger =	snd_usb_substream_playback_trigger,
175362306a36Sopenharmony_ci	.sync_stop =	snd_usb_pcm_sync_stop,
175462306a36Sopenharmony_ci	.pointer =	snd_usb_pcm_pointer,
175562306a36Sopenharmony_ci	.ack =		snd_usb_pcm_playback_ack,
175662306a36Sopenharmony_ci};
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_usb_capture_ops = {
175962306a36Sopenharmony_ci	.open =		snd_usb_pcm_open,
176062306a36Sopenharmony_ci	.close =	snd_usb_pcm_close,
176162306a36Sopenharmony_ci	.hw_params =	snd_usb_hw_params,
176262306a36Sopenharmony_ci	.hw_free =	snd_usb_hw_free,
176362306a36Sopenharmony_ci	.prepare =	snd_usb_pcm_prepare,
176462306a36Sopenharmony_ci	.trigger =	snd_usb_substream_capture_trigger,
176562306a36Sopenharmony_ci	.sync_stop =	snd_usb_pcm_sync_stop,
176662306a36Sopenharmony_ci	.pointer =	snd_usb_pcm_pointer,
176762306a36Sopenharmony_ci};
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_civoid snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
177062306a36Sopenharmony_ci{
177162306a36Sopenharmony_ci	const struct snd_pcm_ops *ops;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	ops = stream == SNDRV_PCM_STREAM_PLAYBACK ?
177462306a36Sopenharmony_ci			&snd_usb_playback_ops : &snd_usb_capture_ops;
177562306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, stream, ops);
177662306a36Sopenharmony_ci}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_civoid snd_usb_preallocate_buffer(struct snd_usb_substream *subs)
177962306a36Sopenharmony_ci{
178062306a36Sopenharmony_ci	struct snd_pcm *pcm = subs->stream->pcm;
178162306a36Sopenharmony_ci	struct snd_pcm_substream *s = pcm->streams[subs->direction].substream;
178262306a36Sopenharmony_ci	struct device *dev = subs->dev->bus->sysdev;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	if (snd_usb_use_vmalloc)
178562306a36Sopenharmony_ci		snd_pcm_set_managed_buffer(s, SNDRV_DMA_TYPE_VMALLOC,
178662306a36Sopenharmony_ci					   NULL, 0, 0);
178762306a36Sopenharmony_ci	else
178862306a36Sopenharmony_ci		snd_pcm_set_managed_buffer(s, SNDRV_DMA_TYPE_DEV_SG,
178962306a36Sopenharmony_ci					   dev, 64*1024, 512*1024);
179062306a36Sopenharmony_ci}
1791