Commit 76fb8789 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai

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: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 7df4a691
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#define INTERRUPT_INTERVAL 16 #define INTERRUPT_INTERVAL 16
#define QUEUE_LENGTH 48 #define QUEUE_LENGTH 48
static void pcm_period_tasklet(unsigned long data);
/** /**
* amdtp_out_stream_init - initialize an AMDTP output stream structure * amdtp_out_stream_init - initialize an AMDTP output stream structure
* @s: the AMDTP output stream to initialize * @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,6 +49,7 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
s->flags = flags; s->flags = flags;
s->context = ERR_PTR(-1); s->context = ERR_PTR(-1);
mutex_init(&s->mutex); mutex_init(&s->mutex);
tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
s->packet_index = 0; s->packet_index = 0;
return 0; return 0;
...@@ -164,6 +167,20 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, ...@@ -164,6 +167,20 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
} }
EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); 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) static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
{ {
unsigned int phase, data_blocks; unsigned int phase, data_blocks;
...@@ -376,11 +393,20 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) ...@@ -376,11 +393,20 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
s->pcm_period_pointer += data_blocks; s->pcm_period_pointer += data_blocks;
if (s->pcm_period_pointer >= pcm->runtime->period_size) { if (s->pcm_period_pointer >= pcm->runtime->period_size) {
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, static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
size_t header_length, void *header, void *data) size_t header_length, void *header, void *data)
{ {
...@@ -532,6 +558,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) ...@@ -532,6 +558,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
return; return;
} }
tasklet_kill(&s->period_tasklet);
fw_iso_context_stop(s->context); fw_iso_context_stop(s->context);
fw_iso_context_destroy(s->context); fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1); s->context = ERR_PTR(-1);
......
#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
#define SOUND_FIREWIRE_AMDTP_H_INCLUDED #define SOUND_FIREWIRE_AMDTP_H_INCLUDED
#include <linux/interrupt.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include "packets-buffer.h" #include "packets-buffer.h"
...@@ -55,6 +56,7 @@ struct amdtp_out_stream { ...@@ -55,6 +56,7 @@ struct amdtp_out_stream {
struct iso_packets_buffer buffer; struct iso_packets_buffer buffer;
struct snd_pcm_substream *pcm; struct snd_pcm_substream *pcm;
struct tasklet_struct period_tasklet;
int packet_index; int packet_index;
unsigned int data_block_counter; unsigned int data_block_counter;
...@@ -81,6 +83,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s); ...@@ -81,6 +83,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s);
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
snd_pcm_format_t format); snd_pcm_format_t format);
void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
/** /**
...@@ -122,18 +125,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s) ...@@ -122,18 +125,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
return s->packet_index < 0; return s->packet_index < 0;
} }
/**
* 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.
*/
static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
{
s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0;
}
/** /**
* amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
* @s: the AMDTP output stream * @s: the AMDTP output stream
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment