Commit e229853d authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: firewire-lib: schedule hardware IRQ according to the size of PCM period

ALSA IEC 61883-1/6 packet streaming engine controls 1394 OHCI controller
to generate hardware IRQ for fixed number of isochronous packets (=16)
since its first commit.

This commit allow the engine to generate it for variable period according
to the number of event to handle. For outgoing stream, internal
calculator is used to check the accumulated events. For incoming stream,
the number of data block in the packet of stream is used to check the
accumulated events. When it's unavailable, fixed number of packet
roughly calculated in advance is used instead of event counting.
Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20191017155424.885-11-o-takashi@sakamocchi.jpSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 4de3eb06
...@@ -52,9 +52,6 @@ ...@@ -52,9 +52,6 @@
#define CIP_FMT_AM 0x10 #define CIP_FMT_AM 0x10
#define AMDTP_FDF_NO_DATA 0xff #define AMDTP_FDF_NO_DATA 0xff
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
// For iso header, tstamp and 2 CIP header. // For iso header, tstamp and 2 CIP header.
#define IR_CTX_HEADER_SIZE_CIP 16 #define IR_CTX_HEADER_SIZE_CIP 16
// For iso header and tstamp. // For iso header and tstamp.
...@@ -435,11 +432,12 @@ static void pcm_period_tasklet(unsigned long data) ...@@ -435,11 +432,12 @@ static void pcm_period_tasklet(unsigned long data)
snd_pcm_period_elapsed(pcm); snd_pcm_period_elapsed(pcm);
} }
static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
bool sched_irq)
{ {
int err; int err;
params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); params->interrupt = sched_irq;
params->tag = s->tag; params->tag = s->tag;
params->sy = 0; params->sy = 0;
...@@ -457,21 +455,21 @@ static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) ...@@ -457,21 +455,21 @@ static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params)
} }
static inline int queue_out_packet(struct amdtp_stream *s, static inline int queue_out_packet(struct amdtp_stream *s,
struct fw_iso_packet *params) struct fw_iso_packet *params, bool sched_irq)
{ {
params->skip = params->skip =
!!(params->header_length == 0 && params->payload_length == 0); !!(params->header_length == 0 && params->payload_length == 0);
return queue_packet(s, params); return queue_packet(s, params, sched_irq);
} }
static inline int queue_in_packet(struct amdtp_stream *s, static inline int queue_in_packet(struct amdtp_stream *s,
struct fw_iso_packet *params) struct fw_iso_packet *params, bool sched_irq)
{ {
// Queue one packet for IR context. // Queue one packet for IR context.
params->header_length = s->ctx_data.tx.ctx_header_size; params->header_length = s->ctx_data.tx.ctx_header_size;
params->payload_length = s->ctx_data.tx.max_ctx_payload_length; params->payload_length = s->ctx_data.tx.max_ctx_payload_length;
params->skip = false; params->skip = false;
return queue_packet(s, params); return queue_packet(s, params, sched_irq);
} }
static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
...@@ -779,6 +777,8 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -779,6 +777,8 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
const __be32 *ctx_header = header; const __be32 *ctx_header = header;
unsigned int events_per_period = s->events_per_period;
unsigned int event_count = s->event_count;
unsigned int packets; unsigned int packets;
int i; int i;
...@@ -799,6 +799,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -799,6 +799,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
struct fw_iso_packet params; struct fw_iso_packet params;
__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
} template = { {0}, {0} }; } template = { {0}, {0} };
bool sched_irq = false;
if (s->ctx_data.rx.syt_override < 0) if (s->ctx_data.rx.syt_override < 0)
syt = desc->syt; syt = desc->syt;
...@@ -809,12 +810,20 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -809,12 +810,20 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
desc->data_blocks, desc->data_block_counter, desc->data_blocks, desc->data_block_counter,
syt, i); syt, i);
if (queue_out_packet(s, &template.params) < 0) { event_count += desc->data_blocks;
if (event_count >= events_per_period) {
event_count -= events_per_period;
sched_irq = true;
}
if (queue_out_packet(s, &template.params, sched_irq) < 0) {
cancel_stream(s); cancel_stream(s);
return; return;
} }
} }
s->event_count = event_count;
fw_iso_context_queue_flush(s->context); fw_iso_context_queue_flush(s->context);
} }
...@@ -823,8 +832,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -823,8 +832,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
void *private_data) void *private_data)
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
unsigned int packets;
__be32 *ctx_header = header; __be32 *ctx_header = header;
unsigned int events_per_period = s->events_per_period;
unsigned int event_count = s->event_count;
unsigned int packets;
int i; int i;
int err; int err;
...@@ -845,14 +856,29 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -845,14 +856,29 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
} }
for (i = 0; i < packets; ++i) { for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = s->pkt_descs + i;
struct fw_iso_packet params = {0}; struct fw_iso_packet params = {0};
bool sched_irq = false;
if (err >= 0) {
event_count += desc->data_blocks;
if (event_count >= events_per_period) {
event_count -= events_per_period;
sched_irq = true;
}
} else {
sched_irq =
!((s->packet_index + 1) % s->idle_irq_interval);
}
if (queue_in_packet(s, &params) < 0) { if (queue_in_packet(s, &params, sched_irq) < 0) {
cancel_stream(s); cancel_stream(s);
return; return;
} }
} }
s->event_count = event_count;
fw_iso_context_queue_flush(s->context); fw_iso_context_queue_flush(s->context);
} }
...@@ -913,6 +939,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -913,6 +939,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
[CIP_SFC_176400] = { 0, 67 }, [CIP_SFC_176400] = { 0, 67 },
}; };
unsigned int events_per_buffer = d->events_per_buffer; unsigned int events_per_buffer = d->events_per_buffer;
unsigned int events_per_period = d->events_per_period;
unsigned int ctx_header_size; unsigned int ctx_header_size;
unsigned int max_ctx_payload_size; unsigned int max_ctx_payload_size;
enum dma_data_direction dir; enum dma_data_direction dir;
...@@ -958,11 +985,21 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -958,11 +985,21 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
} }
// This is a case that AMDTP streams in domain run just for MIDI
// substream. Use the number of events equivalent to 10 msec as
// interval of hardware IRQ.
if (events_per_period == 0)
events_per_period = amdtp_rate_table[s->sfc] / 100;
if (events_per_buffer == 0) if (events_per_buffer == 0)
events_per_buffer = INTERRUPT_INTERVAL * 3; events_per_buffer = events_per_period * 3;
s->idle_irq_interval =
DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
amdtp_rate_table[s->sfc]);
s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
amdtp_rate_table[s->sfc]); amdtp_rate_table[s->sfc]);
s->events_per_period = events_per_period;
s->event_count = 0;
err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size, err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size,
max_ctx_payload_size, dir); max_ctx_payload_size, dir);
...@@ -1002,12 +1039,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -1002,12 +1039,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
s->packet_index = 0; s->packet_index = 0;
do { do {
struct fw_iso_packet params; struct fw_iso_packet params;
bool sched_irq;
sched_irq = !((s->packet_index + 1) % s->idle_irq_interval);
if (s->direction == AMDTP_IN_STREAM) { if (s->direction == AMDTP_IN_STREAM) {
err = queue_in_packet(s, &params); err = queue_in_packet(s, &params, sched_irq);
} else { } else {
params.header_length = 0; params.header_length = 0;
params.payload_length = 0; params.payload_length = 0;
err = queue_out_packet(s, &params); err = queue_out_packet(s, &params, sched_irq);
} }
if (err < 0) if (err < 0)
goto err_pkt_descs; goto err_pkt_descs;
......
...@@ -145,6 +145,9 @@ struct amdtp_stream { ...@@ -145,6 +145,9 @@ struct amdtp_stream {
int syt_override; int syt_override;
} rx; } rx;
} ctx_data; } ctx_data;
unsigned int event_count;
unsigned int events_per_period;
unsigned int idle_irq_interval;
/* For CIP headers. */ /* For CIP headers. */
unsigned int source_node_id_field; unsigned int source_node_id_field;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment