summaryrefslogtreecommitdiffstats
path: root/sound/firewire/motu
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-03-22 08:30:23 -0400
committerTakashi Iwai <tiwai@suse.de>2017-03-28 06:33:56 -0400
commit9e796e7d59e71f8a556cfbdc2ffa3aff0555dd0e (patch)
tree2248865f8f2695690589c5792cda0092f0afc1e8 /sound/firewire/motu
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')
-rw-r--r--sound/firewire/motu/Makefile2
-rw-r--r--sound/firewire/motu/amdtp-motu.c84
-rw-r--r--sound/firewire/motu/motu-midi.c153
-rw-r--r--sound/firewire/motu/motu-stream.c9
-rw-r--r--sound/firewire/motu/motu.c7
-rw-r--r--sound/firewire/motu/motu.h9
6 files changed, 260 insertions, 4 deletions
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 508b6894826a..a512c1e0f49c 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,3 +1,3 @@
1snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \ 1snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
2 motu-proc.o motu-pcm.o 2 motu-proc.o motu-pcm.o motu-midi.o
3obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o 3obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
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 @@
13#define CIP_FMT_MOTU 0x02 13#define CIP_FMT_MOTU 0x02
14#define MOTU_FDF_AM824 0x22 14#define MOTU_FDF_AM824 0x22
15 15
16/*
17 * Nominally 3125 bytes/second, but the MIDI port's clock might be
18 * 1% too slow, and the bus clock 100 ppm too fast.
19 */
20#define MIDI_BYTES_PER_SECOND 3093
21
16struct amdtp_motu { 22struct amdtp_motu {
17 /* For timestamp processing. */ 23 /* For timestamp processing. */
18 unsigned int quotient_ticks_per_event; 24 unsigned int quotient_ticks_per_event;
@@ -24,9 +30,18 @@ struct amdtp_motu {
24 30
25 unsigned int pcm_chunks; 31 unsigned int pcm_chunks;
26 unsigned int pcm_byte_offset; 32 unsigned int pcm_byte_offset;
33
34 struct snd_rawmidi_substream *midi;
35 unsigned int midi_ports;
36 unsigned int midi_flag_offset;
37 unsigned int midi_byte_offset;
38
39 int midi_db_count;
40 unsigned int midi_db_interval;
27}; 41};
28 42
29int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, 43int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
44 unsigned int midi_ports,
30 struct snd_motu_packet_format *formats) 45 struct snd_motu_packet_format *formats)
31{ 46{
32 static const struct { 47 static const struct {
@@ -76,6 +91,13 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
76 p->pcm_chunks = pcm_chunks; 91 p->pcm_chunks = pcm_chunks;
77 p->pcm_byte_offset = formats->pcm_byte_offset; 92 p->pcm_byte_offset = formats->pcm_byte_offset;
78 93
94 p->midi_ports = midi_ports;
95 p->midi_flag_offset = formats->midi_flag_offset;
96 p->midi_byte_offset = formats->midi_byte_offset;
97
98 p->midi_db_count = 0;
99 p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
100
79 /* IEEE 1394 bus requires. */ 101 /* IEEE 1394 bus requires. */
80 delay = 0x2e00; 102 delay = 0x2e00;
81 103
@@ -187,12 +209,70 @@ int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
187 return amdtp_stream_add_pcm_hw_constraints(s, runtime); 209 return amdtp_stream_add_pcm_hw_constraints(s, runtime);
188} 210}
189 211
212void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
213 struct snd_rawmidi_substream *midi)
214{
215 struct amdtp_motu *p = s->protocol;
216
217 if (port < p->midi_ports)
218 WRITE_ONCE(p->midi, midi);
219}
220
221static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
222 unsigned int data_blocks)
223{
224 struct amdtp_motu *p = s->protocol;
225 struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
226 u8 *b;
227 int i;
228
229 for (i = 0; i < data_blocks; i++) {
230 b = (u8 *)buffer;
231
232 if (midi && p->midi_db_count == 0 &&
233 snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
234 b[p->midi_flag_offset] = 0x01;
235 } else {
236 b[p->midi_byte_offset] = 0x00;
237 b[p->midi_flag_offset] = 0x00;
238 }
239
240 buffer += s->data_block_quadlets;
241
242 if (--p->midi_db_count < 0)
243 p->midi_db_count = p->midi_db_interval;
244 }
245}
246
247static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
248 unsigned int data_blocks)
249{
250 struct amdtp_motu *p = s->protocol;
251 struct snd_rawmidi_substream *midi;
252 u8 *b;
253 int i;
254
255 for (i = 0; i < data_blocks; i++) {
256 b = (u8 *)buffer;
257 midi = READ_ONCE(p->midi);
258
259 if (midi && (b[p->midi_flag_offset] & 0x01))
260 snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
261
262 buffer += s->data_block_quadlets;
263 }
264}
265
190static unsigned int process_tx_data_blocks(struct amdtp_stream *s, 266static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
191 __be32 *buffer, unsigned int data_blocks, 267 __be32 *buffer, unsigned int data_blocks,
192 unsigned int *syt) 268 unsigned int *syt)
193{ 269{
270 struct amdtp_motu *p = s->protocol;
194 struct snd_pcm_substream *pcm; 271 struct snd_pcm_substream *pcm;
195 272
273 if (p->midi_ports)
274 read_midi_messages(s, buffer, data_blocks);
275
196 pcm = ACCESS_ONCE(s->pcm); 276 pcm = ACCESS_ONCE(s->pcm);
197 if (data_blocks > 0 && pcm) 277 if (data_blocks > 0 && pcm)
198 read_pcm_s32(s, pcm->runtime, buffer, data_blocks); 278 read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
@@ -246,6 +326,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
246 __be32 *buffer, unsigned int data_blocks, 326 __be32 *buffer, unsigned int data_blocks,
247 unsigned int *syt) 327 unsigned int *syt)
248{ 328{
329 struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
249 struct snd_pcm_substream *pcm; 330 struct snd_pcm_substream *pcm;
250 331
251 /* Not used. */ 332 /* Not used. */
@@ -253,6 +334,9 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
253 334
254 /* TODO: how to interact control messages between userspace? */ 335 /* TODO: how to interact control messages between userspace? */
255 336
337 if (p->midi_ports)
338 write_midi_messages(s, buffer, data_blocks);
339
256 pcm = ACCESS_ONCE(s->pcm); 340 pcm = ACCESS_ONCE(s->pcm);
257 if (pcm) 341 if (pcm)
258 write_pcm_s32(s, pcm->runtime, buffer, data_blocks); 342 write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
new file mode 100644
index 000000000000..f232f29589d0
--- /dev/null
+++ b/sound/firewire/motu/motu-midi.c
@@ -0,0 +1,153 @@
1/*
2 * motu-midi.h - a part of driver for MOTU FireWire series
3 *
4 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
5 *
6 * Licensed under the terms of the GNU General Public License, version 2.
7 */
8#include "motu.h"
9
10static int midi_capture_open(struct snd_rawmidi_substream *substream)
11{
12 struct snd_motu *motu = substream->rmidi->private_data;
13 int err;
14
15 mutex_lock(&motu->mutex);
16
17 motu->capture_substreams++;
18 err = snd_motu_stream_start_duplex(motu, 0);
19
20 mutex_unlock(&motu->mutex);
21
22 return err;
23}
24
25static int midi_playback_open(struct snd_rawmidi_substream *substream)
26{
27 struct snd_motu *motu = substream->rmidi->private_data;
28 int err;
29
30 mutex_lock(&motu->mutex);
31
32 motu->playback_substreams++;
33 err = snd_motu_stream_start_duplex(motu, 0);
34
35 mutex_unlock(&motu->mutex);
36
37 return err;
38}
39
40static int midi_capture_close(struct snd_rawmidi_substream *substream)
41{
42 struct snd_motu *motu = substream->rmidi->private_data;
43
44 mutex_lock(&motu->mutex);
45
46 motu->capture_substreams--;
47 snd_motu_stream_stop_duplex(motu);
48
49 mutex_unlock(&motu->mutex);
50
51 return 0;
52}
53
54static int midi_playback_close(struct snd_rawmidi_substream *substream)
55{
56 struct snd_motu *motu = substream->rmidi->private_data;
57
58 mutex_lock(&motu->mutex);
59
60 motu->playback_substreams--;
61 snd_motu_stream_stop_duplex(motu);
62
63 mutex_unlock(&motu->mutex);
64
65 return 0;
66}
67
68static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
69{
70 struct snd_motu *motu = substrm->rmidi->private_data;
71 unsigned long flags;
72
73 spin_lock_irqsave(&motu->lock, flags);
74
75 if (up)
76 amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
77 substrm);
78 else
79 amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
80 NULL);
81
82 spin_unlock_irqrestore(&motu->lock, flags);
83}
84
85static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
86{
87 struct snd_motu *motu = substrm->rmidi->private_data;
88 unsigned long flags;
89
90 spin_lock_irqsave(&motu->lock, flags);
91
92 if (up)
93 amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
94 substrm);
95 else
96 amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
97 NULL);
98
99 spin_unlock_irqrestore(&motu->lock, flags);
100}
101
102static void set_midi_substream_names(struct snd_motu *motu,
103 struct snd_rawmidi_str *str)
104{
105 struct snd_rawmidi_substream *subs;
106
107 list_for_each_entry(subs, &str->substreams, list) {
108 snprintf(subs->name, sizeof(subs->name),
109 "%s MIDI %d", motu->card->shortname, subs->number + 1);
110 }
111}
112
113int snd_motu_create_midi_devices(struct snd_motu *motu)
114{
115 static struct snd_rawmidi_ops capture_ops = {
116 .open = midi_capture_open,
117 .close = midi_capture_close,
118 .trigger = midi_capture_trigger,
119 };
120 static struct snd_rawmidi_ops playback_ops = {
121 .open = midi_playback_open,
122 .close = midi_playback_close,
123 .trigger = midi_playback_trigger,
124 };
125 struct snd_rawmidi *rmidi;
126 struct snd_rawmidi_str *str;
127 int err;
128
129 /* create midi ports */
130 err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
131 if (err < 0)
132 return err;
133
134 snprintf(rmidi->name, sizeof(rmidi->name),
135 "%s MIDI", motu->card->shortname);
136 rmidi->private_data = motu;
137
138 rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
139 SNDRV_RAWMIDI_INFO_OUTPUT |
140 SNDRV_RAWMIDI_INFO_DUPLEX;
141
142 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
143 &capture_ops);
144 str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
145 set_midi_substream_names(motu, str);
146
147 snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
148 &playback_ops);
149 str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
150 set_midi_substream_names(motu, str);
151
152 return 0;
153}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 9aa698fc8da2..911d3487f775 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -28,22 +28,25 @@
28 28
29static int start_both_streams(struct snd_motu *motu, unsigned int rate) 29static int start_both_streams(struct snd_motu *motu, unsigned int rate)
30{ 30{
31 unsigned int midi_ports = 0;
31 __be32 reg; 32 __be32 reg;
32 u32 data; 33 u32 data;
33 int err; 34 int err;
34 35
36 if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI)
37 midi_ports = 1;
38
35 /* Set packet formation to our packet streaming engine. */ 39 /* Set packet formation to our packet streaming engine. */
36 err = amdtp_motu_set_parameters(&motu->rx_stream, rate, 40 err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
37 &motu->rx_packet_formats); 41 &motu->rx_packet_formats);
38 if (err < 0) 42 if (err < 0)
39 return err; 43 return err;
40 44
41 err = amdtp_motu_set_parameters(&motu->tx_stream, rate, 45 err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
42 &motu->tx_packet_formats); 46 &motu->tx_packet_formats);
43 if (err < 0) 47 if (err < 0)
44 return err; 48 return err;
45 49
46
47 /* Get isochronous resources on the bus. */ 50 /* Get isochronous resources on the bus. */
48 err = fw_iso_resources_allocate(&motu->rx_resources, 51 err = fw_iso_resources_allocate(&motu->rx_resources,
49 amdtp_stream_get_max_payload(&motu->rx_stream), 52 amdtp_stream_get_max_payload(&motu->rx_stream),
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index 801d6a73b0f3..d4da1377fa50 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -103,6 +103,12 @@ static void do_registration(struct work_struct *work)
103 if (err < 0) 103 if (err < 0)
104 goto error; 104 goto error;
105 105
106 if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI) {
107 err = snd_motu_create_midi_devices(motu);
108 if (err < 0)
109 goto error;
110 }
111
106 err = snd_card_register(motu->card); 112 err = snd_card_register(motu->card);
107 if (err < 0) 113 if (err < 0)
108 goto error; 114 goto error;
@@ -138,6 +144,7 @@ static int motu_probe(struct fw_unit *unit,
138 dev_set_drvdata(&unit->device, motu); 144 dev_set_drvdata(&unit->device, motu);
139 145
140 mutex_init(&motu->mutex); 146 mutex_init(&motu->mutex);
147 spin_lock_init(&motu->lock);
141 148
142 /* Allocate and register this sound card later. */ 149 /* Allocate and register this sound card later. */
143 INIT_DEFERRABLE_WORK(&motu->dwork, do_registration); 150 INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index afc6de654daa..338b35193001 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -21,12 +21,15 @@
21#include <sound/core.h> 21#include <sound/core.h>
22#include <sound/pcm.h> 22#include <sound/pcm.h>
23#include <sound/info.h> 23#include <sound/info.h>
24#include <sound/rawmidi.h>
24 25
25#include "../lib.h" 26#include "../lib.h"
26#include "../amdtp-stream.h" 27#include "../amdtp-stream.h"
27#include "../iso-resources.h" 28#include "../iso-resources.h"
28 29
29struct snd_motu_packet_format { 30struct snd_motu_packet_format {
31 unsigned char midi_flag_offset;
32 unsigned char midi_byte_offset;
30 unsigned char pcm_byte_offset; 33 unsigned char pcm_byte_offset;
31 34
32 unsigned char msg_chunks; 35 unsigned char msg_chunks;
@@ -38,6 +41,7 @@ struct snd_motu {
38 struct snd_card *card; 41 struct snd_card *card;
39 struct fw_unit *unit; 42 struct fw_unit *unit;
40 struct mutex mutex; 43 struct mutex mutex;
44 spinlock_t lock;
41 45
42 bool registered; 46 bool registered;
43 struct delayed_work dwork; 47 struct delayed_work dwork;
@@ -113,9 +117,12 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
113 enum amdtp_stream_direction dir, 117 enum amdtp_stream_direction dir,
114 const struct snd_motu_protocol *const protocol); 118 const struct snd_motu_protocol *const protocol);
115int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, 119int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
120 unsigned int midi_ports,
116 struct snd_motu_packet_format *formats); 121 struct snd_motu_packet_format *formats);
117int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s, 122int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
118 struct snd_pcm_runtime *runtime); 123 struct snd_pcm_runtime *runtime);
124void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
125 struct snd_rawmidi_substream *midi);
119 126
120int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg, 127int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
121 size_t size); 128 size_t size);
@@ -133,4 +140,6 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu);
133void snd_motu_proc_init(struct snd_motu *motu); 140void snd_motu_proc_init(struct snd_motu *motu);
134 141
135int snd_motu_create_pcm_devices(struct snd_motu *motu); 142int snd_motu_create_pcm_devices(struct snd_motu *motu);
143
144int snd_motu_create_midi_devices(struct snd_motu *motu);
136#endif 145#endif