[alsa-devel] [PATCH 0/8] ALSA: fireface: add support for Fireface 800 with MIDI functionality only
Hi,
In 2004, RME GmbH shipped Fireface 800 as a flagship model of its Fireface series. This model consists of some chips: - TI TSB81BA3D for physical layer on cable environment of EEE 1394 bus - TI TSB82AA2 for link layer for 1394 OHCI bus bridge to PCI bus - Xilinx Spartan-3 FPGA XC3S400 - Xilinx High-Performance CPLD XC9572XL
A remarkable point of this model is that its physical/link layer support S800 (800Mbps) mode of IEEE 1394b. Within devices of audio and music unit on IEEE 1394 bus, this is an unique model to support it, as long as I know.
This commit adds support for the model with its MIDI functionality only. The functionality is quite similar to the one of Fireface 400. In patch 01-06, some codes specific to Fireface 400 are arranged to be shared by both models.
On the other hand, there're some differences. The largest difference is that the model has no register to switch enabling/disabling tx asynchronous transactions for MIDI messages. Therefore the functionality work well without any assist by userspace.
For this work, I use v2.77 firmware and v3.1.24 driver shipped by RME GmbH. If finding some differences between older firmware, please report it with enough information for debug. To help users for test, I prepare a remote branch including these patches. This can be backport to Linux v4.17 or later. https://github.com/takaswie/snd-firewire-improve/tree/topic/ff800-midi
Takashi Sakamoto (8): ALSA: fireface: share some registers for status of clock synchronization ALSA: fireface: share status and configuration dump ALSA: fireface: share helper function to get current sampling rate and clock source ALSA: fireface: add support for second optical interface for ADAT stream ALSA: fireface: share register for async transaction of MIDI messages ALSA: fireface: add driver data for register for MIDI high address ALSA: fireface: localize a handler for MIDI messages on tx transaction ALSA: fireface: add support for Fireface 800 with MIDI functionality only
sound/firewire/Kconfig | 1 + sound/firewire/fireface/Makefile | 3 +- sound/firewire/fireface/ff-pcm.c | 2 +- sound/firewire/fireface/ff-proc.c | 193 +++++++++++++- sound/firewire/fireface/ff-protocol-ff400.c | 282 ++------------------ sound/firewire/fireface/ff-protocol-ff800.c | 27 ++ sound/firewire/fireface/ff-stream.c | 2 +- sound/firewire/fireface/ff-transaction.c | 157 ++++++----- sound/firewire/fireface/ff.c | 54 +++- sound/firewire/fireface/ff.h | 31 ++- 10 files changed, 406 insertions(+), 346 deletions(-) create mode 100644 sound/firewire/fireface/ff-protocol-ff800.c
As long as investigating packet dumps from Fireface 400/800, status registers for clock synchronization is common.
This commit moves some macros for them to header file.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff400.c | 11 ++++------- sound/firewire/fireface/ff.h | 5 +++++ 2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 654a50319198..20e0c0ec3a58 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -14,9 +14,6 @@ #define FF400_ISOC_COMM_START 0x000080100508ull #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull -#define FF400_SYNC_STATUS 0x0000801c0000ull -#define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */ -#define FF400_CLOCK_CONFIG 0x0000801c0004ull
#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull #define FF400_MIDI_RX_PORT_0 0x000080180000ull @@ -30,7 +27,7 @@ static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate, int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF400_CLOCK_CONFIG, ®, sizeof(reg), 0); + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); if (err < 0) return err; data = le32_to_cpu(reg); @@ -165,7 +162,7 @@ static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) }
err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, - FF400_FETCH_PCM_FRAMES, reg, + SND_FF_REG_FETCH_PCM_FRAMES, reg, sizeof(__le32) * 18, 0); kfree(reg); return err; @@ -179,7 +176,7 @@ static void ff400_dump_sync_status(struct snd_ff *ff, int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FF400_SYNC_STATUS, ®, sizeof(reg), 0); + SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); if (err < 0) return;
@@ -294,7 +291,7 @@ static void ff400_dump_clock_config(struct snd_ff *ff, int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, - FF400_CLOCK_CONFIG, ®, sizeof(reg), 0); + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); if (err < 0) return;
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 64df44beb950..9122fb6ed8fd 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -37,6 +37,11 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2
+#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull +/* For block wriet request. */ +#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull +#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull + struct snd_ff_protocol; struct snd_ff_spec { const char *const name;
As long as investigating packet dumps from Fireface 400/800, bits on status registers for clock synchronization are the same.
This commit moves a parser for the registers to obsolete model-specific operations.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-proc.c | 178 +++++++++++++++++- sound/firewire/fireface/ff-protocol-ff400.c | 191 -------------------- sound/firewire/fireface/ff.h | 5 - 3 files changed, 176 insertions(+), 198 deletions(-)
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index 40ccbfd8ef89..f5f3a1997a9e 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -12,16 +12,190 @@ static void proc_dump_clock_config(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; + __le32 reg; + u32 data; + unsigned int rate; + const char *src; + int err;
- ff->spec->protocol->dump_clock_config(ff, buffer); + err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return; + + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", + (data & 0x20) ? "Professional" : "Consumer", + (data & 0x40) ? "on" : "off"); + + snd_iprintf(buffer, "Optical output interface format: %s\n", + ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); + + snd_iprintf(buffer, "Word output single speed: %s\n", + ((data >> 8) & 0x20) ? "on" : "off"); + + snd_iprintf(buffer, "S/PDIF input interface: %s\n", + ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); + + switch ((data >> 1) & 0x03) { + case 0x01: + rate = 32000; + break; + case 0x00: + rate = 44100; + break; + case 0x03: + rate = 48000; + break; + case 0x02: + default: + return; + } + + if (data & 0x08) + rate *= 2; + else if (data & 0x10) + rate *= 4; + + snd_iprintf(buffer, "Sampling rate: %d\n", rate); + + if (data & 0x01) { + src = "Internal"; + } else { + switch ((data >> 10) & 0x07) { + case 0x00: + src = "ADAT"; + break; + case 0x03: + src = "S/PDIF"; + break; + case 0x04: + src = "Word"; + break; + case 0x05: + src = "LTC"; + break; + default: + return; + } + } + + snd_iprintf(buffer, "Sync to clock source: %s\n", src); }
static void proc_dump_sync_status(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data; + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return; + + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "External source detection:\n"); + + snd_iprintf(buffer, "Word Clock:"); + if ((data >> 24) & 0x20) { + if ((data >> 24) & 0x40) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "S/PDIF:"); + if ((data >> 16) & 0x10) { + if ((data >> 16) & 0x04) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "ADAT:"); + if ((data >> 8) & 0x04) { + if ((data >> 8) & 0x10) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + + snd_iprintf(buffer, "\nUsed external source:\n"); + + if (((data >> 22) & 0x07) == 0x07) { + snd_iprintf(buffer, "None\n"); + } else { + switch ((data >> 22) & 0x07) { + case 0x00: + snd_iprintf(buffer, "ADAT:"); + break; + case 0x03: + snd_iprintf(buffer, "S/PDIF:"); + break; + case 0x04: + snd_iprintf(buffer, "Word:"); + break; + case 0x07: + snd_iprintf(buffer, "Nothing:"); + break; + case 0x01: + case 0x02: + case 0x05: + case 0x06: + default: + snd_iprintf(buffer, "unknown:"); + break; + } + + if ((data >> 25) & 0x07) { + switch ((data >> 25) & 0x07) { + case 0x01: + snd_iprintf(buffer, "32000\n"); + break; + case 0x02: + snd_iprintf(buffer, "44100\n"); + break; + case 0x03: + snd_iprintf(buffer, "48000\n"); + break; + case 0x04: + snd_iprintf(buffer, "64000\n"); + break; + case 0x05: + snd_iprintf(buffer, "88200\n"); + break; + case 0x06: + snd_iprintf(buffer, "96000\n"); + break; + case 0x07: + snd_iprintf(buffer, "128000\n"); + break; + case 0x08: + snd_iprintf(buffer, "176400\n"); + break; + case 0x09: + snd_iprintf(buffer, "192000\n"); + break; + case 0x00: + snd_iprintf(buffer, "unknown\n"); + break; + } + } + }
- ff->spec->protocol->dump_sync_status(ff, buffer); + snd_iprintf(buffer, "Multiplied:"); + snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); }
static void add_node(struct snd_ff *ff, struct snd_info_entry *root, diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 20e0c0ec3a58..31c381dcb452 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -168,203 +168,12 @@ static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) return err; }
-static void ff400_dump_sync_status(struct snd_ff *ff, - struct snd_info_buffer *buffer) -{ - __le32 reg; - u32 data; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - SND_FF_REG_SYNC_STATUS, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "External source detection:\n"); - - snd_iprintf(buffer, "Word Clock:"); - if ((data >> 24) & 0x20) { - if ((data >> 24) & 0x40) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "S/PDIF:"); - if ((data >> 16) & 0x10) { - if ((data >> 16) & 0x04) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "ADAT:"); - if ((data >> 8) & 0x04) { - if ((data >> 8) & 0x10) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); - } else { - snd_iprintf(buffer, "none\n"); - } - - snd_iprintf(buffer, "\nUsed external source:\n"); - - if (((data >> 22) & 0x07) == 0x07) { - snd_iprintf(buffer, "None\n"); - } else { - switch ((data >> 22) & 0x07) { - case 0x00: - snd_iprintf(buffer, "ADAT:"); - break; - case 0x03: - snd_iprintf(buffer, "S/PDIF:"); - break; - case 0x04: - snd_iprintf(buffer, "Word:"); - break; - case 0x07: - snd_iprintf(buffer, "Nothing:"); - break; - case 0x01: - case 0x02: - case 0x05: - case 0x06: - default: - snd_iprintf(buffer, "unknown:"); - break; - } - - if ((data >> 25) & 0x07) { - switch ((data >> 25) & 0x07) { - case 0x01: - snd_iprintf(buffer, "32000\n"); - break; - case 0x02: - snd_iprintf(buffer, "44100\n"); - break; - case 0x03: - snd_iprintf(buffer, "48000\n"); - break; - case 0x04: - snd_iprintf(buffer, "64000\n"); - break; - case 0x05: - snd_iprintf(buffer, "88200\n"); - break; - case 0x06: - snd_iprintf(buffer, "96000\n"); - break; - case 0x07: - snd_iprintf(buffer, "128000\n"); - break; - case 0x08: - snd_iprintf(buffer, "176400\n"); - break; - case 0x09: - snd_iprintf(buffer, "192000\n"); - break; - case 0x00: - snd_iprintf(buffer, "unknown\n"); - break; - } - } - } - - snd_iprintf(buffer, "Multiplied:"); - snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); -} - -static void ff400_dump_clock_config(struct snd_ff *ff, - struct snd_info_buffer *buffer) -{ - __le32 reg; - u32 data; - unsigned int rate; - const char *src; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, - SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); - if (err < 0) - return; - - data = le32_to_cpu(reg); - - snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", - (data & 0x20) ? "Professional" : "Consumer", - (data & 0x40) ? "on" : "off"); - - snd_iprintf(buffer, "Optical output interface format: %s\n", - ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); - - snd_iprintf(buffer, "Word output single speed: %s\n", - ((data >> 8) & 0x20) ? "on" : "off"); - - snd_iprintf(buffer, "S/PDIF input interface: %s\n", - ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); - - switch ((data >> 1) & 0x03) { - case 0x01: - rate = 32000; - break; - case 0x00: - rate = 44100; - break; - case 0x03: - rate = 48000; - break; - case 0x02: - default: - return; - } - - if (data & 0x08) - rate *= 2; - else if (data & 0x10) - rate *= 4; - - snd_iprintf(buffer, "Sampling rate: %d\n", rate); - - if (data & 0x01) { - src = "Internal"; - } else { - switch ((data >> 10) & 0x07) { - case 0x00: - src = "ADAT"; - break; - case 0x03: - src = "S/PDIF"; - break; - case 0x04: - src = "Word"; - break; - case 0x05: - src = "LTC"; - break; - default: - return; - } - } - - snd_iprintf(buffer, "Sync to clock source: %s\n", src); -} - const struct snd_ff_protocol snd_ff_protocol_ff400 = { .get_clock = ff400_get_clock, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .switch_fetching_mode = ff400_switch_fetching_mode,
- .dump_sync_status = ff400_dump_sync_status, - .dump_clock_config = ff400_dump_clock_config, - .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR, .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0, .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1, diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 9122fb6ed8fd..6dc36a2623b3 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -107,11 +107,6 @@ struct snd_ff_protocol { void (*finish_session)(struct snd_ff *ff); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
- void (*dump_sync_status)(struct snd_ff *ff, - struct snd_info_buffer *buffer); - void (*dump_clock_config)(struct snd_ff *ff, - struct snd_info_buffer *buffer); - u64 midi_high_addr_reg; u64 midi_rx_port_0_reg; u64 midi_rx_port_1_reg;
As long as investigating packet dumps from Fireface 400/800, bits on status registers for clock synchronization are the same.
This commit moves a parser for a register of clock configuration to obsolete model-specific operations.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-pcm.c | 2 +- sound/firewire/fireface/ff-protocol-ff400.c | 60 --------------------- sound/firewire/fireface/ff-stream.c | 2 +- sound/firewire/fireface/ff-transaction.c | 59 ++++++++++++++++++++ sound/firewire/fireface/ff.h | 4 +- 5 files changed, 63 insertions(+), 64 deletions(-)
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index bf47f9ec8703..63b0be6f05e8 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -141,7 +141,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock;
- err = ff->spec->protocol->get_clock(ff, &rate, &src); + err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock;
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 31c381dcb452..d2fbb0382223 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -19,65 +19,6 @@ #define FF400_MIDI_RX_PORT_0 0x000080180000ull #define FF400_MIDI_RX_PORT_1 0x000080190000ull
-static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src) -{ - __le32 reg; - u32 data; - int err; - - err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); - if (err < 0) - return err; - data = le32_to_cpu(reg); - - /* Calculate sampling rate. */ - switch ((data >> 1) & 0x03) { - case 0x01: - *rate = 32000; - break; - case 0x00: - *rate = 44100; - break; - case 0x03: - *rate = 48000; - break; - case 0x02: - default: - return -EIO; - } - - if (data & 0x08) - *rate *= 2; - else if (data & 0x10) - *rate *= 4; - - /* Calculate source of clock. */ - if (data & 0x01) { - *src = SND_FF_CLOCK_SRC_INTERNAL; - } else { - /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */ - switch ((data >> 10) & 0x07) { - case 0x03: - *src = SND_FF_CLOCK_SRC_SPDIF; - break; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - case 0x00: - default: - *src = SND_FF_CLOCK_SRC_ADAT; - break; - } - } - - return 0; -} - static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { __le32 reg; @@ -169,7 +110,6 @@ static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) }
const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .get_clock = ff400_get_clock, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .switch_fetching_mode = ff400_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 78880922120e..59ca2e84d41c 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -149,7 +149,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (ff->substreams_counter == 0) return 0;
- err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); + err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); if (err < 0) return err; if (curr_rate != rate || diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 332b29f8ed75..1dad51da13e0 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -8,6 +8,65 @@
#include "ff.h"
+int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + __le32 reg; + u32 data; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + if (err < 0) + return err; + data = le32_to_cpu(reg); + + /* Calculate sampling rate. */ + switch ((data >> 1) & 0x03) { + case 0x01: + *rate = 32000; + break; + case 0x00: + *rate = 44100; + break; + case 0x03: + *rate = 48000; + break; + case 0x02: + default: + return -EIO; + } + + if (data & 0x08) + *rate *= 2; + else if (data & 0x10) + *rate *= 4; + + /* Calculate source of clock. */ + if (data & 0x01) { + *src = SND_FF_CLOCK_SRC_INTERNAL; + } else { + /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */ + switch ((data >> 10) & 0x07) { + case 0x03: + *src = SND_FF_CLOCK_SRC_SPDIF; + break; + case 0x04: + *src = SND_FF_CLOCK_SRC_WORD; + break; + case 0x05: + *src = SND_FF_CLOCK_SRC_LTC; + break; + case 0x00: + default: + *src = SND_FF_CLOCK_SRC_ADAT; + break; + } + } + + return 0; +} + static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port, int rcode) { diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 6dc36a2623b3..cdb1326f65b7 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -101,8 +101,6 @@ enum snd_ff_clock_src { };
struct snd_ff_protocol { - int (*get_clock)(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); @@ -114,6 +112,8 @@ struct snd_ff_protocol {
extern const struct snd_ff_protocol snd_ff_protocol_ff400;
+int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src); int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff); void snd_ff_transaction_unregister(struct snd_ff *ff);
Unlike Fireface 400, Fireface 800 have two pair of optical interface for ADAT signal and S/PDIF signal. ADAT signals for the interface are handled for sampling clock source separately.
This commit modifies a parser for clock configuration to distinguish these two ADAT signals.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-proc.c | 23 +++++++++++++++++++---- sound/firewire/fireface/ff-transaction.c | 12 ++++++++---- sound/firewire/fireface/ff.h | 5 +++-- 3 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index f5f3a1997a9e..a0c550dabe9a 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -65,7 +65,10 @@ static void proc_dump_clock_config(struct snd_info_entry *entry, } else { switch ((data >> 10) & 0x07) { case 0x00: - src = "ADAT"; + src = "ADAT1"; + break; + case 0x01: + src = "ADAT2"; break; case 0x03: src = "S/PDIF"; @@ -121,7 +124,7 @@ static void proc_dump_sync_status(struct snd_info_entry *entry, snd_iprintf(buffer, "none\n"); }
- snd_iprintf(buffer, "ADAT:"); + snd_iprintf(buffer, "ADAT1:"); if ((data >> 8) & 0x04) { if ((data >> 8) & 0x10) snd_iprintf(buffer, "sync\n"); @@ -131,6 +134,16 @@ static void proc_dump_sync_status(struct snd_info_entry *entry, snd_iprintf(buffer, "none\n"); }
+ snd_iprintf(buffer, "ADAT2:"); + if ((data >> 8) & 0x08) { + if ((data >> 8) & 0x20) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + snd_iprintf(buffer, "\nUsed external source:\n");
if (((data >> 22) & 0x07) == 0x07) { @@ -138,7 +151,10 @@ static void proc_dump_sync_status(struct snd_info_entry *entry, } else { switch ((data >> 22) & 0x07) { case 0x00: - snd_iprintf(buffer, "ADAT:"); + snd_iprintf(buffer, "ADAT1:"); + break; + case 0x01: + snd_iprintf(buffer, "ADAT2:"); break; case 0x03: snd_iprintf(buffer, "S/PDIF:"); @@ -149,7 +165,6 @@ static void proc_dump_sync_status(struct snd_info_entry *entry, case 0x07: snd_iprintf(buffer, "Nothing:"); break; - case 0x01: case 0x02: case 0x05: case 0x06: diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 1dad51da13e0..751662b62389 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -46,8 +46,14 @@ int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, if (data & 0x01) { *src = SND_FF_CLOCK_SRC_INTERNAL; } else { - /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */ + /* TODO: 0x02, 0x06, 0x07? */ switch ((data >> 10) & 0x07) { + case 0x00: + *src = SND_FF_CLOCK_SRC_ADAT1; + break; + case 0x01: + *src = SND_FF_CLOCK_SRC_ADAT2; + break; case 0x03: *src = SND_FF_CLOCK_SRC_SPDIF; break; @@ -57,10 +63,8 @@ int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, case 0x05: *src = SND_FF_CLOCK_SRC_LTC; break; - case 0x00: default: - *src = SND_FF_CLOCK_SRC_ADAT; - break; + return -EIO; } }
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index cdb1326f65b7..95cf90b4b533 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -94,10 +94,11 @@ struct snd_ff { enum snd_ff_clock_src { SND_FF_CLOCK_SRC_INTERNAL, SND_FF_CLOCK_SRC_SPDIF, - SND_FF_CLOCK_SRC_ADAT, + SND_FF_CLOCK_SRC_ADAT1, + SND_FF_CLOCK_SRC_ADAT2, SND_FF_CLOCK_SRC_WORD, SND_FF_CLOCK_SRC_LTC, - /* TODO: perhaps ADAT2 and TCO exists. */ + /* TODO: perhaps TCO exists. */ };
struct snd_ff_protocol {
As long as investigating packet dumps from Fireface 400/800, a register to receive asynchronous transactions for MIDI messages is the same. For Fireface 800, minor register is used.
This commit declares macros for the transactions and obsoletes model-specific parameters.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff400.c | 4 ---- sound/firewire/fireface/ff-transaction.c | 7 +++++-- sound/firewire/fireface/ff.h | 2 -- 3 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index d2fbb0382223..5cbaff9d6a40 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -16,8 +16,6 @@ #define FF400_ISOC_COMM_STOP 0x000080100510ull
#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull -#define FF400_MIDI_RX_PORT_0 0x000080180000ull -#define FF400_MIDI_RX_PORT_1 0x000080190000ull
static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { @@ -115,6 +113,4 @@ const struct snd_ff_protocol snd_ff_protocol_ff400 = { .switch_fetching_mode = ff400_switch_fetching_mode,
.midi_high_addr_reg = FF400_MIDI_HIGH_ADDR, - .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0, - .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1, }; diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 751662b62389..fa0bc956696f 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -8,6 +8,9 @@
#include "ff.h"
+#define SND_FF_REG_MIDI_RX_PORT_0 0x000080180000ull +#define SND_FF_REG_MIDI_RX_PORT_1 0x000080190000ull + int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, enum snd_ff_clock_src *src) { @@ -153,10 +156,10 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) fill_midi_buf(ff, port, i, buf[i]);
if (port == 0) { - addr = ff->spec->protocol->midi_rx_port_0_reg; + addr = SND_FF_REG_MIDI_RX_PORT_0; callback = finish_transmit_midi0_msg; } else { - addr = ff->spec->protocol->midi_rx_port_1_reg; + addr = SND_FF_REG_MIDI_RX_PORT_1; callback = finish_transmit_midi1_msg; }
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 95cf90b4b533..ea905285beab 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -107,8 +107,6 @@ struct snd_ff_protocol { int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
u64 midi_high_addr_reg; - u64 midi_rx_port_0_reg; - u64 midi_rx_port_1_reg; };
extern const struct snd_ff_protocol snd_ff_protocol_ff400;
Fireface 400 and 800 have the same mechanism to decide address to which asynchronous transactions are sent for MIDI messages, however they use different registers for controllers to notify higher 4 byte of the address.
This commit adds a model-specific parameter to represent the address. Additionally, it corrects some comments. I note that these two models have a difference to enable/disable the transaction.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff400.c | 4 -- sound/firewire/fireface/ff-transaction.c | 53 ++++++++++----------- sound/firewire/fireface/ff.c | 3 ++ sound/firewire/fireface/ff.h | 8 +++- 4 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index 5cbaff9d6a40..b283762e785c 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -15,8 +15,6 @@ #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull
-#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull - static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { __le32 reg; @@ -111,6 +109,4 @@ const struct snd_ff_protocol snd_ff_protocol_ff400 = { .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .switch_fetching_mode = ff400_switch_fetching_mode, - - .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR, }; diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index fa0bc956696f..1ce4cef6ca35 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -269,36 +269,33 @@ static int allocate_own_address(struct snd_ff *ff, int i) }
/* - * The configuration to start asynchronous transactions for MIDI messages is in - * 0x'0000'8010'051c. This register includes the other options, thus this driver - * doesn't touch it and leaves the decision to userspace. The userspace MUST add - * 0x04000000 to write transactions to the register to receive any MIDI - * messages. - * - * Here, I just describe MIDI-related offsets of the register, in little-endian - * order. - * * Controllers are allowed to register higher 4 bytes of address to receive - * the transactions. The register is 0x'0000'8010'03f4. On the other hand, the - * controllers are not allowed to register lower 4 bytes of the address. They - * are forced to select from 4 options by writing corresponding bits to - * 0x'0000'8010'051c. + * the transactions. Different models have different registers for this purpose; + * e.g. 0x'0000'8010'03f4 for Fireface 400. + * The controllers are not allowed to register lower 4 bytes of the address. + * They are forced to select one of 4 options for the part of address by writing + * corresponding bits to 0x'0000'8010'051f. + * + * The 3rd-6th bits of this register are flags to indicate lower 4 bytes of + * address to which the device transferrs the transactions. In short: + * - 0x20: 0x'....'....'0000'0180 + * - 0x10: 0x'....'....'0000'0100 + * - 0x08: 0x'....'....'0000'0080 + * - 0x04: 0x'....'....'0000'0000 * - * The 3rd-6th bits in MSB of this register are used to indicate lower 4 bytes - * of address to which the device transferrs the transactions. - * - 6th: 0x'....'....'0000'0180 - * - 5th: 0x'....'....'0000'0100 - * - 4th: 0x'....'....'0000'0080 - * - 3rd: 0x'....'....'0000'0000 + * This driver configure 0x'....'....'0000'0000 to receive MIDI messages from + * units. The 3rd bit of the register should be configured, however this driver + * deligates this task to userspace applications due to a restriction that this + * register is write-only and the other bits have own effects. * - * This driver configure 0x'....'....'0000'0000 for units to receive MIDI - * messages. 3rd bit of the register should be configured, however this driver - * deligates this task to user space applications due to a restriction that - * this register is write-only and the other bits have own effects. + * Unlike Fireface 800, Fireface 400 cancels transferring asynchronous + * transactions when the 1st and 2nd of the register stand. These two bits have + * the same effect. + * - 0x02, 0x01: cancel transferring * - * The 1st and 2nd bits in LSB of this register are used to cancel transferring - * asynchronous transactions. These two bits have the same effect. - * - 1st/2nd: cancel transferring + * On the other hand, the bits have no effect on Fireface 800. This model + * cancels asynchronous transactions when the higher 4 bytes of address is + * overwritten with zero. */ int snd_ff_transaction_reregister(struct snd_ff *ff) { @@ -313,7 +310,7 @@ int snd_ff_transaction_reregister(struct snd_ff *ff) addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32); reg = cpu_to_le32(addr); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->protocol->midi_high_addr_reg, + ff->spec->regs[SND_FF_REG_TYPE_MIDI_HIGH_ADDR], ®, sizeof(reg), 0); }
@@ -354,7 +351,7 @@ void snd_ff_transaction_unregister(struct snd_ff *ff) /* Release higher 4 bytes of address. */ reg = cpu_to_le32(0x00000000); snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - ff->spec->protocol->midi_high_addr_reg, + ff->spec->regs[SND_FF_REG_TYPE_MIDI_HIGH_ADDR], ®, sizeof(reg), 0);
fw_core_remove_address_handler(&ff->async_handler); diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 3f61cfeace69..2ce5e115b0eb 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -152,6 +152,9 @@ static const struct snd_ff_spec spec_ff400 = { .midi_in_ports = 2, .midi_out_ports = 2, .protocol = &snd_ff_protocol_ff400, + .regs = { + [SND_FF_REG_TYPE_MIDI_HIGH_ADDR] = 0x0000801003f4ull, + }, };
static const struct ieee1394_device_id snd_ff_id_table[] = { diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index ea905285beab..466304c72d76 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -42,6 +42,11 @@ #define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull #define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull
+enum snd_ff_reg_type { + SND_FF_REG_TYPE_MIDI_HIGH_ADDR = 0, + SND_FF_REG_TYPE_COUNT, +}; + struct snd_ff_protocol; struct snd_ff_spec { const char *const name; @@ -53,6 +58,7 @@ struct snd_ff_spec { unsigned int midi_out_ports;
const struct snd_ff_protocol *protocol; + u64 regs[SND_FF_REG_TYPE_COUNT]; };
struct snd_ff { @@ -105,8 +111,6 @@ struct snd_ff_protocol { int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); - - u64 midi_high_addr_reg; };
extern const struct snd_ff_protocol snd_ff_protocol_ff400;
Content of asynchronous transaction for MIDI messages differs between Fireface 400 and 800.
This commit adds a model-specific handler for the transaction and adds arrangement.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-ff400.c | 38 +++++++++++++++++++++ sound/firewire/fireface/ff-transaction.c | 34 +----------------- sound/firewire/fireface/ff.h | 1 + 3 files changed, 40 insertions(+), 33 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c index b283762e785c..8f34174ee813 100644 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ b/sound/firewire/fireface/ff-protocol-ff400.c @@ -105,7 +105,45 @@ static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) return err; }
+static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) +{ + int i; + + for (i = 0; i < length / 4; i++) { + u32 quad = le32_to_cpu(buf[i]); + u8 byte; + unsigned int index; + struct snd_rawmidi_substream *substream; + + /* Message in first port. */ + /* + * This value may represent the index of this unit when the same + * units are on the same IEEE 1394 bus. This driver doesn't use + * it. + */ + index = (quad >> 8) & 0xff; + if (index > 0) { + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream != NULL) { + byte = quad & 0xff; + snd_rawmidi_receive(substream, &byte, 1); + } + } + + /* Message in second port. */ + index = (quad >> 24) & 0xff; + if (index > 0) { + substream = READ_ONCE(ff->tx_midi_substreams[1]); + if (substream != NULL) { + byte = (quad >> 16) & 0xff; + snd_rawmidi_receive(substream, &byte, 1); + } + } + } +} + const struct snd_ff_protocol snd_ff_protocol_ff400 = { + .handle_midi_msg = ff400_handle_midi_msg, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .switch_fetching_mode = ff400_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 1ce4cef6ca35..d8768348067b 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -206,42 +206,10 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request, { struct snd_ff *ff = callback_data; __le32 *buf = data; - u32 quad; - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream; - int i;
fw_send_response(card, request, RCODE_COMPLETE);
- for (i = 0; i < length / 4; i++) { - quad = le32_to_cpu(buf[i]); - - /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - } + ff->spec->protocol->handle_midi_msg(ff, buf, length); }
static int allocate_own_address(struct snd_ff *ff, int i) diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 466304c72d76..178a96cb6e2a 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -108,6 +108,7 @@ enum snd_ff_clock_src { };
struct snd_ff_protocol { + void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
Fireface 800 is a flagship model of RME GmbH for audio and music units on IEEE 1394 bus, shipped 2004. This model consists of four chips: - TI TSB81BA3D for physical layer on cable environment of EEE 1394 bus - TI TSB82AA2 for link layer for 1394 OHCI bus bridge to PCI bus - Xilinx Spartan-3 FPGA XC3S400 - Xilinx High-Performance CPLD XC9572XL
This commit adds support Fireface 800. In this time, the support is restricted to its MIDI functionality, thus this commit adds some condition statements to avoid touching streaming functionality.
Unlike Fireface 400, Fireface 800 has no functionality to suppress asynchronous transactions for MIDI messages except for unregister of listen address in controller side, thus the feature is available as is.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 1 + sound/firewire/fireface/Makefile | 3 +- sound/firewire/fireface/ff-protocol-ff800.c | 27 +++++++++++ sound/firewire/fireface/ff.c | 51 ++++++++++++++++----- sound/firewire/fireface/ff.h | 1 + 5 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 sound/firewire/fireface/ff-protocol-ff800.c
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 44cedb65bb88..052e00590259 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -162,5 +162,6 @@ config SND_FIREFACE help Say Y here to include support for RME fireface series. * Fireface 400 + * Fireface 800
endif # SND_FIREWIRE diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 8f807284ba54..79a7d6d99d72 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,3 +1,4 @@ snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ - ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o + ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o \ + ff-protocol-ff800.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c new file mode 100644 index 000000000000..d24439734304 --- /dev/null +++ b/sound/firewire/fireface/ff-protocol-ff800.c @@ -0,0 +1,27 @@ +/* + * ff-protocol-ff800.c - a part of driver for RME Fireface series + * + * Copyright (c) 2018 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "ff.h" + +static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length) +{ + int i; + + for (i = 0; i < length / 4; i++) { + u8 byte = le32_to_cpu(buf[i]) & 0xff; + struct snd_rawmidi_substream *substream; + + substream = READ_ONCE(ff->tx_midi_substreams[0]); + if (substream) + snd_rawmidi_receive(substream, &byte, 1); + } +} + +const struct snd_ff_protocol snd_ff_protocol_ff800 = { + .handle_midi_msg = ff800_handle_midi_msg, +}; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 2ce5e115b0eb..d486984c0e5b 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -31,7 +31,8 @@ static void ff_card_free(struct snd_card *card) { struct snd_ff *ff = card->private_data;
- snd_ff_stream_destroy_duplex(ff); + if (ff->spec->protocol->begin_session) + snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff); }
@@ -56,9 +57,11 @@ static void do_registration(struct work_struct *work)
name_card(ff);
- err = snd_ff_stream_init_duplex(ff); - if (err < 0) - goto error; + if (ff->spec->protocol->begin_session) { + err = snd_ff_stream_init_duplex(ff); + if (err < 0) + goto error; + }
snd_ff_proc_init(ff);
@@ -66,13 +69,15 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error;
- err = snd_ff_create_pcm_devices(ff); - if (err < 0) - goto error; + if (ff->spec->protocol->begin_session) { + err = snd_ff_create_pcm_devices(ff); + if (err < 0) + goto error;
- err = snd_ff_create_hwdep_devices(ff); - if (err < 0) - goto error; + err = snd_ff_create_hwdep_devices(ff); + if (err < 0) + goto error; + }
err = snd_card_register(ff->card); if (err < 0) @@ -121,7 +126,7 @@ static void snd_ff_update(struct fw_unit *unit)
snd_ff_transaction_reregister(ff);
- if (ff->registered) + if (ff->registered && ff->spec->protocol->begin_session) snd_ff_stream_update_duplex(ff); }
@@ -145,6 +150,16 @@ static void snd_ff_remove(struct fw_unit *unit) fw_unit_put(ff->unit); }
+static const struct snd_ff_spec spec_ff800 = { + .name = "Fireface800", + .midi_in_ports = 1, + .midi_out_ports = 1, + .protocol = &snd_ff_protocol_ff800, + .regs = { + [SND_FF_REG_TYPE_MIDI_HIGH_ADDR] = 0x000200000320ull, + }, +}; + static const struct snd_ff_spec spec_ff400 = { .name = "Fireface400", .pcm_capture_channels = {18, 14, 10}, @@ -158,6 +173,18 @@ static const struct snd_ff_spec spec_ff400 = { };
static const struct ieee1394_device_id snd_ff_id_table[] = { + /* Fireface 800 */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = 0x000001, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ff800, + }, /* Fireface 400 */ { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -165,7 +192,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_VERSION | IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, - .specifier_id = 0x000a35, + .specifier_id = OUI_RME, .version = 0x000002, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 178a96cb6e2a..6e4a8197d3ca 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -114,6 +114,7 @@ struct snd_ff_protocol { int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); };
+extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff400;
int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate,
On Tue, 11 Dec 2018 11:17:27 +0100, Takashi Sakamoto wrote:
Hi,
In 2004, RME GmbH shipped Fireface 800 as a flagship model of its Fireface series. This model consists of some chips:
- TI TSB81BA3D for physical layer on cable environment of EEE 1394 bus
- TI TSB82AA2 for link layer for 1394 OHCI bus bridge to PCI bus
- Xilinx Spartan-3 FPGA XC3S400
- Xilinx High-Performance CPLD XC9572XL
A remarkable point of this model is that its physical/link layer support S800 (800Mbps) mode of IEEE 1394b. Within devices of audio and music unit on IEEE 1394 bus, this is an unique model to support it, as long as I know.
This commit adds support for the model with its MIDI functionality only. The functionality is quite similar to the one of Fireface 400. In patch 01-06, some codes specific to Fireface 400 are arranged to be shared by both models.
On the other hand, there're some differences. The largest difference is that the model has no register to switch enabling/disabling tx asynchronous transactions for MIDI messages. Therefore the functionality work well without any assist by userspace.
For this work, I use v2.77 firmware and v3.1.24 driver shipped by RME GmbH. If finding some differences between older firmware, please report it with enough information for debug. To help users for test, I prepare a remote branch including these patches. This can be backport to Linux v4.17 or later. https://github.com/takaswie/snd-firewire-improve/tree/topic/ff800-midi
Takashi Sakamoto (8): ALSA: fireface: share some registers for status of clock synchronization ALSA: fireface: share status and configuration dump ALSA: fireface: share helper function to get current sampling rate and clock source ALSA: fireface: add support for second optical interface for ADAT stream ALSA: fireface: share register for async transaction of MIDI messages ALSA: fireface: add driver data for register for MIDI high address ALSA: fireface: localize a handler for MIDI messages on tx transaction ALSA: fireface: add support for Fireface 800 with MIDI functionality only
Applied all eight patches now. Thanks.
Takashi
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto