summaryrefslogtreecommitdiff
path: root/sound/firewire/motu/amdtp-motu.c
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-03-22 21:30:23 +0900
committerTakashi Iwai <tiwai@suse.de>2017-03-28 12:33:56 +0200
commit9e796e7d59e71f8a556cfbdc2ffa3aff0555dd0e (patch)
tree2248865f8f2695690589c5792cda0092f0afc1e8 /sound/firewire/motu/amdtp-motu.c
parentdd49b2d1f04af9b1f44e9fe82c85f374f662c61b (diff)
ALSA: firewire-motu: add MIDI functionality
In MOTU FireWire series, MIDI messages are multiplexed to isochronous packets as well as PCM frames, while the way is different from the one in IEC 61883-6. MIDI messages are put into a certain position in message chunks. One data block can includes one byte of the MIDI messages. When data block includes a MIDI byte, the block has a flag in a certain position of the message chunk. These positions are unique depending on protocols. Once a data block includes a MIDI byte, some following data blocks includes no MIDI bytes. Next MIDI byte appears on a data block corresponding to next cycle of physical MIDI bus. This seems to avoid buffer overflow caused by bandwidth differences between IEEE 1394 bus and physical MIDI bus. This commit adds MIDI functionality to transfer/receive MIDI messages. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/firewire/motu/amdtp-motu.c')
-rw-r--r--sound/firewire/motu/amdtp-motu.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index 11e44123ad65..0930cd8ca2cb 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -13,6 +13,12 @@
#define CIP_FMT_MOTU 0x02
#define MOTU_FDF_AM824 0x22
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
struct amdtp_motu {
/* For timestamp processing. */
unsigned int quotient_ticks_per_event;
@@ -24,9 +30,18 @@ struct amdtp_motu {
unsigned int pcm_chunks;
unsigned int pcm_byte_offset;
+
+ struct snd_rawmidi_substream *midi;
+ unsigned int midi_ports;
+ unsigned int midi_flag_offset;
+ unsigned int midi_byte_offset;
+
+ int midi_db_count;
+ unsigned int midi_db_interval;
};
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
struct snd_motu_packet_format *formats)
{
static const struct {
@@ -76,6 +91,13 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->pcm_chunks = pcm_chunks;
p->pcm_byte_offset = formats->pcm_byte_offset;
+ p->midi_ports = midi_ports;
+ p->midi_flag_offset = formats->midi_flag_offset;
+ p->midi_byte_offset = formats->midi_byte_offset;
+
+ p->midi_db_count = 0;
+ p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
+
/* IEEE 1394 bus requires. */
delay = 0x2e00;
@@ -187,12 +209,70 @@ int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_motu *p = s->protocol;
+
+ if (port < p->midi_ports)
+ WRITE_ONCE(p->midi, midi);
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+
+ if (midi && p->midi_db_count == 0 &&
+ snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
+ b[p->midi_flag_offset] = 0x01;
+ } else {
+ b[p->midi_byte_offset] = 0x00;
+ b[p->midi_flag_offset] = 0x00;
+ }
+
+ buffer += s->data_block_quadlets;
+
+ if (--p->midi_db_count < 0)
+ p->midi_db_count = p->midi_db_interval;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi;
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+ midi = READ_ONCE(p->midi);
+
+ if (midi && (b[p->midi_flag_offset] & 0x01))
+ snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
{
+ struct amdtp_motu *p = s->protocol;
struct snd_pcm_substream *pcm;
+ if (p->midi_ports)
+ read_midi_messages(s, buffer, data_blocks);
+
pcm = ACCESS_ONCE(s->pcm);
if (data_blocks > 0 && pcm)
read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
@@ -246,6 +326,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
{
+ struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
struct snd_pcm_substream *pcm;
/* Not used. */
@@ -253,6 +334,9 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
/* TODO: how to interact control messages between userspace? */
+ if (p->midi_ports)
+ write_midi_messages(s, buffer, data_blocks);
+
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);