diff options
author | Takashi Iwai <tiwai@suse.de> | 2019-09-14 17:24:57 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2019-09-14 17:26:10 +0200 |
commit | d3f9990f1b48514b33342612b51fad238592d774 (patch) | |
tree | 9ad5ae81ba3e820e91a09e2675b7c98675bd809e /sound/firewire | |
parent | ALSA: firewire-tascam: check intermediate state of clock status and retry (diff) | |
parent | Merge tag 'asoc-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/brooni... (diff) | |
download | linux-d3f9990f1b48514b33342612b51fad238592d774.tar.xz linux-d3f9990f1b48514b33342612b51fad238592d774.zip |
Merge branch 'for-next' into for-linus
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/firewire')
26 files changed, 1169 insertions, 721 deletions
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index fd5d6b8ac557..67d735e9a6a4 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -146,19 +146,24 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s, } EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position); -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_am824 *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; const u32 *src; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { @@ -172,19 +177,24 @@ static void write_pcm_s32(struct amdtp_stream *s, } } -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_am824 *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; u32 *dst; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { @@ -284,7 +294,7 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) } static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, - unsigned int frames) + unsigned int frames, unsigned int data_block_counter) { struct amdtp_am824 *p = s->protocol; unsigned int f, port; @@ -293,7 +303,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, for (f = 0; f < frames; f++) { b = (u8 *)&buffer[p->midi_position]; - port = (s->data_block_counter + f) % 8; + port = (data_block_counter + f) % 8; if (f < MAX_MIDI_RX_BLOCKS && midi_ratelimit_per_packet(s, port) && p->midi[port] != NULL && @@ -311,16 +321,20 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, } } -static void read_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) +static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int frames, unsigned int data_block_counter) { struct amdtp_am824 *p = s->protocol; - unsigned int f, port; int len; u8 *b; + int f; for (f = 0; f < frames; f++) { - port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8; + unsigned int port = f; + + if (!(s->flags & CIP_UNALIGHED_DBC)) + port += data_block_counter; + port %= 8; b = (u8 *)&buffer[p->midi_position]; len = b[0] - 0x80; @@ -331,43 +345,60 @@ static void read_midi_messages(struct amdtp_stream *s, } } -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) +static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { struct amdtp_am824 *p = s->protocol; - struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - write_pcm_s32(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * p->frame_multiplier; - } else { - write_pcm_silence(s, buffer, data_blocks); - pcm_frames = 0; - } + unsigned int pcm_frames = 0; + int i; - if (p->midi_ports) - write_midi_messages(s, buffer, data_blocks); + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + + if (pcm) { + write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks * p->frame_multiplier; + } else { + write_pcm_silence(s, buf, data_blocks); + } + + if (p->midi_ports) { + write_midi_messages(s, buf, data_blocks, + desc->data_block_counter); + } + } return pcm_frames; } -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) +static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { struct amdtp_am824 *p = s->protocol; - struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - read_pcm_s32(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * p->frame_multiplier; - } else { - pcm_frames = 0; - } + unsigned int pcm_frames = 0; + int i; + + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - if (p->midi_ports) - read_midi_messages(s, buffer, data_blocks); + if (pcm) { + read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks * p->frame_multiplier; + } + + if (p->midi_ports) { + read_midi_messages(s, buf, data_blocks, + desc->data_block_counter); + } + } return pcm_frames; } @@ -383,15 +414,14 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags) { - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; if (dir == AMDTP_IN_STREAM) - process_data_blocks = process_tx_data_blocks; + process_ctx_payloads = process_ir_ctx_payloads; else - process_data_blocks = process_rx_data_blocks; + process_ctx_payloads = process_it_ctx_payloads; return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM, - process_data_blocks, - sizeof(struct amdtp_am824)); + process_ctx_payloads, sizeof(struct amdtp_am824)); } EXPORT_SYMBOL_GPL(amdtp_am824_init); diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 4adbbf789cbe..16c7f6605511 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -14,8 +14,8 @@ #include <linux/tracepoint.h> TRACE_EVENT(amdtp_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index), - TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index), + TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int index), + TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, index), TP_STRUCT__entry( __field(unsigned int, second) __field(unsigned int, cycle) @@ -47,7 +47,7 @@ TRACE_EVENT(amdtp_packet, } __entry->payload_quadlets = payload_length / sizeof(__be32); __entry->data_blocks = data_blocks; - __entry->data_block_counter = s->data_block_counter, + __entry->data_block_counter = data_block_counter, __entry->packet_index = s->packet_index; __entry->irq = !!in_interrupt(); __entry->index = index; diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 4d71d74707cf..e50e28f77e74 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -74,16 +74,16 @@ static void pcm_period_tasklet(unsigned long data); * @dir: the direction of stream * @flags: the packet transmission method to use * @fmt: the value of fmt field in CIP header - * @process_data_blocks: callback handler to process data blocks + * @process_ctx_payloads: callback handler to process payloads of isoc context * @protocol_size: the size to allocate newly for protocol */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags, unsigned int fmt, - amdtp_stream_process_data_blocks_t process_data_blocks, + amdtp_stream_process_ctx_payloads_t process_ctx_payloads, unsigned int protocol_size) { - if (process_data_blocks == NULL) + if (process_ctx_payloads == NULL) return -EINVAL; s->protocol = kzalloc(protocol_size, GFP_KERNEL); @@ -102,7 +102,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->fmt = fmt; - s->process_data_blocks = process_data_blocks; + s->process_ctx_payloads = process_ctx_payloads; + + if (dir == AMDTP_OUT_STREAM) + s->ctx_data.rx.syt_override = -1; return 0; } @@ -473,12 +476,12 @@ static inline int queue_in_packet(struct amdtp_stream *s, } static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], - unsigned int syt) + unsigned int data_block_counter, unsigned int syt) { cip_header[0] = cpu_to_be32(READ_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); + data_block_counter); cip_header[1] = cpu_to_be32(CIP_EOH | ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | ((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | @@ -487,8 +490,9 @@ static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, struct fw_iso_packet *params, - unsigned int data_blocks, unsigned int syt, - unsigned int index) + unsigned int data_blocks, + unsigned int data_block_counter, + unsigned int syt, unsigned int index) { unsigned int payload_length; __be32 *cip_header; @@ -496,14 +500,9 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets; params->payload_length = payload_length; - if (s->flags & CIP_DBC_IS_END_EVENT) { - s->data_block_counter = - (s->data_block_counter + data_blocks) & 0xff; - } - if (!(s->flags & CIP_NO_HEADER)) { cip_header = (__be32 *)params->header; - generate_cip_header(s, cip_header, syt); + generate_cip_header(s, cip_header, data_block_counter, syt); params->header_length = 2 * sizeof(__be32); payload_length += params->header_length; } else { @@ -511,23 +510,19 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, } trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks, - index); - - if (!(s->flags & CIP_DBC_IS_END_EVENT)) { - s->data_block_counter = - (s->data_block_counter + data_blocks) & 0xff; - } + data_block_counter, index); } static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, unsigned int payload_length, - unsigned int *data_blocks, unsigned int *dbc, - unsigned int *syt) + unsigned int *data_blocks, + unsigned int *data_block_counter, unsigned int *syt) { u32 cip_header[2]; unsigned int sph; unsigned int fmt; unsigned int fdf; + unsigned int dbc; bool lost; cip_header[0] = be32_to_cpu(buf[0]); @@ -579,17 +574,16 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, } /* Check data block counter continuity */ - *dbc = cip_header[0] & CIP_DBC_MASK; + dbc = cip_header[0] & CIP_DBC_MASK; if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && - s->data_block_counter != UINT_MAX) - *dbc = s->data_block_counter; + *data_block_counter != UINT_MAX) + dbc = *data_block_counter; - if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && - *dbc == s->ctx_data.tx.first_dbc) || - s->data_block_counter == UINT_MAX) { + if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) || + *data_block_counter == UINT_MAX) { lost = false; } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { - lost = *dbc != s->data_block_counter; + lost = dbc != *data_block_counter; } else { unsigned int dbc_interval; @@ -598,16 +592,18 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, else dbc_interval = *data_blocks; - lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff); + lost = dbc != ((*data_block_counter + dbc_interval) & 0xff); } if (lost) { dev_err(&s->unit->device, "Detect discontinuity of CIP: %02X %02X\n", - s->data_block_counter, *dbc); + *data_block_counter, dbc); return -EIO; } + *data_block_counter = dbc; + *syt = cip_header[1] & CIP_SYT_MASK; return 0; @@ -616,10 +612,10 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, const __be32 *ctx_header, unsigned int *payload_length, - unsigned int *data_blocks, unsigned int *syt, - unsigned int index) + unsigned int *data_blocks, + unsigned int *data_block_counter, + unsigned int *syt, unsigned int index) { - unsigned int dbc; const __be32 *cip_header; int err; @@ -635,7 +631,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, if (!(s->flags & CIP_NO_HEADER)) { cip_header = ctx_header + 2; err = check_cip_header(s, cip_header, *payload_length, - data_blocks, &dbc, syt); + data_blocks, data_block_counter, syt); if (err < 0) return err; } else { @@ -645,16 +641,12 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, s->data_block_quadlets; *syt = 0; - if (s->data_block_counter != UINT_MAX) - dbc = s->data_block_counter; - else - dbc = 0; + if (*data_block_counter == UINT_MAX) + *data_block_counter = 0; } - s->data_block_counter = dbc; - trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks, - index); + *data_block_counter, index); return err; } @@ -686,6 +678,80 @@ static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp) return increment_cycle_count(cycle, QUEUE_LENGTH); } +static int generate_device_pkt_descs(struct amdtp_stream *s, + struct pkt_desc *descs, + const __be32 *ctx_header, + unsigned int packets) +{ + unsigned int dbc = s->data_block_counter; + int i; + int err; + + for (i = 0; i < packets; ++i) { + struct pkt_desc *desc = descs + i; + unsigned int index = (s->packet_index + i) % QUEUE_LENGTH; + unsigned int cycle; + unsigned int payload_length; + unsigned int data_blocks; + unsigned int syt; + + cycle = compute_cycle_count(ctx_header[1]); + + err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length, + &data_blocks, &dbc, &syt, i); + if (err < 0) + return err; + + desc->cycle = cycle; + desc->syt = syt; + desc->data_blocks = data_blocks; + desc->data_block_counter = dbc; + desc->ctx_payload = s->buffer.packets[index].buffer; + + if (!(s->flags & CIP_DBC_IS_END_EVENT)) + dbc = (dbc + desc->data_blocks) & 0xff; + + ctx_header += + s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); + } + + s->data_block_counter = dbc; + + return 0; +} + +static void generate_ideal_pkt_descs(struct amdtp_stream *s, + struct pkt_desc *descs, + const __be32 *ctx_header, + unsigned int packets) +{ + unsigned int dbc = s->data_block_counter; + int i; + + for (i = 0; i < packets; ++i) { + struct pkt_desc *desc = descs + i; + unsigned int index = (s->packet_index + i) % QUEUE_LENGTH; + + desc->cycle = compute_it_cycle(*ctx_header); + desc->syt = calculate_syt(s, desc->cycle); + desc->data_blocks = calculate_data_blocks(s, desc->syt); + + if (s->flags & CIP_DBC_IS_END_EVENT) + dbc = (dbc + desc->data_blocks) & 0xff; + + desc->data_block_counter = dbc; + + if (!(s->flags & CIP_DBC_IS_END_EVENT)) + dbc = (dbc + desc->data_blocks) & 0xff; + + desc->ctx_payload = s->buffer.packets[index].buffer; + + ++ctx_header; + } + + s->data_block_counter = dbc; +} + static inline void cancel_stream(struct amdtp_stream *s) { s->packet_index = -1; @@ -694,6 +760,19 @@ static inline void cancel_stream(struct amdtp_stream *s) WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } +static void process_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets) +{ + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; + + pcm = READ_ONCE(s->pcm); + pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm); + if (pcm) + update_pcm_pointers(s, pcm, pcm_frames); +} + static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) @@ -706,38 +785,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, if (s->packet_index < 0) return; + generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets); + + process_ctx_payloads(s, s->pkt_descs, packets); + for (i = 0; i < packets; ++i) { - u32 cycle; + const struct pkt_desc *desc = s->pkt_descs + i; unsigned int syt; - unsigned int data_blocks; - __be32 *buffer; - unsigned int pcm_frames; struct { struct fw_iso_packet params; __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; } template = { {0}, {0} }; - struct snd_pcm_substream *pcm; - cycle = compute_it_cycle(*ctx_header); - syt = calculate_syt(s, cycle); - data_blocks = calculate_data_blocks(s, syt); - buffer = s->buffer.packets[s->packet_index].buffer; - pcm_frames = s->process_data_blocks(s, buffer, data_blocks, - &syt); + if (s->ctx_data.rx.syt_override < 0) + syt = desc->syt; + else + syt = s->ctx_data.rx.syt_override; - build_it_pkt_header(s, cycle, &template.params, data_blocks, + build_it_pkt_header(s, desc->cycle, &template.params, + desc->data_blocks, desc->data_block_counter, syt, i); if (queue_out_packet(s, &template.params) < 0) { cancel_stream(s); return; } - - pcm = READ_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - ++ctx_header; } fw_iso_context_queue_flush(s->context); @@ -748,8 +820,10 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int i, packets; + unsigned int packets; __be32 *ctx_header = header; + int i; + int err; if (s->packet_index < 0) return; @@ -757,48 +831,23 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, // The number of packets in buffer. packets = header_length / s->ctx_data.tx.ctx_header_size; - for (i = 0; i < packets; i++) { - u32 cycle; - unsigned int payload_length; - unsigned int data_blocks; - unsigned int syt; - __be32 *buffer; - unsigned int pcm_frames = 0; - struct fw_iso_packet params = {0}; - struct snd_pcm_substream *pcm; - int err; - - cycle = compute_cycle_count(ctx_header[1]); - err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length, - &data_blocks, &syt, i); - if (err < 0 && err != -EAGAIN) - break; - - if (err >= 0) { - buffer = s->buffer.packets[s->packet_index].buffer; - pcm_frames = s->process_data_blocks(s, buffer, - data_blocks, &syt); - - if (!(s->flags & CIP_DBC_IS_END_EVENT)) { - s->data_block_counter += data_blocks; - s->data_block_counter &= 0xff; - } + err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets); + if (err < 0) { + if (err != -EAGAIN) { + cancel_stream(s); + return; } - - if (queue_in_packet(s, ¶ms) < 0) - break; - - pcm = READ_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); + } else { + process_ctx_payloads(s, s->pkt_descs, packets); } - /* Queueing error or detecting invalid payload. */ - if (i < packets) { - cancel_stream(s); - return; + for (i = 0; i < packets; ++i) { + struct fw_iso_packet params = {0}; + + if (queue_in_packet(s, ¶ms) < 0) { + cancel_stream(s); + return; + } } fw_iso_context_queue_flush(s->context); @@ -845,7 +894,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI * device can be started. */ -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) { static const struct { unsigned int data_block; @@ -932,6 +981,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) else s->tag = TAG_CIP; + s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs), + GFP_KERNEL); + if (!s->pkt_descs) { + err = -ENOMEM; + goto err_context; + } + s->packet_index = 0; do { struct fw_iso_packet params; @@ -943,7 +999,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) err = queue_out_packet(s, ¶ms); } if (err < 0) - goto err_context; + goto err_pkt_descs; } while (s->packet_index > 0); /* NOTE: TAG1 matches CIP. This just affects in stream. */ @@ -954,12 +1010,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->callbacked = false; err = fw_iso_context_start(s->context, -1, 0, tag); if (err < 0) - goto err_context; + goto err_pkt_descs; mutex_unlock(&s->mutex); return 0; - +err_pkt_descs: + kfree(s->pkt_descs); err_context: fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); @@ -970,7 +1027,6 @@ err_unlock: return err; } -EXPORT_SYMBOL(amdtp_stream_start); /** * amdtp_stream_pcm_pointer - get the PCM buffer position @@ -1041,7 +1097,7 @@ EXPORT_SYMBOL(amdtp_stream_update); * All PCM and MIDI devices of the stream must be stopped before the stream * itself can be stopped. */ -void amdtp_stream_stop(struct amdtp_stream *s) +static void amdtp_stream_stop(struct amdtp_stream *s) { mutex_lock(&s->mutex); @@ -1055,12 +1111,12 @@ void amdtp_stream_stop(struct amdtp_stream *s) fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit); + kfree(s->pkt_descs); s->callbacked = false; mutex_unlock(&s->mutex); } -EXPORT_SYMBOL(amdtp_stream_stop); /** * amdtp_stream_pcm_abort - abort the running PCM device @@ -1078,3 +1134,92 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s) snd_pcm_stop_xrun(pcm); } EXPORT_SYMBOL(amdtp_stream_pcm_abort); + +/** + * amdtp_domain_init - initialize an AMDTP domain structure + * @d: the AMDTP domain to initialize. + */ +int amdtp_domain_init(struct amdtp_domain *d) +{ + INIT_LIST_HEAD(&d->streams); + + return 0; +} +EXPORT_SYMBOL_GPL(amdtp_domain_init); + +/** + * amdtp_domain_destroy - destroy an AMDTP domain structure + * @d: the AMDTP domain to destroy. + */ +void amdtp_domain_destroy(struct amdtp_domain *d) +{ + // At present nothing to do. + return; +} +EXPORT_SYMBOL_GPL(amdtp_domain_destroy); + +/** + * amdtp_domain_add_stream - register isoc context into the domain. + * @d: the AMDTP domain. + * @s: the AMDTP stream. + * @channel: the isochronous channel on the bus. + * @speed: firewire speed code. + */ +int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, + int channel, int speed) +{ + struct amdtp_stream *tmp; + + list_for_each_entry(tmp, &d->streams, list) { + if (s == tmp) + return -EBUSY; + } + + list_add(&s->list, &d->streams); + + s->channel = channel; + s->speed = speed; + + return 0; +} +EXPORT_SYMBOL_GPL(amdtp_domain_add_stream); + +/** + * amdtp_domain_start - start sending packets for isoc context in the domain. + * @d: the AMDTP domain. + */ +int amdtp_domain_start(struct amdtp_domain *d) +{ + struct amdtp_stream *s; + int err = 0; + + list_for_each_entry(s, &d->streams, list) { + err = amdtp_stream_start(s, s->channel, s->speed); + if (err < 0) + break; + } + + if (err < 0) { + list_for_each_entry(s, &d->streams, list) + amdtp_stream_stop(s); + } + + return err; +} +EXPORT_SYMBOL_GPL(amdtp_domain_start); + +/** + * amdtp_domain_stop - stop sending packets for isoc context in the same domain. + * @d: the AMDTP domain to which the isoc contexts belong. + */ +void amdtp_domain_stop(struct amdtp_domain *d) +{ + struct amdtp_stream *s, *next; + + list_for_each_entry_safe(s, next, &d->streams, list) { + list_del(&s->list); + + amdtp_stream_stop(s); + } +} +EXPORT_SYMBOL_GPL(amdtp_domain_stop); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 3942894c11ac..bbbca964b9b4 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -33,6 +33,8 @@ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include * valid EOH. * @CIP_NO_HEADERS: a lack of headers in packets + * @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to + * the value of current SYT_INTERVAL; e.g. initial value is not zero. */ enum cip_flags { CIP_NONBLOCKING = 0x00, @@ -45,6 +47,7 @@ enum cip_flags { CIP_JUMBO_PAYLOAD = 0x40, CIP_HEADER_WITHOUT_EOH = 0x80, CIP_NO_HEADER = 0x100, + CIP_UNALIGHED_DBC = 0x200, }; /** @@ -91,12 +94,20 @@ enum amdtp_stream_direction { AMDTP_IN_STREAM }; +struct pkt_desc { + u32 cycle; + u32 syt; + unsigned int data_blocks; + unsigned int data_block_counter; + __be32 *ctx_payload; +}; + struct amdtp_stream; -typedef unsigned int (*amdtp_stream_process_data_blocks_t)( +typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)( struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt); + const struct pkt_desc *desc, + unsigned int packets, + struct snd_pcm_substream *pcm); struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; @@ -107,6 +118,7 @@ struct amdtp_stream { struct fw_iso_context *context; struct iso_packets_buffer buffer; int packet_index; + struct pkt_desc *pkt_descs; int tag; union { struct { @@ -119,8 +131,6 @@ struct amdtp_stream { // Fixed interval of dbc between previos/current // packets. unsigned int dbc_interval; - // Indicate the value of dbc field in a first packet. - unsigned int first_dbc; } tx; struct { // To calculate CIP data blocks and tstamp. @@ -131,6 +141,7 @@ struct amdtp_stream { // To generate CIP header. unsigned int fdf; + int syt_override; } rx; } ctx_data; @@ -158,13 +169,18 @@ struct amdtp_stream { /* For backends to process data blocks. */ void *protocol; - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; + + // For domain. + int channel; + int speed; + struct list_head list; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags, unsigned int fmt, - amdtp_stream_process_data_blocks_t process_data_blocks, + amdtp_stream_process_ctx_payloads_t process_ctx_payloads, unsigned int protocol_size); void amdtp_stream_destroy(struct amdtp_stream *s); @@ -172,9 +188,7 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int data_block_quadlets); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); -int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); void amdtp_stream_update(struct amdtp_stream *s); -void amdtp_stream_stop(struct amdtp_stream *s); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); @@ -256,4 +270,17 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, msecs_to_jiffies(timeout)) > 0; } +struct amdtp_domain { + struct list_head streams; +}; + +int amdtp_domain_init(struct amdtp_domain *d); +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); +void amdtp_domain_stop(struct amdtp_domain *d); + #endif diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 9e0b689fe34a..356d6ba60959 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -115,6 +115,8 @@ struct snd_bebob { /* For BeBoB version quirk. */ unsigned int version; + + struct amdtp_domain domain; }; static inline int diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 334dc7c96e1d..73fee991bd75 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -445,10 +445,9 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) goto end; } - /* start amdtp stream */ - err = amdtp_stream_start(stream, - conn->resources.channel, - conn->speed); + // start amdtp stream. + err = amdtp_domain_add_stream(&bebob->domain, stream, + conn->resources.channel, conn->speed); end: return err; } @@ -523,7 +522,13 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) return err; } - return 0; + err = amdtp_domain_init(&bebob->domain); + if (err < 0) { + destroy_stream(bebob, &bebob->tx_stream); + destroy_stream(bebob, &bebob->rx_stream); + } + + return err; } static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, @@ -566,9 +571,7 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) if (rate == 0) rate = curr_rate; if (curr_rate != rate) { - amdtp_stream_stop(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); - + amdtp_domain_stop(&bebob->domain); break_both_connections(bebob); cmp_connection_release(&bebob->out_conn); @@ -620,9 +623,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) // packet queueing error or detecting discontinuity if (amdtp_streaming_error(&bebob->rx_stream) || amdtp_streaming_error(&bebob->tx_stream)) { - amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_stop(&bebob->tx_stream); - + amdtp_domain_stop(&bebob->domain); break_both_connections(bebob); } @@ -640,11 +641,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) return err; err = start_stream(bebob, &bebob->rx_stream); - if (err < 0) { - dev_err(&bebob->unit->device, - "fail to run AMDTP master stream:%d\n", err); + if (err < 0) + goto error; + + err = start_stream(bebob, &bebob->tx_stream); + if (err < 0) + goto error; + + err = amdtp_domain_start(&bebob->domain); + if (err < 0) goto error; - } // NOTE: // The firmware customized by M-Audio uses these commands to @@ -660,21 +666,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) } if (!amdtp_stream_wait_callback(&bebob->rx_stream, - CALLBACK_TIMEOUT)) { - err = -ETIMEDOUT; - goto error; - } - } - - if (!amdtp_stream_running(&bebob->tx_stream)) { - err = start_stream(bebob, &bebob->tx_stream); - if (err < 0) { - dev_err(&bebob->unit->device, - "fail to run AMDTP slave stream:%d\n", err); - goto error; - } - - if (!amdtp_stream_wait_callback(&bebob->tx_stream, + CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(&bebob->tx_stream, CALLBACK_TIMEOUT)) { err = -ETIMEDOUT; goto error; @@ -683,8 +676,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) return 0; error: - amdtp_stream_stop(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); + amdtp_domain_stop(&bebob->domain); break_both_connections(bebob); return err; } @@ -692,9 +684,7 @@ error: void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) { if (bebob->substreams_counter == 0) { - amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_stop(&bebob->tx_stream); - + amdtp_domain_stop(&bebob->domain); break_both_connections(bebob); cmp_connection_release(&bebob->out_conn); @@ -708,6 +698,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) */ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob) { + amdtp_domain_destroy(&bebob->domain); + destroy_stream(bebob, &bebob->tx_stream); destroy_stream(bebob, &bebob->rx_stream); } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index a9f0c77734c3..f6a8627ae5a2 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -154,14 +154,10 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, for (i = 0; i < params->count; i++) { reg = cpu_to_be32((u32)-1); if (dir == AMDTP_IN_STREAM) { - amdtp_stream_stop(&dice->tx_stream[i]); - snd_dice_transaction_write_tx(dice, params->size * i + TX_ISOCHRONOUS, ®, sizeof(reg)); } else { - amdtp_stream_stop(&dice->rx_stream[i]); - snd_dice_transaction_write_rx(dice, params->size * i + RX_ISOCHRONOUS, ®, sizeof(reg)); @@ -297,10 +293,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate) if (dice->substreams_counter == 0 || curr_rate != rate) { struct reg_params tx_params, rx_params; + amdtp_domain_stop(&dice->domain); + err = get_register_params(dice, &tx_params, &rx_params); if (err < 0) return err; - finish_session(dice, &tx_params, &rx_params); release_resources(dice); @@ -377,7 +374,8 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, return err; } - err = amdtp_stream_start(stream, resources->channel, max_speed); + err = amdtp_domain_add_stream(&dice->domain, stream, + resources->channel, max_speed); if (err < 0) return err; } @@ -410,6 +408,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) for (i = 0; i < MAX_STREAMS; ++i) { if (amdtp_streaming_error(&dice->tx_stream[i]) || amdtp_streaming_error(&dice->rx_stream[i])) { + amdtp_domain_stop(&dice->domain); finish_session(dice, &tx_params, &rx_params); break; } @@ -456,6 +455,10 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) goto error; } + err = amdtp_domain_start(&dice->domain); + if (err < 0) + goto error; + for (i = 0; i < MAX_STREAMS; i++) { if ((i < tx_params.count && !amdtp_stream_wait_callback(&dice->tx_stream[i], @@ -471,6 +474,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) return 0; error: + amdtp_domain_stop(&dice->domain); finish_session(dice, &tx_params, &rx_params); return err; } @@ -485,8 +489,10 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice) struct reg_params tx_params, rx_params; if (dice->substreams_counter == 0) { - if (get_register_params(dice, &tx_params, &rx_params) >= 0) + if (get_register_params(dice, &tx_params, &rx_params) >= 0) { + amdtp_domain_stop(&dice->domain); finish_session(dice, &tx_params, &rx_params); + } release_resources(dice); } @@ -564,7 +570,15 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice) destroy_stream(dice, AMDTP_OUT_STREAM, i); for (i = 0; i < MAX_STREAMS; i++) destroy_stream(dice, AMDTP_IN_STREAM, i); - break; + goto end; + } + } + + err = amdtp_domain_init(&dice->domain); + if (err < 0) { + for (i = 0; i < MAX_STREAMS; ++i) { + destroy_stream(dice, AMDTP_OUT_STREAM, i); + destroy_stream(dice, AMDTP_IN_STREAM, i); } } end: @@ -579,6 +593,8 @@ void snd_dice_stream_destroy_duplex(struct snd_dice *dice) destroy_stream(dice, AMDTP_IN_STREAM, i); destroy_stream(dice, AMDTP_OUT_STREAM, i); } + + amdtp_domain_destroy(&dice->domain); } void snd_dice_stream_update_duplex(struct snd_dice *dice) @@ -596,6 +612,8 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) dice->global_enabled = false; if (get_register_params(dice, &tx_params, &rx_params) == 0) { + amdtp_domain_stop(&dice->domain); + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); } diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index c6304e5e9fc4..fa6d74303f54 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -112,6 +112,8 @@ struct snd_dice { bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; + + struct amdtp_domain domain; }; enum snd_dice_addr_type { diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index 45ff73d16074..d613642a2ce3 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -143,17 +143,23 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, } static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) + __be32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_dot *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; const u32 *src; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; buffer++; for (i = 0; i < frames; ++i) { @@ -169,17 +175,23 @@ static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, } static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) + __be32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_dot *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; u32 *dst; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; buffer++; for (i = 0; i < frames; ++i) { @@ -234,7 +246,7 @@ static inline void midi_use_bytes(struct amdtp_stream *s, } static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks) + unsigned int data_blocks, unsigned int data_block_counter) { struct amdtp_dot *p = s->protocol; unsigned int f, port; @@ -242,7 +254,7 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, u8 *b; for (f = 0; f < data_blocks; f++) { - port = (s->data_block_counter + f) % 8; + port = (data_block_counter + f) % 8; b = (u8 *)&buffer[0]; len = 0; @@ -329,45 +341,53 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, WRITE_ONCE(p->midi[port], midi); } -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm; - unsigned int pcm_frames; + unsigned int pcm_frames = 0; + int i; - pcm = READ_ONCE(s->pcm); - if (pcm) { - read_pcm_s32(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks; - } else { - pcm_frames = 0; - } + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - read_midi_messages(s, buffer, data_blocks); + if (pcm) { + read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } + + read_midi_messages(s, buf, data_blocks); + } return pcm_frames; } -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm; - unsigned int pcm_frames; + unsigned int pcm_frames = 0; + int i; - pcm = READ_ONCE(s->pcm); - if (pcm) { - write_pcm_s32(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks; - } else { - write_pcm_silence(s, buffer, data_blocks); - pcm_frames = 0; - } + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - write_midi_messages(s, buffer, data_blocks); + if (pcm) { + write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } else { + write_pcm_silence(s, buf, data_blocks); + } + + write_midi_messages(s, buf, data_blocks, + desc->data_block_counter); + } return pcm_frames; } @@ -375,20 +395,20 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir) { - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; enum cip_flags flags; - /* Use different mode between incoming/outgoing. */ + // Use different mode between incoming/outgoing. if (dir == AMDTP_IN_STREAM) { flags = CIP_NONBLOCKING; - process_data_blocks = process_tx_data_blocks; + process_ctx_payloads = process_ir_ctx_payloads; } else { flags = CIP_BLOCKING; - process_data_blocks = process_rx_data_blocks; + process_ctx_payloads = process_it_ctx_payloads; } return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM, - process_data_blocks, sizeof(struct amdtp_dot)); + process_ctx_payloads, sizeof(struct amdtp_dot)); } void amdtp_dot_reset(struct amdtp_stream *s) diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 3e77dbd3ee22..d6a92460060f 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -126,9 +126,6 @@ static void finish_session(struct snd_dg00x *dg00x) { __be32 data; - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - data = cpu_to_be32(0x00000003); snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, @@ -218,29 +215,59 @@ static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream, fw_parent_device(dg00x->unit)->max_speed); } -int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) +static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) { + struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; int err; - /* For out-stream. */ - err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit); + if (s == &dg00x->tx_stream) { + resources = &dg00x->tx_resources; + dir = AMDTP_IN_STREAM; + } else { + resources = &dg00x->rx_resources; + dir = AMDTP_OUT_STREAM; + } + + err = fw_iso_resources_init(resources, dg00x->unit); if (err < 0) - goto error; - err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM); + return err; + + err = amdtp_dot_init(s, dg00x->unit, dir); if (err < 0) - goto error; + fw_iso_resources_destroy(resources); + + return err; +} + +static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) +{ + amdtp_stream_destroy(s); + + if (s == &dg00x->tx_stream) + fw_iso_resources_destroy(&dg00x->tx_resources); + else + fw_iso_resources_destroy(&dg00x->rx_resources); +} + +int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) +{ + int err; - /* For in-stream. */ - err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit); + err = init_stream(dg00x, &dg00x->rx_stream); if (err < 0) - goto error; - err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM); + return err; + + err = init_stream(dg00x, &dg00x->tx_stream); if (err < 0) - goto error; + destroy_stream(dg00x, &dg00x->rx_stream); + + err = amdtp_domain_init(&dg00x->domain); + if (err < 0) { + destroy_stream(dg00x, &dg00x->rx_stream); + destroy_stream(dg00x, &dg00x->tx_stream); + } - return 0; -error: - snd_dg00x_stream_destroy_duplex(dg00x); return err; } @@ -250,11 +277,10 @@ error: */ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) { - amdtp_stream_destroy(&dg00x->rx_stream); - fw_iso_resources_destroy(&dg00x->rx_resources); + amdtp_domain_destroy(&dg00x->domain); - amdtp_stream_destroy(&dg00x->tx_stream); - fw_iso_resources_destroy(&dg00x->tx_resources); + destroy_stream(dg00x, &dg00x->rx_stream); + destroy_stream(dg00x, &dg00x->tx_stream); } int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) @@ -269,6 +295,8 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) rate = curr_rate; if (dg00x->substreams_counter == 0 || curr_rate != rate) { + amdtp_domain_stop(&dg00x->domain); + finish_session(dg00x); fw_iso_resources_free(&dg00x->tx_resources); @@ -301,8 +329,10 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) return 0; if (amdtp_streaming_error(&dg00x->tx_stream) || - amdtp_streaming_error(&dg00x->rx_stream)) + amdtp_streaming_error(&dg00x->rx_stream)) { + amdtp_domain_stop(&dg00x->domain); finish_session(dg00x); + } if (generation != fw_parent_device(dg00x->unit)->card->generation) { err = fw_iso_resources_update(&dg00x->tx_resources); @@ -319,36 +349,30 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) * which source of clock is used. */ if (!amdtp_stream_running(&dg00x->rx_stream)) { + int spd = fw_parent_device(dg00x->unit)->max_speed; + err = begin_session(dg00x); if (err < 0) goto error; - err = amdtp_stream_start(&dg00x->rx_stream, - dg00x->rx_resources.channel, - fw_parent_device(dg00x->unit)->max_speed); + err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream, + dg00x->rx_resources.channel, spd); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&dg00x->rx_stream, - CALLBACK_TIMEOUT)) { - err = -ETIMEDOUT; + err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream, + dg00x->tx_resources.channel, spd); + if (err < 0) goto error; - } - } - /* - * The value of SYT field in transmitted packets is always 0x0000. Thus, - * duplex streams with timestamp synchronization cannot be built. - */ - if (!amdtp_stream_running(&dg00x->tx_stream)) { - err = amdtp_stream_start(&dg00x->tx_stream, - dg00x->tx_resources.channel, - fw_parent_device(dg00x->unit)->max_speed); + err = amdtp_domain_start(&dg00x->domain); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&dg00x->tx_stream, - CALLBACK_TIMEOUT)) { + if (!amdtp_stream_wait_callback(&dg00x->rx_stream, + CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(&dg00x->tx_stream, + CALLBACK_TIMEOUT)) { err = -ETIMEDOUT; goto error; } @@ -356,6 +380,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) return 0; error: + amdtp_domain_stop(&dg00x->domain); finish_session(dg00x); return err; @@ -364,6 +389,7 @@ error: void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) { if (dg00x->substreams_counter == 0) { + amdtp_domain_stop(&dg00x->domain); finish_session(dg00x); fw_iso_resources_free(&dg00x->tx_resources); diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 0994d191ccda..8041c65f2736 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -59,6 +59,8 @@ struct snd_dg00x { /* Console models have additional MIDI ports for control surface. */ bool is_console; + + struct amdtp_domain domain; }; #define DG00X_ADDR_BASE 0xffffe0000000ull diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c index 2938489740b4..119c0076b17a 100644 --- a/sound/firewire/fireface/amdtp-ff.c +++ b/sound/firewire/fireface/amdtp-ff.c @@ -27,19 +27,24 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate, return amdtp_stream_set_parameters(s, rate, data_channels); } -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __le32 *buffer, unsigned int frames) +static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __le32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_ff *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; const u32 *src; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { @@ -52,19 +57,24 @@ static void write_pcm_s32(struct amdtp_stream *s, } } -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __le32 *buffer, unsigned int frames) +static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __le32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_ff *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; u32 *dst; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { @@ -102,38 +112,47 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks); - pcm_frames = data_blocks; - } else { - write_pcm_silence(s, (__le32 *)buffer, data_blocks); - pcm_frames = 0; + unsigned int pcm_frames = 0; + int i; + + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __le32 *buf = (__le32 *)desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + + if (pcm) { + write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } else { + write_pcm_silence(s, buf, data_blocks); + } } return pcm_frames; } -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks); - pcm_frames = data_blocks; - } else { - pcm_frames = 0; + unsigned int pcm_frames = 0; + int i; + + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __le32 *buf = (__le32 *)desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + + if (pcm) { + read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } } return pcm_frames; @@ -142,13 +161,13 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir) { - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; if (dir == AMDTP_IN_STREAM) - process_data_blocks = process_tx_data_blocks; + process_ctx_payloads = process_ir_ctx_payloads; else - process_data_blocks = process_rx_data_blocks; + process_ctx_payloads = process_it_ctx_payloads; return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0, - process_data_blocks, sizeof(struct amdtp_ff)); + process_ctx_payloads, sizeof(struct amdtp_ff)); } diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 4208b8004d1a..e8e6f9fd6433 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -32,61 +32,65 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, static inline void finish_session(struct snd_ff *ff) { - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - ff->spec->protocol->finish_session(ff); ff->spec->protocol->switch_fetching_mode(ff, false); } -static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) +static int init_stream(struct snd_ff *ff, struct amdtp_stream *s) { - int err; struct fw_iso_resources *resources; - struct amdtp_stream *stream; + enum amdtp_stream_direction dir; + int err; - if (dir == AMDTP_IN_STREAM) { + if (s == &ff->tx_stream) { resources = &ff->tx_resources; - stream = &ff->tx_stream; + dir = AMDTP_IN_STREAM; } else { resources = &ff->rx_resources; - stream = &ff->rx_stream; + dir = AMDTP_OUT_STREAM; } err = fw_iso_resources_init(resources, ff->unit); if (err < 0) return err; - err = amdtp_ff_init(stream, ff->unit, dir); + err = amdtp_ff_init(s, ff->unit, dir); if (err < 0) fw_iso_resources_destroy(resources); return err; } -static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) +static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s) { - if (dir == AMDTP_IN_STREAM) { - amdtp_stream_destroy(&ff->tx_stream); + amdtp_stream_destroy(s); + + if (s == &ff->tx_stream) fw_iso_resources_destroy(&ff->tx_resources); - } else { - amdtp_stream_destroy(&ff->rx_stream); + else fw_iso_resources_destroy(&ff->rx_resources); - } } int snd_ff_stream_init_duplex(struct snd_ff *ff) { int err; - err = init_stream(ff, AMDTP_OUT_STREAM); + err = init_stream(ff, &ff->rx_stream); if (err < 0) - goto end; + return err; + + err = init_stream(ff, &ff->tx_stream); + if (err < 0) { + destroy_stream(ff, &ff->rx_stream); + return err; + } + + err = amdtp_domain_init(&ff->domain); + if (err < 0) { + destroy_stream(ff, &ff->rx_stream); + destroy_stream(ff, &ff->tx_stream); + } - err = init_stream(ff, AMDTP_IN_STREAM); - if (err < 0) - destroy_stream(ff, AMDTP_OUT_STREAM); -end: return err; } @@ -96,8 +100,10 @@ end: */ void snd_ff_stream_destroy_duplex(struct snd_ff *ff) { - destroy_stream(ff, AMDTP_IN_STREAM); - destroy_stream(ff, AMDTP_OUT_STREAM); + amdtp_domain_destroy(&ff->domain); + + destroy_stream(ff, &ff->rx_stream); + destroy_stream(ff, &ff->tx_stream); } int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) @@ -114,6 +120,7 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) enum snd_ff_stream_mode mode; int i; + amdtp_domain_stop(&ff->domain); finish_session(ff); fw_iso_resources_free(&ff->tx_resources); @@ -156,51 +163,52 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) return 0; if (amdtp_streaming_error(&ff->tx_stream) || - amdtp_streaming_error(&ff->rx_stream)) + amdtp_streaming_error(&ff->rx_stream)) { + amdtp_domain_stop(&ff->domain); finish_session(ff); + } /* * Regardless of current source of clock signal, drivers transfer some * packets. Then, the device transfers packets. */ if (!amdtp_stream_running(&ff->rx_stream)) { + int spd = fw_parent_device(ff->unit)->max_speed; + err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) goto error; - err = amdtp_stream_start(&ff->rx_stream, - ff->rx_resources.channel, - fw_parent_device(ff->unit)->max_speed); + err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream, + ff->rx_resources.channel, spd); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&ff->rx_stream, - CALLBACK_TIMEOUT_MS)) { - err = -ETIMEDOUT; - goto error; - } - - err = ff->spec->protocol->switch_fetching_mode(ff, true); + err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream, + ff->tx_resources.channel, spd); if (err < 0) goto error; - } - if (!amdtp_stream_running(&ff->tx_stream)) { - err = amdtp_stream_start(&ff->tx_stream, - ff->tx_resources.channel, - fw_parent_device(ff->unit)->max_speed); + err = amdtp_domain_start(&ff->domain); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&ff->tx_stream, + if (!amdtp_stream_wait_callback(&ff->rx_stream, + CALLBACK_TIMEOUT_MS) || + !amdtp_stream_wait_callback(&ff->tx_stream, CALLBACK_TIMEOUT_MS)) { err = -ETIMEDOUT; goto error; } + + err = ff->spec->protocol->switch_fetching_mode(ff, true); + if (err < 0) + goto error; } return 0; error: + amdtp_domain_stop(&ff->domain); finish_session(ff); return err; @@ -209,6 +217,7 @@ error: void snd_ff_stream_stop_duplex(struct snd_ff *ff) { if (ff->substreams_counter == 0) { + amdtp_domain_stop(&ff->domain); finish_session(ff); fw_iso_resources_free(&ff->tx_resources); @@ -218,12 +227,11 @@ void snd_ff_stream_stop_duplex(struct snd_ff *ff) void snd_ff_stream_update_duplex(struct snd_ff *ff) { + amdtp_domain_stop(&ff->domain); + // The device discontinue to transfer packets. amdtp_stream_pcm_abort(&ff->tx_stream); - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_pcm_abort(&ff->rx_stream); - amdtp_stream_stop(&ff->rx_stream); } void snd_ff_stream_lock_changed(struct snd_ff *ff) diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 36dd0c75b9f7..b4c22ca6079e 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -91,6 +91,8 @@ struct snd_ff { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; + + struct amdtp_domain domain; }; enum snd_ff_clock_src { diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 31efd4b53b4f..4cda297f8438 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -107,6 +107,8 @@ struct snd_efw { u8 *resp_buf; u8 *pull_ptr; u8 *push_ptr; + + struct amdtp_domain domain; }; int snd_efw_transaction_cmd(struct fw_unit *unit, diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index e659a0b89ba5..f2de304d2f26 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -8,8 +8,7 @@ #define CALLBACK_TIMEOUT 100 -static int -init_stream(struct snd_efw *efw, struct amdtp_stream *stream) +static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream) { struct cmp_connection *conn; enum cmp_direction c_dir; @@ -28,26 +27,38 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream) err = cmp_connection_init(conn, efw->unit, c_dir, 0); if (err < 0) - goto end; + return err; err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(stream); cmp_connection_destroy(conn); + return err; } -end: - return err; -} -static void -stop_stream(struct snd_efw *efw, struct amdtp_stream *stream) -{ - amdtp_stream_stop(stream); + if (stream == &efw->tx_stream) { + // Fireworks transmits NODATA packets with TAG0. + efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0; + // Fireworks has its own meaning for dbc. + efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT; + // Fireworks reset dbc at bus reset. + efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK; + // But Recent firmwares starts packets with non-zero dbc. + // Driver version 5.7.6 installs firmware version 5.7.3. + if (efw->is_fireworks3 && + (efw->firmware_version == 0x5070000 || + efw->firmware_version == 0x5070300 || + efw->firmware_version == 0x5080000)) + efw->tx_stream.flags |= CIP_UNALIGHED_DBC; + // AudioFire9 always reports wrong dbs. + if (efw->is_af9) + efw->tx_stream.flags |= CIP_WRONG_DBS; + // Firmware version 5.5 reports fixed interval for dbc. + if (efw->firmware_version == 0x5050000) + efw->tx_stream.ctx_data.tx.dbc_interval = 8; + } - if (stream == &efw->tx_stream) - cmp_connection_break(&efw->out_conn); - else - cmp_connection_break(&efw->in_conn); + return err; } static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream, @@ -67,38 +78,26 @@ static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream, return err; // Start amdtp stream. - err = amdtp_stream_start(stream, conn->resources.channel, conn->speed); + err = amdtp_domain_add_stream(&efw->domain, stream, + conn->resources.channel, conn->speed); if (err < 0) { cmp_connection_break(conn); return err; } - // Wait first callback. - if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(stream); - cmp_connection_break(conn); - return -ETIMEDOUT; - } - return 0; } -/* - * This function should be called before starting the stream or after stopping - * the streams. - */ -static void -destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) +// This function should be called before starting the stream or after stopping +// the streams. +static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) { - struct cmp_connection *conn; + amdtp_stream_destroy(stream); if (stream == &efw->tx_stream) - conn = &efw->out_conn; + cmp_connection_destroy(&efw->out_conn); else - conn = &efw->in_conn; - - amdtp_stream_destroy(stream); - cmp_connection_destroy(conn); + cmp_connection_destroy(&efw->in_conn); } static int @@ -131,42 +130,28 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw) err = init_stream(efw, &efw->tx_stream); if (err < 0) - goto end; - /* Fireworks transmits NODATA packets with TAG0. */ - efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0; - /* Fireworks has its own meaning for dbc. */ - efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT; - /* Fireworks reset dbc at bus reset. */ - efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK; - /* - * But Recent firmwares starts packets with non-zero dbc. - * Driver version 5.7.6 installs firmware version 5.7.3. - */ - if (efw->is_fireworks3 && - (efw->firmware_version == 0x5070000 || - efw->firmware_version == 0x5070300 || - efw->firmware_version == 0x5080000)) - efw->tx_stream.ctx_data.tx.first_dbc = 0x02; - /* AudioFire9 always reports wrong dbs. */ - if (efw->is_af9) - efw->tx_stream.flags |= CIP_WRONG_DBS; - /* Firmware version 5.5 reports fixed interval for dbc. */ - if (efw->firmware_version == 0x5050000) - efw->tx_stream.ctx_data.tx.dbc_interval = 8; + return err; err = init_stream(efw, &efw->rx_stream); if (err < 0) { destroy_stream(efw, &efw->tx_stream); - goto end; + return err; + } + + err = amdtp_domain_init(&efw->domain); + if (err < 0) { + destroy_stream(efw, &efw->tx_stream); + destroy_stream(efw, &efw->rx_stream); + return err; } - /* set IEC61883 compliant mode (actually not fully compliant...) */ + // set IEC61883 compliant mode (actually not fully compliant...). err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883); if (err < 0) { destroy_stream(efw, &efw->tx_stream); destroy_stream(efw, &efw->rx_stream); } -end: + return err; } @@ -214,8 +199,10 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate) if (rate == 0) rate = curr_rate; if (rate != curr_rate) { - stop_stream(efw, &efw->tx_stream); - stop_stream(efw, &efw->rx_stream); + amdtp_domain_stop(&efw->domain); + + cmp_connection_break(&efw->out_conn); + cmp_connection_break(&efw->in_conn); cmp_connection_release(&efw->out_conn); cmp_connection_release(&efw->in_conn); @@ -255,47 +242,57 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw) if (efw->substreams_counter == 0) return -EIO; - err = snd_efw_command_get_sampling_rate(efw, &rate); - if (err < 0) - return err; - if (amdtp_streaming_error(&efw->rx_stream) || amdtp_streaming_error(&efw->tx_stream)) { - stop_stream(efw, &efw->rx_stream); - stop_stream(efw, &efw->tx_stream); + amdtp_domain_stop(&efw->domain); + cmp_connection_break(&efw->out_conn); + cmp_connection_break(&efw->in_conn); } - /* master should be always running */ + err = snd_efw_command_get_sampling_rate(efw, &rate); + if (err < 0) + return err; + if (!amdtp_stream_running(&efw->rx_stream)) { err = start_stream(efw, &efw->rx_stream, rate); - if (err < 0) { - dev_err(&efw->unit->device, - "fail to start AMDTP master stream:%d\n", err); + if (err < 0) goto error; - } - } - if (!amdtp_stream_running(&efw->tx_stream)) { err = start_stream(efw, &efw->tx_stream, rate); - if (err < 0) { - dev_err(&efw->unit->device, - "fail to start AMDTP slave stream:%d\n", err); + if (err < 0) + goto error; + + err = amdtp_domain_start(&efw->domain); + if (err < 0) + goto error; + + // Wait first callback. + if (!amdtp_stream_wait_callback(&efw->rx_stream, + CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(&efw->tx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; goto error; } } return 0; error: - stop_stream(efw, &efw->rx_stream); - stop_stream(efw, &efw->tx_stream); + amdtp_domain_stop(&efw->domain); + + cmp_connection_break(&efw->out_conn); + cmp_connection_break(&efw->in_conn); + return err; } void snd_efw_stream_stop_duplex(struct snd_efw *efw) { if (efw->substreams_counter == 0) { - stop_stream(efw, &efw->tx_stream); - stop_stream(efw, &efw->rx_stream); + amdtp_domain_stop(&efw->domain); + + cmp_connection_break(&efw->out_conn); + cmp_connection_break(&efw->in_conn); cmp_connection_release(&efw->out_conn); cmp_connection_release(&efw->in_conn); @@ -304,18 +301,19 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw) void snd_efw_stream_update_duplex(struct snd_efw *efw) { - if (cmp_connection_update(&efw->out_conn) < 0 || - cmp_connection_update(&efw->in_conn) < 0) { - stop_stream(efw, &efw->rx_stream); - stop_stream(efw, &efw->tx_stream); - } else { - amdtp_stream_update(&efw->rx_stream); - amdtp_stream_update(&efw->tx_stream); - } + amdtp_domain_stop(&efw->domain); + + cmp_connection_break(&efw->out_conn); + cmp_connection_break(&efw->in_conn); + + amdtp_stream_pcm_abort(&efw->rx_stream); + amdtp_stream_pcm_abort(&efw->tx_stream); } void snd_efw_stream_destroy_duplex(struct snd_efw *efw) { + amdtp_domain_destroy(&efw->domain); + destroy_stream(efw, &efw->rx_stream); destroy_stream(efw, &efw->tx_stream); } diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index 7973dedd31ef..0fd36e469ad0 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -117,19 +117,25 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, return 0; } -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_runtime *runtime, - __be32 *buffer, unsigned int data_blocks) +static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int data_blocks, + unsigned int pcm_frames) { struct amdtp_motu *p = s->protocol; - unsigned int channels, remaining_frames, i, c; + unsigned int channels = p->pcm_chunks; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int pcm_buffer_pointer; + int remaining_frames; u8 *byte; u32 *dst; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_chunks; dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < data_blocks; ++i) { byte = (u8 *)buffer + p->pcm_byte_offset; @@ -147,19 +153,25 @@ static void read_pcm_s32(struct amdtp_stream *s, } } -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_runtime *runtime, - __be32 *buffer, unsigned int data_blocks) +static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int data_blocks, + unsigned int pcm_frames) { struct amdtp_motu *p = s->protocol; - unsigned int channels, remaining_frames, i, c; + unsigned int channels = p->pcm_chunks; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int pcm_buffer_pointer; + int remaining_frames; u8 *byte; const u32 *src; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_chunks; src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < data_blocks; ++i) { byte = (u8 *)buffer + p->pcm_byte_offset; @@ -298,24 +310,52 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer, } } -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, unsigned int data_blocks, - unsigned int *syt) +static void probe_tracepoints_events(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets) +{ + int i; + + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + + trace_data_block_sph(s, data_blocks, buf); + trace_data_block_message(s, data_blocks, buf); + } +} + +static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { struct amdtp_motu *p = s->protocol; - struct snd_pcm_substream *pcm; + unsigned int pcm_frames = 0; + int i; + + // For data block processing. + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - trace_data_block_sph(s, data_blocks, buffer); - trace_data_block_message(s, data_blocks, buffer); + if (pcm) { + read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } - if (p->midi_ports) - read_midi_messages(s, buffer, data_blocks); + if (p->midi_ports) + read_midi_messages(s, buf, data_blocks); + } - pcm = READ_ONCE(s->pcm); - if (data_blocks > 0 && pcm) - read_pcm_s32(s, pcm->runtime, buffer, data_blocks); + // For tracepoints. + if (trace_data_block_sph_enabled() || + trace_data_block_message_enabled()) + probe_tracepoints_events(s, descs, packets); - return data_blocks; + return pcm_frames; } static inline void compute_next_elapse_from_start(struct amdtp_motu *p) @@ -360,46 +400,55 @@ static void write_sph(struct amdtp_stream *s, __be32 *buffer, } } -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct amdtp_motu *p = (struct amdtp_motu *)s->protocol; - struct snd_pcm_substream *pcm; + struct amdtp_motu *p = s->protocol; + unsigned int pcm_frames = 0; + int i; - /* Not used. */ - *syt = 0xffff; + // For data block processing. + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - /* TODO: how to interact control messages between userspace? */ + if (pcm) { + write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } else { + write_pcm_silence(s, buf, data_blocks); + } - if (p->midi_ports) - write_midi_messages(s, buffer, data_blocks); + if (p->midi_ports) + write_midi_messages(s, buf, data_blocks); - pcm = READ_ONCE(s->pcm); - if (pcm) - write_pcm_s32(s, pcm->runtime, buffer, data_blocks); - else - write_pcm_silence(s, buffer, data_blocks); + // TODO: how to interact control messages between userspace? - write_sph(s, buffer, data_blocks); + write_sph(s, buf, data_blocks); + } - trace_data_block_sph(s, data_blocks, buffer); - trace_data_block_message(s, data_blocks, buffer); + // For tracepoints. + if (trace_data_block_sph_enabled() || + trace_data_block_message_enabled()) + probe_tracepoints_events(s, descs, packets); - return data_blocks; + return pcm_frames; } int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, const struct snd_motu_protocol *const protocol) { - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; int fmt = CIP_FMT_MOTU; int flags = CIP_BLOCKING; int err; if (dir == AMDTP_IN_STREAM) { - process_data_blocks = process_tx_data_blocks; + process_ctx_payloads = process_ir_ctx_payloads; /* * Units of version 3 transmits packets with invalid CIP header @@ -418,17 +467,23 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, CIP_SKIP_DBC_ZERO_CHECK; } } else { - process_data_blocks = process_rx_data_blocks; + process_ctx_payloads = process_it_ctx_payloads; flags |= CIP_DBC_IS_END_EVENT; } - err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks, + err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads, sizeof(struct amdtp_motu)); if (err < 0) return err; s->sph = 1; - s->ctx_data.rx.fdf = MOTU_FDF_AM824; + + if (dir == AMDTP_OUT_STREAM) { + // Use fixed value for FDF field. + s->ctx_data.rx.fdf = MOTU_FDF_AM824; + // Not used. + s->ctx_data.rx.syt_override = 0xffff; + } return 0; } diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 2bbb335e8de1..813e38e6a86e 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -92,9 +92,6 @@ static void finish_session(struct snd_motu *motu) if (err < 0) return; - amdtp_stream_stop(&motu->tx_stream); - amdtp_stream_stop(&motu->rx_stream); - err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®, sizeof(reg)); if (err < 0) @@ -109,27 +106,6 @@ static void finish_session(struct snd_motu *motu) sizeof(reg)); } -static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream) -{ - struct fw_iso_resources *resources; - int err; - - if (stream == &motu->rx_stream) - resources = &motu->rx_resources; - else - resources = &motu->tx_resources; - - err = amdtp_stream_start(stream, resources->channel, - fw_parent_device(motu->unit)->max_speed); - if (err < 0) - return err; - - if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) - return -ETIMEDOUT; - - return 0; -} - int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) { int err; @@ -169,6 +145,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate) rate = curr_rate; if (motu->substreams_counter == 0 || curr_rate != rate) { + amdtp_domain_stop(&motu->domain); finish_session(motu); fw_iso_resources_free(&motu->tx_resources); @@ -234,8 +211,10 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) return 0; if (amdtp_streaming_error(&motu->rx_stream) || - amdtp_streaming_error(&motu->tx_stream)) + amdtp_streaming_error(&motu->tx_stream)) { + amdtp_domain_stop(&motu->domain); finish_session(motu); + } if (generation != fw_parent_device(motu->unit)->card->generation) { err = fw_iso_resources_update(&motu->rx_resources); @@ -248,6 +227,8 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) } if (!amdtp_stream_running(&motu->rx_stream)) { + int spd = fw_parent_device(motu->unit)->max_speed; + err = ensure_packet_formats(motu); if (err < 0) return err; @@ -259,26 +240,32 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) goto stop_streams; } - err = start_isoc_ctx(motu, &motu->rx_stream); - if (err < 0) { - dev_err(&motu->unit->device, - "fail to start IT context: %d\n", err); + err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream, + motu->tx_resources.channel, spd); + if (err < 0) goto stop_streams; - } - err = motu->spec->protocol->switch_fetching_mode(motu, true); - if (err < 0) { - dev_err(&motu->unit->device, - "fail to enable frame fetching: %d\n", err); + err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream, + motu->rx_resources.channel, spd); + if (err < 0) + goto stop_streams; + + err = amdtp_domain_start(&motu->domain); + if (err < 0) + goto stop_streams; + + if (!amdtp_stream_wait_callback(&motu->tx_stream, + CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(&motu->rx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; goto stop_streams; } - } - if (!amdtp_stream_running(&motu->tx_stream)) { - err = start_isoc_ctx(motu, &motu->tx_stream); + err = motu->spec->protocol->switch_fetching_mode(motu, true); if (err < 0) { dev_err(&motu->unit->device, - "fail to start IR context: %d", err); + "fail to enable frame fetching: %d\n", err); goto stop_streams; } } @@ -286,6 +273,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) return 0; stop_streams: + amdtp_domain_stop(&motu->domain); finish_session(motu); return err; } @@ -293,6 +281,7 @@ stop_streams: void snd_motu_stream_stop_duplex(struct snd_motu *motu) { if (motu->substreams_counter == 0) { + amdtp_domain_stop(&motu->domain); finish_session(motu); fw_iso_resources_free(&motu->tx_resources); @@ -300,74 +289,72 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu) } } -static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir) +static int init_stream(struct snd_motu *motu, struct amdtp_stream *s) { - int err; - struct amdtp_stream *stream; struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; + int err; - if (dir == AMDTP_IN_STREAM) { - stream = &motu->tx_stream; + if (s == &motu->tx_stream) { resources = &motu->tx_resources; + dir = AMDTP_IN_STREAM; } else { - stream = &motu->rx_stream; resources = &motu->rx_resources; + dir = AMDTP_OUT_STREAM; } err = fw_iso_resources_init(resources, motu->unit); if (err < 0) return err; - err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol); - if (err < 0) { - amdtp_stream_destroy(stream); + err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol); + if (err < 0) fw_iso_resources_destroy(resources); - } return err; } -static void destroy_stream(struct snd_motu *motu, - enum amdtp_stream_direction dir) +static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s) { - struct amdtp_stream *stream; - struct fw_iso_resources *resources; - - if (dir == AMDTP_IN_STREAM) { - stream = &motu->tx_stream; - resources = &motu->tx_resources; - } else { - stream = &motu->rx_stream; - resources = &motu->rx_resources; - } + amdtp_stream_destroy(s); - amdtp_stream_destroy(stream); - fw_iso_resources_destroy(resources); + if (s == &motu->tx_stream) + fw_iso_resources_destroy(&motu->tx_resources); + else + fw_iso_resources_destroy(&motu->rx_resources); } int snd_motu_stream_init_duplex(struct snd_motu *motu) { int err; - err = init_stream(motu, AMDTP_IN_STREAM); + err = init_stream(motu, &motu->tx_stream); if (err < 0) return err; - err = init_stream(motu, AMDTP_OUT_STREAM); - if (err < 0) - destroy_stream(motu, AMDTP_IN_STREAM); + err = init_stream(motu, &motu->rx_stream); + if (err < 0) { + destroy_stream(motu, &motu->tx_stream); + return err; + } + + err = amdtp_domain_init(&motu->domain); + if (err < 0) { + destroy_stream(motu, &motu->tx_stream); + destroy_stream(motu, &motu->rx_stream); + } return err; } -/* - * This function should be called before starting streams or after stopping - * streams. - */ +// This function should be called before starting streams or after stopping +// streams. void snd_motu_stream_destroy_duplex(struct snd_motu *motu) { - destroy_stream(motu, AMDTP_IN_STREAM); - destroy_stream(motu, AMDTP_OUT_STREAM); + amdtp_domain_destroy(&motu->domain); + + destroy_stream(motu, &motu->rx_stream); + destroy_stream(motu, &motu->tx_stream); motu->substreams_counter = 0; } diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index 03cda2166ea3..72908b4de77c 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -247,6 +247,17 @@ static const struct snd_motu_spec motu_audio_express = { .analog_out_ports = 4, }; +static const struct snd_motu_spec motu_4pre = { + .name = "4pre", + .protocol = &snd_motu_protocol_v3, + .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | + SND_MOTU_SPEC_TX_MICINST_CHUNK | + SND_MOTU_SPEC_TX_RETURN_CHUNK | + SND_MOTU_SPEC_RX_SEPARETED_MAIN, + .analog_in_ports = 2, + .analog_out_ports = 2, +}; + #define SND_MOTU_DEV_ENTRY(model, data) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ @@ -265,6 +276,7 @@ static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */ SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */ SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express), + SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre), { } }; MODULE_DEVICE_TABLE(ieee1394, motu_id_table); diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 09d1451d7de4..350ee2c16f4a 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -69,6 +69,8 @@ struct snd_motu { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; + + struct amdtp_domain domain; }; enum snd_motu_spec_flags { diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 74c972d25c66..3c9a796b6526 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -114,19 +114,13 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) if (err < 0) return err; - err = amdtp_stream_start(stream, conn->resources.channel, conn->speed); + err = amdtp_domain_add_stream(&oxfw->domain, stream, + conn->resources.channel, conn->speed); if (err < 0) { cmp_connection_break(conn); return err; } - // Wait first packet. - if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(stream); - cmp_connection_break(conn); - return -ETIMEDOUT; - } - return 0; } @@ -280,12 +274,12 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, pcm_channels = formation.pcm; } if (formation.rate != rate || formation.pcm != pcm_channels) { - amdtp_stream_stop(&oxfw->rx_stream); + amdtp_domain_stop(&oxfw->domain); + cmp_connection_break(&oxfw->in_conn); cmp_connection_release(&oxfw->in_conn); if (oxfw->has_output) { - amdtp_stream_stop(&oxfw->tx_stream); cmp_connection_break(&oxfw->out_conn); cmp_connection_release(&oxfw->out_conn); } @@ -325,30 +319,46 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) if (amdtp_streaming_error(&oxfw->rx_stream) || amdtp_streaming_error(&oxfw->tx_stream)) { - amdtp_stream_stop(&oxfw->rx_stream); - cmp_connection_break(&oxfw->in_conn); + amdtp_domain_stop(&oxfw->domain); - if (oxfw->has_output) { - amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->in_conn); + if (oxfw->has_output) cmp_connection_break(&oxfw->out_conn); - } } if (!amdtp_stream_running(&oxfw->rx_stream)) { err = start_stream(oxfw, &oxfw->rx_stream); if (err < 0) { dev_err(&oxfw->unit->device, - "fail to start rx stream: %d\n", err); + "fail to prepare rx stream: %d\n", err); goto error; } - } - if (oxfw->has_output) { - if (!amdtp_stream_running(&oxfw->tx_stream)) { + if (oxfw->has_output && + !amdtp_stream_running(&oxfw->tx_stream)) { err = start_stream(oxfw, &oxfw->tx_stream); if (err < 0) { dev_err(&oxfw->unit->device, - "fail to start tx stream: %d\n", err); + "fail to prepare tx stream: %d\n", err); + goto error; + } + } + + err = amdtp_domain_start(&oxfw->domain); + if (err < 0) + goto error; + + // Wait first packet. + if (!amdtp_stream_wait_callback(&oxfw->rx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; + } + + if (oxfw->has_output) { + if (!amdtp_stream_wait_callback(&oxfw->tx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; goto error; } } @@ -356,24 +366,24 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) return 0; error: - amdtp_stream_stop(&oxfw->rx_stream); + amdtp_domain_stop(&oxfw->domain); + cmp_connection_break(&oxfw->in_conn); - if (oxfw->has_output) { - amdtp_stream_stop(&oxfw->tx_stream); + if (oxfw->has_output) cmp_connection_break(&oxfw->out_conn); - } + return err; } void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw) { if (oxfw->substreams_count == 0) { - amdtp_stream_stop(&oxfw->rx_stream); + amdtp_domain_stop(&oxfw->domain); + cmp_connection_break(&oxfw->in_conn); cmp_connection_release(&oxfw->in_conn); if (oxfw->has_output) { - amdtp_stream_stop(&oxfw->tx_stream); cmp_connection_break(&oxfw->out_conn); cmp_connection_release(&oxfw->out_conn); } @@ -409,13 +419,22 @@ int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw) } } - return 0; + err = amdtp_domain_init(&oxfw->domain); + if (err < 0) { + destroy_stream(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + destroy_stream(oxfw, &oxfw->tx_stream); + } + + return err; } // This function should be called before starting the stream or after stopping // the streams. void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) { + amdtp_domain_destroy(&oxfw->domain); + destroy_stream(oxfw, &oxfw->rx_stream); if (oxfw->has_output) @@ -424,13 +443,13 @@ void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw) { - amdtp_stream_stop(&oxfw->rx_stream); + amdtp_domain_stop(&oxfw->domain); + cmp_connection_break(&oxfw->in_conn); amdtp_stream_pcm_abort(&oxfw->rx_stream); if (oxfw->has_output) { - amdtp_stream_stop(&oxfw->tx_stream); cmp_connection_break(&oxfw->out_conn); amdtp_stream_pcm_abort(&oxfw->tx_stream); diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index cb69ab87bb14..c9627b8c5d6e 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -63,6 +63,8 @@ struct snd_oxfw { const struct ieee1394_device_id *entry; void *spec; + + struct amdtp_domain domain; }; /* diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index 95fb10b7a737..e80bb84c43f6 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -32,19 +32,24 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) return amdtp_stream_set_parameters(s, rate, data_channels); } -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_tscm *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; const u32 *src; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { @@ -57,19 +62,24 @@ static void write_pcm_s32(struct amdtp_stream *s, } } -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames, + unsigned int pcm_frames) { struct amdtp_tscm *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; u32 *dst; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - channels = p->pcm_channels; dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; /* The first data channel is for event counter. */ buffer += 1; @@ -165,65 +175,82 @@ static void read_status_messages(struct amdtp_stream *s, } } -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm; + unsigned int pcm_frames = 0; + int i; + + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - pcm = READ_ONCE(s->pcm); - if (data_blocks > 0 && pcm) - read_pcm_s32(s, pcm, buffer, data_blocks); + if (pcm) { + read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } - read_status_messages(s, buffer, data_blocks); + read_status_messages(s, buf, data_blocks); + } - return data_blocks; + return pcm_frames; } -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, + const struct pkt_desc *descs, + unsigned int packets, + struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm; + unsigned int pcm_frames = 0; + int i; - /* This field is not used. */ - *syt = 0x0000; + for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - pcm = READ_ONCE(s->pcm); - if (pcm) - write_pcm_s32(s, pcm, buffer, data_blocks); - else - write_pcm_silence(s, buffer, data_blocks); + if (pcm) { + write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } else { + write_pcm_silence(s, buf, data_blocks); + } + } - return data_blocks; + return pcm_frames; } int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, unsigned int pcm_channels) { - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; struct amdtp_tscm *p; unsigned int fmt; int err; if (dir == AMDTP_IN_STREAM) { fmt = AMDTP_FMT_TSCM_TX; - process_data_blocks = process_tx_data_blocks; + process_ctx_payloads = process_ir_ctx_payloads; } else { fmt = AMDTP_FMT_TSCM_RX; - process_data_blocks = process_rx_data_blocks; + process_ctx_payloads = process_it_ctx_payloads; } err = amdtp_stream_init(s, unit, dir, - CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, - process_data_blocks, sizeof(struct amdtp_tscm)); + CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, + process_ctx_payloads, sizeof(struct amdtp_tscm)); if (err < 0) return 0; - /* Use fixed value for FDF field. */ - s->ctx_data.rx.fdf = 0x00; + if (dir == AMDTP_OUT_STREAM) { + // Use fixed value for FDF field. + s->ctx_data.rx.fdf = 0x00; + // Not used. + s->ctx_data.rx.syt_override = 0x0000; + } /* This protocol uses fixed number of data channels for PCM samples. */ p = s->protocol; diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index ccfa92fbc145..adf69a520b80 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -194,9 +194,6 @@ static void finish_session(struct snd_tscm *tscm) { __be32 reg; - amdtp_stream_stop(&tscm->rx_stream); - amdtp_stream_stop(&tscm->tx_stream); - reg = 0; snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, @@ -301,38 +298,68 @@ static int keep_resources(struct snd_tscm *tscm, unsigned int rate, fw_parent_device(tscm->unit)->max_speed); } -int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) +static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s) { + struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; unsigned int pcm_channels; int err; - /* For out-stream. */ - err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit); - if (err < 0) - return err; - pcm_channels = tscm->spec->pcm_playback_analog_channels; + if (s == &tscm->tx_stream) { + resources = &tscm->tx_resources; + dir = AMDTP_IN_STREAM; + pcm_channels = tscm->spec->pcm_capture_analog_channels; + } else { + resources = &tscm->rx_resources; + dir = AMDTP_OUT_STREAM; + pcm_channels = tscm->spec->pcm_playback_analog_channels; + } + if (tscm->spec->has_adat) pcm_channels += 8; if (tscm->spec->has_spdif) pcm_channels += 2; - err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM, - pcm_channels); + + err = fw_iso_resources_init(resources, tscm->unit); if (err < 0) return err; - /* For in-stream. */ - err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit); + err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels); if (err < 0) - return err; - pcm_channels = tscm->spec->pcm_capture_analog_channels; - if (tscm->spec->has_adat) - pcm_channels += 8; - if (tscm->spec->has_spdif) - pcm_channels += 2; - err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM, - pcm_channels); + fw_iso_resources_free(resources); + + return err; +} + +static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s) +{ + amdtp_stream_destroy(s); + + if (s == &tscm->tx_stream) + fw_iso_resources_destroy(&tscm->tx_resources); + else + fw_iso_resources_destroy(&tscm->rx_resources); +} + +int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) +{ + int err; + + err = init_stream(tscm, &tscm->tx_stream); if (err < 0) - amdtp_stream_destroy(&tscm->rx_stream); + return err; + + err = init_stream(tscm, &tscm->rx_stream); + if (err < 0) { + destroy_stream(tscm, &tscm->tx_stream); + return err; + } + + err = amdtp_domain_init(&tscm->domain); + if (err < 0) { + destroy_stream(tscm, &tscm->tx_stream); + destroy_stream(tscm, &tscm->rx_stream); + } return err; } @@ -340,24 +367,20 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) // At bus reset, streaming is stopped and some registers are clear. void snd_tscm_stream_update_duplex(struct snd_tscm *tscm) { - amdtp_stream_pcm_abort(&tscm->tx_stream); - amdtp_stream_stop(&tscm->tx_stream); + amdtp_domain_stop(&tscm->domain); + amdtp_stream_pcm_abort(&tscm->tx_stream); amdtp_stream_pcm_abort(&tscm->rx_stream); - amdtp_stream_stop(&tscm->rx_stream); } -/* - * This function should be called before starting streams or after stopping - * streams. - */ +// This function should be called before starting streams or after stopping +// streams. void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) { - amdtp_stream_destroy(&tscm->rx_stream); - amdtp_stream_destroy(&tscm->tx_stream); + amdtp_domain_destroy(&tscm->domain); - fw_iso_resources_destroy(&tscm->rx_resources); - fw_iso_resources_destroy(&tscm->tx_resources); + destroy_stream(tscm, &tscm->rx_stream); + destroy_stream(tscm, &tscm->tx_stream); } int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) @@ -370,6 +393,8 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) return err; if (tscm->substreams_counter == 0 || rate != curr_rate) { + amdtp_domain_stop(&tscm->domain); + finish_session(tscm); fw_iso_resources_free(&tscm->tx_resources); @@ -402,8 +427,10 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) return 0; if (amdtp_streaming_error(&tscm->rx_stream) || - amdtp_streaming_error(&tscm->tx_stream)) + amdtp_streaming_error(&tscm->tx_stream)) { + amdtp_domain_stop(&tscm->domain); finish_session(tscm); + } if (generation != fw_parent_device(tscm->unit)->card->generation) { err = fw_iso_resources_update(&tscm->tx_resources); @@ -416,6 +443,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) } if (!amdtp_stream_running(&tscm->rx_stream)) { + int spd = fw_parent_device(tscm->unit)->max_speed; + err = set_stream_formats(tscm, rate); if (err < 0) goto error; @@ -424,27 +453,23 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) goto error; - err = amdtp_stream_start(&tscm->rx_stream, - tscm->rx_resources.channel, - fw_parent_device(tscm->unit)->max_speed); + err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream, + tscm->rx_resources.channel, spd); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&tscm->rx_stream, - CALLBACK_TIMEOUT)) { - err = -ETIMEDOUT; + err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream, + tscm->tx_resources.channel, spd); + if (err < 0) goto error; - } - } - if (!amdtp_stream_running(&tscm->tx_stream)) { - err = amdtp_stream_start(&tscm->tx_stream, - tscm->tx_resources.channel, - fw_parent_device(tscm->unit)->max_speed); + err = amdtp_domain_start(&tscm->domain); if (err < 0) - goto error; + return err; - if (!amdtp_stream_wait_callback(&tscm->tx_stream, + if (!amdtp_stream_wait_callback(&tscm->rx_stream, + CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(&tscm->tx_stream, CALLBACK_TIMEOUT)) { err = -ETIMEDOUT; goto error; @@ -453,6 +478,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) return 0; error: + amdtp_domain_stop(&tscm->domain); finish_session(tscm); return err; @@ -461,6 +487,7 @@ error: void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) { if (tscm->substreams_counter == 0) { + amdtp_domain_stop(&tscm->domain); finish_session(tscm); fw_iso_resources_free(&tscm->tx_resources); diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index 231052db5680..addc464503bc 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -39,6 +39,9 @@ static const struct snd_tscm_spec model_specs[] = { .midi_capture_ports = 2, .midi_playback_ports = 4, }, + // This kernel module doesn't support FE-8 because the most of features + // can be implemented in userspace without any specific support of this + // module. }; static int identify_model(struct snd_tscm *tscm) @@ -214,7 +217,6 @@ static const struct ieee1394_device_id snd_tscm_id_table[] = { .vendor_id = 0x00022e, .specifier_id = 0x00022e, }, - /* FE-08 requires reverse-engineering because it just has faders. */ {} }; MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table); diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 734e5bb9c3da..15bd335fa07f 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -97,6 +97,8 @@ struct snd_tscm { struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT]; unsigned int pull_pos; unsigned int push_pos; + + struct amdtp_domain domain; }; #define TSCM_ADDR_BASE 0xffff00000000ull @@ -127,6 +129,26 @@ struct snd_tscm { #define TSCM_OFFSET_MIDI_RX_QUAD 0x4000 +// Although FE-8 supports the above registers, it has no I/O interfaces for +// audio samples and music messages. Otherwise it supports another notification +// for status and control message as well as LED brightening. The message +// consists of quadlet-aligned data up to 32 quadlets. The first byte of message +// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each +// control: +// fader: 0x00-0x07 +// button: 0x0d, 0x0e +// knob: 0x14-0x1b +// sensing: 0x0b +// +// The rest two bytes represent state of the controls; e.g. current value for +// fader and knob, bitmasks for button and sensing. +// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately +// sent in one transaction. After, several quadlets are sent in one transaction. +// +// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310 +// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314 +// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318 + enum snd_tscm_clock { SND_TSCM_CLOCK_INTERNAL = 0, SND_TSCM_CLOCK_WORD = 1, |