// SPDX-License-Identifier: GPL-2.0-or-later
/*
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/bitrev.h>
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "usbaudio.h"
#include "card.h"
#include "quirks.h"
#include "endpoint.h"
#include "helper.h"
#include "pcm.h"
#include "clock.h"
#include "power.h"
#include "media.h"
#include "implicit.h"
#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
/* return the estimated delay based on USB frame counters */
static snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime)
{
unsigned int current_frame_number;
unsigned int frame_diff;
int est_delay;
int queued;
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
queued = bytes_to_frames(runtime, subs->inflight_bytes);
if (!queued)
return 0;
} else if (!subs->running) {
return 0;
}
current_frame_number = usb_get_current_frame_number(subs->dev);
/*
* HCD implementations use different widths, use lower 8 bits.
* The delay will be managed up to 256ms, which is more than
* enough
*/
frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
/* Approximation based on number of samples per USB frame (ms),
some truncation for 44.1 but the estimate is good enough */
est_delay = frame_diff * runtime->rate / 1000;
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
est_delay = queued - est_delay;
if (est_delay < 0)
est_delay = 0;
}
return est_delay;
}
/*
* return the current pcm pointer. just based on the hwptr_done value.
*/
static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
|