Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[pandora-kernel.git] / sound / usb / pcm.c
index b8dcbf4..0220b0f 100644 (file)
 #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);