summary refs log tree commit diff
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.c230
1 files changed, 168 insertions, 62 deletions
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index b9d7a4846459..e60b84d7a0f6 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -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)