diff options
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/Kconfig | 1 | ||||
-rw-r--r-- | sound/firewire/motu/Makefile | 3 | ||||
-rw-r--r-- | sound/firewire/motu/motu-protocol-v1.c | 225 | ||||
-rw-r--r-- | sound/firewire/motu/motu.c | 1 | ||||
-rw-r--r-- | sound/firewire/motu/motu.h | 23 |
5 files changed, 252 insertions, 1 deletions
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; } |