From patchwork Sun May 18 13:36:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Takashi Sakamoto X-Patchwork-Id: 4198031 X-Patchwork-Delegate: tiwai@suse.de Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 554CABEEAB for ; Sun, 18 May 2014 13:42:22 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 05089202A1 for ; Sun, 18 May 2014 13:42:21 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 3341620127 for ; Sun, 18 May 2014 13:42:18 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 0A6B42654A6; Sun, 18 May 2014 15:42:17 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 486BD26509A; Sun, 18 May 2014 15:37:22 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 84D4826512F; Sun, 18 May 2014 15:37:20 +0200 (CEST) Received: from smtp302.phy.lolipop.jp (smtp302.phy.lolipop.jp [210.157.22.85]) by alsa0.perex.cz (Postfix) with ESMTP id 11FCC264F47 for ; Sun, 18 May 2014 15:36:59 +0200 (CEST) Received: from smtp302.phy.lolipop.lan (HELO smtp302.phy.lolipop.jp) (172.17.1.85) (smtp-auth username m12129643-o-takashi, mechanism plain) by smtp302.phy.lolipop.jp (qpsmtpd/0.82) with ESMTPA; Sun, 18 May 2014 22:36:58 +0900 Received: from 127.0.0.1 (127.0.0.1) by smtp302.phy.lolipop.jp (LOLIPOP-Fsecure); Sun, 18 May 2014 22:36:39 +0900 (JST) X-Virus-Status: clean(LOLIPOP-Fsecure) From: Takashi Sakamoto To: clemens@ladisch.de Date: Sun, 18 May 2014 22:36:35 +0900 Message-Id: <1400420198-24312-11-git-send-email-o-takashi@sakamocchi.jp> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1400420198-24312-1-git-send-email-o-takashi@sakamocchi.jp> References: <1400420198-24312-1-git-send-email-o-takashi@sakamocchi.jp> Cc: alsa-devel@alsa-project.org, ffado-devel@lists.sf.net Subject: [alsa-devel] [PATCH 10/13] dice: Add support for duplex streams with synchronization X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This commit adds support for AMDTP in-stream. As a result, Dice driver supports full duplex streams with synchronization. When Dice chipset is 'enabled', it starts stream with correct settings. And 'enabling' is global setting. So, when a stream is running, an opposite stream can't start unless turning off 'enabling'. So a pair of streams must be running. This is a loss of CPU usage when single stream is required. Currently, sampling clock source is restricted to SYT-Match mode. This is improved in followed commit. I note that at SYT-Match mode, Dice can select from 4 streams for synchronization but this driver just uses the 1st stream for simplicity. Signed-off-by: Takashi Sakamoto --- sound/firewire/dice/dice.c | 18 ++- sound/firewire/dice/dice.h | 26 +++-- sound/firewire/dice/dice_pcm.c | 4 +- sound/firewire/dice/dice_stream.c | 227 ++++++++++++++++++++++++++++---------- 4 files changed, 202 insertions(+), 73 deletions(-) diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 33c5253..6e849e0 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -141,6 +141,8 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) int err; if (highest_supported_mode_rate(dice, mode, &rate) < 0) { + dice->tx_channels[mode] = 0; + dice->tx_midi_ports[mode] = 0; dice->rx_channels[mode] = 0; dice->rx_midi_ports[mode] = 0; return 0; @@ -150,6 +152,14 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) if (err < 0) return err; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + values, 2 * 4); + if (err < 0) + return err; + + dice->tx_channels[mode] = be32_to_cpu(values[0]); + dice->tx_midi_ports[mode] = be32_to_cpu(values[1]); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, values, 2 * 4); if (err < 0) @@ -279,13 +289,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) snd_dice_create_proc(dice); - err = snd_dice_stream_init(dice); + err = snd_dice_stream_init_duplex(dice); if (err < 0) goto error; err = snd_card_register(card); if (err < 0) { - snd_dice_stream_destroy(dice); + snd_dice_stream_destroy_duplex(dice); goto error; } @@ -303,7 +313,7 @@ static void dice_remove(struct fw_unit *unit) snd_card_disconnect(dice->card); - snd_dice_stream_destroy(dice); + snd_dice_stream_destroy_duplex(dice); snd_card_free_when_closed(dice->card); } @@ -320,7 +330,7 @@ static void dice_bus_reset(struct fw_unit *unit) } dice->global_enabled = false; - snd_dice_stream_update(dice); + snd_dice_stream_update_duplex(dice); } #define DICE_INTERFACE 0x000001 diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 8be530f..a868485 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -52,18 +52,28 @@ struct snd_dice { unsigned int rsrv_offset; unsigned int clock_caps; + unsigned int tx_channels[3]; unsigned int rx_channels[3]; + unsigned int tx_midi_ports[3]; unsigned int rx_midi_ports[3]; + struct fw_address_handler notification_handler; int owner_generation; + u32 notification_bits; + + /* For uapi */ int dev_lock_count; /* > 0 driver, < 0 userspace */ bool dev_lock_changed; - bool global_enabled; - struct completion clock_accepted; wait_queue_head_t hwdep_wait; - u32 notification_bits; + + /* For streaming */ + struct fw_iso_resources tx_resources; struct fw_iso_resources rx_resources; + struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; + bool global_enabled; + struct completion clock_accepted; + atomic_t substreams_counter; }; enum snd_dice_addr_type { @@ -160,11 +170,11 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, unsigned int *mode); -int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate); -void snd_dice_stream_stop(struct snd_dice *dice); -int snd_dice_stream_init(struct snd_dice *dice); -void snd_dice_stream_destroy(struct snd_dice *dice); -void snd_dice_stream_update(struct snd_dice *dice); +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); +void snd_dice_stream_stop_duplex(struct snd_dice *dice); +int snd_dice_stream_init_duplex(struct snd_dice *dice); +void snd_dice_stream_destroy_duplex(struct snd_dice *dice); +void snd_dice_stream_update_duplex(struct snd_dice *dice); int snd_dice_stream_lock_try(struct snd_dice *dice); void snd_dice_stream_lock_release(struct snd_dice *dice); diff --git a/sound/firewire/dice/dice_pcm.c b/sound/firewire/dice/dice_pcm.c index 79958e5..414c1c6 100644 --- a/sound/firewire/dice/dice_pcm.c +++ b/sound/firewire/dice/dice_pcm.c @@ -181,7 +181,7 @@ static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - snd_dice_stream_stop(dice); + snd_dice_stream_stop_duplex(dice); return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -191,7 +191,7 @@ static int playback_prepare(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; int err; - err = snd_dice_stream_start(dice, substream->runtime->rate); + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&dice->rx_stream); diff --git a/sound/firewire/dice/dice_stream.c b/sound/firewire/dice/dice_stream.c index ed5c961..d8b2ca6 100644 --- a/sound/firewire/dice/dice_stream.c +++ b/sound/firewire/dice/dice_stream.c @@ -40,52 +40,79 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, return -EINVAL; } -static void release_resources(struct snd_dice *dice) +static void release_resources(struct snd_dice *dice, + struct fw_iso_resources *resources) { unsigned int channel; /* Reset channel number */ channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); - - fw_iso_resources_free(&dice->rx_resources); + if (resources == &dice->tx_resources) + snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); + + fw_iso_resources_free(resources); } -static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes) +static int keep_resources(struct snd_dice *dice, + struct fw_iso_resources *resources, + unsigned int max_payload_bytes) { unsigned int channel; int err; - err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes, - fw_parent_device(dice->unit)->max_speed); + err = fw_iso_resources_allocate(resources, max_payload_bytes, + fw_parent_device(dice->unit)->max_speed); if (err < 0) goto end; /* Set channel number */ - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, 4); + channel = cpu_to_be32(resources->channel); + if (resources == &dice->tx_resources) + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); if (err < 0) - release_resources(dice); + release_resources(dice, resources); end: return err; } -static void stop_stream(struct snd_dice *dice) +static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) { - amdtp_stream_pcm_abort(&dice->rx_stream); - amdtp_stream_stop(&dice->rx_stream); - release_resources(dice); + amdtp_stream_pcm_abort(stream); + amdtp_stream_stop(stream); + + if (stream == &dice->tx_stream) + release_resources(dice, &dice->tx_resources); + else + release_resources(dice, &dice->rx_resources); } -static int start_stream(struct snd_dice *dice, unsigned int rate) +static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, + unsigned int rate) { + struct fw_iso_resources *resources; unsigned int i, mode, pcm_chs, midi_ports; int err; err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (err < 0) goto end; + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + pcm_chs = dice->tx_channels[mode]; + midi_ports = dice->tx_midi_ports[mode]; + } else { + resources = &dice->rx_resources; + pcm_chs = dice->rx_channels[mode]; + midi_ports = dice->rx_midi_ports[mode]; + } /* * At rates above 96 kHz, pretend that the stream runs at half the @@ -93,47 +120,67 @@ static int start_stream(struct snd_dice *dice, unsigned int rate) * of a channel are stored consecutively in the packet. Requires * blocking mode and PCM buffer size should be aligned to SYT_INTERVAL. */ - pcm_chs = dice->rx_channels[mode]; - midi_ports = dice->rx_midi_ports[mode]; if (mode >= 2) { for (i = 0; i < pcm_chs; i++) { - dice->rx_stream.pcm_positions[i * 2] = i; - dice->rx_stream.pcm_positions[i * 2 + 1] = i + pcm_chs; + stream->pcm_positions[i * 2] = i; + stream->pcm_positions[i * 2 + 1] = i + pcm_chs; } rate /= 2; pcm_chs *= 2; } - amdtp_stream_set_parameters(&dice->rx_stream, rate, - pcm_chs, midi_ports); + amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); - err = keep_resources(dice, - amdtp_stream_get_max_payload(&dice->rx_stream)); + err = keep_resources(dice, resources, + amdtp_stream_get_max_payload(stream)); if (err < 0) { dev_err(&dice->unit->device, "fail to keep isochronous resources\n"); goto end; } - err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, + err = amdtp_stream_start(stream, resources->channel, fw_parent_device(dice->unit)->max_speed); if (err < 0) - release_resources(dice); + release_resources(dice, resources); end: return err; } -int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) +static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) { + /* Currently, clock source is fixed at SYT-Match mode. */ + *sync_mode = 0; + return 0; +} + +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) +{ + struct amdtp_stream *master, *slave; unsigned int curr_rate; - int err; + enum cip_flags sync_mode; + int err = 0; + + if (atomic_read(&dice->substreams_counter) == 0) + goto end; mutex_lock(&dice->mutex); + err = get_sync_mode(dice, &sync_mode); + if (err < 0) + goto end; + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master = &dice->tx_stream; + slave = &dice->rx_stream; + } else { + master = &dice->rx_stream; + slave = &dice->tx_stream; + } + /* Some packet queueing errors. */ - if (amdtp_streaming_error(&dice->rx_stream)) - stop_stream(dice); + if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) + stop_stream(dice, slave); /* Stop stream if rate is different. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); @@ -143,11 +190,17 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) goto end; } if (rate != curr_rate) - stop_stream(dice); + stop_stream(dice, slave); - if (!amdtp_stream_running(&dice->rx_stream)) { + if (!amdtp_stream_running(slave)) + stop_stream(dice, master); + + if (!amdtp_stream_running(master)) { + stop_stream(dice, slave); snd_dice_transaction_clear_enable(dice); + amdtp_stream_set_sync(sync_mode, master, slave); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) { dev_err(&dice->unit->device, @@ -155,82 +208,135 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) goto end; } - /* Start stream. */ - err = start_stream(dice, rate); + /* Start both streams. */ + err = start_stream(dice, master, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to start AMDTP master stream\n"); + goto end; + } + err = start_stream(dice, slave, rate); if (err < 0) { dev_err(&dice->unit->device, - "fail to start AMDTP stream\n"); + "fail to start AMDTP slave stream\n"); + stop_stream(dice, master); goto end; } err = snd_dice_transaction_set_enable(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to enable interface\n"); - stop_stream(dice); + stop_stream(dice, slave); + stop_stream(dice, master); goto end; } - if (!amdtp_stream_wait_callback(&dice->rx_stream, - CALLBACK_TIMEOUT)) { + /* Wait first callbacks */ + if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { snd_dice_transaction_clear_enable(dice); - stop_stream(dice); + stop_stream(dice, master); + stop_stream(dice, slave); err = -ETIMEDOUT; } } - end: mutex_unlock(&dice->mutex); return err; } -void snd_dice_stream_stop(struct snd_dice *dice) +void snd_dice_stream_stop_duplex(struct snd_dice *dice) { + if (atomic_read(&dice->substreams_counter) > 0) + return; + mutex_lock(&dice->mutex); snd_dice_transaction_clear_enable(dice); - stop_stream(dice); + + stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream); mutex_unlock(&dice->mutex); } -int snd_dice_stream_init(struct snd_dice *dice) +static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) { int err; + struct fw_iso_resources *resources; - err = fw_iso_resources_init(&dice->rx_resources, dice->unit); + if (stream == &dice->tx_stream) + resources = &dice->tx_resources; + else + resources = &dice->rx_resources; + + err = fw_iso_resources_init(resources, dice->unit); if (err < 0) goto end; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; + resources->channels_mask = 0x00000000ffffffffuLL; - err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM, + err = amdtp_stream_init(stream, dice->unit, AMDTP_OUT_STREAM, CIP_BLOCKING); + if (err < 0) { + amdtp_stream_destroy(stream); + fw_iso_resources_destroy(resources); + } +end: + return err; +} + +static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +{ + amdtp_stream_destroy(stream); + + if (stream == &dice->tx_stream) + fw_iso_resources_destroy(&dice->rx_resources); + else + fw_iso_resources_destroy(&dice->rx_resources); +} + +int snd_dice_stream_init_duplex(struct snd_dice *dice) +{ + int err; + + atomic_set(&dice->substreams_counter, 0); + + err = init_stream(dice, &dice->tx_stream); if (err < 0) - goto error; + goto end; - err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + err = init_stream(dice, &dice->rx_stream); if (err < 0) - goto error; + goto end; + + /* Currently, clock source is fixed at SYT-Match mode. */ + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + if (err < 0) { + destroy_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->tx_stream); + } end: return err; -error: - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); - return err; } -void snd_dice_stream_destroy(struct snd_dice *dice) +void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { mutex_lock(&dice->mutex); snd_dice_transaction_clear_enable(dice); - stop_stream(dice); - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); + + stop_stream(dice, &dice->tx_stream); + destroy_stream(dice, &dice->tx_stream); + + stop_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->rx_stream); + + atomic_set(&dice->substreams_counter, 0); mutex_unlock(&dice->mutex); } -void snd_dice_stream_update(struct snd_dice *dice) +void snd_dice_stream_update_duplex(struct snd_dice *dice) { /* * On a bus reset, the DICE firmware disables streaming and then goes @@ -242,7 +348,10 @@ void snd_dice_stream_update(struct snd_dice *dice) */ mutex_lock(&dice->mutex); - stop_stream(dice); + stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream); + + fw_iso_resources_update(&dice->tx_resources); fw_iso_resources_update(&dice->rx_resources); mutex_unlock(&dice->mutex);