tasklet_kill(&s->period_tasklet);
s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0;
- s->pointer_flush = true;
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
s->pcm_period_pointer += frames;
if (s->pcm_period_pointer >= pcm->runtime->period_size) {
s->pcm_period_pointer -= pcm->runtime->period_size;
- s->pointer_flush = false;
tasklet_hi_schedule(&s->period_tasklet);
}
}
amdtp_stream_get_max_payload(s), false);
}
-static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
- unsigned int cycle, unsigned int syt)
+static int handle_out_packet(struct amdtp_stream *s, 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;
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 + 2, data_blocks, &syt);
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
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);
+ trace_out_packet(s, cycle, buffer, payload_length, index);
if (queue_out_packet(s, payload_length, false) < 0)
return -EIO;
}
static int handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets, unsigned int cycle)
+ unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int index)
{
__be32 *buffer;
u32 cip_header[2];
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);
+ trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
/*
* This module supports 'Two-quadlet CIP header with SYT field'.
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int i, syt, packets = header_length / 4;
- unsigned int data_blocks;
+ unsigned int i, packets = header_length / 4;
u32 cycle;
if (s->packet_index < 0)
for (i = 0; i < packets; ++i) {
cycle = increment_cycle_count(cycle, 1);
- syt = calculate_syt(s, cycle);
- data_blocks = calculate_data_blocks(s, syt);
-
- if (handle_out_packet(s, data_blocks, cycle, syt) < 0) {
+ if (handle_out_packet(s, cycle, i) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
break;
}
- if (handle_in_packet(s, payload_quadlets, cycle) < 0)
+ if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
break;
}
goto err_unlock;
}
- if (s->direction == AMDTP_IN_STREAM &&
- s->flags & CIP_SKIP_INIT_DBC_CHECK)
+ if (s->direction == AMDTP_IN_STREAM)
s->data_block_counter = UINT_MAX;
else
s->data_block_counter = 0;
*/
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
{
- /* this optimization is allowed to be racy */
- if (s->pointer_flush && amdtp_stream_running(s))
+ /*
+ * This function is called in software IRQ context of period_tasklet or
+ * process context.
+ *
+ * When the software IRQ context was scheduled by software IRQ context
+ * of IR/IT contexts, queued packets were already handled. Therefore,
+ * no need to flush the queue in buffer anymore.
+ *
+ * When the process context reach here, some packets will be already
+ * queued in the buffer. These packets should be handled immediately
+ * to keep better granularity of PCM pointer.
+ *
+ * Later, the process context will sometimes schedules software IRQ
+ * context of the period_tasklet. Then, no need to flush the queue by
+ * the same reason as described for IR/IT contexts.
+ */
+ if (!in_interrupt() && amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context);
- else
- s->pointer_flush = true;
return ACCESS_ONCE(s->pcm_buffer_pointer);
}