[alsa-devel] [PATCH 16/19] firewire-motu: add support for MOTU 828 as a model with protocol version 1
Takashi Sakamoto
o-takashi at sakamocchi.jp
Sun Jan 29 04:54:14 CET 2017
MOTU 828 is a first model in this series, produced in 2001. This model
consists of three chips:
* TI TSB41AB1 (Physical layer for IEEE 1394 bus)
* PDI 1394L21BE (Link layer for IEEE 1394 bus and packet processing layer)
* QuickLogic DA828FW (Data block processing layer and digital signal
processing)
This commit adds a support for this model, with its unique protocol as
version 1. The features of this protocol are:
* no MIDI support.
* Rx packets have no data chunks for control and status messages.
* Tx packets have data chunks for control and status messages in the end
of each data block.
* All of settings are represented in bit flag in one quadlet address
(0x'ffff'f000'0b00).
* When optical interface is configured as S/PDIF, signals of the interface
is multiplexed for packets, instead of signals of coaxial interface.
Thus, differed part of data chunks is not used.
Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
sound/firewire/Kconfig | 1 +
sound/firewire/motu/Makefile | 3 +-
sound/firewire/motu/motu-protocol-v1.c | 204 +++++++++++++++++++++++++++++++++
sound/firewire/motu/motu.c | 9 ++
sound/firewire/motu/motu.h | 2 +
5 files changed, 218 insertions(+), 1 deletion(-)
create mode 100644 sound/firewire/motu/motu-protocol-v1.c
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 11a3285..a031b9c 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -146,6 +146,7 @@ config SND_FIREWIRE_MOTU
select SND_HWDEP
help
Say Y here to enable support for FireWire devices which MOTU produced:
+ * 828
To compile this driver as a module, choose M here: the module
will be called snd-firewire-motu.
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index cc195d5..15090be 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,4 @@
snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
- motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o
+ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
+ motu-protocol-v1.o
obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/motu-protocol-v1.c b/sound/firewire/motu/motu-protocol-v1.c
new file mode 100644
index 0000000..1087f46
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v1.c
@@ -0,0 +1,204 @@
+/*
+ * motu-protocol-v1.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi at sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "motu.h"
+
+#define V1_CLOCK_STATUS_OFFSET 0x0b00
+#define V1_OPT_IN_IFACE_IS_SPDIF 0x00008000
+#define V1_OPT_OUT_IFACE_IS_SPDIF 0x00004000
+#define V1_FETCH_PCM_FRAMES 0x00000080
+#define V1_CLOCK_SRC_IS_NOT_FROM_ADAT_DSUB 0x00000020
+#define V1_CLOCK_RATE_BASED_ON_48000 0x00000004
+#define V1_CLOCK_SRC_SPDIF_ON_OPT_OR_COAX 0x00000002
+#define V1_CLOCK_SRC_ADAT_ON_OPT_OR_DSUB 0x00000001
+
+static int v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ u32 data;
+ int index, err;
+
+ err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ if (data & V1_CLOCK_RATE_BASED_ON_48000)
+ index = 1;
+ else
+ index = 0;
+
+ *rate = snd_motu_clock_rates[index];
+
+ return 0;
+}
+
+static int v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int i, err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~V1_FETCH_PCM_FRAMES;
+ if (rate == 48000)
+ data |= V1_CLOCK_RATE_BASED_ON_48000;
+ else
+ data &= ~V1_CLOCK_RATE_BASED_ON_48000;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+}
+
+static int v1_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg);
+ if (data & V1_CLOCK_SRC_ADAT_ON_OPT_OR_DSUB) {
+ if (data & V1_CLOCK_SRC_IS_NOT_FROM_ADAT_DSUB)
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ } else if (data & V1_CLOCK_SRC_SPDIF_ON_OPT_OR_COAX) {
+ if (data & V1_OPT_IN_IFACE_IS_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ } else {
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ }
+
+ return 0;
+}
+
+static int v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ if (enable)
+ data |= V1_FETCH_PCM_FRAMES;
+ else
+ data &= ~V1_FETCH_PCM_FRAMES;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+}
+
+static void calculate_fixed_part(struct snd_motu_packet_format *formats,
+ enum amdtp_stream_direction dir,
+ enum snd_motu_spec_flags flags,
+ unsigned char analog_ports)
+{
+ unsigned char pcm_chunks[3] = {0, 0, 0};
+ int i;
+
+ if (dir == AMDTP_IN_STREAM)
+ formats->msg_chunks = 2;
+ else
+ formats->msg_chunks = 0;
+
+ pcm_chunks[0] = analog_ports;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X2)
+ pcm_chunks[1] = analog_ports;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ pcm_chunks[2] = analog_ports;
+
+ pcm_chunks[0] += 2;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X2)
+ pcm_chunks[1] += 2;
+
+ for (i = 0; i < 3; ++i)
+ formats->fixed_part_pcm_chunks[i] = pcm_chunks[i];
+}
+
+static void calculate_differed_part(struct snd_motu_packet_format *formats,
+ enum snd_motu_spec_flags flags,
+ u32 opt_iface_mode_data,
+ u32 opt_iface_mode_mask)
+{
+ unsigned char pcm_chunks[3] = {0, 0, 0};
+ int i;
+
+ /* Packet includes PCM frames from ADAT on optical interface. */
+ if (!(opt_iface_mode_data & opt_iface_mode_mask)) {
+ pcm_chunks[0] += 8;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X2)
+ pcm_chunks[1] += 4;
+ }
+
+ for (i = 0; i < 3; ++i)
+ formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
+}
+
+static int v1_cache_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 opt_iface_mode_data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V1_CLOCK_STATUS_OFFSET, ®,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ opt_iface_mode_data = be32_to_cpu(reg);
+
+ calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
+ motu->spec->flags, motu->spec->analog_in_ports);
+ calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
+ opt_iface_mode_data, V1_OPT_IN_IFACE_IS_SPDIF);
+
+ calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
+ motu->spec->flags, motu->spec->analog_out_ports);
+ calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
+ opt_iface_mode_data, V1_OPT_OUT_IFACE_IS_SPDIF);
+
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+
+ return 0;
+}
+
+const struct snd_motu_protocol snd_motu_protocol_v1 = {
+ .get_clock_rate = v1_get_clock_rate,
+ .set_clock_rate = v1_set_clock_rate,
+ .get_clock_source = v1_get_clock_source,
+ .switch_fetching_mode = v1_switch_fetching_mode,
+ .cache_packet_formats = v1_cache_packet_formats,
+};
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 619554b..e9705e3 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -190,6 +190,14 @@ static void motu_bus_update(struct fw_unit *unit)
snd_motu_transaction_reregister(motu);
}
+static struct snd_motu_spec motu_828orig = {
+ .name = "828",
+ .protocol = &snd_motu_protocol_v1,
+
+ .analog_in_ports = 8,
+ .analog_out_ports = 8,
+};
+
#define SND_MOTU_DEV_ENTRY(model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
@@ -202,6 +210,7 @@ static void motu_bus_update(struct fw_unit *unit)
}
static const struct ieee1394_device_id motu_id_table[] = {
+ SND_MOTU_DEV_ENTRY(0x102802, &motu_828orig),
{ }
};
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 407ce09..d091f68 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -121,6 +121,8 @@ struct snd_motu_spec {
const struct snd_motu_protocol *const protocol;
};
+extern const struct snd_motu_protocol snd_motu_protocol_v1;
+
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
const struct snd_motu_protocol *const protocol);
--
2.9.3
More information about the Alsa-devel
mailing list