cf5f11c09198679c588f846a76026c84a1194863
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0408-staging-bcm2835-audio-interpolate-audio-delay.patch
1 From 2ba82d9516203ce41f33e98adb667bedee3622bc Mon Sep 17 00:00:00 2001
2 From: Mike Brady <mikebrady@eircom.net>
3 Date: Mon, 22 Oct 2018 20:17:08 +0100
4 Subject: [PATCH] staging: bcm2835-audio: interpolate audio delay
5
6 commit a105a3a72824e0ac685a0711a67e4dbe29de62d0 upstream.
7
8 When the BCM2835 audio output is used, userspace sees a jitter up to 10ms
9 in the audio position, aka "delay" -- the number of frames that must
10 be output before a new frame would be played.
11 Make this a bit nicer for userspace by interpolating the position
12 using the CPU clock.
13 The overhead is small -- an extra ktime_get() every time a GPU message
14 is sent -- and another call and a few calculations whenever the delay
15 is sought from userland.
16 At 48,000 frames per second, i.e. approximately 20 microseconds per
17 frame, it would take a clock inaccuracy of
18 20 microseconds in 10 milliseconds -- 2,000 parts per million --
19 to result in an inaccurate estimate, whereas
20 crystal- or resonator-based clocks typically have an
21 inaccuracy of 10s to 100s of parts per million.
22
23 Signed-off-by: Mike Brady <mikebrady@eircom.net>
24 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
25 ---
26 .../vc04_services/bcm2835-audio/bcm2835-pcm.c | 20 +++++++++++++++++++
27 .../vc04_services/bcm2835-audio/bcm2835.h | 1 +
28 2 files changed, 21 insertions(+)
29
30 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
31 +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
32 @@ -74,6 +74,7 @@ void bcm2835_playback_fifo(struct bcm283
33 atomic_set(&alsa_stream->pos, pos);
34
35 alsa_stream->period_offset += bytes;
36 + alsa_stream->interpolate_start = ktime_get();
37 if (alsa_stream->period_offset >= alsa_stream->period_size) {
38 alsa_stream->period_offset %= alsa_stream->period_size;
39 snd_pcm_period_elapsed(substream);
40 @@ -237,6 +238,7 @@ static int snd_bcm2835_pcm_prepare(struc
41 atomic_set(&alsa_stream->pos, 0);
42 alsa_stream->period_offset = 0;
43 alsa_stream->draining = false;
44 + alsa_stream->interpolate_start = ktime_get();
45
46 return 0;
47 }
48 @@ -286,6 +288,24 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_s
49 {
50 struct snd_pcm_runtime *runtime = substream->runtime;
51 struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
52 + ktime_t now = ktime_get();
53 +
54 + /* Give userspace better delay reporting by interpolating between GPU
55 + * notifications, assuming audio speed is close enough to the clock
56 + * used for ktime
57 + */
58 +
59 + if ((ktime_to_ns(alsa_stream->interpolate_start)) &&
60 + (ktime_compare(alsa_stream->interpolate_start, now) < 0)) {
61 + u64 interval =
62 + (ktime_to_ns(ktime_sub(now,
63 + alsa_stream->interpolate_start)));
64 + u64 frames_output_in_interval =
65 + div_u64((interval * runtime->rate), 1000000000);
66 + snd_pcm_sframes_t frames_output_in_interval_sized =
67 + -frames_output_in_interval;
68 + runtime->delay = frames_output_in_interval_sized;
69 + }
70
71 return snd_pcm_indirect_playback_pointer(substream,
72 &alsa_stream->pcm_indirect,
73 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
74 +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
75 @@ -78,6 +78,7 @@ struct bcm2835_alsa_stream {
76 unsigned int period_offset;
77 unsigned int buffer_size;
78 unsigned int period_size;
79 + ktime_t interpolate_start;
80
81 struct bcm2835_audio_instance *instance;
82 int idx;