summaryrefslogtreecommitdiff
path: root/sound/firewire/dice
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-06-10 05:30:17 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-06-10 05:30:17 -0300
commitd635711daa98be86d4c7fd01499c34f566b54ccb (patch)
treeaa5cc3760a27c3d57146498cb82fa549547de06c /sound/firewire/dice
parentc91265cd0efb83778f015b4d4b1129bd2cfd075e (diff)
Linux-libre 4.6.2-gnu
Diffstat (limited to 'sound/firewire/dice')
-rw-r--r--sound/firewire/dice/dice-midi.c31
-rw-r--r--sound/firewire/dice/dice-pcm.c278
-rw-r--r--sound/firewire/dice/dice-stream.c468
-rw-r--r--sound/firewire/dice/dice-transaction.c56
-rw-r--r--sound/firewire/dice/dice.c108
-rw-r--r--sound/firewire/dice/dice.h41
6 files changed, 490 insertions, 492 deletions
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index 151b09f24..a04061750 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_am824_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
substrm->number, substrm);
else
- amdtp_am824_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_am824_midi_trigger(&dice->rx_stream,
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
substrm->number, substrm);
else
- amdtp_am824_midi_trigger(&dice->rx_stream,
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice,
int snd_dice_create_midi(struct snd_dice *dice)
{
+ __be32 reg;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
- unsigned int i, midi_in_ports, midi_out_ports;
+ unsigned int midi_in_ports, midi_out_ports;
int err;
- midi_in_ports = midi_out_ports = 0;
- for (i = 0; i < 3; i++) {
- midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
- midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
- }
+ /*
+ * Use the number of MIDI conformant data channel at current sampling
+ * transfer frequency.
+ */
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ midi_in_ports = be32_to_cpu(reg);
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ midi_out_ports = be32_to_cpu(reg);
if (midi_in_ports + midi_out_ports == 0)
return 0;
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 9b3431999..4aa024982 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -9,99 +9,46 @@
#include "dice.h"
-static int dice_rate_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_pcm_substream *substream = rule->private;
- struct snd_dice *dice = substream->private_data;
-
- const struct snd_interval *c =
- hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval *r =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval rates = {
- .min = UINT_MAX, .max = 0, .integer = 1
- };
- unsigned int i, rate, mode, *pcm_channels;
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- pcm_channels = dice->tx_channels;
- else
- pcm_channels = dice->rx_channels;
-
- 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;
-
- 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(r, &rates);
-}
-
-static int dice_channels_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_pcm_substream *substream = rule->private;
- struct snd_dice *dice = substream->private_data;
-
- const struct snd_interval *r =
- hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *c =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval channels = {
- .min = UINT_MAX, .max = 0, .integer = 1
- };
- unsigned int i, rate, mode, *pcm_channels;
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- pcm_channels = dice->tx_channels;
- else
- pcm_channels = dice->rx_channels;
-
- 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;
-
- 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 void limit_channels_and_rates(struct snd_dice *dice,
- struct snd_pcm_runtime *runtime,
- unsigned int *pcm_channels)
+static int limit_channels_and_rates(struct snd_dice *dice,
+ struct snd_pcm_runtime *runtime,
+ enum amdtp_stream_direction dir,
+ unsigned int index, unsigned int size)
{
struct snd_pcm_hardware *hw = &runtime->hw;
- unsigned int i, rate, mode;
+ struct amdtp_stream *stream;
+ unsigned int rate;
+ __be32 reg;
+ int err;
- hw->channels_min = UINT_MAX;
- hw->channels_max = 0;
+ /*
+ * Retrieve current Multi Bit Linear Audio data channel and limit to
+ * it.
+ */
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ err = snd_dice_transaction_read_tx(dice,
+ size * index + TX_NUMBER_AUDIO,
+ &reg, sizeof(reg));
+ } else {
+ stream = &dice->rx_stream[index];
+ err = snd_dice_transaction_read_rx(dice,
+ size * index + RX_NUMBER_AUDIO,
+ &reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
- 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;
- hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->channels_min = hw->channels_max = be32_to_cpu(reg);
- if (pcm_channels[mode] == 0)
- continue;
- hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
- hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
- }
+ /* Retrieve current sampling transfer frequency and limit to it. */
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ return err;
+ hw->rates = snd_pcm_rate_to_rate_bit(rate);
snd_pcm_limit_hw_rates(runtime);
+
+ return 0;
}
static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
@@ -121,8 +68,10 @@ static int init_hw_info(struct snd_dice *dice,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
+ enum amdtp_stream_direction dir;
struct amdtp_stream *stream;
- unsigned int *pcm_channels;
+ __be32 reg[2];
+ unsigned int count, size;
int err;
hw->info = SNDRV_PCM_INFO_MMAP |
@@ -134,38 +83,38 @@ static int init_hw_info(struct snd_dice *dice,
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw->formats = AM824_IN_PCM_FORMAT_BITS;
- stream = &dice->tx_stream;
- pcm_channels = dice->tx_channels;
+ dir = AMDTP_IN_STREAM;
+ stream = &dice->tx_stream[substream->pcm->device];
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
+ sizeof(reg));
} else {
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
- stream = &dice->rx_stream;
- pcm_channels = dice->rx_channels;
+ dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[substream->pcm->device];
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
+ sizeof(reg));
}
- limit_channels_and_rates(dice, runtime, pcm_channels);
- limit_period_and_buffer(hw);
-
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- dice_rate_constraint, substream,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
- goto end;
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- dice_channels_constraint, substream,
- SNDRV_PCM_HW_PARAM_RATE, -1);
+ return err;
+
+ count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ if (substream->pcm->device >= count)
+ return -ENXIO;
+
+ size = be32_to_cpu(reg[1]) * 4;
+ err = limit_channels_and_rates(dice, substream->runtime, dir,
+ substream->pcm->device, size);
if (err < 0)
- goto end;
+ return err;
+ limit_period_and_buffer(hw);
- err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
-end:
- return err;
+ return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
}
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
- unsigned int source, rate;
- bool internal;
int err;
err = snd_dice_stream_lock_try(dice);
@@ -176,39 +125,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- err = snd_dice_transaction_get_clock_source(dice, &source);
- if (err < 0)
- goto err_locked;
- switch (source) {
- case CLOCK_SOURCE_AES1:
- case CLOCK_SOURCE_AES2:
- case CLOCK_SOURCE_AES3:
- case CLOCK_SOURCE_AES4:
- case CLOCK_SOURCE_AES_ANY:
- case CLOCK_SOURCE_ADAT:
- case CLOCK_SOURCE_TDIF:
- case CLOCK_SOURCE_WC:
- internal = false;
- break;
- default:
- internal = true;
- break;
- }
-
- /*
- * When source of clock is not internal or any PCM streams are running,
- * available sampling rate is limited at current sampling rate.
- */
- if (!internal ||
- amdtp_stream_pcm_running(&dice->tx_stream) ||
- amdtp_stream_pcm_running(&dice->rx_stream)) {
- err = snd_dice_transaction_get_rate(dice, &rate);
- if (err < 0)
- goto err_locked;
- substream->runtime->hw.rate_min = rate;
- substream->runtime->hw.rate_max = rate;
- }
-
snd_pcm_set_sync(substream);
end:
return err;
@@ -230,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -243,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0;
}
@@ -251,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -264,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0;
}
@@ -304,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
static int capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
if (err >= 0)
- amdtp_stream_pcm_prepare(&dice->tx_stream);
+ amdtp_stream_pcm_prepare(stream);
return 0;
}
static int playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
if (err >= 0)
- amdtp_stream_pcm_prepare(&dice->rx_stream);
+ amdtp_stream_pcm_prepare(stream);
return err;
}
@@ -331,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream)
static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+ amdtp_stream_pcm_trigger(stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+ amdtp_stream_pcm_trigger(stream, NULL);
break;
default:
return -EINVAL;
@@ -348,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+ amdtp_stream_pcm_trigger(stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+ amdtp_stream_pcm_trigger(stream, NULL);
break;
default:
return -EINVAL;
@@ -366,14 +288,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
- return amdtp_stream_pcm_pointer(&dice->tx_stream);
+ return amdtp_stream_pcm_pointer(stream);
}
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
- return amdtp_stream_pcm_pointer(&dice->rx_stream);
+ return amdtp_stream_pcm_pointer(stream);
}
int snd_dice_create_pcm(struct snd_dice *dice)
@@ -402,29 +326,53 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
+ __be32 reg;
struct snd_pcm *pcm;
- unsigned int i, capture, playback;
+ unsigned int i, max_capture, max_playback, capture, playback;
int err;
- capture = playback = 0;
- for (i = 0; i < 3; i++) {
- if (dice->tx_channels[i] > 0)
+ /* Check whether PCM substreams are required. */
+ if (dice->force_two_pcms) {
+ max_capture = max_playback = 2;
+ } else {
+ max_capture = max_playback = 0;
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ capture = playback = 0;
+ if (i < max_capture)
capture = 1;
- if (dice->rx_channels[i] > 0)
+ if (i < max_playback)
playback = 1;
- }
+ if (capture == 0 && playback == 0)
+ break;
- err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = dice;
- strcpy(pcm->name, dice->card->shortname);
+ err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+ &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = dice;
+ strcpy(pcm->name, dice->card->shortname);
- if (capture > 0)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ if (capture > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &capture_ops);
- if (playback > 0)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ if (playback > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &playback_ops);
+ }
return 0;
}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index a6a39f7ef..ec4db3a51 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -10,6 +10,12 @@
#include "dice.h"
#define CALLBACK_TIMEOUT 200
+#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
+
+struct reg_params {
+ unsigned int count;
+ unsigned int size;
+};
const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
/* mode 0 */
@@ -24,96 +30,126 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
[6] = 192000,
};
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
- unsigned int *mode)
+/*
+ * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
+ * to GLOBAL_STATUS. Especially, just after powering on, these are different.
+ */
+static int ensure_phase_lock(struct snd_dice *dice)
{
- int i;
+ __be32 reg, nominal;
+ int err;
+
+ err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
- if (!(dice->clock_caps & BIT(i)))
- continue;
- if (snd_dice_rates[i] != rate)
- continue;
+ if (completion_done(&dice->clock_accepted))
+ reinit_completion(&dice->clock_accepted);
- *mode = (i - 1) / 2;
- return 0;
+ err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (wait_for_completion_timeout(&dice->clock_accepted,
+ msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+ /*
+ * Old versions of Dice firmware transfer no notification when
+ * the same clock status as current one is set. In this case,
+ * just check current clock status.
+ */
+ err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
+ &nominal, sizeof(nominal));
+ if (err < 0)
+ return err;
+ if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
+ return -ETIMEDOUT;
}
- return -EINVAL;
-}
-static void release_resources(struct snd_dice *dice,
- struct fw_iso_resources *resources)
-{
- __be32 channel;
-
- /* Reset channel number */
- channel = cpu_to_be32((u32)-1);
- if (resources == &dice->tx_resources)
- snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, sizeof(channel));
- else
- snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, sizeof(channel));
-
- fw_iso_resources_free(resources);
+ return 0;
}
-static int keep_resources(struct snd_dice *dice,
- struct fw_iso_resources *resources,
- unsigned int max_payload_bytes)
+static int get_register_params(struct snd_dice *dice,
+ struct reg_params *tx_params,
+ struct reg_params *rx_params)
{
- __be32 channel;
+ __be32 reg[2];
int err;
- err = fw_iso_resources_allocate(resources, max_payload_bytes,
- fw_parent_device(dice->unit)->max_speed);
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
if (err < 0)
- goto end;
+ return err;
+ tx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ tx_params->size = be32_to_cpu(reg[1]) * 4;
- /* Set channel number */
- channel = cpu_to_be32(resources->channel);
- if (resources == &dice->tx_resources)
- err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, sizeof(channel));
- else
- err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, sizeof(channel));
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
if (err < 0)
- release_resources(dice, resources);
-end:
- return err;
+ return err;
+ rx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ rx_params->size = be32_to_cpu(reg[1]) * 4;
+
+ return 0;
}
-static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static void release_resources(struct snd_dice *dice)
{
- amdtp_stream_pcm_abort(stream);
- amdtp_stream_stop(stream);
+ unsigned int i;
- if (stream == &dice->tx_stream)
- release_resources(dice, &dice->tx_resources);
- else
- release_resources(dice, &dice->rx_resources);
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if (amdtp_stream_running(&dice->tx_stream[i])) {
+ amdtp_stream_pcm_abort(&dice->tx_stream[i]);
+ amdtp_stream_stop(&dice->tx_stream[i]);
+ }
+ if (amdtp_stream_running(&dice->rx_stream[i])) {
+ amdtp_stream_pcm_abort(&dice->rx_stream[i]);
+ amdtp_stream_stop(&dice->rx_stream[i]);
+ }
+
+ fw_iso_resources_free(&dice->tx_resources[i]);
+ fw_iso_resources_free(&dice->rx_resources[i]);
+ }
}
-static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
- unsigned int rate)
+static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ struct reg_params *params)
{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < params->count; i++) {
+ reg = cpu_to_be32((u32)-1);
+ if (dir == AMDTP_IN_STREAM) {
+ snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ } else {
+ snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ }
+ }
+}
+
+static int keep_resources(struct snd_dice *dice,
+ enum amdtp_stream_direction dir, unsigned int index,
+ unsigned int rate, unsigned int pcm_chs,
+ unsigned int midi_ports)
+{
+ struct amdtp_stream *stream;
struct fw_iso_resources *resources;
- unsigned int i, mode, pcm_chs, midi_ports;
bool double_pcm_frames;
+ unsigned int i;
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];
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
} else {
- resources = &dice->rx_resources;
- pcm_chs = dice->rx_channels[mode];
- midi_ports = dice->rx_midi_ports[mode];
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
}
/*
@@ -126,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
* For this quirk, blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL.
*/
- double_pcm_frames = mode > 1;
+ double_pcm_frames = rate > 96000;
if (double_pcm_frames) {
rate /= 2;
pcm_chs *= 2;
@@ -135,7 +171,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
double_pcm_frames);
if (err < 0)
- goto end;
+ return err;
if (double_pcm_frames) {
pcm_chs /= 2;
@@ -147,158 +183,201 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *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(stream, resources->channel,
- fw_parent_device(dice->unit)->max_speed);
- if (err < 0)
- release_resources(dice, resources);
-end:
- return err;
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(dice->unit)->max_speed);
}
-static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
+static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int rate, struct reg_params *params)
{
- u32 source;
- int err;
+ __be32 reg[2];
+ unsigned int i, pcm_chs, midi_ports;
+ struct amdtp_stream *streams;
+ struct fw_iso_resources *resources;
+ int err = 0;
- err = snd_dice_transaction_get_clock_source(dice, &source);
- if (err < 0)
- goto end;
+ if (dir == AMDTP_IN_STREAM) {
+ streams = dice->tx_stream;
+ resources = dice->tx_resources;
+ } else {
+ streams = dice->rx_stream;
+ resources = dice->rx_resources;
+ }
+
+ for (i = 0; i < params->count; i++) {
+ if (dir == AMDTP_IN_STREAM) {
+ err = snd_dice_transaction_read_tx(dice,
+ params->size * i + TX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ } else {
+ err = snd_dice_transaction_read_rx(dice,
+ params->size * i + RX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
+ pcm_chs = be32_to_cpu(reg[0]);
+ midi_ports = be32_to_cpu(reg[1]);
+
+ err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
+ if (err < 0)
+ return err;
+
+ reg[0] = cpu_to_be32(resources[i].channel);
+ if (dir == AMDTP_IN_STREAM) {
+ err = snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ reg, sizeof(reg[0]));
+ } else {
+ err = snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ reg, sizeof(reg[0]));
+ }
+ if (err < 0)
+ return err;
- switch (source) {
- /* So-called 'SYT Match' modes, sync_to_syt value of packets received */
- case CLOCK_SOURCE_ARX4: /* in 4th stream */
- case CLOCK_SOURCE_ARX3: /* in 3rd stream */
- case CLOCK_SOURCE_ARX2: /* in 2nd stream */
- err = -ENOSYS;
- break;
- case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */
- *sync_mode = 0;
- break;
- default:
- *sync_mode = CIP_SYNC_TO_DEVICE;
- break;
+ err = amdtp_stream_start(&streams[i], resources[i].channel,
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ return err;
}
-end:
+
return err;
}
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
{
- struct amdtp_stream *master, *slave;
unsigned int curr_rate;
- enum cip_flags sync_mode;
- int err = 0;
+ unsigned int i;
+ struct reg_params tx_params, rx_params;
+ bool need_to_start;
+ int err;
if (dice->substreams_counter == 0)
- goto end;
+ return -EIO;
- err = get_sync_mode(dice, &sync_mode);
+ err = get_register_params(dice, &tx_params, &rx_params);
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(master) || amdtp_streaming_error(slave))
- stop_stream(dice, master);
+ return err;
- /* Stop stream if rate is different. */
err = snd_dice_transaction_get_rate(dice, &curr_rate);
if (err < 0) {
dev_err(&dice->unit->device,
"fail to get sampling rate\n");
- goto end;
+ return err;
}
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate)
- stop_stream(dice, master);
+ return -EINVAL;
+
+ /* Judge to need to restart streams. */
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if (i < tx_params.count) {
+ if (amdtp_streaming_error(&dice->tx_stream[i]) ||
+ !amdtp_stream_running(&dice->tx_stream[i]))
+ break;
+ }
+ if (i < rx_params.count) {
+ if (amdtp_streaming_error(&dice->rx_stream[i]) ||
+ !amdtp_stream_running(&dice->rx_stream[i]))
+ break;
+ }
+ }
+ need_to_start = (i < MAX_STREAMS);
- if (!amdtp_stream_running(master)) {
- stop_stream(dice, slave);
+ if (need_to_start) {
+ /* Stop transmission. */
snd_dice_transaction_clear_enable(dice);
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ release_resources(dice);
- amdtp_stream_set_sync(sync_mode, master, slave);
-
- err = snd_dice_transaction_set_rate(dice, rate);
+ err = ensure_phase_lock(dice);
if (err < 0) {
dev_err(&dice->unit->device,
- "fail to set sampling rate\n");
- goto end;
+ "fail to ensure phase lock\n");
+ return err;
}
/* 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 slave stream\n");
- stop_stream(dice, master);
- goto end;
- }
+ err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
+ if (err < 0)
+ goto error;
+ err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
+ if (err < 0)
+ goto error;
+
err = snd_dice_transaction_set_enable(dice);
if (err < 0) {
dev_err(&dice->unit->device,
"fail to enable interface\n");
- stop_stream(dice, master);
- stop_stream(dice, slave);
- goto end;
+ goto error;
}
- /* 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, master);
- stop_stream(dice, slave);
- err = -ETIMEDOUT;
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if ((i < tx_params.count &&
+ !amdtp_stream_wait_callback(&dice->tx_stream[i],
+ CALLBACK_TIMEOUT)) ||
+ (i < rx_params.count &&
+ !amdtp_stream_wait_callback(&dice->rx_stream[i],
+ CALLBACK_TIMEOUT))) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
}
}
-end:
+
+ return err;
+error:
+ snd_dice_transaction_clear_enable(dice);
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ release_resources(dice);
return err;
}
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
void snd_dice_stream_stop_duplex(struct snd_dice *dice)
{
+ struct reg_params tx_params, rx_params;
+
if (dice->substreams_counter > 0)
return;
snd_dice_transaction_clear_enable(dice);
- stop_stream(dice, &dice->tx_stream);
- stop_stream(dice, &dice->rx_stream);
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
+
+ release_resources(dice);
}
-static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int index)
{
- int err;
+ struct amdtp_stream *stream;
struct fw_iso_resources *resources;
- enum amdtp_stream_direction dir;
+ int err;
- if (stream == &dice->tx_stream) {
- resources = &dice->tx_resources;
- dir = AMDTP_IN_STREAM;
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
} else {
- resources = &dice->rx_resources;
- dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
}
err = fw_iso_resources_init(resources, dice->unit);
@@ -319,14 +398,20 @@ end:
* This function should be called before starting streams or after stopping
* streams.
*/
-static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static void destroy_stream(struct snd_dice *dice,
+ enum amdtp_stream_direction dir,
+ unsigned int index)
{
+ struct amdtp_stream *stream;
struct fw_iso_resources *resources;
- if (stream == &dice->tx_stream)
- resources = &dice->tx_resources;
- else
- resources = &dice->rx_resources;
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
+ } else {
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
+ }
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
@@ -334,33 +419,45 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
int snd_dice_stream_init_duplex(struct snd_dice *dice)
{
- int err;
-
- dice->substreams_counter = 0;
+ int i, err;
- err = init_stream(dice, &dice->tx_stream);
- if (err < 0)
- goto end;
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_IN_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ goto end;
+ }
+ }
- err = init_stream(dice, &dice->rx_stream);
- if (err < 0)
- destroy_stream(dice, &dice->tx_stream);
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_OUT_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ for (i = 0; i < MAX_STREAMS; i++)
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ break;
+ }
+ }
end:
return err;
}
void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
{
- snd_dice_transaction_clear_enable(dice);
-
- destroy_stream(dice, &dice->tx_stream);
- destroy_stream(dice, &dice->rx_stream);
+ unsigned int i;
- dice->substreams_counter = 0;
+ for (i = 0; i < MAX_STREAMS; i++) {
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ }
}
void snd_dice_stream_update_duplex(struct snd_dice *dice)
{
+ struct reg_params tx_params, rx_params;
+
/*
* On a bus reset, the DICE firmware disables streaming and then goes
* off contemplating its own navel for hundreds of milliseconds before
@@ -371,11 +468,10 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
*/
dice->global_enabled = false;
- stop_stream(dice, &dice->rx_stream);
- stop_stream(dice, &dice->tx_stream);
-
- fw_iso_resources_update(&dice->rx_resources);
- fw_iso_resources_update(&dice->tx_resources);
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
}
static void dice_lock_changed(struct snd_dice *dice)
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
index a4ff4e0bc..0f0350320 100644
--- a/sound/firewire/dice/dice-transaction.c
+++ b/sound/firewire/dice/dice-transaction.c
@@ -9,8 +9,6 @@
#include "dice.h"
-#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
-
static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
u64 offset)
{
@@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
info, 4);
}
-static int set_clock_info(struct snd_dice *dice,
- unsigned int rate, unsigned int source)
-{
- unsigned int i;
- __be32 info;
- u32 mask;
- u32 clock;
- int err;
-
- err = get_clock_info(dice, &info);
- if (err < 0)
- return err;
-
- clock = be32_to_cpu(info);
- if (source != UINT_MAX) {
- mask = CLOCK_SOURCE_MASK;
- clock &= ~mask;
- clock |= source;
- }
- if (rate != UINT_MAX) {
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
- if (snd_dice_rates[i] == rate)
- break;
- }
- if (i == ARRAY_SIZE(snd_dice_rates))
- return -EINVAL;
-
- mask = CLOCK_RATE_MASK;
- clock &= ~mask;
- clock |= i << CLOCK_RATE_SHIFT;
- }
- info = cpu_to_be32(clock);
-
- if (completion_done(&dice->clock_accepted))
- reinit_completion(&dice->clock_accepted);
-
- err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
- &info, 4);
- if (err < 0)
- return err;
-
- if (wait_for_completion_timeout(&dice->clock_accepted,
- msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
- return -ETIMEDOUT;
-
- return 0;
-}
-
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
unsigned int *source)
{
@@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
end:
return err;
}
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
-{
- return set_clock_info(dice, rate, UINT_MAX);
-}
int snd_dice_transaction_set_enable(struct snd_dice *dice)
{
@@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
fw_send_response(card, request, RCODE_COMPLETE);
- if (bits & NOTIFY_CLOCK_ACCEPTED)
+ if (bits & NOTIFY_LOCK_CHG)
complete(&dice->clock_accepted);
wake_up(&dice->hwdep_wait);
}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index b91b3739c..8b64aef31 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2");
#define OUI_WEISS 0x001c6a
#define OUI_LOUD 0x000ff2
+#define OUI_FOCUSRITE 0x00130e
+#define OUI_TCELECTRONIC 0x001486
#define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00
@@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2");
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
+/*
+ * Some models support several isochronous channels, while these streams are not
+ * always available. In this case, add the model name to this list.
+ */
+static bool force_two_pcm_support(struct fw_unit *unit)
+{
+ const char *const models[] = {
+ /* TC Electronic models. */
+ "StudioKonnekt48",
+ /* Focusrite models. */
+ "SAFFIRE_PRO_40",
+ "LIQUID_SAFFIRE_56",
+ "SAFFIRE_PRO_40_1",
+ };
+ char model[32];
+ unsigned int i;
+ int err;
+
+ err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model));
+ if (err < 0)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ if (strcmp(models[i], model) == 0)
+ break;
+ }
+
+ return i < ARRAY_SIZE(models);
+}
+
static int check_dice_category(struct fw_unit *unit)
{
struct fw_device *device = fw_parent_device(unit);
@@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit)
break;
}
}
+
+ if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
+ if (force_two_pcm_support(unit))
+ return 0;
+ }
+
if (vendor == OUI_WEISS)
category = WEISS_CATEGORY_ID;
else if (vendor == OUI_LOUD)
@@ -57,65 +95,10 @@ static int check_dice_category(struct fw_unit *unit)
return 0;
}
-static int highest_supported_mode_rate(struct snd_dice *dice,
- unsigned int mode, unsigned int *rate)
-{
- unsigned int i, m;
-
- 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 0;
-}
-
-static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
-{
- __be32 values[2];
- unsigned int rate;
- 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;
- }
-
- err = snd_dice_transaction_set_rate(dice, rate);
- if (err < 0)
- return err;
-
- err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
- values, sizeof(values));
- 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, sizeof(values));
- if (err < 0)
- return err;
-
- dice->rx_channels[mode] = be32_to_cpu(values[0]);
- dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
-
- return 0;
-}
-
-static int dice_read_params(struct snd_dice *dice)
+static int check_clock_caps(struct snd_dice *dice)
{
__be32 value;
- int mode, err;
+ int err;
/* some very old firmwares don't tell about their clock support */
if (dice->clock_caps > 0) {
@@ -133,12 +116,6 @@ static int dice_read_params(struct snd_dice *dice)
CLOCK_CAP_SOURCE_INTERNAL;
}
- for (mode = 2; mode >= 0; --mode) {
- err = dice_read_mode_params(dice, mode);
- if (err < 0)
- return err;
- }
-
return 0;
}
@@ -211,11 +188,14 @@ static void do_registration(struct work_struct *work)
if (err < 0)
return;
+ if (force_two_pcm_support(dice->unit))
+ dice->force_two_pcms = true;
+
err = snd_dice_transaction_init(dice);
if (err < 0)
goto error;
- err = dice_read_params(dice);
+ err = check_clock_caps(dice);
if (err < 0)
goto error;
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 3d5ebebe6..e6c07857f 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -39,6 +39,29 @@
#include "../lib.h"
#include "dice-interface.h"
+/*
+ * This module support maximum 2 pairs of tx/rx isochronous streams for
+ * our convinience.
+ *
+ * In documents for ASICs called with a name of 'DICE':
+ * - ASIC for DICE II:
+ * - Maximum 2 tx and 4 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2210/2210-E (so-called 'Dice Mini'):
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2220/2220-E (so-called 'Dice Jr.')
+ * - 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD3070-CH (so-called 'Dice III')
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 32 data channels.
+ *
+ * For the above, MIDI conformant data channel is just on the first isochronous
+ * stream.
+ */
+#define MAX_STREAMS 2
+
struct snd_dice {
struct snd_card *card;
struct fw_unit *unit;
@@ -56,10 +79,6 @@ 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;
@@ -71,13 +90,15 @@ struct snd_dice {
wait_queue_head_t hwdep_wait;
/* For streaming */
- struct fw_iso_resources tx_resources;
- struct fw_iso_resources rx_resources;
- struct amdtp_stream tx_stream;
- struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources[MAX_STREAMS];
+ struct fw_iso_resources rx_resources[MAX_STREAMS];
+ struct amdtp_stream tx_stream[MAX_STREAMS];
+ struct amdtp_stream rx_stream[MAX_STREAMS];
bool global_enabled;
struct completion clock_accepted;
unsigned int substreams_counter;
+
+ bool force_two_pcms;
};
enum snd_dice_addr_type {
@@ -158,7 +179,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
unsigned int *source);
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
int snd_dice_transaction_set_enable(struct snd_dice *dice);
void snd_dice_transaction_clear_enable(struct snd_dice *dice);
@@ -169,9 +189,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
#define SND_DICE_RATES_COUNT 7
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_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);