[PATCH 3/3] ALSA: firewire-motu: sequence replay for source packet header

Takashi Sakamoto o-takashi at sakamocchi.jp
Wed Jun 2 03:34:06 CEST 2021


This commit takes ALSA firewire-motu driver to perform sequence replay for
media clock recovery.

Unlike the other types of device, the devices in MOTU FireWire series
require two levels of sequence replay; the sequence of the number of
data blocks per packet and the sequence of source packet header per data
block. The former is already cached by ALSA IEC 61883-1/6 packet streaming
engine and ready to be replayed. The latter is also cached by ALSA
firewire-motu driver itself with a previous patch. This commit takes
the driver to replay both of them from the caches.

The sequence replay is tested with below models:

 * 828 mkII
 * Traveler
 * UltraLite
 * 828 mk3 FireWire
 * 828 mk3 Hybrid (except for high sampling transfer frequency
 * UltraLite mk3 FireWire
 * 4pre
 * AudioExpress

Unfortunately, below models still don't generate better sound, requires
more work:

 * 8pre
 * 828 mk3 Hybrid at high sampling transfer frequency

As long as I know, MOTU protocol version 1 requires extra care of the
format of data block, thus below models are not supported yet in this
time:

 * 828
 * 896

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 sound/firewire/motu/amdtp-motu.c  | 91 ++++++-------------------------
 sound/firewire/motu/motu-stream.c |  7 ++-
 sound/firewire/motu/motu.h        |  2 +
 3 files changed, 26 insertions(+), 74 deletions(-)

diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index 1741ceb381c7..5388b85fb60e 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -20,10 +20,6 @@
 #define CYCLES_PER_SECOND	8000
 #define TICKS_PER_SECOND	(TICKS_PER_CYCLE * CYCLES_PER_SECOND)
 
-#define IEEE1394_SEC_MODULUS	128
-
-#define TRANSFER_DELAY_TICKS	0x2e00 /* 479.17 microseconds */
-
 #define CIP_SPH_CYCLE_SHIFT	12
 #define CIP_SPH_CYCLE_MASK	0x01fff000
 #define CIP_SPH_OFFSET_MASK	0x00000fff
@@ -35,14 +31,6 @@
 #define MIDI_BYTES_PER_SECOND	3093
 
 struct amdtp_motu {
-	/* For timestamp processing.  */
-	unsigned int quotient_ticks_per_event;
-	unsigned int remainder_ticks_per_event;
-	unsigned int next_ticks;
-	unsigned int next_accumulated;
-	unsigned int next_cycles;
-	unsigned int next_seconds;
-
 	unsigned int pcm_chunks;
 	unsigned int pcm_byte_offset;
 
@@ -61,20 +49,8 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
 			      unsigned int midi_ports,
 			      struct snd_motu_packet_format *formats)
 {
-	static const struct {
-		unsigned int quotient_ticks_per_event;
-		unsigned int remainder_ticks_per_event;
-	} params[] = {
-		[CIP_SFC_44100]  = { 557, 123 },
-		[CIP_SFC_48000]  = { 512,   0 },
-		[CIP_SFC_88200]  = { 278, 282 },
-		[CIP_SFC_96000]  = { 256,   0 },
-		[CIP_SFC_176400] = { 139, 141 },
-		[CIP_SFC_192000] = { 128,   0 },
-	};
 	struct amdtp_motu *p = s->protocol;
 	unsigned int pcm_chunks, data_chunks, data_block_quadlets;
-	unsigned int delay;
 	unsigned int mode;
 	int i, err;
 
@@ -111,18 +87,6 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
 	p->midi_db_count = 0;
 	p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
 
-	delay = TRANSFER_DELAY_TICKS;
-
-	// For no-data or empty packets to adjust PCM sampling frequency.
-	delay += TICKS_PER_SECOND * s->syt_interval / rate;
-
-	p->next_seconds = 0;
-	p->next_cycles = delay / TICKS_PER_CYCLE;
-	p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
-	p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
-	p->next_ticks = delay % TICKS_PER_CYCLE;
-	p->next_accumulated = 0;
-
 	return 0;
 }
 
@@ -400,47 +364,26 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
 	return pcm_frames;
 }
 
-static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
+static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks,
+		      unsigned int data_block_quadlets)
 {
-	p->next_accumulated += p->remainder_ticks_per_event;
-	if (p->next_accumulated >= 441) {
-		p->next_accumulated -= 441;
-		p->next_ticks++;
-	}
-
-	p->next_ticks += p->quotient_ticks_per_event;
-	if (p->next_ticks >= TICKS_PER_CYCLE) {
-		p->next_ticks -= TICKS_PER_CYCLE;
-		p->next_cycles++;
-	}
-
-	if (p->next_cycles >= CYCLES_PER_SECOND) {
-		p->next_cycles -= CYCLES_PER_SECOND;
-		p->next_seconds++;
-	}
-
-	if (p->next_seconds >= IEEE1394_SEC_MODULUS)
-		p->next_seconds -= IEEE1394_SEC_MODULUS;
-}
-
-static void write_sph(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks,
-		      const unsigned int rx_start_cycle)
-{
-	struct amdtp_motu *p = s->protocol;
-	unsigned int next_cycles;
-	unsigned int i;
-	u32 sph;
+	unsigned int *event_offsets = cache->event_offsets;
+	const unsigned int cache_size = cache->size;
+	unsigned int cache_head = cache->head;
+	unsigned int base_tick = cache->rx_cycle_count * TICKS_PER_CYCLE;
+	int i;
 
 	for (i = 0; i < data_blocks; i++) {
-		next_cycles = (rx_start_cycle + p->next_cycles) % CYCLES_PER_SECOND;
-		sph = ((next_cycles << CIP_SPH_CYCLE_SHIFT) | p->next_ticks) &
-		      (CIP_SPH_CYCLE_MASK | CIP_SPH_OFFSET_MASK);
+		unsigned int tick = (base_tick + event_offsets[cache_head]) % TICKS_PER_SECOND;
+		u32 sph = ((tick / TICKS_PER_CYCLE) << CIP_SPH_CYCLE_SHIFT) | (tick % TICKS_PER_CYCLE);
 		*buffer = cpu_to_be32(sph);
 
-		compute_next_elapse_from_start(p);
-
-		buffer += s->data_block_quadlets;
+		cache_head = (cache_head + 1) % cache_size;
+		buffer += data_block_quadlets;
 	}
+
+	cache->head = cache_head;
+	cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND;
 }
 
 static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
@@ -448,11 +391,13 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
 					    unsigned int packets,
 					    struct snd_pcm_substream *pcm)
 {
-	const unsigned int rx_start_cycle = s->domain->processing_cycle.rx_start;
 	struct amdtp_motu *p = s->protocol;
 	unsigned int pcm_frames = 0;
 	int i;
 
+	if (p->cache->rx_cycle_count == UINT_MAX)
+		p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
+
 	// For data block processing.
 	for (i = 0; i < packets; ++i) {
 		const struct pkt_desc *desc = descs + i;
@@ -471,7 +416,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
 
 		// TODO: how to interact control messages between userspace?
 
-		write_sph(s, buf, data_blocks, rx_start_cycle);
+		write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
 	}
 
 	// For tracepoints.
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 369002568b2d..43ff5be32b15 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -274,8 +274,13 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
 
 		motu->cache.tail = 0;
 		motu->cache.tx_cycle_count = UINT_MAX;
+		motu->cache.head = 0;
+		motu->cache.rx_cycle_count = UINT_MAX;
 
-		err = amdtp_domain_start(&motu->domain, 0, false, false);
+		// NOTE: The device requires both of replay; the sequence of the number of data
+		// blocks per packet, and the sequence of source packet header per data block as
+		// presentation time.
+		err = amdtp_domain_start(&motu->domain, 0, true, false);
 		if (err < 0)
 			goto stop_streams;
 
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 10ba87062e81..674e3dc4e45d 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -44,6 +44,8 @@ struct amdtp_motu_cache {
 	unsigned int size;
 	unsigned int tail;
 	unsigned int tx_cycle_count;
+	unsigned int head;
+	unsigned int rx_cycle_count;
 };
 
 struct snd_motu {
-- 
2.27.0



More information about the Alsa-devel mailing list