diff options
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b8dcbf407bbb..0eed6115c2d4 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); @@ -667,6 +695,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { struct audioformat *fp; + int *rate_list; int count = 0, needs_knot = 0; int err; @@ -680,7 +709,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, if (!needs_knot) return 0; - subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL); + subs->rate_list.list = rate_list = + kmalloc(sizeof(int) * count, GFP_KERNEL); if (!subs->rate_list.list) return -ENOMEM; subs->rate_list.count = count; @@ -689,7 +719,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, list_for_each_entry(fp, &subs->fmt_list, list) { int i; for (i = 0; i < fp->nr_rates; i++) - subs->rate_list.list[count++] = fp->rate_table[i]; + rate_list[count++] = fp->rate_table[i]; } err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &subs->rate_list); |