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