mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
					
				
					 2 changed files with 31 additions and 13 deletions
				
			
		| 
						 | 
				
			
			@ -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,9 +393,18 @@ 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;
 | 
			
		||||
			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,
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
 | 
			
		||||
#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include <linux/interrupt.h>
 | 
			
		||||
#include <linux/mutex.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include "packets-buffer.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +56,7 @@ struct amdtp_out_stream {
 | 
			
		|||
	struct iso_packets_buffer buffer;
 | 
			
		||||
 | 
			
		||||
	struct snd_pcm_substream *pcm;
 | 
			
		||||
	struct tasklet_struct period_tasklet;
 | 
			
		||||
 | 
			
		||||
	int packet_index;
 | 
			
		||||
	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,
 | 
			
		||||
				     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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -122,18 +125,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
 | 
			
		|||
	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
 | 
			
		||||
 * @s: the AMDTP output stream
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue