summary refs log tree commit diff
path: root/sound/firewire/amdtp.c
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2012-05-13 22:03:09 +0200
committerTakashi Iwai <tiwai@suse.de>2012-05-14 10:43:30 +0200
commit76fb87894828756e069a43ce55f775a6c893a53d (patch)
tree270c4ca71a84ace95f7aef751477427365f395e3 /sound/firewire/amdtp.c
parent7df4a691fb6645405c9d3dad8d27f8e5e3451e00 (diff)
downloadlinux-76fb87894828756e069a43ce55f775a6c893a53d.tar.gz
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/amdtp.c')
-rw-r--r--sound/firewire/amdtp.c29
1 files changed, 28 insertions, 1 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 @@
 #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);