|
@@ -41,55 +41,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,
|
|
|
+ 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)
|
|
|
{
|
|
|
- if (!amdtp_stream_running(&dice->rx_stream))
|
|
|
- return;
|
|
|
+ amdtp_stream_pcm_abort(stream);
|
|
|
+ amdtp_stream_stop(stream);
|
|
|
|
|
|
- amdtp_stream_pcm_abort(&dice->rx_stream);
|
|
|
- amdtp_stream_stop(&dice->rx_stream);
|
|
|
- release_resources(dice);
|
|
|
+ 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 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
|
|
@@ -101,51 +125,71 @@ static int start_stream(struct snd_dice *dice, unsigned int rate)
|
|
|
* For this quirk, blocking mode is required 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 > 1) {
|
|
|
rate /= 2;
|
|
|
pcm_chs *= 2;
|
|
|
- dice->rx_stream.double_pcm_frames = true;
|
|
|
+ stream->double_pcm_frames = true;
|
|
|
} else {
|
|
|
- dice->rx_stream.double_pcm_frames = false;
|
|
|
+ stream->double_pcm_frames = false;
|
|
|
}
|
|
|
|
|
|
- amdtp_stream_set_parameters(&dice->rx_stream, rate,
|
|
|
- pcm_chs, midi_ports);
|
|
|
+ amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
|
|
|
if (mode > 1) {
|
|
|
pcm_chs /= 2;
|
|
|
|
|
|
for (i = 0; i < pcm_chs; i++) {
|
|
|
- dice->rx_stream.pcm_positions[i] = i * 2;
|
|
|
- dice->rx_stream.pcm_positions[i + pcm_chs] = i * 2 + 1;
|
|
|
+ stream->pcm_positions[i] = i * 2;
|
|
|
+ stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- 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 (dice->substreams_counter == 0)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ 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, master);
|
|
|
|
|
|
/* Stop stream if rate is different. */
|
|
|
err = snd_dice_transaction_get_rate(dice, &curr_rate);
|
|
@@ -155,11 +199,14 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate)
|
|
|
goto end;
|
|
|
}
|
|
|
if (rate != curr_rate)
|
|
|
- stop_stream(dice);
|
|
|
+ stop_stream(dice, master);
|
|
|
|
|
|
- if (!amdtp_stream_running(&dice->rx_stream)) {
|
|
|
+ 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,
|
|
@@ -167,25 +214,35 @@ 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, master);
|
|
|
+ stop_stream(dice, slave);
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
@@ -193,46 +250,93 @@ end:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-void snd_dice_stream_stop(struct snd_dice *dice)
|
|
|
+void snd_dice_stream_stop_duplex(struct snd_dice *dice)
|
|
|
{
|
|
|
+ if (dice->substreams_counter > 0)
|
|
|
+ return;
|
|
|
+
|
|
|
snd_dice_transaction_clear_enable(dice);
|
|
|
- stop_stream(dice);
|
|
|
+
|
|
|
+ stop_stream(dice, &dice->tx_stream);
|
|
|
+ stop_stream(dice, &dice->rx_stream);
|
|
|
}
|
|
|
|
|
|
-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;
|
|
|
+ enum amdtp_stream_direction dir;
|
|
|
+
|
|
|
+ if (stream == &dice->tx_stream) {
|
|
|
+ resources = &dice->tx_resources;
|
|
|
+ dir = AMDTP_IN_STREAM;
|
|
|
+ } else {
|
|
|
+ resources = &dice->rx_resources;
|
|
|
+ dir = AMDTP_OUT_STREAM;
|
|
|
+ }
|
|
|
|
|
|
- err = fw_iso_resources_init(&dice->rx_resources, dice->unit);
|
|
|
+ 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,
|
|
|
- CIP_BLOCKING);
|
|
|
+ err = amdtp_stream_init(stream, dice->unit, dir, 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->tx_resources);
|
|
|
+ else
|
|
|
+ fw_iso_resources_destroy(&dice->rx_resources);
|
|
|
+}
|
|
|
+
|
|
|
+int snd_dice_stream_init_duplex(struct snd_dice *dice)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ 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)
|
|
|
{
|
|
|
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);
|
|
|
+
|
|
|
+ dice->substreams_counter = 0;
|
|
|
}
|
|
|
|
|
|
-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
|
|
@@ -244,9 +348,11 @@ void snd_dice_stream_update(struct snd_dice *dice)
|
|
|
*/
|
|
|
dice->global_enabled = false;
|
|
|
|
|
|
- stop_stream(dice);
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
static void dice_lock_changed(struct snd_dice *dice)
|