Commit 60dd4929 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: firewire-lib: handle several AMDTP streams in callback handler of IRQ target

This commit changes AMDTP domain to run on an IT context of 1394 OHCI as
IRQ target. No hardware interrupt is scheduled for the other isoc
contexts. All of the isoc context are processed in a callback for an isoc
context of IRQ target.

The IRQ target is automatically selected from a list of AMDTP streams,
thus users of AMDTP domain should add an AMDTP stream for IT context
at least.

The reason to select IT context as IRQ target is that the IT context
runs on local 1394 OHCI controller and it can be used as reliable,
constant IRQ generator. On the other hand, IR context can include skip
cycle according to isoc packet transferred by device.
Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20191018061911.24909-6-o-takashi@sakamocchi.jpSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 813dfbd6
...@@ -482,13 +482,13 @@ static inline int queue_out_packet(struct amdtp_stream *s, ...@@ -482,13 +482,13 @@ static inline int queue_out_packet(struct amdtp_stream *s,
} }
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, bool sched_irq) struct fw_iso_packet *params)
{ {
// 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, sched_irq); return queue_packet(s, params, false);
} }
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],
...@@ -790,15 +790,24 @@ static void process_ctx_payloads(struct amdtp_stream *s, ...@@ -790,15 +790,24 @@ static void process_ctx_payloads(struct amdtp_stream *s,
update_pcm_pointers(s, pcm, pcm_frames); update_pcm_pointers(s, pcm, pcm_frames);
} }
static void amdtp_stream_master_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data);
static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data);
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header, size_t header_length, void *header,
void *private_data) void *private_data)
{ {
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 events_per_period = s->ctx_data.rx.events_per_period;
unsigned int event_count = s->event_count; unsigned int event_count = s->ctx_data.rx.event_count;
unsigned int packets; unsigned int packets;
bool is_irq_target;
int i; int i;
if (s->packet_index < 0) if (s->packet_index < 0)
...@@ -811,6 +820,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -811,6 +820,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
process_ctx_payloads(s, s->pkt_descs, packets); process_ctx_payloads(s, s->pkt_descs, packets);
is_irq_target =
!!(context->callback.sc == amdtp_stream_master_callback ||
context->callback.sc == amdtp_stream_master_first_callback);
for (i = 0; i < packets; ++i) { for (i = 0; i < packets; ++i) {
const struct pkt_desc *desc = s->pkt_descs + i; const struct pkt_desc *desc = s->pkt_descs + i;
unsigned int syt; unsigned int syt;
...@@ -829,11 +842,13 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -829,11 +842,13 @@ 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 (is_irq_target) {
event_count += desc->data_blocks; event_count += desc->data_blocks;
if (event_count >= events_per_period) { if (event_count >= events_per_period) {
event_count -= events_per_period; event_count -= events_per_period;
sched_irq = true; sched_irq = true;
} }
}
if (queue_out_packet(s, &template.params, sched_irq) < 0) { if (queue_out_packet(s, &template.params, sched_irq) < 0) {
cancel_stream(s); cancel_stream(s);
...@@ -841,7 +856,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -841,7 +856,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
} }
} }
s->event_count = event_count; s->ctx_data.rx.event_count = event_count;
} }
static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
...@@ -850,8 +865,6 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -850,8 +865,6 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
__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; unsigned int packets;
int i; int i;
int err; int err;
...@@ -873,31 +886,47 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -873,31 +886,47 @@ 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) { if (queue_in_packet(s, &params) < 0) {
event_count += desc->data_blocks; cancel_stream(s);
if (event_count >= events_per_period) { return;
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, sched_irq) < 0) { static void amdtp_stream_master_callback(struct fw_iso_context *context,
cancel_stream(s); u32 tstamp, size_t header_length,
return; void *header, void *private_data)
{
struct amdtp_domain *d = private_data;
struct amdtp_stream *irq_target = d->irq_target;
struct amdtp_stream *s;
out_stream_callback(context, tstamp, header_length, header, irq_target);
if (amdtp_streaming_error(irq_target))
goto error;
list_for_each_entry(s, &d->streams, list) {
if (s != irq_target && amdtp_stream_running(s)) {
fw_iso_context_flush_completions(s->context);
if (amdtp_streaming_error(s))
goto error;
} }
} }
s->event_count = event_count; return;
error:
if (amdtp_stream_running(irq_target))
cancel_stream(irq_target);
list_for_each_entry(s, &d->streams, list) {
if (amdtp_stream_running(s))
cancel_stream(s);
}
} }
/* this is executed one time */ // this is executed one time.
static void amdtp_stream_first_callback(struct fw_iso_context *context, static void amdtp_stream_first_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length, u32 tstamp, size_t header_length,
void *header, void *private_data) void *header, void *private_data)
...@@ -928,18 +957,39 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, ...@@ -928,18 +957,39 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
context->callback.sc(context, tstamp, header_length, header, s); context->callback.sc(context, tstamp, header_length, header, s);
} }
static void amdtp_stream_master_first_callback(struct fw_iso_context *context,
u32 tstamp, size_t header_length,
void *header, void *private_data)
{
struct amdtp_domain *d = private_data;
struct amdtp_stream *s = d->irq_target;
const __be32 *ctx_header = header;
s->callbacked = true;
wake_up(&s->callback_wait);
s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size);
context->callback.sc = amdtp_stream_master_callback;
context->callback.sc(context, tstamp, header_length, header, d);
}
/** /**
* amdtp_stream_start - start transferring packets * amdtp_stream_start - start transferring packets
* @s: the AMDTP stream to start * @s: the AMDTP stream to start
* @channel: the isochronous channel on the bus * @channel: the isochronous channel on the bus
* @speed: firewire speed code * @speed: firewire speed code
* @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.
* *
* The stream cannot be started until it has been configured with * 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 * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
* device can be started. * device can be started.
*/ */
static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
struct amdtp_domain *d) struct amdtp_domain *d, bool is_irq_target)
{ {
static const struct { static const struct {
unsigned int data_block; unsigned int data_block;
...@@ -955,10 +1005,13 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -955,10 +1005,13 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
}; };
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 events_per_period = d->events_per_period;
unsigned int idle_irq_interval;
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;
int type, tag, err; int type, tag, err;
fw_iso_callback_t ctx_cb;
void *ctx_data;
mutex_lock(&s->mutex); mutex_lock(&s->mutex);
...@@ -969,6 +1022,12 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -969,6 +1022,12 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
} }
if (s->direction == AMDTP_IN_STREAM) { if (s->direction == AMDTP_IN_STREAM) {
// NOTE: IT context should be used for constant IRQ.
if (is_irq_target) {
err = -EINVAL;
goto err_unlock;
}
s->data_block_counter = UINT_MAX; s->data_block_counter = UINT_MAX;
} else { } else {
entry = &initial_state[s->sfc]; entry = &initial_state[s->sfc];
...@@ -1008,22 +1067,29 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -1008,22 +1067,29 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
if (events_per_buffer == 0) if (events_per_buffer == 0)
events_per_buffer = events_per_period * 3; events_per_buffer = events_per_period * 3;
s->idle_irq_interval = idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
amdtp_rate_table[s->sfc]); 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);
if (err < 0) if (err < 0)
goto err_unlock; goto err_unlock;
if (is_irq_target) {
s->ctx_data.rx.events_per_period = events_per_period;
s->ctx_data.rx.event_count = 0;
ctx_cb = amdtp_stream_master_first_callback;
ctx_data = d;
} else {
ctx_cb = amdtp_stream_first_callback;
ctx_data = s;
}
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
type, channel, speed, ctx_header_size, type, channel, speed, ctx_header_size,
amdtp_stream_first_callback, s); ctx_cb, ctx_data);
if (IS_ERR(s->context)) { if (IS_ERR(s->context)) {
err = PTR_ERR(s->context); err = PTR_ERR(s->context);
if (err == -EBUSY) if (err == -EBUSY)
...@@ -1054,14 +1120,20 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, ...@@ -1054,14 +1120,20 @@ 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, sched_irq); err = queue_in_packet(s, &params);
} else { } else {
bool sched_irq = false;
params.header_length = 0; params.header_length = 0;
params.payload_length = 0; params.payload_length = 0;
if (is_irq_target) {
sched_irq = !((s->packet_index + 1) %
idle_irq_interval);
}
err = queue_out_packet(s, &params, sched_irq); err = queue_out_packet(s, &params, sched_irq);
} }
if (err < 0) if (err < 0)
...@@ -1276,17 +1348,33 @@ int amdtp_domain_start(struct amdtp_domain *d) ...@@ -1276,17 +1348,33 @@ int amdtp_domain_start(struct amdtp_domain *d)
struct amdtp_stream *s; struct amdtp_stream *s;
int err = 0; int err = 0;
// Select an IT context as IRQ target.
list_for_each_entry(s, &d->streams, list) { list_for_each_entry(s, &d->streams, list) {
err = amdtp_stream_start(s, s->channel, s->speed, d); if (s->direction == AMDTP_OUT_STREAM)
if (err < 0)
break; break;
} }
if (!s)
return -ENXIO;
d->irq_target = s;
if (err < 0) { list_for_each_entry(s, &d->streams, list) {
list_for_each_entry(s, &d->streams, list) if (s != d->irq_target) {
amdtp_stream_stop(s); err = amdtp_stream_start(s, s->channel, s->speed, d,
false);
if (err < 0)
goto error;
}
} }
s = d->irq_target;
err = amdtp_stream_start(s, s->channel, s->speed, d, true);
if (err < 0)
goto error;
return 0;
error:
list_for_each_entry(s, &d->streams, list)
amdtp_stream_stop(s);
return err; return err;
} }
EXPORT_SYMBOL_GPL(amdtp_domain_start); EXPORT_SYMBOL_GPL(amdtp_domain_start);
...@@ -1299,12 +1387,17 @@ void amdtp_domain_stop(struct amdtp_domain *d) ...@@ -1299,12 +1387,17 @@ void amdtp_domain_stop(struct amdtp_domain *d)
{ {
struct amdtp_stream *s, *next; struct amdtp_stream *s, *next;
if (d->irq_target)
amdtp_stream_stop(d->irq_target);
list_for_each_entry_safe(s, next, &d->streams, list) { list_for_each_entry_safe(s, next, &d->streams, list) {
list_del(&s->list); list_del(&s->list);
if (s != d->irq_target)
amdtp_stream_stop(s); amdtp_stream_stop(s);
} }
d->events_per_period = 0; d->events_per_period = 0;
d->irq_target = NULL;
} }
EXPORT_SYMBOL_GPL(amdtp_domain_stop); EXPORT_SYMBOL_GPL(amdtp_domain_stop);
...@@ -143,11 +143,12 @@ struct amdtp_stream { ...@@ -143,11 +143,12 @@ struct amdtp_stream {
// To generate CIP header. // To generate CIP header.
unsigned int fdf; unsigned int fdf;
int syt_override; int syt_override;
} rx;
} ctx_data; // To generate constant hardware IRQ.
unsigned int event_count; unsigned int event_count;
unsigned int events_per_period; unsigned int events_per_period;
unsigned int idle_irq_interval; } rx;
} ctx_data;
/* 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