summaryrefslogtreecommitdiff
path: root/sound/firewire/amdtp-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/amdtp-stream.c')
-rw-r--r--sound/firewire/amdtp-stream.c158
1 files changed, 130 insertions, 28 deletions
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 00060c4a9deb..9e6f54f8c45d 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -27,6 +27,7 @@
/* isochronous header parameters */
#define ISO_DATA_LENGTH_SHIFT 16
+#define TAG_NO_CIP_HEADER 0
#define TAG_CIP 1
/* common isochronous packet header parameters */
@@ -37,6 +38,8 @@
#define CIP_SID_MASK 0x3f000000
#define CIP_DBS_MASK 0x00ff0000
#define CIP_DBS_SHIFT 16
+#define CIP_SPH_MASK 0x00000400
+#define CIP_SPH_SHIFT 10
#define CIP_DBC_MASK 0x000000ff
#define CIP_FMT_SHIFT 24
#define CIP_FMT_MASK 0x3f000000
@@ -232,11 +235,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
unsigned int multiplier = 1;
+ unsigned int header_size = 0;
if (s->flags & CIP_JUMBO_PAYLOAD)
multiplier = 5;
+ if (!(s->flags & CIP_NO_HEADER))
+ header_size = 8;
- return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
+ return header_size +
+ s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@@ -378,7 +385,7 @@ static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
goto end;
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
- p.tag = TAG_CIP;
+ p.tag = s->tag;
p.header_length = header_length;
if (payload_length > 0)
p.payload_length = payload_length;
@@ -405,17 +412,16 @@ static inline int queue_out_packet(struct amdtp_stream *s,
static inline int queue_in_packet(struct amdtp_stream *s)
{
- return queue_packet(s, IN_PACKET_HEADER_SIZE,
- amdtp_stream_get_max_payload(s));
+ return queue_packet(s, IN_PACKET_HEADER_SIZE, s->max_payload_length);
}
-static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
+static int handle_out_packet(struct amdtp_stream *s,
+ unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int syt;
unsigned int data_blocks;
- unsigned int payload_length;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
@@ -424,15 +430,22 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+ if (s->flags & CIP_DBC_IS_END_EVENT)
+ s->data_block_counter =
+ (s->data_block_counter + data_blocks) & 0xff;
+
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
+ ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH |
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
(syt & CIP_SYT_MASK));
- s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+ if (!(s->flags & CIP_DBC_IS_END_EVENT))
+ s->data_block_counter =
+ (s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
trace_out_packet(s, cycle, buffer, payload_length, index);
@@ -448,13 +461,45 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
return 0;
}
+static int handle_out_packet_without_header(struct amdtp_stream *s,
+ unsigned int payload_length, unsigned int cycle,
+ unsigned int index)
+{
+ __be32 *buffer;
+ unsigned int syt;
+ unsigned int data_blocks;
+ unsigned int pcm_frames;
+ struct snd_pcm_substream *pcm;
+
+ buffer = s->buffer.packets[s->packet_index].buffer;
+ syt = calculate_syt(s, cycle);
+ data_blocks = calculate_data_blocks(s, syt);
+ pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt);
+ s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+ payload_length = data_blocks * 4 * s->data_block_quadlets;
+
+ trace_out_packet_without_header(s, cycle, payload_length, data_blocks,
+ index);
+
+ if (queue_out_packet(s, payload_length) < 0)
+ return -EIO;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
+
+ /* No need to return the number of handled data blocks. */
+ return 0;
+}
+
static int handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
u32 cip_header[2];
- unsigned int fmt, fdf, syt;
+ unsigned int sph, fmt, fdf, syt;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
unsigned int data_blocks;
struct snd_pcm_substream *pcm;
@@ -465,14 +510,15 @@ static int handle_in_packet(struct amdtp_stream *s,
cip_header[0] = be32_to_cpu(buffer[0]);
cip_header[1] = be32_to_cpu(buffer[1]);
- trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
+ trace_in_packet(s, cycle, cip_header, payload_length, index);
/*
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
*/
- if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+ if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+ (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
@@ -482,8 +528,9 @@ static int handle_in_packet(struct amdtp_stream *s,
}
/* Check valid protocol or not. */
+ sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
- if (fmt != s->fmt) {
+ if (sph != s->sph || fmt != s->fmt) {
dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]);
@@ -494,7 +541,7 @@ static int handle_in_packet(struct amdtp_stream *s,
/* Calculate data blocks */
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
- if (payload_quadlets < 3 ||
+ if (payload_length < 12 ||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
data_blocks = 0;
} else {
@@ -510,7 +557,8 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+ data_blocks = (payload_length / 4 - 2) /
+ data_block_quadlets;
}
/* Check data block counter continuity */
@@ -561,6 +609,34 @@ end:
return 0;
}
+static int handle_in_packet_without_header(struct amdtp_stream *s,
+ unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int index)
+{
+ __be32 *buffer;
+ unsigned int data_blocks;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ buffer = s->buffer.packets[s->packet_index].buffer;
+ data_blocks = payload_quadlets / s->data_block_quadlets;
+
+ trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks,
+ index);
+
+ pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
+ s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+ if (queue_in_packet(s) < 0)
+ return -EIO;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
+
+ return 0;
+}
+
/*
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
@@ -604,7 +680,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
for (i = 0; i < packets; ++i) {
cycle = increment_cycle_count(cycle, 1);
- if (handle_out_packet(s, cycle, i) < 0) {
+ if (s->handle_packet(s, 0, cycle, i) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
@@ -620,7 +696,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
{
struct amdtp_stream *s = private_data;
unsigned int i, packets;
- unsigned int payload_quadlets, max_payload_quadlets;
+ unsigned int payload_length, max_payload_length;
__be32 *headers = header;
u32 cycle;
@@ -636,22 +712,22 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
cycle = decrement_cycle_count(cycle, packets);
/* For buffer-over-run prevention. */
- max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
+ max_payload_length = s->max_payload_length;
for (i = 0; i < packets; i++) {
cycle = increment_cycle_count(cycle, 1);
- /* The number of quadlets in this packet */
- payload_quadlets =
- (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
- if (payload_quadlets > max_payload_quadlets) {
+ /* The number of bytes in this packet */
+ payload_length =
+ (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT);
+ if (payload_length > max_payload_length) {
dev_err(&s->unit->device,
- "Detect jumbo payload: %02x %02x\n",
- payload_quadlets, max_payload_quadlets);
+ "Detect jumbo payload: %04x %04x\n",
+ payload_length, max_payload_length);
break;
}
- if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
+ if (s->handle_packet(s, payload_length, cycle, i) < 0)
break;
}
@@ -671,6 +747,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
void *header, void *private_data)
{
struct amdtp_stream *s = private_data;
+ u32 cycle;
+ unsigned int packets;
+
+ s->max_payload_length = amdtp_stream_get_max_payload(s);
/*
* For in-stream, first packet has come.
@@ -679,10 +759,27 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
s->callbacked = true;
wake_up(&s->callback_wait);
- if (s->direction == AMDTP_IN_STREAM)
+ cycle = compute_cycle_count(tstamp);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ packets = header_length / IN_PACKET_HEADER_SIZE;
+ cycle = decrement_cycle_count(cycle, packets);
context->callback.sc = in_stream_callback;
- else
+ if (s->flags & CIP_NO_HEADER)
+ s->handle_packet = handle_in_packet_without_header;
+ else
+ s->handle_packet = handle_in_packet;
+ } else {
+ packets = header_length / 4;
+ cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
context->callback.sc = out_stream_callback;
+ if (s->flags & CIP_NO_HEADER)
+ s->handle_packet = handle_out_packet_without_header;
+ else
+ s->handle_packet = handle_out_packet;
+ }
+
+ s->start_cycle = cycle;
context->callback.sc(context, tstamp, header_length, header, s);
}
@@ -759,6 +856,11 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
amdtp_stream_update(s);
+ if (s->flags & CIP_NO_HEADER)
+ s->tag = TAG_NO_CIP_HEADER;
+ else
+ s->tag = TAG_CIP;
+
s->packet_index = 0;
do {
if (s->direction == AMDTP_IN_STREAM)
@@ -771,7 +873,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
/* NOTE: TAG1 matches CIP. This just affects in stream. */
tag = FW_ISO_CONTEXT_MATCH_TAG1;
- if (s->flags & CIP_EMPTY_WITH_TAG0)
+ if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false;