diff options
-rw-r--r-- | sound/usb/card.h | 2 | ||||
-rw-r--r-- | sound/usb/pcm.c | 28 | ||||
-rw-r--r-- | sound/usb/pcm.h | 3 | ||||
-rw-r--r-- | sound/usb/urb.c | 30 |
4 files changed, 60 insertions, 3 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h index ae4251d5abf7..a39edcc32a93 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h | |||
@@ -94,6 +94,8 @@ struct snd_usb_substream { | |||
94 | spinlock_t lock; | 94 | spinlock_t lock; |
95 | 95 | ||
96 | struct snd_urb_ops ops; /* callbacks (must be filled at init) */ | 96 | struct snd_urb_ops ops; /* callbacks (must be filled at init) */ |
97 | int last_frame_number; /* stored frame number */ | ||
98 | int last_delay; /* stored delay */ | ||
97 | }; | 99 | }; |
98 | 100 | ||
99 | struct snd_usb_stream { | 101 | struct snd_usb_stream { |
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b8dcbf407bbb..0b699ca1957e 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c | |||
@@ -34,6 +34,30 @@ | |||
34 | #include "clock.h" | 34 | #include "clock.h" |
35 | #include "power.h" | 35 | #include "power.h" |
36 | 36 | ||
37 | /* return the estimated delay based on USB frame counters */ | ||
38 | snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, | ||
39 | unsigned int rate) | ||
40 | { | ||
41 | int current_frame_number; | ||
42 | int frame_diff; | ||
43 | int est_delay; | ||
44 | |||
45 | current_frame_number = usb_get_current_frame_number(subs->dev); | ||
46 | /* | ||
47 | * HCD implementations use different widths, use lower 8 bits. | ||
48 | * The delay will be managed up to 256ms, which is more than | ||
49 | * enough | ||
50 | */ | ||
51 | frame_diff = (current_frame_number - subs->last_frame_number) & 0xff; | ||
52 | |||
53 | /* Approximation based on number of samples per USB frame (ms), | ||
54 | some truncation for 44.1 but the estimate is good enough */ | ||
55 | est_delay = subs->last_delay - (frame_diff * rate / 1000); | ||
56 | if (est_delay < 0) | ||
57 | est_delay = 0; | ||
58 | return est_delay; | ||
59 | } | ||
60 | |||
37 | /* | 61 | /* |
38 | * return the current pcm pointer. just based on the hwptr_done value. | 62 | * return the current pcm pointer. just based on the hwptr_done value. |
39 | */ | 63 | */ |
@@ -45,6 +69,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream | |||
45 | subs = (struct snd_usb_substream *)substream->runtime->private_data; | 69 | subs = (struct snd_usb_substream *)substream->runtime->private_data; |
46 | spin_lock(&subs->lock); | 70 | spin_lock(&subs->lock); |
47 | hwptr_done = subs->hwptr_done; | 71 | hwptr_done = subs->hwptr_done; |
72 | substream->runtime->delay = snd_usb_pcm_delay(subs, | ||
73 | substream->runtime->rate); | ||
48 | spin_unlock(&subs->lock); | 74 | spin_unlock(&subs->lock); |
49 | return hwptr_done / (substream->runtime->frame_bits >> 3); | 75 | return hwptr_done / (substream->runtime->frame_bits >> 3); |
50 | } | 76 | } |
@@ -417,6 +443,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) | |||
417 | subs->hwptr_done = 0; | 443 | subs->hwptr_done = 0; |
418 | subs->transfer_done = 0; | 444 | subs->transfer_done = 0; |
419 | subs->phase = 0; | 445 | subs->phase = 0; |
446 | subs->last_delay = 0; | ||
447 | subs->last_frame_number = 0; | ||
420 | runtime->delay = 0; | 448 | runtime->delay = 0; |
421 | 449 | ||
422 | return snd_usb_substream_prepare(subs, runtime); | 450 | return snd_usb_substream_prepare(subs, runtime); |
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index ed3e283f618d..df7a003682ad 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef __USBAUDIO_PCM_H | 1 | #ifndef __USBAUDIO_PCM_H |
2 | #define __USBAUDIO_PCM_H | 2 | #define __USBAUDIO_PCM_H |
3 | 3 | ||
4 | snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, | ||
5 | unsigned int rate); | ||
6 | |||
4 | void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); | 7 | void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); |
5 | 8 | ||
6 | int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, | 9 | int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, |
diff --git a/sound/usb/urb.c b/sound/usb/urb.c index e184349aee83..b4dcccc237dc 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c | |||
@@ -718,7 +718,16 @@ static int prepare_playback_urb(struct snd_usb_substream *subs, | |||
718 | subs->hwptr_done += bytes; | 718 | subs->hwptr_done += bytes; |
719 | if (subs->hwptr_done >= runtime->buffer_size * stride) | 719 | if (subs->hwptr_done >= runtime->buffer_size * stride) |
720 | subs->hwptr_done -= runtime->buffer_size * stride; | 720 | subs->hwptr_done -= runtime->buffer_size * stride; |
721 | |||
722 | /* update delay with exact number of samples queued */ | ||
723 | runtime->delay = subs->last_delay; | ||
721 | runtime->delay += frames; | 724 | runtime->delay += frames; |
725 | subs->last_delay = runtime->delay; | ||
726 | |||
727 | /* realign last_frame_number */ | ||
728 | subs->last_frame_number = usb_get_current_frame_number(subs->dev); | ||
729 | subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ | ||
730 | |||
722 | spin_unlock_irqrestore(&subs->lock, flags); | 731 | spin_unlock_irqrestore(&subs->lock, flags); |
723 | urb->transfer_buffer_length = bytes; | 732 | urb->transfer_buffer_length = bytes; |
724 | if (period_elapsed) | 733 | if (period_elapsed) |
@@ -737,12 +746,27 @@ static int retire_playback_urb(struct snd_usb_substream *subs, | |||
737 | unsigned long flags; | 746 | unsigned long flags; |
738 | int stride = runtime->frame_bits >> 3; | 747 | int stride = runtime->frame_bits >> 3; |
739 | int processed = urb->transfer_buffer_length / stride; | 748 | int processed = urb->transfer_buffer_length / stride; |
749 | int est_delay; | ||
740 | 750 | ||
741 | spin_lock_irqsave(&subs->lock, flags); | 751 | spin_lock_irqsave(&subs->lock, flags); |
742 | if (processed > runtime->delay) | 752 | |
743 | runtime->delay = 0; | 753 | est_delay = snd_usb_pcm_delay(subs, runtime->rate); |
754 | /* update delay with exact number of samples played */ | ||
755 | if (processed > subs->last_delay) | ||
756 | subs->last_delay = 0; | ||
744 | else | 757 | else |
745 | runtime->delay -= processed; | 758 | subs->last_delay -= processed; |
759 | runtime->delay = subs->last_delay; | ||
760 | |||
761 | /* | ||
762 | * Report when delay estimate is off by more than 2ms. | ||
763 | * The error should be lower than 2ms since the estimate relies | ||
764 | * on two reads of a counter updated every ms. | ||
765 | */ | ||
766 | if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) | ||
767 | snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n", | ||
768 | est_delay, subs->last_delay); | ||
769 | |||
746 | spin_unlock_irqrestore(&subs->lock, flags); | 770 | spin_unlock_irqrestore(&subs->lock, flags); |
747 | return 0; | 771 | return 0; |
748 | } | 772 | } |