From e84d15f619c13e83b33023c84527ee35ef01b6b4 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 4 Sep 2011 22:12:48 +0200 Subject: [PATCH] ALSA: dice, firewire-lib: add blocking mode Allow AMDTP output streams to use blocking mode. Use it for DICE devices, because the old DICE-II chip will in some cases not be able to lock to non-blocking streams (erratum E7). Signed-off-by: Clemens Ladisch --- sound/firewire/amdtp.c | 54 +++++++++++++++++++++++++++----------------------- sound/firewire/amdtp.h | 7 ++++++- sound/firewire/dice.c | 2 +- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ea995af..efb2e29 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data); int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, enum cip_out_flags flags) { - if (flags != CIP_NONBLOCKING) - return -EINVAL; - s->unit = fw_unit_get(unit); s->flags = flags; s->context = ERR_PTR(-1); @@ -96,12 +93,20 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) return; for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) - if (rate_info[sfc].rate == rate) { - s->sfc = sfc; - s->syt_interval = rate_info[sfc].syt_interval; - return; - } + if (rate_info[sfc].rate == rate) + goto sfc_found; WARN_ON(1); + return; + +sfc_found: + s->sfc = sfc; + s->syt_interval = rate_info[sfc].syt_interval; + + /* 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; } EXPORT_SYMBOL(amdtp_out_stream_set_rate); @@ -110,25 +115,15 @@ EXPORT_SYMBOL(amdtp_out_stream_set_rate); * @s: the AMDTP output 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 + * with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and * amdtp_out_stream_set_midi(). */ unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) { - static const unsigned int max_data_blocks[] = { - [CIP_SFC_32000] = 4, - [CIP_SFC_44100] = 6, - [CIP_SFC_48000] = 6, - [CIP_SFC_88200] = 12, - [CIP_SFC_96000] = 12, - [CIP_SFC_176400] = 23, - [CIP_SFC_192000] = 24, - }; - 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; + return 8 + s->syt_interval * s->data_block_quadlets * 4; } EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); @@ -248,7 +243,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, s->last_syt_offset = syt_offset; if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + syt_offset += s->transfer_delay; syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt += syt_offset % TICKS_PER_CYCLE; @@ -344,8 +339,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) return; index = s->packet_index; - data_blocks = calculate_data_blocks(s); syt = calculate_syt(s, cycle); + if (!(s->flags & CIP_BLOCKING)) { + data_blocks = calculate_data_blocks(s); + } else { + if (syt != 0xffff) { + data_blocks = s->syt_interval; + } else { + data_blocks = 0; + syt = 0xffffff; + } + } buffer = s->buffer.packets[index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | @@ -455,9 +459,9 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) * @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 - * PCM or MIDI device can be started. + * amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), + * amdtp_out_stream_set_midi(), and amdtp_out_stream_set_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) { diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index f6103d6..fd4ce30 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -11,9 +11,13 @@ * 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. + * @CIP_BLOCKING: In blocking mode, each packet contains either zero or + * SYT_INTERVAL samples, with these two types alternating so that + * the overall sample rate comes out right. */ enum cip_out_flags { - CIP_NONBLOCKING = 0, + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, }; /** @@ -51,6 +55,7 @@ struct amdtp_out_stream { __be32 *buffer, unsigned int frames); unsigned int syt_interval; + unsigned int transfer_delay; unsigned int source_node_id_field; struct iso_packets_buffer buffer; diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index 02c7b5a..63446f8 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -942,7 +942,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) goto err_notification_handler; dice->resources.channels_mask = 0x00000000ffffffffuLL; - err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING); + err = amdtp_out_stream_init(&dice->stream, unit, CIP_BLOCKING); if (err < 0) goto err_resources; -- 2.7.4