mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-04 18:09:57 +00:00
ALSA: firewire-motu: sequence replay for source packet header
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> Link: https://lore.kernel.org/r/20210602013406.26442-4-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
e50dfac81f
commit
f2ac3b8395
3 changed files with 26 additions and 74 deletions
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue