mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-09 06:33:34 +00:00
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>
This commit is contained in:
parent
7df4a691fb
commit
76fb878948
@ -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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
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)
|
|||||||
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);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#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 {
|
|||||||
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);
|
|||||||
|
|
||||||
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)
|
|||||||
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
|
||||||
|
Loading…
Reference in New Issue
Block a user