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 | |
| 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>
| -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 |
