diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 48be31eae9a5..37d38efb4c87 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -983,13 +984,16 @@ static void amdtp_stream_master_first_callback(struct fw_iso_context *context, * @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. * * 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, - struct amdtp_domain *d, bool is_irq_target) + struct amdtp_domain *d, bool is_irq_target, + int start_cycle) { static const struct { unsigned int data_block; @@ -1146,7 +1150,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, -1, 0, tag); + err = fw_iso_context_start(s->context, start_cycle, 0, tag); if (err < 0) goto err_pkt_descs; @@ -1339,14 +1343,40 @@ 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. */ -int amdtp_domain_start(struct amdtp_domain *d) +int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { struct amdtp_stream *s; - int err = 0; + int cycle; + int err; // Select an IT context as IRQ target. list_for_each_entry(s, &d->streams, list) { @@ -1357,17 +1387,54 @@ int amdtp_domain_start(struct amdtp_domain *d) return -ENXIO; d->irq_target = s; + 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; + + // 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 != d->irq_target) { err = amdtp_stream_start(s, s->channel, s->speed, d, - false); + false, cycle_match); if (err < 0) goto error; } } s = d->irq_target; - err = amdtp_stream_start(s, s->channel, s->speed, d, true); + err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1); if (err < 0) goto error; diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index c4bde69c2d4f..f2d44e2dc3c8 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -288,7 +288,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); +int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle); 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 5e4a61458be2..7ac0d9f495c4 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -658,7 +658,15 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) if (err < 0) goto error; - err = amdtp_domain_start(&bebob->domain); + // The device postpones start of transmission mostly for 1 sec + // after receives packets firstly. For safe, IR context starts + // 1.5 sec (=12000 cycles) later. This is within 2.0 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. + err = amdtp_domain_start(&bebob->domain, 12000); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 0cff346e8052..6a3d60913e10 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -462,7 +462,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) goto error; } - err = amdtp_domain_start(&dice->domain); + err = amdtp_domain_start(&dice->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 0c539188ba18..405d6903bfbc 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -375,7 +375,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) if (err < 0) goto error; - err = amdtp_domain_start(&dg00x->domain); + err = amdtp_domain_start(&dg00x->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index a13754f914e8..63b79c4a5405 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -184,6 +184,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) */ if (!amdtp_stream_running(&ff->rx_stream)) { int spd = fw_parent_device(ff->unit)->max_speed; + unsigned int ir_delay_cycle; err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) @@ -199,7 +200,14 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) goto error; - err = amdtp_domain_start(&ff->domain); + // The device postpones start of transmission mostly for several + // cycles after receiving packets firstly. + if (ff->spec->protocol == &snd_ff_protocol_ff800) + ir_delay_cycle = 800; // = 100 msec + else + ir_delay_cycle = 16; // = 2 msec + + err = amdtp_domain_start(&ff->domain, ir_delay_cycle); if (err < 0) goto error; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index f35a33d4d4e6..2206af0fef42 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -272,7 +272,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw) if (err < 0) goto error; - err = amdtp_domain_start(&efw->domain); + err = amdtp_domain_start(&efw->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 9975770c9b1f..a17ddceb1bec 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -260,7 +260,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) if (err < 0) goto stop_streams; - err = amdtp_domain_start(&motu->domain); + err = amdtp_domain_start(&motu->domain, 0); if (err < 0) goto stop_streams; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 995e9c5bd84b..501a80094bf7 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -355,7 +355,7 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) } } - err = amdtp_domain_start(&oxfw->domain); + err = amdtp_domain_start(&oxfw->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index a9b3b7eb6d21..eb07e1decf9b 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -473,7 +473,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) goto error; - err = amdtp_domain_start(&tscm->domain); + err = amdtp_domain_start(&tscm->domain, 0); if (err < 0) return err;