From 76fb87894828756e069a43ce55f775a6c893a53d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 13 May 2012 22:03:09 +0200 Subject: ALSA: firewire-lib: taskletize the snd_pcm_period_elapsed() call The following patch might introduce this call chain: PCM .pointer callback + fw_iso_context_flush_completions + packet callback + snd_pcm_period_elapsed + PCM .pointer callback Recursive calls to the pointer callback are not possible due to the PCM group locking, so avoid this by moving the period notification into a separate tasklet. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'sound/firewire/amdtp.c') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 87657dd7714c..3284ee9c1eca 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -31,6 +31,8 @@ #define INTERRUPT_INTERVAL 16 #define QUEUE_LENGTH 48 +static void pcm_period_tasklet(unsigned long data); + /** * amdtp_out_stream_init - initialize an AMDTP output stream structure * @s: the AMDTP output stream to initialize @@ -47,6 +49,7 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); + tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); s->packet_index = 0; return 0; @@ -164,6 +167,20 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, } EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); +/** + * amdtp_out_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP output stream + * + * This function should be called from the PCM device's .prepare callback. + */ +void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) +{ + tasklet_kill(&s->period_tasklet); + s->pcm_buffer_pointer = 0; + s->pcm_period_pointer = 0; +} +EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare); + static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) { unsigned int phase, data_blocks; @@ -376,11 +393,20 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) s->pcm_period_pointer += data_blocks; if (s->pcm_period_pointer >= pcm->runtime->period_size) { s->pcm_period_pointer -= pcm->runtime->period_size; - snd_pcm_period_elapsed(pcm); + tasklet_hi_schedule(&s->period_tasklet); } } } +static void pcm_period_tasklet(unsigned long data) +{ + struct amdtp_out_stream *s = (void *)data; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + + if (pcm) + snd_pcm_period_elapsed(pcm); +} + static void out_packet_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *data) { @@ -532,6 +558,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) return; } + tasklet_kill(&s->period_tasklet); fw_iso_context_stop(s->context); fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); -- cgit v1.2.3 From e9148dddc3c7b6121300319c3e31f9380d459be8 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 13 May 2012 18:49:14 +0200 Subject: ALSA: firewire-lib: flush completed packets when reading PCM position By flushing all completed but not yet reported packets before reading the PCM hardware position, the granularity of the pointer is improved from the interrupt interval to the packet size. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sound/firewire/amdtp.c') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 3284ee9c1eca..c2685fbd7366 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -531,6 +531,20 @@ err_unlock: } EXPORT_SYMBOL(amdtp_out_stream_start); +/** + * amdtp_out_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP output stream that transports the PCM data + * + * Returns the current buffer position, in frames. + */ +unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) +{ + fw_iso_context_flush_completions(s->context); + + return ACCESS_ONCE(s->pcm_buffer_pointer); +} +EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer); + /** * amdtp_out_stream_update - update the stream after a bus reset * @s: the AMDTP output stream -- cgit v1.2.3 From 92b862c7d685f5971a954e6ded51891d4adc412b Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 13 May 2012 19:07:22 +0200 Subject: ALSA: firewire-lib: optimize packet flushing Trying to flush completed packets is pointless when the pointer callback was called from the packet completion callback; avoid it. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound/firewire/amdtp.c') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index c2685fbd7366..ea995af6d049 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -178,6 +178,7 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) tasklet_kill(&s->period_tasklet); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; + s->pointer_flush = true; } EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare); @@ -393,6 +394,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) s->pcm_period_pointer += data_blocks; if (s->pcm_period_pointer >= pcm->runtime->period_size) { s->pcm_period_pointer -= pcm->runtime->period_size; + s->pointer_flush = false; tasklet_hi_schedule(&s->period_tasklet); } } @@ -539,7 +541,11 @@ EXPORT_SYMBOL(amdtp_out_stream_start); */ unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) { - fw_iso_context_flush_completions(s->context); + /* this optimization is allowed to be racy */ + if (s->pointer_flush) + fw_iso_context_flush_completions(s->context); + else + s->pointer_flush = true; return ACCESS_ONCE(s->pcm_buffer_pointer); } -- cgit v1.2.3