* 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, sizeof(reg), 0);
+}
+
static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
{
int i;
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,
};
{
struct snd_ff *ff = card->private_data;
- if (ff->spec->protocol->begin_session)
- snd_ff_stream_destroy_duplex(ff);
+ snd_ff_stream_destroy_duplex(ff);
snd_ff_transaction_unregister(ff);
}
name_card(ff);
- if (ff->spec->protocol->begin_session) {
- err = snd_ff_stream_init_duplex(ff);
- if (err < 0)
- goto error;
- }
+ err = snd_ff_stream_init_duplex(ff);
+ if (err < 0)
+ goto error;
snd_ff_proc_init(ff);
if (err < 0)
goto error;
- if (ff->spec->protocol->begin_session) {
- err = snd_ff_create_pcm_devices(ff);
- if (err < 0)
- goto error;
+ err = snd_ff_create_pcm_devices(ff);
+ if (err < 0)
+ goto error;
- err = snd_ff_create_hwdep_devices(ff);
- if (err < 0)
- goto error;
- }
+ err = snd_ff_create_hwdep_devices(ff);
+ if (err < 0)
+ goto error;
err = snd_card_register(ff->card);
if (err < 0)
snd_ff_transaction_reregister(ff);
- if (ff->registered && ff->spec->protocol->begin_session)
+ if (ff->registered)
snd_ff_stream_update_duplex(ff);
}
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,