summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Brady <mikebrady@eircom.net>2018-10-22 20:17:08 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-11-08 03:59:47 -0800
commit01c5c5614a9e67eda3bbec74d4863bdc57b5c990 (patch)
treead9822e99f65fe8814cdc11035a4e75c56e406c6
parent92a4d9a8bcfed4e12f6e7fbcc558a45ecccc7f3c (diff)
staging: bcm2835-audio: interpolate audio delay
When the BCM2835 audio output is used, userspace sees a jitter up to 10ms in the audio position, aka "delay" -- the number of frames that must be output before a new frame would be played. Make this a bit nicer for userspace by interpolating the position using the CPU clock. The overhead is small -- an extra ktime_get() every time a GPU message is sent -- and another call and a few calculations whenever the delay is sought from userland. At 48,000 frames per second, i.e. approximately 20 microseconds per frame, it would take a clock inaccuracy of 20 microseconds in 10 milliseconds -- 2,000 parts per million -- to result in an inaccurate estimate, whereas crystal- or resonator-based clocks typically have an inaccuracy of 10s to 100s of parts per million. Signed-off-by: Mike Brady <mikebrady@eircom.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c20
-rw-r--r--drivers/staging/vc04_services/bcm2835-audio/bcm2835.h1
2 files changed, 21 insertions, 0 deletions
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
index 98b6977bdce7..bc1eaa3a0773 100644
--- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
+++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
@@ -74,6 +74,7 @@ void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream,
atomic_set(&alsa_stream->pos, pos);
alsa_stream->period_offset += bytes;
+ alsa_stream->interpolate_start = ktime_get();
if (alsa_stream->period_offset >= alsa_stream->period_size) {
alsa_stream->period_offset %= alsa_stream->period_size;
snd_pcm_period_elapsed(substream);
@@ -237,6 +238,7 @@ static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
atomic_set(&alsa_stream->pos, 0);
alsa_stream->period_offset = 0;
alsa_stream->draining = false;
+ alsa_stream->interpolate_start = ktime_get();
return 0;
}
@@ -286,6 +288,24 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+ ktime_t now = ktime_get();
+
+ /* Give userspace better delay reporting by interpolating between GPU
+ * notifications, assuming audio speed is close enough to the clock
+ * used for ktime
+ */
+
+ if ((ktime_to_ns(alsa_stream->interpolate_start)) &&
+ (ktime_compare(alsa_stream->interpolate_start, now) < 0)) {
+ u64 interval =
+ (ktime_to_ns(ktime_sub(now,
+ alsa_stream->interpolate_start)));
+ u64 frames_output_in_interval =
+ div_u64((interval * runtime->rate), 1000000000);
+ snd_pcm_sframes_t frames_output_in_interval_sized =
+ -frames_output_in_interval;
+ runtime->delay = frames_output_in_interval_sized;
+ }
return snd_pcm_indirect_playback_pointer(substream,
&alsa_stream->pcm_indirect,
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
index 34a0125ce646..ed0feb34b6c8 100644
--- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
+++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
@@ -77,6 +77,7 @@ struct bcm2835_alsa_stream {
unsigned int period_offset;
unsigned int buffer_size;
unsigned int period_size;
+ ktime_t interpolate_start;
struct bcm2835_audio_instance *instance;
int idx;