Update Linux to v5.4.2

Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
index 1123e68..8add0cd 100644
--- a/sound/firewire/digi00x/Makefile
+++ b/sound/firewire/digi00x/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
 snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
 			     digi00x-pcm.o digi00x-hwdep.o \
 			     digi00x-transaction.o digi00x-midi.o digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index 4a884a3..d613642 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
  * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
  * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include <sound/pcm.h>
@@ -128,7 +127,7 @@
 	if (err < 0)
 		return err;
 
-	s->fdf = AMDTP_FDF_AM824 | s->sfc;
+	s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
 
 	p->pcm_channels = pcm_channels;
 
@@ -144,17 +143,23 @@
 }
 
 static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
-			  __be32 *buffer, unsigned int frames)
+			  __be32 *buffer, unsigned int frames,
+			  unsigned int pcm_frames)
 {
 	struct amdtp_dot *p = s->protocol;
+	unsigned int channels = p->pcm_channels;
 	struct snd_pcm_runtime *runtime = pcm->runtime;
-	unsigned int channels, remaining_frames, i, c;
+	unsigned int pcm_buffer_pointer;
+	int remaining_frames;
 	const u32 *src;
+	int i, c;
 
-	channels = p->pcm_channels;
+	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+	pcm_buffer_pointer %= runtime->buffer_size;
+
 	src = (void *)runtime->dma_area +
-			frames_to_bytes(runtime, s->pcm_buffer_pointer);
-	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+				frames_to_bytes(runtime, pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
 
 	buffer++;
 	for (i = 0; i < frames; ++i) {
@@ -170,17 +175,23 @@
 }
 
 static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
-			 __be32 *buffer, unsigned int frames)
+			 __be32 *buffer, unsigned int frames,
+			 unsigned int pcm_frames)
 {
 	struct amdtp_dot *p = s->protocol;
+	unsigned int channels = p->pcm_channels;
 	struct snd_pcm_runtime *runtime = pcm->runtime;
-	unsigned int channels, remaining_frames, i, c;
+	unsigned int pcm_buffer_pointer;
+	int remaining_frames;
 	u32 *dst;
+	int i, c;
 
-	channels = p->pcm_channels;
+	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+	pcm_buffer_pointer %= runtime->buffer_size;
+
 	dst  = (void *)runtime->dma_area +
-			frames_to_bytes(runtime, s->pcm_buffer_pointer);
-	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+				frames_to_bytes(runtime, pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
 
 	buffer++;
 	for (i = 0; i < frames; ++i) {
@@ -235,7 +246,7 @@
 }
 
 static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
-				unsigned int data_blocks)
+		unsigned int data_blocks, unsigned int data_block_counter)
 {
 	struct amdtp_dot *p = s->protocol;
 	unsigned int f, port;
@@ -243,7 +254,7 @@
 	u8 *b;
 
 	for (f = 0; f < data_blocks; f++) {
-		port = (s->data_block_counter + f) % 8;
+		port = (data_block_counter + f) % 8;
 		b = (u8 *)&buffer[0];
 
 		len = 0;
@@ -330,66 +341,74 @@
 		WRITE_ONCE(p->midi[port], midi);
 }
 
-static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
-					   __be32 *buffer,
-					   unsigned int data_blocks,
-					   unsigned int *syt)
+static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
+					    const struct pkt_desc *descs,
+					    unsigned int packets,
+					    struct snd_pcm_substream *pcm)
 {
-	struct snd_pcm_substream *pcm;
-	unsigned int pcm_frames;
+	unsigned int pcm_frames = 0;
+	int i;
 
-	pcm = READ_ONCE(s->pcm);
-	if (pcm) {
-		read_pcm_s32(s, pcm, buffer, data_blocks);
-		pcm_frames = data_blocks;
-	} else {
-		pcm_frames = 0;
+	for (i = 0; i < packets; ++i) {
+		const struct pkt_desc *desc = descs + i;
+		__be32 *buf = desc->ctx_payload;
+		unsigned int data_blocks = desc->data_blocks;
+
+		if (pcm) {
+			read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+			pcm_frames += data_blocks;
+		}
+
+		read_midi_messages(s, buf, data_blocks);
 	}
 
-	read_midi_messages(s, buffer, data_blocks);
-
 	return pcm_frames;
 }
 
-static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
-					   __be32 *buffer,
-					   unsigned int data_blocks,
-					   unsigned int *syt)
+static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
+					    const struct pkt_desc *descs,
+					    unsigned int packets,
+					    struct snd_pcm_substream *pcm)
 {
-	struct snd_pcm_substream *pcm;
-	unsigned int pcm_frames;
+	unsigned int pcm_frames = 0;
+	int i;
 
-	pcm = READ_ONCE(s->pcm);
-	if (pcm) {
-		write_pcm_s32(s, pcm, buffer, data_blocks);
-		pcm_frames = data_blocks;
-	} else {
-		write_pcm_silence(s, buffer, data_blocks);
-		pcm_frames = 0;
+	for (i = 0; i < packets; ++i) {
+		const struct pkt_desc *desc = descs + i;
+		__be32 *buf = desc->ctx_payload;
+		unsigned int data_blocks = desc->data_blocks;
+
+		if (pcm) {
+			write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+			pcm_frames += data_blocks;
+		} else {
+			write_pcm_silence(s, buf, data_blocks);
+		}
+
+		write_midi_messages(s, buf, data_blocks,
+				    desc->data_block_counter);
 	}
 
-	write_midi_messages(s, buffer, data_blocks);
-
 	return pcm_frames;
 }
 
 int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
 		 enum amdtp_stream_direction dir)
 {
-	amdtp_stream_process_data_blocks_t process_data_blocks;
+	amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
 	enum cip_flags flags;
 
-	/* Use different mode between incoming/outgoing. */
+	// Use different mode between incoming/outgoing.
 	if (dir == AMDTP_IN_STREAM) {
 		flags = CIP_NONBLOCKING;
-		process_data_blocks = process_tx_data_blocks;
+		process_ctx_payloads = process_ir_ctx_payloads;
 	} else {
 		flags = CIP_BLOCKING;
-		process_data_blocks = process_rx_data_blocks;
+		process_ctx_payloads = process_it_ctx_payloads;
 	}
 
 	return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
-				 process_data_blocks, sizeof(struct amdtp_dot));
+				process_ctx_payloads, sizeof(struct amdtp_dot));
 }
 
 void amdtp_dot_reset(struct amdtp_stream *s)
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
index 426cd39..41c5857 100644
--- a/sound/firewire/digi00x/digi00x-hwdep.c
+++ b/sound/firewire/digi00x/digi00x-hwdep.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 /*
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
index 7ab3d08..2b57ece 100644
--- a/sound/firewire/digi00x/digi00x-midi.c
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include "digi00x.h"
@@ -18,8 +17,13 @@
 		return err;
 
 	mutex_lock(&dg00x->mutex);
-	dg00x->substreams_counter++;
-	err = snd_dg00x_stream_start_duplex(dg00x, 0);
+	err = snd_dg00x_stream_reserve_duplex(dg00x, 0);
+	if (err >= 0) {
+		++dg00x->substreams_counter;
+		err = snd_dg00x_stream_start_duplex(dg00x);
+		if (err < 0)
+			--dg00x->substreams_counter;
+	}
 	mutex_unlock(&dg00x->mutex);
 	if (err < 0)
 		snd_dg00x_stream_lock_release(dg00x);
@@ -32,7 +36,7 @@
 	struct snd_dg00x *dg00x = substream->rmidi->private_data;
 
 	mutex_lock(&dg00x->mutex);
-	dg00x->substreams_counter--;
+	--dg00x->substreams_counter;
 	snd_dg00x_stream_stop_duplex(dg00x);
 	mutex_unlock(&dg00x->mutex);
 
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
index fdcff04..18e561b 100644
--- a/sound/firewire/digi00x/digi00x-pcm.c
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include "digi00x.h"
@@ -155,8 +154,8 @@
 	return 0;
 }
 
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_dg00x *dg00x = substream->private_data;
 	int err;
@@ -167,58 +166,26 @@
 		return err;
 
 	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+		unsigned int rate = params_rate(hw_params);
+
 		mutex_lock(&dg00x->mutex);
-		dg00x->substreams_counter++;
+		err = snd_dg00x_stream_reserve_duplex(dg00x, rate);
+		if (err >= 0)
+			++dg00x->substreams_counter;
 		mutex_unlock(&dg00x->mutex);
 	}
 
-	return 0;
+	return err;
 }
 
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_dg00x *dg00x = substream->private_data;
-	int err;
-
-	err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-					       params_buffer_bytes(hw_params));
-	if (err < 0)
-		return err;
-
-	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-		mutex_lock(&dg00x->mutex);
-		dg00x->substreams_counter++;
-		mutex_unlock(&dg00x->mutex);
-	}
-
-	return 0;
-}
-
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_dg00x *dg00x = substream->private_data;
 
 	mutex_lock(&dg00x->mutex);
 
 	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		dg00x->substreams_counter--;
-
-	snd_dg00x_stream_stop_duplex(dg00x);
-
-	mutex_unlock(&dg00x->mutex);
-
-	return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_dg00x *dg00x = substream->private_data;
-
-	mutex_lock(&dg00x->mutex);
-
-	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-		dg00x->substreams_counter--;
+		--dg00x->substreams_counter;
 
 	snd_dg00x_stream_stop_duplex(dg00x);
 
@@ -230,12 +197,11 @@
 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_dg00x *dg00x = substream->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
 	mutex_lock(&dg00x->mutex);
 
-	err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+	err = snd_dg00x_stream_start_duplex(dg00x);
 	if (err >= 0)
 		amdtp_stream_pcm_prepare(&dg00x->tx_stream);
 
@@ -247,12 +213,11 @@
 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_dg00x *dg00x = substream->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
 	int err;
 
 	mutex_lock(&dg00x->mutex);
 
-	err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+	err = snd_dg00x_stream_start_duplex(dg00x);
 	if (err >= 0) {
 		amdtp_stream_pcm_prepare(&dg00x->rx_stream);
 		amdtp_dot_reset(&dg00x->rx_stream);
@@ -333,8 +298,8 @@
 		.open		= pcm_open,
 		.close		= pcm_close,
 		.ioctl		= snd_pcm_lib_ioctl,
-		.hw_params	= pcm_capture_hw_params,
-		.hw_free	= pcm_capture_hw_free,
+		.hw_params	= pcm_hw_params,
+		.hw_free	= pcm_hw_free,
 		.prepare	= pcm_capture_prepare,
 		.trigger	= pcm_capture_trigger,
 		.pointer	= pcm_capture_pointer,
@@ -345,8 +310,8 @@
 		.open		= pcm_open,
 		.close		= pcm_close,
 		.ioctl		= snd_pcm_lib_ioctl,
-		.hw_params	= pcm_playback_hw_params,
-		.hw_free	= pcm_playback_hw_free,
+		.hw_params	= pcm_hw_params,
+		.hw_free	= pcm_hw_free,
 		.prepare	= pcm_playback_prepare,
 		.trigger	= pcm_playback_trigger,
 		.pointer	= pcm_playback_pointer,
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
index 6996d5a..00b047f 100644
--- a/sound/firewire/digi00x/digi00x-proc.c
+++ b/sound/firewire/digi00x/digi00x-proc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include "digi00x.h"
@@ -80,20 +79,8 @@
 		return;
 
 	root->mode = S_IFDIR | 0555;
-	if (snd_info_register(root) < 0) {
-		snd_info_free_entry(root);
-		return;
-	}
 
 	entry = snd_info_create_card_entry(dg00x->card, "clock", root);
-	if (entry == NULL) {
-		snd_info_free_entry(root);
-		return;
-	}
-
-	snd_info_set_text_ops(entry, dg00x, proc_read_clock);
-	if (snd_info_register(entry) < 0) {
-		snd_info_free_entry(entry);
-		snd_info_free_entry(root);
-	}
+	if (entry)
+		snd_info_set_text_ops(entry, dg00x, proc_read_clock);
 }
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
index 4d3b4eb..d6a9246 100644
--- a/sound/firewire/digi00x/digi00x-stream.c
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include "digi00x.h"
@@ -125,11 +124,22 @@
 
 static void finish_session(struct snd_dg00x *dg00x)
 {
-	__be32 data = cpu_to_be32(0x00000003);
+	__be32 data;
 
+	data = cpu_to_be32(0x00000003);
 	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
 			   DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
 			   &data, sizeof(data), 0);
+
+	// Unregister isochronous channels for both direction.
+	data = 0;
+	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+			   DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+			   &data, sizeof(data), 0);
+
+	// Just after finishing the session, the device may lost transmitting
+	// functionality for a short time.
+	msleep(50);
 }
 
 static int begin_session(struct snd_dg00x *dg00x)
@@ -138,11 +148,20 @@
 	u32 curr;
 	int err;
 
+	// Register isochronous channels for both direction.
+	data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+			   dg00x->rx_resources.channel);
+	err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+				 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+				 &data, sizeof(data), 0);
+	if (err < 0)
+		return err;
+
 	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
 				 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
 				 &data, sizeof(data), 0);
 	if (err < 0)
-		goto error;
+		return err;
 	curr = be32_to_cpu(data);
 
 	if (curr == 0)
@@ -157,39 +176,23 @@
 					 DG00X_OFFSET_STREAMING_SET,
 					 &data, sizeof(data), 0);
 		if (err < 0)
-			goto error;
+			break;
 
 		msleep(20);
 		curr--;
 	}
 
-	return 0;
-error:
-	finish_session(dg00x);
 	return err;
 }
 
-static void release_resources(struct snd_dg00x *dg00x)
+static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
+			  unsigned int rate)
 {
-	__be32 data = 0;
-
-	/* Unregister isochronous channels for both direction. */
-	snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
-			   DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
-			   &data, sizeof(data), 0);
-
-	/* Release isochronous resources. */
-	fw_iso_resources_free(&dg00x->tx_resources);
-	fw_iso_resources_free(&dg00x->rx_resources);
-}
-
-static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
-{
-	unsigned int i;
-	__be32 data;
+	struct fw_iso_resources *resources;
+	int i;
 	int err;
 
-	/* Check sampling rate. */
+	// Check sampling rate.
 	for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
 		if (snd_dg00x_stream_rates[i] == rate)
 			break;
@@ -197,66 +200,74 @@
 	if (i == SND_DG00X_RATE_COUNT)
 		return -EINVAL;
 
-	/* Keep resources for out-stream. */
-	err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+	if (stream == &dg00x->tx_stream)
+		resources = &dg00x->tx_resources;
+	else
+		resources = &dg00x->rx_resources;
+
+	err = amdtp_dot_set_parameters(stream, rate,
 				       snd_dg00x_stream_pcm_channels[i]);
 	if (err < 0)
 		return err;
-	err = fw_iso_resources_allocate(&dg00x->rx_resources,
-				amdtp_stream_get_max_payload(&dg00x->rx_stream),
+
+	return fw_iso_resources_allocate(resources,
+				amdtp_stream_get_max_payload(stream),
 				fw_parent_device(dg00x->unit)->max_speed);
+}
+
+static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
+{
+	struct fw_iso_resources *resources;
+	enum amdtp_stream_direction dir;
+	int err;
+
+	if (s == &dg00x->tx_stream) {
+		resources = &dg00x->tx_resources;
+		dir = AMDTP_IN_STREAM;
+	} else {
+		resources = &dg00x->rx_resources;
+		dir = AMDTP_OUT_STREAM;
+	}
+
+	err = fw_iso_resources_init(resources, dg00x->unit);
 	if (err < 0)
 		return err;
 
-	/* Keep resources for in-stream. */
-	err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
-				       snd_dg00x_stream_pcm_channels[i]);
+	err = amdtp_dot_init(s, dg00x->unit, dir);
 	if (err < 0)
-		return err;
-	err = fw_iso_resources_allocate(&dg00x->tx_resources,
-				amdtp_stream_get_max_payload(&dg00x->tx_stream),
-				fw_parent_device(dg00x->unit)->max_speed);
-	if (err < 0)
-		goto error;
+		fw_iso_resources_destroy(resources);
 
-	/* Register isochronous channels for both direction. */
-	data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
-			   dg00x->rx_resources.channel);
-	err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
-				 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
-				 &data, sizeof(data), 0);
-	if (err < 0)
-		goto error;
-
-	return 0;
-error:
-	release_resources(dg00x);
 	return err;
 }
 
+static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
+{
+	amdtp_stream_destroy(s);
+
+	if (s == &dg00x->tx_stream)
+		fw_iso_resources_destroy(&dg00x->tx_resources);
+	else
+		fw_iso_resources_destroy(&dg00x->rx_resources);
+}
+
 int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
 {
 	int err;
 
-	/* For out-stream. */
-	err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+	err = init_stream(dg00x, &dg00x->rx_stream);
 	if (err < 0)
-		goto error;
-	err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
-	if (err < 0)
-		goto error;
+		return err;
 
-	/* For in-stream. */
-	err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+	err = init_stream(dg00x, &dg00x->tx_stream);
 	if (err < 0)
-		goto error;
-	err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
-	if (err < 0)
-		goto error;
+		destroy_stream(dg00x, &dg00x->rx_stream);
 
-	return 0;
-error:
-	snd_dg00x_stream_destroy_duplex(dg00x);
+	err = amdtp_domain_init(&dg00x->domain);
+	if (err < 0) {
+		destroy_stream(dg00x, &dg00x->rx_stream);
+		destroy_stream(dg00x, &dg00x->tx_stream);
+	}
+
 	return err;
 }
 
@@ -266,35 +277,71 @@
  */
 void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
 {
-	amdtp_stream_destroy(&dg00x->rx_stream);
-	fw_iso_resources_destroy(&dg00x->rx_resources);
+	amdtp_domain_destroy(&dg00x->domain);
 
-	amdtp_stream_destroy(&dg00x->tx_stream);
-	fw_iso_resources_destroy(&dg00x->tx_resources);
+	destroy_stream(dg00x, &dg00x->rx_stream);
+	destroy_stream(dg00x, &dg00x->tx_stream);
 }
 
-int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
 {
 	unsigned int curr_rate;
+	int err;
+
+	err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+	if (err < 0)
+		return err;
+	if (rate == 0)
+		rate = curr_rate;
+
+	if (dg00x->substreams_counter == 0 || curr_rate != rate) {
+		amdtp_domain_stop(&dg00x->domain);
+
+		finish_session(dg00x);
+
+		fw_iso_resources_free(&dg00x->tx_resources);
+		fw_iso_resources_free(&dg00x->rx_resources);
+
+		err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+		if (err < 0)
+			return err;
+
+		err = keep_resources(dg00x, &dg00x->rx_stream, rate);
+		if (err < 0)
+			return err;
+
+		err = keep_resources(dg00x, &dg00x->tx_stream, rate);
+		if (err < 0) {
+			fw_iso_resources_free(&dg00x->rx_resources);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
+{
+	unsigned int generation = dg00x->rx_resources.generation;
 	int err = 0;
 
 	if (dg00x->substreams_counter == 0)
-		goto end;
+		return 0;
 
-	/* Check current sampling rate. */
-	err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
-	if (err < 0)
-		goto error;
-	if (rate == 0)
-		rate = curr_rate;
-	if (curr_rate != rate ||
-	    amdtp_streaming_error(&dg00x->tx_stream) ||
+	if (amdtp_streaming_error(&dg00x->tx_stream) ||
 	    amdtp_streaming_error(&dg00x->rx_stream)) {
+		amdtp_domain_stop(&dg00x->domain);
 		finish_session(dg00x);
+	}
 
-		amdtp_stream_stop(&dg00x->tx_stream);
-		amdtp_stream_stop(&dg00x->rx_stream);
-		release_resources(dg00x);
+	if (generation != fw_parent_device(dg00x->unit)->card->generation) {
+		err = fw_iso_resources_update(&dg00x->tx_resources);
+		if (err < 0)
+			goto error;
+
+		err = fw_iso_resources_update(&dg00x->rx_resources);
+		if (err < 0)
+			goto error;
 	}
 
 	/*
@@ -302,75 +349,52 @@
 	 * which source of clock is used.
 	 */
 	if (!amdtp_stream_running(&dg00x->rx_stream)) {
-		err = snd_dg00x_stream_set_local_rate(dg00x, rate);
-		if (err < 0)
-			goto error;
-
-		err = keep_resources(dg00x, rate);
-		if (err < 0)
-			goto error;
+		int spd = fw_parent_device(dg00x->unit)->max_speed;
 
 		err = begin_session(dg00x);
 		if (err < 0)
 			goto error;
 
-		err = amdtp_stream_start(&dg00x->rx_stream,
-				dg00x->rx_resources.channel,
-				fw_parent_device(dg00x->unit)->max_speed);
+		err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
+					      dg00x->rx_resources.channel, spd);
+		if (err < 0)
+			goto error;
+
+		err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
+					      dg00x->tx_resources.channel, spd);
+		if (err < 0)
+			goto error;
+
+		err = amdtp_domain_start(&dg00x->domain);
 		if (err < 0)
 			goto error;
 
 		if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
-					      CALLBACK_TIMEOUT)) {
+						CALLBACK_TIMEOUT) ||
+		    !amdtp_stream_wait_callback(&dg00x->tx_stream,
+						CALLBACK_TIMEOUT)) {
 			err = -ETIMEDOUT;
 			goto error;
 		}
 	}
 
-	/*
-	 * The value of SYT field in transmitted packets is always 0x0000. Thus,
-	 * duplex streams with timestamp synchronization cannot be built.
-	 */
-	if (!amdtp_stream_running(&dg00x->tx_stream)) {
-		err = amdtp_stream_start(&dg00x->tx_stream,
-				dg00x->tx_resources.channel,
-				fw_parent_device(dg00x->unit)->max_speed);
-		if (err < 0)
-			goto error;
-
-		if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
-					      CALLBACK_TIMEOUT)) {
-			err = -ETIMEDOUT;
-			goto error;
-		}
-	}
-end:
-	return err;
+	return 0;
 error:
+	amdtp_domain_stop(&dg00x->domain);
 	finish_session(dg00x);
 
-	amdtp_stream_stop(&dg00x->tx_stream);
-	amdtp_stream_stop(&dg00x->rx_stream);
-	release_resources(dg00x);
-
 	return err;
 }
 
 void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
 {
-	if (dg00x->substreams_counter > 0)
-		return;
+	if (dg00x->substreams_counter == 0) {
+		amdtp_domain_stop(&dg00x->domain);
+		finish_session(dg00x);
 
-	amdtp_stream_stop(&dg00x->tx_stream);
-	amdtp_stream_stop(&dg00x->rx_stream);
-	finish_session(dg00x);
-	release_resources(dg00x);
-
-	/*
-	 * Just after finishing the session, the device may lost transmitting
-	 * functionality for a short time.
-	 */
-	msleep(50);
+		fw_iso_resources_free(&dg00x->tx_resources);
+		fw_iso_resources_free(&dg00x->rx_resources);
+	}
 }
 
 void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
index af9bc85..cf0bcf1 100644
--- a/sound/firewire/digi00x/digi00x-transaction.c
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include <sound/asound.h>
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index ef68999..1f5fc0e 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * digi00x.c - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #include "digi00x.h"
@@ -41,20 +40,12 @@
 	return 0;
 }
 
-static void dg00x_free(struct snd_dg00x *dg00x)
-{
-	snd_dg00x_stream_destroy_duplex(dg00x);
-	snd_dg00x_transaction_unregister(dg00x);
-
-	fw_unit_put(dg00x->unit);
-
-	mutex_destroy(&dg00x->mutex);
-	kfree(dg00x);
-}
-
 static void dg00x_card_free(struct snd_card *card)
 {
-	dg00x_free(card->private_data);
+	struct snd_dg00x *dg00x = card->private_data;
+
+	snd_dg00x_stream_destroy_duplex(dg00x);
+	snd_dg00x_transaction_unregister(dg00x);
 }
 
 static void do_registration(struct work_struct *work)
@@ -70,6 +61,8 @@
 			   &dg00x->card);
 	if (err < 0)
 		return;
+	dg00x->card->private_free = dg00x_card_free;
+	dg00x->card->private_data = dg00x;
 
 	err = name_card(dg00x);
 	if (err < 0)
@@ -101,14 +94,10 @@
 	if (err < 0)
 		goto error;
 
-	dg00x->card->private_free = dg00x_card_free;
-	dg00x->card->private_data = dg00x;
 	dg00x->registered = true;
 
 	return;
 error:
-	snd_dg00x_transaction_unregister(dg00x);
-	snd_dg00x_stream_destroy_duplex(dg00x);
 	snd_card_free(dg00x->card);
 	dev_info(&dg00x->unit->device,
 		 "Sound card registration failed: %d\n", err);
@@ -120,8 +109,9 @@
 	struct snd_dg00x *dg00x;
 
 	/* Allocate this independent of sound card instance. */
-	dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
-	if (dg00x == NULL)
+	dg00x = devm_kzalloc(&unit->device, sizeof(struct snd_dg00x),
+			     GFP_KERNEL);
+	if (!dg00x)
 		return -ENOMEM;
 
 	dg00x->unit = fw_unit_get(unit);
@@ -173,12 +163,12 @@
 	cancel_delayed_work_sync(&dg00x->dwork);
 
 	if (dg00x->registered) {
-		/* No need to wait for releasing card object in this context. */
-		snd_card_free_when_closed(dg00x->card);
-	} else {
-		/* Don't forget this case. */
-		dg00x_free(dg00x);
+		// Block till all of ALSA character devices are released.
+		snd_card_free(dg00x->card);
 	}
+
+	mutex_destroy(&dg00x->mutex);
+	fw_unit_put(dg00x->unit);
 }
 
 static const struct ieee1394_device_id snd_dg00x_id_table[] = {
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 4dd1bbf..8041c65 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * digi00x.h - a part of driver for Digidesign Digi 002/003 family
  *
  * Copyright (c) 2014-2015 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
  */
 
 #ifndef SOUND_DIGI00X_H_INCLUDED
@@ -60,6 +59,8 @@
 
 	/* Console models have additional MIDI ports for control surface. */
 	bool is_console;
+
+	struct amdtp_domain domain;
 };
 
 #define DG00X_ADDR_BASE		0xffffe0000000ull
@@ -140,7 +141,8 @@
 int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
 					  bool *detect);
 int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
-int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x);
 void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
 void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
 void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);