[PATCH 0/2] ALSA: firewire-motu: add support for MOTU 828 and 896
Hi,
This patchset is to add support for Mark of the unicorn (MOTU) 828 and 896, which were shipped two decades ago (2001) and already discontinued. (yes, it's 2021).
It's reasonable to count them as first generation of MOTU FireWire series since they are based on QuickLogic QuickRAM FPGA apart from Altera or Xilinx FPGA in the latter generation. Unlike the latter generation, they doesn't allow software to configure internal multiplexer.
I note that the sequence replay for media clock recovery[1] allows to support them. The device doesn't generate better sound with nominal (ideal) sequence of packets, thus the patches should be applied to development tree for v5.14 kernel.
[1] [PATCH 0/3] ALSA: firewire-motu: media clock recovery for sph-aware devices https://lore.kernel.org/alsa-devel/20210602013406.26442-1-o-takashi@sakamocc...
Takashi Sakamoto (2): ALSA: firewire-motu: add support for MOTU 828 ALSA: firewire-motu: add support for MOTU 896
sound/firewire/Kconfig | 2 + sound/firewire/motu/Makefile | 3 +- sound/firewire/motu/motu-protocol-v1.c | 440 +++++++++++++++++++++++++ sound/firewire/motu/motu.c | 2 + sound/firewire/motu/motu.h | 24 ++ 5 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/motu/motu-protocol-v1.c
MOTU 828 is a first model in MOTU FireWire series, produced in 2001. This model consists of three chips: * Texas Instruments TSB41AB1 (Physical layer for IEEE 1394 bus) * Philips Semiconductors 1394L21BE (Link layer for IEEE 1394 bus and packet processing layer) * QuickLogic QuickRAM QL4016 (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 2 data chunks for control and status messages in the end of each data block. The chunks consist of data block counter (4 byte) and message (2 byte). * 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. * The internal multiplexer is not configured by software.
I note that the device has a quirk to mute output voluntarily during receiving batch of packets in the beginning of packet streaming. The operation to unmute should be done by software enough after the device shifts the state, however it's not deterministic. Furthermore, just after switching rate of sampling clock, the device keeps the state longer. This patch manages to sleep 100 msec before unmute operation, but it may fail to release the mute in the case that the rate is changed. As a workaround, users can restart packet streaming at the same rate, or write to specific register from userspace.
$ python3 crpp < /sys/bus/firewire/devices/fw1/config_rom ROM header and bus information block ----------------------------------------------------------------- 400 04105c54 bus_info_length 4, crc_length 16, crc 23636 404 31333934 bus_name "1394" 408 20001000 irmc 0, cmc 0, isc 1, bmc 0, cyc_clk_acc 0, max_rec 1 (4) 40c 0001f200 company_id 0001f2 | 410 00005015 device_id 0000005015 | EUI-64 0001f20000005015
root directory ----------------------------------------------------------------- 414 0004c65c directory_length 4, crc 50780 418 030001f2 vendor 41c 0c0083c0 node capabilities per IEEE 1394 420 8d000006 --> eui-64 leaf at 438 424 d1000001 --> unit directory at 428
unit directory at 428 ----------------------------------------------------------------- 428 00035052 directory_length 3, crc 20562 42c 120001f2 specifier id 430 13000001 version 434 17101800 model
eui-64 leaf at 438 ----------------------------------------------------------------- 438 0002eeb6 leaf_length 2, crc 61110 43c 0001f200 company_id 0001f2 | 440 00005015 device_id 0000005015 | EUI-64 0001f20000005015
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 1 + sound/firewire/motu/Makefile | 3 +- sound/firewire/motu/motu-protocol-v1.c | 225 +++++++++++++++++++++++++ sound/firewire/motu/motu.c | 1 + sound/firewire/motu/motu.h | 23 +++ 5 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/motu/motu-protocol-v1.c
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index a08a0dbfc951..a70732bbddab 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -165,6 +165,7 @@ config SND_FIREWIRE_MOTU select SND_HWDEP help Say Y here to enable support for FireWire devices which MOTU produced: + * 828 * 828mk2 * Traveler * Ultralite diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile index 7c502d35103c..acdf66564fb0 100644 --- a/sound/firewire/motu/Makefile +++ b/sound/firewire/motu/Makefile @@ -3,5 +3,6 @@ CFLAGS_amdtp-motu.o := -I$(src)
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-protocol-v2.o motu-protocol-v3.o + motu-protocol-v2.o motu-protocol-v3.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 000000000000..fab6aef392af --- /dev/null +++ b/sound/firewire/motu/motu-protocol-v1.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-only + +// motu-protocol-v1.c - a part of driver for MOTU FireWire series +// +// Copyright (c) 2021 Takashi Sakamoto o-takashi@sakamocchi.jp +// +// Licensed under the terms of the GNU General Public License, version 2. + +#include "motu.h" + +#include <linux/delay.h> + +// Status register for MOTU 828 (0x'ffff'f000'0b00). +// +// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c. +// 0x00008000: mode of optical input interface. +// 0x00008000: for S/PDIF signal. +// 0x00000000: disabled or for ADAT signal. +// 0x00004000: mode of optical output interface. +// 0x00004000: for S/PDIF signal. +// 0x00000000: disabled or for ADAT signal. +// 0x00003f40: monitor input mode. +// 0x00000800: analog-1/2 +// 0x00001a00: analog-3/4 +// 0x00002c00: analog-5/6 +// 0x00003e00: analog-7/8 +// 0x00000000: analog-1 +// 0x00000900: analog-2 +// 0x00001200: analog-3 +// 0x00001b00: analog-4 +// 0x00002400: analog-5 +// 0x00002d00: analog-6 +// 0x00003600: analog-7 +// 0x00003f00: analog-8 +// 0x00000040: disabled +// 0x00000004: rate of sampling clock. +// 0x00000004: 48.0 kHz +// 0x00000000: 44.1 kHz +// 0x00000023: source of sampling clock. +// 0x00000002: S/PDIF on optical/coaxial interface. +// 0x00000021: ADAT on optical interface +// 0x00000001: ADAT on Dsub 9pin +// 0x00000000: internal or SMPTE + +#define CLK_828_STATUS_OFFSET 0x0b00 +#define CLK_828_STATUS_MASK 0x0000ffff +#define CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF 0x00008000 +#define CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF 0x00004000 +#define CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES 0x00000080 +#define CLK_828_STATUS_FLAG_SRC_IS_NOT_FROM_ADAT_DSUB 0x00000020 +#define CLK_828_STATUS_FLAG_OUTPUT_MUTE 0x00000008 +#define CLK_828_STATUS_FLAG_RATE_48000 0x00000004 +#define CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX 0x00000002 +#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB 0x00000001 + +static void parse_clock_rate_828(u32 data, unsigned int *rate) +{ + if (data & CLK_828_STATUS_FLAG_RATE_48000) + *rate = 48000; + else + *rate = 44100; +} + +static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate) +{ + __be32 reg; + int err; + + err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + parse_clock_rate_828(be32_to_cpu(reg), rate); + + return 0; +} + +int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate) +{ + if (motu->spec == &snd_motu_spec_828) + return get_clock_rate_828(motu, rate); + else + return -ENXIO; +} + +static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; + + data &= ~CLK_828_STATUS_FLAG_RATE_48000; + if (rate == 48000) + data |= CLK_828_STATUS_FLAG_RATE_48000; + + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); +} + +int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate) +{ + if (motu->spec == &snd_motu_spec_828) + return set_clock_rate_828(motu, rate); + else + return -ENXIO; +} + +static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; + + if (data & CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB) { + if (data & CLK_828_STATUS_FLAG_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 & CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX) { + if (data & CLK_828_STATUS_FLAG_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; +} + +int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src) +{ + if (motu->spec == &snd_motu_spec_828) + return get_clock_source_828(motu, src); + else + return -ENXIO; +} + +static int switch_fetching_mode_828(struct snd_motu *motu, bool enable) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; + + data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_OUTPUT_MUTE); + if (enable) { + // This transaction should be initiated after the device receives batch of packets + // since the device voluntarily mutes outputs. As a workaround, yield processor over + // 100 msec. + msleep(100); + data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_OUTPUT_MUTE; + } + + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); +} + +int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable) +{ + if (motu->spec == &snd_motu_spec_828) + return switch_fetching_mode_828(motu, enable); + else + return -ENXIO; +} + +static int detect_packet_formats_828(struct snd_motu *motu) +{ + __be32 reg; + u32 data; + int err; + + motu->tx_packet_formats.pcm_byte_offset = 4; + motu->tx_packet_formats.msg_chunks = 2; + + motu->rx_packet_formats.pcm_byte_offset = 4; + motu->rx_packet_formats.msg_chunks = 0; + + err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg) & CLK_828_STATUS_MASK; + + // The number of chunks is just reduced when SPDIF is activated. + if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)) + motu->tx_packet_formats.pcm_chunks[0] += 8; + + if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF)) + motu->rx_packet_formats.pcm_chunks[0] += 8; + + return 0; +} + +int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu) +{ + memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks, + sizeof(motu->tx_packet_formats.pcm_chunks)); + memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks, + sizeof(motu->rx_packet_formats.pcm_chunks)); + + if (motu->spec == &snd_motu_spec_828) + return detect_packet_formats_828(motu); + else + return 0; +} + +const struct snd_motu_spec snd_motu_spec_828 = { + .name = "828", + .protocol_version = SND_MOTU_PROTOCOL_V1, + .tx_fixed_pcm_chunks = {10, 0, 0}, + .rx_fixed_pcm_chunks = {10, 0, 0}, +}; diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index 531eeb36eb87..add5f183dc1d 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -150,6 +150,7 @@ static void motu_bus_update(struct fw_unit *unit) }
static const struct ieee1394_device_id motu_id_table[] = { + SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828), SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2), SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler), SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index c5c0e446deb2..13e2637a0330 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -107,6 +107,7 @@ enum snd_motu_clock_source { };
enum snd_motu_protocol_version { + SND_MOTU_PROTOCOL_V1, SND_MOTU_PROTOCOL_V2, SND_MOTU_PROTOCOL_V3, }; @@ -121,6 +122,8 @@ struct snd_motu_spec { unsigned char rx_fixed_pcm_chunks[3]; };
+extern const struct snd_motu_spec snd_motu_spec_828; + extern const struct snd_motu_spec snd_motu_spec_828mk2; extern const struct snd_motu_spec snd_motu_spec_traveler; extern const struct snd_motu_spec snd_motu_spec_ultralite; @@ -170,6 +173,16 @@ int snd_motu_create_midi_devices(struct snd_motu *motu);
int snd_motu_create_hwdep_device(struct snd_motu *motu);
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, + unsigned int *rate); +int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, + unsigned int rate); +int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src); +int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, + bool enable); +int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu); + int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate); int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, @@ -197,6 +210,8 @@ static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu, return snd_motu_protocol_v2_get_clock_rate(motu, rate); else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) return snd_motu_protocol_v3_get_clock_rate(motu, rate); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1) + return snd_motu_protocol_v1_get_clock_rate(motu, rate); else return -ENXIO; } @@ -208,6 +223,8 @@ static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu, return snd_motu_protocol_v2_set_clock_rate(motu, rate); else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) return snd_motu_protocol_v3_set_clock_rate(motu, rate); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1) + return snd_motu_protocol_v1_set_clock_rate(motu, rate); else return -ENXIO; } @@ -219,6 +236,8 @@ static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu, return snd_motu_protocol_v2_get_clock_source(motu, source); else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) return snd_motu_protocol_v3_get_clock_source(motu, source); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1) + return snd_motu_protocol_v1_get_clock_source(motu, source); else return -ENXIO; } @@ -230,6 +249,8 @@ static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu, return snd_motu_protocol_v2_switch_fetching_mode(motu, enable); else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) return snd_motu_protocol_v3_switch_fetching_mode(motu, enable); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1) + return snd_motu_protocol_v1_switch_fetching_mode(motu, enable); else return -ENXIO; } @@ -240,6 +261,8 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) return snd_motu_protocol_v2_cache_packet_formats(motu); else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) return snd_motu_protocol_v3_cache_packet_formats(motu); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1) + return snd_motu_protocol_v1_cache_packet_formats(motu); else return -ENXIO; }
MOTU 896 is a second model in MOTU FireWire series, produced in 2001. This model consists of three chips:
* Texas Instruments TSB41AB2 (Physical layer for IEEE 1394 bus) * Philips Semiconductors PDI 1394L21BE (Link layer for IEEE 1394 bus and packet processing layer) * QuickLogic QuickRAM QL4016 (Data block processing layer and digital signal processing)
This commit adds a support for the 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 2 bytes for control and status messages in the end of each data block. * The most of settings are represented in bit flag in one quadlet address (0x'ffff'f000'0b14). * It's selectable to use signal on optical interface, however the device has no register specific to it. The state has effect just to whether to exclude differed data chunks. * The internal multiplexer is not configured by software.
Just after powering on, the device has a quirk to fail handling transaction. I recommend users to connect the device enough after powering on.
$ python3 crpp < /sys/bus/firewire/devices/fw1/config_rom ROM header and bus information block ----------------------------------------------------------------- 400 04102814 bus_info_length 4, crc_length 16, crc 10260 404 31333934 bus_name "1394" 408 20001000 irmc 0, cmc 0, isc 1, bmc 0, cyc_clk_acc 0, max_rec 1 (4) 40c 0001f200 company_id 0001f2 | 410 0000d645 device_id 000000d645 | EUI-64 0001f2000000d645
root directory ----------------------------------------------------------------- 414 0004c65c directory_length 4, crc 50780 418 030001f2 vendor 41c 0c0083c0 node capabilities per IEEE 1394 420 8d000006 --> eui-64 leaf at 438 424 d1000001 --> unit directory at 428
unit directory at 428 ----------------------------------------------------------------- 428 0003ab34 directory_length 3, crc 43828 42c 120001f2 specifier id 430 13000002 version 434 17102801 model
eui-64 leaf at 438 ----------------------------------------------------------------- 438 0002057d leaf_length 2, crc 1405 43c 0001f200 company_id 0001f2 | 440 0000d645 device_id 000000d645 | EUI-64 0001f2000000d645
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 1 + sound/firewire/motu/motu-protocol-v1.c | 215 +++++++++++++++++++++++++ sound/firewire/motu/motu.c | 1 + sound/firewire/motu/motu.h | 1 + 4 files changed, 218 insertions(+)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index a70732bbddab..62658e2b111a 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -166,6 +166,7 @@ config SND_FIREWIRE_MOTU help Say Y here to enable support for FireWire devices which MOTU produced: * 828 + * 896 * 828mk2 * Traveler * Ultralite diff --git a/sound/firewire/motu/motu-protocol-v1.c b/sound/firewire/motu/motu-protocol-v1.c index fab6aef392af..d3eb9f377b71 100644 --- a/sound/firewire/motu/motu-protocol-v1.c +++ b/sound/firewire/motu/motu-protocol-v1.c @@ -53,6 +53,62 @@ #define CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX 0x00000002 #define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB 0x00000001
+// Status register for MOTU 896 (0x'ffff'f000'0b14). +// +// 0x20000000: fetch PCM frames from communication IC to DAC. +// 0x08000000: speed of word clock signal output on BNC interface. +// 0x00000000: follow to system clock. +// 0x08000000: half of system clock. +// 0x01000000: Route main output to headphone output. +// 0x00ffff00: input to monitor. +// 0x00000000: none +// 0x00004800: analog-1/2 +// 0x00005a00: analog-3/4 +// 0x00006c00: analog-5/6 +// 0x00007e00: analog-7/8 +// 0x00104800: AES/EBU-1/2 +// 0x00004000: analog-1 +// 0x00004900: analog-2 +// 0x00005200: analog-3 +// 0x00005b00: analog-4 +// 0x00006400: analog-5 +// 0x00006d00: analog-6 +// 0x00007600: analog-7 +// 0x00007f00: analog-8 +// 0x00104000: AES/EBU-1 +// 0x00104900: AES/EBU-2 +// 0x00000060: sample rate conversin for AES/EBU input/output. +// 0x00000000: None +// 0x00000020: input signal is converted to system rate +// 0x00000040: output is slave to input, ignoring system rate +// 0x00000060: output is double rate than system rate +// 0x00000018: nominal rate of sampling clock. +// 0x00000000: 44.1 kHz +// 0x00000008: 48.0 kHz +// 0x00000010: 88.2 kHz +// 0x00000018: 96.0 kHz +// 0x00000007: source of sampling clock. +// 0x00000000: internal +// 0x00000001: ADAT on optical interface +// 0x00000002: AES/EBU on XLR +// 0x00000004: word clock on BNC +// 0x00000005: ADAT on Dsub 9pin + +#define CLK_896_STATUS_OFFSET 0x0b14 +#define CLK_896_STATUS_FLAG_FETCH_ENABLE 0x20000000 +#define CLK_896_STATUS_FLAG_MAIN_TO_HP 0x01000000 +#define CLK_896_STATUS_MASK_SRC 0x00000007 +#define CLK_896_STATUS_FLAG_SRC_INTERNAL 0x00000000 +#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000001 +#define CLK_896_STATUS_FLAG_SRC_AESEBU 0x00000002 +#define CLK_896_STATUS_FLAG_SRC_WORD 0x00000004 +#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000005 +#define CLK_896_STATUS_MASK_RATE 0x00000018 +#define CLK_896_STATUS_FLAG_RATE_44100 0x00000000 +#define CLK_896_STATUS_FLAG_RATE_48000 0x00000008 +#define CLK_896_STATUS_FLAG_RATE_88200 0x00000010 +#define CLK_896_STATUS_FLAG_RATE_96000 0x00000018 + static void parse_clock_rate_828(u32 data, unsigned int *rate) { if (data & CLK_828_STATUS_FLAG_RATE_48000) @@ -74,10 +130,45 @@ static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate) return 0; }
+static int parse_clock_rate_896(u32 data, unsigned int *rate) +{ + switch (data & CLK_896_STATUS_MASK_RATE) { + case CLK_896_STATUS_FLAG_RATE_44100: + *rate = 44100; + break; + case CLK_896_STATUS_FLAG_RATE_48000: + *rate = 48000; + break; + case CLK_896_STATUS_FLAG_RATE_88200: + *rate = 88200; + break; + case CLK_896_STATUS_FLAG_RATE_96000: + *rate = 96000; + break; + default: + return -ENXIO; + } + + return 0; +} + +static int get_clock_rate_896(struct snd_motu *motu, unsigned int *rate) +{ + __be32 reg; + int err; + + err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + return parse_clock_rate_896(be32_to_cpu(reg), rate); +} + int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate) { if (motu->spec == &snd_motu_spec_828) return get_clock_rate_828(motu, rate); + else if (motu->spec == &snd_motu_spec_896) + return get_clock_rate_896(motu, rate); else return -ENXIO; } @@ -101,10 +192,48 @@ static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate) return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); }
+static int set_clock_rate_896(struct snd_motu *motu, unsigned int rate) +{ + unsigned int flag; + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); + + switch (rate) { + case 44100: + flag = CLK_896_STATUS_FLAG_RATE_44100; + break; + case 48000: + flag = CLK_896_STATUS_FLAG_RATE_48000; + break; + case 88200: + flag = CLK_896_STATUS_FLAG_RATE_88200; + break; + case 96000: + flag = CLK_896_STATUS_FLAG_RATE_96000; + break; + default: + return -EINVAL; + } + + data &= ~CLK_896_STATUS_MASK_RATE; + data |= flag; + + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg)); +} + int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate) { if (motu->spec == &snd_motu_spec_828) return set_clock_rate_828(motu, rate); + else if (motu->spec == &snd_motu_spec_896) + return set_clock_rate_896(motu, rate); else return -ENXIO; } @@ -137,10 +266,46 @@ static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_sourc return 0; }
+static int get_clock_source_896(struct snd_motu *motu, enum snd_motu_clock_source *src) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); + + switch (data & CLK_896_STATUS_MASK_SRC) { + case CLK_896_STATUS_FLAG_SRC_INTERNAL: + *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; + break; + case CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT: + *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; + break; + case CLK_896_STATUS_FLAG_SRC_AESEBU: + *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; + break; + case CLK_896_STATUS_FLAG_SRC_WORD: + *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; + break; + case CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB: + *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; + break; + default: + return -ENXIO; + } + + return 0; +} + int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src) { if (motu->spec == &snd_motu_spec_828) return get_clock_source_828(motu, src); + else if (motu->spec == &snd_motu_spec_896) + return get_clock_source_896(motu, src); else return -ENXIO; } @@ -169,10 +334,31 @@ static int switch_fetching_mode_828(struct snd_motu *motu, bool enable) return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg)); }
+static int switch_fetching_mode_896(struct snd_motu *motu, bool enable) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); + + data &= ~(CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_MAIN_TO_HP); + if (enable) + data |= (CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_MAIN_TO_HP); + + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg)); +} + int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable) { if (motu->spec == &snd_motu_spec_828) return switch_fetching_mode_828(motu, enable); + else if (motu->spec == &snd_motu_spec_896) + return switch_fetching_mode_896(motu, enable); else return -ENXIO; } @@ -204,6 +390,27 @@ static int detect_packet_formats_828(struct snd_motu *motu) return 0; }
+static int detect_packet_formats_896(struct snd_motu *motu) +{ + // 24bit PCM frames follow to source packet header without message chunk. + motu->tx_packet_formats.pcm_byte_offset = 4; + motu->rx_packet_formats.pcm_byte_offset = 4; + + // No message chunk in data block. + motu->tx_packet_formats.msg_chunks = 0; + motu->rx_packet_formats.msg_chunks = 0; + + // Always enable optical interface for ADAT signal since the device have no registers + // to refer to current configuration. + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 8; + + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 8; + + return 0; +} + int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu) { memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks, @@ -213,6 +420,8 @@ int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu)
if (motu->spec == &snd_motu_spec_828) return detect_packet_formats_828(motu); + else if (motu->spec == &snd_motu_spec_896) + return detect_packet_formats_896(motu); else return 0; } @@ -223,3 +432,9 @@ const struct snd_motu_spec snd_motu_spec_828 = { .tx_fixed_pcm_chunks = {10, 0, 0}, .rx_fixed_pcm_chunks = {10, 0, 0}, }; + +const struct snd_motu_spec snd_motu_spec_896 = { + .name = "896", + .tx_fixed_pcm_chunks = {10, 10, 0}, + .rx_fixed_pcm_chunks = {10, 10, 0}, +}; diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index add5f183dc1d..0cae670d711c 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -151,6 +151,7 @@ static void motu_bus_update(struct fw_unit *unit)
static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828), + SND_MOTU_DEV_ENTRY(0x000002, &snd_motu_spec_896), SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2), SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler), SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 13e2637a0330..a3deabdf9e34 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -123,6 +123,7 @@ struct snd_motu_spec { };
extern const struct snd_motu_spec snd_motu_spec_828; +extern const struct snd_motu_spec snd_motu_spec_896;
extern const struct snd_motu_spec snd_motu_spec_828mk2; extern const struct snd_motu_spec snd_motu_spec_traveler;
On Wed, 16 Jun 2021 10:28:45 +0200, Takashi Sakamoto wrote:
Hi,
This patchset is to add support for Mark of the unicorn (MOTU) 828 and 896, which were shipped two decades ago (2001) and already discontinued. (yes, it's 2021).
It's reasonable to count them as first generation of MOTU FireWire series since they are based on QuickLogic QuickRAM FPGA apart from Altera or Xilinx FPGA in the latter generation. Unlike the latter generation, they doesn't allow software to configure internal multiplexer.
I note that the sequence replay for media clock recovery[1] allows to support them. The device doesn't generate better sound with nominal (ideal) sequence of packets, thus the patches should be applied to development tree for v5.14 kernel.
[1] [PATCH 0/3] ALSA: firewire-motu: media clock recovery for sph-aware devices https://lore.kernel.org/alsa-devel/20210602013406.26442-1-o-takashi@sakamocc...
Takashi Sakamoto (2): ALSA: firewire-motu: add support for MOTU 828 ALSA: firewire-motu: add support for MOTU 896
Applied both patches now. Thanks.
Takashi
On Thu, Jun 17, 2021 at 11:37:57AM +0200, Takashi Iwai wrote:
On Wed, 16 Jun 2021 10:28:45 +0200, Takashi Sakamoto wrote:
Hi,
This patchset is to add support for Mark of the unicorn (MOTU) 828 and 896, which were shipped two decades ago (2001) and already discontinued. (yes, it's 2021).
It's reasonable to count them as first generation of MOTU FireWire series since they are based on QuickLogic QuickRAM FPGA apart from Altera or Xilinx FPGA in the latter generation. Unlike the latter generation, they doesn't allow software to configure internal multiplexer.
I note that the sequence replay for media clock recovery[1] allows to support them. The device doesn't generate better sound with nominal (ideal) sequence of packets, thus the patches should be applied to development tree for v5.14 kernel.
[1] [PATCH 0/3] ALSA: firewire-motu: media clock recovery for sph-aware devices https://lore.kernel.org/alsa-devel/20210602013406.26442-1-o-takashi@sakamocc...
Takashi Sakamoto (2): ALSA: firewire-motu: add support for MOTU 828 ALSA: firewire-motu: add support for MOTU 896
Applied both patches now. Thanks.
Thanks for applying them.
I'm glad if getting your care of below fix:
[PATCH] ALSA: firewire-motu: fix stream format for MOTU 8pre FireWire * https://lore.kernel.org/alsa-devel/20210614083133.39753-1-o-takashi@sakamocc...
Thanks
Takashi Sakamoto
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto