diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 594b6d1..6b9b0a1 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -120,6 +120,7 @@ config SND_BEBOB
 
 config SND_DIGI00X
 	tristate "Digidesign 002/003 family support"
+	select SND_FIREWIRE_LIB
 	help
 	 Say Y here to include support for Digidesign 002/003 family.
 
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
index e30e233..fe8fceb 100644
--- a/sound/firewire/digi00x/Makefile
+++ b/sound/firewire/digi00x/Makefile
@@ -1,2 +1,2 @@
-snd-digi00x-objs := digi00x.o
+snd-digi00x-objs := digi00x.o digi00x-stream.o
 obj-m += snd-digi00x.o
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644
index 0000000..1d5ef01
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -0,0 +1,349 @@
+/*
+ * 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"
+
+#define CALLBACK_TIMEOUT 500
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+	[0] = 44100,
+	[1] = 48000,
+	[2] = 88200,
+	[3] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_mbla_data_channels[SND_DG00X_RATE_COUNT] = {
+	/* Analog/ADAT/SPDIF */
+	[0] = (8 + 8 + 2),
+	[1] = (8 + 8 + 2),
+	/* Analog/SPDIF */
+	[2] = (8 + 2),
+	[3] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+	__be32 data;
+	int err;
+
+	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+				 DG00X_ADDR_BASE + DG00X_OFFSET_RATE_GET,
+				 &data, sizeof(data), 0);
+	if (err < 0)
+		goto end;
+
+	data = be32_to_cpu(data) & 0x0f;
+	if (data >= ARRAY_SIZE(snd_dg00x_stream_rates)) {
+		err = -EIO;
+		goto end;
+	}
+
+	*rate = snd_dg00x_stream_rates[data];
+end:
+	return err;
+}
+
+int snd_dg00x_stream_set_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+	__be32 data;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+		if (rate == snd_dg00x_stream_rates[i])
+			break;
+	}
+	if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+		return -EIO;
+
+	data = cpu_to_be32(i);
+	return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+				  DG00X_ADDR_BASE + DG00X_OFFSET_RATE_SET,
+				  &data, sizeof(data), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+			       enum snd_dg00x_clock *clock)
+{
+	__be32 data;
+	int err;
+
+	err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+				 DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+				 &data, sizeof(data), 0);
+	if (err < 0)
+		return err;
+
+	*clock = be32_to_cpu(data) & 0x0f;
+	if (*clock >= SND_DG00X_CLOCK_COUNT)
+		err = -EIO;
+
+	return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+	__be32 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);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+	__be32 data;
+	u32 curr;
+	int 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;
+	curr = be32_to_cpu(data);
+
+	if (curr == 0)
+		curr = 2;
+
+	curr--;
+	while (curr > 0) {
+		data = cpu_to_be32(curr);
+		err = snd_fw_transaction(dg00x->unit,
+					 TCODE_WRITE_QUADLET_REQUEST,
+					 DG00X_ADDR_BASE +
+					 DG00X_OFFSET_STREAMING_SET,
+					 &data, sizeof(data), 0);
+		if (err < 0)
+			goto error;
+
+		msleep(20);
+		curr--;
+	}
+
+	return 0;
+error:
+	finish_session(dg00x);
+	return err;
+}
+
+static void release_resources(struct snd_dg00x *dg00x)
+{
+	__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, c;
+	__be32 data;
+	int err;
+
+	/* Check sampling rate. */
+	for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+		if (snd_dg00x_stream_rates[i] == rate)
+			break;
+	}
+	if (i == SND_DG00X_RATE_COUNT)
+		return -EINVAL;
+
+	/* Keep resources for out-stream. */
+	amdtp_stream_set_parameters(&dg00x->rx_stream, rate,
+				    snd_dg00x_stream_mbla_data_channels[i], 2);
+	err = fw_iso_resources_allocate(&dg00x->rx_resources,
+				amdtp_stream_get_max_payload(&dg00x->rx_stream),
+				fw_parent_device(dg00x->unit)->max_speed);
+	if (err < 0)
+		return err;
+
+	/* Keep resources for in-stream. */
+	amdtp_stream_set_parameters(&dg00x->tx_stream, rate,
+				    snd_dg00x_stream_mbla_data_channels[i], 1);
+	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;
+
+	/* 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;
+
+	/* The first data channel in a packet is for MIDI conformant data. */
+	dg00x->rx_stream.midi_position = 0;
+	dg00x->tx_stream.midi_position = 0;
+	for (c = 0; c < snd_dg00x_stream_mbla_data_channels[i]; c++) {
+		dg00x->rx_stream.pcm_positions[c] = c + 1;
+		dg00x->tx_stream.pcm_positions[c] = c + 1;
+	}
+
+	return 0;
+error:
+	release_resources(dg00x);
+	return err;
+}
+
+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);
+	if (err < 0)
+		return err;
+	err = amdtp_stream_init(&dg00x->rx_stream, dg00x->unit,
+				AMDTP_OUT_STREAM, CIP_NONBLOCKING);
+
+	/* For in-stream. */
+	err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+	if (err < 0)
+		return err;
+	err = amdtp_stream_init(&dg00x->tx_stream, dg00x->unit,
+				AMDTP_IN_STREAM,
+				CIP_BLOCKING | CIP_SKIP_INIT_DBC_CHECK);
+	if (err < 0)
+		amdtp_stream_destroy(&dg00x->rx_stream);
+
+	return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+	amdtp_stream_destroy(&dg00x->rx_stream);
+	fw_iso_resources_destroy(&dg00x->rx_resources);
+
+	amdtp_stream_destroy(&dg00x->tx_stream);
+	fw_iso_resources_destroy(&dg00x->tx_resources);
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+{
+	unsigned int curr_rate;
+	int err = 0;
+
+	if (dg00x->playback_substreams == 0 &&
+	    dg00x->capture_substreams == 0)
+		goto end;
+
+	/* Check current sampling rate. */
+	err = snd_dg00x_stream_get_rate(dg00x, &curr_rate);
+	if (err < 0)
+		goto error;
+	if ((curr_rate != rate) |
+	    amdtp_streaming_error(&dg00x->tx_stream) |
+	    amdtp_streaming_error(&dg00x->rx_stream)) {
+		finish_session(dg00x);
+
+		amdtp_stream_stop(&dg00x->tx_stream);
+		amdtp_stream_stop(&dg00x->rx_stream);
+		release_resources(dg00x);
+	}
+
+	/* No streams are transmitted without receiving a stream. */
+	if (!amdtp_stream_running(&dg00x->rx_stream)) {
+		err = snd_dg00x_stream_set_rate(dg00x, rate);
+		if (err < 0)
+			goto error;
+
+		err = keep_resources(dg00x, rate);
+		if (err < 0)
+			goto error;
+
+		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);
+		if (err < 0)
+			goto error;
+
+		if (!amdtp_stream_wait_callback(&dg00x->rx_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 (dg00x->capture_substreams > 0 &&
+	    !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;
+error:
+	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->capture_substreams > 0)
+		return;
+	amdtp_stream_stop(&dg00x->tx_stream);
+
+	if (dg00x->playback_substreams > 0)
+		return;
+	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);
+}
+
+/* TODO: investigation. */
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+	fw_iso_resources_update(&dg00x->tx_resources);
+	fw_iso_resources_update(&dg00x->rx_resources);
+
+	amdtp_stream_update(&dg00x->tx_stream);
+	amdtp_stream_update(&dg00x->rx_stream);
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index 6f427a2..b82bce7 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -47,6 +47,8 @@ static void dg00x_card_free(struct snd_card *card)
 {
 	struct snd_dg00x *dg00x = card->private_data;
 
+	snd_dg00x_stream_destroy_duplex(dg00x);
+
 	fw_unit_put(dg00x->unit);
 
 	mutex_destroy(&dg00x->mutex);
@@ -78,6 +80,10 @@ static int snd_dg00x_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	err = snd_dg00x_stream_init_duplex(dg00x);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
@@ -90,6 +96,15 @@ error:
 	return err;
 }
 
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+	struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+	mutex_lock(&dg00x->mutex);
+	snd_dg00x_stream_update_duplex(dg00x);
+	mutex_unlock(&dg00x->mutex);
+}
+
 static void snd_dg00x_remove(struct fw_unit *unit)
 {
 	struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
@@ -118,6 +133,7 @@ static struct fw_driver dg00x_driver = {
 		.bus = &fw_bus_type,
 	},
 	.probe    = snd_dg00x_probe,
+	.update   = snd_dg00x_update,
 	.remove   = snd_dg00x_remove,
 	.id_table = snd_dg00x_id_table,
 };
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 59425cd..2e960c7 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -21,6 +21,11 @@
 #include <sound/core.h>
 #include <sound/initval.h>
 
+#include "../lib.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+
 struct snd_dg00x {
 	struct snd_card *card;
 	struct fw_unit *unit;
@@ -28,6 +33,76 @@ struct snd_dg00x {
 
 	struct mutex mutex;
 	spinlock_t lock;
+
+	struct amdtp_stream tx_stream;
+	struct fw_iso_resources tx_resources;
+
+	struct amdtp_stream rx_stream;
+	struct fw_iso_resources rx_resources;
+
+	unsigned int playback_substreams;
+	unsigned int capture_substreams;
 };
 
+#define DG00X_ADDR_BASE		0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE	0x0000
+#define DG00X_OFFSET_STREAMING_SET	0x0004
+#define DG00X_OFFSET_MIDI_CTL_ADDR	0x0008
+/* For LSB of the address		0x000c */
+/* unknown				0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR	0x0014
+/* For LSB of the address		0x0018 */
+/* unknown				0x001c */
+/* unknown				0x0020 */
+/* not used			0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS	0x0100
+/* unknown				0x0104 */
+/* unknown				0x0118 */
+/* unknown				0x010c */
+#define DG00X_OFFSET_RATE_SET		0x0110
+#define DG00X_OFFSET_RATE_GET		0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE	0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE	0x011c
+/* unknown				0x0120 */
+/* unknown				0x0124 */
+/* unknown				0x0128 */
+/* unknown				0x012c */
+/* unknown				0x0138 */
+
+enum snd_dg00x_rate {
+	SND_DG00X_RATE_44100 = 0,
+	SND_DG00X_RATE_48000,
+	SND_DG00X_RATE_88200,
+	SND_DG00X_RATE_96000,
+	SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+	SND_DG00X_CLOCK_INTERNAL = 0,
+	SND_DG00X_CLOCK_SPDIF,
+	SND_DG00X_CLOCK_ADAT,
+	SND_DG00X_CLOCK_WORD,
+	SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+	SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+	SND_DG00X_OPT_IFACE_MODE_SPDIF,
+	SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int
+snd_dg00x_stream_mbla_data_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_rate(struct snd_dg00x *dg00x, unsigned int *rate);
+int snd_dg00x_stream_set_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+			       enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+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);
+
 #endif
