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@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 {