[alsa-devel] [PATCH 0/4] snd-firewire-lib: add handling AMDTP receive stream
Current implementation of snd-firewire-lib can handle only AMDTP transmit stream with PCM. This series of patch enable it to handle both AMDTP receive and transmit stream with PCM according to IEC 61883-6.
I develop with Echo Audio's Fireworks (AudioFirePre8). Unfortunately it's not fully compliant to IEC 61883-6. The last patch includes some work arounds for Fireworks. The snd-fireworks is still under development and not prepared for commit.
Takashi Sakamoto (4): Rename functions, structure and member name for AMDTP Add macros for fixed value related to AMDTP Add "direction" member to amdtp_stream structure for AMDTP Add handling AMDTP receive stream
sound/firewire/amdtp.c | 422 ++++++++++++++++++++++++++++++++++++--------- sound/firewire/amdtp.h | 64 +++---- sound/firewire/speakers.c | 33 ++-- 3 files changed, 393 insertions(+), 126 deletions(-)
This patch rename some functions, structure and member name to reuse them in both AMDTP receive and AMDTP transmit stream.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 130 ++++++++++++++++++++++----------------------- sound/firewire/amdtp.h | 58 ++++++++++---------- sound/firewire/speakers.c | 32 +++++------ 3 files changed, 110 insertions(+), 110 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ea995af..32ea650 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) { if (flags != CIP_NONBLOCKING) return -EINVAL; @@ -54,29 +54,29 @@ 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(!IS_ERR(s->context)); mutex_destroy(&s->mutex); fw_unit_put(s->unit); } -EXPORT_SYMBOL(amdtp_out_stream_destroy); +EXPORT_SYMBOL(amdtp_stream_destroy);
/** - * amdtp_out_stream_set_rate - set the sample rate - * @s: the AMDTP output stream to configure + * amdtp_stream_set_rate - set the sample rate + * @s: the AMDTP stream to configure * @rate: the sample rate * * The sample rate must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) +void amdtp_stream_set_rate(struct amdtp_stream *s, unsigned int rate) { static const struct { unsigned int rate; @@ -103,17 +103,17 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) } WARN_ON(1); } -EXPORT_SYMBOL(amdtp_out_stream_set_rate); +EXPORT_SYMBOL(amdtp_stream_set_rate);
/** - * 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_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(). + * with amdtp_stream_set_hw_params(), amdtp_stream_set_pcm(), and + * amdtp_stream_set_midi(). */ -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { static const unsigned int max_data_blocks[] = { [CIP_SFC_32000] = 4, @@ -130,24 +130,24 @@ unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; } -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);
/** - * 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 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, +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) { if (WARN_ON(!IS_ERR(s->context))) @@ -165,24 +165,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;
@@ -215,7 +215,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; @@ -258,7 +258,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) { @@ -284,7 +284,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) { @@ -310,7 +310,7 @@ static void amdtp_write_s16(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; @@ -322,7 +322,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; @@ -332,7 +332,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; @@ -377,7 +377,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; }
@@ -402,7 +402,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) @@ -410,9 +410,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;
/* @@ -427,7 +427,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, @@ -449,17 +449,17 @@ 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_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(); and it must be started before any + * amdtp_stream_set_hw_params(), amdtp_stream_set_pcm(), and + * amdtp_stream_set_midi(); 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; @@ -488,7 +488,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; @@ -501,11 +501,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; @@ -531,15 +531,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) @@ -549,27 +549,27 @@ 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);
@@ -586,16 +586,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;
@@ -607,4 +607,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 b680c5e..82392ea 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -7,13 +7,13 @@ #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 * be used if supported by the device. */ -enum cip_out_flags { +enum cip_flags { CIP_NONBLOCKING = 0, };
@@ -37,9 +37,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;
@@ -47,7 +47,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);
@@ -71,72 +71,72 @@ 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_rate(struct amdtp_out_stream *s, unsigned int rate); -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); +void amdtp_stream_set_rate(struct amdtp_stream *s, unsigned int rate); +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, +void amdtp_stream_set_pcm_format(struct amdtp_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_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);
/** - * amdtp_out_stream_set_pcm - configure format of PCM samples - * @s: the AMDTP output stream to be configured + * 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_out_stream_set_pcm(struct amdtp_out_stream *s, +static inline void amdtp_stream_set_pcm(struct amdtp_stream *s, unsigned int pcm_channels) { s->pcm_channels = pcm_channels; }
/** - * amdtp_out_stream_set_midi - configure format of MIDI data - * @s: the AMDTP output stream to be configured + * amdtp_stream_set_midi - configure format of MIDI data + * @s: the AMDTP stream to be configured * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) * * This function must not be called while the stream is running. */ -static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, +static inline void amdtp_stream_set_midi(struct amdtp_stream *s, unsigned int midi_ports) { s->midi_ports = midi_ports; }
/** - * amdtp_out_streaming_error - check for streaming error - * @s: the AMDTP output stream + * 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, +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/speakers.c b/sound/firewire/speakers.c index 1e1f003..7df20ac 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -52,7 +52,7 @@ struct fwspk { struct snd_pcm_substream *pcm; struct mutex mutex; struct cmp_connection connection; - struct amdtp_out_stream stream; + struct amdtp_stream stream; bool stream_running; bool mute; s16 volume[6]; @@ -190,7 +190,7 @@ static int fwspk_close(struct snd_pcm_substream *substream) static void fwspk_stop_stream(struct fwspk *fwspk) { if (fwspk->stream_running) { - amdtp_out_stream_stop(&fwspk->stream); + amdtp_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); fwspk->stream_running = false; } @@ -247,10 +247,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto error;
- amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); - amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); + amdtp_stream_set_rate(&fwspk->stream, params_rate(hw_params)); + amdtp_stream_set_pcm(&fwspk->stream, params_channels(hw_params));
- amdtp_out_stream_set_pcm_format(&fwspk->stream, + amdtp_stream_set_pcm_format(&fwspk->stream, params_format(hw_params));
err = fwspk_set_rate(fwspk, fwspk->stream.sfc); @@ -283,16 +283,16 @@ 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 (!fwspk->stream_running) { 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, + err = amdtp_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) @@ -303,7 +303,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
mutex_unlock(&fwspk->mutex);
- amdtp_out_stream_pcm_prepare(&fwspk->stream); + amdtp_stream_pcm_prepare(&fwspk->stream);
return 0;
@@ -330,7 +330,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; }
@@ -338,7 +338,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) @@ -657,7 +657,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); @@ -727,7 +727,7 @@ static int fwspk_probe(struct device *unit_dev) 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;
@@ -774,7 +774,7 @@ static int fwspk_remove(struct device *dev) { struct fwspk *fwspk = dev_get_drvdata(dev);
- amdtp_out_stream_pcm_abort(&fwspk->stream); + amdtp_stream_pcm_abort(&fwspk->stream); snd_card_disconnect(fwspk->card);
mutex_lock(&fwspk->mutex); @@ -793,14 +793,14 @@ 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 const struct ieee1394_device_id fwspk_id_table[] = {
Takashi Sakamoto wrote:
This patch rename some functions, structure and member name to reuse them in both AMDTP receive and AMDTP transmit stream.
+++ b/sound/firewire/amdtp.c
-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)
Please adjust the indentation in such cases.
Regards, Clemens
Clemens,
Thanks for your review.
Please adjust the indentation in such cases.
OK. I check whole indentation of arguments in functions and rewrite if needed.
Regards
Takashi Sakamoto o-takashi@sakamocchi.jp
(Apr 28 2013 21:52), Clemens Ladisch wrote:
Takashi Sakamoto wrote:
This patch rename some functions, structure and member name to reuse them in both AMDTP receive and AMDTP transmit stream.
+++ b/sound/firewire/amdtp.c
-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)
Please adjust the indentation in such cases.
Regards, Clemens
This patch adds some macros for fixed value related to AMDTP in IEC 61883-6. These macros will be used by followed patches.
This patch also replace existed fixed value to the corresponding macro.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 32ea650..5bb7836 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -20,12 +20,22 @@
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */
+#define ISO_DATA_LENGTH_SHIFT 16 #define TAG_CIP 1 - -#define CIP_EOH (1u << 31) -#define CIP_FMT_AM (0x10 << 24) -#define AMDTP_FDF_AM824 (0 << 19) +#define CIP_EOH_MASK 0x80000000 +#define CIP_EOH_SHIFT 31 +#define CIP_EOH (1u << CIP_EOH_SHIFT) +#define CIP_FMT_MASK 0x3F000000 +#define CIP_FMT_SHIFT 24 +#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT) +#define AMDTP_FDF_MASK 0x00FF0000 #define AMDTP_FDF_SFC_SHIFT 16 +#define AMDTP_FDF_NO_DATA (0xFF << AMDTP_FDF_SFC_SHIFT) +/* only "Clock-based rate controll mode" is supported */ +#define AMDTP_FDF_AM824 (0 << (AMDTP_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 @@ -349,7 +359,7 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle)
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);
To indicate the direction of amdtp stream, this patch adds "direction" member to amdtp_stream structure. To determine the direction, this patch also adds "direction" argument to amdtp_stream_init() function. The amdtp_stream_init() function is exported and used by snd-firewire-speakers so this patch also affects 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/amdtp.c | 2 +- sound/firewire/amdtp.h | 8 +++++++- sound/firewire/speakers.c | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5bb7836..98bc68e 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -50,7 +50,7 @@ static void pcm_period_tasklet(unsigned long data); * @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 direction, enum cip_flags flags) { if (flags != CIP_NONBLOCKING) return -EINVAL; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 82392ea..09803d6 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -37,9 +37,15 @@ struct fw_unit; struct fw_iso_context; struct snd_pcm_substream;
+enum amdtp_stream_direction { + AMDTP_STREAM_RECEIVE = 0, + AMDTP_STREAM_TRANSMIT +}; + struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; + enum amdtp_stream_direction direction; struct fw_iso_context *context; struct mutex mutex;
@@ -72,7 +78,7 @@ struct amdtp_stream { };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum cip_flags flags); + enum amdtp_stream_direction direction, enum cip_flags flags); void amdtp_stream_destroy(struct amdtp_stream *s);
void amdtp_stream_set_rate(struct amdtp_stream *s, unsigned int rate); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 7df20ac..4164163 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -727,7 +727,8 @@ static int fwspk_probe(struct device *unit_dev) if (err < 0) goto err_unit;
- err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + err = amdtp_stream_init(&fwspk->stream, unit, + AMDTP_STREAM_TRANSMIT, CIP_NONBLOCKING); if (err < 0) goto err_connection;
To handle AMDTP receive stream, this patch adds some codes with condition of its direction and new functions. Once amdtp_stream_init() is executed with its direction, AMDTP receive and transmit stream can be handles by the same way.
Unfortunatelly Echo Audio's Fireworks(TM) is not fully compliant to IEC 61883-6 against their guide. This patch include some work arounds for Fireworks.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 274 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 262 insertions(+), 12 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 98bc68e..b8914f7 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -56,6 +56,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, return -EINVAL;
s->unit = fw_unit_get(unit); + s->direction = direction; s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); @@ -138,7 +139,10 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) s->data_block_quadlets = s->pcm_channels; s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
- return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; + if (s->direction == AMDTP_STREAM_RECEIVE) + return 8 + s->syt_interval * s->data_block_quadlets * 4; + else + return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; } EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@@ -148,6 +152,12 @@ static void amdtp_write_s16(struct amdtp_stream *s, static void amdtp_write_s32(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);
/** * amdtp_stream_set_pcm_format - set the PCM format @@ -168,10 +178,16 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S16: - s->transfer_samples = amdtp_write_s16; + if (s->direction == AMDTP_STREAM_RECEIVE) + s->transfer_samples = amdtp_read_s16; + else + s->transfer_samples = amdtp_write_s16; break; case SNDRV_PCM_FORMAT_S32: - s->transfer_samples = amdtp_write_s32; + if (s->direction == AMDTP_STREAM_RECEIVE) + s->transfer_samples = amdtp_read_s32; + else + s->transfer_samples = amdtp_write_s32; break; } } @@ -320,6 +336,58 @@ static void amdtp_write_s16(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 channels, remaining_frames, frame_step, i, c; + u32 *dst; + + channels = s->pcm_channels; + dst = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + 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) { + *dst = be32_to_cpu(*buffer) << 8; + dst += 1; + buffer += 1; + } + buffer += frame_step; + 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 channels, remaining_frames, frame_step, i, c; + u16 *dst; + + channels = s->pcm_channels; + dst = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + 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) { + *dst = be32_to_cpu(*buffer) << 8; + dst += 1; + buffer += 1; + } + buffer += frame_step; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + static void amdtp_fill_pcm_silence(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -342,6 +410,13 @@ static void amdtp_fill_midi(struct amdtp_stream *s, cpu_to_be32(0x80000000); }
+static void amdtp_pull_midi(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + /* not implemented yet */ + return; +} + static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; @@ -410,6 +485,116 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) } }
+static void handle_in_packet_data(struct amdtp_stream *s, + unsigned int data_quadlets) +{ + __be32 *buffer; + unsigned int index, frames, data_block_quadlets, + data_block_counter, ptr; + struct snd_pcm_substream *pcm; + struct fw_iso_packet packet; + int err; + + if (s->packet_index < 0) + return; + index = s->packet_index; + + buffer = s->buffer.packets[index].buffer; + + /* checking CIP headers for AMDTP with restriction of this module */ + if (((be32_to_cpu(buffer[0]) & CIP_EOH_MASK) == CIP_EOH) || + ((be32_to_cpu(buffer[1]) & CIP_EOH_MASK) != CIP_EOH) || + ((be32_to_cpu(buffer[1]) & CIP_FMT_MASK) != CIP_FMT_AM)) { + dev_err(&s->unit->device, "CIP headers error: %08X:%08X\n", + be32_to_cpu(buffer[0]), be32_to_cpu(buffer[1])); + return; + } else if ((data_quadlets < 3) || + ((be32_to_cpu(buffer[1]) & AMDTP_FDF_MASK) == + AMDTP_FDF_NO_DATA)) { + pcm = NULL; + } else { + /* + * NOTE: this module doesn't check dbc and syt field + * + * Echo Audio's Fireworks reports wrong number of data block + * counter. Mostly it reports it with increment of 8 blocks + * but sometimes it increments with NO-DATA packet. + * + * Handling syt field is related to time stamp, + * but the cost is bigger than the effect. + * this module don't support it. + */ + data_block_quadlets = + (be32_to_cpu(buffer[0]) & AMDTP_DBS_MASK) >> + AMDTP_DBS_SHIFT; + data_block_counter = (be32_to_cpu(buffer[0]) & AMDTP_DBC_MASK); + + /* + * NOTE: Echo Audio's Fireworks reports a fixed value for data + * block quadlets but the actual value differs depending on + * current sampling rate. This is a workaround for Fireworks. + */ + if ((data_quadlets - 2) % data_block_quadlets > 0) + s->data_block_quadlets = + s->pcm_channels + s->midi_ports; + else + s->data_block_quadlets = data_block_quadlets; + + /* finish to check CIP header */ + buffer += 2; + + /* + * NOTE: here "frames" is equivalent to "events" + * in IEC 61883-1 + */ + frames = (data_quadlets - 2) / s->data_block_quadlets; + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + s->transfer_samples(s, pcm, buffer, frames); + if (s->midi_ports) + amdtp_pull_midi(s, buffer, frames); + + /* for next packet */ + s->data_block_quadlets = data_block_quadlets; + s->data_block_counter = data_block_counter; + } + + /* queueing a packet for next cycle */ + packet.payload_length = amdtp_stream_get_max_payload(s); + packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL); + packet.skip = 0; + packet.header_length = 4; + + 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; + amdtp_stream_pcm_abort(s); + return; + } + + /* calcurate packet index */ + if (++index >= QUEUE_LENGTH) + index = 0; + s->packet_index = index; + + /* calcurate period and buffer borders */ + if (pcm != NULL) { + 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; @@ -437,6 +622,50 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); }
+static void in_packet_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, data_quadlets, packets = header_length / 4; + __be32 *headers = header; + + /* each fields in an isochronous header are already used in juju */ + for (p = 0; p < packets; p += 1) { + /* how many quadlet for data in this packet */ + data_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + /* handle each packet data */ + handle_in_packet_data(s, data_quadlets); + } + + fw_iso_context_queue_flush(s->context); +} + +static int queue_initial_packets(struct amdtp_stream *s) +{ + struct fw_iso_packet initial_packet; + unsigned int i; + int err; + + /* header length is needed for receive stream */ + initial_packet.payload_length = amdtp_stream_get_max_payload(s); + initial_packet.skip = 0; + initial_packet.header_length = 4; + + for (i = 0; i < QUEUE_LENGTH; ++i) { + initial_packet.interrupt = IS_ALIGNED(s->packet_index + 1, + INTERRUPT_INTERVAL); + err = fw_iso_context_queue(s->context, &initial_packet, + &s->buffer.iso_buffer, s->buffer.packets[i].offset); + if (err < 0) + return err; + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; + } + + return 0; +} + static int queue_initial_skip_packets(struct amdtp_stream *s) { struct fw_iso_packet skip_packet = { @@ -497,16 +726,31 @@ 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;
- err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), - DMA_TO_DEVICE); + /* initialize packet buffer */ + if (s->direction == AMDTP_STREAM_RECEIVE) + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_stream_get_max_payload(s), + DMA_FROM_DEVICE); + else + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_stream_get_max_payload(s), + DMA_TO_DEVICE); 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_packet_callback, s); + /* create isochronous context */ + if (s->direction == AMDTP_STREAM_RECEIVE) + s->context = + fw_iso_context_create(fw_parent_device(s->unit)->card, + FW_ISO_CONTEXT_RECEIVE, + channel, speed, 4, + in_packet_callback, s); + else + s->context = + fw_iso_context_create(fw_parent_device(s->unit)->card, + FW_ISO_CONTEXT_TRANSMIT, + channel, speed, 4, + out_packet_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -519,11 +763,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->packet_index = 0; s->data_block_counter = 0; - err = queue_initial_skip_packets(s); + + if (s->direction == AMDTP_STREAM_RECEIVE) + err = queue_initial_packets(s); + else + err = queue_initial_skip_packets(s); if (err < 0) goto err_context;
- err = fw_iso_context_start(s->context, -1, 0, 0); + /* NOTE: TAG1 matches CIP. This just affects receive stream */ + err = fw_iso_context_start(s->context, -1, 0, + FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context;
Takashi Sakamoto wrote:
To handle AMDTP receive stream, this patch adds some codes with condition of its direction and new functions. Once amdtp_stream_init() is executed with its direction, AMDTP receive and transmit stream can be handles by the same way.
Unfortunatelly Echo Audio's Fireworks(TM) is not fully compliant to IEC 61883-6 against their guide. This patch include some work arounds for Fireworks.
+++ b/sound/firewire/amdtp.c
+static void handle_in_packet_data(struct amdtp_stream *s,
unsigned int data_quadlets)
- /* checking CIP headers for AMDTP with restriction of this module */
- if (((be32_to_cpu(buffer[0]) & CIP_EOH_MASK) == CIP_EOH) ||
((be32_to_cpu(buffer[1]) & CIP_EOH_MASK) != CIP_EOH) ||
((be32_to_cpu(buffer[1]) & CIP_FMT_MASK) != CIP_FMT_AM)) {
It would be easier and a little bit more efficient to convert the two header quadlets at the beginning and to store them in some local variables.
dev_err(&s->unit->device, "CIP headers error: %08X:%08X\n",
be32_to_cpu(buffer[0]), be32_to_cpu(buffer[1]));
return;
This error should either be ignored (resubmit the packet) or cause the stream to abort (see "queueing error" below).
But I'm not sure which.
* Echo Audio's Fireworks reports wrong number of data block
* counter. Mostly it reports it with increment of 8 blocks
* but sometimes it increments with NO-DATA packet.
Does this imply that the DBC field could not be used to detect dropped packets?
* Handling syt field is related to time stamp,
* but the cost is bigger than the effect.
Actually, the SYT specifies the presentation timestamp, but when doing capturing in ALSA, there is no "presentation" in the sense of the spec, so not handling the SYT field is the correct thing to do regardless of the cost. :-)
* NOTE: this module doesn't check dbc ...
data_block_counter = (be32_to_cpu(buffer[0]) & AMDTP_DBC_MASK);
Unused.
/* for next packet */
s->data_block_quadlets = data_block_quadlets;
s->data_block_counter = data_block_counter;
Unused.
- /* calcurate packet index */
- /* calcurate period and buffer borders */
calculate
Regards, Clemens
Clemens,
Thanks for your review. Thanks for your review. I arrange these issues to 6 items below.
amdtp[PATCH4/4]: 1. It would be easier to add some local variables for CIP headers. OK. Actually be32_to_cpu() was used six times. I add "u32 cip_header[2]" for them and reduce the time.
2. "CIP header error" should be handled approproately, ignored or abort. OK. Here I want to select ignoring.
3. The comment about Fireworks' data block counter I'm sorry but it includes wrong description in the patch. It was old one working with inappropriate code. The wrong code reported me the wrong result and I added the comment with it...
This comment is true: "Echo Audio's Fireworks reports wrong number of data block counter. It always reports it with increment by 8 blocks even if actual data blocks different from 8."
4. The comment related to ignoring SYT field should be indicate that ALSA has no spec equivalent to "presentation timestamp". OK. I rewrite the comment to mean it.
5. "data_block_counter" is unused. I forget to add some description to commit log that I plan to use it for future patches to add handling MIDI. For my convinience, I want to keep it here.
6. typo of "calcurate" OK. I rewrite.
Regards
Takashi Sakamoto o-takashi@sakamocchi.jp
(Apr 28 2013 21:52), Clemens Ladisch wrote:
Takashi Sakamoto wrote:
To handle AMDTP receive stream, this patch adds some codes with condition of its direction and new functions. Once amdtp_stream_init() is executed with its direction, AMDTP receive and transmit stream can be handles by the same way.
Unfortunatelly Echo Audio's Fireworks(TM) is not fully compliant to IEC 61883-6 against their guide. This patch include some work arounds for Fireworks.
+++ b/sound/firewire/amdtp.c
+static void handle_in_packet_data(struct amdtp_stream *s,
unsigned int data_quadlets)
- /* checking CIP headers for AMDTP with restriction of this module */
- if (((be32_to_cpu(buffer[0]) & CIP_EOH_MASK) == CIP_EOH) ||
((be32_to_cpu(buffer[1]) & CIP_EOH_MASK) != CIP_EOH) ||
((be32_to_cpu(buffer[1]) & CIP_FMT_MASK) != CIP_FMT_AM)) {
It would be easier and a little bit more efficient to convert the two header quadlets at the beginning and to store them in some local variables.
dev_err(&s->unit->device, "CIP headers error: %08X:%08X\n",
be32_to_cpu(buffer[0]), be32_to_cpu(buffer[1]));
return;
This error should either be ignored (resubmit the packet) or cause the stream to abort (see "queueing error" below).
But I'm not sure which.
* Echo Audio's Fireworks reports wrong number of data block
* counter. Mostly it reports it with increment of 8 blocks
* but sometimes it increments with NO-DATA packet.
Does this imply that the DBC field could not be used to detect dropped packets?
* Handling syt field is related to time stamp,
* but the cost is bigger than the effect.
Actually, the SYT specifies the presentation timestamp, but when doing capturing in ALSA, there is no "presentation" in the sense of the spec, so not handling the SYT field is the correct thing to do regardless of the cost. :-)
* NOTE: this module doesn't check dbc ...
data_block_counter = (be32_to_cpu(buffer[0]) & AMDTP_DBC_MASK);
Unused.
/* for next packet */
s->data_block_quadlets = data_block_quadlets;
s->data_block_counter = data_block_counter;
Unused.
- /* calcurate packet index */
- /* calcurate period and buffer borders */
calculate
Regards, Clemens
Current implementation of snd-firewire-lib can handle only AMDTP transmit stream with PCM. This series of patch enable it to handle both AMDTP receive and transmit stream with PCM according to IEC 61883-6.
I develop with Echo Audio's Fireworks (AudioFirePre8). Unfortunately it's not fully compliant to IEC 61883-6. The last patch includes some work arounds for Fireworks. The snd-fireworks is still under development and not prepared for commit.
And this commit includes some variables and functions which are currently useless. They are for future MIDI implementation referring MMA/AMEI RP-027.
Takashi Sakamoto (4): Rename functions, structure and member name for AMDTP Add macros for fixed value related to AMDTP Add "direction" member to amdtp_stream structure for AMDTP Add handling AMDTP receive stream
sound/firewire/amdtp.c | 425 ++++++++++++++++++++++++++++++++++++--------- sound/firewire/amdtp.h | 72 ++++---- sound/firewire/speakers.c | 33 ++-- 3 files changed, 399 insertions(+), 131 deletions(-)
This patch rename some functions, structure and member name to reuse them in both AMDTP receive and AMDTP transmit stream.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 132 ++++++++++++++++++++++----------------------- sound/firewire/amdtp.h | 66 +++++++++++------------ sound/firewire/speakers.c | 32 +++++------ 3 files changed, 115 insertions(+), 115 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ea995af..d6d1abb 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) { if (flags != CIP_NONBLOCKING) return -EINVAL; @@ -54,29 +54,29 @@ 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(!IS_ERR(s->context)); mutex_destroy(&s->mutex); fw_unit_put(s->unit); } -EXPORT_SYMBOL(amdtp_out_stream_destroy); +EXPORT_SYMBOL(amdtp_stream_destroy);
/** - * amdtp_out_stream_set_rate - set the sample rate - * @s: the AMDTP output stream to configure + * amdtp_stream_set_rate - set the sample rate + * @s: the AMDTP stream to configure * @rate: the sample rate * * The sample rate must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) +void amdtp_stream_set_rate(struct amdtp_stream *s, unsigned int rate) { static const struct { unsigned int rate; @@ -103,17 +103,17 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) } WARN_ON(1); } -EXPORT_SYMBOL(amdtp_out_stream_set_rate); +EXPORT_SYMBOL(amdtp_stream_set_rate);
/** - * 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_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(). + * with amdtp_stream_set_hw_params(), amdtp_stream_set_pcm(), and + * amdtp_stream_set_midi(). */ -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { static const unsigned int max_data_blocks[] = { [CIP_SFC_32000] = 4, @@ -130,25 +130,25 @@ unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; } -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);
/** - * 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 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(!IS_ERR(s->context))) return; @@ -165,24 +165,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;
@@ -215,7 +215,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; @@ -258,7 +258,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) { @@ -284,7 +284,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) { @@ -310,7 +310,7 @@ static void amdtp_write_s16(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; @@ -322,7 +322,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; @@ -332,7 +332,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; @@ -377,7 +377,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; }
@@ -402,7 +402,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) @@ -410,9 +410,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;
/* @@ -427,7 +427,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, @@ -449,17 +449,17 @@ 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_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(); and it must be started before any + * amdtp_stream_set_hw_params(), amdtp_stream_set_pcm(), and + * amdtp_stream_set_midi(); 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; @@ -488,7 +488,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; @@ -501,11 +501,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; @@ -531,15 +531,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) @@ -549,27 +549,27 @@ 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);
@@ -586,16 +586,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;
@@ -607,4 +607,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 b680c5e..482d57e 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -7,13 +7,13 @@ #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 * be used if supported by the device. */ -enum cip_out_flags { +enum cip_flags { CIP_NONBLOCKING = 0, };
@@ -37,9 +37,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;
@@ -47,7 +47,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);
@@ -71,73 +71,73 @@ 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_rate(struct amdtp_out_stream *s, unsigned int rate); -unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); +void amdtp_stream_set_rate(struct amdtp_stream *s, unsigned int rate); +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);
/** - * amdtp_out_stream_set_pcm - configure format of PCM samples - * @s: the AMDTP output stream to be configured + * 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_out_stream_set_pcm(struct amdtp_out_stream *s, - unsigned int pcm_channels) +static inline void amdtp_stream_set_pcm(struct amdtp_stream *s, + unsigned int pcm_channels) { s->pcm_channels = pcm_channels; }
/** - * amdtp_out_stream_set_midi - configure format of MIDI data - * @s: the AMDTP output stream to be configured + * amdtp_stream_set_midi - configure format of MIDI data + * @s: the AMDTP stream to be configured * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) * * This function must not be called while the stream is running. */ -static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, - unsigned int midi_ports) +static inline void amdtp_stream_set_midi(struct amdtp_stream *s, + unsigned int midi_ports) { s->midi_ports = midi_ports; }
/** - * amdtp_out_streaming_error - check for streaming error - * @s: the AMDTP output stream + * 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/speakers.c b/sound/firewire/speakers.c index 1e1f003..7df20ac 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -52,7 +52,7 @@ struct fwspk { struct snd_pcm_substream *pcm; struct mutex mutex; struct cmp_connection connection; - struct amdtp_out_stream stream; + struct amdtp_stream stream; bool stream_running; bool mute; s16 volume[6]; @@ -190,7 +190,7 @@ static int fwspk_close(struct snd_pcm_substream *substream) static void fwspk_stop_stream(struct fwspk *fwspk) { if (fwspk->stream_running) { - amdtp_out_stream_stop(&fwspk->stream); + amdtp_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); fwspk->stream_running = false; } @@ -247,10 +247,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto error;
- amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); - amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); + amdtp_stream_set_rate(&fwspk->stream, params_rate(hw_params)); + amdtp_stream_set_pcm(&fwspk->stream, params_channels(hw_params));
- amdtp_out_stream_set_pcm_format(&fwspk->stream, + amdtp_stream_set_pcm_format(&fwspk->stream, params_format(hw_params));
err = fwspk_set_rate(fwspk, fwspk->stream.sfc); @@ -283,16 +283,16 @@ 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 (!fwspk->stream_running) { 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, + err = amdtp_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) @@ -303,7 +303,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
mutex_unlock(&fwspk->mutex);
- amdtp_out_stream_pcm_prepare(&fwspk->stream); + amdtp_stream_pcm_prepare(&fwspk->stream);
return 0;
@@ -330,7 +330,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; }
@@ -338,7 +338,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) @@ -657,7 +657,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); @@ -727,7 +727,7 @@ static int fwspk_probe(struct device *unit_dev) 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;
@@ -774,7 +774,7 @@ static int fwspk_remove(struct device *dev) { struct fwspk *fwspk = dev_get_drvdata(dev);
- amdtp_out_stream_pcm_abort(&fwspk->stream); + amdtp_stream_pcm_abort(&fwspk->stream); snd_card_disconnect(fwspk->card);
mutex_lock(&fwspk->mutex); @@ -793,14 +793,14 @@ 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 const struct ieee1394_device_id fwspk_id_table[] = {
This patch adds some macros for fixed value related to AMDTP in IEC 61883-6. These macros will be used by followed patches.
This patch also replace existed fixed value to the corresponding macro.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index d6d1abb..e3947ad 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -20,12 +20,22 @@
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */
+#define ISO_DATA_LENGTH_SHIFT 16 #define TAG_CIP 1 - -#define CIP_EOH (1u << 31) -#define CIP_FMT_AM (0x10 << 24) -#define AMDTP_FDF_AM824 (0 << 19) +#define CIP_EOH_MASK 0x80000000 +#define CIP_EOH_SHIFT 31 +#define CIP_EOH (1u << CIP_EOH_SHIFT) +#define CIP_FMT_MASK 0x3F000000 +#define CIP_FMT_SHIFT 24 +#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT) +#define AMDTP_FDF_MASK 0x00FF0000 #define AMDTP_FDF_SFC_SHIFT 16 +#define AMDTP_FDF_NO_DATA (0xFF << AMDTP_FDF_SFC_SHIFT) +/* only "Clock-based rate controll mode" is supported */ +#define AMDTP_FDF_AM824 (0 << (AMDTP_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 @@ -349,7 +359,7 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle)
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);
To indicate the direction of amdtp stream, this patch adds "direction" member to amdtp_stream structure. To determine the direction, this patch also adds "direction" argument to amdtp_stream_init() function. The amdtp_stream_init() function is exported and used by snd-firewire-speakers so this patch also affects 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/amdtp.c | 2 +- sound/firewire/amdtp.h | 8 +++++++- sound/firewire/speakers.c | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index e3947ad..59fd34c 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -50,7 +50,7 @@ static void pcm_period_tasklet(unsigned long data); * @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 direction, enum cip_flags flags) { if (flags != CIP_NONBLOCKING) return -EINVAL; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 482d57e..f3b9403 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -37,9 +37,15 @@ struct fw_unit; struct fw_iso_context; struct snd_pcm_substream;
+enum amdtp_stream_direction { + AMDTP_STREAM_RECEIVE = 0, + AMDTP_STREAM_TRANSMIT +}; + struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; + enum amdtp_stream_direction direction; struct fw_iso_context *context; struct mutex mutex;
@@ -72,7 +78,7 @@ struct amdtp_stream { };
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum cip_flags flags); + enum amdtp_stream_direction direction, enum cip_flags flags); void amdtp_stream_destroy(struct amdtp_stream *s);
void amdtp_stream_set_rate(struct amdtp_stream *s, unsigned int rate); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 7df20ac..4164163 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -727,7 +727,8 @@ static int fwspk_probe(struct device *unit_dev) if (err < 0) goto err_unit;
- err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + err = amdtp_stream_init(&fwspk->stream, unit, + AMDTP_STREAM_TRANSMIT, CIP_NONBLOCKING); if (err < 0) goto err_connection;
To handle AMDTP receive stream, this patch adds some codes with condition of its direction and new functions. Once amdtp_stream_init() is executed with its direction, AMDTP receive and transmit stream can be handles by the same way.
Unfortunatelly Echo Audio's Fireworks(TM) is not fully compliant to IEC 61883-6 against their guide. This patch include some work arounds for Fireworks.
For future implementation of MIDI referring to MMA/AMEI RP-027, this commit add some variables and functions which are currently useless.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/amdtp.c | 275 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 263 insertions(+), 12 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 59fd34c..136087b 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -56,6 +56,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, return -EINVAL;
s->unit = fw_unit_get(unit); + s->direction = direction; s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); @@ -138,7 +139,10 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) s->data_block_quadlets = s->pcm_channels; s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
- return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; + if (s->direction == AMDTP_STREAM_RECEIVE) + return 8 + s->syt_interval * s->data_block_quadlets * 4; + else + return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; } EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@@ -148,6 +152,12 @@ static void amdtp_write_s16(struct amdtp_stream *s, static void amdtp_write_s32(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);
/** * amdtp_stream_set_pcm_format - set the PCM format @@ -168,10 +178,16 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S16: - s->transfer_samples = amdtp_write_s16; + if (s->direction == AMDTP_STREAM_RECEIVE) + s->transfer_samples = amdtp_read_s16; + else + s->transfer_samples = amdtp_write_s16; break; case SNDRV_PCM_FORMAT_S32: - s->transfer_samples = amdtp_write_s32; + if (s->direction == AMDTP_STREAM_RECEIVE) + s->transfer_samples = amdtp_read_s32; + else + s->transfer_samples = amdtp_write_s32; break; } } @@ -320,6 +336,58 @@ static void amdtp_write_s16(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 channels, remaining_frames, frame_step, i, c; + u32 *dst; + + channels = s->pcm_channels; + dst = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + 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) { + *dst = be32_to_cpu(*buffer) << 8; + dst += 1; + buffer += 1; + } + buffer += frame_step; + 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 channels, remaining_frames, frame_step, i, c; + u16 *dst; + + channels = s->pcm_channels; + dst = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + 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) { + *dst = be32_to_cpu(*buffer) << 8; + dst += 1; + buffer += 1; + } + buffer += frame_step; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + static void amdtp_fill_pcm_silence(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { @@ -342,6 +410,13 @@ static void amdtp_fill_midi(struct amdtp_stream *s, cpu_to_be32(0x80000000); }
+static void amdtp_pull_midi(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + /* not implemented yet */ + return; +} + static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; @@ -410,6 +485,117 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle) } }
+static void handle_in_packet_data(struct amdtp_stream *s, + unsigned int data_quadlets) +{ + __be32 *buffer; + u32 cip_header[2]; + unsigned int index, frames, data_block_quadlets, + data_block_counter, ptr; + struct snd_pcm_substream *pcm; + struct fw_iso_packet packet; + int err; + + if (s->packet_index < 0) + return; + index = s->packet_index; + + buffer = s->buffer.packets[index].buffer; + cip_header[0] = be32_to_cpu(buffer[0]); + cip_header[1] = be32_to_cpu(buffer[1]); + + /* checking CIP headers for AMDTP with restriction of this module */ + 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_err(&s->unit->device, "CIP header error: %08X:%08X\n", + cip_header[0], cip_header[1]); + pcm = NULL; + } else if ((data_quadlets < 3) || + ((cip_header[1] & AMDTP_FDF_MASK) == AMDTP_FDF_NO_DATA)) { + pcm = NULL; + } else { + /* + * NOTE: this module doesn't check dbc and syt field + * + * Echo Audio's Fireworks reports wrong number of data block + * counter. It always reports it with increment by 8 blocks + * even if actual data blocks different from 8. + * + * Handling syt field is related to "presentation" time stamp, + * but ALSA has no implements equivalent to it so this module + * don't support it. + */ + data_block_quadlets = (cip_header[0] & AMDTP_DBS_MASK) >> + AMDTP_DBS_SHIFT; + data_block_counter = cip_header[0] & AMDTP_DBC_MASK; + + /* + * NOTE: Echo Audio's Fireworks reports a fixed value for data + * block quadlets but the actual value differs depending on + * current sampling rate. This is a workaround for Fireworks. + */ + if ((data_quadlets - 2) % data_block_quadlets > 0) + s->data_block_quadlets = + s->pcm_channels + s->midi_ports; + else + s->data_block_quadlets = data_block_quadlets; + + /* finish to check CIP header */ + buffer += 2; + + /* + * NOTE: here "frames" is equivalent to "events" + * in IEC 61883-1 + */ + frames = (data_quadlets - 2) / s->data_block_quadlets; + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + s->transfer_samples(s, pcm, buffer, frames); + if (s->midi_ports) + amdtp_pull_midi(s, buffer, frames); + + /* for next packet */ + s->data_block_quadlets = data_block_quadlets; + s->data_block_counter = data_block_counter; + } + + /* queueing a packet for next cycle */ + packet.payload_length = amdtp_stream_get_max_payload(s); + packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL); + packet.skip = 0; + packet.header_length = 4; + + 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; + amdtp_stream_pcm_abort(s); + return; + } + + /* calculate packet index */ + if (++index >= QUEUE_LENGTH) + index = 0; + s->packet_index = index; + + /* calculate period and buffer borders */ + if (pcm != NULL) { + 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; @@ -437,6 +623,50 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); }
+static void in_packet_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, data_quadlets, packets = header_length / 4; + __be32 *headers = header; + + /* each fields in an isochronous header are already used in juju */ + for (p = 0; p < packets; p += 1) { + /* how many quadlet for data in this packet */ + data_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + /* handle each packet data */ + handle_in_packet_data(s, data_quadlets); + } + + fw_iso_context_queue_flush(s->context); +} + +static int queue_initial_packets(struct amdtp_stream *s) +{ + struct fw_iso_packet initial_packet; + unsigned int i; + int err; + + /* header length is needed for receive stream */ + initial_packet.payload_length = amdtp_stream_get_max_payload(s); + initial_packet.skip = 0; + initial_packet.header_length = 4; + + for (i = 0; i < QUEUE_LENGTH; ++i) { + initial_packet.interrupt = IS_ALIGNED(s->packet_index + 1, + INTERRUPT_INTERVAL); + err = fw_iso_context_queue(s->context, &initial_packet, + &s->buffer.iso_buffer, s->buffer.packets[i].offset); + if (err < 0) + return err; + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; + } + + return 0; +} + static int queue_initial_skip_packets(struct amdtp_stream *s) { struct fw_iso_packet skip_packet = { @@ -497,16 +727,31 @@ 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;
- err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), - DMA_TO_DEVICE); + /* initialize packet buffer */ + if (s->direction == AMDTP_STREAM_RECEIVE) + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_stream_get_max_payload(s), + DMA_FROM_DEVICE); + else + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_stream_get_max_payload(s), + DMA_TO_DEVICE); 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_packet_callback, s); + /* create isochronous context */ + if (s->direction == AMDTP_STREAM_RECEIVE) + s->context = + fw_iso_context_create(fw_parent_device(s->unit)->card, + FW_ISO_CONTEXT_RECEIVE, + channel, speed, 4, + in_packet_callback, s); + else + s->context = + fw_iso_context_create(fw_parent_device(s->unit)->card, + FW_ISO_CONTEXT_TRANSMIT, + channel, speed, 4, + out_packet_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -519,11 +764,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
s->packet_index = 0; s->data_block_counter = 0; - err = queue_initial_skip_packets(s); + + if (s->direction == AMDTP_STREAM_RECEIVE) + err = queue_initial_packets(s); + else + err = queue_initial_skip_packets(s); if (err < 0) goto err_context;
- err = fw_iso_context_start(s->context, -1, 0, 0); + /* NOTE: TAG1 matches CIP. This just affects receive stream */ + err = fw_iso_context_start(s->context, -1, 0, + FW_ISO_CONTEXT_MATCH_TAG1); if (err < 0) goto err_context;
participants (2)
-
Clemens Ladisch
-
Takashi Sakamoto