[alsa-devel] [RFC][PATCH 00/17] Enhancement for firewire-lib
This series of patch extend functionalities of current firewire-lib: - support for both in/out AMDTP stream - support for duplex stream synchronization (blocking mode only) - support for both MIDI/PCM stream - support for both in/out CMP - support for some AV/C general commands
Additionally, at the last of this series, some quirks for Fireworks are added.
I'm happy to receive your comments, especially for sort processing.
Takashi Sakamoto (17): firewire-lib: Rename functions, structure, member for both direction firewire-lib: Add macros instead of fixed value for AMDTP firewire-lib: Add 'direction' member to 'amdtp_stream' structure firewire-lib: Split some codes into functions to reuse in future firewire-lib: Add support for AMDTP transmit stream and PCM capture firewire-lib: Add support for MIDI capture/playback firewire-lib: give syt value to handle_out_packet() firewire-lib: Add support for duplex streams with synchronization firewire-lib: Add sort function for transmitted packet firewire-lib: Add transfer delay to synchronized duplex streams firewire-lib: Add support for channel mapping firewire-lib: Rename macros, variables and functions for CMP firewire-lib: Add 'direction' member to 'cmp_connection' structure firewire-lib: Add handling output connection by CMP firewire-lib: Add a new function to check others' connection firewire-lib: Add some AV/C general commands firewire-lib: Add quirks for Fireworks chipset
sound/firewire/amdtp.c | 1014 +++++++++++++++++++++++++++++++++++---------- sound/firewire/amdtp.h | 161 +++++-- sound/firewire/cmp.c | 217 +++++++--- sound/firewire/cmp.h | 14 +- sound/firewire/dice.c | 46 +- sound/firewire/fcp.c | 149 +++++++ sound/firewire/fcp.h | 16 + sound/firewire/speakers.c | 94 ++--- 8 files changed, 1319 insertions(+), 392 deletions(-)
This patch renames some functions, structure and member to reuse them in both AMDTP in/out stream.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 152 +++++++++++++++++++++++----------------------- sound/firewire/amdtp.h | 78 +++++++++++++++--------- sound/firewire/dice.c | 46 +++++++------- sound/firewire/speakers.c | 47 +++++++------- 4 files changed, 172 insertions(+), 151 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 9048777..c9b45a0 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -34,13 +34,13 @@ static void pcm_period_tasklet(unsigned long data);
/** - * amdtp_out_stream_init - initialize an AMDTP output stream structure - * @s: the AMDTP output stream to initialize + * amdtp_stream_init - initialize an AMDTP stream structure + * @s: the AMDTP stream to initialize * @unit: the target of the stream * @flags: the packet transmission method to use */ -int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, - enum cip_out_flags flags) +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum cip_flags flags) { s->unit = fw_unit_get(unit); s->flags = flags; @@ -51,19 +51,19 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
return 0; } -EXPORT_SYMBOL(amdtp_out_stream_init); +EXPORT_SYMBOL(amdtp_stream_init);
/** - * amdtp_out_stream_destroy - free stream resources - * @s: the AMDTP output stream to destroy + * amdtp_stream_destroy - free stream resources + * @s: the AMDTP stream to destroy */ -void amdtp_out_stream_destroy(struct amdtp_out_stream *s) +void amdtp_stream_destroy(struct amdtp_stream *s) { - WARN_ON(amdtp_out_stream_running(s)); + WARN_ON(amdtp_stream_running(s)); mutex_destroy(&s->mutex); fw_unit_put(s->unit); } -EXPORT_SYMBOL(amdtp_out_stream_destroy); +EXPORT_SYMBOL(amdtp_stream_destroy);
const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { [CIP_SFC_32000] = 8, @@ -77,8 +77,8 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { EXPORT_SYMBOL(amdtp_syt_intervals);
/** - * amdtp_out_stream_set_parameters - set stream parameters - * @s: the AMDTP output stream to configure + * amdtp_stream_set_parameters - set stream parameters + * @s: the AMDTP stream to configure * @rate: the sample rate * @pcm_channels: the number of PCM samples in each data block, to be encoded * as AM824 multi-bit linear audio @@ -87,10 +87,10 @@ EXPORT_SYMBOL(amdtp_syt_intervals); * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports) +void amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports) { static const unsigned int rates[] = { [CIP_SFC_32000] = 32000, @@ -103,7 +103,7 @@ void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, }; unsigned int sfc;
- if (WARN_ON(amdtp_out_stream_running(s))) + if (WARN_ON(amdtp_stream_running(s))) return;
for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) @@ -132,47 +132,47 @@ sfc_found: /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; } -EXPORT_SYMBOL(amdtp_out_stream_set_parameters); +EXPORT_SYMBOL(amdtp_stream_set_parameters);
/** - * amdtp_out_stream_get_max_payload - get the stream's packet size - * @s: the AMDTP output stream + * amdtp_stream_get_max_payload - get the stream's packet size + * @s: the AMDTP stream * * This function must not be called before the stream has been configured - * with amdtp_out_stream_set_parameters(). + * with amdtp_stream_set_parameters(). */ -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { return 8 + s->syt_interval * s->data_block_quadlets * 4; } -EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); +EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-static void amdtp_write_s16(struct amdtp_out_stream *s, +static void amdtp_write_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); -static void amdtp_write_s32(struct amdtp_out_stream *s, +static void amdtp_write_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); -static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s16_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); -static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s32_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames);
/** - * amdtp_out_stream_set_pcm_format - set the PCM format - * @s: the AMDTP output stream to configure + * amdtp_stream_set_pcm_format - set the PCM format + * @s: the AMDTP stream to configure * @format: the format of the ALSA PCM device * * The sample format must be set after the other paramters (rate/PCM channels/ * MIDI) and before the stream is started, and must not be changed while the * stream is running. */ -void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, - snd_pcm_format_t format) +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format) { - if (WARN_ON(amdtp_out_stream_running(s))) + if (WARN_ON(amdtp_stream_running(s))) return;
switch (format) { @@ -193,24 +193,24 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, break; } } -EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); +EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
/** - * amdtp_out_stream_pcm_prepare - prepare PCM device for running - * @s: the AMDTP output stream + * amdtp_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP stream * * This function should be called from the PCM device's .prepare callback. */ -void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) +void amdtp_stream_pcm_prepare(struct amdtp_stream *s) { tasklet_kill(&s->period_tasklet); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; s->pointer_flush = true; } -EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare); +EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) +static unsigned int calculate_data_blocks(struct amdtp_stream *s) { unsigned int phase, data_blocks;
@@ -243,7 +243,7 @@ static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) return data_blocks; }
-static unsigned int calculate_syt(struct amdtp_out_stream *s, +static unsigned int calculate_syt(struct amdtp_stream *s, unsigned int cycle) { unsigned int syt_offset, phase, index, syt; @@ -286,7 +286,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, } }
-static void amdtp_write_s32(struct amdtp_out_stream *s, +static void amdtp_write_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -312,7 +312,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s, } }
-static void amdtp_write_s16(struct amdtp_out_stream *s, +static void amdtp_write_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -338,7 +338,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, } }
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s32_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -369,7 +369,7 @@ static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, } }
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, +static void amdtp_write_s16_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -400,7 +400,7 @@ static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, } }
-static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, +static void amdtp_fill_pcm_silence(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { unsigned int i, c; @@ -412,7 +412,7 @@ static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, } }
-static void amdtp_fill_midi(struct amdtp_out_stream *s, +static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { unsigned int i; @@ -422,7 +422,7 @@ static void amdtp_fill_midi(struct amdtp_out_stream *s, cpu_to_be32(0x80000000); }
-static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) +static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; unsigned int index, data_blocks, syt, ptr; @@ -473,7 +473,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) if (err < 0) { dev_err(&s->unit->device, "queueing error: %d\n", err); s->packet_index = -1; - amdtp_out_stream_pcm_abort(s); + amdtp_stream_pcm_abort(s); return; }
@@ -501,7 +501,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
static void pcm_period_tasklet(unsigned long data) { - struct amdtp_out_stream *s = (void *)data; + struct amdtp_stream *s = (void *)data; struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
if (pcm) @@ -509,9 +509,9 @@ static void pcm_period_tasklet(unsigned long data) }
static void out_packet_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, void *data) + size_t header_length, void *header, void *private_data) { - struct amdtp_out_stream *s = data; + struct amdtp_stream *s = private_data; unsigned int i, packets = header_length / 4;
/* @@ -526,7 +526,7 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); }
-static int queue_initial_skip_packets(struct amdtp_out_stream *s) +static int queue_initial_skip_packets(struct amdtp_stream *s) { struct fw_iso_packet skip_packet = { .skip = 1, @@ -548,16 +548,16 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) }
/** - * amdtp_out_stream_start - start sending packets - * @s: the AMDTP output stream to start + * amdtp_stream_start - start sending packets + * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @speed: firewire speed code * * The stream cannot be started until it has been configured with - * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(), + * amdtp_stream_set_parameters() and amdtp_stream_set_pcm_format(), * and it must be started before any PCM or MIDI device can be started. */ -int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) { static const struct { unsigned int data_block; @@ -575,7 +575,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
mutex_lock(&s->mutex);
- if (WARN_ON(amdtp_out_stream_running(s) || + if (WARN_ON(amdtp_stream_running(s) || (!s->pcm_channels && !s->midi_ports))) { err = -EBADFD; goto err_unlock; @@ -586,7 +586,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) s->last_syt_offset = TICKS_PER_CYCLE;
err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_out_stream_get_max_payload(s), + amdtp_stream_get_max_payload(s), DMA_TO_DEVICE); if (err < 0) goto err_unlock; @@ -599,11 +599,11 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) err = PTR_ERR(s->context); if (err == -EBUSY) dev_err(&s->unit->device, - "no free output stream on this controller\n"); + "no free stream on this controller\n"); goto err_buffer; }
- amdtp_out_stream_update(s); + amdtp_stream_update(s);
s->packet_index = 0; s->data_block_counter = 0; @@ -629,15 +629,15 @@ err_unlock:
return err; } -EXPORT_SYMBOL(amdtp_out_stream_start); +EXPORT_SYMBOL(amdtp_stream_start);
/** - * amdtp_out_stream_pcm_pointer - get the PCM buffer position - * @s: the AMDTP output stream that transports the PCM data + * amdtp_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP stream that transports the PCM data * * Returns the current buffer position, in frames. */ -unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) { /* this optimization is allowed to be racy */ if (s->pointer_flush) @@ -647,31 +647,31 @@ unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
return ACCESS_ONCE(s->pcm_buffer_pointer); } -EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer); +EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
/** - * amdtp_out_stream_update - update the stream after a bus reset - * @s: the AMDTP output stream + * amdtp_stream_update - update the stream after a bus reset + * @s: the AMDTP stream */ -void amdtp_out_stream_update(struct amdtp_out_stream *s) +void amdtp_stream_update(struct amdtp_stream *s) { ACCESS_ONCE(s->source_node_id_field) = (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; } -EXPORT_SYMBOL(amdtp_out_stream_update); +EXPORT_SYMBOL(amdtp_stream_update);
/** - * amdtp_out_stream_stop - stop sending packets - * @s: the AMDTP output stream to stop + * amdtp_stream_stop - stop sending packets + * @s: the AMDTP stream to stop * * All PCM and MIDI devices of the stream must be stopped before the stream * itself can be stopped. */ -void amdtp_out_stream_stop(struct amdtp_out_stream *s) +void amdtp_stream_stop(struct amdtp_stream *s) { mutex_lock(&s->mutex);
- if (!amdtp_out_stream_running(s)) { + if (!amdtp_stream_running(s)) { mutex_unlock(&s->mutex); return; } @@ -684,16 +684,16 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
mutex_unlock(&s->mutex); } -EXPORT_SYMBOL(amdtp_out_stream_stop); +EXPORT_SYMBOL(amdtp_stream_stop);
/** - * amdtp_out_stream_pcm_abort - abort the running PCM device + * amdtp_stream_pcm_abort - abort the running PCM device * @s: the AMDTP stream about to be stopped * * If the isochronous stream needs to be stopped asynchronously, call this * function first to stop the PCM device. */ -void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) +void amdtp_stream_pcm_abort(struct amdtp_stream *s) { struct snd_pcm_substream *pcm;
@@ -705,4 +705,4 @@ void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) snd_pcm_stream_unlock_irq(pcm); } } -EXPORT_SYMBOL(amdtp_out_stream_pcm_abort); +EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 2746ecd..757ea6c 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -8,7 +8,7 @@ #include "packets-buffer.h"
/** - * enum cip_out_flags - describes details of the streaming protocol + * enum cip_flags - describes details of the streaming protocol * @CIP_NONBLOCKING: In non-blocking mode, each packet contains * sample_rate/8000 samples, with rounding up or down to adjust * for clock skew and left-over fractional samples. This should @@ -21,7 +21,7 @@ * two samples of a channel are stored consecutively in the packet. * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. */ -enum cip_out_flags { +enum cip_flags { CIP_NONBLOCKING = 0x00, CIP_BLOCKING = 0x01, CIP_HI_DUALWIRE = 0x02, @@ -48,9 +48,9 @@ struct fw_unit; struct fw_iso_context; struct snd_pcm_substream;
-struct amdtp_out_stream { +struct amdtp_stream { struct fw_unit *unit; - enum cip_out_flags flags; + enum cip_flags flags; struct fw_iso_context *context; struct mutex mutex;
@@ -59,7 +59,7 @@ struct amdtp_out_stream { unsigned int data_block_quadlets; unsigned int pcm_channels; unsigned int midi_ports; - void (*transfer_samples)(struct amdtp_out_stream *s, + void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames);
@@ -84,56 +84,76 @@ struct amdtp_out_stream { bool pointer_flush; };
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, - enum cip_out_flags flags); -void amdtp_out_stream_destroy(struct amdtp_out_stream *s); +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum cip_flags flags); +void amdtp_stream_destroy(struct amdtp_stream *s);
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); +void amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); -void amdtp_out_stream_update(struct amdtp_out_stream *s); -void amdtp_out_stream_stop(struct amdtp_out_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);
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, - snd_pcm_format_t format); -void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); -unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); -void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); +void amdtp_stream_pcm_prepare(struct amdtp_stream *s); +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); +void amdtp_stream_pcm_abort(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
-static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) +/** + * amdtp_stream_running - check stream is running or not + * @s: the AMDTP stream + * + * If this function returns true, the stream is running. + */ +static inline bool amdtp_stream_running(struct amdtp_stream *s) { return !IS_ERR(s->context); }
/** - * amdtp_out_streaming_error - check for streaming error - * @s: the AMDTP output stream + * amdtp_stream_set_pcm - configure format of PCM samples + * @s: the AMDTP stream to be configured + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * + * This function must not be called while the stream is running. + */ +static inline void amdtp_stream_set_pcm(struct amdtp_stream *s, + unsigned int pcm_channels) +{ + s->pcm_channels = pcm_channels; +} + +/** + * amdtp_streaming_error - check for streaming error + * @s: the AMDTP stream * * If this function returns true, the stream's packet queue has stopped due to * an asynchronous error. */ -static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s) +static inline bool amdtp_streaming_error(struct amdtp_stream *s) { return s->packet_index < 0; }
/** - * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device - * @s: the AMDTP output stream + * amdtp_stream_pcm_trigger - start/stop playback from a PCM device + * @s: the AMDTP stream * @pcm: the PCM device to be started, or %NULL to stop the current device * * Call this function on a running isochronous stream to enable the actual * transmission of PCM data. This function should be called from the PCM * device's .trigger callback. */ -static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, - struct snd_pcm_substream *pcm) +static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, + struct snd_pcm_substream *pcm) { ACCESS_ONCE(s->pcm) = pcm; } diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 6feee66..c76c519 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -51,7 +51,7 @@ struct dice { wait_queue_head_t hwdep_wait; u32 notification_bits; struct fw_iso_resources resources; - struct amdtp_out_stream stream; + struct amdtp_stream stream; };
MODULE_DESCRIPTION("DICE driver"); @@ -460,17 +460,17 @@ static int dice_stream_start_packets(struct dice *dice) { int err;
- if (amdtp_out_stream_running(&dice->stream)) + if (amdtp_stream_running(&dice->stream)) return 0;
- err = amdtp_out_stream_start(&dice->stream, dice->resources.channel, - fw_parent_device(dice->unit)->max_speed); + err = amdtp_stream_start(&dice->stream, dice->resources.channel, + fw_parent_device(dice->unit)->max_speed); if (err < 0) return err;
err = dice_enable_set(dice); if (err < 0) { - amdtp_out_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->stream); return err; }
@@ -484,7 +484,7 @@ static int dice_stream_start(struct dice *dice)
if (!dice->resources.allocated) { err = fw_iso_resources_allocate(&dice->resources, - amdtp_out_stream_get_max_payload(&dice->stream), + amdtp_stream_get_max_payload(&dice->stream), fw_parent_device(dice->unit)->max_speed); if (err < 0) goto error; @@ -516,9 +516,9 @@ error:
static void dice_stream_stop_packets(struct dice *dice) { - if (amdtp_out_stream_running(&dice->stream)) { + if (amdtp_stream_running(&dice->stream)) { dice_enable_clear(dice); - amdtp_out_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->stream); } }
@@ -581,12 +581,12 @@ static int dice_hw_params(struct snd_pcm_substream *substream, return err;
mode = rate_index_to_mode(rate_index); - amdtp_out_stream_set_parameters(&dice->stream, - params_rate(hw_params), - params_channels(hw_params), - dice->rx_midi_ports[mode]); - amdtp_out_stream_set_pcm_format(&dice->stream, - params_format(hw_params)); + amdtp_stream_set_parameters(&dice->stream, + params_rate(hw_params), + params_channels(hw_params), + dice->rx_midi_ports[mode]); + amdtp_stream_set_pcm_format(&dice->stream, + params_format(hw_params));
return 0; } @@ -609,7 +609,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_lock(&dice->mutex);
- if (amdtp_out_streaming_error(&dice->stream)) + if (amdtp_streaming_error(&dice->stream)) dice_stream_stop_packets(dice);
err = dice_stream_start(dice); @@ -620,7 +620,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_unlock(&dice->mutex);
- amdtp_out_stream_pcm_prepare(&dice->stream); + amdtp_stream_pcm_prepare(&dice->stream);
return 0; } @@ -640,7 +640,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd) default: return -EINVAL; } - amdtp_out_stream_pcm_trigger(&dice->stream, pcm); + amdtp_stream_pcm_trigger(&dice->stream, pcm);
return 0; } @@ -649,7 +649,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) { struct dice *dice = substream->private_data;
- return amdtp_out_stream_pcm_pointer(&dice->stream); + return amdtp_stream_pcm_pointer(&dice->stream); }
static int dice_create_pcm(struct dice *dice) @@ -1104,7 +1104,7 @@ static void dice_card_free(struct snd_card *card) { struct dice *dice = card->private_data;
- amdtp_out_stream_destroy(&dice->stream); + amdtp_stream_destroy(&dice->stream); fw_core_remove_address_handler(&dice->notification_handler); mutex_destroy(&dice->mutex); } @@ -1360,8 +1360,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_owner; dice->resources.channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_out_stream_init(&dice->stream, unit, - CIP_BLOCKING | CIP_HI_DUALWIRE); + err = amdtp_stream_init(&dice->stream, unit, + CIP_BLOCKING | CIP_HI_DUALWIRE); if (err < 0) goto err_resources;
@@ -1417,7 +1417,7 @@ static void dice_remove(struct fw_unit *unit) { struct dice *dice = dev_get_drvdata(&unit->device);
- amdtp_out_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->stream);
snd_card_disconnect(dice->card);
@@ -1443,7 +1443,7 @@ static void dice_bus_reset(struct fw_unit *unit) * to stop so that the application can restart them in an orderly * manner. */ - amdtp_out_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->stream);
mutex_lock(&dice->mutex);
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index cc8bc3a..fc11c0f 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -51,7 +51,8 @@ struct fwspk { const struct device_info *device_info; struct mutex mutex; struct cmp_connection connection; - struct amdtp_out_stream stream; + struct amdtp_stream stream; + bool stream_running; bool mute; s16 volume[6]; s16 volume_min; @@ -187,8 +188,8 @@ static int fwspk_close(struct snd_pcm_substream *substream)
static void fwspk_stop_stream(struct fwspk *fwspk) { - if (amdtp_out_stream_running(&fwspk->stream)) { - amdtp_out_stream_stop(&fwspk->stream); + if (fwspk->stream_running) { + amdtp_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); } } @@ -244,13 +245,13 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto error;
- amdtp_out_stream_set_parameters(&fwspk->stream, - params_rate(hw_params), - params_channels(hw_params), - 0); + amdtp_stream_set_parameters(&fwspk->stream, + params_rate(hw_params), + params_channels(hw_params), + 0);
- amdtp_out_stream_set_pcm_format(&fwspk->stream, - params_format(hw_params)); + amdtp_stream_set_pcm_format(&fwspk->stream, + params_format(hw_params));
err = fwspk_set_rate(fwspk, fwspk->stream.sfc); if (err < 0) @@ -282,25 +283,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
mutex_lock(&fwspk->mutex);
- if (amdtp_out_streaming_error(&fwspk->stream)) + if (amdtp_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk);
- if (!amdtp_out_stream_running(&fwspk->stream)) { + if (!amdtp_stream_running(&fwspk->stream)) { err = cmp_connection_establish(&fwspk->connection, - amdtp_out_stream_get_max_payload(&fwspk->stream)); + amdtp_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex;
- err = amdtp_out_stream_start(&fwspk->stream, - fwspk->connection.resources.channel, - fwspk->connection.speed); + err = amdtp_stream_start(&fwspk->stream, + fwspk->connection.resources.channel, + fwspk->connection.speed); if (err < 0) goto err_connection; }
mutex_unlock(&fwspk->mutex);
- amdtp_out_stream_pcm_prepare(&fwspk->stream); + amdtp_stream_pcm_prepare(&fwspk->stream);
return 0;
@@ -327,7 +328,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) default: return -EINVAL; } - amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); + amdtp_stream_pcm_trigger(&fwspk->stream, pcm); return 0; }
@@ -335,7 +336,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data;
- return amdtp_out_stream_pcm_pointer(&fwspk->stream); + return amdtp_stream_pcm_pointer(&fwspk->stream); }
static int fwspk_create_pcm(struct fwspk *fwspk) @@ -653,7 +654,7 @@ static void fwspk_card_free(struct snd_card *card) { struct fwspk *fwspk = card->private_data;
- amdtp_out_stream_destroy(&fwspk->stream); + amdtp_stream_destroy(&fwspk->stream); cmp_connection_destroy(&fwspk->connection); fw_unit_put(fwspk->unit); mutex_destroy(&fwspk->mutex); @@ -683,7 +684,7 @@ static int fwspk_probe(struct fw_unit *unit, if (err < 0) goto err_unit;
- err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); if (err < 0) goto err_connection;
@@ -733,21 +734,21 @@ static void fwspk_bus_reset(struct fw_unit *unit) fcp_bus_reset(fwspk->unit);
if (cmp_connection_update(&fwspk->connection) < 0) { - amdtp_out_stream_pcm_abort(&fwspk->stream); + amdtp_stream_pcm_abort(&fwspk->stream); mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return; }
- amdtp_out_stream_update(&fwspk->stream); + amdtp_stream_update(&fwspk->stream); }
static void fwspk_remove(struct fw_unit *unit) { struct fwspk *fwspk = dev_get_drvdata(&unit->device);
- amdtp_out_stream_pcm_abort(&fwspk->stream); + amdtp_stream_pcm_abort(&fwspk->stream); snd_card_disconnect(fwspk->card);
mutex_lock(&fwspk->mutex);
This patch adds some macros instead of fixed value for AMDTP in IEC 61883-6. These macros will also be used by followed patches.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index c9b45a0..fbd24db 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -20,12 +20,28 @@
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */
+/* isochronous header parameters */ +#define ISO_DATA_LENGTH_SHIFT 16 #define TAG_CIP 1
+/* common isochronous packet header parameters */ #define CIP_EOH (1u << 31) +#define CIP_EOH_MASK 0x80000000 #define CIP_FMT_AM (0x10 << 24) -#define AMDTP_FDF_AM824 (0 << 19) -#define AMDTP_FDF_SFC_SHIFT 16 +#define CIP_FMT_MASK 0x3f000000 +#define CIP_SYT_MASK 0x0000ffff +#define CIP_SYT_NO_INFO 0xffff +#define CIP_FDF_MASK 0x00ff0000 +#define CIP_FDF_SFC_SHIFT 16 + +/* + * Audio and Music transfer protocol specific parameters + * only "Clock-based rate controll mode" is supported + */ +#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3)) +#define AMDTP_DBS_MASK 0x00ff0000 +#define AMDTP_DBS_SHIFT 16 +#define AMDTP_DBC_MASK 0x000000ff
/* TODO: make these configurable */ #define INTERRUPT_INTERVAL 16 @@ -280,9 +296,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s, syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt += syt_offset % TICKS_PER_CYCLE;
- return syt & 0xffff; + return syt & CIP_SYT_MASK; } else { - return 0xffff; /* no info */ + return CIP_SYT_NO_INFO; /* no info */ } }
@@ -438,17 +454,17 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) syt = calculate_syt(s, cycle); if (!(s->flags & CIP_BLOCKING)) data_blocks = calculate_data_blocks(s); - else if (syt != 0xffff) + else if (syt != CIP_SYT_NO_INFO) data_blocks = s->syt_interval; else data_blocks = 0;
buffer = s->buffer.packets[index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | - (s->data_block_quadlets << 16) | + (s->data_block_quadlets << AMDTP_DBS_SHIFT) | s->data_block_counter); buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | - (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt); + (s->sfc << CIP_FDF_SFC_SHIFT) | syt); buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
Takashi Sakamoto wrote:
This patch adds some macros instead of fixed value for AMDTP in IEC 61883-6. These macros will also be used by followed patches.
+++ b/sound/firewire/amdtp.c
+/*
- Audio and Music transfer protocol specific parameters
- only "Clock-based rate controll mode" is supported
^^^^^^^^ control
return 0xffff; /* no info */
return CIP_SYT_NO_INFO; /* no info */
The comment is now superfluous.
Regards, Clemens
Hi Clemens,
(Nov 26 2013 00:27), Clemens Ladisch wrote:
Takashi Sakamoto wrote:
This patch adds some macros instead of fixed value for AMDTP in IEC 61883-6. These macros will also be used by followed patches.
+++ b/sound/firewire/amdtp.c
+/*
- Audio and Music transfer protocol specific parameters
- only "Clock-based rate controll mode" is supported
^^^^^^^^ control
OK. It's my typo.
return 0xffff; /* no info */
return CIP_SYT_NO_INFO; /* no info */
The comment is now superfluous.
Oops. I forget to remove it when I applied git-rebase to include my patch about empty packet...
Thank you.
Takashi Sakamoto o-takashi@sakamocchi.jp
This patch adds 'direction' member to amdtp_stream structure to indicate its direction. This patch also adds 'direction' argument to amdtp_stream_init() function to determine its direction.
The amdtp_stream_init() function is exported and used by firewire-speakers and dice so this patch also affects them.
This patch just add them. Actual implementation will be done by followed patches.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 4 +++- sound/firewire/amdtp.h | 7 +++++++ sound/firewire/dice.c | 2 +- sound/firewire/speakers.c | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index fbd24db..5d94252 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -53,12 +53,14 @@ static void pcm_period_tasklet(unsigned long data); * amdtp_stream_init - initialize an AMDTP stream structure * @s: the AMDTP stream to initialize * @unit: the target of the stream + * @dir: the direction of stream * @flags: the packet transmission method to use */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum cip_flags flags) + enum amdtp_stream_direction dir, enum cip_flags flags) { s->unit = fw_unit_get(unit); + s->direction = dir; s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 757ea6c..6d7aa36 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -48,9 +48,15 @@ struct fw_unit; struct fw_iso_context; struct snd_pcm_substream;
+enum amdtp_stream_direction { + AMDTP_RECEIVE_STREAM = 0, + AMDTP_TRANSMIT_STREAM +}; + struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; + enum amdtp_stream_direction direction; struct fw_iso_context *context; struct mutex mutex;
@@ -85,6 +91,7 @@ struct amdtp_stream { };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags); void amdtp_stream_destroy(struct amdtp_stream *s);
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index c76c519..d5b01e2 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -1360,7 +1360,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_owner; dice->resources.channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_stream_init(&dice->stream, unit, + err = amdtp_stream_init(&dice->stream, unit, AMDTP_RECEIVE_STREAM, CIP_BLOCKING | CIP_HI_DUALWIRE); if (err < 0) goto err_resources; diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index fc11c0f..8f0257e 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -684,7 +684,8 @@ static int fwspk_probe(struct fw_unit *unit, if (err < 0) goto err_unit;
- err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_RECEIVE_STREAM, + CIP_NONBLOCKING); if (err < 0) goto err_connection;
Takashi Sakamoto wrote:
This patch adds 'direction' member to amdtp_stream structure to indicate its direction.
+++ b/sound/firewire/amdtp.h
+enum amdtp_stream_direction {
- AMDTP_RECEIVE_STREAM = 0,
- AMDTP_TRANSMIT_STREAM
+};
We have two FireWire nodes, the PC and the audio device. Both streams are transmitted by a node and received by a node, so these names does not actually specify a direction.
The DMA API uses names like DMA_FROM/TO_DEVICE for this. Alternatively, you could use something like PLAYBACK/CAPTURE.
Regards, Clemens
(Nov 26 2013 00:27), Clemens Ladisch wrote:
+enum amdtp_stream_direction {
- AMDTP_RECEIVE_STREAM = 0,
- AMDTP_TRANSMIT_STREAM
+};
We have two FireWire nodes, the PC and the audio device. Both streams are transmitted by a node and received by a node, so these names does not actually specify a direction.
The DMA API uses names like DMA_FROM/TO_DEVICE for this. Alternatively, you could use something like PLAYBACK/CAPTURE.
OK. This is related to patch 04. How is to use 'in/out' like this?
enum amdtp_stream_direction { AMDTP_IN_STREAM, AMDTP_OUT_STREAM };
#define IN_PACKET_HEADER_SIZE 4 #define OUT_PACKET_HEADER_SIZE 0
Here I choice 'in/out' because of characters.
Thank you.
Takashi Sakamoto o-takashi@sakamocchi.jp
Some codes can be reused in handling transmitted stream. This commit adds new functions. This commit also renames some functions to keep naming consistency.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 158 ++++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 75 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5d94252..c32d75d 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -47,6 +47,8 @@ #define INTERRUPT_INTERVAL 16 #define QUEUE_LENGTH 48
+#define TRANSMIT_PACKET_HEADER_SIZE 4 + static void pcm_period_tasklet(unsigned long data);
/** @@ -440,13 +442,74 @@ static void amdtp_fill_midi(struct amdtp_stream *s, cpu_to_be32(0x80000000); }
-static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) +static void check_pcm_pointer(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + unsigned int frames) +{ unsigned int ptr; + + if (s->dual_wire) + frames *= 2; + + ptr = s->pcm_buffer_pointer + frames; + if (ptr >= pcm->runtime->buffer_size) + ptr -= pcm->runtime->buffer_size; + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; + + s->pcm_period_pointer += frames; + if (s->pcm_period_pointer >= pcm->runtime->period_size) { + s->pcm_period_pointer -= pcm->runtime->period_size; + s->pointer_flush = false; + tasklet_hi_schedule(&s->period_tasklet); + } +} + +static void pcm_period_tasklet(unsigned long data) +{ + struct amdtp_stream *s = (void *)data; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + + if (pcm) + snd_pcm_period_elapsed(pcm); +} + +static int queue_packet(struct amdtp_stream *s, + unsigned int header_length, + unsigned int payload_length, bool skip) +{ + struct fw_iso_packet p = {0}; + int err; + + p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + p.tag = TAG_CIP; + p.header_length = header_length; + p.payload_length = (!skip) ? payload_length : 0; + p.skip = (!skip) ? 0: 1; + err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, + s->buffer.packets[s->packet_index].offset); + if (err < 0) { + dev_err(&s->unit->device, "queueing error: %d\n", err); + s->packet_index = -1; + goto end; + } + + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; +end: + return err; +} + +static inline int queue_out_packet(struct amdtp_stream *s, + unsigned int payload_length, bool skip) +{ + return queue_packet(s, RECEIVE_PACKET_HEADER_SIZE, + payload_length, skip); +} + +static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; - unsigned int index, data_blocks, syt, ptr; + unsigned int index, data_blocks, syt, payload_length; struct snd_pcm_substream *pcm; - struct fw_iso_packet packet; - int err;
if (s->packet_index < 0) return; @@ -479,55 +542,19 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle)
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
- packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL); - packet.skip = 0; - packet.tag = TAG_CIP; - packet.sy = 0; - packet.header_length = 0; - - err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer, - s->buffer.packets[index].offset); - if (err < 0) { - dev_err(&s->unit->device, "queueing error: %d\n", err); - s->packet_index = -1; + payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + if (queue_out_packet(s, payload_length, false) < 0) { amdtp_stream_pcm_abort(s); return; }
- if (++index >= QUEUE_LENGTH) - index = 0; - s->packet_index = index; - - if (pcm) { - if (s->dual_wire) - data_blocks *= 2; - - ptr = s->pcm_buffer_pointer + data_blocks; - if (ptr >= pcm->runtime->buffer_size) - ptr -= pcm->runtime->buffer_size; - ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; - - s->pcm_period_pointer += data_blocks; - if (s->pcm_period_pointer >= pcm->runtime->period_size) { - s->pcm_period_pointer -= pcm->runtime->period_size; - s->pointer_flush = false; - tasklet_hi_schedule(&s->period_tasklet); - } - } -} - -static void pcm_period_tasklet(unsigned long data) -{ - struct amdtp_stream *s = (void *)data; - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - if (pcm) - snd_pcm_period_elapsed(pcm); + check_pcm_pointer(s, pcm, data_blocks); }
-static void out_packet_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, void *private_data) +static void out_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) { struct amdtp_stream *s = private_data; unsigned int i, packets = header_length / 4; @@ -540,31 +567,10 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle, cycle += QUEUE_LENGTH - packets;
for (i = 0; i < packets; ++i) - queue_out_packet(s, ++cycle); + handle_out_packet(s, ++cycle); fw_iso_context_queue_flush(s->context); }
-static int queue_initial_skip_packets(struct amdtp_stream *s) -{ - struct fw_iso_packet skip_packet = { - .skip = 1, - }; - unsigned int i; - int err; - - for (i = 0; i < QUEUE_LENGTH; ++i) { - skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1, - INTERRUPT_INTERVAL); - err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0); - if (err < 0) - return err; - if (++s->packet_index >= QUEUE_LENGTH) - s->packet_index = 0; - } - - return 0; -} - /** * amdtp_stream_start - start sending packets * @s: the AMDTP stream to start @@ -594,7 +600,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) mutex_lock(&s->mutex);
if (WARN_ON(amdtp_stream_running(s) || - (!s->pcm_channels && !s->midi_ports))) { + (s->data_block_quadlets < 1))) { err = -EBADFD; goto err_unlock; } @@ -612,8 +618,8 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, FW_ISO_CONTEXT_TRANSMIT, channel, speed, 0, - out_packet_callback, s); - if (IS_ERR(s->context)) { + out_stream_callback, s); + if (!amdtp_stream_running(s)) { err = PTR_ERR(s->context); if (err == -EBUSY) dev_err(&s->unit->device, @@ -624,11 +630,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) amdtp_stream_update(s);
s->packet_index = 0; - s->data_block_counter = 0; - err = queue_initial_skip_packets(s); - if (err < 0) - goto err_context; + do { + err = queue_out_packet(s, 0, true); + if (err < 0) + goto err_context; + } while (s->packet_index > 0);
+ s->data_block_counter = 0; err = fw_iso_context_start(s->context, -1, 0, 0); if (err < 0) goto err_context;
Takashi Sakamoto wrote:
Some codes can be reused in handling transmitted stream. This commit adds new functions. This commit also renames some functions to keep naming consistency.
+++ b/sound/firewire/amdtp.c
+#define TRANSMIT_PACKET_HEADER_SIZE 4 ... +static inline int queue_out_packet(struct amdtp_stream *s,
unsigned int payload_length, bool skip)
+{
- return queue_packet(s, RECEIVE_PACKET_HEADER_SIZE,
payload_length, skip);
And this is what I complained about for patch 3.
+static void check_pcm_pointer(struct amdtp_stream *s,
Thw word "check" does not imply that the pointers get changed. Please use something like "update_pcm_pointers".
+static int queue_packet(struct amdtp_stream *s, +{
- p.skip = (!skip) ? 0: 1;
p.skip = skip;
@@ -612,8 +618,8 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, FW_ISO_CONTEXT_TRANSMIT, channel, speed, 0,
out_packet_callback, s);
out_stream_callback, s);
- if (IS_ERR(s->context)) {
- if (!amdtp_stream_running(s)) {
The stream is not yet running at this point, so using this function here would be confusing.
Regards, Clemens
(Nov 26 2013 00:27), Clemens Ladisch wrote:
+static void check_pcm_pointer(struct amdtp_stream *s,
Thw word "check" does not imply that the pointers get changed. Please use something like "update_pcm_pointers".
OK. I apply 'update_pcm_pointers'.
+static int queue_packet(struct amdtp_stream *s, +{
- p.skip = (!skip) ? 0: 1;
p.skip = skip;
@@ -612,8 +618,8 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, FW_ISO_CONTEXT_TRANSMIT, channel, speed, 0,
out_packet_callback, s);
out_stream_callback, s);
- if (IS_ERR(s->context)) {
- if (!amdtp_stream_running(s)) {
The stream is not yet running at this point, so using this function here would be confusing.
OK.
Thank you.
Takashi Sakamoto
For capturing PCM, this commit adds the functionality to handle transmitted stream. This is also applied for dual-wire mode.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 253 insertions(+), 10 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index c32d75d..456eb64 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -48,6 +48,7 @@ #define QUEUE_LENGTH 48
#define TRANSMIT_PACKET_HEADER_SIZE 4 +#define RECEIVE_PACKET_HEADER_SIZE 0
static void pcm_period_tasklet(unsigned long data);
@@ -179,6 +180,18 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, static void amdtp_write_s32_dualwire(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); +static void amdtp_read_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_read_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_read_s16_dualwire(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_read_s32_dualwire(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames);
/** * amdtp_stream_set_pcm_format - set the PCM format @@ -200,13 +213,23 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S16: - if (s->dual_wire) + if (s->direction == AMDTP_TRANSMIT_STREAM) { + if (s->dual_wire) + s->transfer_samples = amdtp_read_s16_dualwire; + else + s->transfer_samples = amdtp_read_s16; + } else if (s->dual_wire) s->transfer_samples = amdtp_write_s16_dualwire; else s->transfer_samples = amdtp_write_s16; break; case SNDRV_PCM_FORMAT_S32: - if (s->dual_wire) + if (s->direction == AMDTP_TRANSMIT_STREAM) { + if (s->dual_wire) + s->transfer_samples = amdtp_read_s32_dualwire; + else + s->transfer_samples = amdtp_read_s32; + } else if (s->dual_wire) s->transfer_samples = amdtp_write_s32_dualwire; else s->transfer_samples = amdtp_write_s32; @@ -420,6 +443,114 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, } }
+static void amdtp_read_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int remaining_frames, i, c; + u32 *dst; + + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) { + *dst = be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void amdtp_read_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int remaining_frames, i, c; + u16 *dst; + + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) { + *dst = be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer +=s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void amdtp_read_s32_dualwire(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = + be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += 1; + for (c = 0; c < channels; ++c) { + *dst = + be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void amdtp_read_s16_dualwire(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u16 *dst; + + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = + be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += 1; + for (c = 0; c < channels; ++c) { + *dst = + be32_to_cpu(buffer[c]) << 8; + dst++; + } + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + static void amdtp_fill_pcm_silence(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -505,6 +636,12 @@ static inline int queue_out_packet(struct amdtp_stream *s, payload_length, skip); }
+static inline int queue_in_packet(struct amdtp_stream *s) +{ + return queue_packet(s, TRANSMIT_PACKET_HEADER_SIZE, + amdtp_stream_get_max_payload(s), false); +} + static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; @@ -552,6 +689,66 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) check_pcm_pointer(s, pcm, data_blocks); }
+static void handle_in_packet(struct amdtp_stream *s, + unsigned int payload_quadlets, + __be32 *buffer) +{ + u32 cip_header[2]; + unsigned int data_block_quadlets, data_blocks, data_block_counter; + struct snd_pcm_substream *pcm; + + cip_header[0] = be32_to_cpu(buffer[0]); + cip_header[1] = be32_to_cpu(buffer[1]); + + /* + * This module supports 'Two-quadlet CIP header with SYT field'. + * For convinience, also check FMT field is AM824 or not. + */ + if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || + ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) || + ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) { + dev_info(&s->unit->device, + "Invalid CIP header for AMDTP: %08X:%08X\n", + cip_header[0], cip_header[1]); + return; + } + + if ((payload_quadlets < 3) || + ((s->flags & CIP_BLOCKING) && + ((cip_header[1] & CIP_SYT_MASK) == CIP_SYT_NO_INFO))) + return; + + data_block_quadlets = + (cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT; + /* avoid division by zero */ + if (data_block_quadlets == 0) { + dev_info(&s->unit->device, + "Detect invalid value in dbs field: %08X\n", + data_block_quadlets); + return; + } + + data_blocks = (payload_quadlets - 2) / data_block_quadlets; + data_block_counter = (cip_header[1] & AMDTP_DBC_MASK); + + /* check packet continuity */ + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + if (s->data_block_counter != data_block_counter) { + dev_err(&s->unit->device, + "Detect uncontinuity of CIP packets\n"); + s->data_block_counter = data_block_counter; + return; + } + + buffer += 2; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + check_pcm_pointer(s, pcm, data_blocks); + } +} + static void out_stream_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *private_data) @@ -571,6 +768,35 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); }
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + unsigned int p, packets, data_quadlets; + __be32 *buffer, *headers = header; + + /* The number of packets in buffer */ + packets = header_length / TRANSMIT_PACKET_HEADER_SIZE; + + for (p = 0; p < packets; p++) { + buffer = s->buffer.packets[s->packet_index].buffer; + + /* The number of quadlets in this packet */ + data_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + /* handle each packet data */ + handle_in_packet(s, data_quadlets, buffer); + + if (queue_in_packet(s) < 0) { + amdtp_stream_pcm_abort(s); + return; + } + } + + fw_iso_context_queue_flush(s->context); +} + /** * amdtp_stream_start - start sending packets * @s: the AMDTP stream to start @@ -595,7 +821,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) [CIP_SFC_88200] = { 0, 67 }, [CIP_SFC_176400] = { 0, 67 }, }; - int err; + unsigned int header_size; + enum dma_data_direction dir; + fw_iso_callback_t cb; + int type, err;
mutex_lock(&s->mutex);
@@ -609,16 +838,26 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->syt_offset_state = initial_state[s->sfc].syt_offset; s->last_syt_offset = TICKS_PER_CYCLE;
+ /* initialize packet buffer */ + if (s->direction == AMDTP_TRANSMIT_STREAM) { + dir = DMA_FROM_DEVICE; + type = FW_ISO_CONTEXT_RECEIVE; + header_size = TRANSMIT_PACKET_HEADER_SIZE; + cb = in_stream_callback; + } else { + dir = DMA_TO_DEVICE; + type = FW_ISO_CONTEXT_TRANSMIT; + header_size = RECEIVE_PACKET_HEADER_SIZE; + cb = out_stream_callback; + } err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), - DMA_TO_DEVICE); + amdtp_stream_get_max_payload(s), dir); if (err < 0) goto err_unlock;
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, - FW_ISO_CONTEXT_TRANSMIT, - channel, speed, 0, - out_stream_callback, s); + type, channel, speed, header_size, + cb, s); if (!amdtp_stream_running(s)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -631,13 +870,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->packet_index = 0; do { - err = queue_out_packet(s, 0, true); + if (s->direction == AMDTP_TRANSMIT_STREAM) + err = queue_in_packet(s); + else + err = queue_out_packet(s, 0, true); if (err < 0) goto err_context; } while (s->packet_index > 0);
+ /* NOTE: TAG1 matches CIP. This just affects in stream */ s->data_block_counter = 0; - err = fw_iso_context_start(s->context, -1, 0, 0); + err = fw_iso_context_start(s->context, -1, 0, FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context;
Takashi Sakamoto wrote:
For capturing PCM, this commit adds the functionality to handle transmitted stream. This is also applied for dual-wire mode.
if (s->dual_wire)
if (s->direction == AMDTP_TRANSMIT_STREAM) {
if (s->dual_wire)
s->transfer_samples = amdtp_read_s16_dualwire;
else
s->transfer_samples = amdtp_read_s16;
else s->transfer_samples = amdtp_write_s16;} else if (s->dual_wire) s->transfer_samples = amdtp_write_s16_dualwire;
It's inconsistent to have braces around the if branch but not around the else branch.
+static void amdtp_read_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
+{
*dst = be32_to_cpu(buffer[c]) << 8;
>> 8 (also in _dualwire)
It might be a good idea to completely drop S16 support.
+static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
+{
- for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*dst =
be32_to_cpu(buffer[c]) << 8;
dst++;
}
buffer += 1;
for (c = 0; c < channels; ++c) {
*dst =
be32_to_cpu(buffer[c]) << 8;
dst++;
}
buffer += s->data_block_quadlets - 1;
This is not correct.
If we assume SYT_INTERVAL = 4, two channels at 192 kHz, and MIDI, the samples L1 R1 L2 R2 L3 R3 ... L8 R8 would be arranged in the data block's quadlets like four channels at 96 kHz, like this:
L1 L4 R1 R4 L2 L5 R2 R5 L3 L6 R3 R6 L4 L7 R4 R8 MIDI
+static void handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets,
__be32 *buffer)
- if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
dev_info(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
If some device sends 'wrong' values for all packets, the log will overflow with these messages. Please use printk_ratelimit().
- if ((payload_quadlets < 3) ||
((s->flags & CIP_BLOCKING) &&
((cip_header[1] & CIP_SYT_MASK) == CIP_SYT_NO_INFO)))
In the general case, we do not know if the stream is blocking or non- blocking, so the driver should always be able to handle (=ignore) no- data packets.
- data_block_counter = (cip_header[1] & AMDTP_DBC_MASK);
These parentheses are not needed.
- s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
- if (s->data_block_counter != data_block_counter) {
dev_err(&s->unit->device,
"Detect uncontinuity of CIP packets\n");
s->data_block_counter = data_block_counter;
return;
- }
The correct thing to do would be to insert the missing samples, or to stop the stream.
Regards, Clemens
(Nov 26 2013 00:27), Clemens Ladisch wrote:
if (s->dual_wire)
if (s->direction == AMDTP_TRANSMIT_STREAM) {
if (s->dual_wire)
s->transfer_samples = amdtp_read_s16_dualwire;
else
s->transfer_samples = amdtp_read_s16;
else s->transfer_samples = amdtp_write_s16;} else if (s->dual_wire) s->transfer_samples = amdtp_write_s16_dualwire;
It's inconsistent to have braces around the if branch but not around the else branch.
OK. I'm sorry but I forget to run checkpatch.pl for this series.
+static void amdtp_read_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
+{
*dst = be32_to_cpu(buffer[c]) << 8;
>> 8
(also in _dualwire)
I also took mistakes of this bit-shift in this patch and patch 11. I should have tested it with S16LE.
It might be a good idea to completely drop S16 support.
Please give me a bit time to consider about this.
+static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
+{
- for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*dst =
be32_to_cpu(buffer[c]) << 8;
dst++;
}
buffer += 1;
for (c = 0; c < channels; ++c) {
*dst =
be32_to_cpu(buffer[c]) << 8;
dst++;
}
buffer += s->data_block_quadlets - 1;
This is not correct.
If we assume SYT_INTERVAL = 4, two channels at 192 kHz, and MIDI, the samples L1 R1 L2 R2 L3 R3 ... L8 R8 would be arranged in the data block's quadlets like four channels at 96 kHz, like this:
L1 L4 R1 R4 L2 L5 R2 R5 L3 L6 R3 R6 L4 L7 R4 R8 MIDI
This is my fault to create patches. These codes will be correctly modified in later patch 11. But I should have notice in this patch...
+static void handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets,
__be32 *buffer)
- if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
dev_info(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
If some device sends 'wrong' values for all packets, the log will overflow with these messages. Please use printk_ratelimit().
OK.
- if ((payload_quadlets < 3) ||
((s->flags & CIP_BLOCKING) &&
((cip_header[1] & CIP_SYT_MASK) == CIP_SYT_NO_INFO)))
In the general case, we do not know if the stream is blocking or non- blocking, so the driver should always be able to handle (=ignore) no- data packets.
OK. And we should consider about AMDTP special non-empty packet for no-data, I should have written like this:
#define AMDTP_FDF_NO_DATA 0xff
/* ignore CIP empty packet or AMDTP NO-DATA packet */ if ((payload_quadlets < 3) || ((cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SFC_SHIFT) == AMDTP_FDF_NO_DATA)))
- data_block_counter = (cip_header[1] & AMDTP_DBC_MASK);
These parentheses are not needed.
OK.
- s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
- if (s->data_block_counter != data_block_counter) {
dev_err(&s->unit->device,
"Detect uncontinuity of CIP packets\n");
s->data_block_counter = data_block_counter;
return;
- }
The correct thing to do would be to insert the missing samples, or to stop the stream.
OK. But I remember there are some devices which transmits packets with 0x0000 in its SYT field (Digidesign 003 Rack, I don't touch its development.). So I want to remove these codes if possible.
Thank you.
Takashi Sakamoto
(Nov 26 2013 19:50), Takashi Sakamoto wrote:
- s->data_block_counter = (s->data_block_counter + data_blocks) &
0xff;
- if (s->data_block_counter != data_block_counter) {
dev_err(&s->unit->device,
"Detect uncontinuity of CIP packets\n");
s->data_block_counter = data_block_counter;
return;
- }
The correct thing to do would be to insert the missing samples, or to stop the stream.
OK. But I remember there are some devices which transmits packets with 0x0000 in its SYT field (Digidesign 003 Rack, I don't touch its development.). So I want to remove these codes if possible.
Oops! I was confused between DBC and SYT. Digidesign 003 Rack transmits DBC correctly. Please forget my reply about this...
Here I think it better to insert the missing samples. Then just output information and continue to handle samples.
Thank you
Takashi Sakamoto
Hi Clemens,
(Nov 26 2013 00:27), Clemens Ladisch wrote:
It might be a good idea to completely drop S16 support.
As long as I investigated some resources given by Echo Audio, there are no way to change PCM format in transmitted packet. This is the same for some documents related to BeBoB.
So I agree with this idea for Fireworks/BeBoB. But I don't know about the other devices.
Regards
Takashi Sakamoto
For capturing/playbacking MIDI message, this commit adds the functionality to multiplex/demultiplex MIDI messages into AMDTP sream in IEC 61883-6. As a result, the number of MIDI ports is limited by 16 ports, for my convinience.
And this commit allows to set PCM format even if AMDTP stream already starts. I suppose the case that PCM data starts to be multiplexed/demultiplexed after AMDTP stream is already started for MIDI. To distinguish whether PCM or MIDI functionality starts AMDTP stream, this commit adds amdtp_stream_pcm_running() and amdtp_stream_midi_running(). I think this is better than reference-counter.
IEC 61883-6 refers to AMEI/MMA RP-027 for implementation of MIDI conformant data. This module implement 'MIDI1.0-1x-SPEED' mode. In the specification, 'MIDI1.0-2x/3x-SPEED' mode is defined with 'negotiation procedure' and 'encapsulation details'. But I cannot find specifications about them.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 76 ++++++++++++++++++++++++++++++++++++++++++-------- sound/firewire/amdtp.h | 30 +++++++++++++++++++- 2 files changed, 94 insertions(+), 12 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 456eb64..ebdaf76 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <sound/pcm.h> +#include <sound/rawmidi.h> #include "amdtp.h"
#define TICKS_PER_CYCLE 3072 @@ -122,9 +123,11 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, [CIP_SFC_176400] = 176400, [CIP_SFC_192000] = 192000, }; - unsigned int sfc; + unsigned int sfc, midi_channels;
- if (WARN_ON(amdtp_stream_running(s))) + midi_channels = DIV_ROUND_UP(midi_ports, 8); + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) return;
for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) @@ -143,7 +146,7 @@ sfc_found: s->sfc = sfc; s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); s->pcm_channels = pcm_channels; - s->midi_ports = midi_ports; + s->midi_channels = midi_channels;
s->syt_interval = amdtp_syt_intervals[sfc];
@@ -205,7 +208,7 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s, void amdtp_stream_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) { - if (WARN_ON(amdtp_stream_running(s))) + if (WARN_ON(amdtp_stream_pcm_running(s))) return;
switch (format) { @@ -566,11 +569,58 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s, static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { - unsigned int i; + unsigned int m, f, c, port; + int len; + u8 b[2]; + + for (f = 0; f < frames; f++) { + m = (s->data_block_counter + f) % 8; + for (c = 0; c < s->midi_channels; c++) { + b[0] = 0x80; + b[1] = 0x00; + len = 0; + + port = c * 8 + m; + if ((s->midi[port] != NULL) && + test_bit(port, &s->midi_triggered)) { + len = snd_rawmidi_transmit(s->midi[port], + b + 1, 1); + if (len <= 0) + b[1] = 0x00; + else + b[0] = 0x81; + } + buffer[s->pcm_channels + c] = + be32_to_cpu((b[0] << 24) | (b[1] << 16)); + } + buffer += s->data_block_quadlets; + } +}
- for (i = 0; i < frames; ++i) - buffer[s->pcm_channels + i * s->data_block_quadlets] = - cpu_to_be32(0x80000000); +static void amdtp_pull_midi(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int m, f, c, port; + int len; + u8 *b; + + for (f = 0; f < frames; f++) { + m = (s->data_block_counter + f) % 8; + for (c = 0; c < s->midi_channels; c++) { + b = (u8 *)&buffer[s->pcm_channels + c]; + if (b[0] < 0x81 || 0x83 < b[0]) + continue; + + port = c * 8 + m; + if ((s->midi[port] == NULL) || + !test_bit(port, &s->midi_triggered)) + continue; + + len = b[0] - 0x80; + snd_rawmidi_receive(s->midi[port], b + 1, len); + } + buffer += s->data_block_quadlets; + } }
static void check_pcm_pointer(struct amdtp_stream *s, @@ -674,7 +724,7 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) s->transfer_samples(s, pcm, buffer, data_blocks); else amdtp_fill_pcm_silence(s, buffer, data_blocks); - if (s->midi_ports) + if (s->midi_channels) amdtp_fill_midi(s, buffer, data_blocks);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; @@ -743,10 +793,14 @@ static void handle_in_packet(struct amdtp_stream *s, buffer += 2;
pcm = ACCESS_ONCE(s->pcm); - if (pcm) { + if (pcm) s->transfer_samples(s, pcm, buffer, data_blocks); + + if (s->midi_channels) + amdtp_pull_midi(s, buffer, data_blocks); + + if (pcm) check_pcm_pointer(s, pcm, data_blocks); - } }
static void out_stream_callback(struct fw_iso_context *context, u32 cycle, diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 6d7aa36..93489d8 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -44,9 +44,17 @@ enum cip_sfc { #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ SNDRV_PCM_FMTBIT_S32)
+/* + * This module supports maximum 2 MIDI channels. + * Then AMDTP packets include maximum 16 MIDI streams multiplexed. + * This is for our convinience. + */ +#define AMDTP_MAX_CHANNELS_FOR_MIDI 2 + struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; +struct snd_rawmidi_substream;
enum amdtp_stream_direction { AMDTP_RECEIVE_STREAM = 0, @@ -64,7 +72,7 @@ struct amdtp_stream { bool dual_wire; unsigned int data_block_quadlets; unsigned int pcm_channels; - unsigned int midi_ports; + unsigned int midi_channels; void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); @@ -88,6 +96,9 @@ struct amdtp_stream { unsigned int pcm_buffer_pointer; unsigned int pcm_period_pointer; bool pointer_flush; + + struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned long midi_triggered; };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -124,6 +135,12 @@ static inline bool amdtp_stream_running(struct amdtp_stream *s) return !IS_ERR(s->context); }
+void amdtp_stream_midi_add(struct amdtp_stream *s, + struct snd_rawmidi_substream *substream); +void amdtp_stream_midi_remove(struct amdtp_stream *s, + struct snd_rawmidi_substream *substream); +bool amdtp_stream_midi_running(struct amdtp_stream *s); + /** * amdtp_stream_set_pcm - configure format of PCM samples * @s: the AMDTP stream to be configured @@ -151,6 +168,17 @@ static inline bool amdtp_streaming_error(struct amdtp_stream *s) }
/** + * amdtp_stream_pcm_running - check PCM stream is running or not + * @s: the AMDTP stream + * + * If this function returns true, PCM stream in the stream is running. + */ +static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s) +{ + return !IS_ERR_OR_NULL(s->pcm); +} + +/** * amdtp_stream_pcm_trigger - start/stop playback from a PCM device * @s: the AMDTP stream * @pcm: the PCM device to be started, or %NULL to stop the current device
Takashi Sakamoto wrote:
For capturing/playbacking MIDI message, this commit adds the functionality to multiplex/demultiplex MIDI messages into AMDTP sream in IEC 61883-6. As a result, the number of MIDI ports is limited by 16 ports, for my convinience.
+++ b/sound/firewire/amdtp.h +/*
- This module supports maximum 2 MIDI channels.
- Then AMDTP packets include maximum 16 MIDI streams multiplexed.
- This is for our convinience.
- */
+#define AMDTP_MAX_CHANNELS_FOR_MIDI 2
This does not look like 16 ports ...
- unsigned int midi_ports;
- unsigned int midi_channels;
... and "MIDI channels" means something different. The RP-027 document defines things like "MIDI data stream", "MIDI Conformant Data Channel", and "MPX-MIDI Data Channel"; please clarify which one you mean.
IEC 61883-6 refers to AMEI/MMA RP-027 for implementation of MIDI conformant data. This module implement 'MIDI1.0-1x-SPEED' mode. In the specification, 'MIDI1.0-2x/3x-SPEED' mode is defined with 'negotiation procedure' and 'encapsulation details'. But I cannot find specifications about them.
The 2x/3x speed modes are not specified.
static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames)
if ((s->midi[port] != NULL) &&
test_bit(port, &s->midi_triggered)) {
The stream pointer (s->midi[]) is not needed when the stream is not active (triggered). So you could drop the midi_triggered field and just update the stream pointer when triggering (like s->pcm).
buffer[s->pcm_channels + c] =
be32_to_cpu((b[0] << 24) | (b[1] << 16));
Endian conversions are required only for values that occupy more than one byte. You should make 'buffer' a byte pointer so that you can copy b[] directly.
Regards, Clemens
(Nov 26 2013 00:27), Clemens Ladisch wrote:
+++ b/sound/firewire/amdtp.h +/*
- This module supports maximum 2 MIDI channels.
- Then AMDTP packets include maximum 16 MIDI streams multiplexed.
- This is for our convinience.
- */
+#define AMDTP_MAX_CHANNELS_FOR_MIDI 2
This does not look like 16 ports ...
- unsigned int midi_ports;
- unsigned int midi_channels;
... and "MIDI channels" means something different. The RP-027 document defines things like "MIDI data stream", "MIDI Conformant Data Channel", and "MPX-MIDI Data Channel"; please clarify which one you mean.
In my understanding of RP-027: 'MIDI data stream' is a data stream to/from each MIDI ports 'MPX-MIDI data channel' is a data channel for each 'MIDI data stream' 'MIDI comformant data channel' has multiplexed 8 'MPX-MIDI data channel's
So I should write:
/* * This module supports maximum 2 MIDI comformant data channels. * Each MIDI comformant data channels include 8 MPX-MIDI data stream. * Each MPX-MIDI data streams includes data stream from/to MIDI ports. * Then AMDTP packets include maximum 16 MIDI streams. * This limitation is for our convinience. */
Is this OK?
IEC 61883-6 refers to AMEI/MMA RP-027 for implementation of MIDI conformant data. This module implement 'MIDI1.0-1x-SPEED' mode. In the specification, 'MIDI1.0-2x/3x-SPEED' mode is defined with 'negotiation procedure' and 'encapsulation details'. But I cannot find specifications about them.
The 2x/3x speed modes are not specified.
OK. I'll add it in comment.
static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames)
if ((s->midi[port] != NULL) &&
test_bit(port, &s->midi_triggered)) {
The stream pointer (s->midi[]) is not needed when the stream is not active (triggered). So you could drop the midi_triggered field and just update the stream pointer when triggering (like s->pcm).
Oh. That's better.
buffer[s->pcm_channels + c] =
be32_to_cpu((b[0] << 24) | (b[1] << 16));
Endian conversions are required only for values that occupy more than one byte. You should make 'buffer' a byte pointer so that you can copy b[] directly.
OK.
Thank you
Takashi Sakamoto
Takashi Sakamoto wrote:
In my understanding of RP-027: 'MIDI data stream' is a data stream to/from each MIDI ports 'MPX-MIDI data channel' is a data channel for each 'MIDI data stream' 'MIDI comformant data channel' has multiplexed 8 'MPX-MIDI data channel's
Yes.
So I should write:
/*
- This module supports maximum 2 MIDI comformant data channels.
Are there devices that use more than one?
- Each MIDI comformant data channels include 8 MPX-MIDI data stream.
Each MIDI comformant data channel includes 8 MPX-MIDI data streams.
- Each MPX-MIDI data streams includes data stream from/to MIDI ports.
Each MPX-MIDI data stream contains the data stream from/to one MIDI port.
Regards, Clemens
(Nov 26 2013 22:02), Clemens Ladisch wrote:
Takashi Sakamoto wrote:
In my understanding of RP-027: 'MIDI data stream' is a data stream to/from each MIDI ports 'MPX-MIDI data channel' is a data channel for each 'MIDI data stream' 'MIDI comformant data channel' has multiplexed 8 'MPX-MIDI data channel's
Yes.
OK. Here I use 'midi_channels' as 'MIDI comformant data channels' for struct amdtp_stream.
/*
- This module supports maximum 2 MIDI comformant data channels.
Are there devices that use more than one?
I know nothing. As long as I know, in BeBoB and Fireworks models, maximum number of 'MIDI comformant data channel' is 1 and 'maximum number of MPX-MIDI data channel' is 2.
Should I limit the number of MIDI comformant data channel by 1?
Thank you
Takashi Sakamoto
Takashi Sakamoto wrote:
(Nov 26 2013 22:02), Clemens Ladisch wrote:
Takashi Sakamoto wrote:
/*
- This module supports maximum 2 MIDI comformant data channels.
Are there devices that use more than one?
I know nothing. As long as I know, in BeBoB and Fireworks models, maximum number of 'MIDI comformant data channel' is 1 and 'maximum number of MPX-MIDI data channel' is 2.
Should I limit the number of MIDI comformant data channel by 1?
If it makes the code simpler.
Regards, Clemens
On Tuesday 26 November 2013 17:10, Takashi Sakamoto wrote:
(Nov 26 2013 22:02), Clemens Ladisch wrote:
Takashi Sakamoto wrote:
In my understanding of RP-027: 'MIDI data stream' is a data stream to/from each MIDI ports 'MPX-MIDI data channel' is a data channel for each 'MIDI data stream' 'MIDI comformant data channel' has multiplexed 8 'MPX-MIDI data channel's
Yes.
OK. Here I use 'midi_channels' as 'MIDI comformant data channels' for struct amdtp_stream.
/*
- This module supports maximum 2 MIDI comformant data channels.
s/ conformant ??
Regards
Alan
(Nov 27 2013 17:33), Alan Horstmann wrote:
/*
- This module supports maximum 2 MIDI comformant data channels.
s/ conformant ??
Yes. It's my bad habit to put 'm' just before 'f' instead of 'n'...
Thank you
Takashi Sakamoto
For duplex streams with synchronization, this driver should pass 'presentation timestamp' from transmit packets to out packets. This commit is preparation for this.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ebdaf76..292403d 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -260,7 +260,9 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s) { unsigned int phase, data_blocks;
- if (!cip_sfc_is_base_44100(s->sfc)) { + if (s->flags & CIP_BLOCKING) + data_blocks = s->syt_interval; + else if (!cip_sfc_is_base_44100(s->sfc)) { /* Sample_rate / 8000 is an integer, and precomputed. */ data_blocks = s->data_block_state; } else { @@ -692,26 +694,22 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); }
-static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) +static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) { __be32 *buffer; - unsigned int index, data_blocks, syt, payload_length; + unsigned int data_blocks, payload_length; struct snd_pcm_substream *pcm;
if (s->packet_index < 0) return; - index = s->packet_index;
/* this module generate empty packet for 'no data' */ - syt = calculate_syt(s, cycle); - if (!(s->flags & CIP_BLOCKING)) + if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO)) data_blocks = calculate_data_blocks(s); - else if (syt != CIP_SYT_NO_INFO) - data_blocks = s->syt_interval; else data_blocks = 0;
- buffer = s->buffer.packets[index].buffer; + buffer = s->buffer.packets[s->packet_index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << AMDTP_DBS_SHIFT) | s->data_block_counter); @@ -808,7 +806,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int i, packets = header_length / 4; + unsigned int i, syt, packets = header_length / 4;
/* * Compute the cycle of the last queued packet. @@ -817,8 +815,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, */ cycle += QUEUE_LENGTH - packets;
- for (i = 0; i < packets; ++i) + for (i = 0; i < packets; ++i) { + syt = calculate_syt(s, ++cycle); handle_out_packet(s, ++cycle); + } fw_iso_context_queue_flush(s->context); }
Takashi Sakamoto wrote:
For duplex streams with synchronization, this driver should pass 'presentation timestamp' from transmit packets to out packets. This commit is preparation for this.
- for (i = 0; i < packets; ++i) {
handle_out_packet(s, ++cycle);syt = calculate_syt(s, ++cycle);
- }
The syt is thrown away, and cycle is incremented twice.
... I'll review the other patches later.
Regards, Clemens
(Nov 26 2013 00:27), Clemens Ladisch wrote:
- for (i = 0; i < packets; ++i) {
handle_out_packet(s, ++cycle);syt = calculate_syt(s, ++cycle);
- }
The syt is thrown away, and cycle is incremented twice.
Yes. This is my fault to create patches...
... I'll review the other patches later.
Thank you
Takashi Sakamoto
Clemens,
(Dec 26 2013 00:27), Clemens Ladisch wrote:
... I'll review the other patches later.
If you don't mind, I want to post updateed patches for firewire-lib/fireworks/bebob. I didn't post them in one time because it's a lot (38 patches). But now they depends each other (firewire-lib <-> each driver, asound.h/firewire.h <-> each drivers).
Is it OK?
Regards
Takashi Sakamoto
Takashi Sakamoto wrote:
(Dec 26 2013 00:27), Clemens Ladisch wrote:
... I'll review the other patches later.
If you don't mind, I want to post updateed patches for firewire-lib/fireworks/bebob. I didn't post them in one time because it's a lot (38 patches). But now they depends each other (firewire-lib <-> each driver, asound.h/firewire.h <-> each drivers).
Is it OK?
Yes; go ahead.
Regards, Clemens
Generally, the devices can synchronize to handle 'presentation timestamp' in CIP packets. This commit adds the functionality to pick up this timestamp in packets transmitted by the device, then use it for out packets.
In previous implementation, this module generated the timestamp by itself. Then it acts as synchronization master. This commit allows this module to act as synchronization slave.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++--- sound/firewire/amdtp.h | 32 +++++++++++-- 2 files changed, 148 insertions(+), 10 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 292403d..6830dd8 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -11,6 +11,7 @@ #include <linux/firewire.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sched.h> #include <sound/pcm.h> #include <sound/rawmidi.h> #include "amdtp.h" @@ -47,6 +48,7 @@ /* TODO: make these configurable */ #define INTERRUPT_INTERVAL 16 #define QUEUE_LENGTH 48 +#define CALLBACK_TIMEOUT_MS 100
#define TRANSMIT_PACKET_HEADER_SIZE 4 #define RECEIVE_PACKET_HEADER_SIZE 0 @@ -71,6 +73,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); s->packet_index = 0;
+ init_waitqueue_head(&s->callback_wait); + s->callbacked = false; + s->sync_slave = ERR_PTR(-1); + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -698,7 +704,7 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) { __be32 *buffer; unsigned int data_blocks, payload_length; - struct snd_pcm_substream *pcm; + struct snd_pcm_substream *pcm = NULL;
if (s->packet_index < 0) return; @@ -817,7 +823,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
for (i = 0; i < packets; ++i) { syt = calculate_syt(s, ++cycle); - handle_out_packet(s, ++cycle); + handle_out_packet(s, syt); } fw_iso_context_queue_flush(s->context); } @@ -827,7 +833,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int p, packets, data_quadlets; + unsigned int p, packets, syt, data_quadlets; __be32 *buffer, *headers = header;
/* The number of packets in buffer */ @@ -839,6 +845,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, /* The number of quadlets in this packet */ data_quadlets = (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + + /* Process sync slave stream */ + if ((s->flags & CIP_BLOCKING) && + (s->flags & CIP_SYNC_TO_DEVICE) && + s->sync_slave->callbacked) { + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + handle_out_packet(s->sync_slave, syt); + } + /* handle each packet data */ handle_in_packet(s, data_quadlets, buffer);
@@ -848,9 +863,43 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, } }
+ /* when sync to device, flush the packets for slave stream */ + if ((s->flags & CIP_BLOCKING) && + (s->flags & CIP_SYNC_TO_DEVICE) && s->sync_slave->callbacked) + fw_iso_context_queue_flush(s->sync_slave->context); + fw_iso_context_queue_flush(s->context); }
+/* processing is done by master callback */ +static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + return; +} + +/* this is executed one time */ +static void amdtp_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + + s->callbacked = true; + + if (s->direction == AMDTP_TRANSMIT_STREAM) + context->callback.sc = in_stream_callback; + else if (!(s->flags & CIP_SYNC_TO_DEVICE)) + context->callback.sc = out_stream_callback; + else + context->callback.sc = slave_stream_callback; + + context->callback.sc(context, cycle, header_length, header, s); + + return; +} + /** * amdtp_stream_start - start sending packets * @s: the AMDTP stream to start @@ -877,7 +926,6 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) }; unsigned int header_size; enum dma_data_direction dir; - fw_iso_callback_t cb; int type, err;
mutex_lock(&s->mutex); @@ -897,12 +945,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) dir = DMA_FROM_DEVICE; type = FW_ISO_CONTEXT_RECEIVE; header_size = TRANSMIT_PACKET_HEADER_SIZE; - cb = in_stream_callback; } else { dir = DMA_TO_DEVICE; type = FW_ISO_CONTEXT_TRANSMIT; header_size = RECEIVE_PACKET_HEADER_SIZE; - cb = out_stream_callback; } err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, amdtp_stream_get_max_payload(s), dir); @@ -911,7 +957,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, header_size, - cb, s); + amdtp_stream_callback, s); if (!amdtp_stream_running(s)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -934,6 +980,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
/* NOTE: TAG1 matches CIP. This just affects in stream */ s->data_block_counter = 0; + s->callbacked = false; err = fw_iso_context_start(s->context, -1, 0, FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context; @@ -1005,6 +1052,8 @@ void amdtp_stream_stop(struct amdtp_stream *s) s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit);
+ s->callbacked = false; + mutex_unlock(&s->mutex); } EXPORT_SYMBOL(amdtp_stream_stop); @@ -1029,3 +1078,66 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s) } } EXPORT_SYMBOL(amdtp_stream_pcm_abort); + +/** + * amdtp_stream_wait_callback - block till callbacked or timeout + * @s: the AMDTP stream + * + * If this function return false, the AMDTP stream should be stopped. + */ +bool amdtp_stream_wait_callback(struct amdtp_stream *s) +{ + wait_event_timeout(s->callback_wait, + s->callbacked == true, + msecs_to_jiffies(CALLBACK_TIMEOUT_MS)); + return s->callbacked; +} +EXPORT_SYMBOL(amdtp_stream_wait_callback); + +/** + * amdtp_stream_midi_add - add MIDI stream + * @s: the AMDTP stream + * @substream: the MIDI stream to be added + * + * This function don't check the number of midi substream but it should be + * within AMDTP_MAX_MIDI_STREAMS. + */ +void amdtp_stream_midi_add(struct amdtp_stream *s, + struct snd_rawmidi_substream *substream) +{ + ACCESS_ONCE(s->midi[substream->number]) = substream; +} +EXPORT_SYMBOL(amdtp_stream_midi_add); + +/** + * amdtp_stream_midi_remove - remove MIDI stream + * @s: the AMDTP stream + * @substream: the MIDI stream to be removed + * + * This function should not be automatically called by amdtp_stream_stop + * because the AMDTP stream only with MIDI stream need to be restarted by + * PCM streams at requested sampling rate. + */ +void amdtp_stream_midi_remove(struct amdtp_stream *s, + struct snd_rawmidi_substream *substream) +{ + ACCESS_ONCE(s->midi[substream->number]) = NULL; +} +EXPORT_SYMBOL(amdtp_stream_midi_remove); + +/** + * amdtp_stream_midi_running - check any MIDI streams are running or not + * @s: the AMDTP stream + * + * If this function returns true, any MIDI streams are running. + */ +bool amdtp_stream_midi_running(struct amdtp_stream *s) +{ + int i; + for (i = 0; i < AMDTP_MAX_CHANNELS_FOR_MIDI * 8; i++) + if (!IS_ERR_OR_NULL(s->midi[i])) + return true; + + return false; +} +EXPORT_SYMBOL(amdtp_stream_midi_running); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 93489d8..a2a7818 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -20,11 +20,14 @@ * at half the actual sample rate with twice the number of channels; * two samples of a channel are stored consecutively in the packet. * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. + * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is + * generated by in packets. Defaultly this driver generates timestamp. */ enum cip_flags { - CIP_NONBLOCKING = 0x00, - CIP_BLOCKING = 0x01, - CIP_HI_DUALWIRE = 0x02, + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, + CIP_HI_DUALWIRE = 0x02, + CIP_SYNC_TO_DEVICE = 0x04 };
/** @@ -99,6 +102,10 @@ struct amdtp_stream {
struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; unsigned long midi_triggered; + + bool callbacked; + wait_queue_head_t callback_wait; + struct amdtp_stream *sync_slave; };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -121,6 +128,7 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); +bool amdtp_stream_wait_callback(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
@@ -198,4 +206,22 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) return sfc & 1; }
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, + struct amdtp_stream *master, + struct amdtp_stream *slave) +{ + /* clear sync flag */ + master->flags &= ~CIP_SYNC_TO_DEVICE; + slave->flags &= ~CIP_SYNC_TO_DEVICE; + + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master->flags |= CIP_SYNC_TO_DEVICE; + slave->flags |= CIP_SYNC_TO_DEVICE; + master->sync_slave = slave; + } else + master->sync_slave = ERR_PTR(-1); + + slave->sync_slave = ERR_PTR(-1); +} + #endif
The aim of this commit is to assure the continuity of timestamp in received packets. Fireworks with firmware version 5.5 or former is a type of device which transmits packets with a bit disorder. For example,
Index Payload CIP header 1 CIP header 2 023 210 3F1100F8 900478E9 024 002 3F1100F8 90FFFFFF 025 210 3F110000 900490E9 026 210 3F110008 9004A4E9 027 210 3F110010 9004B8E9 028 002 3F110010 90FFFFFF 029 210 3F110020 9004E4E8 <- 030 210 3F110018 9004D0E8 <- 031 210 3F110028 9004F8E8 032 002 3F110028 90FFFFFF 033 210 3F110030 900410E8 034 210 3F110038 900424E8 035 210 3F110040 900438E8 036 002 3F110040 90FFFFFF 037 210 3F110050 900464E8 <- 038 210 3F110048 900450E8 <- 039 210 3F110058 900478E8 040 002 3F110058 90FFFFFF 041 210 3F110068 9004A4E8 <- 042 210 3F110060 900490E8 <- 043 210 3F110070 9004B8E8 044 002 3F110070 90FFFFFF 045 210 3F110080 9004E4E7 <- 046 210 3F110078 9004D0E8 <-
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 130 +++++++++++++++++++++++++++++++++++++++++++------ sound/firewire/amdtp.h | 4 ++ 2 files changed, 119 insertions(+), 15 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 6830dd8..d02ccac 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -44,6 +44,7 @@ #define AMDTP_DBS_MASK 0x00ff0000 #define AMDTP_DBS_SHIFT 16 #define AMDTP_DBC_MASK 0x000000ff +#define DBC_THREADSHOULD (AMDTP_DBC_MASK / 2)
/* TODO: make these configurable */ #define INTERRUPT_INTERVAL 16 @@ -53,6 +54,13 @@ #define TRANSMIT_PACKET_HEADER_SIZE 4 #define RECEIVE_PACKET_HEADER_SIZE 0
+/* for re-ordering receive packets */ +struct sort_table { + unsigned int id; + unsigned int dbc; + unsigned int payload_size; +}; + static void pcm_period_tasklet(unsigned long data);
/** @@ -77,6 +85,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = ERR_PTR(-1);
+ s->sort_table = NULL; + s->left_packets = NULL; + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -807,6 +818,44 @@ static void handle_in_packet(struct amdtp_stream *s, check_pcm_pointer(s, pcm, data_blocks); }
+#define SWAP(tbl, m, n) \ + t = tbl[n].id; \ + tbl[n].id = tbl[m].id; \ + tbl[m].id = t; \ + t = tbl[n].dbc; \ + tbl[n].dbc = tbl[m].dbc; \ + tbl[m].dbc = t; \ + t = tbl[n].payload_size; \ + tbl[n].payload_size = tbl[m].payload_size; \ + tbl[m].payload_size = t; +static void packet_sort(struct sort_table *tbl, unsigned int len) +{ + unsigned int i, j, k, t; + + i = 0; + do { + for (j = i + 1; j < len; j++) { + if (((tbl[i].dbc > tbl[j].dbc) && + (tbl[i].dbc - tbl[j].dbc < DBC_THREADSHOULD))) { + SWAP(tbl, i, j); + } else if ((tbl[j].dbc > tbl[i].dbc) && + (tbl[j].dbc - tbl[i].dbc > + DBC_THREADSHOULD)) { + for (k = i; k > 0; k--) { + if ((tbl[k].dbc > tbl[j].dbc) || + (tbl[j].dbc - tbl[k].dbc > + DBC_THREADSHOULD)) { + SWAP(tbl, j, k); + } + break; + } + } + break; + } + i = j; + } while (i < len); +} + static void out_stream_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *private_data) @@ -833,30 +882,65 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int p, packets, syt, data_quadlets; + struct sort_table *entry, *tbl = s->sort_table; + unsigned int i, j, k, index, packets, syt, remain_packets; __be32 *buffer, *headers = header;
/* The number of packets in buffer */ packets = header_length / TRANSMIT_PACKET_HEADER_SIZE;
- for (p = 0; p < packets; p++) { - buffer = s->buffer.packets[s->packet_index].buffer; + /* Store into sort table and sort. */ + for (i = 0; i < packets; i++) { + entry = &tbl[s->remain_packets + i]; + entry->id = i;
- /* The number of quadlets in this packet */ - data_quadlets = - (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + index = s->packet_index + i; + if (index >= QUEUE_LENGTH) + index -= QUEUE_LENGTH; + buffer = s->buffer.packets[index].buffer; + entry->dbc = be32_to_cpu(buffer[0]) & AMDTP_DBC_MASK;
- /* Process sync slave stream */ - if ((s->flags & CIP_BLOCKING) && - (s->flags & CIP_SYNC_TO_DEVICE) && - s->sync_slave->callbacked) { - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - handle_out_packet(s->sync_slave, syt); - } + entry->payload_size = be32_to_cpu(headers[i]) >> + ISO_DATA_LENGTH_SHIFT; + } + packet_sort(tbl, packets + s->remain_packets);
- /* handle each packet data */ - handle_in_packet(s, data_quadlets, buffer); + /* + * for convinience, tbl[i].id >= QUEUE_LENGTH is a label to identify + * previous packets in buffer. + */ + remain_packets = s->remain_packets; + s->remain_packets = packets / 4; + for (i = 0, j = 0, k = 0; i < remain_packets + packets; i++) { + if (tbl[i].id < QUEUE_LENGTH) { + index = s->packet_index + tbl[i].id; + if (index >= QUEUE_LENGTH) + index -= QUEUE_LENGTH; + buffer = s->buffer.packets[index].buffer; + } else + buffer = s->left_packets + + amdtp_stream_get_max_payload(s) * j++; + + if (i < remain_packets + packets - s->remain_packets) { + /* Process sync slave stream */ + if ((s->flags & CIP_BLOCKING) && + (s->flags & CIP_SYNC_TO_DEVICE) && + s->sync_slave->callbacked) { + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + handle_out_packet(s->sync_slave, syt); + } + handle_in_packet(s, tbl[i].payload_size / 4, buffer); + } else { + tbl[k].id = tbl[i].id + QUEUE_LENGTH; + tbl[k].dbc = tbl[i].dbc; + tbl[k].payload_size = tbl[i].payload_size; + memcpy(s->left_packets + + amdtp_stream_get_max_payload(s) * k++, + buffer, tbl[i].payload_size); + } + }
+ for (i = 0; i < packets; i ++) { if (queue_in_packet(s) < 0) { amdtp_stream_pcm_abort(s); return; @@ -955,6 +1039,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) if (err < 0) goto err_unlock;
+ /* for sorting transmitted packets */ + if (s->direction == AMDTP_TRANSMIT_STREAM) { + s->remain_packets = 0; + s->sort_table = kzalloc(sizeof(struct sort_table) * + QUEUE_LENGTH, GFP_KERNEL); + if (s->sort_table == NULL) + return -ENOMEM; + s->left_packets = kzalloc(amdtp_stream_get_max_payload(s) * + QUEUE_LENGTH / 4, GFP_KERNEL); + } + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, header_size, amdtp_stream_callback, s); @@ -1052,6 +1147,11 @@ void amdtp_stream_stop(struct amdtp_stream *s) s->context = ERR_PTR(-1); iso_packets_buffer_destroy(&s->buffer, s->unit);
+ if (s->sort_table != NULL) + kfree(s->sort_table); + if (s->left_packets != NULL) + kfree(s->left_packets); + s->callbacked = false;
mutex_unlock(&s->mutex); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index a2a7818..0ba65bb 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -106,6 +106,10 @@ struct amdtp_stream { bool callbacked; wait_queue_head_t callback_wait; struct amdtp_stream *sync_slave; + + void *sort_table; + void *left_packets; + unsigned int remain_packets; };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
Currently, when duplex streams with synchronization mode, this module just pass 'presentation timestamp' from transmitted packets to out packets. This is enough to handle actual device but logically it should include 'transfer delay' and 'processing time'. This means the timestamp in out packet should be at future.
To be simple, this commit add only 'transfer delay'.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index d02ccac..bc21276 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -856,6 +856,15 @@ static void packet_sort(struct sort_table *tbl, unsigned int len) } while (i < len); }
+static inline void add_transfer_delay(struct amdtp_stream *s, unsigned int *syt) +{ + if (*syt != CIP_SYT_NO_INFO) { + *syt += (s->transfer_delay / TICKS_PER_CYCLE) << 12; + *syt += s->transfer_delay % TICKS_PER_CYCLE; + *syt &= CIP_SYT_MASK; + } +} + static void out_stream_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *private_data) @@ -927,6 +936,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, (s->flags & CIP_SYNC_TO_DEVICE) && s->sync_slave->callbacked) { syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + add_transfer_delay(s, &syt); handle_out_packet(s->sync_slave, syt); } handle_in_packet(s, tbl[i].payload_size / 4, buffer);
Some devices arrange the position of PCM/MIDI data. This commit allows drivers to set channel mapping.
To be simple, the mapping table is an array with fixed length. Then the number of channels for PCM is restricted by 64 channels.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 131 +++++++++++++++++++++++++++++-------------------- sound/firewire/amdtp.h | 7 +++ 2 files changed, 84 insertions(+), 54 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index bc21276..9c7aee4 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -140,14 +140,15 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, [CIP_SFC_176400] = 176400, [CIP_SFC_192000] = 192000, }; - unsigned int sfc, midi_channels; + unsigned int i, sfc, midi_channels;
midi_channels = DIV_ROUND_UP(midi_ports, 8); - if (WARN_ON(amdtp_stream_running(s)) || + if (WARN_ON(amdtp_stream_running(s)) | + WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) | WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) return;
- for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) + for (sfc = 0; sfc < sizeof(rates); ++sfc) if (rates[sfc] == rate) goto sfc_found; WARN_ON(1); @@ -156,22 +157,31 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, sfc_found: s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000; if (s->dual_wire) { - sfc -= 2; + s->sfc = sfc - 2; + s->pcm_channels = pcm_channels * 2; rate /= 2; - pcm_channels *= 2; + } else { + s->sfc = sfc; + s->pcm_channels = pcm_channels; } s->sfc = sfc; - s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); s->pcm_channels = pcm_channels; s->midi_channels = midi_channels; + s->data_block_quadlets = s->pcm_channels + s->midi_channels;
- s->syt_interval = amdtp_syt_intervals[sfc]; + s->syt_interval = amdtp_syt_intervals[s->sfc];
/* default buffering in the device */ s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; if (s->flags & CIP_BLOCKING) /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + + /* init the position map for PCM and MIDI channels */ + for (i = 0; i < pcm_channels; i++) + s->pcm_positions[i] = i; + for (i = 0; i < midi_channels; i++) + s->midi_positions[i] = s->pcm_channels + i; } EXPORT_SYMBOL(amdtp_stream_set_parameters);
@@ -356,22 +366,20 @@ static void amdtp_write_s32(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, frame_step, i, c; + unsigned int remaining_frames, i, c; const u32 *src;
- channels = s->pcm_channels; src = (void *)runtime->dma_area + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - frame_step = s->data_block_quadlets - channels;
for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + for (c = 0; c < s->pcm_channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src >> 8) | 0x40000000); src++; - buffer++; } - buffer += frame_step; + buffer += s->data_block_quadlets; if (--remaining_frames == 0) src = (void *)runtime->dma_area; } @@ -382,22 +390,20 @@ static void amdtp_write_s16(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, frame_step, i, c; + unsigned int remaining_frames, i, c; const u16 *src;
- channels = s->pcm_channels; src = (void *)runtime->dma_area + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - frame_step = s->data_block_quadlets - channels;
for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src << 8) | 0x40000000); + for (c = 0; c < s->pcm_channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src << 8) | 0x40000000); src++; - buffer++; } - buffer += frame_step; + buffer += s->data_block_quadlets; if (--remaining_frames == 0) src = (void *)runtime->dma_area; } @@ -408,29 +414,29 @@ static void amdtp_write_s32_dualwire(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + unsigned int channels, remaining_frames, i, c; const u32 *src;
- channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); - frame_adjust_1 = channels - 1; - frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2;
- channels /= 2; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src >> 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_1; + buffer += 1; for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src >> 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_2; + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; } }
@@ -439,29 +445,29 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + unsigned int channels, remaining_frames, i, c; const u16 *src;
- channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); - frame_adjust_1 = channels - 1; - frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + channels = s->pcm_channels / 2;
- channels /= 2; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src << 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src << 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_1; + buffer += 1; for (c = 0; c < channels; ++c) { - *buffer = cpu_to_be32((*src << 8) | 0x40000000); + buffer[s->pcm_positions[c] * 2] = + cpu_to_be32((*src << 8) | 0x40000000); src++; - buffer += 2; } - buffer -= frame_adjust_2; + buffer += s->data_block_quadlets - 1; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; } }
@@ -479,7 +485,7 @@ static void amdtp_read_s32(struct amdtp_stream *s,
for (i = 0; i < frames; ++i) { for (c = 0; c < s->pcm_channels; ++c) { - *dst = be32_to_cpu(buffer[c]) << 8; + *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; dst++; } buffer += s->data_block_quadlets; @@ -502,7 +508,7 @@ static void amdtp_read_s16(struct amdtp_stream *s,
for (i = 0; i < frames; ++i) { for (c = 0; c < s->pcm_channels; ++c) { - *dst = be32_to_cpu(buffer[c]) << 8; + *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; dst++; } buffer +=s->data_block_quadlets; @@ -527,13 +533,13 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s, for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { *dst = - be32_to_cpu(buffer[c]) << 8; + be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8; dst++; } buffer += 1; for (c = 0; c < channels; ++c) { *dst = - be32_to_cpu(buffer[c]) << 8; + be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8; dst++; } buffer += s->data_block_quadlets - 1; @@ -558,13 +564,13 @@ static void amdtp_read_s16_dualwire(struct amdtp_stream *s, for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { *dst = - be32_to_cpu(buffer[c]) << 8; + be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8; dst++; } buffer += 1; for (c = 0; c < channels; ++c) { *dst = - be32_to_cpu(buffer[c]) << 8; + be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8; dst++; } buffer += s->data_block_quadlets - 1; @@ -580,11 +586,26 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
for (i = 0; i < frames; ++i) { for (c = 0; c < s->pcm_channels; ++c) - buffer[c] = cpu_to_be32(0x40000000); + buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); buffer += s->data_block_quadlets; } }
+static void amdtp_fill_pcm_silence_dualwire(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i, c, channels; + + channels = s->pcm_channels /2; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[s->pcm_positions[c] * 2] = + buffer[s->pcm_positions[c] * 2 + 1] = + cpu_to_be32(0x40000000); + } + buffer += s->data_block_quadlets; + } +} static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -609,7 +630,7 @@ static void amdtp_fill_midi(struct amdtp_stream *s, else b[0] = 0x81; } - buffer[s->pcm_channels + c] = + buffer[s->midi_positions[c]] = be32_to_cpu((b[0] << 24) | (b[1] << 16)); } buffer += s->data_block_quadlets; @@ -626,7 +647,7 @@ static void amdtp_pull_midi(struct amdtp_stream *s, for (f = 0; f < frames; f++) { m = (s->data_block_counter + f) % 8; for (c = 0; c < s->midi_channels; c++) { - b = (u8 *)&buffer[s->pcm_channels + c]; + b = (u8 *)&buffer[s->midi_positions[c]]; if (b[0] < 0x81 || 0x83 < b[0]) continue;
@@ -737,6 +758,8 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) pcm = ACCESS_ONCE(s->pcm); if (pcm) s->transfer_samples(s, pcm, buffer, data_blocks); + else if (s->dual_wire) + amdtp_fill_pcm_silence_dualwire(s, buffer, data_blocks); else amdtp_fill_pcm_silence(s, buffer, data_blocks); if (s->midi_channels) diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 0ba65bb..539c007 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -48,6 +48,11 @@ enum cip_sfc { SNDRV_PCM_FMTBIT_S32)
/* + * This module supports maximum 16 PCM channel for one PCM stream + * This is for our convinience. + */ +#define AMDTP_MAX_CHANNELS_FOR_PCM 64 +/* * This module supports maximum 2 MIDI channels. * Then AMDTP packets include maximum 16 MIDI streams multiplexed. * This is for our convinience. @@ -79,6 +84,8 @@ struct amdtp_stream { void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); + u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 midi_positions[AMDTP_MAX_CHANNELS_FOR_MIDI];
unsigned int syt_interval; unsigned int transfer_delay;
Referring to IEC 61883-1, oMPR and iMPR, oPCR and iPCR have some fields with the same role in the same position. This patch renames some macros, variables and function arguments with "i" in its prefix to reuse them between oMPR and iMPR, oPCR and iPCR.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/cmp.c | 91 ++++++++++++++++++++++++++-------------------------- sound/firewire/cmp.h | 6 ++-- 2 files changed, 49 insertions(+), 48 deletions(-)
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index efdbf58..17ad4f2 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -14,18 +14,20 @@ #include "iso-resources.h" #include "cmp.h"
-#define IMPR_SPEED_MASK 0xc0000000 -#define IMPR_SPEED_SHIFT 30 -#define IMPR_XSPEED_MASK 0x00000060 -#define IMPR_XSPEED_SHIFT 5 -#define IMPR_PLUGS_MASK 0x0000001f - -#define IPCR_ONLINE 0x80000000 -#define IPCR_BCAST_CONN 0x40000000 -#define IPCR_P2P_CONN_MASK 0x3f000000 -#define IPCR_P2P_CONN_SHIFT 24 -#define IPCR_CHANNEL_MASK 0x003f0000 -#define IPCR_CHANNEL_SHIFT 16 +/* MPR common fields */ +#define MPR_SPEED_MASK 0xc0000000 +#define MPR_SPEED_SHIFT 30 +#define MPR_XSPEED_MASK 0x00000060 +#define MPR_XSPEED_SHIFT 5 +#define MPR_PLUGS_MASK 0x0000001f + +/* PCR common fields */ +#define PCR_ONLINE 0x80000000 +#define PCR_BCAST_CONN 0x40000000 +#define PCR_P2P_CONN_MASK 0x3f000000 +#define PCR_P2P_CONN_SHIFT 24 +#define PCR_CHANNEL_MASK 0x003f0000 +#define PCR_CHANNEL_SHIFT 16
enum bus_reset_handling { ABORT_ON_BUS_RESET, @@ -88,24 +90,24 @@ static int pcr_modify(struct cmp_connection *c, * cmp_connection_init - initializes a connection manager * @c: the connection manager to initialize * @unit: a unit of the target device - * @ipcr_index: the index of the iPCR on the target device + * @pcr_index: the index of the iPCR/oPCR on the target device */ int cmp_connection_init(struct cmp_connection *c, struct fw_unit *unit, - unsigned int ipcr_index) + unsigned int pcr_index) { - __be32 impr_be; - u32 impr; + __be32 mpr_be; + u32 mpr; int err;
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, CSR_REGISTER_BASE + CSR_IMPR, - &impr_be, 4, 0); + &mpr_be, 4, 0); if (err < 0) return err; - impr = be32_to_cpu(impr_be); + mpr = be32_to_cpu(mpr_be);
- if (ipcr_index >= (impr & IMPR_PLUGS_MASK)) + if (pcr_index >= (mpr & MPR_PLUGS_MASK)) return -EINVAL;
err = fw_iso_resources_init(&c->resources, unit); @@ -115,10 +117,10 @@ int cmp_connection_init(struct cmp_connection *c, c->connected = false; mutex_init(&c->mutex); c->last_pcr_value = cpu_to_be32(0x80000000); - c->pcr_index = ipcr_index; - c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT; + c->pcr_index = pcr_index; + c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT; if (c->max_speed == SCODE_BETA) - c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT; + c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
return 0; } @@ -139,23 +141,23 @@ EXPORT_SYMBOL(cmp_connection_destroy);
static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) { - ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN | - IPCR_P2P_CONN_MASK | - IPCR_CHANNEL_MASK); - ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT); - ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT); + ipcr &= ~cpu_to_be32(PCR_BCAST_CONN | + PCR_P2P_CONN_MASK | + PCR_CHANNEL_MASK); + ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); + ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
return ipcr; }
-static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) +static int pcr_set_check(struct cmp_connection *c, __be32 pcr) { - if (ipcr & cpu_to_be32(IPCR_BCAST_CONN | - IPCR_P2P_CONN_MASK)) { + if (pcr & cpu_to_be32(PCR_BCAST_CONN | + PCR_P2P_CONN_MASK)) { cmp_error(c, "plug is already in use\n"); return -EBUSY; } - if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) { + if (!(pcr & cpu_to_be32(PCR_ONLINE))) { cmp_error(c, "plug is not on-line\n"); return -ECONNREFUSED; } @@ -170,9 +172,9 @@ static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) * * This function establishes a point-to-point connection from the local * computer to the target by allocating isochronous resources (channel and - * bandwidth) and setting the target's input plug control register. When this - * function succeeds, the caller is responsible for starting transmitting - * packets. + * bandwidth) and setting the target's input/output plug control register. + * When this function succeeds, the caller is responsible for starting + * transmitting packets. */ int cmp_connection_establish(struct cmp_connection *c, unsigned int max_payload_bytes) @@ -193,7 +195,7 @@ retry_after_bus_reset: if (err < 0) goto err_mutex;
- err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, + err = pcr_modify(c, ipcr_set_modify, pcr_set_check, ABORT_ON_BUS_RESET); if (err == -EAGAIN) { fw_iso_resources_free(&c->resources); @@ -221,8 +223,8 @@ EXPORT_SYMBOL(cmp_connection_establish); * cmp_connection_update - update the connection after a bus reset * @c: the connection manager * - * This function must be called from the driver's .update handler to reestablish - * any connection that might have been active. + * This function must be called from the driver's .update handler to + * reestablish any connection that might have been active. * * Returns zero on success, or a negative error code. On an error, the * connection is broken and the caller must stop transmitting iso packets. @@ -242,7 +244,7 @@ int cmp_connection_update(struct cmp_connection *c) if (err < 0) goto err_unconnect;
- err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, + err = pcr_modify(c, ipcr_set_modify, pcr_set_check, SUCCEED_ON_BUS_RESET); if (err < 0) goto err_resources; @@ -261,19 +263,18 @@ err_unconnect: } EXPORT_SYMBOL(cmp_connection_update);
- -static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr) +static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr) { - return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK); + return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK); }
/** * cmp_connection_break - break the connection to the target * @c: the connection manager * - * This function deactives the connection in the target's input plug control - * register, and frees the isochronous resources of the connection. Before - * calling this function, the caller should cease transmitting packets. + * This function deactives the connection in the target's input/output plug + * control register, and frees the isochronous resources of the connection. + * Before calling this function, the caller should cease transmitting packets. */ void cmp_connection_break(struct cmp_connection *c) { @@ -286,7 +287,7 @@ void cmp_connection_break(struct cmp_connection *c) return; }
- err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); + err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); if (err < 0) cmp_error(c, "plug is still connected\n");
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index f47de08..2320cd4 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -11,8 +11,8 @@ struct fw_unit; * struct cmp_connection - manages an isochronous connection to a device * @speed: the connection's actual speed * - * This structure manages (using CMP) an isochronous stream from the local - * computer to a device's input plug (iPCR). + * This structure manages (using CMP) an isochronous stream between the local + * computer and a device's input plug (iPCR) and output plug (oPCR). * * There is no corresponding oPCR created on the local computer, so it is not * possible to overlay connections on top of this one. @@ -30,7 +30,7 @@ struct cmp_connection {
int cmp_connection_init(struct cmp_connection *connection, struct fw_unit *unit, - unsigned int ipcr_index); + unsigned int pcr_index); void cmp_connection_destroy(struct cmp_connection *connection);
int cmp_connection_establish(struct cmp_connection *connection,
This patch adds 'direction' member to 'cmp_connection' structure to indicate the direction of connection. This patch also adds 'direction' argument to cmp_connection_init() function to determine the direction.
The cmp_connection_init() function is exported and used in snd-firewire-speakers so this patch also affect it.
This patch just add them. Actual implementation will be done by followed patches.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/cmp.c | 1 + sound/firewire/cmp.h | 7 +++++++ sound/firewire/speakers.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 17ad4f2..1430fec 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -94,6 +94,7 @@ static int pcr_modify(struct cmp_connection *c, */ int cmp_connection_init(struct cmp_connection *c, struct fw_unit *unit, + enum cmp_direction direction, unsigned int pcr_index) { __be32 mpr_be; diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index 2320cd4..9b58448 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -7,6 +7,11 @@
struct fw_unit;
+enum cmp_direction { + CMP_INPUT = 0, + CMP_OUTPUT, +}; + /** * struct cmp_connection - manages an isochronous connection to a device * @speed: the connection's actual speed @@ -26,10 +31,12 @@ struct cmp_connection { __be32 last_pcr_value; unsigned int pcr_index; unsigned int max_speed; + enum cmp_direction direction; };
int cmp_connection_init(struct cmp_connection *connection, struct fw_unit *unit, + enum cmp_direction direction, unsigned int pcr_index); void cmp_connection_destroy(struct cmp_connection *connection);
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 8f0257e..d548376 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -680,7 +680,7 @@ static int fwspk_probe(struct fw_unit *unit, fwspk->unit = fw_unit_get(unit); fwspk->device_info = (const struct device_info *)id->driver_data;
- err = cmp_connection_init(&fwspk->connection, unit, 0); + err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0); if (err < 0) goto err_unit;
This patch adds some macros, codes with condition of direction and new functions to handle output connection. Once cmp_connection_init() is executed with its direction, CMP input and output connection can be handled by the same way.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/cmp.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 9 deletions(-)
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 1430fec..00b1b2d 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -29,6 +29,16 @@ #define PCR_CHANNEL_MASK 0x003f0000 #define PCR_CHANNEL_SHIFT 16
+/* oPCR specific fields */ +#define OPCR_XSPEED_MASK 0x00C00000 +#define OPCR_XSPEED_SHIFT 22 +#define OPCR_SPEED_MASK 0x0000C000 +#define OPCR_SPEED_SHIFT 14 +#define OPCR_OVERHEAD_ID_MASK 0x00003C00 +#define OPCR_OVERHEAD_ID_SHIFT 10 +#define OPCR_PAYLOAD_MASK 0x000003FF +#define OPCR_PAYLOAD_SHIFT 0 + enum bus_reset_handling { ABORT_ON_BUS_RESET, SUCCEED_ON_BUS_RESET, @@ -41,10 +51,30 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
va_start(va, fmt); dev_err(&c->resources.unit->device, "%cPCR%u: %pV", - 'i', c->pcr_index, &(struct va_format){ fmt, &va }); + (c->direction == CMP_INPUT) ? 'i' : 'o', + c->pcr_index, &(struct va_format){ fmt, &va }); va_end(va); }
+static unsigned long long get_offset(struct cmp_connection *c, bool master) +{ + unsigned long long offset = CSR_REGISTER_BASE; + + if (!master) { + if (c->direction == CMP_INPUT) + offset += CSR_IPCR(c->pcr_index); + else + offset += CSR_OPCR(c->pcr_index); + } else { + if (c->direction == CMP_INPUT) + offset += CSR_IMPR; + else + offset += CSR_OMPR; + } + + return offset; +} + static int pcr_modify(struct cmp_connection *c, __be32 (*modify)(struct cmp_connection *c, __be32 old), int (*check)(struct cmp_connection *c, __be32 pcr), @@ -60,8 +90,7 @@ static int pcr_modify(struct cmp_connection *c,
err = snd_fw_transaction( c->resources.unit, TCODE_LOCK_COMPARE_SWAP, - CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), - buffer, 8, + get_offset(c, false), buffer, 8, FW_FIXED_GENERATION | c->resources.generation);
if (err < 0) { @@ -102,8 +131,7 @@ int cmp_connection_init(struct cmp_connection *c, int err;
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - CSR_REGISTER_BASE + CSR_IMPR, - &mpr_be, 4, 0); + get_offset(c, true), &mpr_be, 4, 0); if (err < 0) return err; mpr = be32_to_cpu(mpr_be); @@ -122,6 +150,7 @@ int cmp_connection_init(struct cmp_connection *c, c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT; if (c->max_speed == SCODE_BETA) c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT; + c->direction = direction;
return 0; } @@ -151,6 +180,62 @@ static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) return ipcr; }
+static int get_overhead_id(struct cmp_connection *c) +{ + int id; + + /* + * apply "oPCR overhead ID encoding" + * the encoding table can convert up to 512. + * here the value over 512 is converted as the same way as 512. + */ + for (id = 1; id < 16; id++) { + if (c->resources.bandwidth_overhead < (id << 5)) + break; + } + if (id == 16) + id = 0; + + return id; +} + +static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr) +{ + unsigned int spd, xspd; + + /* generate speed and extended speed field value */ + if (c->speed > SCODE_400) { + spd = SCODE_800; + xspd = c->speed - SCODE_800; + } else { + spd = c->speed; + xspd = 0; + } + + opcr &= ~cpu_to_be32(PCR_BCAST_CONN | + PCR_P2P_CONN_MASK | + OPCR_XSPEED_MASK | + PCR_CHANNEL_MASK | + OPCR_SPEED_MASK | + OPCR_OVERHEAD_ID_MASK | + OPCR_PAYLOAD_MASK); + opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); + opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT); + opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); + opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT); + opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT); + /* + * here zero is applied to payload field. + * it means the maximum number of quadlets in an isochronous packet is + * 1024 when spd is less than three, 1024 * 2 * xspd + 1 when spd is + * equal to three. An arbitrary value can be set here but 0 is enough + * for our purpose. + */ + opcr |= cpu_to_be32(0 << OPCR_PAYLOAD_SHIFT); + + return opcr; +} + static int pcr_set_check(struct cmp_connection *c, __be32 pcr) { if (pcr & cpu_to_be32(PCR_BCAST_CONN | @@ -196,8 +281,13 @@ retry_after_bus_reset: if (err < 0) goto err_mutex;
- err = pcr_modify(c, ipcr_set_modify, pcr_set_check, - ABORT_ON_BUS_RESET); + if (c->direction == CMP_OUTPUT) + err = pcr_modify(c, opcr_set_modify, pcr_set_check, + ABORT_ON_BUS_RESET); + else + err = pcr_modify(c, ipcr_set_modify, pcr_set_check, + ABORT_ON_BUS_RESET); + if (err == -EAGAIN) { fw_iso_resources_free(&c->resources); goto retry_after_bus_reset; @@ -245,8 +335,13 @@ int cmp_connection_update(struct cmp_connection *c) if (err < 0) goto err_unconnect;
- err = pcr_modify(c, ipcr_set_modify, pcr_set_check, - SUCCEED_ON_BUS_RESET); + if (c->direction == CMP_OUTPUT) + err = pcr_modify(c, opcr_set_modify, pcr_set_check, + SUCCEED_ON_BUS_RESET); + else + err = pcr_modify(c, ipcr_set_modify, pcr_set_check, + SUCCEED_ON_BUS_RESET); + if (err < 0) goto err_resources;
The register for CMP plugs has two fields related to the number of established connections, one is 'Broadcast connection counter' and another is 'Point-to-point connection counter'. The driver can know there are established connections or not to check these fields.
This is considering for JACK/FFADO streaming. Currently, when JACK/FFADO starts its streaming to the device, cmp_connection_establish() is failed. This seems to be enough but there are some devices which needs to change samplling frequency before trying to establish connections. For these devices, the functionality to check connections is needed.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/cmp.c | 18 ++++++++++++++++++ sound/firewire/cmp.h | 1 + 2 files changed, 19 insertions(+)
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 00b1b2d..354920a 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -157,6 +157,24 @@ int cmp_connection_init(struct cmp_connection *c, EXPORT_SYMBOL(cmp_connection_init);
/** + * cmp_connection_check_used - check connection is already esablished or not + * @c: the connection manager to be checked + */ +int cmp_connection_check_used(struct cmp_connection *c, bool *used) +{ + __be32 pcr; + int err; + + err = snd_fw_transaction( + c->resources.unit, TCODE_READ_QUADLET_REQUEST, + get_offset(c, false), &pcr, 4, 0); + if (err >= 0) + *used = (pcr & cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK)); + return err; +} +EXPORT_SYMBOL(cmp_connection_check_used); + +/** * cmp_connection_destroy - free connection manager resources * @c: the connection manager */ diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index 9b58448..ebcb484 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -38,6 +38,7 @@ int cmp_connection_init(struct cmp_connection *connection, struct fw_unit *unit, enum cmp_direction direction, unsigned int pcr_index); +int cmp_connection_check_used(struct cmp_connection *connection, bool *used); void cmp_connection_destroy(struct cmp_connection *connection);
int cmp_connection_establish(struct cmp_connection *connection,
This commit add three commands. I think many devices use these commands. These commands are defined in 'AV/C Digital Interface Command Set General Specification Version 4.2 (2004006, 1394TA)'.
1. INPUT PLUG SIGNAL FORMAT command 2. OUTPUT PLUG SIGNAL FORMAT command 3. PLUG INFO command
By the command 1 and 2, the drivers can get/set sampling frequency. By the command 3, the drivers can get the number of each plugs.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 24 ++++---- sound/firewire/amdtp.h | 1 + sound/firewire/fcp.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++ sound/firewire/fcp.h | 16 +++++ sound/firewire/speakers.c | 44 +++----------- 5 files changed, 186 insertions(+), 48 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 9c7aee4..cb74ade 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -115,6 +115,17 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { }; EXPORT_SYMBOL(amdtp_syt_intervals);
+const unsigned int amdtp_rate_table[] = { + [CIP_SFC_32000] = 32000, + [CIP_SFC_44100] = 44100, + [CIP_SFC_48000] = 48000, + [CIP_SFC_88200] = 88200, + [CIP_SFC_96000] = 96000, + [CIP_SFC_176400] = 176400, + [CIP_SFC_192000] = 192000, +}; +EXPORT_SYMBOL(amdtp_rate_table); + /** * amdtp_stream_set_parameters - set stream parameters * @s: the AMDTP stream to configure @@ -131,15 +142,6 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int pcm_channels, unsigned int midi_ports) { - static const unsigned int rates[] = { - [CIP_SFC_32000] = 32000, - [CIP_SFC_44100] = 44100, - [CIP_SFC_48000] = 48000, - [CIP_SFC_88200] = 88200, - [CIP_SFC_96000] = 96000, - [CIP_SFC_176400] = 176400, - [CIP_SFC_192000] = 192000, - }; unsigned int i, sfc, midi_channels;
midi_channels = DIV_ROUND_UP(midi_ports, 8); @@ -148,8 +150,8 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) return;
- for (sfc = 0; sfc < sizeof(rates); ++sfc) - if (rates[sfc] == rate) + for (sfc = 0; sfc < sizeof(amdtp_rate_table); ++sfc) + if (amdtp_rate_table[sfc] == rate) goto sfc_found; WARN_ON(1); return; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 539c007..02ff8de 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -142,6 +142,7 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s); bool amdtp_stream_wait_callback(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; +extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
/** * amdtp_stream_running - check stream is running or not diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c index 860c080..ccdd0de 100644 --- a/sound/firewire/fcp.c +++ b/sound/firewire/fcp.c @@ -10,12 +10,14 @@ #include <linux/firewire-constants.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/wait.h> #include <linux/delay.h> #include "fcp.h" #include "lib.h" +#include "amdtp.h"
#define CTS_AVC 0x00
@@ -23,6 +25,153 @@ #define ERROR_DELAY_MS 5 #define FCP_TIMEOUT_MS 125
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate, + enum avc_general_plug_dir dir, + unsigned short pid) +{ + unsigned int sfc; + u8 *buf; + bool flag; + int err; + + flag = false; + for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) { + if (amdtp_rate_table[sfc] == rate) { + flag = true; + break; + } + } + if (!flag) + return -EINVAL; + + buf = kzalloc(8, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x00; /* AV/C CONTROL */ + buf[1] = 0xff; /* UNIT */ + if (dir == AVC_GENERAL_PLUG_DIR_IN) + buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ + else + buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */ + buf[3] = 0xff & pid; /* plug id */ + buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */ + buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */ + buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used)*/ + buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */ + + /* do transaction and check buf[1-5] are the same against command */ + err = fcp_avc_transaction(unit, buf, 8, buf, 8, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); + if (err < 0) + goto end; + + /* check length */ + if (err != 8) { + dev_err(&unit->device, "failed to set sample rate\n"); + err = -EIO; + goto end; + } + + /* return response code */ + err = buf[0]; +end: + kfree(buf); + return err; +} +EXPORT_SYMBOL(avc_general_set_sig_fmt); + +int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate, + enum avc_general_plug_dir dir, + unsigned short pid) +{ + unsigned int sfc; + u8 *buf; + int err; + + buf = kzalloc(8, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x01; /* AV/C STATUS */ + buf[1] = 0xff; /* Unit */ + if (dir == AVC_GENERAL_PLUG_DIR_IN) + buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ + else + buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */ + buf[3] = 0xff & pid; /* plug id */ + buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */ + buf[5] = 0xff; /* FDF-hi. AM824, frequency */ + buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */ + buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */ + + /* do transaction and check buf[1-4] are the same against command */ + err = fcp_avc_transaction(unit, buf, 8, buf, 8, + BIT(1) | BIT(2) | BIT(3) | BIT(4)); + if (err < 0) + goto end; + + /* check length */ + if (err != 8) { + dev_err(&unit->device, "failed to get sample rate\n"); + err = -EIO; + goto end; + } + + /* check sfc field and pick up rate */ + sfc = 0x07 & buf[5]; + if (sfc >= CIP_SFC_COUNT) { + err = -EINVAL; + goto end; + } + *rate = amdtp_rate_table[sfc]; + + /* return response code */ + err = buf[0]; +end: + kfree(buf); + return err; +} +EXPORT_SYMBOL(avc_general_get_sig_fmt); + +int avc_general_get_plug_info(struct fw_unit *unit, + unsigned short bus_plugs[AVC_GENERAL_PLUG_DIR_COUNT], + unsigned short ext_plugs[AVC_GENERAL_PLUG_DIR_COUNT]) +{ + u8 *buf; + int err; + + buf = kzalloc(8, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x01; /* AV/C STATUS */ + buf[1] = 0xff; /* UNIT */ + buf[2] = 0x02; /* PLUG INFO */ + + err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2)); + if (err < 0) + goto end; + + /* check length */ + if (err != 8) { + err = -EIO; + goto end; + } + + bus_plugs[AVC_GENERAL_PLUG_DIR_IN] = buf[4]; + bus_plugs[AVC_GENERAL_PLUG_DIR_OUT] = buf[5]; + ext_plugs[AVC_GENERAL_PLUG_DIR_IN] = buf[6]; + ext_plugs[AVC_GENERAL_PLUG_DIR_OUT] = buf[7]; + + /* return response code */ + err = buf[0]; +end: + kfree(buf); + return err; +} +EXPORT_SYMBOL(avc_general_get_plug_info); + static DEFINE_SPINLOCK(transactions_lock); static LIST_HEAD(transactions);
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h index 8659568..84791e0 100644 --- a/sound/firewire/fcp.h +++ b/sound/firewire/fcp.h @@ -3,6 +3,22 @@
struct fw_unit;
+/* AV/C Digital Interface Command Set General Specification 4.2 (1394TA) */ +enum avc_general_plug_dir { + AVC_GENERAL_PLUG_DIR_IN = 0, + AVC_GENERAL_PLUG_DIR_OUT = 1, + AVC_GENERAL_PLUG_DIR_COUNT +}; +int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate, + enum avc_general_plug_dir dir, + unsigned short plug); +int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate, + enum avc_general_plug_dir dir, + unsigned short plug); +int avc_general_get_plug_info(struct fw_unit *unit, + unsigned short bus_plugs[AVC_GENERAL_PLUG_DIR_COUNT], + unsigned short ext_plugs[AVC_GENERAL_PLUG_DIR_COUNT]); + int fcp_avc_transaction(struct fw_unit *unit, const void *command, unsigned int command_size, void *response, unsigned int response_size, diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index d548376..5fb3e2c 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -194,42 +194,6 @@ static void fwspk_stop_stream(struct fwspk *fwspk) } }
-static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) -{ - u8 *buf; - int err; - - buf = kmalloc(8, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - buf[0] = 0x00; /* AV/C, CONTROL */ - buf[1] = 0xff; /* unit */ - buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ - buf[3] = 0x00; /* plug 0 */ - buf[4] = 0x90; /* format: audio */ - buf[5] = 0x00 | sfc; /* AM824, frequency */ - buf[6] = 0xff; /* SYT (not used) */ - buf[7] = 0xff; - - err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, - BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); - if (err < 0) - goto error; - if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { - dev_err(&fwspk->unit->device, "failed to set sample rate\n"); - err = -EIO; - goto error; - } - - err = 0; - -error: - kfree(buf); - - return err; -} - static int fwspk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { @@ -253,9 +217,15 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, amdtp_stream_set_pcm_format(&fwspk->stream, params_format(hw_params));
- err = fwspk_set_rate(fwspk, fwspk->stream.sfc); + err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params), + AVC_GENERAL_PLUG_DIR_IN, 0); if (err < 0) goto err_buffer; + if (err != 0x09 /* ACCEPTED */) { + dev_err(&fwspk->unit->device, "failed to set sample rate\n"); + err = -EIO; + goto error; + }
return 0;
This commit adds two modifications for Fireworks quirks.
1. The driver can indicate the number of first data blocks in which this module put MIDI messages for out stream. This commit add 'blocks_for_midi' member to 'amdtp_stream' structure for this purpose. Fireworks can pick up MIDI messages in first 8 data blocks and ignore in other data blocks.
2. This module calculate data block size by itself don't check counter. One of Fireworks device, AudioFirePre8, always reports 8 data block size in transmitted packets and the data block counter is incremented by 8. But this is wrong. This commit force this module to calculate data block size and not to check counter continuity.
I confirm these quirks in firmware version 4.8 to 5.8. I have no investigation for the other versions.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 52 ++++++++++++++++++++++++++------------------------ sound/firewire/amdtp.h | 2 ++ 2 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index cb74ade..eb42e57 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -88,6 +88,8 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->sort_table = NULL; s->left_packets = NULL;
+ s->blocks_for_midi = UINT_MAX; + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -622,8 +624,14 @@ static void amdtp_fill_midi(struct amdtp_stream *s, b[1] = 0x00; len = 0;
+ /* + * NOTE: + * Fireworks ignores midi messages in more than first 8 + * data blocks in an packet. + */ port = c * 8 + m; - if ((s->midi[port] != NULL) && + if ((f < s->blocks_for_midi) && + (s->midi[port] != NULL) && test_bit(port, &s->midi_triggered)) { len = snd_rawmidi_transmit(s->midi[port], b + 1, 1); @@ -784,7 +792,7 @@ static void handle_in_packet(struct amdtp_stream *s, __be32 *buffer) { u32 cip_header[2]; - unsigned int data_block_quadlets, data_blocks, data_block_counter; + unsigned int data_blocks; struct snd_pcm_substream *pcm;
cip_header[0] = be32_to_cpu(buffer[0]); @@ -808,27 +816,17 @@ static void handle_in_packet(struct amdtp_stream *s, ((cip_header[1] & CIP_SYT_MASK) == CIP_SYT_NO_INFO))) return;
- data_block_quadlets = - (cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT; - /* avoid division by zero */ - if (data_block_quadlets == 0) { - dev_info(&s->unit->device, - "Detect invalid value in dbs field: %08X\n", - data_block_quadlets); - return; - } - - data_blocks = (payload_quadlets - 2) / data_block_quadlets; - data_block_counter = (cip_header[1] & AMDTP_DBC_MASK); - - /* check packet continuity */ - s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; - if (s->data_block_counter != data_block_counter) { - dev_err(&s->unit->device, - "Detect uncontinuity of CIP packets\n"); - s->data_block_counter = data_block_counter; - return; - } + /* + * This module don't use the value of dbs and dbc beceause Echo + * AudioFirePre8 reports inappropriate value. + * + * This device always reports a fixed value "16" as data block + * size at any sampling rates but actually data block size isdifferent. + * + * Additionally the value of data block count always incremented by + * "8" at any sampling rates but actually it's different. + */ + data_blocks = (payload_quadlets - 2) / s->data_block_quadlets;
buffer += 2;
@@ -1108,10 +1106,14 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) goto err_context; } while (s->packet_index > 0);
- /* NOTE: TAG1 matches CIP. This just affects in stream */ + /* + * NOTE: TAG1 matches CIP. This just affects in stream. + * Fireworks transmits NODATA packets with TAG0. + */ s->data_block_counter = 0; s->callbacked = false; - err = fw_iso_context_start(s->context, -1, 0, FW_ISO_CONTEXT_MATCH_TAG1); + err = fw_iso_context_start(s->context, -1, 0, + FW_ISO_CONTEXT_MATCH_TAG0 | FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context;
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 02ff8de..d79ae1c 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -109,6 +109,8 @@ struct amdtp_stream {
struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; unsigned long midi_triggered; + /* quirk: the first count of data blocks in an AMDTP packet for MIDI */ + unsigned int blocks_for_midi;
bool callbacked; wait_queue_head_t callback_wait;
Hi,
I forgot to add this comment about Fireworks quirk.
==== 3. This module handle transmitted packets with both TAG0 and TAG1
In IEC 61883-1, each common isochronous packet (CIP) has 'tag' field in its packet header. The value is generally 0x01 (CIP header is included = TAG1). But Fireworks in IEC 61883-1/6 compliant mode transmits 'no data' packet with 0x00 (No CIP header is included = TAG0) but the packet includes CIP structure. For duplex streams synchronization, this packets must be handled.
Current firewire-lib supports packet with TAG1. This commit makes firewire-lib to support both TAG0 and TAG1 for this quirk.
When handling CIP packet with invalid header, this module already output 'Invalid CIP header for AMDTP' log and continue to process.
(I note this 'no data' packet is out of specification because it includes no dummy data. In IEC 61883-6, 'no data' packet is generated by two ways. One is empty CIP packet defined in IEC 61883-1. Another is 'special non-empty packet' defined in IEC 61883-6. Fireworks uses strange combination of them.) ====
Regards
Takashi Sakamoto o-takashi@sakamocchi.jp
participants (3)
-
Alan Horstmann
-
Clemens Ladisch
-
Takashi Sakamoto