[PATCH 0/8] ALSA: firewire-lib: start processing content of packet at the same cycle in several contexts for each direction
Hi,
Within devices supported by drivers in ALSA firewire stack, only DICE ASICs support several pair of isochronous packet streams. As long as I experienced, the ASIC requires drivers to transfer packets at the same isochronous cycle. Unless, it generates no sound.
Additionally, in future integration I have a plan to take drivers performing sequence replay for media clock recovery. It's convenient to manage the cycle to start processing content of packet in several IR contexts.
This patchset is to start processing content of packet at the same cycle in several contexts for each direction, including code refactoring.
Takashi Sakamoto (8): ALSA: firewire-lib: code refactoring for size of CIP header ALSA: firewire-lib: code refactoring for calculation of context payload ALSA: firewire-lib: code refactoring for selection of IT context header length ALSA: firewire-lib: start processing content of packet at the same cycle in several IR contexts ALSA: firewire-lib: skip initial packets instead of scheduling IR context ALSA: firewire-lib: code refactoring to start several IT/IR contexts ALSA: firewire-lib: start processing content of packet at the same cycle in several IT contexts ALSA: firewire-lib: change waking up timing to process packets
sound/firewire/amdtp-stream.c | 492 ++++++++++++++------ sound/firewire/amdtp-stream.h | 53 ++- sound/firewire/bebob/bebob_stream.c | 29 +- sound/firewire/dice/dice-stream.c | 15 +- sound/firewire/digi00x/digi00x-stream.c | 7 +- sound/firewire/fireface/ff-stream.c | 7 +- sound/firewire/fireworks/fireworks_stream.c | 8 +- sound/firewire/motu/amdtp-motu.c | 9 +- sound/firewire/motu/motu-stream.c | 7 +- sound/firewire/oxfw/oxfw-stream.c | 14 +- sound/firewire/tascam/tascam-stream.c | 7 +- 11 files changed, 401 insertions(+), 247 deletions(-)
Some macros are added to refactor codes related to CIP header.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index af5c3629f1ac..f178cb5f2df3 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -33,7 +33,8 @@ #define TAG_NO_CIP_HEADER 0 #define TAG_CIP 1
-/* common isochronous packet header parameters */ +// Common Isochronous Packet (CIP) header parameters. Use two quadlets CIP header when supported. +#define CIP_HEADER_QUADLETS 2 #define CIP_EOH_SHIFT 31 #define CIP_EOH (1u << CIP_EOH_SHIFT) #define CIP_EOH_MASK 0x80000000 @@ -51,17 +52,21 @@ #define CIP_SYT_MASK 0x0000ffff #define CIP_SYT_NO_INFO 0xffff
+#define CIP_HEADER_SIZE (sizeof(__be32) * CIP_HEADER_QUADLETS) + /* Audio and Music transfer protocol specific parameters */ #define CIP_FMT_AM 0x10 #define AMDTP_FDF_NO_DATA 0xff
-// For iso header, tstamp and 2 CIP header. -#define IR_CTX_HEADER_SIZE_CIP 16 // For iso header and tstamp. -#define IR_CTX_HEADER_SIZE_NO_CIP 8 +#define IR_CTX_HEADER_DEFAULT_QUADLETS 2 +// Add nothing. +#define IR_CTX_HEADER_SIZE_NO_CIP (sizeof(__be32) * IR_CTX_HEADER_DEFAULT_QUADLETS) +// Add two quadlets CIP header. +#define IR_CTX_HEADER_SIZE_CIP (IR_CTX_HEADER_SIZE_NO_CIP + CIP_HEADER_SIZE) #define HEADER_TSTAMP_MASK 0x0000ffff
-#define IT_PKT_HEADER_SIZE_CIP 8 // For 2 CIP header. +#define IT_PKT_HEADER_SIZE_CIP CIP_HEADER_SIZE #define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing.
// The initial firmware of OXFW970 can postpone transmission of packet during finishing @@ -323,7 +328,7 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) if (s->flags & CIP_JUMBO_PAYLOAD) multiplier = IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES; if (!(s->flags & CIP_NO_HEADER)) - cip_header_size = sizeof(__be32) * 2; + cip_header_size = CIP_HEADER_SIZE;
return cip_header_size + s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier; @@ -642,7 +647,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT;
if (!(s->flags & CIP_NO_HEADER)) - cip_header_size = 8; + cip_header_size = CIP_HEADER_SIZE; else cip_header_size = 0;
@@ -655,7 +660,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
if (cip_header_size > 0) { if (payload_length >= cip_header_size) { - cip_header = ctx_header + 2; + cip_header = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS; err = check_cip_header(s, cip_header, payload_length - cip_header_size, data_blocks, data_block_counter, syt); if (err < 0) @@ -907,7 +912,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, unsigned int syt; struct { struct fw_iso_packet params; - __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; + __be32 header[CIP_HEADER_QUADLETS]; } template = { {0}, {0} }; bool sched_irq = false;
@@ -1140,7 +1145,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, dir = DMA_FROM_DEVICE; type = FW_ISO_CONTEXT_RECEIVE; if (!(s->flags & CIP_NO_HEADER)) { - max_ctx_payload_size -= 8; + max_ctx_payload_size -= CIP_HEADER_SIZE; ctx_header_size = IR_CTX_HEADER_SIZE_CIP; } else { ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
It's convenient to calculate the size of context payload apart from the size of isochronous packet payload.
This commit adds a helper function for it.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index f178cb5f2df3..36135296c144 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -313,6 +313,19 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, } EXPORT_SYMBOL(amdtp_stream_set_parameters);
+// The CIP header is processed in context header apart from context payload. +static int amdtp_stream_get_max_ctx_payload_size(struct amdtp_stream *s) +{ + unsigned int multiplier; + + if (s->flags & CIP_JUMBO_PAYLOAD) + multiplier = IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES; + else + multiplier = 1; + + return s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier; +} + /** * amdtp_stream_get_max_payload - get the stream's packet size * @s: the AMDTP stream @@ -322,16 +335,14 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters); */ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { - unsigned int multiplier = 1; - unsigned int cip_header_size = 0; + unsigned int cip_header_size;
- if (s->flags & CIP_JUMBO_PAYLOAD) - multiplier = IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES; if (!(s->flags & CIP_NO_HEADER)) cip_header_size = CIP_HEADER_SIZE; + else + cip_header_size = 0;
- return cip_header_size + - s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier; + return cip_header_size + amdtp_stream_get_max_ctx_payload_size(s); } EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@@ -1140,27 +1151,21 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, }
// initialize packet buffer. - max_ctx_payload_size = amdtp_stream_get_max_payload(s); if (s->direction == AMDTP_IN_STREAM) { dir = DMA_FROM_DEVICE; type = FW_ISO_CONTEXT_RECEIVE; - if (!(s->flags & CIP_NO_HEADER)) { - max_ctx_payload_size -= CIP_HEADER_SIZE; + if (!(s->flags & CIP_NO_HEADER)) ctx_header_size = IR_CTX_HEADER_SIZE_CIP; - } else { + else ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP; - } } else { dir = DMA_TO_DEVICE; type = FW_ISO_CONTEXT_TRANSMIT; ctx_header_size = 0; // No effect for IT context. - - if (!(s->flags & CIP_NO_HEADER)) - max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; } + max_ctx_payload_size = amdtp_stream_get_max_ctx_payload_size(s);
- err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, - max_ctx_payload_size, dir); + err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir); if (err < 0) goto err_unlock; s->queue_size = queue_size;
This commit refactors regarding to the size of CIP header.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 36135296c144..87644cb0d8ab 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -526,7 +526,7 @@ static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], }
static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, - struct fw_iso_packet *params, + struct fw_iso_packet *params, unsigned int header_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int syt, unsigned int index) @@ -537,16 +537,15 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets; params->payload_length = payload_length;
- if (!(s->flags & CIP_NO_HEADER)) { + if (header_length > 0) { cip_header = (__be32 *)params->header; generate_cip_header(s, cip_header, data_block_counter, syt); - params->header_length = 2 * sizeof(__be32); - payload_length += params->header_length; + params->header_length = header_length; } else { cip_header = NULL; }
- trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks, + trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks, data_block_counter, s->packet_index, index); }
@@ -904,6 +903,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, const __be32 *ctx_header = header; unsigned int events_per_period = d->events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; + unsigned int pkt_header_length; unsigned int packets; int i;
@@ -918,6 +918,11 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
process_ctx_payloads(s, s->pkt_descs, packets);
+ if (!(s->flags & CIP_NO_HEADER)) + pkt_header_length = IT_PKT_HEADER_SIZE_CIP; + else + pkt_header_length = 0; + for (i = 0; i < packets; ++i) { const struct pkt_desc *desc = s->pkt_descs + i; unsigned int syt; @@ -932,7 +937,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, else syt = s->ctx_data.rx.syt_override;
- build_it_pkt_header(s, desc->cycle, &template.params, + build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length, desc->data_blocks, desc->data_block_counter, syt, i);
DICE ASICs support several pairs of isochronous packet streaming. It's convenient for drivers to process content of the packet in the same cycle timing.
This commit adds structure member to manage the cycle to start processing packet in several IR contexts. The cycle is decided in the first callback of the IR contexts. The content of packet is dropped till the cycle.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 115 ++++++++++++++++++++++++++++++++-- sound/firewire/amdtp-stream.h | 4 ++ 2 files changed, 113 insertions(+), 6 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 87644cb0d8ab..35925c9666fc 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -958,9 +958,8 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, s->ctx_data.rx.event_count = event_count; }
-static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, - size_t header_length, void *header, - void *private_data) +static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, + void *header, void *private_data) { struct amdtp_stream *s = private_data; __be32 *ctx_header = header; @@ -996,6 +995,82 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, } }
+static void drop_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + const __be32 *ctx_header = header; + unsigned int packets; + unsigned int cycle; + int i; + + if (s->packet_index < 0) + return; + + packets = header_length / s->ctx_data.tx.ctx_header_size; + + ctx_header += (packets - 1) * s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); + cycle = compute_ohci_cycle_count(ctx_header[1]); + s->next_cycle = increment_ohci_cycle_count(cycle, 1); + + for (i = 0; i < packets; ++i) { + struct fw_iso_packet params = {0}; + + if (queue_in_packet(s, ¶ms) < 0) { + cancel_stream(s); + return; + } + } +} + +static void process_tx_packets_intermediately(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; + __be32 *ctx_header; + unsigned int packets; + unsigned int offset; + + if (s->packet_index < 0) + return; + + packets = header_length / s->ctx_data.tx.ctx_header_size; + + offset = 0; + ctx_header = header; + while (offset < packets) { + unsigned int cycle = compute_ohci_cycle_count(ctx_header[1]); + + if (compare_ohci_cycle_count(cycle, d->processing_cycle.tx_start) >= 0) + break; + + ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32); + ++offset; + } + + ctx_header = header; + + if (offset > 0) { + size_t length = s->ctx_data.tx.ctx_header_size * offset; + + drop_tx_packets(context, tstamp, length, ctx_header, s); + if (amdtp_streaming_error(s)) + return; + + ctx_header += length / sizeof(*ctx_header); + header_length -= length; + } + + if (offset < packets) { + process_tx_packets(context, tstamp, header_length, ctx_header, s); + if (amdtp_streaming_error(s)) + return; + + context->callback.sc = process_tx_packets; + } +} + static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets) { struct amdtp_stream *irq_target = d->irq_target; @@ -1082,6 +1157,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, void *header, void *private_data) { struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; const __be32 *ctx_header = header; u32 cycle;
@@ -1094,13 +1170,12 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM) { cycle = compute_ohci_cycle_count(ctx_header[1]); - s->next_cycle = cycle;
- context->callback.sc = in_stream_callback; + context->callback.sc = drop_tx_packets; } else { cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size);
- if (s == s->domain->irq_target) + if (s == d->irq_target) context->callback.sc = irq_target_callback; else context->callback.sc = out_stream_callback; @@ -1109,6 +1184,34 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, s->start_cycle = cycle;
context->callback.sc(context, tstamp, header_length, header, s); + + // Decide the cycle count to begin processing content of packet in IR contexts. + if (s->direction == AMDTP_IN_STREAM) { + unsigned int stream_count = 0; + unsigned int callbacked_count = 0; + + list_for_each_entry(s, &d->streams, list) { + if (s->direction == AMDTP_IN_STREAM) { + ++stream_count; + if (s->callbacked) + ++callbacked_count; + } + } + + if (stream_count == callbacked_count) { + list_for_each_entry(s, &d->streams, list) { + if (s->direction != AMDTP_IN_STREAM) + continue; + + if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0) + cycle = s->next_cycle; + + s->context->callback.sc = process_tx_packets_intermediately; + } + + d->processing_cycle.tx_start = cycle; + } + } }
/** diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 58769ca184a2..6fad113188fe 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -288,6 +288,10 @@ struct amdtp_domain {
struct amdtp_stream *irq_target;
+ struct { + unsigned int tx_start; + } processing_cycle; + struct { struct seq_desc *descs; unsigned int size;
Current implementation of ALSA IEC 61883-1/6 packet streaming engine allows drivers to decide isochronous cycle to start IR context. This option is mainly used to avoid processing the sequence of packet with some quirks; e.g. discontinuity of counter. However, it's inconvenient to fail to continue packet processing when the target device doesn't start transmission of packet till the decided cycle.
This commit changes the behaviour. As an alternative to the start cycle for IR context, the cycle count to drop content of packet in the beginning of IR context.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 80 +++++------------------------ sound/firewire/amdtp-stream.h | 3 +- sound/firewire/bebob/bebob_stream.c | 21 +++----- 3 files changed, 23 insertions(+), 81 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 35925c9666fc..48ed9612407f 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -1199,12 +1199,16 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, }
if (stream_count == callbacked_count) { + unsigned int next_cycle; + list_for_each_entry(s, &d->streams, list) { if (s->direction != AMDTP_IN_STREAM) continue;
- if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0) - cycle = s->next_cycle; + next_cycle = increment_ohci_cycle_count(s->next_cycle, + d->processing_cycle.tx_init_skip); + if (compare_ohci_cycle_count(next_cycle, cycle) > 0) + cycle = next_cycle;
s->context->callback.sc = process_tx_packets_intermediately; } @@ -1533,36 +1537,13 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, } EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
-static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) -{ - int generation; - int rcode; - __be32 reg; - u32 data; - - // This is a request to local 1394 OHCI controller and expected to - // complete without any event waiting. - generation = fw_card->generation; - smp_rmb(); // node_id vs. generation. - rcode = fw_run_transaction(fw_card, TCODE_READ_QUADLET_REQUEST, - fw_card->node_id, generation, SCODE_100, - CSR_REGISTER_BASE + CSR_CYCLE_TIME, - ®, sizeof(reg)); - if (rcode != RCODE_COMPLETE) - return -EIO; - - data = be32_to_cpu(reg); - *cur_cycle = data >> 12; - - return 0; -} - /** * amdtp_domain_start - start sending packets for isoc context in the domain. * @d: the AMDTP domain. - * @ir_delay_cycle: the cycle delay to start all IR contexts. + * @tx_init_skip_cycles: the number of cycles to skip processing packets at initial stage of IR + * contexts. */ -int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) +int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles) { static const struct { unsigned int data_block; @@ -1581,7 +1562,6 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) unsigned int idle_irq_interval; unsigned int queue_size; struct amdtp_stream *s; - int cycle; int err;
// Select an IT context as IRQ target. @@ -1593,6 +1573,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) return -ENXIO; d->irq_target = s;
+ d->processing_cycle.tx_init_skip = tx_init_skip_cycles; + // 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. @@ -1615,48 +1597,12 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) 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; - - err = get_current_cycle_time(fw_card, &cycle); - if (err < 0) - goto error; - - // No need to care overflow in cycle field because of enough - // width. - cycle += ir_delay_cycle; - - // Round up to sec field. - if ((cycle & 0x00001fff) >= CYCLES_PER_SECOND) { - unsigned int sec; - - // The sec field can overflow. - sec = (cycle & 0xffffe000) >> 13; - cycle = (++sec << 13) | - ((cycle & 0x00001fff) / CYCLES_PER_SECOND); - } - - // In OHCI 1394 specification, lower 2 bits are available for - // sec field. - cycle &= 0x00007fff; - } else { - cycle = -1; - } - list_for_each_entry(s, &d->streams, list) { - int cycle_match; - - if (s->direction == AMDTP_IN_STREAM) { - cycle_match = cycle; - } else { - // IT context starts immediately. - cycle_match = -1; + if (s->direction == AMDTP_OUT_STREAM) s->ctx_data.rx.seq_index = 0; - }
if (s != d->irq_target) { - err = amdtp_stream_start(s, s->channel, s->speed, - cycle_match, queue_size, 0); + err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, 0); if (err < 0) goto error; } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 6fad113188fe..ebd040560791 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -289,6 +289,7 @@ struct amdtp_domain { struct amdtp_stream *irq_target;
struct { + unsigned int tx_init_skip; unsigned int tx_start; } processing_cycle;
@@ -309,7 +310,7 @@ void amdtp_domain_destroy(struct amdtp_domain *d); int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, int channel, int speed);
-int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle); +int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles); void amdtp_domain_stop(struct amdtp_domain *d);
static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d, diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index b612ee3e33b6..8053d02b68f0 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -626,7 +626,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) enum snd_bebob_clock_type src; struct amdtp_stream *master, *slave; unsigned int curr_rate; - unsigned int ir_delay_cycle; + unsigned int tx_init_skip_cycles;
if (bebob->maudio_special_quirk) { err = bebob->spec->rate->get(bebob, &curr_rate); @@ -654,20 +654,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) if (err < 0) goto error;
- // The device postpones start of transmission mostly for 1 sec - // after receives packets firstly. For safe, IR context starts - // 0.4 sec (=3200 cycles) later to version 1 or 2 firmware, - // 2.0 sec (=16000 cycles) for version 3 firmware. This is - // within 2.5 sec (=CALLBACK_TIMEOUT). - // Furthermore, some devices transfer isoc packets with - // discontinuous counter in the beginning of packet streaming. - // The delay has an effect to avoid detection of this - // discontinuity. + // Some devices transfer isoc packets with discontinuous counter in the beginning + // of packet streaming. if (bebob->version < 2) - ir_delay_cycle = 3200; + tx_init_skip_cycles = 3200; else - ir_delay_cycle = 16000; - err = amdtp_domain_start(&bebob->domain, ir_delay_cycle); + tx_init_skip_cycles = 16000; + err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles); if (err < 0) goto error;
@@ -684,6 +677,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) } }
+ // Some devices postpone start of transmission mostly for 1 sec after receives + // packets firstly. if (!amdtp_stream_wait_callback(&bebob->rx_stream, CALLBACK_TIMEOUT) || !amdtp_stream_wait_callback(&bebob->tx_stream,
It's several hundred cycles from starting isochronous contexts and the actual cycle to start processing content of packet. It's useless to start the context for IRQ target apart from the other contexts.
This commit refactors helper function to start AMDTP domain in the point.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 37 +++++++++++++---------------------- 1 file changed, 14 insertions(+), 23 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 48ed9612407f..b244fd863ca9 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -1223,8 +1223,6 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @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. * @@ -1233,8 +1231,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, * device can be started. */ 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) + unsigned int queue_size, unsigned int idle_irq_interval) { bool is_irq_target = (s == s->domain->irq_target); unsigned int ctx_header_size; @@ -1298,6 +1295,9 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, if (s->direction == AMDTP_IN_STREAM) { s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size; s->ctx_data.tx.ctx_header_size = ctx_header_size; + } else { + s->ctx_data.rx.seq_index = 0; + s->ctx_data.rx.event_count = 0; }
if (s->flags & CIP_NO_HEADER) @@ -1341,7 +1341,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false; - err = fw_iso_context_start(s->context, start_cycle, 0, tag); + err = fw_iso_context_start(s->context, -1, 0, tag); if (err < 0) goto err_pkt_descs;
@@ -1559,7 +1559,6 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles) }; 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 err; @@ -1598,26 +1597,18 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles) d->last_syt_offset = TICKS_PER_CYCLE;
list_for_each_entry(s, &d->streams, list) { - if (s->direction == AMDTP_OUT_STREAM) - s->ctx_data.rx.seq_index = 0; + unsigned int idle_irq_interval = 0;
- if (s != d->irq_target) { - err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, 0); - if (err < 0) - goto error; + if (s->direction == AMDTP_OUT_STREAM && s == d->irq_target) { + idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, + amdtp_rate_table[d->irq_target->sfc]); } - }
- s = d->irq_target; - 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]); - err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, - idle_irq_interval); - if (err < 0) - goto error; + // Starts immediately but actually DMA context starts several hundred cycles later. + err = amdtp_stream_start(s, s->channel, s->speed, queue_size, idle_irq_interval); + if (err < 0) + goto error; + }
return 0; error:
DICE ASICs support several pairs of isochronous packet streaming and expect software to queue packets with the same timing information into the same isochronous cycle.
This commit adds structure member to manage the cycle to start processing packet in several IT contexts. The cycle is decided when batch of isochronous cycle is skipped in callback to isochronous context for IRQ target.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 177 ++++++++++++++++++++++++++----- sound/firewire/amdtp-stream.h | 2 +- sound/firewire/motu/amdtp-motu.c | 9 +- 3 files changed, 156 insertions(+), 32 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index b244fd863ca9..e9bdb609f2eb 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -894,14 +894,13 @@ static void process_ctx_payloads(struct amdtp_stream *s, update_pcm_pointers(s, pcm, pcm_frames); }
-static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, - size_t header_length, void *header, - void *private_data) +static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, + void *header, 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 = d->events_per_period; + const unsigned int events_per_period = d->events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; unsigned int pkt_header_length; unsigned int packets; @@ -958,6 +957,89 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, s->ctx_data.rx.event_count = event_count; }
+static void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; + const __be32 *ctx_header = header; + unsigned int packets; + unsigned int cycle; + int i; + + if (s->packet_index < 0) + return; + + packets = header_length / sizeof(*ctx_header); + + cycle = compute_ohci_it_cycle(ctx_header[packets - 1], s->queue_size); + s->next_cycle = increment_ohci_cycle_count(cycle, 1); + + for (i = 0; i < packets; ++i) { + struct fw_iso_packet params = { + .header_length = 0, + .payload_length = 0, + }; + bool sched_irq = (s == d->irq_target && i == packets - 1); + + if (queue_out_packet(s, ¶ms, sched_irq) < 0) { + cancel_stream(s); + return; + } + } +} + +static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, + void *header, void *private_data); + +static void process_rx_packets_intermediately(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; + __be32 *ctx_header = header; + const unsigned int queue_size = s->queue_size; + unsigned int packets; + unsigned int offset; + + if (s->packet_index < 0) + return; + + packets = header_length / sizeof(*ctx_header); + + offset = 0; + while (offset < packets) { + unsigned int cycle = compute_ohci_it_cycle(ctx_header[offset], queue_size); + + if (compare_ohci_cycle_count(cycle, d->processing_cycle.rx_start) >= 0) + break; + + ++offset; + } + + if (offset > 0) { + unsigned int length = sizeof(*ctx_header) * offset; + + skip_rx_packets(context, tstamp, length, ctx_header, private_data); + if (amdtp_streaming_error(s)) + return; + + ctx_header += offset; + header_length -= length; + } + + if (offset < packets) { + process_rx_packets(context, tstamp, header_length, ctx_header, private_data); + if (amdtp_streaming_error(s)) + return; + + if (s == d->irq_target) + s->context->callback.sc = irq_target_callback; + else + s->context->callback.sc = process_rx_packets; + } +} + static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { @@ -1116,34 +1198,22 @@ static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets) 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) +static void process_ctxs_in_domain(struct amdtp_domain *d) { - 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; - list_for_each_entry(s, &d->streams, list) { - if (s != irq_target && amdtp_stream_running(s)) { + if (s != d->irq_target && amdtp_stream_running(s)) fw_iso_context_flush_completions(s->context); - if (amdtp_streaming_error(s)) - goto error; - } + + if (amdtp_streaming_error(s)) + goto error; }
return; error: - if (amdtp_stream_running(irq_target)) - cancel_stream(irq_target); + if (amdtp_stream_running(d->irq_target)) + cancel_stream(d->irq_target);
list_for_each_entry(s, &d->streams, list) { if (amdtp_stream_running(s)) @@ -1151,6 +1221,61 @@ static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, } }
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; + unsigned int packets = header_length / sizeof(__be32); + + pool_ideal_seq_descs(d, packets); + + process_rx_packets(context, tstamp, header_length, header, private_data); + process_ctxs_in_domain(d); +} + +static void irq_target_callback_intermediately(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; + unsigned int packets = header_length / sizeof(__be32); + + pool_ideal_seq_descs(d, packets); + + process_rx_packets_intermediately(context, tstamp, header_length, header, private_data); + process_ctxs_in_domain(d); +} + +static void irq_target_callback_skip(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + struct amdtp_domain *d = s->domain; + unsigned int cycle; + + skip_rx_packets(context, tstamp, header_length, header, private_data); + process_ctxs_in_domain(d); + + // Decide the cycle count to begin processing content of packet in IT contexts. All of IT + // contexts are expected to start and get callback when reaching here. + cycle = s->next_cycle; + list_for_each_entry(s, &d->streams, list) { + if (s->direction != AMDTP_OUT_STREAM) + continue; + + if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0) + cycle = s->next_cycle; + + if (s == d->irq_target) + s->context->callback.sc = irq_target_callback_intermediately; + else + s->context->callback.sc = process_rx_packets_intermediately; + } + + d->processing_cycle.rx_start = cycle; +} + // this is executed one time. static void amdtp_stream_first_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, @@ -1176,13 +1301,11 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size);
if (s == d->irq_target) - context->callback.sc = irq_target_callback; + context->callback.sc = irq_target_callback_skip; else - context->callback.sc = out_stream_callback; + context->callback.sc = skip_rx_packets; }
- s->start_cycle = cycle; - context->callback.sc(context, tstamp, header_length, header, s);
// Decide the cycle count to begin processing content of packet in IR contexts. diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index ebd040560791..7725d9793458 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -170,7 +170,6 @@ struct amdtp_stream { /* To wait for first packet. */ bool callbacked; wait_queue_head_t callback_wait; - u32 start_cycle; unsigned int next_cycle;
/* For backends to process data blocks. */ @@ -291,6 +290,7 @@ struct amdtp_domain { struct { unsigned int tx_init_skip; unsigned int tx_start; + unsigned int rx_start; } processing_cycle;
struct { diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index edb31ac26868..9ccde07d6295 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -377,8 +377,8 @@ static inline void compute_next_elapse_from_start(struct amdtp_motu *p) p->next_seconds -= 128; }
-static void write_sph(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks) +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; @@ -386,7 +386,7 @@ static void write_sph(struct amdtp_stream *s, __be32 *buffer, u32 sph;
for (i = 0; i < data_blocks; i++) { - next_cycles = (s->start_cycle + p->next_cycles) % 8000; + next_cycles = (rx_start_cycle + p->next_cycles) % 8000; sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff; *buffer = cpu_to_be32(sph);
@@ -401,6 +401,7 @@ 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; @@ -423,7 +424,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); + write_sph(s, buf, data_blocks, rx_start_cycle); }
// For tracepoints.
When starting AMDTP domain, tasks in process context yields running CPU till all of isochronous context get callback, with an assumption that it's OK to process content of packet.
However several isochronous cycles are skipped to transfer rx packets, or the content of rx packets are dropped, to manage the timing to start processing the packets.
This commit changes the timing for tasks in process context to wake up when processing content of packet is actually ready.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp-stream.c | 16 +++++--- sound/firewire/amdtp-stream.h | 44 ++++++++++++--------- sound/firewire/bebob/bebob_stream.c | 8 +--- sound/firewire/dice/dice-stream.c | 15 ++----- sound/firewire/digi00x/digi00x-stream.c | 7 +--- sound/firewire/fireface/ff-stream.c | 7 +--- sound/firewire/fireworks/fireworks_stream.c | 8 +--- sound/firewire/motu/motu-stream.c | 7 +--- sound/firewire/oxfw/oxfw-stream.c | 14 +------ sound/firewire/tascam/tascam-stream.c | 7 +--- 10 files changed, 54 insertions(+), 79 deletions(-)
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index e9bdb609f2eb..a6a7a72a2452 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -107,7 +107,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, INIT_WORK(&s->period_work, pcm_period_work); s->packet_index = 0;
- init_waitqueue_head(&s->callback_wait); + init_waitqueue_head(&s->ready_wait); s->callbacked = false;
s->fmt = fmt; @@ -1029,6 +1029,9 @@ static void process_rx_packets_intermediately(struct fw_iso_context *context, u3 }
if (offset < packets) { + s->ready_processing = true; + wake_up(&s->ready_wait); + process_rx_packets(context, tstamp, header_length, ctx_header, private_data); if (amdtp_streaming_error(s)) return; @@ -1145,6 +1148,9 @@ static void process_tx_packets_intermediately(struct fw_iso_context *context, u3 }
if (offset < packets) { + s->ready_processing = true; + wake_up(&s->ready_wait); + process_tx_packets(context, tstamp, header_length, ctx_header, s); if (amdtp_streaming_error(s)) return; @@ -1286,12 +1292,9 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, const __be32 *ctx_header = header; u32 cycle;
- /* - * For in-stream, first packet has come. - * For out-stream, prepared to transmit first packet - */ + // For in-stream, first packet has come. + // For out-stream, prepared to transmit first packet s->callbacked = true; - wake_up(&s->callback_wait);
if (s->direction == AMDTP_IN_STREAM) { cycle = compute_ohci_cycle_count(ctx_header[1]); @@ -1464,6 +1467,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false; + s->ready_processing = false; err = fw_iso_context_start(s->context, -1, 0, tag); if (err < 0) goto err_pkt_descs; diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 7725d9793458..b362a6499265 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -167,9 +167,11 @@ struct amdtp_stream { snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer;
- /* To wait for first packet. */ - bool callbacked; - wait_queue_head_t callback_wait; + // To start processing content of packets at the same cycle in several contexts for + // each direction. + bool callbacked:1; + bool ready_processing:1; + wait_queue_head_t ready_wait; unsigned int next_cycle;
/* For backends to process data blocks. */ @@ -259,21 +261,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) return sfc & 1; }
-/** - * amdtp_stream_wait_callback - sleep till callbacked or timeout - * @s: the AMDTP stream - * @timeout: msec till timeout - * - * If this function return false, the AMDTP stream should be stopped. - */ -static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, - unsigned int timeout) -{ - return wait_event_timeout(s->callback_wait, - s->callbacked, - msecs_to_jiffies(timeout)) > 0; -} - struct seq_desc { unsigned int syt_offset; unsigned int data_blocks; @@ -327,4 +314,25 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, struct amdtp_stream *s); int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s);
+/** + * amdtp_domain_wait_ready - sleep till being ready to process packets or timeout + * @d: the AMDTP domain + * @timeout_ms: msec till timeout + * + * If this function return false, the AMDTP domain should be stopped. + */ +static inline bool amdtp_domain_wait_ready(struct amdtp_domain *d, unsigned int timeout_ms) +{ + struct amdtp_stream *s; + + list_for_each_entry(s, &d->streams, list) { + unsigned int j = msecs_to_jiffies(timeout_ms); + + if (wait_event_interruptible_timeout(s->ready_wait, s->ready_processing, j) <= 0) + return false; + } + + return true; +} + #endif diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 8053d02b68f0..df764171f84b 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -7,8 +7,7 @@
#include "./bebob.h"
-#define CALLBACK_TIMEOUT 2500 -#define FW_ISO_RESOURCE_DELAY 1000 +#define READY_TIMEOUT_MS 2500
/* * NOTE; @@ -679,10 +678,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
// Some devices postpone start of transmission mostly for 1 sec after receives // packets firstly. - if (!amdtp_stream_wait_callback(&bebob->rx_stream, - CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(&bebob->tx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_domain_wait_ready(&bebob->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index c4dfe76500c2..a9a0fe9635dd 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -8,7 +8,7 @@
#include "dice.h"
-#define CALLBACK_TIMEOUT 200 +#define READY_TIMEOUT_MS 200 #define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
struct reg_params { @@ -463,16 +463,9 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) if (err < 0) goto error;
- for (i = 0; i < MAX_STREAMS; i++) { - if ((i < tx_params.count && - !amdtp_stream_wait_callback(&dice->tx_stream[i], - CALLBACK_TIMEOUT)) || - (i < rx_params.count && - !amdtp_stream_wait_callback(&dice->rx_stream[i], - CALLBACK_TIMEOUT))) { - err = -ETIMEDOUT; - goto error; - } + if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) { + err = -ETIMEDOUT; + goto error; } }
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 405d6903bfbc..f11aaff2e248 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -7,7 +7,7 @@
#include "digi00x.h"
-#define CALLBACK_TIMEOUT 500 +#define READY_TIMEOUT_MS 500
const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = { [SND_DG00X_RATE_44100] = 44100, @@ -379,10 +379,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) if (err < 0) goto error;
- if (!amdtp_stream_wait_callback(&dg00x->rx_stream, - CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(&dg00x->tx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; } diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 5452115c0ef9..53a21fb95add 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -7,7 +7,7 @@
#include "ff.h"
-#define CALLBACK_TIMEOUT_MS 200 +#define READY_TIMEOUT_MS 200
int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, enum snd_ff_stream_mode *mode) @@ -203,10 +203,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) goto error;
- if (!amdtp_stream_wait_callback(&ff->rx_stream, - CALLBACK_TIMEOUT_MS) || - !amdtp_stream_wait_callback(&ff->tx_stream, - CALLBACK_TIMEOUT_MS)) { + if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; } diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 2206af0fef42..858cd6085c1f 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -6,7 +6,7 @@ */ #include "./fireworks.h"
-#define CALLBACK_TIMEOUT 100 +#define READY_TIMEOUT_MS 100
static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream) { @@ -276,11 +276,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw) if (err < 0) goto error;
- // Wait first callback. - if (!amdtp_stream_wait_callback(&efw->rx_stream, - CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(&efw->tx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; } diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 2028c5419f6f..925241ae2551 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -7,7 +7,7 @@
#include "motu.h"
-#define CALLBACK_TIMEOUT 200 +#define READY_TIMEOUT_MS 200
#define ISOC_COMM_CONTROL_OFFSET 0x0b00 #define ISOC_COMM_CONTROL_MASK 0xffff0000 @@ -264,10 +264,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) if (err < 0) goto stop_streams;
- if (!amdtp_stream_wait_callback(&motu->tx_stream, - CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(&motu->rx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_domain_wait_ready(&motu->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto stop_streams; } diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index e9b6a9f171bf..4121d95e161f 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -9,7 +9,7 @@ #include <linux/delay.h>
#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512 -#define CALLBACK_TIMEOUT 200 +#define READY_TIMEOUT_MS 200
/* * According to datasheet of Oxford Semiconductor: @@ -358,20 +358,10 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) if (err < 0) goto error;
- // Wait first packet. - if (!amdtp_stream_wait_callback(&oxfw->rx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_domain_wait_ready(&oxfw->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; } - - if (oxfw->has_output) { - if (!amdtp_stream_wait_callback(&oxfw->tx_stream, - CALLBACK_TIMEOUT)) { - err = -ETIMEDOUT; - goto error; - } - } }
return 0; diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index eb07e1decf9b..296ecf5f6ddc 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -11,7 +11,7 @@ #define CLOCK_STATUS_MASK 0xffff0000 #define CLOCK_CONFIG_MASK 0x0000ffff
-#define CALLBACK_TIMEOUT 500 +#define READY_TIMEOUT_MS 500
static int get_clock(struct snd_tscm *tscm, u32 *data) { @@ -477,10 +477,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) return err;
- if (!amdtp_stream_wait_callback(&tscm->rx_stream, - CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(&tscm->tx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_domain_wait_ready(&tscm->domain, READY_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; }
On Thu, 20 May 2021 06:01:46 +0200, Takashi Sakamoto wrote:
Hi,
Within devices supported by drivers in ALSA firewire stack, only DICE ASICs support several pair of isochronous packet streams. As long as I experienced, the ASIC requires drivers to transfer packets at the same isochronous cycle. Unless, it generates no sound.
Additionally, in future integration I have a plan to take drivers performing sequence replay for media clock recovery. It's convenient to manage the cycle to start processing content of packet in several IR contexts.
This patchset is to start processing content of packet at the same cycle in several contexts for each direction, including code refactoring.
Takashi Sakamoto (8): ALSA: firewire-lib: code refactoring for size of CIP header ALSA: firewire-lib: code refactoring for calculation of context payload ALSA: firewire-lib: code refactoring for selection of IT context header length ALSA: firewire-lib: start processing content of packet at the same cycle in several IR contexts ALSA: firewire-lib: skip initial packets instead of scheduling IR context ALSA: firewire-lib: code refactoring to start several IT/IR contexts ALSA: firewire-lib: start processing content of packet at the same cycle in several IT contexts ALSA: firewire-lib: change waking up timing to process packets
Thanks, applied all eight patches.
Takashi
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto