[PATCH 00/10] ALSA: firewire-lib: pool sequence of syt offset and data blocks in AMDTP domain structure
Hi,
In current implementation, the packets for outgoing AMDTP streams are processed by per-stream calculation of syt offset and the number of data blocks per packet.
This patchset is a preparation for future extension that the packets for outgoing AMDTP streams are processed according to the result of 'sampling clock recovery' in IEC 61883-6:2005 from selected incoming AMDTP stream. The preparation is to process packets for outgoing AMDTP streams by pool in AMDTP domain structure for the sequence of syt offset and the number of data blocks. The way to generate sequence is still the same as the current implementation, which generates by ideal sampling transmission frequency against IEEE 1394 bus clock.
Takashi Sakamoto (10): ALSA: firewire-lib: fix invalid assignment to union data for directional parameter ALSA: firewire-lib: use macro for maximum value of second in 1394 OHCI isoc descriptor ALSA: firewire-lib: add reference to domain structure from stream structure ALSA: firewire-lib: code refactoring for parameters of packet queue and IRQ timing ALSA: firewire-lib: code refactoring for syt computation ALSA: firewire-lib: code refactoring for syt offset calculation ALSA: firewire-lib: code refactoring for data block calculation ALSA: firewire-lib: add cache for packet sequence to AMDTP domain structure ALSA: firewire-lib: pool ideal sequence of syt offset and data block ALSA: firewire-lib: use sequence of syt offset and data block on pool in AMDTP domain
sound/firewire/amdtp-am824.c | 3 +- sound/firewire/amdtp-stream.c | 326 ++++++++++++++++++++-------------- sound/firewire/amdtp-stream.h | 20 ++- 3 files changed, 209 insertions(+), 140 deletions(-)
Although the value of FDF is used just for outgoing stream, the assignment to union member is done for both directions of stream. At present this causes no issue because the value of same position is reassigned later for opposite stream. However, it's better to add if statement.
Fixes: d3d10a4a1b19 ("ALSA: firewire-lib: use union for directional parameters") Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-am824.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 67d735e9a6a4..fea92e148790 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -82,7 +82,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, if (err < 0) return err;
- s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; + if (s->direction == AMDTP_OUT_STREAM) + s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
p->pcm_channels = pcm_channels; p->midi_ports = midi_ports;
In descriptor of isochronous context in 1394 OHCI, the field of second has 3 bit, thus the maximum value is 8. The value is used for correct cycle calculation.
This commit replaces hard-coded value with macro to obsolete magic number.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 37d38efb4c87..fcde01b54d11 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -20,6 +20,8 @@ #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+#define OHCI_MAX_SECOND 8 + /* Always support Linux tracing subsystem. */ #define CREATE_TRACE_POINTS #include "amdtp-stream-trace.h" @@ -680,8 +682,8 @@ static inline u32 compute_cycle_count(__be32 ctx_header_tstamp) static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) { cycle += addend; - if (cycle >= 8 * CYCLES_PER_SECOND) - cycle -= 8 * CYCLES_PER_SECOND; + if (cycle >= OHCI_MAX_SECOND * CYCLES_PER_SECOND) + cycle -= OHCI_MAX_SECOND * CYCLES_PER_SECOND; return cycle; }
In current implementation, AMDTP domain structure and AMDTP stream structure has one way of reference from the former to the latter. For future extension, bidirectional reference is needed.
This commit adds a member into stream structure to refer to domain structure to which the stream belongs.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 75 ++++++++++------------------------- sound/firewire/amdtp-stream.h | 3 ++ 2 files changed, 23 insertions(+), 55 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index fcde01b54d11..ce63ff6b7f03 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -793,14 +793,6 @@ static void process_ctx_payloads(struct amdtp_stream *s, update_pcm_pointers(s, pcm, pcm_frames); }
-static void amdtp_stream_master_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data); - -static void amdtp_stream_master_first_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data); - static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) @@ -810,7 +802,6 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, unsigned int events_per_period = s->ctx_data.rx.events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; unsigned int packets; - bool is_irq_target; int i;
if (s->packet_index < 0) @@ -823,10 +814,6 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
process_ctx_payloads(s, s->pkt_descs, packets);
- is_irq_target = - !!(context->callback.sc == amdtp_stream_master_callback || - context->callback.sc == amdtp_stream_master_first_callback); - for (i = 0; i < packets; ++i) { const struct pkt_desc *desc = s->pkt_descs + i; unsigned int syt; @@ -845,7 +832,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, desc->data_blocks, desc->data_block_counter, syt, i);
- if (is_irq_target) { + if (s == s->domain->irq_target) { event_count += desc->data_blocks; if (event_count >= events_per_period) { event_count -= events_per_period; @@ -898,12 +885,12 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, } }
-static void amdtp_stream_master_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data) +static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, + void *private_data) { - struct amdtp_domain *d = private_data; - struct amdtp_stream *irq_target = d->irq_target; + struct amdtp_stream *irq_target = private_data; + struct amdtp_domain *d = irq_target->domain; struct amdtp_stream *s;
out_stream_callback(context, tstamp, header_length, header, irq_target); @@ -952,7 +939,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, } else { cycle = compute_it_cycle(*ctx_header, s->queue_size);
- context->callback.sc = out_stream_callback; + if (s == s->domain->irq_target) + context->callback.sc = irq_target_callback; + else + context->callback.sc = out_stream_callback; }
s->start_cycle = cycle; @@ -960,32 +950,11 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, context->callback.sc(context, tstamp, header_length, header, s); }
-static void amdtp_stream_master_first_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data) -{ - struct amdtp_domain *d = private_data; - struct amdtp_stream *s = d->irq_target; - const __be32 *ctx_header = header; - - s->callbacked = true; - wake_up(&s->callback_wait); - - s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size); - - context->callback.sc = amdtp_stream_master_callback; - - context->callback.sc(context, tstamp, header_length, header, d); -} - /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @speed: firewire speed code - * @d: the AMDTP domain to which the AMDTP stream belongs - * @is_irq_target: whether isoc context for the AMDTP stream is used to generate - * hardware IRQ. * @start_cycle: the isochronous cycle to start the context. Start immediately * if negative value is given. * @@ -994,7 +963,6 @@ static void amdtp_stream_master_first_callback(struct fw_iso_context *context, * device can be started. */ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, - struct amdtp_domain *d, bool is_irq_target, int start_cycle) { static const struct { @@ -1009,15 +977,14 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, [CIP_SFC_88200] = { 0, 67 }, [CIP_SFC_176400] = { 0, 67 }, }; - unsigned int events_per_buffer = d->events_per_buffer; - unsigned int events_per_period = d->events_per_period; + bool is_irq_target = (s == s->domain->irq_target); + unsigned int events_per_buffer; + unsigned int events_per_period; unsigned int idle_irq_interval; unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; int type, tag, err; - fw_iso_callback_t ctx_cb; - void *ctx_data;
mutex_lock(&s->mutex);
@@ -1068,6 +1035,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, // This is a case that AMDTP streams in domain run just for MIDI // substream. Use the number of events equivalent to 10 msec as // interval of hardware IRQ. + events_per_buffer = s->domain->events_per_buffer; + events_per_period = s->domain->events_per_period; if (events_per_period == 0) events_per_period = amdtp_rate_table[s->sfc] / 100; if (events_per_buffer == 0) @@ -1086,16 +1055,11 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, if (is_irq_target) { s->ctx_data.rx.events_per_period = events_per_period; s->ctx_data.rx.event_count = 0; - ctx_cb = amdtp_stream_master_first_callback; - ctx_data = d; - } else { - ctx_cb = amdtp_stream_first_callback; - ctx_data = s; }
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size, - ctx_cb, ctx_data); + amdtp_stream_first_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -1340,6 +1304,7 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
s->channel = channel; s->speed = speed; + s->domain = d;
return 0; } @@ -1428,15 +1393,15 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) }
if (s != d->irq_target) { - err = amdtp_stream_start(s, s->channel, s->speed, d, - false, cycle_match); + err = amdtp_stream_start(s, s->channel, s->speed, + cycle_match); if (err < 0) goto error; } }
s = d->irq_target; - err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1); + err = amdtp_stream_start(s, s->channel, s->speed, -1); if (err < 0) goto error;
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index f2d44e2dc3c8..477fbfe713e5 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -108,6 +108,8 @@ typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)( const struct pkt_desc *desc, unsigned int packets, struct snd_pcm_substream *pcm); + +struct amdtp_domain; struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; @@ -180,6 +182,7 @@ struct amdtp_stream { int channel; int speed; struct list_head list; + struct amdtp_domain *domain; };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
Although the parameter for packet queue and IRQ timing is calculated when AMDTP stream starts, the calculated parameters are the same between streams in AMDTP domain.
This commit moves the calculation and decide the parameters when AMDTP domain starts.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 56 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index ce63ff6b7f03..6130c240ff33 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -957,13 +957,16 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, * @speed: firewire speed code * @start_cycle: the isochronous cycle to start the context. Start immediately * if negative value is given. + * @queue_size: The number of packets in the queue. + * @idle_irq_interval: the interval to queue packet during initial state. * * The stream cannot be started until it has been configured with * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI * device can be started. */ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, - int start_cycle) + int start_cycle, unsigned int queue_size, + unsigned int idle_irq_interval) { static const struct { unsigned int data_block; @@ -978,9 +981,6 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, [CIP_SFC_176400] = { 0, 67 }, }; bool is_irq_target = (s == s->domain->irq_target); - unsigned int events_per_buffer; - unsigned int events_per_period; - unsigned int idle_irq_interval; unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; @@ -1032,30 +1032,11 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; }
- // This is a case that AMDTP streams in domain run just for MIDI - // substream. Use the number of events equivalent to 10 msec as - // interval of hardware IRQ. - events_per_buffer = s->domain->events_per_buffer; - events_per_period = s->domain->events_per_period; - if (events_per_period == 0) - events_per_period = amdtp_rate_table[s->sfc] / 100; - if (events_per_buffer == 0) - events_per_buffer = events_per_period * 3; - - idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, - amdtp_rate_table[s->sfc]); - s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, - amdtp_rate_table[s->sfc]); - - err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size, + err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir); if (err < 0) goto err_unlock; - - if (is_irq_target) { - s->ctx_data.rx.events_per_period = events_per_period; - s->ctx_data.rx.event_count = 0; - } + s->queue_size = queue_size;
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size, @@ -1341,6 +1322,10 @@ static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) */ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { + unsigned int events_per_buffer = d->events_per_buffer; + unsigned int events_per_period = d->events_per_period; + unsigned int idle_irq_interval; + unsigned int queue_size; struct amdtp_stream *s; int cycle; int err; @@ -1354,6 +1339,17 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) return -ENXIO; d->irq_target = s;
+ // This is a case that AMDTP streams in domain run just for MIDI + // substream. Use the number of events equivalent to 10 msec as + // interval of hardware IRQ. + if (events_per_period == 0) + events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100; + if (events_per_buffer == 0) + events_per_buffer = events_per_period * 3; + + queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, + amdtp_rate_table[d->irq_target->sfc]); + if (ir_delay_cycle > 0) { struct fw_card *fw_card = fw_parent_device(s->unit)->card;
@@ -1394,14 +1390,20 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
if (s != d->irq_target) { err = amdtp_stream_start(s, s->channel, s->speed, - cycle_match); + cycle_match, queue_size, 0); if (err < 0) goto error; } }
s = d->irq_target; - err = amdtp_stream_start(s, s->channel, s->speed, -1); + s->ctx_data.rx.events_per_period = events_per_period; + s->ctx_data.rx.event_count = 0; + + idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, + amdtp_rate_table[d->irq_target->sfc]); + err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, + idle_irq_interval); if (err < 0) goto error;
In current implementation for outgoing AMDTP packet, the value of syt field in CIP header is computed when calculating syt offset. For future extension, it's convenient to split the computation and calculation.
This commit splits them.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 36 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 6130c240ff33..1605ea4301ce 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -383,10 +383,9 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, return data_blocks; }
-static unsigned int calculate_syt(struct amdtp_stream *s, - unsigned int cycle) +static unsigned int calculate_syt_offset(struct amdtp_stream *s) { - unsigned int syt_offset, phase, index, syt; + unsigned int syt_offset, phase, index;
if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) { if (!cip_sfc_is_base_44100(s->sfc)) @@ -416,15 +415,10 @@ static unsigned int calculate_syt(struct amdtp_stream *s, syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE; s->ctx_data.rx.last_syt_offset = syt_offset;
- if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += s->ctx_data.rx.transfer_delay; - syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; - syt += syt_offset % TICKS_PER_CYCLE; + if (syt_offset >= TICKS_PER_CYCLE) + syt_offset = CIP_SYT_NO_INFO;
- return syt & CIP_SYT_MASK; - } else { - return CIP_SYT_NO_INFO; - } + return syt_offset; }
static void update_pcm_pointers(struct amdtp_stream *s, @@ -740,6 +734,17 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, return 0; }
+static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, + unsigned int transfer_delay) +{ + unsigned int syt; + + syt_offset += transfer_delay; + syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) | + (syt_offset % TICKS_PER_CYCLE); + return syt & CIP_SYT_MASK; +} + static void generate_ideal_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs, const __be32 *ctx_header, @@ -751,9 +756,16 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; unsigned int index = (s->packet_index + i) % s->queue_size; + unsigned int syt_offset;
desc->cycle = compute_it_cycle(*ctx_header, s->queue_size); - desc->syt = calculate_syt(s, desc->cycle); + syt_offset = calculate_syt_offset(s); + if (syt_offset != CIP_SYT_NO_INFO) { + desc->syt = compute_syt(syt_offset, desc->cycle, + s->ctx_data.rx.transfer_delay); + } else { + desc->syt = syt_offset; + } desc->data_blocks = calculate_data_blocks(s, desc->syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
When calculating syt offset, some states are stored in AMDTP stream structure. This is inconvenient when reuse the calculation from non-stream structure.
This commit applies refactoring to helper function for the calculation so that the function doesn't touch AMDTP stream structure.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 1605ea4301ce..9041510cb6aa 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -383,14 +383,14 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, return data_blocks; }
-static unsigned int calculate_syt_offset(struct amdtp_stream *s) +static unsigned int calculate_syt_offset(unsigned int *last_syt_offset, + unsigned int *syt_offset_state, enum cip_sfc sfc) { - unsigned int syt_offset, phase, index; + unsigned int syt_offset;
- if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) { - if (!cip_sfc_is_base_44100(s->sfc)) - syt_offset = s->ctx_data.rx.last_syt_offset + - s->ctx_data.rx.syt_offset_state; + if (*last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(sfc)) + syt_offset = *last_syt_offset + *syt_offset_state; else { /* * The time, in ticks, of the n'th SYT_INTERVAL sample is: @@ -402,18 +402,19 @@ static unsigned int calculate_syt_offset(struct amdtp_stream *s) * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... * This code generates _exactly_ the same sequence. */ - phase = s->ctx_data.rx.syt_offset_state; - index = phase % 13; - syt_offset = s->ctx_data.rx.last_syt_offset; + unsigned int phase = *syt_offset_state; + unsigned int index = phase % 13; + + syt_offset = *last_syt_offset; syt_offset += 1386 + ((index && !(index & 3)) || phase == 146); if (++phase >= 147) phase = 0; - s->ctx_data.rx.syt_offset_state = phase; + *syt_offset_state = phase; } } else - syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE; - s->ctx_data.rx.last_syt_offset = syt_offset; + syt_offset = *last_syt_offset - TICKS_PER_CYCLE; + *last_syt_offset = syt_offset;
if (syt_offset >= TICKS_PER_CYCLE) syt_offset = CIP_SYT_NO_INFO; @@ -759,7 +760,9 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, unsigned int syt_offset;
desc->cycle = compute_it_cycle(*ctx_header, s->queue_size); - syt_offset = calculate_syt_offset(s); + syt_offset = calculate_syt_offset( + &s->ctx_data.rx.last_syt_offset, + &s->ctx_data.rx.syt_offset_state, s->sfc); if (syt_offset != CIP_SYT_NO_INFO) { desc->syt = compute_syt(syt_offset, desc->cycle, s->ctx_data.rx.transfer_delay);
When calculating the number of data blocks per packet, some states are stored in AMDTP stream structure. This is inconvenient when reuse the calculation from non-stream structure.
This commit applies refactoring to helper function for the calculation so that the function doesn't touch AMDTP stream structure.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 9041510cb6aa..efd1f2a40cf1 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -339,25 +339,26 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_stream *s, - unsigned int syt) +static unsigned int calculate_data_blocks(unsigned int *data_block_state, + bool is_blocking, bool is_no_info, + unsigned int syt_interval, enum cip_sfc sfc) { - unsigned int phase, data_blocks; + unsigned int data_blocks;
/* Blocking mode. */ - if (s->flags & CIP_BLOCKING) { + if (is_blocking) { /* This module generate empty packet for 'no data'. */ - if (syt == CIP_SYT_NO_INFO) + if (is_no_info) data_blocks = 0; else - data_blocks = s->syt_interval; + data_blocks = syt_interval; /* Non-blocking mode. */ } else { - if (!cip_sfc_is_base_44100(s->sfc)) { + if (!cip_sfc_is_base_44100(sfc)) { // Sample_rate / 8000 is an integer, and precomputed. - data_blocks = s->ctx_data.rx.data_block_state; + data_blocks = *data_block_state; } else { - phase = s->ctx_data.rx.data_block_state; + unsigned int phase = *data_block_state;
/* * This calculates the number of data blocks per packet so that @@ -367,16 +368,16 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, * as possible in the sequence (to prevent underruns of the * device's buffer). */ - if (s->sfc == CIP_SFC_44100) + if (sfc == CIP_SFC_44100) /* 6 6 5 6 5 6 5 ... */ data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40)); else /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ - data_blocks = 11 * (s->sfc >> 1) + (phase == 0); - if (++phase >= (80 >> (s->sfc >> 1))) + data_blocks = 11 * (sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (sfc >> 1))) phase = 0; - s->ctx_data.rx.data_block_state = phase; + *data_block_state = phase; } }
@@ -769,7 +770,11 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, } else { desc->syt = syt_offset; } - desc->data_blocks = calculate_data_blocks(s, desc->syt); + desc->data_blocks = + calculate_data_blocks(&s->ctx_data.rx.data_block_state, + !!(s->flags & CIP_BLOCKING), + desc->syt == CIP_SYT_NO_INFO, + s->syt_interval, s->sfc);
if (s->flags & CIP_DBC_IS_END_EVENT) dbc = (dbc + desc->data_blocks) & 0xff;
For future extension, storage is required to store packet sequence in incoming AMDTP stream to recover media clock for outgoing AMDTP stream.
This commit adds the storage to AMDTP domain for this purpose. The packet sequence is represented by 'struct seq_desc' which has two members; syt_offset and the number of data blocks. The size of storage is decided according to the size of packet queue.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 15 ++++++++++++++- sound/firewire/amdtp-stream.h | 9 +++++++++ 2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index efd1f2a40cf1..f1c8611cfc70 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -1269,6 +1269,8 @@ int amdtp_domain_init(struct amdtp_domain *d)
d->events_per_period = 0;
+ d->seq_descs = NULL; + return 0; } EXPORT_SYMBOL_GPL(amdtp_domain_init); @@ -1370,12 +1372,18 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, amdtp_rate_table[d->irq_target->sfc]);
+ d->seq_descs = kcalloc(queue_size, sizeof(*d->seq_descs), GFP_KERNEL); + if (!d->seq_descs) + return -ENOMEM; + d->seq_size = queue_size; + d->seq_tail = 0; + if (ir_delay_cycle > 0) { struct fw_card *fw_card = fw_parent_device(s->unit)->card;
err = get_current_cycle_time(fw_card, &cycle); if (err < 0) - return err; + goto error;
// No need to care overflow in cycle field because of enough // width. @@ -1431,6 +1439,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) error: list_for_each_entry(s, &d->streams, list) amdtp_stream_stop(s); + kfree(d->seq_descs); + d->seq_descs = NULL; return err; } EXPORT_SYMBOL_GPL(amdtp_domain_start); @@ -1455,5 +1465,8 @@ void amdtp_domain_stop(struct amdtp_domain *d)
d->events_per_period = 0; d->irq_target = NULL; + + kfree(d->seq_descs); + d->seq_descs = NULL; } EXPORT_SYMBOL_GPL(amdtp_domain_stop); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 477fbfe713e5..84a01efa5a85 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -276,6 +276,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, msecs_to_jiffies(timeout)) > 0; }
+struct seq_desc { + unsigned int syt_offset; + unsigned int data_blocks; +}; + struct amdtp_domain { struct list_head streams;
@@ -283,6 +288,10 @@ struct amdtp_domain { unsigned int events_per_buffer;
struct amdtp_stream *irq_target; + + struct seq_desc *seq_descs; + unsigned int seq_size; + unsigned int seq_tail; };
int amdtp_domain_init(struct amdtp_domain *d);
In current implementation, sequence of syt offset and the number of data blocks is generated when packets for outgoing stream are going to be queued.
This commit generates and pools the sequence independently of the processing of outgoing packets for future extension.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 68 +++++++++++++++++++++++++++++++++++ sound/firewire/amdtp-stream.h | 6 ++++ 2 files changed, 74 insertions(+)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index f1c8611cfc70..a2af598e9b9a 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -905,14 +905,63 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, } }
+static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets) +{ + struct amdtp_stream *irq_target = d->irq_target; + unsigned int seq_tail = d->seq_tail; + unsigned int seq_size = d->seq_size; + unsigned int min_avail; + struct amdtp_stream *s; + + min_avail = d->seq_size; + list_for_each_entry(s, &d->streams, list) { + unsigned int seq_index; + unsigned int avail; + + if (s->direction == AMDTP_IN_STREAM) + continue; + + seq_index = s->ctx_data.rx.seq_index; + avail = d->seq_tail; + if (seq_index > avail) + avail += d->seq_size; + avail -= seq_index; + + if (avail < min_avail) + min_avail = avail; + } + + while (min_avail < packets) { + struct seq_desc *desc = d->seq_descs + seq_tail; + + desc->syt_offset = calculate_syt_offset(&d->last_syt_offset, + &d->syt_offset_state, irq_target->sfc); + desc->data_blocks = calculate_data_blocks(&d->data_block_state, + !!(irq_target->flags & CIP_BLOCKING), + desc->syt_offset == CIP_SYT_NO_INFO, + irq_target->syt_interval, irq_target->sfc); + + ++seq_tail; + seq_tail %= seq_size; + + ++min_avail; + } + + d->seq_tail = seq_tail; +} + static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { struct amdtp_stream *irq_target = private_data; struct amdtp_domain *d = irq_target->domain; + unsigned int packets = header_length / sizeof(__be32); struct amdtp_stream *s;
+ // Record enough entries with extra 3 cycles at least. + pool_ideal_seq_descs(d, packets + 3); + out_stream_callback(context, tstamp, header_length, header, irq_target); if (amdtp_streaming_error(irq_target)) goto error; @@ -1344,6 +1393,18 @@ static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) */ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } *entry, initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; unsigned int events_per_buffer = d->events_per_buffer; unsigned int events_per_period = d->events_per_period; unsigned int idle_irq_interval; @@ -1378,6 +1439,11 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) d->seq_size = queue_size; d->seq_tail = 0;
+ entry = &initial_state[s->sfc]; + d->data_block_state = entry->data_block; + d->syt_offset_state = entry->syt_offset; + d->last_syt_offset = TICKS_PER_CYCLE; + if (ir_delay_cycle > 0) { struct fw_card *fw_card = fw_parent_device(s->unit)->card;
@@ -1414,6 +1480,7 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) } else { // IT context starts immediately. cycle_match = -1; + s->ctx_data.rx.seq_index = 0; }
if (s != d->irq_target) { @@ -1427,6 +1494,7 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) s = d->irq_target; s->ctx_data.rx.events_per_period = events_per_period; s->ctx_data.rx.event_count = 0; + s->ctx_data.rx.seq_index = 0;
idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, amdtp_rate_table[d->irq_target->sfc]); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 84a01efa5a85..11cff4cafd90 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -138,6 +138,8 @@ struct amdtp_stream { struct { // To calculate CIP data blocks and tstamp. unsigned int transfer_delay; + unsigned int seq_index; + unsigned int data_block_state; unsigned int last_syt_offset; unsigned int syt_offset_state; @@ -292,6 +294,10 @@ struct amdtp_domain { struct seq_desc *seq_descs; unsigned int seq_size; unsigned int seq_tail; + + unsigned int data_block_state; + unsigned int syt_offset_state; + unsigned int last_syt_offset; };
int amdtp_domain_init(struct amdtp_domain *d);
In previous commit, the sequence of syt offset and the number of data blocks per packet is calculated for pool in AMDTP domain structure in advance of processing outgoing packets.
This commit uses the sequence for outgoing packet processing to obsolete per-stream processing of the sequence.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 56 +++++++++++++---------------------- sound/firewire/amdtp-stream.h | 4 --- 2 files changed, 20 insertions(+), 40 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index a2af598e9b9a..f8586f75441d 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -747,34 +747,30 @@ static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, return syt & CIP_SYT_MASK; }
-static void generate_ideal_pkt_descs(struct amdtp_stream *s, - struct pkt_desc *descs, - const __be32 *ctx_header, - unsigned int packets) +static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs, + const __be32 *ctx_header, unsigned int packets, + const struct seq_desc *seq_descs, + unsigned int seq_size) { unsigned int dbc = s->data_block_counter; + unsigned int seq_index = s->ctx_data.rx.seq_index; int i;
for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; unsigned int index = (s->packet_index + i) % s->queue_size; - unsigned int syt_offset; + const struct seq_desc *seq = seq_descs + seq_index; + unsigned int syt;
desc->cycle = compute_it_cycle(*ctx_header, s->queue_size); - syt_offset = calculate_syt_offset( - &s->ctx_data.rx.last_syt_offset, - &s->ctx_data.rx.syt_offset_state, s->sfc); - if (syt_offset != CIP_SYT_NO_INFO) { - desc->syt = compute_syt(syt_offset, desc->cycle, - s->ctx_data.rx.transfer_delay); - } else { - desc->syt = syt_offset; + + syt = seq->syt_offset; + if (syt != CIP_SYT_NO_INFO) { + syt = compute_syt(syt, desc->cycle, + s->ctx_data.rx.transfer_delay); } - desc->data_blocks = - calculate_data_blocks(&s->ctx_data.rx.data_block_state, - !!(s->flags & CIP_BLOCKING), - desc->syt == CIP_SYT_NO_INFO, - s->syt_interval, s->sfc); + desc->syt = syt; + desc->data_blocks = seq->data_blocks;
if (s->flags & CIP_DBC_IS_END_EVENT) dbc = (dbc + desc->data_blocks) & 0xff; @@ -786,10 +782,13 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s,
desc->ctx_payload = s->buffer.packets[index].buffer;
+ seq_index = (seq_index + 1) % seq_size; + ++ctx_header; }
s->data_block_counter = dbc; + s->ctx_data.rx.seq_index = seq_index; }
static inline void cancel_stream(struct amdtp_stream *s) @@ -818,6 +817,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, void *private_data) { struct amdtp_stream *s = private_data; + const struct amdtp_domain *d = s->domain; const __be32 *ctx_header = header; unsigned int events_per_period = s->ctx_data.rx.events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; @@ -830,7 +830,8 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, // Calculate the number of packets in buffer and check XRUN. packets = header_length / sizeof(*ctx_header);
- generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets); + generate_pkt_descs(s, s->pkt_descs, ctx_header, packets, d->seq_descs, + d->seq_size);
process_ctx_payloads(s, s->pkt_descs, packets);
@@ -1037,18 +1038,6 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, int start_cycle, unsigned int queue_size, unsigned int idle_irq_interval) { - static const struct { - unsigned int data_block; - unsigned int syt_offset; - } *entry, initial_state[] = { - [CIP_SFC_32000] = { 4, 3072 }, - [CIP_SFC_48000] = { 6, 1024 }, - [CIP_SFC_96000] = { 12, 1024 }, - [CIP_SFC_192000] = { 24, 1024 }, - [CIP_SFC_44100] = { 0, 67 }, - [CIP_SFC_88200] = { 0, 67 }, - [CIP_SFC_176400] = { 0, 67 }, - }; bool is_irq_target = (s == s->domain->irq_target); unsigned int ctx_header_size; unsigned int max_ctx_payload_size; @@ -1072,12 +1061,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
s->data_block_counter = UINT_MAX; } else { - entry = &initial_state[s->sfc]; - s->data_block_counter = 0; - s->ctx_data.rx.data_block_state = entry->data_block; - s->ctx_data.rx.syt_offset_state = entry->syt_offset; - s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE; }
/* initialize packet buffer */ diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 11cff4cafd90..703b710aaf7f 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -140,10 +140,6 @@ struct amdtp_stream { unsigned int transfer_delay; unsigned int seq_index;
- unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; - // To generate CIP header. unsigned int fdf; int syt_override;
On Fri, 08 May 2020 06:36:25 +0200, Takashi Sakamoto wrote:
Hi,
In current implementation, the packets for outgoing AMDTP streams are processed by per-stream calculation of syt offset and the number of data blocks per packet.
This patchset is a preparation for future extension that the packets for outgoing AMDTP streams are processed according to the result of 'sampling clock recovery' in IEC 61883-6:2005 from selected incoming AMDTP stream. The preparation is to process packets for outgoing AMDTP streams by pool in AMDTP domain structure for the sequence of syt offset and the number of data blocks. The way to generate sequence is still the same as the current implementation, which generates by ideal sampling transmission frequency against IEEE 1394 bus clock.
Takashi Sakamoto (10): ALSA: firewire-lib: fix invalid assignment to union data for directional parameter ALSA: firewire-lib: use macro for maximum value of second in 1394 OHCI isoc descriptor ALSA: firewire-lib: add reference to domain structure from stream structure ALSA: firewire-lib: code refactoring for parameters of packet queue and IRQ timing ALSA: firewire-lib: code refactoring for syt computation ALSA: firewire-lib: code refactoring for syt offset calculation ALSA: firewire-lib: code refactoring for data block calculation ALSA: firewire-lib: add cache for packet sequence to AMDTP domain structure ALSA: firewire-lib: pool ideal sequence of syt offset and data block ALSA: firewire-lib: use sequence of syt offset and data block on pool in AMDTP domain
Applied all ten patches to for-next branch now.
thanks,
Takashi
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto