Commit 26541cb1 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: firewire-lib: skip initial packets instead of scheduling IR context

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20210520040154.80450-6-o-takashi@sakamocchi.jpSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent da3623ab
...@@ -1199,12 +1199,16 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, ...@@ -1199,12 +1199,16 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
} }
if (stream_count == callbacked_count) { if (stream_count == callbacked_count) {
unsigned int next_cycle;
list_for_each_entry(s, &d->streams, list) { list_for_each_entry(s, &d->streams, list) {
if (s->direction != AMDTP_IN_STREAM) if (s->direction != AMDTP_IN_STREAM)
continue; continue;
if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0) next_cycle = increment_ohci_cycle_count(s->next_cycle,
cycle = 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; 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, ...@@ -1533,36 +1537,13 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
} }
EXPORT_SYMBOL_GPL(amdtp_domain_add_stream); 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,
&reg, 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. * amdtp_domain_start - start sending packets for isoc context in the domain.
* @d: the AMDTP 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 { static const struct {
unsigned int data_block; unsigned int data_block;
...@@ -1581,7 +1562,6 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) ...@@ -1581,7 +1562,6 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
unsigned int idle_irq_interval; unsigned int idle_irq_interval;
unsigned int queue_size; unsigned int queue_size;
struct amdtp_stream *s; struct amdtp_stream *s;
int cycle;
int err; int err;
// Select an IT context as IRQ target. // Select an IT context as IRQ target.
...@@ -1593,6 +1573,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) ...@@ -1593,6 +1573,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
return -ENXIO; return -ENXIO;
d->irq_target = s; 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 // This is a case that AMDTP streams in domain run just for MIDI
// substream. Use the number of events equivalent to 10 msec as // substream. Use the number of events equivalent to 10 msec as
// interval of hardware IRQ. // interval of hardware IRQ.
...@@ -1615,48 +1597,12 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) ...@@ -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->syt_offset_state = entry->syt_offset;
d->last_syt_offset = TICKS_PER_CYCLE; 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) { list_for_each_entry(s, &d->streams, list) {
int cycle_match; if (s->direction == AMDTP_OUT_STREAM)
if (s->direction == AMDTP_IN_STREAM) {
cycle_match = cycle;
} else {
// IT context starts immediately.
cycle_match = -1;
s->ctx_data.rx.seq_index = 0; s->ctx_data.rx.seq_index = 0;
}
if (s != d->irq_target) { if (s != d->irq_target) {
err = amdtp_stream_start(s, s->channel, s->speed, err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, 0);
cycle_match, queue_size, 0);
if (err < 0) if (err < 0)
goto error; goto error;
} }
......
...@@ -289,6 +289,7 @@ struct amdtp_domain { ...@@ -289,6 +289,7 @@ struct amdtp_domain {
struct amdtp_stream *irq_target; struct amdtp_stream *irq_target;
struct { struct {
unsigned int tx_init_skip;
unsigned int tx_start; unsigned int tx_start;
} processing_cycle; } processing_cycle;
...@@ -309,7 +310,7 @@ void amdtp_domain_destroy(struct amdtp_domain *d); ...@@ -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 amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
int channel, int speed); 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); void amdtp_domain_stop(struct amdtp_domain *d);
static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d, static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d,
......
...@@ -626,7 +626,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) ...@@ -626,7 +626,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
enum snd_bebob_clock_type src; enum snd_bebob_clock_type src;
struct amdtp_stream *master, *slave; struct amdtp_stream *master, *slave;
unsigned int curr_rate; unsigned int curr_rate;
unsigned int ir_delay_cycle; unsigned int tx_init_skip_cycles;
if (bebob->maudio_special_quirk) { if (bebob->maudio_special_quirk) {
err = bebob->spec->rate->get(bebob, &curr_rate); err = bebob->spec->rate->get(bebob, &curr_rate);
...@@ -654,20 +654,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) ...@@ -654,20 +654,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
if (err < 0) if (err < 0)
goto error; goto error;
// The device postpones start of transmission mostly for 1 sec // Some devices transfer isoc packets with discontinuous counter in the beginning
// after receives packets firstly. For safe, IR context starts // of packet streaming.
// 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.
if (bebob->version < 2) if (bebob->version < 2)
ir_delay_cycle = 3200; tx_init_skip_cycles = 3200;
else else
ir_delay_cycle = 16000; tx_init_skip_cycles = 16000;
err = amdtp_domain_start(&bebob->domain, ir_delay_cycle); err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles);
if (err < 0) if (err < 0)
goto error; goto error;
...@@ -684,6 +677,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) ...@@ -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, if (!amdtp_stream_wait_callback(&bebob->rx_stream,
CALLBACK_TIMEOUT) || CALLBACK_TIMEOUT) ||
!amdtp_stream_wait_callback(&bebob->tx_stream, !amdtp_stream_wait_callback(&bebob->tx_stream,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment