diff options
Diffstat (limited to '')
-rw-r--r-- | sound/firewire/motu/motu-protocol-v2.c | 329 |
1 files changed, 174 insertions, 155 deletions
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c index 619b6ae73f62..a5f70efa2e88 100644 --- a/sound/firewire/motu/motu-protocol-v2.c +++ b/sound/firewire/motu/motu-protocol-v2.c @@ -12,6 +12,13 @@ #define V2_CLOCK_RATE_SHIFT 3 #define V2_CLOCK_SRC_MASK 0x00000007 #define V2_CLOCK_SRC_SHIFT 0 +#define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07 // In Traveler. +#define V2_CLOCK_SRC_ADAT_ON_DSUB 0x05 +#define V2_CLOCK_SRC_WORD_ON_BNC 0x04 +#define V2_CLOCK_SRC_SPH 0x03 +#define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical. AES/EBU in 896HD. +#define V2_CLOCK_SRC_ADAT_ON_OPT 0x01 +#define V2_CLOCK_SRC_INTERNAL 0x00 #define V2_CLOCK_FETCH_ENABLE 0x02000000 #define V2_CLOCK_MODEL_SPECIFIC 0x04000000 @@ -35,7 +42,8 @@ static int get_clock_rate(u32 data, unsigned int *rate) return 0; } -static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) +int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) { __be32 reg; int err; @@ -48,7 +56,8 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) return get_clock_rate(be32_to_cpu(reg), rate); } -static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) +int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, + unsigned int rate) { __be32 reg; u32 data; @@ -79,51 +88,62 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) static int get_clock_source(struct snd_motu *motu, u32 data, enum snd_motu_clock_source *src) { - unsigned int index = data & V2_CLOCK_SRC_MASK; - if (index > 5) - return -EIO; - - switch (index) { - case 0: + switch (data & V2_CLOCK_SRC_MASK) { + case V2_CLOCK_SRC_INTERNAL: *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; break; - case 1: + case V2_CLOCK_SRC_ADAT_ON_OPT: + *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; + break; + case V2_CLOCK_SRC_SPDIF: { - __be32 reg; - - // To check the configuration of optical interface. - int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, - ®, sizeof(reg)); - if (err < 0) - return err; - - if (be32_to_cpu(reg) & 0x00000200) - *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT; - else - *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; + bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 || + motu->spec == &snd_motu_spec_traveler); + + if (motu->spec == &snd_motu_spec_896hd) { + *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; + } else if (!support_iec60958_on_opt) { + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; + } else { + __be32 reg; + + // To check the configuration of optical interface. + int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, + sizeof(reg)); + if (err < 0) + return err; + + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_SPDIF) + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT; + else + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; + } break; } - case 2: - *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; - break; - case 3: + case V2_CLOCK_SRC_SPH: *src = SND_MOTU_CLOCK_SOURCE_SPH; break; - case 4: + case V2_CLOCK_SRC_WORD_ON_BNC: *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; break; - case 5: + case V2_CLOCK_SRC_ADAT_ON_DSUB: *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; break; + case V2_CLOCK_SRC_AESEBU_ON_XLR: + // For Traveler. + *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; + break; default: - return -EIO; + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; } return 0; } -static int v2_get_clock_source(struct snd_motu *motu, - enum snd_motu_clock_source *src) +int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src) { __be32 reg; int err; @@ -136,167 +156,166 @@ static int v2_get_clock_source(struct snd_motu *motu, return get_clock_source(motu, be32_to_cpu(reg), src); } -static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable) +// Expected for Traveler, which implements Altera Cyclone EP1C3. +static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data, + bool enable) { - enum snd_motu_clock_source src; - __be32 reg; - u32 data; - int err = 0; + *data |= V2_CLOCK_MODEL_SPECIFIC; - // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. - if (motu->spec == &snd_motu_spec_828mk2) - return 0; + return 0; +} - err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); +// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200. +static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data, + bool enable) +{ + unsigned int rate; + enum snd_motu_clock_source src; + int err; + + err = get_clock_source(motu, *data, &src); if (err < 0) return err; - data = be32_to_cpu(reg); - err = get_clock_source(motu, data, &src); + err = get_clock_rate(*data, &rate); if (err < 0) return err; - data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); - if (enable) - data |= V2_CLOCK_FETCH_ENABLE; - - if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) { - // Expected for Traveler and 896HD, which implements Altera - // Cyclone EP1C3. - data |= V2_CLOCK_MODEL_SPECIFIC; - } else { - // For UltraLite and 8pre, which implements Xilinx Spartan - // XC3S200. - unsigned int rate; - - err = get_clock_rate(data, &rate); - if (err < 0) - return err; + if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) + *data |= V2_CLOCK_MODEL_SPECIFIC; - if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) - data |= V2_CLOCK_MODEL_SPECIFIC; - } - - reg = cpu_to_be32(data); - return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); + return 0; } -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) +int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, + bool enable) { - unsigned char pcm_chunks[3] = {0, 0, 0}; - - formats->msg_chunks = 2; - - pcm_chunks[0] = analog_ports; - pcm_chunks[1] = analog_ports; - if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) - pcm_chunks[2] = analog_ports; - - if (dir == AMDTP_IN_STREAM) { - if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } - if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } + if (motu->spec == &snd_motu_spec_828mk2) { + // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. + return 0; + } else if (motu->spec == &snd_motu_spec_896hd) { + // 896HD implements Altera Cyclone EP1C3 but nothing to do. + return 0; } else { - if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } + __be32 reg; + u32 data; + int err; - // Packets to v2 units include 2 chunks for phone 1/2, except - // for 176.4/192.0 kHz. - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } + err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, + ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); - if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } + data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); + if (enable) + data |= V2_CLOCK_FETCH_ENABLE; - /* - * All of v2 models have a pair of coaxial interfaces for digital in/out - * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these - * ports. - */ - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - - formats->fixed_part_pcm_chunks[0] = pcm_chunks[0]; - formats->fixed_part_pcm_chunks[1] = pcm_chunks[1]; - formats->fixed_part_pcm_chunks[2] = pcm_chunks[2]; -} + if (motu->spec == &snd_motu_spec_traveler) + err = switch_fetching_mode_cyclone(motu, &data, enable); + else + err = switch_fetching_mode_spartan(motu, &data, enable); + if (err < 0) + return err; -static void calculate_differed_part(struct snd_motu_packet_format *formats, - enum snd_motu_spec_flags flags, - u32 data, u32 mask, u32 shift) -{ - unsigned char pcm_chunks[2] = {0, 0}; - - /* - * When optical interfaces are configured for S/PDIF (TOSLINK), - * the above PCM frames come from them, instead of coaxial - * interfaces. - */ - data = (data & mask) >> shift; - if (data == V2_OPT_IFACE_MODE_ADAT) { - if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) { - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; - } - // 8pre has two sets of optical interface and doesn't reduce - // chunks for ADAT signals. - if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) { - pcm_chunks[1] += 4; - } + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, + ®, sizeof(reg)); } - - /* At mode x4, no data chunks are supported in this part. */ - formats->differed_part_pcm_chunks[0] = pcm_chunks[0]; - formats->differed_part_pcm_chunks[1] = pcm_chunks[1]; } -static int v2_cache_packet_formats(struct snd_motu *motu) +int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu) { + bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre); __be32 reg; u32 data; int err; + motu->tx_packet_formats.pcm_byte_offset = 10; + motu->rx_packet_formats.pcm_byte_offset = 10; + + motu->tx_packet_formats.msg_chunks = 2; + motu->rx_packet_formats.msg_chunks = 2; + err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, sizeof(reg)); if (err < 0) return err; 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, - data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT); + 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)); - 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, - data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT); + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; - motu->tx_packet_formats.pcm_byte_offset = 10; - motu->rx_packet_formats.pcm_byte_offset = 10; + if (!has_two_opt_ifaces) + motu->tx_packet_formats.pcm_chunks[1] += 4; + else + motu->tx_packet_formats.pcm_chunks[1] += 8; + } + + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + + if (!has_two_opt_ifaces) + motu->rx_packet_formats.pcm_chunks[1] += 4; + else + motu->rx_packet_formats.pcm_chunks[1] += 8; + } return 0; } -const struct snd_motu_protocol snd_motu_protocol_v2 = { - .get_clock_rate = v2_get_clock_rate, - .set_clock_rate = v2_set_clock_rate, - .get_clock_source = v2_get_clock_source, - .switch_fetching_mode = v2_switch_fetching_mode, - .cache_packet_formats = v2_cache_packet_formats, +const struct snd_motu_spec snd_motu_spec_828mk2 = { + .name = "828mk2", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, + .tx_fixed_pcm_chunks = {14, 14, 0}, + .rx_fixed_pcm_chunks = {14, 14, 0}, +}; + +const struct snd_motu_spec snd_motu_spec_896hd = { + .name = "896HD", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_REGISTER_DSP, + .tx_fixed_pcm_chunks = {14, 14, 8}, + .rx_fixed_pcm_chunks = {14, 14, 8}, +}; + +const struct snd_motu_spec snd_motu_spec_traveler = { + .name = "Traveler", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, + .tx_fixed_pcm_chunks = {14, 14, 8}, + .rx_fixed_pcm_chunks = {14, 14, 8}, +}; + +const struct snd_motu_spec snd_motu_spec_ultralite = { + .name = "UltraLite", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, + .tx_fixed_pcm_chunks = {14, 14, 0}, + .rx_fixed_pcm_chunks = {14, 14, 0}, +}; + +const struct snd_motu_spec snd_motu_spec_8pre = { + .name = "8pre", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, + // Two dummy chunks always in the end of data block. + .tx_fixed_pcm_chunks = {10, 10, 0}, + .rx_fixed_pcm_chunks = {6, 6, 0}, }; |