summaryrefslogtreecommitdiffstats
path: root/sound/usb/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r--sound/usb/pcm.c34
1 files changed, 31 insertions, 3 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index b8dcbf407bbb..0220b0f335b9 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -28,12 +28,36 @@
#include "card.h"
#include "quirks.h"
#include "debug.h"
-#include "urb.h"
+#include "endpoint.h"
#include "helper.h"
#include "pcm.h"
#include "clock.h"
#include "power.h"
+/* return the estimated delay based on USB frame counters */
+snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
+ unsigned int rate)
+{
+ int current_frame_number;
+ int frame_diff;
+ int est_delay;
+
+ 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 = subs->last_delay - (frame_diff * rate / 1000);
+ if (est_delay < 0)
+ est_delay = 0;
+ return est_delay;
+}
+
/*
* return the current pcm pointer. just based on the hwptr_done value.
*/
@@ -45,6 +69,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
subs = (struct snd_usb_substream *)substream->runtime->private_data;
spin_lock(&subs->lock);
hwptr_done = subs->hwptr_done;
+ substream->runtime->delay = snd_usb_pcm_delay(subs,
+ substream->runtime->rate);
spin_unlock(&subs->lock);
return hwptr_done / (substream->runtime->frame_bits >> 3);
}
@@ -126,7 +152,7 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
- data, sizeof(data), 1000)) < 0) {
+ data, sizeof(data))) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
dev->devnum, iface, ep);
return err;
@@ -150,7 +176,7 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface,
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
UAC2_EP_CS_PITCH << 8, 0,
- data, sizeof(data), 1000)) < 0) {
+ data, sizeof(data))) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH (v2)\n",
dev->devnum, iface, fmt->altsetting);
return err;
@@ -417,6 +443,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
subs->hwptr_done = 0;
subs->transfer_done = 0;
subs->phase = 0;
+ subs->last_delay = 0;
+ subs->last_frame_number = 0;
runtime->delay = 0;
return snd_usb_substream_prepare(subs, runtime);
OpenPOWER on IntegriCloud