--- /dev/null
+/*
+ * 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)) {
+ 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);
+}
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
-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,
-};
-
-static unsigned int rate_to_index(unsigned int rate)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
- if (snd_dice_rates[i] == rate)
- return i;
-
- return 0;
-}
-
-static unsigned int rate_index_to_mode(unsigned int rate_index)
-{
- return ((int)rate_index - 1) / 2;
-}
-
-static void dice_lock_changed(struct snd_dice *dice)
-{
- dice->dev_lock_changed = true;
- wake_up(&dice->hwdep_wait);
-}
-
-static int dice_try_lock(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;
-}
-
-static void dice_unlock(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);
-}
-
static int dice_rate_constraint(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_dice *dice = rule->private;
- const struct snd_interval *channels =
+
+ const struct snd_interval *c =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval *rate =
+ struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval allowed_rates = {
+ struct snd_interval rates = {
.min = UINT_MAX, .max = 0, .integer = 1
};
- unsigned int i, mode;
+ unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
- mode = rate_index_to_mode(i);
- if ((dice->clock_caps & (1 << i)) &&
- snd_interval_test(channels, dice->rx_channels[mode])) {
- allowed_rates.min = min(allowed_rates.min,
- snd_dice_rates[i]);
- allowed_rates.max = max(allowed_rates.max,
- snd_dice_rates[i]);
- }
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
}
- return snd_interval_refine(rate, &allowed_rates);
+ return snd_interval_refine(r, &rates);
}
static int dice_channels_constraint(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_dice *dice = rule->private;
- const struct snd_interval *rate =
+
+ const struct snd_interval *r =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels =
+ struct snd_interval *c =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval allowed_channels = {
+ struct snd_interval channels = {
.min = UINT_MAX, .max = 0, .integer = 1
};
- unsigned int i, mode;
+ unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i)
- if ((dice->clock_caps & (1 << i)) &&
- snd_interval_test(rate, snd_dice_rates[i])) {
- mode = rate_index_to_mode(i);
- allowed_channels.min = min(allowed_channels.min,
- dice->rx_channels[mode]);
- allowed_channels.max = max(allowed_channels.max,
- dice->rx_channels[mode]);
- }
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
- return snd_interval_refine(channels, &allowed_channels);
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ channels.min = min(channels.min, pcm_channels[mode]);
+ channels.max = max(channels.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &channels);
}
static int dice_open(struct snd_pcm_substream *substream)
unsigned int i;
int err;
- err = dice_try_lock(dice);
+ err = snd_dice_stream_lock_try(dice);
if (err < 0)
goto error;
return 0;
err_lock:
- dice_unlock(dice);
+ snd_dice_stream_lock_release(dice);
error:
return err;
}
{
struct snd_dice *dice = substream->private_data;
- dice_unlock(dice);
+ snd_dice_stream_lock_release(dice);
return 0;
}
-static int 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;
-}
-
-static int 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, TX_ISOCHRONOUS,
- &channel, 4);
- if (err < 0)
- goto err_resources;
- }
-
- err = 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;
-}
-
-static void dice_stream_stop_packets(struct snd_dice *dice)
-{
- if (amdtp_stream_running(&dice->rx_stream)) {
- snd_dice_transaction_clear_enable(dice);
- amdtp_stream_stop(&dice->rx_stream);
- }
-}
-
-static void dice_stream_stop(struct snd_dice *dice)
-{
- __be32 channel;
-
- 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);
-}
-
static int dice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
- unsigned int rate_index, mode, rate, channels, i;
+ unsigned int mode, rate, channels, i;
int err;
mutex_lock(&dice->mutex);
- dice_stream_stop(dice);
+ snd_dice_stream_stop(dice);
mutex_unlock(&dice->mutex);
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
if (err < 0)
return err;
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+
/*
* At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
* one data block of AMDTP packet. Thus sampling transfer frequency is
* be aligned to SYT_INTERVAL.
*/
channels = params_channels(hw_params);
- rate_index = rate_to_index(rate);
- if (rate_index > 4) {
+ if (mode > 1) {
if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
err = -ENOSYS;
return err;
dice->rx_stream.double_pcm_frames = false;
}
- mode = rate_index_to_mode(rate_index);
amdtp_stream_set_parameters(&dice->rx_stream, rate, channels,
dice->rx_midi_ports[mode]);
- if (rate_index > 4) {
+ if (mode > 4) {
channels /= 2;
for (i = 0; i < channels; i++) {
struct snd_dice *dice = substream->private_data;
mutex_lock(&dice->mutex);
- dice_stream_stop(dice);
+ snd_dice_stream_stop(dice);
mutex_unlock(&dice->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
mutex_lock(&dice->mutex);
if (amdtp_streaming_error(&dice->rx_stream))
- dice_stream_stop_packets(dice);
+ snd_dice_stream_stop_packets(dice);
- err = dice_stream_start(dice);
+ err = snd_dice_stream_start(dice);
if (err < 0) {
mutex_unlock(&dice->mutex);
return err;
return err;
}
-static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode)
+static int highest_supported_mode_rate(struct snd_dice *dice,
+ unsigned int mode, unsigned int *rate)
{
- int i;
+ unsigned int i, m;
- for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i)
- if ((dice->clock_caps & (1 << i)) &&
- rate_index_to_mode(i) == mode)
- return i;
+ for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
+ *rate = snd_dice_rates[i - 1];
+ if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
+ continue;
+ if (mode == m)
+ break;
+ }
+ if (i == 0)
+ return -EINVAL;
- return -1;
+ return 0;
}
static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
{
__be32 values[2];
- int rate_index, err;
+ unsigned int rate;
+ int err;
- rate_index = highest_supported_mode_rate(dice, mode);
- if (rate_index < 0) {
+ if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
dice->rx_channels[mode] = 0;
dice->rx_midi_ports[mode] = 0;
return 0;
}
- err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]);
+ err = snd_dice_transaction_set_rate(dice, rate);
if (err < 0)
return err;
dice_card_strings(dice);
- err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1);
- if (err < 0)
- goto error;
-
err = dice_create_pcm(dice);
if (err < 0)
goto error;
dice_create_proc(dice);
- err = fw_iso_resources_init(&dice->rx_resources, unit);
+ err = snd_dice_stream_init(dice);
if (err < 0)
goto error;
- dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
-
- err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM,
- CIP_BLOCKING);
- if (err < 0) {
- fw_iso_resources_destroy(&dice->rx_resources);
- goto error;
- }
err = snd_card_register(card);
if (err < 0) {
- amdtp_stream_destroy(&dice->rx_stream);
- fw_iso_resources_destroy(&dice->rx_resources);
+ snd_dice_stream_destroy(dice);
goto error;
}
{
struct snd_dice *dice = dev_get_drvdata(&unit->device);
- amdtp_stream_pcm_abort(&dice->rx_stream);
-
snd_card_disconnect(dice->card);
mutex_lock(&dice->mutex);
- dice_stream_stop(dice);
+ snd_dice_stream_destroy(dice);
mutex_unlock(&dice->mutex);
/* The handler address register becomes initialized. */
snd_dice_transaction_reinit(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.
- */
- amdtp_stream_pcm_abort(&dice->rx_stream);
-
mutex_lock(&dice->mutex);
-
- dice->global_enabled = false;
-
- dice_stream_stop_packets(dice);
- fw_iso_resources_update(&dice->rx_resources);
-
+ snd_dice_stream_update(dice);
mutex_unlock(&dice->mutex);
}