summaryrefslogtreecommitdiff
path: root/sound/firewire/dice/dice-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/dice/dice-stream.c')
-rw-r--r--sound/firewire/dice/dice-stream.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
new file mode 100644
index 0000000..4c4c4ff
--- /dev/null
+++ b/sound/firewire/dice/dice-stream.c
@@ -0,0 +1,207 @@
+/*
+ * dice_stream.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "dice.h"
+
+const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
+ /* mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+ unsigned int *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+ if (!(dice->clock_caps & BIT(i)))
+ continue;
+ if (snd_dice_rates[i] != rate)
+ continue;
+
+ *mode = (i - 1) / 2;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int snd_dice_stream_start_packets(struct snd_dice *dice)
+{
+ int err;
+
+ if (amdtp_stream_running(&dice->rx_stream))
+ return 0;
+
+ err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel,
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ err = snd_dice_transaction_set_enable(dice);
+ if (err < 0) {
+ amdtp_stream_stop(&dice->rx_stream);
+ return err;
+ }
+
+ return 0;
+}
+
+int snd_dice_stream_start(struct snd_dice *dice)
+{
+ __be32 channel;
+ int err;
+
+ if (!dice->rx_resources.allocated) {
+ err = fw_iso_resources_allocate(&dice->rx_resources,
+ amdtp_stream_get_max_payload(&dice->rx_stream),
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ channel = cpu_to_be32(dice->rx_resources.channel);
+ err = snd_dice_transaction_write_tx(dice, RX_ISOCHRONOUS,
+ &channel, 4);
+ if (err < 0)
+ goto err_resources;
+ }
+
+ err = snd_dice_stream_start_packets(dice);
+ if (err < 0)
+ goto err_rx_channel;
+
+ return 0;
+
+err_rx_channel:
+ channel = cpu_to_be32((u32)-1);
+ snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+err_resources:
+ fw_iso_resources_free(&dice->rx_resources);
+error:
+ return err;
+}
+
+void snd_dice_stream_stop_packets(struct snd_dice *dice)
+{
+ if (!amdtp_stream_running(&dice->rx_stream))
+ return;
+
+ snd_dice_transaction_clear_enable(dice);
+ amdtp_stream_stop(&dice->rx_stream);
+}
+
+void snd_dice_stream_stop(struct snd_dice *dice)
+{
+ __be32 channel;
+
+ snd_dice_stream_stop_packets(dice);
+
+ if (!dice->rx_resources.allocated)
+ return;
+
+ channel = cpu_to_be32((u32)-1);
+ snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
+
+ fw_iso_resources_free(&dice->rx_resources);
+}
+
+int snd_dice_stream_init(struct snd_dice *dice)
+{
+ int err;
+
+ err = fw_iso_resources_init(&dice->rx_resources, dice->unit);
+ if (err < 0)
+ goto end;
+ dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
+
+ err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM,
+ CIP_BLOCKING);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
+ if (err < 0)
+ goto error;
+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)
+{
+ amdtp_stream_pcm_abort(&dice->rx_stream);
+ snd_dice_stream_stop(dice);
+ amdtp_stream_destroy(&dice->rx_stream);
+ fw_iso_resources_destroy(&dice->rx_resources);
+}
+
+void snd_dice_stream_update(struct snd_dice *dice)
+{
+ /*
+ * On a bus reset, the DICE firmware disables streaming and then goes
+ * off contemplating its own navel for hundreds of milliseconds before
+ * it can react to any of our attempts to reenable streaming. This
+ * means that we lose synchronization anyway, so we force our streams
+ * to stop so that the application can restart them in an orderly
+ * manner.
+ */
+ dice->global_enabled = false;
+
+ amdtp_stream_pcm_abort(&dice->rx_stream);
+ snd_dice_stream_stop_packets(dice);
+ fw_iso_resources_update(&dice->rx_resources);
+}
+
+static void dice_lock_changed(struct snd_dice *dice)
+{
+ dice->dev_lock_changed = true;
+ wake_up(&dice->hwdep_wait);
+}
+
+int snd_dice_stream_lock_try(struct snd_dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (dice->dev_lock_count++ == 0)
+ dice_lock_changed(dice);
+ err = 0;
+out:
+ spin_unlock_irq(&dice->lock);
+ return err;
+}
+
+void snd_dice_stream_lock_release(struct snd_dice *dice)
+{
+ spin_lock_irq(&dice->lock);
+
+ if (WARN_ON(dice->dev_lock_count <= 0))
+ goto out;
+
+ if (--dice->dev_lock_count == 0)
+ dice_lock_changed(dice);
+out:
+ spin_unlock_irq(&dice->lock);
+}