summaryrefslogtreecommitdiffstats
path: root/sound/firewire/motu
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-03-22 08:30:18 -0400
committerTakashi Iwai <tiwai@suse.de>2017-03-28 06:33:30 -0400
commit4641c939401076c0ab7faba024827069723f719c (patch)
tree14abda097816a3a011e253811700dc7941754d76 /sound/firewire/motu
parent9dae017bf69b1c5aacba7be18cb734b66df30a37 (diff)
ALSA: firewire-motu: add MOTU specific protocol layer
MOTU FireWire series uses blocking transmission for AMDTP packet streaming. They transmit/receive 8,000 packets per second, to handle the same number of data blocks as current sampling transmission frequency. Thus, IEC 61883-1/6 packet streaming engine of ALSA firewire stack is available for them. However, the sequence of packet and data blocks includes some quirks. Below sample is a sequence of CIP headers of packets received by 828mk2, at 44.1kHz of sampling transmission frequency. quads CIP1 CIP2 488 0x020F04E8 0x8222FFFF 8 0x020F04F8 0x8222FFFF 488 0x020F0400 0x8222FFFF 488 0x020F0408 0x8222FFFF 8 0x020F04E8 0x8222FFFF 488 0x020F04F0 0x8222FFFF 488 0x020F04F8 0x8222FFFF The SID (source node ID), DBS (data block size), SPH (source packet header), FMT (format ID), FDF (format dependent field) and SYT (time stamp) fields are in IEC 61883-1. Especially, FMT is 0x02, FDF is 0x22 and SYT is 0xffff to define MOTU specific protocol. In an aspect of dbc field, the value represents accumulated number of data blocks included the packet. This is against IEC 61883-1, because according to the specification this value should be the number of data blocks already transferred. In ALSA IEC 61883-1/6 engine, this quirk is already supported by CIP_DBC_IS_END_EVENT flag, because Echo Audio Fireworks has. Each data block includes SPH as its first quadlet field, to represent its presentation time stamp. Actual value of SPH is compliant to IEC 61883-1; lower 25 bits of 32 bits width consists of 13 bits cycle count and 12 bits cycle offset. The rest of each data block consists of 24 bit chunks. All of PCM samples, MIDI messages, status and control messages are transferred by the chunks. This is similar to '24-bit * 4 Audio Pack' in IEC 61883-6. The position of each kind of data depends on generations of each model. The number of whole chunks in a data block is a multiple of 4, to consists of quadlet-aligned packets. This commit adds data block processing layer specific for the MOTU protocol. The remarkable point is the way to generate SPH header. Time stamps for each data blocks are generated by below calculation: * Using pre-computed table for the number of ticks per event * 44,1kHz: (557 + 123/441) * 48.0kHz: (512 + 0/441) * 88.2kHz: (278 + 282/441) * 96.0kHz: (256 + 0/441) * 176.4kHz: (139 + 141/441) * 192.0kHz: (128 + 0/441) * Accumulate the ticks and set the value to SPH for every events. * This way makes sense only for blocking transmission because this mode transfers fixed number or none of events. This calculation assumes that each data block has a PCM frame which is sampled according to event timing clock. Current packet streaming layer has the same assumption. Although this sequence works fine for MOTU FireWire series at sampling transmission frequency based on 48.0kHz, it is not enough at the frequency based on 44.1kHz. The units generate choppy noise every few seconds. 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.c292
-rw-r--r--sound/firewire/motu/motu.h13
3 files changed, 304 insertions, 3 deletions
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index d7819d57eadf..37391f5c623d 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -1,2 +1,2 @@
1snd-firewire-motu-objs := motu.o 1snd-firewire-motu-objs := motu.o amdtp-motu.o
2obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o 2obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
new file mode 100644
index 000000000000..11e44123ad65
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -0,0 +1,292 @@
1/*
2 * amdtp-motu.c - 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
9#include <linux/slab.h>
10#include <sound/pcm.h>
11#include "motu.h"
12
13#define CIP_FMT_MOTU 0x02
14#define MOTU_FDF_AM824 0x22
15
16struct amdtp_motu {
17 /* For timestamp processing. */
18 unsigned int quotient_ticks_per_event;
19 unsigned int remainder_ticks_per_event;
20 unsigned int next_ticks;
21 unsigned int next_accumulated;
22 unsigned int next_cycles;
23 unsigned int next_seconds;
24
25 unsigned int pcm_chunks;
26 unsigned int pcm_byte_offset;
27};
28
29int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
30 struct snd_motu_packet_format *formats)
31{
32 static const struct {
33 unsigned int quotient_ticks_per_event;
34 unsigned int remainder_ticks_per_event;
35 } params[] = {
36 [CIP_SFC_44100] = { 557, 123 },
37 [CIP_SFC_48000] = { 512, 0 },
38 [CIP_SFC_88200] = { 278, 282 },
39 [CIP_SFC_96000] = { 256, 0 },
40 [CIP_SFC_176400] = { 139, 141 },
41 [CIP_SFC_192000] = { 128, 0 },
42 };
43 struct amdtp_motu *p = s->protocol;
44 unsigned int pcm_chunks, data_chunks, data_block_quadlets;
45 unsigned int delay;
46 unsigned int mode;
47 int i, err;
48
49 if (amdtp_stream_running(s))
50 return -EBUSY;
51
52 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
53 if (snd_motu_clock_rates[i] == rate) {
54 mode = i >> 1;
55 break;
56 }
57 }
58 if (i == ARRAY_SIZE(snd_motu_clock_rates))
59 return -EINVAL;
60
61 pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
62 formats->differed_part_pcm_chunks[mode];
63 data_chunks = formats->msg_chunks + pcm_chunks;
64
65 /*
66 * Each data block includes SPH in its head. Data chunks follow with
67 * 3 byte alignment. Padding follows with zero to conform to quadlet
68 * alignment.
69 */
70 data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
71
72 err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
73 if (err < 0)
74 return err;
75
76 p->pcm_chunks = pcm_chunks;
77 p->pcm_byte_offset = formats->pcm_byte_offset;
78
79 /* IEEE 1394 bus requires. */
80 delay = 0x2e00;
81
82 /* For no-data or empty packets to adjust PCM sampling frequency. */
83 delay += 8000 * 3072 * s->syt_interval / rate;
84
85 p->next_seconds = 0;
86 p->next_cycles = delay / 3072;
87 p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
88 p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
89 p->next_ticks = delay % 3072;
90 p->next_accumulated = 0;
91
92 return 0;
93}
94
95static void read_pcm_s32(struct amdtp_stream *s,
96 struct snd_pcm_runtime *runtime,
97 __be32 *buffer, unsigned int data_blocks)
98{
99 struct amdtp_motu *p = s->protocol;
100 unsigned int channels, remaining_frames, i, c;
101 u8 *byte;
102 u32 *dst;
103
104 channels = p->pcm_chunks;
105 dst = (void *)runtime->dma_area +
106 frames_to_bytes(runtime, s->pcm_buffer_pointer);
107 remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
108
109 for (i = 0; i < data_blocks; ++i) {
110 byte = (u8 *)buffer + p->pcm_byte_offset;
111
112 for (c = 0; c < channels; ++c) {
113 *dst = (byte[0] << 24) | (byte[1] << 16) | byte[2];
114 byte += 3;
115 dst++;
116 }
117 buffer += s->data_block_quadlets;
118 if (--remaining_frames == 0)
119 dst = (void *)runtime->dma_area;
120 }
121}
122
123static void write_pcm_s32(struct amdtp_stream *s,
124 struct snd_pcm_runtime *runtime,
125 __be32 *buffer, unsigned int data_blocks)
126{
127 struct amdtp_motu *p = s->protocol;
128 unsigned int channels, remaining_frames, i, c;
129 u8 *byte;
130 const u32 *src;
131
132 channels = p->pcm_chunks;
133 src = (void *)runtime->dma_area +
134 frames_to_bytes(runtime, s->pcm_buffer_pointer);
135 remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
136
137 for (i = 0; i < data_blocks; ++i) {
138 byte = (u8 *)buffer + p->pcm_byte_offset;
139
140 for (c = 0; c < channels; ++c) {
141 byte[0] = (*src >> 24) & 0xff;
142 byte[1] = (*src >> 16) & 0xff;
143 byte[2] = (*src >> 8) & 0xff;
144 byte += 3;
145 src++;
146 }
147
148 buffer += s->data_block_quadlets;
149 if (--remaining_frames == 0)
150 src = (void *)runtime->dma_area;
151 }
152}
153
154static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
155 unsigned int data_blocks)
156{
157 struct amdtp_motu *p = s->protocol;
158 unsigned int channels, i, c;
159 u8 *byte;
160
161 channels = p->pcm_chunks;
162
163 for (i = 0; i < data_blocks; ++i) {
164 byte = (u8 *)buffer + p->pcm_byte_offset;
165
166 for (c = 0; c < channels; ++c) {
167 byte[0] = 0;
168 byte[1] = 0;
169 byte[2] = 0;
170 byte += 3;
171 }
172
173 buffer += s->data_block_quadlets;
174 }
175}
176
177int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
178 struct snd_pcm_runtime *runtime)
179{
180 int err;
181
182 /* TODO: how to set an constraint for exactly 24bit PCM sample? */
183 err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
184 if (err < 0)
185 return err;
186
187 return amdtp_stream_add_pcm_hw_constraints(s, runtime);
188}
189
190static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
191 __be32 *buffer, unsigned int data_blocks,
192 unsigned int *syt)
193{
194 struct snd_pcm_substream *pcm;
195
196 pcm = ACCESS_ONCE(s->pcm);
197 if (data_blocks > 0 && pcm)
198 read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
199
200 return data_blocks;
201}
202
203static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
204{
205 p->next_accumulated += p->remainder_ticks_per_event;
206 if (p->next_accumulated >= 441) {
207 p->next_accumulated -= 441;
208 p->next_ticks++;
209 }
210
211 p->next_ticks += p->quotient_ticks_per_event;
212 if (p->next_ticks >= 3072) {
213 p->next_ticks -= 3072;
214 p->next_cycles++;
215 }
216
217 if (p->next_cycles >= 8000) {
218 p->next_cycles -= 8000;
219 p->next_seconds++;
220 }
221
222 if (p->next_seconds >= 128)
223 p->next_seconds -= 128;
224}
225
226static void write_sph(struct amdtp_stream *s, __be32 *buffer,
227 unsigned int data_blocks)
228{
229 struct amdtp_motu *p = s->protocol;
230 unsigned int next_cycles;
231 unsigned int i;
232 u32 sph;
233
234 for (i = 0; i < data_blocks; i++) {
235 next_cycles = (s->start_cycle + p->next_cycles) % 8000;
236 sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
237 *buffer = cpu_to_be32(sph);
238
239 compute_next_elapse_from_start(p);
240
241 buffer += s->data_block_quadlets;
242 }
243}
244
245static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
246 __be32 *buffer, unsigned int data_blocks,
247 unsigned int *syt)
248{
249 struct snd_pcm_substream *pcm;
250
251 /* Not used. */
252 *syt = 0xffff;
253
254 /* TODO: how to interact control messages between userspace? */
255
256 pcm = ACCESS_ONCE(s->pcm);
257 if (pcm)
258 write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
259 else
260 write_pcm_silence(s, buffer, data_blocks);
261
262 write_sph(s, buffer, data_blocks);
263
264 return data_blocks;
265}
266
267int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
268 enum amdtp_stream_direction dir,
269 const struct snd_motu_protocol *const protocol)
270{
271 amdtp_stream_process_data_blocks_t process_data_blocks;
272 int fmt = CIP_FMT_MOTU;
273 int flags = CIP_BLOCKING;
274 int err;
275
276 if (dir == AMDTP_IN_STREAM) {
277 process_data_blocks = process_tx_data_blocks;
278 } else {
279 process_data_blocks = process_rx_data_blocks;
280 flags |= CIP_DBC_IS_END_EVENT;
281 }
282
283 err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
284 sizeof(struct amdtp_motu));
285 if (err < 0)
286 return err;
287
288 s->sph = 1;
289 s->fdf = MOTU_FDF_AM824;
290
291 return 0;
292}
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index cb6b57353cc1..cd1b3dd3e371 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -19,12 +19,12 @@
19 19
20#include <sound/control.h> 20#include <sound/control.h>
21#include <sound/core.h> 21#include <sound/core.h>
22#include <sound/pcm.h>
22 23
23#include "../lib.h" 24#include "../lib.h"
25#include "../amdtp-stream.h"
24 26
25struct snd_motu_packet_format { 27struct snd_motu_packet_format {
26 unsigned char midi_flag_offset;
27 unsigned char midi_byte_offset;
28 unsigned char pcm_byte_offset; 28 unsigned char pcm_byte_offset;
29 29
30 unsigned char msg_chunks; 30 unsigned char msg_chunks;
@@ -46,6 +46,8 @@ struct snd_motu {
46 /* For packet streaming */ 46 /* For packet streaming */
47 struct snd_motu_packet_format tx_packet_formats; 47 struct snd_motu_packet_format tx_packet_formats;
48 struct snd_motu_packet_format rx_packet_formats; 48 struct snd_motu_packet_format rx_packet_formats;
49 struct amdtp_stream tx_stream;
50 struct amdtp_stream rx_stream;
49}; 51};
50 52
51enum snd_motu_spec_flags { 53enum snd_motu_spec_flags {
@@ -97,4 +99,11 @@ struct snd_motu_spec {
97 const struct snd_motu_protocol *const protocol; 99 const struct snd_motu_protocol *const protocol;
98}; 100};
99 101
102int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
103 enum amdtp_stream_direction dir,
104 const struct snd_motu_protocol *const protocol);
105int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
106 struct snd_motu_packet_format *formats);
107int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
108 struct snd_pcm_runtime *runtime);
100#endif 109#endif