diff options
author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2021-05-27 21:32:53 +0900 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2021-05-28 11:07:21 +0200 |
commit | d360870a5bcff79bcb9633bd66bd5a63943c5e9d (patch) | |
tree | 7093611385b105de0897bff915f07d7b184b86b4 /sound | |
parent | 2f21a177631ae969537cf4ed602293d9aac9f73e (diff) |
ALSA: firewire-lib: support NO_PERIOD_WAKEUP in ALSA PCM runtime
Drivers of ALSA firewire stack can process packets for IT/IR context in
process context when the process operates ALSA PCM character device by
calling ioctl(2) with some requests. The ioctl requests are:
* SNDRV_PCM_IOCTL_HWSYNC
* SNDRV_PCM_IOCTL_SYNC_PTR
* SNDRV_PCM_IOCTL_REWIND
* SNDRV_PCM_IOCTL_FORWARD
* SNDRV_PCM_IOCTL_WRITEI_FRAMES
* SNDRV_PCM_IOCTL_READI_FRAMES
* SNDRV_PCM_IOCTL_WRITEN_FRAMES
* SNDRV_PCM_IOCTL_READN_FRAMES
This means that general application can process PCM frames apart from
hardware IRQ invocation, even if they are programmed by either IRQ-based
scheduling model or Timer-based scheduling model.
This commit add support for Timer-based scheduling model by allowing
PCM runtime to suppress both process wakeup per period and scheduling
hardware IRQ.
SNDRV_PCM_INFO_BATCH is obsoleted since ALSA IEC 61883-1/6 packet streaming
engine can report the number of transferred PCM frames within PCM period
boundary. The granularity equals to SYT_INTERVAL in blocking transmission.
In non-blocking transmission, it doesn't equal to SYT_INTERVAL but doesn't
exceed.
This patch is tested with PulseAudio, and --sched-model option of axfer
with fix against the issue reported at:
* https://lore.kernel.org/alsa-devel/687f9871-7484-1370-04d1-9c968e86f72b@linux.intel.com/#r
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20210527123253.174315-1-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/firewire/amdtp-stream.c | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index d0e9b417c019..945597ffacc2 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -192,14 +192,13 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, unsigned int maximum_usec_per_period; int err; - hw->info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | + hw->info = SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; - /* SNDRV_PCM_INFO_BATCH */ hw->periods_min = 2; hw->periods_max = UINT_MAX; @@ -610,7 +609,12 @@ static void update_pcm_pointers(struct amdtp_stream *s, s->pcm_period_pointer += frames; if (s->pcm_period_pointer >= pcm->runtime->period_size) { s->pcm_period_pointer -= pcm->runtime->period_size; - queue_work(system_highpri_wq, &s->period_work); + + // The program in user process should periodically check the status of intermediate + // buffer associated to PCM substream to process PCM frames in the buffer, instead + // of receiving notification of period elapsed by poll wait. + if (!pcm->runtime->no_period_wakeup) + queue_work(system_highpri_wq, &s->period_work); } } @@ -1056,6 +1060,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ unsigned int event_count = s->ctx_data.rx.event_count; unsigned int pkt_header_length; unsigned int packets; + bool need_hw_irq; int i; if (s->packet_index < 0) @@ -1075,6 +1080,16 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ else pkt_header_length = 0; + if (s == d->irq_target) { + // At NO_PERIOD_WAKEUP mode, the packets for all IT/IR contexts are processed by + // the tasks of user process operating ALSA PCM character device by calling ioctl(2) + // with some requests, instead of scheduled hardware IRQ of an IT context. + struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); + need_hw_irq = !pcm || !pcm->runtime->no_period_wakeup; + } else { + need_hw_irq = false; + } + for (i = 0; i < packets; ++i) { const struct pkt_desc *desc = s->pkt_descs + i; struct { @@ -1091,7 +1106,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ event_count += desc->data_blocks; if (event_count >= events_per_period) { event_count -= events_per_period; - sched_irq = true; + sched_irq = need_hw_irq; } } |