aboutsummaryrefslogtreecommitdiffstats
path: root/sound/firewire/fireface
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/fireface')
-rw-r--r--sound/firewire/fireface/Makefile3
-rw-r--r--sound/firewire/fireface/ff-pcm.c35
-rw-r--r--sound/firewire/fireface/ff-proc.c193
-rw-r--r--sound/firewire/fireface/ff-protocol-ff400.c341
-rw-r--r--sound/firewire/fireface/ff-protocol-ff800.c143
-rw-r--r--sound/firewire/fireface/ff-stream.c126
-rw-r--r--sound/firewire/fireface/ff-transaction.c157
-rw-r--r--sound/firewire/fireface/ff.c25
-rw-r--r--sound/firewire/fireface/ff.h42
9 files changed, 637 insertions, 428 deletions
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-pcm.c b/sound/firewire/fireface/ff-pcm.c
index bf47f9ec8703..d0bc96b20a65 100644
--- a/sound/firewire/fireface/ff-pcm.c
+++ b/sound/firewire/fireface/ff-pcm.c
@@ -8,11 +8,6 @@
#include "ff.h"
-static inline unsigned int get_multiplier_mode_with_index(unsigned int index)
-{
- return ((int)index - 1) / 2;
-}
-
static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
@@ -24,10 +19,16 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
- unsigned int i, mode;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
- mode = get_multiplier_mode_with_index(i);
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
if (!snd_interval_test(c, pcm_channels[mode]))
continue;
@@ -49,10 +50,16 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
- unsigned int i, mode;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
- mode = get_multiplier_mode_with_index(i);
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
if (!snd_interval_test(r, amdtp_rate_table[i]))
continue;
@@ -66,7 +73,6 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
const unsigned int *pcm_channels)
{
- unsigned int mode;
unsigned int rate, channels;
int i;
@@ -76,7 +82,12 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
hw->rate_max = 0;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
- mode = get_multiplier_mode_with_index(i);
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
channels = pcm_channels[mode];
if (pcm_channels[mode] == 0)
@@ -141,7 +152,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-proc.c b/sound/firewire/fireface/ff-proc.c
index 40ccbfd8ef89..a0c550dabe9a 100644
--- a/sound/firewire/fireface/ff-proc.c
+++ b/sound/firewire/fireface/ff-proc.c
@@ -12,16 +12,205 @@ 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, &reg, 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 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, &reg, 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;
+ }
+ }
+ }
- 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 64c3cb0fb926..2280fab9b3c7 100644
--- a/sound/firewire/fireface/ff-protocol-ff400.c
+++ b/sound/firewire/fireface/ff-protocol-ff400.c
@@ -14,85 +14,60 @@
#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
-#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)
+/*
+ * 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)
{
- __le32 reg;
- u32 data;
+ enum snd_ff_stream_mode mode;
+ int i;
int err;
- err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
- FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
+ // 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;
- 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;
+ /* 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;
- /* 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;
- }
- }
+ /* 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 0;
+ return err;
}
static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
{
__le32 reg;
- int i, err;
+ 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 = keep_resources(ff, rate);
+ if (err < 0)
+ return err;
/* Set the number of data blocks transferred in a second. */
reg = cpu_to_le32(rate);
@@ -142,233 +117,45 @@ static void ff400_finish_session(struct snd_ff *ff)
FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
-static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable)
+static void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
{
- __le32 *reg;
int i;
- int err;
- reg = kcalloc(18, sizeof(__le32), GFP_KERNEL);
- if (reg == NULL)
- return -ENOMEM;
+ for (i = 0; i < length / 4; i++) {
+ u32 quad = le32_to_cpu(buf[i]);
+ u8 byte;
+ unsigned int index;
+ struct snd_rawmidi_substream *substream;
- if (enable) {
+ /* Message in first port. */
/*
- * 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.
+ * 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.
*/
- for (i = 0; i < 18; ++i)
- reg[i] = cpu_to_le32(0x00000001);
- }
-
- err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
- FF400_FETCH_PCM_FRAMES, reg,
- sizeof(__le32) * 18, 0);
- kfree(reg);
- 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,
- FF400_SYNC_STATUS, &reg, 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;
+ 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);
}
}
- }
-
- 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,
- FF400_CLOCK_CONFIG, &reg, 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;
+ /* 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);
+ }
}
}
-
- snd_iprintf(buffer, "Sync to clock source: %s\n", src);
}
const struct snd_ff_protocol snd_ff_protocol_ff400 = {
- .get_clock = ff400_get_clock,
+ .handle_midi_msg = ff400_handle_midi_msg,
.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-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c
new file mode 100644
index 000000000000..2acbf6039770
--- /dev/null
+++ b/sound/firewire/fireface/ff-protocol-ff800.c
@@ -0,0 +1,143 @@
+/*
+ * 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 <linux/delay.h>
+
+#include "ff.h"
+
+#define FF800_STF 0x0000fc88f000
+#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
+#define FF800_ALLOC_TX_STREAM 0x0000fc88f008
+#define FF800_ISOC_COMM_START 0x0000fc88f00c
+#define FF800_TX_S800_FLAG 0x00000800
+#define FF800_ISOC_COMM_STOP 0x0000fc88f010
+
+#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
+
+static int allocate_rx_resources(struct snd_ff *ff)
+{
+ u32 data;
+ __le32 reg;
+ int err;
+
+ // Controllers should allocate isochronous resources for rx stream.
+ 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)
+ return err;
+
+ // Set isochronous channel and the number of quadlets of rx packets.
+ data = ff->rx_stream.data_block_quadlets << 3;
+ data = (data << 8) | ff->rx_resources.channel;
+ reg = cpu_to_le32(data);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+}
+
+static int allocate_tx_resources(struct snd_ff *ff)
+{
+ __le32 reg;
+ unsigned int count;
+ unsigned int tx_isoc_channel;
+ int err;
+
+ reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Wait till the format of tx packet is available.
+ count = 0;
+ while (count++ < 10) {
+ u32 data;
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = le32_to_cpu(reg);
+ if (data != 0xffffffff) {
+ tx_isoc_channel = data;
+ break;
+ }
+
+ msleep(50);
+ }
+ if (count >= 10)
+ return -ETIMEDOUT;
+
+ // NOTE: this is a makeshift to start OHCI 1394 IR context in the
+ // channel. On the other hand, 'struct fw_iso_resources.allocated' is
+ // not true and it's not deallocated at stop.
+ ff->tx_resources.channel = tx_isoc_channel;
+
+ return 0;
+}
+
+static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ __le32 reg;
+ int err;
+
+ reg = cpu_to_le32(rate);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // If starting isochronous communication immediately, change of STF has
+ // no effect. In this case, the communication runs based on former STF.
+ // Let's sleep for a bit.
+ msleep(100);
+
+ err = allocate_rx_resources(ff);
+ if (err < 0)
+ return err;
+
+ err = allocate_tx_resources(ff);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_le32(0x80000000);
+ reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
+ if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
+ reg |= cpu_to_le32(FF800_TX_S800_FLAG);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff800_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x80000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+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,
+ .begin_session = ff800_begin_session,
+ .finish_session = ff800_finish_session,
+};
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
index 78880922120e..a490e4553721 100644
--- a/sound/firewire/fireface/ff-stream.c
+++ b/sound/firewire/fireface/ff-stream.c
@@ -10,73 +10,71 @@
#define CALLBACK_TIMEOUT_MS 200
-static int get_rate_mode(unsigned int rate, unsigned int *mode)
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode)
{
- int i;
-
- for (i = 0; i < CIP_SFC_COUNT; i++) {
- if (amdtp_rate_table[i] == rate)
- break;
- }
-
- if (i == CIP_SFC_COUNT)
+ static const enum snd_ff_stream_mode modes[] = {
+ [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID,
+ [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID,
+ [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH,
+ [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH,
+ };
+
+ if (sfc >= CIP_SFC_COUNT)
return -EINVAL;
- *mode = ((int)i - 1) / 2;
+ *mode = modes[sfc];
return 0;
}
-/*
- * 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)
+static void release_resources(struct snd_ff *ff)
{
- int mode;
- int err;
-
- err = get_rate_mode(rate, &mode);
- if (err < 0)
- return err;
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+}
- /* Keep resources for in-stream. */
- err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
- ff->spec->pcm_capture_channels[mode]);
- if (err < 0)
- return err;
- 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;
+static int switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+ unsigned int count;
+ __le32 *reg;
+ int i;
+ int 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);
+ 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 void release_resources(struct snd_ff *ff)
-{
- fw_iso_resources_free(&ff->tx_resources);
- fw_iso_resources_free(&ff->rx_resources);
-}
-
static inline void finish_session(struct snd_ff *ff)
{
ff->spec->protocol->finish_session(ff);
- ff->spec->protocol->switch_fetching_mode(ff, false);
+ switch_fetching_mode(ff, false);
}
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
@@ -149,7 +147,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 ||
@@ -168,9 +166,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
* packets. Then, the device transfers packets.
*/
if (!amdtp_stream_running(&ff->rx_stream)) {
- err = keep_resources(ff, rate);
+ enum snd_ff_stream_mode mode;
+ int i;
+
+ 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)
- goto error;
+ return err;
+
+ err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
+ ff->spec->pcm_capture_channels[mode]);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
+ ff->spec->pcm_playback_channels[mode]);
+ if (err < 0)
+ return err;
err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
@@ -188,7 +206,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
goto error;
}
- err = ff->spec->protocol->switch_fetching_mode(ff, true);
+ err = switch_fetching_mode(ff, true);
if (err < 0)
goto error;
}
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
index 332b29f8ed75..5f4ddfd55403 100644
--- a/sound/firewire/fireface/ff-transaction.c
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -8,6 +8,72 @@
#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)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ SND_FF_REG_CLOCK_CONFIG, &reg, 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)
{
@@ -90,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;
}
@@ -140,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)
@@ -203,36 +237,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)
{
@@ -247,7 +278,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->midi_high_addr,
&reg, sizeof(reg), 0);
}
@@ -288,7 +319,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->midi_high_addr,
&reg, 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..36575f4159d1 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -145,6 +145,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",
+ .pcm_capture_channels = {28, 20, 12},
+ .pcm_playback_channels = {28, 20, 12},
+ .midi_in_ports = 1,
+ .midi_out_ports = 1,
+ .protocol = &snd_ff_protocol_ff800,
+ .midi_high_addr = 0x000200000320ull,
+};
+
static const struct snd_ff_spec spec_ff400 = {
.name = "Fireface400",
.pcm_capture_channels = {18, 14, 10},
@@ -152,9 +162,22 @@ static const struct snd_ff_spec spec_ff400 = {
.midi_in_ports = 2,
.midi_out_ports = 2,
.protocol = &snd_ff_protocol_ff400,
+ .midi_high_addr = 0x0000801003f4ull,
};
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 |
@@ -162,7 +185,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 64df44beb950..7dfc7745a914 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -31,23 +31,34 @@
#include "../amdtp-stream.h"
#include "../iso-resources.h"
-#define SND_FF_STREAM_MODES 3
-
#define SND_FF_MAXIMIM_MIDI_QUADS 9
#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
+
+enum snd_ff_stream_mode {
+ SND_FF_STREAM_MODE_LOW = 0,
+ SND_FF_STREAM_MODE_MID,
+ SND_FF_STREAM_MODE_HIGH,
+ SND_FF_STREAM_MODE_COUNT,
+};
+
struct snd_ff_protocol;
struct snd_ff_spec {
const char *const name;
- const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES];
- const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES];
+ const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT];
+ const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT];
unsigned int midi_in_ports;
unsigned int midi_out_ports;
const struct snd_ff_protocol *protocol;
+ u64 midi_high_addr;
};
struct snd_ff {
@@ -89,31 +100,24 @@ 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 {
- int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
- enum snd_ff_clock_src *src);
+ 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);
-
- 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;
};
+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);
@@ -125,6 +129,8 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir);
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode);
int snd_ff_stream_init_duplex(struct snd_ff *ff);
void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);