diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-03 07:49:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-03 07:49:25 -0700 |
commit | ff0700f03609b9f0defacd4ce96d9519d721e0a2 (patch) | |
tree | 4b7ac6cf015e39f82ef0706ce465a224a43dac42 /sound/usb/pcm.c | |
parent | dcd68326d29b62f3039e4f4d23d3e38f24d37360 (diff) | |
parent | df0380b9539b04c1ae8854a984098da06d5f1e67 (diff) | |
download | linux-ff0700f03609b9f0defacd4ce96d9519d721e0a2.tar.bz2 |
Merge tag 'sound-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Lots of code development have been see in ASoC side as usual, while
the continued development on memalloc helper and USB-audio low-
latency support are found in the rest.
Note that a few changes in the unusual places like arch/sh are
included, which are a part of ASoC DAI format cleanups.
ALSA core:
- Continued memalloc helper updates and cleanups, now supporting
non-coherent and non-contiguous pages
- Fixes for races in mixer OSS layer
ASoC:
- A new version of the audio graph card which supports a wider range
of systems
- Several conversions to YAML DT bindings
- Continuing cleanups to the SOF and Intel code
- Move of the Cirrus DSP framework into drivers/firmware to allow for
future use by non-audio DSPs
- An overhaul of the cs42l42 driver, correcting many problems
- DAI format terminology conversions over many drivers for cleanups
- Support for AMD Vangogh and Yelow Cap, Cirrus CS35L41, Maxim
MAX98520 and MAX98360A, Mediatek MT8195, Nuvoton NAU8821, nVidia
Tegra210, NXP i.MX8ULP, Qualcomm AudioReach, Realtek ALC5682I-VS,
RT5682S, and RT9120 and Rockchip RV1126 and RK3568
USB-audio:
- Continued improvements on low-latency playback
- Quirks for Pioneer devices, Line6 HX-Stomp XL, Audient iD14
HD-audio:
- Reduce excessive udelay() calls on Intel platforms; this should
reduce the CPU load with PulseAudio
- Quirks for HP and Clevo laptops
FireWire:
- Support for meter information on MOTU"
* tag 'sound-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (513 commits)
ALSA: usb-audio: Add quirk for Audient iD14
ALSA: hda/realtek: Add quirk for Clevo PC70HS
ALSA: usb-audio: Line6 HX-Stomp XL USB_ID for 48k-fixed quirk
ALSA: usb-audio: Add registration quirk for JBL Quantum 400
ASoC: rsnd: Fix an error handling path in 'rsnd_node_count()'
ASoC: tlv320aic3x: Make aic3x_remove() return void
ASoC: Intel: soc-acpi: use const for all uses of snd_soc_acpi_codecs
ASoC: Intel: soc-acpi-cht: shrink tables using compatible IDs
ASoC: Intel: soc-acpi-byt: shrink tables using compatible IDs
ASoC: Intel: sof_rt5682: use comp_ids to enumerate rt5682s
ASoC: Intel: sof_rt5682: detect codec variant in probe function
ASoC: soc-acpi: add comp_ids field for machine driver matching
ASoC: mediatek: mt8195: add mt8195-mt6359-rt1011-rt5682 bindings document
ASoC: mediatek: mt8195: add machine driver with mt6359, rt1011 and rt5682
ASoC: Stop dummy from overriding hwparams
ASoC: topology: Change topology device to card device
ASoC: topology: Use correct device for prints
ASoC: topology: Check for dapm widget completeness
ASoC: topology: Add header payload_size verification
ASoC: core: Remove invalid snd_soc_component_set_jack call
...
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 164 |
1 files changed, 129 insertions, 35 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 5dc9266180e3..95ec8eec1bb0 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -219,16 +219,16 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, return 0; } -static bool stop_endpoints(struct snd_usb_substream *subs) +static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending) { bool stopped = 0; if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { - snd_usb_endpoint_stop(subs->sync_endpoint); + snd_usb_endpoint_stop(subs->sync_endpoint, keep_pending); stopped = true; } if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { - snd_usb_endpoint_stop(subs->data_endpoint); + snd_usb_endpoint_stop(subs->data_endpoint, keep_pending); stopped = true; } return stopped; @@ -261,7 +261,7 @@ static int start_endpoints(struct snd_usb_substream *subs) return 0; error: - stop_endpoints(subs); + stop_endpoints(subs, false); return err; } @@ -437,7 +437,7 @@ static int configure_endpoints(struct snd_usb_audio *chip, if (subs->data_endpoint->need_setup) { /* stop any running stream beforehand */ - if (stop_endpoints(subs)) + if (stop_endpoints(subs, false)) sync_pending_stops(subs); err = snd_usb_endpoint_configure(chip, subs->data_endpoint); if (err < 0) @@ -572,7 +572,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_audiofmt = NULL; mutex_unlock(&chip->mutex); if (!snd_usb_lock_shutdown(chip)) { - if (stop_endpoints(subs)) + if (stop_endpoints(subs, false)) sync_pending_stops(subs); close_endpoints(chip, subs); snd_usb_unlock_shutdown(chip); @@ -581,6 +581,26 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) return 0; } +/* check whether early start is needed for playback stream */ +static int lowlatency_playback_available(struct snd_pcm_runtime *runtime, + struct snd_usb_substream *subs) +{ + struct snd_usb_audio *chip = subs->stream->chip; + + if (subs->direction == SNDRV_PCM_STREAM_CAPTURE) + return false; + /* disabled via module option? */ + if (!chip->lowlatency) + return false; + /* free-wheeling mode? (e.g. dmix) */ + if (runtime->stop_threshold > runtime->buffer_size) + return false; + /* implicit feedback mode has own operation mode */ + if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) + return false; + return true; +} + /* * prepare callback * @@ -614,13 +634,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) subs->period_elapsed_pending = 0; runtime->delay = 0; - /* check whether early start is needed for playback stream */ - subs->early_playback_start = - subs->direction == SNDRV_PCM_STREAM_PLAYBACK && - (!chip->lowlatency || - (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes)); - - if (subs->early_playback_start) + subs->lowlatency_playback = lowlatency_playback_available(runtime, subs); + if (!subs->lowlatency_playback) ret = start_endpoints(subs); unlock: @@ -734,6 +749,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_usb_substream *subs = rule->private; + struct snd_usb_audio *chip = subs->stream->chip; const struct audioformat *fp; struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); unsigned int rmin, rmax, r; @@ -745,6 +761,14 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, list_for_each_entry(fp, &subs->fmt_list, list) { if (!hw_check_valid_format(subs, params, fp)) continue; + r = snd_usb_endpoint_get_clock_rate(chip, fp->clock); + if (r > 0) { + if (!snd_interval_test(it, r)) + continue; + rmin = min(rmin, r); + rmax = max(rmax, r); + continue; + } if (fp->rate_table && fp->nr_rates) { for (i = 0; i < fp->nr_rates; i++) { r = fp->rate_table[i]; @@ -1056,6 +1080,13 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre if (err < 0) return err; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (fp->implicit_fb) { + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + break; + } + } + return 0; } @@ -1068,6 +1099,10 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) int ret; runtime->hw = snd_usb_hardware; + /* need an explicit sync to catch applptr update in low-latency mode */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK && + as->chip->lowlatency) + runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR; runtime->private_data = subs; subs->pcm_substream = substream; /* runtime PM is also done there */ @@ -1320,44 +1355,66 @@ static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs, return bytes; } -static void prepare_playback_urb(struct snd_usb_substream *subs, - struct urb *urb) +static int prepare_playback_urb(struct snd_usb_substream *subs, + struct urb *urb, + bool in_stream_lock) { struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; struct snd_usb_endpoint *ep = subs->data_endpoint; struct snd_urb_ctx *ctx = urb->context; - unsigned int counts, frames, bytes; + unsigned int frames, bytes; + int counts; + unsigned int transfer_done, frame_limit, avail = 0; int i, stride, period_elapsed = 0; unsigned long flags; + int err = 0; stride = ep->stride; frames = 0; ctx->queued = 0; urb->number_of_packets = 0; + spin_lock_irqsave(&subs->lock, flags); - subs->frame_limit += ep->max_urb_frames; + frame_limit = subs->frame_limit + ep->max_urb_frames; + transfer_done = subs->transfer_done; + + if (subs->lowlatency_playback && + runtime->status->state != SNDRV_PCM_STATE_DRAINING) { + unsigned int hwptr = subs->hwptr_done / stride; + + /* calculate the byte offset-in-buffer of the appl_ptr */ + avail = (runtime->control->appl_ptr - runtime->hw_ptr_base) + % runtime->buffer_size; + if (avail <= hwptr) + avail += runtime->buffer_size; + avail -= hwptr; + } + for (i = 0; i < ctx->packets; i++) { - counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); + counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail); + if (counts < 0) + break; /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; urb->iso_frame_desc[i].length = counts * stride; frames += counts; + avail -= counts; urb->number_of_packets++; - subs->transfer_done += counts; - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - subs->frame_limit = 0; + transfer_done += counts; + if (transfer_done >= runtime->period_size) { + transfer_done -= runtime->period_size; + frame_limit = 0; period_elapsed = 1; if (subs->fmt_type == UAC_FORMAT_TYPE_II) { - if (subs->transfer_done > 0) { + if (transfer_done > 0) { /* FIXME: fill-max mode is not * supported yet */ - frames -= subs->transfer_done; - counts -= subs->transfer_done; + frames -= transfer_done; + counts -= transfer_done; urb->iso_frame_desc[i].length = counts * stride; - subs->transfer_done = 0; + transfer_done = 0; } i++; if (i < ctx->packets) { @@ -1371,13 +1428,19 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, } } /* finish at the period boundary or after enough frames */ - if ((period_elapsed || - subs->transfer_done >= subs->frame_limit) && + if ((period_elapsed || transfer_done >= frame_limit) && !snd_usb_endpoint_implicit_feedback_sink(ep)) break; } - bytes = frames * stride; + if (!frames) { + err = -EAGAIN; + goto unlock; + } + + bytes = frames * stride; + subs->transfer_done = transfer_done; + subs->frame_limit = frame_limit; if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE && subs->cur_audiofmt->dsd_dop)) { fill_playback_urb_dsd_dop(subs, urb, bytes); @@ -1403,14 +1466,23 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->trigger_tstamp_pending_update = false; } - if (period_elapsed && !subs->running && !subs->early_playback_start) { + if (period_elapsed && !subs->running && subs->lowlatency_playback) { subs->period_elapsed_pending = 1; period_elapsed = 0; } + + unlock: spin_unlock_irqrestore(&subs->lock, flags); + if (err < 0) + return err; urb->transfer_buffer_length = bytes; - if (period_elapsed) - snd_pcm_period_elapsed(subs->pcm_substream); + if (period_elapsed) { + if (in_stream_lock) + snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream); + else + snd_pcm_period_elapsed(subs->pcm_substream); + } + return 0; } /* @@ -1442,6 +1514,27 @@ static void retire_playback_urb(struct snd_usb_substream *subs, snd_pcm_period_elapsed(subs->pcm_substream); } +/* PCM ack callback for the playback stream; + * this plays a role only when the stream is running in low-latency mode. + */ +static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + struct snd_usb_endpoint *ep; + + if (!subs->lowlatency_playback || !subs->running) + return 0; + ep = subs->data_endpoint; + if (!ep) + return 0; + /* When no more in-flight URBs available, try to process the pending + * outputs here + */ + if (!ep->active_mask) + snd_usb_queue_pending_output_urbs(ep, true); + return 0; +} + static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -1457,7 +1550,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea prepare_playback_urb, retire_playback_urb, subs); - if (!subs->early_playback_start && + if (subs->lowlatency_playback && cmd == SNDRV_PCM_TRIGGER_START) { err = start_endpoints(subs); if (err < 0) { @@ -1473,7 +1566,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea return 0; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs); + stop_endpoints(subs, substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING); snd_usb_endpoint_set_callback(subs->data_endpoint, NULL, NULL, NULL); subs->running = 0; @@ -1521,7 +1614,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream return 0; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs); + stop_endpoints(subs, false); fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_usb_endpoint_set_callback(subs->data_endpoint, @@ -1545,6 +1638,7 @@ static const struct snd_pcm_ops snd_usb_playback_ops = { .trigger = snd_usb_substream_playback_trigger, .sync_stop = snd_usb_pcm_sync_stop, .pointer = snd_usb_pcm_pointer, + .ack = snd_usb_pcm_playback_ack, }; static const struct snd_pcm_ops snd_usb_capture_ops = { |