mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
6d5a2dda9b
Mark of the Unicorn released 896 mk3 FireWire in 2008 as part of the third generation of its FireWire series. In 2011, 896 mk3 hybrid was released to support USB protocol. It supports sampling transfer frequency up to 192.0 kHz. The packet format differs depending on both of current sampling transfer frequency and the type of signal in optical interfaces. The model supports transmission of PCM frames as well as MIDI messages. The 896 mk3 FireWire consists of below ICs: * Texas Instruments TSB41AB2 * Xilinx Spartan-3A FPGA, XC3S500E * Texas Instruments TMS320C6722 * Microchip (Atmel) AT91SAM SAM7S256 It supports sampling transfer frequency up to 192.0 kHz. The packet format differs depending on both of current sampling transfer frequency and the type of signal in two pairs of optical interfaces. The model supports transmission of PCM frames, while has no port for MIDi messages. The model supports command mechanism to configure internal DSP. Hardware meter information is available in the first 2 chunks of each data block of tx packet. This commit adds support for it. The 896 mk3 FireWire is just tested, but the 896 mk3 Hybrid is not yet. $ config-rom-pretty-printer < motu-896mk3fw.img ROM header and bus information block ----------------------------------------------------------------- 1024 04100ce1 bus_info_length 4, crc_length 16, crc 3297 1028 31333934 bus_name "1394" 1032 20ff7000 irmc 0, cmc 0, isc 1, bmc 0, cyc_clk_acc 255, max_rec 7 (256) 1036 0001f200 company_id 0001f2 | 1040 00093add device_id 0000604893 | EUI-64 0547556791237341 root directory ----------------------------------------------------------------- 1044 0004ef04 directory_length 4, crc 61188 1048 030001f2 vendor 1052 0c0083c0 node capabilities: per IEEE 1394 1056 d1000002 --> unit directory at 1064 1060 8d000005 --> eui-64 leaf at 1080 unit directory at 1064 ----------------------------------------------------------------- 1064 0003998d directory_length 3, crc 39309 1068 120001f2 specifier id 1072 13000017 version 1076 17101800 model eui-64 leaf at 1080 ----------------------------------------------------------------- 1080 0002cc82 leaf_length 2, crc 52354 1084 0001f200 company_id 0001f2 | 1088 00093add device_id 0000604893 | EUI-64 0547556791237341 $ config-rom-pretty-printer < motu-896mk3hybrid.img ROM header and bus information block ----------------------------------------------------------------- 1024 04103cbe bus_info_length 4, crc_length 16, crc 15550 1028 31333934 bus_name "1394" 1032 20ff7000 irmc 0, cmc 0, isc 1, bmc 0, cyc_clk_acc 255, max_rec 7 (256) 1036 0001f200 company_id 0001f2 | 1040 000ae601 device_id 0000714241 | EUI-64 0547556791346689 root directory ----------------------------------------------------------------- 1044 0004ef04 directory_length 4, crc 61188 1048 030001f2 vendor 1052 0c0083c0 node capabilities: per IEEE 1394 1056 d1000002 --> unit directory at 1064 1060 8d000005 --> eui-64 leaf at 1080 unit directory at 1064 ----------------------------------------------------------------- 1064 000394ac directory_length 3, crc 38060 1068 120001f2 specifier id 1072 13000037 version 1076 17102800 model eui-64 leaf at 1080 ----------------------------------------------------------------- 1080 0002cf69 leaf_length 2, crc 53097 1084 0001f200 company_id 0001f2 | 1088 000ae601 device_id 0000714241 | EUI-64 0547556791346689 Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Link: https://lore.kernel.org/r/20240129022711.254383-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai <tiwai@suse.de>
347 lines
9.0 KiB
C
347 lines
9.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* motu-protocol-v3.c - a part of driver for MOTU FireWire series
|
|
*
|
|
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include "motu.h"
|
|
|
|
#define V3_CLOCK_STATUS_OFFSET 0x0b14
|
|
#define V3_FETCH_PCM_FRAMES 0x02000000
|
|
#define V3_CLOCK_RATE_MASK 0x0000ff00
|
|
#define V3_CLOCK_RATE_SHIFT 8
|
|
#define V3_CLOCK_SOURCE_MASK 0x000000ff
|
|
#define V3_CLOCK_SRC_INTERNAL 0x00
|
|
#define V3_CLOCK_SRC_WORD_ON_BNC 0x01
|
|
#define V3_CLOCK_SRC_SPH 0x02
|
|
#define V3_CLOCK_SRC_AESEBU_ON_XLR 0x08
|
|
#define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10
|
|
#define V3_CLOCK_SRC_OPT_IFACE_A 0x18
|
|
#define V3_CLOCK_SRC_OPT_IFACE_B 0x19
|
|
|
|
#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
|
|
#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
|
|
#define V3_ENABLE_OPT_IN_IFACE_B 0x00000002
|
|
#define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100
|
|
#define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200
|
|
#define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000
|
|
#define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000
|
|
#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
|
|
#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
|
|
|
|
#define V3_MSG_FLAG_CLK_CHANGED 0x00000002
|
|
#define V3_CLK_WAIT_MSEC 4000
|
|
|
|
int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
|
|
unsigned int *rate)
|
|
{
|
|
__be32 reg;
|
|
u32 data;
|
|
int err;
|
|
|
|
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®,
|
|
sizeof(reg));
|
|
if (err < 0)
|
|
return err;
|
|
data = be32_to_cpu(reg);
|
|
|
|
data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
|
|
if (data >= ARRAY_SIZE(snd_motu_clock_rates))
|
|
return -EIO;
|
|
|
|
*rate = snd_motu_clock_rates[data];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
|
|
unsigned int rate)
|
|
{
|
|
__be32 reg;
|
|
u32 data;
|
|
bool need_to_wait;
|
|
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, V3_CLOCK_STATUS_OFFSET, ®,
|
|
sizeof(reg));
|
|
if (err < 0)
|
|
return err;
|
|
data = be32_to_cpu(reg);
|
|
|
|
data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
|
|
data |= i << V3_CLOCK_RATE_SHIFT;
|
|
|
|
need_to_wait = data != be32_to_cpu(reg);
|
|
|
|
reg = cpu_to_be32(data);
|
|
err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®,
|
|
sizeof(reg));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (need_to_wait) {
|
|
int result;
|
|
|
|
motu->msg = 0;
|
|
result = wait_event_interruptible_timeout(motu->hwdep_wait,
|
|
motu->msg & V3_MSG_FLAG_CLK_CHANGED,
|
|
msecs_to_jiffies(V3_CLK_WAIT_MSEC));
|
|
if (result < 0)
|
|
return result;
|
|
if (result == 0)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_motu_protocol_v3_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, V3_CLOCK_STATUS_OFFSET, ®,
|
|
sizeof(reg));
|
|
if (err < 0)
|
|
return err;
|
|
data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
|
|
|
|
switch (data) {
|
|
case V3_CLOCK_SRC_INTERNAL:
|
|
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
|
break;
|
|
case V3_CLOCK_SRC_WORD_ON_BNC:
|
|
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
|
break;
|
|
case V3_CLOCK_SRC_SPH:
|
|
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
|
break;
|
|
case V3_CLOCK_SRC_AESEBU_ON_XLR:
|
|
*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
|
|
break;
|
|
case V3_CLOCK_SRC_SPDIF_ON_COAX:
|
|
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
|
break;
|
|
case V3_CLOCK_SRC_OPT_IFACE_A:
|
|
case V3_CLOCK_SRC_OPT_IFACE_B:
|
|
{
|
|
__be32 reg;
|
|
u32 options;
|
|
|
|
err = snd_motu_transaction_read(motu,
|
|
V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg));
|
|
if (err < 0)
|
|
return err;
|
|
options = be32_to_cpu(reg);
|
|
|
|
if (data == V3_CLOCK_SRC_OPT_IFACE_A) {
|
|
if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
|
|
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
|
|
else
|
|
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
|
|
} else {
|
|
if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
|
|
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
|
|
else
|
|
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
|
|
bool enable)
|
|
{
|
|
__be32 reg;
|
|
u32 data;
|
|
int err;
|
|
|
|
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®,
|
|
sizeof(reg));
|
|
if (err < 0)
|
|
return 0;
|
|
data = be32_to_cpu(reg);
|
|
|
|
if (enable)
|
|
data |= V3_FETCH_PCM_FRAMES;
|
|
else
|
|
data &= ~V3_FETCH_PCM_FRAMES;
|
|
|
|
reg = cpu_to_be32(data);
|
|
return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®,
|
|
sizeof(reg));
|
|
}
|
|
|
|
static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data)
|
|
{
|
|
if (data & V3_ENABLE_OPT_IN_IFACE_A) {
|
|
if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
|
|
motu->tx_packet_formats.pcm_chunks[0] += 4;
|
|
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
|
} else {
|
|
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
|
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
|
}
|
|
}
|
|
|
|
if (data & V3_ENABLE_OPT_IN_IFACE_B) {
|
|
if (data & V3_NO_ADAT_OPT_IN_IFACE_B) {
|
|
motu->tx_packet_formats.pcm_chunks[0] += 4;
|
|
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
|
} else {
|
|
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
|
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
|
}
|
|
}
|
|
|
|
if (data & V3_ENABLE_OPT_OUT_IFACE_A) {
|
|
if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) {
|
|
motu->rx_packet_formats.pcm_chunks[0] += 4;
|
|
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
|
} else {
|
|
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
|
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
|
}
|
|
}
|
|
|
|
if (data & V3_ENABLE_OPT_OUT_IFACE_B) {
|
|
if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) {
|
|
motu->rx_packet_formats.pcm_chunks[0] += 4;
|
|
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
|
} else {
|
|
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
|
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
|
|
{
|
|
__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, V3_OPT_IFACE_MODE_OFFSET, ®,
|
|
sizeof(reg));
|
|
if (err < 0)
|
|
return err;
|
|
data = be32_to_cpu(reg);
|
|
|
|
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_828mk3_fw ||
|
|
motu->spec == &snd_motu_spec_828mk3_hybrid ||
|
|
motu->spec == &snd_motu_spec_896mk3 ||
|
|
motu->spec == &snd_motu_spec_traveler_mk3 ||
|
|
motu->spec == &snd_motu_spec_track16)
|
|
return detect_packet_formats_with_opt_ifaces(motu, data);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
|
|
.name = "828mk3",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_TX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_COMMAND_DSP,
|
|
.tx_fixed_pcm_chunks = {18, 18, 14},
|
|
.rx_fixed_pcm_chunks = {14, 14, 10},
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
|
|
.name = "828mk3",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_TX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_COMMAND_DSP,
|
|
.tx_fixed_pcm_chunks = {18, 18, 14},
|
|
.rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate.
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_896mk3 = {
|
|
.name = "896mk3",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_COMMAND_DSP,
|
|
.tx_fixed_pcm_chunks = {18, 14, 10},
|
|
.rx_fixed_pcm_chunks = {18, 14, 10},
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_traveler_mk3 = {
|
|
.name = "TravelerMk3",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_TX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_COMMAND_DSP,
|
|
.tx_fixed_pcm_chunks = {18, 14, 10},
|
|
.rx_fixed_pcm_chunks = {14, 14, 10},
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
|
|
.name = "UltraLiteMk3",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_TX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_COMMAND_DSP,
|
|
.tx_fixed_pcm_chunks = {18, 14, 10},
|
|
.rx_fixed_pcm_chunks = {14, 14, 14},
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_audio_express = {
|
|
.name = "AudioExpress",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
|
SND_MOTU_SPEC_TX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_REGISTER_DSP,
|
|
.tx_fixed_pcm_chunks = {10, 10, 0},
|
|
.rx_fixed_pcm_chunks = {10, 10, 0},
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_track16 = {
|
|
.name = "Track16",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_TX_MIDI_3RD_Q |
|
|
SND_MOTU_SPEC_COMMAND_DSP,
|
|
.tx_fixed_pcm_chunks = {14, 14, 14},
|
|
.rx_fixed_pcm_chunks = {6, 6, 6},
|
|
};
|
|
|
|
const struct snd_motu_spec snd_motu_spec_4pre = {
|
|
.name = "4pre",
|
|
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
|
.flags = SND_MOTU_SPEC_REGISTER_DSP,
|
|
.tx_fixed_pcm_chunks = {10, 10, 0},
|
|
.rx_fixed_pcm_chunks = {10, 10, 0},
|
|
};
|