summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/firewire/amdtp-stream.c82
-rw-r--r--sound/firewire/amdtp-stream.h12
-rw-r--r--sound/firewire/bebob/bebob_stream.c2
-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.c2
-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, 12 deletions
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 68ffbc33f692..860942ffb1f1 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -52,6 +52,7 @@
#define CIP_FDF_NO_DATA 0xff
#define CIP_SYT_MASK 0x0000ffff
#define CIP_SYT_NO_INFO 0xffff
+#define CIP_SYT_CYCLE_MODULUS 16
#define CIP_NO_DATA ((CIP_FDF_NO_DATA << CIP_FDF_SHIFT) | CIP_SYT_NO_INFO)
#define CIP_HEADER_SIZE (sizeof(__be32) * CIP_HEADER_QUADLETS)
@@ -473,6 +474,52 @@ static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *desc
s->ctx_data.rx.syt_offset_state = state;
}
+static unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle,
+ unsigned int transfer_delay)
+{
+ unsigned int cycle_lo = (cycle % CYCLES_PER_SECOND) & 0x0f;
+ unsigned int syt_cycle_lo = (syt & 0xf000) >> 12;
+ unsigned int syt_offset;
+
+ // Round up.
+ if (syt_cycle_lo < cycle_lo)
+ syt_cycle_lo += CIP_SYT_CYCLE_MODULUS;
+ syt_cycle_lo -= cycle_lo;
+
+ // Subtract transfer delay so that the synchronization offset is not so large
+ // at transmission.
+ syt_offset = syt_cycle_lo * TICKS_PER_CYCLE + (syt & 0x0fff);
+ if (syt_offset < transfer_delay)
+ syt_offset += CIP_SYT_CYCLE_MODULUS * TICKS_PER_CYCLE;
+
+ return syt_offset - transfer_delay;
+}
+
+static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsigned int desc_count)
+{
+ const unsigned int transfer_delay = s->transfer_delay;
+ const unsigned int cache_size = s->ctx_data.tx.cache.size;
+ struct seq_desc *cache = s->ctx_data.tx.cache.descs;
+ unsigned int cache_tail = s->ctx_data.tx.cache.tail;
+ bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
+ int i;
+
+ for (i = 0; i < desc_count; ++i) {
+ struct seq_desc *dst = cache + cache_tail;
+ const struct pkt_desc *src = descs + i;
+
+ if (aware_syt && src->syt != CIP_SYT_NO_INFO)
+ dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay);
+ else
+ dst->syt_offset = CIP_SYT_NO_INFO;
+ dst->data_blocks = src->data_blocks;
+
+ cache_tail = (cache_tail + 1) % cache_size;
+ }
+
+ s->ctx_data.tx.cache.tail = cache_tail;
+}
+
static void pool_ideal_seq_descs(struct amdtp_stream *s, unsigned int count)
{
struct seq_desc *descs = s->ctx_data.rx.seq.descs;
@@ -1107,7 +1154,12 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_
return;
}
} else {
+ struct amdtp_domain *d = s->domain;
+
process_ctx_payloads(s, s->pkt_descs, desc_count);
+
+ if (d->replay.enable)
+ cache_seq(s, s->pkt_descs, desc_count);
}
for (i = 0; i < packets; ++i) {
@@ -1463,6 +1515,18 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size;
s->ctx_data.tx.ctx_header_size = ctx_header_size;
s->ctx_data.tx.event_starts = false;
+
+ if (s->domain->replay.enable) {
+ // struct fw_iso_context.drop_overflow_headers is false therefore it's
+ // possible to cache much unexpectedly.
+ s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2,
+ queue_size * 3 / 2);
+ s->ctx_data.tx.cache.tail = 0;
+ s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size,
+ sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL);
+ if (!s->ctx_data.tx.cache.descs)
+ goto err_context;
+ }
} else {
static const struct {
unsigned int data_block;
@@ -1543,8 +1607,12 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
err_pkt_descs:
kfree(s->pkt_descs);
err_context:
- if (s->direction == AMDTP_OUT_STREAM)
+ if (s->direction == AMDTP_OUT_STREAM) {
kfree(s->ctx_data.rx.seq.descs);
+ } else {
+ if (s->domain->replay.enable)
+ kfree(s->ctx_data.tx.cache.descs);
+ }
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
err_buffer:
@@ -1655,8 +1723,12 @@ static void amdtp_stream_stop(struct amdtp_stream *s)
iso_packets_buffer_destroy(&s->buffer, s->unit);
kfree(s->pkt_descs);
- if (s->direction == AMDTP_OUT_STREAM)
+ if (s->direction == AMDTP_OUT_STREAM) {
kfree(s->ctx_data.rx.seq.descs);
+ } else {
+ if (s->domain->replay.enable)
+ kfree(s->ctx_data.tx.cache.descs);
+ }
mutex_unlock(&s->mutex);
}
@@ -1735,8 +1807,10 @@ EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
* @d: the AMDTP domain.
* @tx_init_skip_cycles: the number of cycles to skip processing packets at initial stage of IR
* contexts.
+ * @replay_seq: whether to replay the sequence of packet in IR context for the sequence of packet in
+ * IT context.
*/
-int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles)
+int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq)
{
unsigned int events_per_buffer = d->events_per_buffer;
unsigned int events_per_period = d->events_per_period;
@@ -1744,6 +1818,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles)
struct amdtp_stream *s;
int err;
+ d->replay.enable = replay_seq;
+
// Select an IT context as IRQ target.
list_for_each_entry(s, &d->streams, list) {
if (s->direction == AMDTP_OUT_STREAM)
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index 34294776f9e8..ddfb885b6113 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -141,6 +141,12 @@ struct amdtp_stream {
// The device starts multiplexing events to the packet.
bool event_starts;
+
+ struct {
+ struct seq_desc *descs;
+ unsigned int size;
+ unsigned int tail;
+ } cache;
} tx;
struct {
// To generate CIP header.
@@ -292,6 +298,10 @@ struct amdtp_domain {
unsigned int tx_start;
unsigned int rx_start;
} processing_cycle;
+
+ struct {
+ bool enable;
+ } replay;
};
int amdtp_domain_init(struct amdtp_domain *d);
@@ -300,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 tx_init_skip_cycles);
+int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq);
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 91306da1bafe..fb776f871133 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -652,7 +652,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
// MEMO: In the early stage of packet streaming, the device transfers NODATA packets.
// After several hundred cycles, it begins to multiplex event into the packet with
// syt information.
- err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles);
+ err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles, false);
if (err < 0)
goto error;
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index a9a0fe9635dd..064f28f1822b 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -459,7 +459,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
goto error;
}
- err = amdtp_domain_start(&dice->domain, 0);
+ err = amdtp_domain_start(&dice->domain, 0, false);
if (err < 0)
goto error;
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
index f11aaff2e248..5daba75a5bf3 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, 0);
+ err = amdtp_domain_start(&dg00x->domain, 0, false);
if (err < 0)
goto error;
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
index 53a21fb95add..12779e7caf22 100644
--- a/sound/firewire/fireface/ff-stream.c
+++ b/sound/firewire/fireface/ff-stream.c
@@ -199,7 +199,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
if (err < 0)
goto error;
- err = amdtp_domain_start(&ff->domain, 0);
+ err = amdtp_domain_start(&ff->domain, 0, false);
if (err < 0)
goto error;
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 858cd6085c1f..0106fa6d1eaf 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, 0);
+ err = amdtp_domain_start(&efw->domain, 0, false);
if (err < 0)
goto error;
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 925241ae2551..5af9d7487cdc 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, 0);
+ err = amdtp_domain_start(&motu->domain, 0, false);
if (err < 0)
goto stop_streams;
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 4121d95e161f..a6e97a37f129 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -354,7 +354,7 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
}
}
- err = amdtp_domain_start(&oxfw->domain, 0);
+ err = amdtp_domain_start(&oxfw->domain, 0, false);
if (err < 0)
goto error;
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
index 296ecf5f6ddc..eb4c7c47f8e9 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, 0);
+ err = amdtp_domain_start(&tscm->domain, 0, false);
if (err < 0)
return err;