diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2012-05-13 16:03:09 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-05-14 04:43:30 -0400 |
commit | 76fb87894828756e069a43ce55f775a6c893a53d (patch) | |
tree | 270c4ca71a84ace95f7aef751477427365f395e3 /sound/firewire | |
parent | 7df4a691fb6645405c9d3dad8d27f8e5e3451e00 (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.c | 29 | ||||
-rw-r--r-- | sound/firewire/amdtp.h | 15 |
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 | ||
34 | static 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 | } |
165 | EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); | 168 | EXPORT_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 | */ | ||
176 | void 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 | } | ||
182 | EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare); | ||
183 | |||
167 | static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) | 184 | static 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 | ||
401 | static 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 | |||
384 | static void out_packet_callback(struct fw_iso_context *context, u32 cycle, | 410 | static 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 | ||
82 | void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | 84 | void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, |
83 | snd_pcm_format_t format); | 85 | snd_pcm_format_t format); |
86 | void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); | ||
84 | void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); | 87 | void 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 | */ | ||
131 | static 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 |