[alsa-devel] [PATCH 0/9] ALSA: fireface: add support for Fireface UCX
Hi,
In 2012, RME GmbH shipped Fireface UFX. This model is one of latter models of Fireface series and support both of IEEE 1394 bus and USB. Furthermore, it supports two types of remote control unit (Basic Remote and Advanced Remote Control) with by 9pin mini-din connector.
This patchset adds support for this model, as a part of devices by ALSA firewire stack (therefore it's not a device of USB). At present, maintain of isochronous communication and synchronization status are cleared but asynchronous communication for MIDI messages are not yet, therefore this commit adds support for isochronous communication for PCM frames.
This work is based on my packet analysis from rent device for a short term (2 weeks). I'm happy to get your responses till next weekend. Driver code in a remote branch is available for backport till Linux kernel v4.17 or later for your test. https://github.com/takaswie/snd-firewire-improve/tree/topic/ff800-midi
Regards
Takashi Sakamoto (9): ALSA: fireface: rename protocol layer for former models ALSA: fireface: unify protocol layer for FF400/FF800 ALSA: fireface: obsolete proc node to leave one node ALSA: fireface: add protocol-dependent operation to dump status ALSA: fireface: add protocol-dependent operation to switch mode to fetch PCM frame ALSA: fireface: add protocol-dependent operation to get clock status ALSA: fireface: code refactoring for dump of sync status ALSA: fireface: code refactoring to parse of clock configuration ALSA: fireface: add support for Fireface UCX
sound/firewire/Kconfig | 1 + sound/firewire/fireface/Makefile | 4 +- sound/firewire/fireface/ff-pcm.c | 2 +- sound/firewire/fireface/ff-proc.c | 217 +------- sound/firewire/fireface/ff-protocol-ff400.c | 161 ------ sound/firewire/fireface/ff-protocol-ff800.c | 143 ----- sound/firewire/fireface/ff-protocol-former.c | 549 +++++++++++++++++++ sound/firewire/fireface/ff-protocol-latter.c | 273 +++++++++ sound/firewire/fireface/ff-stream.c | 40 +- sound/firewire/fireface/ff-transaction.c | 63 --- sound/firewire/fireface/ff.c | 41 +- sound/firewire/fireface/ff.h | 13 +- 12 files changed, 886 insertions(+), 621 deletions(-) delete mode 100644 sound/firewire/fireface/ff-protocol-ff400.c delete mode 100644 sound/firewire/fireface/ff-protocol-ff800.c create mode 100644 sound/firewire/fireface/ff-protocol-former.c create mode 100644 sound/firewire/fireface/ff-protocol-latter.c
In a series of Fireface, later model supports different protocol from former models.
This commit is a preparation to support both of protocols.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/Makefile | 2 +- .../{ff-protocol-ff800.c => ff-protocol-former.c} | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) rename sound/firewire/fireface/{ff-protocol-ff800.c => ff-protocol-former.c} (94%)
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 79a7d6d99d72..3fb586d32584 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,4 +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-protocol-ff800.o + ff-protocol-former.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-former.c similarity index 94% rename from sound/firewire/fireface/ff-protocol-ff800.c rename to sound/firewire/fireface/ff-protocol-former.c index 2acbf6039770..a383fd5fc879 100644 --- a/sound/firewire/fireface/ff-protocol-ff800.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -1,10 +1,9 @@ -/* - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// ff-protocol-former.c - a part of driver for RME Fireface series +// +// Copyright (c) 2019 Takashi Sakamoto +// +// Licensed under the terms of the GNU General Public License, version 2.
#include <linux/delay.h>
This commit moves codes for Fireface 400 to a file of former protocol.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/Makefile | 3 +- sound/firewire/fireface/ff-protocol-ff400.c | 161 ------------------- sound/firewire/fireface/ff-protocol-former.c | 147 +++++++++++++++++ 3 files changed, 148 insertions(+), 163 deletions(-) delete mode 100644 sound/firewire/fireface/ff-protocol-ff400.c
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 3fb586d32584..62eb78962b93 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,4 +1,3 @@ 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-protocol-former.o + ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c deleted file mode 100644 index 2280fab9b3c7..000000000000 --- a/sound/firewire/fireface/ff-protocol-ff400.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * ff-protocol-ff400.c - a part of driver for RME Fireface series - * - * Copyright (c) 2015-2017 Takashi Sakamoto - * - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include <linux/delay.h> -#include "ff.h" - -#define FF400_STF 0x000080100500ull -#define FF400_RX_PACKET_FORMAT 0x000080100504ull -#define FF400_ISOC_COMM_START 0x000080100508ull -#define FF400_TX_PACKET_FORMAT 0x00008010050cull -#define FF400_ISOC_COMM_STOP 0x000080100510ull - -/* - * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, - * we can allocate between 0 and 7 channel. - */ -static int keep_resources(struct snd_ff *ff, unsigned int rate) -{ - enum snd_ff_stream_mode mode; - int i; - int err; - - // Check whether the given value is supported or not. - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) - break; - } - if (i >= CIP_SFC_COUNT) - return -EINVAL; - - err = snd_ff_stream_get_multiplier_mode(i, &mode); - if (err < 0) - return err; - - /* Keep resources for in-stream. */ - ff->tx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->tx_resources, - amdtp_stream_get_max_payload(&ff->tx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; - - /* Keep resources for out-stream. */ - err = amdtp_ff_set_parameters(&ff->rx_stream, rate, - ff->spec->pcm_playback_channels[mode]); - if (err < 0) - return err; - ff->rx_resources.channels_mask = 0x00000000000000ffuLL; - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - fw_iso_resources_free(&ff->tx_resources); - - return err; -} - -static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) -{ - __le32 reg; - int err; - - err = keep_resources(ff, rate); - if (err < 0) - return err; - - /* Set the number of data blocks transferred in a second. */ - reg = cpu_to_le32(rate); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; - - msleep(100); - - /* - * Set isochronous channel and the number of quadlets of received - * packets. - */ - reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | - ff->rx_resources.channel); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0); - if (err < 0) - return err; - - /* - * Set isochronous channel and the number of quadlets of transmitted - * packet. - */ - /* TODO: investigate the purpose of this 0x80. */ - reg = cpu_to_le32((0x80 << 24) | - (ff->tx_resources.channel << 5) | - (ff->tx_stream.data_block_quadlets)); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0); - if (err < 0) - return err; - - /* Allow to transmit packets. */ - reg = cpu_to_le32(0x00000001); - return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_ISOC_COMM_START, ®, sizeof(reg), 0); -} - -static void ff400_finish_session(struct snd_ff *ff) -{ - __le32 reg; - - reg = cpu_to_le32(0x80000000); - snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); -} - -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, -}; diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index a383fd5fc879..ed1271a89484 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -140,3 +140,150 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = { .begin_session = ff800_begin_session, .finish_session = ff800_finish_session, }; + +#define FF400_STF 0x000080100500ull +#define FF400_RX_PACKET_FORMAT 0x000080100504ull +#define FF400_ISOC_COMM_START 0x000080100508ull +#define FF400_TX_PACKET_FORMAT 0x00008010050cull +#define FF400_ISOC_COMM_STOP 0x000080100510ull + +/* + * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, + * we can allocate between 0 and 7 channel. + */ +static int keep_resources(struct snd_ff *ff, unsigned int rate) +{ + enum snd_ff_stream_mode mode; + int i; + int err; + + // Check whether the given value is supported or not. + for (i = 0; i < CIP_SFC_COUNT; i++) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + return err; + + /* Keep resources for in-stream. */ + ff->tx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->tx_resources, + amdtp_stream_get_max_payload(&ff->tx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + /* Keep resources for out-stream. */ + ff->rx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + fw_iso_resources_free(&ff->tx_resources); + + return err; +} + +static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) +{ + __le32 reg; + int err; + + err = keep_resources(ff, rate); + if (err < 0) + return err; + + /* Set the number of data blocks transferred in a second. */ + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + msleep(100); + + /* + * Set isochronous channel and the number of quadlets of received + * packets. + */ + reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | + ff->rx_resources.channel); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* + * Set isochronous channel and the number of quadlets of transmitted + * packet. + */ + /* TODO: investigate the purpose of this 0x80. */ + reg = cpu_to_le32((0x80 << 24) | + (ff->tx_resources.channel << 5) | + (ff->tx_stream.data_block_quadlets)); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0); + if (err < 0) + return err; + + /* Allow to transmit packets. */ + reg = cpu_to_le32(0x00000001); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_ISOC_COMM_START, ®, sizeof(reg), 0); +} + +static void ff400_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x80000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); +} + +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, +};
In a series of Fireface, latter protocol has no way for drivers to retrieve current clock configuration. On the other hand, this driver has proc node for it.
This commit removes a proc node to dump both clock configuration and synchronization status in one proc node.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-proc.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index a0c550dabe9a..37f84b7fc432 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -8,10 +8,8 @@
#include "./ff.h"
-static void proc_dump_clock_config(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) { - struct snd_ff *ff = entry->private_data; __le32 reg; u32 data; unsigned int rate; @@ -87,10 +85,8 @@ static void proc_dump_clock_config(struct snd_info_entry *entry, 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) +static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer) { - struct snd_ff *ff = entry->private_data; __le32 reg; u32 data; int err; @@ -213,6 +209,15 @@ static void proc_dump_sync_status(struct snd_info_entry *entry, snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); }
+static void proc_dump_status(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ff *ff = entry->private_data; + + dump_clock_config(ff, buffer); + dump_sync_status(ff, buffer); +} + static void add_node(struct snd_ff *ff, struct snd_info_entry *root, const char *name, void (*op)(struct snd_info_entry *e, @@ -247,6 +252,5 @@ void snd_ff_proc_init(struct snd_ff *ff) return; }
- add_node(ff, root, "clock-config", proc_dump_clock_config); - add_node(ff, root, "sync-status", proc_dump_sync_status); + add_node(ff, root, "status", proc_dump_status); }
This commit adds a member for a callback function to dump status and move existing code to former protocol.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-proc.c | 204 +----------------- sound/firewire/fireface/ff-protocol-former.c | 212 +++++++++++++++++++ sound/firewire/fireface/ff.h | 2 +- 3 files changed, 214 insertions(+), 204 deletions(-)
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index 37f84b7fc432..8a7cfb6ccce6 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -8,214 +8,12 @@
#include "./ff.h"
-static void 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 = "ADAT1"; - break; - case 0x01: - src = "ADAT2"; - 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 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, "ADAT1:"); - 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, "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) { - snd_iprintf(buffer, "None\n"); - } else { - switch ((data >> 22) & 0x07) { - case 0x00: - snd_iprintf(buffer, "ADAT1:"); - break; - case 0x01: - snd_iprintf(buffer, "ADAT2:"); - break; - case 0x03: - snd_iprintf(buffer, "S/PDIF:"); - break; - case 0x04: - snd_iprintf(buffer, "Word:"); - break; - case 0x07: - snd_iprintf(buffer, "Nothing:"); - break; - 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 proc_dump_status(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_ff *ff = entry->private_data;
- dump_clock_config(ff, buffer); - dump_sync_status(ff, buffer); + ff->spec->protocol->dump_status(ff, buffer); }
static void add_node(struct snd_ff *ff, struct snd_info_entry *root, diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index ed1271a89484..5f97e7fc7281 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -9,6 +9,216 @@
#include "ff.h"
+#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull + +static void 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 = "ADAT1"; + break; + case 0x01: + src = "ADAT2"; + 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 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, + FORMER_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, "ADAT1:"); + 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, "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) { + snd_iprintf(buffer, "None\n"); + } else { + switch ((data >> 22) & 0x07) { + case 0x00: + snd_iprintf(buffer, "ADAT1:"); + break; + case 0x01: + snd_iprintf(buffer, "ADAT2:"); + break; + case 0x03: + snd_iprintf(buffer, "S/PDIF:"); + break; + case 0x04: + snd_iprintf(buffer, "Word:"); + break; + case 0x07: + snd_iprintf(buffer, "Nothing:"); + break; + 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 former_dump_status(struct snd_ff *ff, + struct snd_info_buffer *buffer) +{ + dump_clock_config(ff, buffer); + dump_sync_status(ff, buffer); +} + #define FF800_STF 0x0000fc88f000 #define FF800_RX_PACKET_FORMAT 0x0000fc88f004 #define FF800_ALLOC_TX_STREAM 0x0000fc88f008 @@ -139,6 +349,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = { .handle_midi_msg = ff800_handle_midi_msg, .begin_session = ff800_begin_session, .finish_session = ff800_finish_session, + .dump_status = former_dump_status, };
#define FF400_STF 0x000080100500ull @@ -286,4 +497,5 @@ 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, + .dump_status = former_dump_status, }; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 7dfc7745a914..3f22b8d84e36 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -35,7 +35,6 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2
-#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull /* For block write request. */ #define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull #define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull @@ -111,6 +110,7 @@ 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); + void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer); };
extern const struct snd_ff_protocol snd_ff_protocol_ff800;
This commit adds a member for a callback function to switch frame fetching mode to former protocol.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-former.c | 38 ++++++++++++++++++++ sound/firewire/fireface/ff-stream.c | 38 ++------------------ sound/firewire/fireface/ff.h | 3 +- 3 files changed, 41 insertions(+), 38 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 5f97e7fc7281..279bd032acf0 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -10,6 +10,42 @@ #include "ff.h"
#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull +/* For block write request. */ +#define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull + +static int former_switch_fetching_mode(struct snd_ff *ff, bool enable) +{ + unsigned int count; + __le32 *reg; + int i; + int err; + + count = 0; + for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) + count = max(count, ff->spec->pcm_playback_channels[i]); + + reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + if (!enable) { + /* + * Each quadlet is corresponding to data channels in a data + * blocks in reverse order. Precisely, quadlets for available + * data channels should be enabled. Here, I take second best + * to fetch PCM frames from all of data channels regardless of + * stf. + */ + for (i = 0; i < count; ++i) + reg[i] = cpu_to_le32(0x00000001); + } + + err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, + FORMER_REG_FETCH_PCM_FRAMES, reg, + sizeof(__le32) * count, 0); + kfree(reg); + return err; +}
static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) { @@ -347,6 +383,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
const struct snd_ff_protocol snd_ff_protocol_ff800 = { .handle_midi_msg = ff800_handle_midi_msg, + .switch_fetching_mode = former_switch_fetching_mode, .begin_session = ff800_begin_session, .finish_session = ff800_finish_session, .dump_status = former_dump_status, @@ -495,6 +532,7 @@ static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
const struct snd_ff_protocol snd_ff_protocol_ff400 = { .handle_midi_msg = ff400_handle_midi_msg, + .switch_fetching_mode = former_switch_fetching_mode, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .dump_status = former_dump_status, diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index a490e4553721..43e1e2679798 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -37,44 +37,10 @@ static void release_resources(struct snd_ff *ff) fw_iso_resources_free(&ff->rx_resources); }
-static int switch_fetching_mode(struct snd_ff *ff, bool enable) -{ - unsigned int count; - __le32 *reg; - int i; - int err; - - count = 0; - for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i) - count = max(count, ff->spec->pcm_playback_channels[i]); - - reg = kcalloc(count, sizeof(__le32), GFP_KERNEL); - if (!reg) - return -ENOMEM; - - if (!enable) { - /* - * Each quadlet is corresponding to data channels in a data - * blocks in reverse order. Precisely, quadlets for available - * data channels should be enabled. Here, I take second best - * to fetch PCM frames from all of data channels regardless of - * stf. - */ - for (i = 0; i < count; ++i) - reg[i] = cpu_to_le32(0x00000001); - } - - err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, - SND_FF_REG_FETCH_PCM_FRAMES, reg, - sizeof(__le32) * count, 0); - kfree(reg); - return err; -} - static inline void finish_session(struct snd_ff *ff) { ff->spec->protocol->finish_session(ff); - switch_fetching_mode(ff, false); + ff->spec->protocol->switch_fetching_mode(ff, false); }
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) @@ -206,7 +172,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) goto error; }
- err = switch_fetching_mode(ff, true); + err = ff->spec->protocol->switch_fetching_mode(ff, true); if (err < 0) goto error; } diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 3f22b8d84e36..29f55518bf85 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -35,8 +35,6 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2
-/* For block write request. */ -#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull #define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull
enum snd_ff_stream_mode { @@ -108,6 +106,7 @@ enum snd_ff_clock_src {
struct snd_ff_protocol { void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); + int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer);
This commit adds a member for a callback function to get clock status to former protocol.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-pcm.c | 2 +- sound/firewire/fireface/ff-protocol-former.c | 68 +++++++++++++++++++- sound/firewire/fireface/ff-stream.c | 2 +- sound/firewire/fireface/ff-transaction.c | 63 ------------------ sound/firewire/fireface/ff.h | 6 +- 5 files changed, 71 insertions(+), 70 deletions(-)
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index d0bc96b20a65..5adf04b95c04 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -152,7 +152,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock;
- err = snd_ff_transaction_get_clock(ff, &rate, &src); + err = ff->spec->protocol->get_clock(ff, &rate, &src); if (err < 0) goto release_lock;
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 279bd032acf0..d32104ed0c08 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -12,6 +12,70 @@ #define FORMER_REG_SYNC_STATUS 0x0000801c0000ull /* For block write request. */ #define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull +#define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull + +static int former_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, + FORMER_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: 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; + case 0x04: + *src = SND_FF_CLOCK_SRC_WORD; + break; + case 0x05: + *src = SND_FF_CLOCK_SRC_LTC; + break; + default: + return -EIO; + } + } + + return 0; +}
static int former_switch_fetching_mode(struct snd_ff *ff, bool enable) { @@ -56,7 +120,7 @@ static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, - SND_FF_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); + FORMER_REG_CLOCK_CONFIG, ®, sizeof(reg), 0); if (err < 0) return;
@@ -383,6 +447,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
const struct snd_ff_protocol snd_ff_protocol_ff800 = { .handle_midi_msg = ff800_handle_midi_msg, + .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, .begin_session = ff800_begin_session, .finish_session = ff800_finish_session, @@ -532,6 +597,7 @@ static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
const struct snd_ff_protocol snd_ff_protocol_ff400 = { .handle_midi_msg = ff400_handle_midi_msg, + .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 43e1e2679798..a8a90f1ae09e 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -113,7 +113,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (ff->substreams_counter == 0) return 0;
- err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); + err = ff->spec->protocol->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 5f4ddfd55403..065e045d3fb5 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -11,69 +11,6 @@ #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) -{ - __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: 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; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - default: - return -EIO; - } - } - - 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 29f55518bf85..1de2f5ec26fd 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -35,8 +35,6 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2
-#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull - enum snd_ff_stream_mode { SND_FF_STREAM_MODE_LOW = 0, SND_FF_STREAM_MODE_MID, @@ -106,6 +104,8 @@ enum snd_ff_clock_src {
struct snd_ff_protocol { void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); + int (*get_clock)(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); @@ -115,8 +115,6 @@ struct snd_ff_protocol { 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, - 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);
This commit adds refactoring for dump of sync status by adding tables for check bits.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-former.c | 176 ++++++++----------- 1 file changed, 75 insertions(+), 101 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index d32104ed0c08..fb2af10d2690 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -190,126 +190,100 @@ static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer) { - __le32 reg; - u32 data; + static const struct { + char *const label; + u32 locked_mask; + u32 synced_mask; + } *clk_entry, clk_entries[] = { + { "WDClk", 0x40000000, 0x20000000, }, + { "S/PDIF", 0x00080000, 0x00040000, }, + { "ADAT1", 0x00000400, 0x00001000, }, + { "ADAT2", 0x00000800, 0x00002000, }, + }; + static const struct { + char *const label; + u32 mask; + } *referred_entry, referred_entries[] = { + { "ADAT1", 0x00000000, }, + { "ADAT2", 0x00400000, }, + { "S/PDIF", 0x00c00000, }, + { "WDclk", 0x01000000, }, + { "TCO", 0x01400000, }, + }; + static const struct { + unsigned int rate; + u32 mask; + } *rate_entry, rate_entries[] = { + { 32000, 0x02000000, }, + { 44100, 0x04000000, }, + { 48000, 0x06000000, }, + { 64000, 0x08000000, }, + { 88200, 0x0a000000, }, + { 96000, 0x0c000000, }, + { 128000, 0x0e000000, }, + { 176400, 0x10000000, }, + { 192000, 0x12000000, }, + }; + __le32 reg[2]; + u32 data[2]; + int i; int err;
- err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, - FORMER_REG_SYNC_STATUS, ®, sizeof(reg), 0); + err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, + FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0); if (err < 0) return; - - data = le32_to_cpu(reg); + data[0] = le32_to_cpu(reg[0]); + data[1] = le32_to_cpu(reg[1]);
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"); - } + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + const char *state; + + clk_entry = clk_entries + i; + if (data[0] & clk_entry->locked_mask) { + if (data[0] & clk_entry->synced_mask) + state = "sync"; + else + state = "lock"; + } else { + state = "none"; + }
- 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, "%s: %s\n", clk_entry->label, state); }
- snd_iprintf(buffer, "ADAT1:"); - 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, "Referred clock:\n");
- snd_iprintf(buffer, "ADAT2:"); - if ((data >> 8) & 0x08) { - if ((data >> 8) & 0x20) - snd_iprintf(buffer, "sync\n"); - else - snd_iprintf(buffer, "lock\n"); + if (data[1] & 0x00000001) { + snd_iprintf(buffer, "Internal\n"); } else { - snd_iprintf(buffer, "none\n"); - } + unsigned int rate; + const char *label;
- 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, "ADAT1:"); - break; - case 0x01: - snd_iprintf(buffer, "ADAT2:"); - break; - case 0x03: - snd_iprintf(buffer, "S/PDIF:"); - break; - case 0x04: - snd_iprintf(buffer, "Word:"); - break; - case 0x07: - snd_iprintf(buffer, "Nothing:"); - break; - case 0x02: - case 0x05: - case 0x06: - default: - snd_iprintf(buffer, "unknown:"); - break; + for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) { + referred_entry = referred_entries + i; + if ((data[0] & 0x1e0000) == referred_entry->mask) { + label = referred_entry->label; + break; + } } + if (i == ARRAY_SIZE(referred_entries)) + label = "none";
- 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"); + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { + rate_entry = rate_entries + i; + if ((data[0] & 0x1e000000) == rate_entry->mask) { + rate = rate_entry->rate; break; } } - } + if (i == ARRAY_SIZE(rate_entries)) + rate = 0;
- snd_iprintf(buffer, "Multiplied:"); - snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); + snd_iprintf(buffer, "%s %d\n", label, rate); + } }
static void former_dump_status(struct snd_ff *ff,
A procedure to retrieve clock configuration is used by two callers. Each of caller has duplicated code to parse bits.
This commit adds refactoring to remove the duplicated code.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-proc.c | 17 ++ sound/firewire/fireface/ff-protocol-former.c | 172 ++++++++----------- sound/firewire/fireface/ff.h | 1 + 3 files changed, 89 insertions(+), 101 deletions(-)
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index 8a7cfb6ccce6..a55e68ec1832 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -8,6 +8,23 @@
#include "./ff.h"
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src) +{ + static const char *const labels[] = { + "Internal", + "S/PDIF", + "ADAT1", + "ADAT2", + "Word", + "LTC", + }; + + if (src >= ARRAY_SIZE(labels)) + return NULL; + + return labels[src]; +} + static void proc_dump_status(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index fb2af10d2690..9c0ae50e88d1 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -14,6 +14,62 @@ #define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull #define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull
+static int parse_clock_bits(u32 data, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + static const struct { + unsigned int rate; + u32 mask; + } *rate_entry, rate_entries[] = { + { 32000, 0x00000002, }, + { 44100, 0x00000000, }, + { 48000, 0x00000006, }, + { 64000, 0x0000000a, }, + { 88200, 0x00000008, }, + { 96000, 0x0000000e, }, + { 128000, 0x00000012, }, + { 176400, 0x00000010, }, + { 192000, 0x00000016, }, + }; + static const struct { + enum snd_ff_clock_src src; + u32 mask; + } *clk_entry, clk_entries[] = { + { SND_FF_CLOCK_SRC_ADAT1, 0x00000000, }, + { SND_FF_CLOCK_SRC_ADAT2, 0x00000400, }, + { SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, }, + { SND_FF_CLOCK_SRC_WORD, 0x00001000, }, + { SND_FF_CLOCK_SRC_LTC, 0x00001800, }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { + rate_entry = rate_entries + i; + if ((data & 0x0000001e) == rate_entry->mask) { + *rate = rate_entry->rate; + break; + } + } + if (i == ARRAY_SIZE(rate_entries)) + return -EIO; + + if (data & 0x00000001) { + *src = SND_FF_CLOCK_SRC_INTERNAL; + } else { + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + clk_entry = clk_entries + i; + if ((data & 0x00001c00) == clk_entry->mask) { + *src = clk_entry->src; + break; + } + } + if (i == ARRAY_SIZE(clk_entries)) + return -EIO; + } + + return 0; +} + static int former_get_clock(struct snd_ff *ff, unsigned int *rate, enum snd_ff_clock_src *src) { @@ -27,54 +83,7 @@ static int former_get_clock(struct snd_ff *ff, unsigned int *rate, 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: 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; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - default: - return -EIO; - } - } - - return 0; + return parse_clock_bits(data, rate, src); }
static int former_switch_fetching_mode(struct snd_ff *ff, bool enable) @@ -116,76 +125,37 @@ static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) __le32 reg; u32 data; unsigned int rate; - const char *src; + enum snd_ff_clock_src src; + const char *label; int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, FORMER_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"); + (data & 0x00000020) ? "Professional" : "Consumer", + (data & 0x00000040) ? "on" : "off");
snd_iprintf(buffer, "Optical output interface format: %s\n", - ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); + (data & 0x00000100) ? "S/PDIF" : "ADAT");
snd_iprintf(buffer, "Word output single speed: %s\n", - ((data >> 8) & 0x20) ? "on" : "off"); + (data & 0x00002000) ? "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); + (data & 0x00000200) ? "Optical" : "Coaxial");
- if (data & 0x01) { - src = "Internal"; - } else { - switch ((data >> 10) & 0x07) { - case 0x00: - src = "ADAT1"; - break; - case 0x01: - src = "ADAT2"; - break; - case 0x03: - src = "S/PDIF"; - break; - case 0x04: - src = "Word"; - break; - case 0x05: - src = "LTC"; - break; - default: - return; - } - } + err = parse_clock_bits(data, &rate, &src); + if (err < 0) + return; + label = snd_ff_proc_get_clk_label(src); + if (!label) + return;
- snd_iprintf(buffer, "Sync to clock source: %s\n", src); + snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label); }
static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer) diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 1de2f5ec26fd..cdb16e931c31 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -139,6 +139,7 @@ int snd_ff_stream_lock_try(struct snd_ff *ff); void snd_ff_stream_lock_release(struct snd_ff *ff);
void snd_ff_proc_init(struct snd_ff *ff); +const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src);
int snd_ff_create_midi_devices(struct snd_ff *ff);
Fireface UFX was shipped by RME GmbH in 2012. This model supports later protocol for management of isochronous communication and synchronization of sampling transmission frequency.
This commit adds support for the model. At present, it's not clear how to encode MIDI messages and decide destination address for asynchronous transaction, thus this commit adds support for isochronous communication for PCM frames only.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 1 + sound/firewire/fireface/Makefile | 3 +- sound/firewire/fireface/ff-protocol-latter.c | 273 +++++++++++++++++++ sound/firewire/fireface/ff.c | 41 ++- sound/firewire/fireface/ff.h | 1 + 5 files changed, 310 insertions(+), 9 deletions(-) create mode 100644 sound/firewire/fireface/ff-protocol-latter.c
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 052e00590259..b9e96d0b3a0a 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -163,5 +163,6 @@ config SND_FIREFACE Say Y here to include support for RME fireface series. * Fireface 400 * Fireface 800 + * Fireface UCX
endif # SND_FIREWIRE diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 62eb78962b93..d64f4e2a1096 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-former.o + ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \ + ff-protocol-latter.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c new file mode 100644 index 000000000000..64767ba439db --- /dev/null +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +// ff-protocol-latter - a part of driver for RME Fireface series +// +// Copyright (c) 2019 Takashi Sakamoto +// +// Licensed under the terms of the GNU General Public License, version 2. + +#include <linux/delay.h> + +#include "ff.h" + +#define LATTER_STF 0xffff00000004 +#define LATTER_ISOC_CHANNELS 0xffff00000008 +#define LATTER_ISOC_START 0xffff0000000c +#define LATTER_FETCH_MODE 0xffff00000010 +#define LATTER_SYNC_STATUS 0x0000801c0000 + +static int parse_clock_bits(u32 data, unsigned int *rate, + enum snd_ff_clock_src *src) +{ + static const struct { + unsigned int rate; + u32 flag; + } *rate_entry, rate_entries[] = { + { 32000, 0x00000000, }, + { 44100, 0x01000000, }, + { 48000, 0x02000000, }, + { 64000, 0x04000000, }, + { 88200, 0x05000000, }, + { 96000, 0x06000000, }, + { 128000, 0x08000000, }, + { 176400, 0x09000000, }, + { 192000, 0x0a000000, }, + }; + static const struct { + enum snd_ff_clock_src src; + u32 flag; + } *clk_entry, clk_entries[] = { + { SND_FF_CLOCK_SRC_SPDIF, 0x00000200, }, + { SND_FF_CLOCK_SRC_ADAT1, 0x00000400, }, + { SND_FF_CLOCK_SRC_WORD, 0x00000600, }, + { SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { + rate_entry = rate_entries + i; + if ((data & 0x0f000000) == rate_entry->flag) { + *rate = rate_entry->rate; + break; + } + } + if (i == ARRAY_SIZE(rate_entries)) + return -EIO; + + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + clk_entry = clk_entries + i; + if ((data & 0x000e00) == clk_entry->flag) { + *src = clk_entry->src; + break; + } + } + if (i == ARRAY_SIZE(clk_entries)) + return -EIO; + + return 0; +} + +static int latter_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, + LATTER_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return err; + data = le32_to_cpu(reg); + + return parse_clock_bits(data, rate, src); +} + +static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable) +{ + u32 data; + __le32 reg; + + if (enable) + data = 0x00000000; + else + data = 0xffffffff; + reg = cpu_to_le32(data); + + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_FETCH_MODE, ®, sizeof(reg), 0); +} + +static int keep_resources(struct snd_ff *ff, unsigned int rate) +{ + enum snd_ff_stream_mode mode; + int i; + int err; + + // Check whether the given value is supported or not. + for (i = 0; i < CIP_SFC_COUNT; i++) { + if (amdtp_rate_table[i] == rate) + break; + } + if (i >= CIP_SFC_COUNT) + return -EINVAL; + + err = snd_ff_stream_get_multiplier_mode(i, &mode); + if (err < 0) + return err; + + /* Keep resources for in-stream. */ + ff->tx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->tx_resources, + amdtp_stream_get_max_payload(&ff->tx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + return err; + + /* Keep resources for out-stream. */ + ff->rx_resources.channels_mask = 0x00000000000000ffuLL; + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); + if (err < 0) + fw_iso_resources_free(&ff->tx_resources); + + return err; +} + +static int latter_begin_session(struct snd_ff *ff, unsigned int rate) +{ + static const struct { + unsigned int stf; + unsigned int code; + unsigned int flag; + } *entry, rate_table[] = { + { 32000, 0x00, 0x92, }, + { 44100, 0x02, 0x92, }, + { 48000, 0x04, 0x92, }, + { 64000, 0x08, 0x8e, }, + { 88200, 0x0a, 0x8e, }, + { 96000, 0x0c, 0x8e, }, + { 128000, 0x10, 0x8c, }, + { 176400, 0x12, 0x8c, }, + { 192000, 0x14, 0x8c, }, + }; + u32 data; + __le32 reg; + unsigned int count; + int i; + int err; + + for (i = 0; i < ARRAY_SIZE(rate_table); ++i) { + entry = rate_table + i; + if (entry->stf == rate) + break; + } + if (i == ARRAY_SIZE(rate_table)) + return -EINVAL; + + reg = cpu_to_le32(entry->code); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Confirm to shift transmission clock. + count = 0; + while (count++ < 10) { + unsigned int curr_rate; + enum snd_ff_clock_src src; + + err = latter_get_clock(ff, &curr_rate, &src); + if (err < 0) + return err; + + if (curr_rate == rate) + break; + } + if (count == 10) + return -ETIMEDOUT; + + err = keep_resources(ff, rate); + if (err < 0) + return err; + + data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_ISOC_CHANNELS, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Always use the maximum number of data channels in data block of + // packet. + reg = cpu_to_le32(entry->flag); + return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_ISOC_START, ®, sizeof(reg), 0); +} + +static void latter_finish_session(struct snd_ff *ff) +{ + __le32 reg; + + reg = cpu_to_le32(0x00000000); + snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_ISOC_START, ®, sizeof(reg), 0); +} + +static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer) +{ + static const struct { + char *const label; + u32 locked_mask; + u32 synced_mask; + } *clk_entry, clk_entries[] = { + { "S/PDIF", 0x00000001, 0x00000010, }, + { "ADAT", 0x00000002, 0x00000020, }, + { "WDClk", 0x00000004, 0x00000040, }, + }; + __le32 reg; + u32 data; + unsigned int rate; + enum snd_ff_clock_src src; + const char *label; + int i; + int err; + + err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, + LATTER_SYNC_STATUS, ®, sizeof(reg), 0); + if (err < 0) + return; + data = le32_to_cpu(reg); + + snd_iprintf(buffer, "External source detection:\n"); + + for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { + clk_entry = clk_entries + i; + snd_iprintf(buffer, "%s: ", clk_entry->label); + if (data & clk_entry->locked_mask) { + if (data & clk_entry->synced_mask) + snd_iprintf(buffer, "sync\n"); + else + snd_iprintf(buffer, "lock\n"); + } else { + snd_iprintf(buffer, "none\n"); + } + } + + err = parse_clock_bits(data, &rate, &src); + if (err < 0) + return; + label = snd_ff_proc_get_clk_label(src); + if (!label) + return; + + snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate); +} + +const struct snd_ff_protocol snd_ff_protocol_latter = { + .get_clock = latter_get_clock, + .switch_fetching_mode = latter_switch_fetching_mode, + .begin_session = latter_begin_session, + .finish_session = latter_finish_session, + .dump_status = latter_dump_status, +}; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 36575f4159d1..fd9c980e3cf4 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -32,7 +32,8 @@ static void ff_card_free(struct snd_card *card) struct snd_ff *ff = card->private_data;
snd_ff_stream_destroy_duplex(ff); - snd_ff_transaction_unregister(ff); + if (ff->spec->midi_high_addr > 0) + snd_ff_transaction_unregister(ff); }
static void do_registration(struct work_struct *work) @@ -50,9 +51,11 @@ static void do_registration(struct work_struct *work) ff->card->private_free = ff_card_free; ff->card->private_data = ff;
- err = snd_ff_transaction_register(ff); - if (err < 0) - goto error; + if (ff->spec->midi_high_addr > 0) { + err = snd_ff_transaction_register(ff); + if (err < 0) + goto error; + }
name_card(ff);
@@ -62,9 +65,11 @@ static void do_registration(struct work_struct *work)
snd_ff_proc_init(ff);
- err = snd_ff_create_midi_devices(ff); - if (err < 0) - goto error; + if (ff->spec->midi_in_ports > 0 || ff->spec->midi_out_ports > 0) { + err = snd_ff_create_midi_devices(ff); + if (err < 0) + goto error; + }
err = snd_ff_create_pcm_devices(ff); if (err < 0) @@ -119,7 +124,8 @@ static void snd_ff_update(struct fw_unit *unit) if (!ff->registered) snd_fw_schedule_registration(unit, &ff->dwork);
- snd_ff_transaction_reregister(ff); + if (ff->spec->midi_high_addr > 0) + snd_ff_transaction_reregister(ff);
if (ff->registered) snd_ff_stream_update_duplex(ff); @@ -165,6 +171,13 @@ static const struct snd_ff_spec spec_ff400 = { .midi_high_addr = 0x0000801003f4ull, };
+static const struct snd_ff_spec spec_ucx = { + .name = "FirefaceUCX", + .pcm_capture_channels = {18, 14, 12}, + .pcm_playback_channels = {18, 14, 12}, + .protocol = &snd_ff_protocol_latter, +}; + static const struct ieee1394_device_id snd_ff_id_table[] = { /* Fireface 800 */ { @@ -190,6 +203,18 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, }, + // Fireface UCX. + { + .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 = 0x000004, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ucx, + }, {} }; MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index cdb16e931c31..8aea7920b57f 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -114,6 +114,7 @@ struct snd_ff_protocol {
extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff400; +extern const struct snd_ff_protocol snd_ff_protocol_latter;
int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff);
On Sun, 20 Jan 2019 09:25:44 +0100, Takashi Sakamoto wrote:
Hi,
In 2012, RME GmbH shipped Fireface UFX. This model is one of latter models of Fireface series and support both of IEEE 1394 bus and USB. Furthermore, it supports two types of remote control unit (Basic Remote and Advanced Remote Control) with by 9pin mini-din connector.
This patchset adds support for this model, as a part of devices by ALSA firewire stack (therefore it's not a device of USB). At present, maintain of isochronous communication and synchronization status are cleared but asynchronous communication for MIDI messages are not yet, therefore this commit adds support for isochronous communication for PCM frames.
This work is based on my packet analysis from rent device for a short term (2 weeks). I'm happy to get your responses till next weekend. Driver code in a remote branch is available for backport till Linux kernel v4.17 or later for your test. https://github.com/takaswie/snd-firewire-improve/tree/topic/ff800-midi
Regards
Takashi Sakamoto (9): ALSA: fireface: rename protocol layer for former models ALSA: fireface: unify protocol layer for FF400/FF800 ALSA: fireface: obsolete proc node to leave one node ALSA: fireface: add protocol-dependent operation to dump status ALSA: fireface: add protocol-dependent operation to switch mode to fetch PCM frame ALSA: fireface: add protocol-dependent operation to get clock status ALSA: fireface: code refactoring for dump of sync status ALSA: fireface: code refactoring to parse of clock configuration ALSA: fireface: add support for Fireface UCX
Applied all patches now. Thanks.
Takashi
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto