aboutsummaryrefslogtreecommitdiffstats
path: root/sound/firewire
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2012-05-13 16:03:09 -0400
committerTakashi Iwai <tiwai@suse.de>2012-05-14 04:43:30 -0400
commit76fb87894828756e069a43ce55f775a6c893a53d (patch)
tree270c4ca71a84ace95f7aef751477427365f395e3 /sound/firewire
parent7df4a691fb6645405c9d3dad8d27f8e5e3451e00 (diff)
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 <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/firewire')
-rw-r--r--sound/firewire/amdtp.c29
-rw-r--r--sound/firewire/amdtp.h15
2 files changed, 31 insertions, 13 deletions
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 @@
31#define INTERRUPT_INTERVAL 16 31#define INTERRUPT_INTERVAL 16
32#define QUEUE_LENGTH 48 32#define QUEUE_LENGTH 48
33 33
34static void pcm_period_tasklet(unsigned long data);
35
34/** 36/**
35 * amdtp_out_stream_init - initialize an AMDTP output stream structure 37 * amdtp_out_stream_init - initialize an AMDTP output stream structure
36 * @s: the AMDTP output stream to initialize 38 * @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,
47 s->flags = flags; 49 s->flags = flags;
48 s->context = ERR_PTR(-1); 50 s->context = ERR_PTR(-1);
49 mutex_init(&s->mutex); 51 mutex_init(&s->mutex);
52 tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
50 s->packet_index = 0; 53 s->packet_index = 0;
51 54
52 return 0; 55 return 0;
@@ -164,6 +167,20 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
164} 167}
165EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); 168EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
166 169
170/**
171 * amdtp_out_stream_pcm_prepare - prepare PCM device for running
172 * @s: the AMDTP output stream
173 *
174 * This function should be called from the PCM device's .prepare callback.
175 */
176void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
177{
178 tasklet_kill(&s->period_tasklet);
179 s->pcm_buffer_pointer = 0;
180 s->pcm_period_pointer = 0;
181}
182EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
183
167static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) 184static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
168{ 185{
169 unsigned int phase, data_blocks; 186 unsigned int phase, data_blocks;
@@ -376,11 +393,20 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
376 s->pcm_period_pointer += data_blocks; 393 s->pcm_period_pointer += data_blocks;
377 if (s->pcm_period_pointer >= pcm->runtime->period_size) { 394 if (s->pcm_period_pointer >= pcm->runtime->period_size) {
378 s->pcm_period_pointer -= pcm->runtime->period_size; 395 s->pcm_period_pointer -= pcm->runtime->period_size;
379 snd_pcm_period_elapsed(pcm); 396 tasklet_hi_schedule(&s->period_tasklet);
380 } 397 }
381 } 398 }
382} 399}
383 400
401static void pcm_period_tasklet(unsigned long data)
402{
403 struct amdtp_out_stream *s = (void *)data;
404 struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
405
406 if (pcm)
407 snd_pcm_period_elapsed(pcm);
408}
409
384static void out_packet_callback(struct fw_iso_context *context, u32 cycle, 410static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
385 size_t header_length, void *header, void *data) 411 size_t header_length, void *header, void *data)
386{ 412{
@@ -532,6 +558,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
532 return; 558 return;
533 } 559 }
534 560
561 tasklet_kill(&s->period_tasklet);
535 fw_iso_context_stop(s->context); 562 fw_iso_context_stop(s->context);
536 fw_iso_context_destroy(s->context); 563 fw_iso_context_destroy(s->context);
537 s->context = ERR_PTR(-1); 564 s->context = ERR_PTR(-1);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 537a9cb83581..4987f826f9f3 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -1,6 +1,7 @@
1#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED 1#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
2#define SOUND_FIREWIRE_AMDTP_H_INCLUDED 2#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
3 3
4#include <linux/interrupt.h>
4#include <linux/mutex.h> 5#include <linux/mutex.h>
5#include <linux/spinlock.h> 6#include <linux/spinlock.h>
6#include "packets-buffer.h" 7#include "packets-buffer.h"
@@ -55,6 +56,7 @@ struct amdtp_out_stream {
55 struct iso_packets_buffer buffer; 56 struct iso_packets_buffer buffer;
56 57
57 struct snd_pcm_substream *pcm; 58 struct snd_pcm_substream *pcm;
59 struct tasklet_struct period_tasklet;
58 60
59 int packet_index; 61 int packet_index;
60 unsigned int data_block_counter; 62 unsigned int data_block_counter;
@@ -81,6 +83,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s);
81 83
82void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, 84void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
83 snd_pcm_format_t format); 85 snd_pcm_format_t format);
86void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
84void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); 87void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
85 88
86/** 89/**
@@ -123,18 +126,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
123} 126}
124 127
125/** 128/**
126 * amdtp_out_stream_pcm_prepare - prepare PCM device for running
127 * @s: the AMDTP output stream
128 *
129 * This function should be called from the PCM device's .prepare callback.
130 */
131static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
132{
133 s->pcm_buffer_pointer = 0;
134 s->pcm_period_pointer = 0;
135}
136
137/**
138 * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device 129 * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
139 * @s: the AMDTP output stream 130 * @s: the AMDTP output stream
140 * @pcm: the PCM device to be started, or %NULL to stop the current device 131 * @pcm: the PCM device to be started, or %NULL to stop the current device