summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/firewire/amdtp-stream.c79
-rw-r--r--sound/firewire/amdtp-stream.h2
-rw-r--r--sound/firewire/bebob/bebob_stream.c10
-rw-r--r--sound/firewire/dice/dice-stream.c2
-rw-r--r--sound/firewire/digi00x/digi00x-stream.c2
-rw-r--r--sound/firewire/fireface/ff-stream.c10
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c2
-rw-r--r--sound/firewire/motu/motu-stream.c2
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c2
-rw-r--r--sound/firewire/tascam/tascam-stream.c2
10 files changed, 98 insertions, 15 deletions
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 <linux/device.h>
#include <linux/err.h>
#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/pcm.h>
@@ -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,
+ &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.
* @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;