Commit | Line | Data |
---|---|---|
76fdb3a9 TS |
1 | /* |
2 | * ff-protocol-ff400.c - a part of driver for RME Fireface series | |
3 | * | |
4 | * Copyright (c) 2015-2017 Takashi Sakamoto | |
5 | * | |
6 | * Licensed under the terms of the GNU General Public License, version 2. | |
7 | */ | |
8 | ||
9 | #include <linux/delay.h> | |
10 | #include "ff.h" | |
11 | ||
12 | #define FF400_STF 0x000080100500ull | |
13 | #define FF400_RX_PACKET_FORMAT 0x000080100504ull | |
14 | #define FF400_ISOC_COMM_START 0x000080100508ull | |
15 | #define FF400_TX_PACKET_FORMAT 0x00008010050cull | |
16 | #define FF400_ISOC_COMM_STOP 0x000080100510ull | |
17 | #define FF400_SYNC_STATUS 0x0000801c0000ull | |
18 | #define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */ | |
19 | #define FF400_CLOCK_CONFIG 0x0000801c0004ull | |
20 | ||
21 | #define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull | |
22 | #define FF400_MIDI_RX_PORT_0 0x000080180000ull | |
23 | #define FF400_MIDI_RX_PORT_1 0x000080190000ull | |
24 | ||
25 | static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate, | |
26 | enum snd_ff_clock_src *src) | |
27 | { | |
28 | __le32 reg; | |
29 | u32 data; | |
30 | int err; | |
31 | ||
32 | err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, | |
33 | FF400_SYNC_STATUS, ®, sizeof(reg), 0); | |
34 | if (err < 0) | |
35 | return err; | |
36 | data = le32_to_cpu(reg); | |
37 | ||
38 | /* Calculate sampling rate. */ | |
39 | switch ((data >> 1) & 0x03) { | |
40 | case 0x01: | |
41 | *rate = 32000; | |
42 | break; | |
43 | case 0x00: | |
44 | *rate = 44100; | |
45 | break; | |
46 | case 0x03: | |
47 | *rate = 48000; | |
48 | break; | |
49 | case 0x02: | |
50 | default: | |
51 | return -EIO; | |
52 | } | |
53 | ||
54 | if (data & 0x08) | |
55 | *rate *= 2; | |
56 | else if (data & 0x10) | |
57 | *rate *= 4; | |
58 | ||
59 | /* Calculate source of clock. */ | |
60 | if (data & 0x01) { | |
61 | *src = SND_FF_CLOCK_SRC_INTERNAL; | |
62 | } else { | |
63 | /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */ | |
64 | switch ((data >> 10) & 0x07) { | |
65 | case 0x03: | |
66 | *src = SND_FF_CLOCK_SRC_SPDIF; | |
67 | break; | |
68 | case 0x04: | |
69 | *src = SND_FF_CLOCK_SRC_WORD; | |
70 | break; | |
71 | case 0x05: | |
72 | *src = SND_FF_CLOCK_SRC_LTC; | |
73 | break; | |
74 | case 0x00: | |
75 | default: | |
76 | *src = SND_FF_CLOCK_SRC_ADAT; | |
77 | break; | |
78 | } | |
79 | } | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) | |
85 | { | |
86 | __le32 reg; | |
87 | int i, err; | |
88 | ||
89 | /* Check whether the given value is supported or not. */ | |
90 | for (i = 0; i < CIP_SFC_COUNT; i++) { | |
91 | if (amdtp_rate_table[i] == rate) | |
92 | break; | |
93 | } | |
94 | if (i == CIP_SFC_COUNT) | |
95 | return -EINVAL; | |
96 | ||
97 | /* Set the number of data blocks transferred in a second. */ | |
98 | reg = cpu_to_le32(rate); | |
99 | err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, | |
100 | FF400_STF, ®, sizeof(reg), 0); | |
101 | if (err < 0) | |
102 | return err; | |
103 | ||
104 | msleep(100); | |
105 | ||
106 | /* | |
107 | * Set isochronous channel and the number of quadlets of received | |
108 | * packets. | |
109 | */ | |
110 | reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | | |
111 | ff->rx_resources.channel); | |
112 | err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, | |
113 | FF400_RX_PACKET_FORMAT, ®, sizeof(reg), 0); | |
114 | if (err < 0) | |
115 | return err; | |
116 | ||
117 | /* | |
118 | * Set isochronous channel and the number of quadlets of transmitted | |
119 | * packet. | |
120 | */ | |
121 | /* TODO: investigate the purpose of this 0x80. */ | |
122 | reg = cpu_to_le32((0x80 << 24) | | |
123 | (ff->tx_resources.channel << 5) | | |
124 | (ff->tx_stream.data_block_quadlets)); | |
125 | err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, | |
126 | FF400_TX_PACKET_FORMAT, ®, sizeof(reg), 0); | |
127 | if (err < 0) | |
128 | return err; | |
129 | ||
130 | /* Allow to transmit packets. */ | |
131 | reg = cpu_to_le32(0x00000001); | |
132 | return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, | |
133 | FF400_ISOC_COMM_START, ®, sizeof(reg), 0); | |
134 | } | |
135 | ||
136 | static void ff400_finish_session(struct snd_ff *ff) | |
137 | { | |
138 | __le32 reg; | |
139 | ||
140 | reg = cpu_to_le32(0x80000000); | |
141 | snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, | |
142 | FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); | |
143 | } | |
144 | ||
145 | static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable) | |
146 | { | |
147 | __le32 *reg; | |
148 | int i; | |
36f3a6e0 | 149 | int err; |
76fdb3a9 | 150 | |
6396bb22 | 151 | reg = kcalloc(18, sizeof(__le32), GFP_KERNEL); |
76fdb3a9 TS |
152 | if (reg == NULL) |
153 | return -ENOMEM; | |
154 | ||
155 | if (enable) { | |
156 | /* | |
157 | * Each quadlet is corresponding to data channels in a data | |
158 | * blocks in reverse order. Precisely, quadlets for available | |
159 | * data channels should be enabled. Here, I take second best | |
160 | * to fetch PCM frames from all of data channels regardless of | |
161 | * stf. | |
162 | */ | |
163 | for (i = 0; i < 18; ++i) | |
164 | reg[i] = cpu_to_le32(0x00000001); | |
165 | } | |
166 | ||
36f3a6e0 TS |
167 | err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST, |
168 | FF400_FETCH_PCM_FRAMES, reg, | |
169 | sizeof(__le32) * 18, 0); | |
170 | kfree(reg); | |
171 | return err; | |
76fdb3a9 TS |
172 | } |
173 | ||
174 | static void ff400_dump_sync_status(struct snd_ff *ff, | |
175 | struct snd_info_buffer *buffer) | |
176 | { | |
177 | __le32 reg; | |
178 | u32 data; | |
179 | int err; | |
180 | ||
181 | err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST, | |
182 | FF400_SYNC_STATUS, ®, sizeof(reg), 0); | |
183 | if (err < 0) | |
184 | return; | |
185 | ||
186 | data = le32_to_cpu(reg); | |
187 | ||
188 | snd_iprintf(buffer, "External source detection:\n"); | |
189 | ||
190 | snd_iprintf(buffer, "Word Clock:"); | |
191 | if ((data >> 24) & 0x20) { | |
192 | if ((data >> 24) & 0x40) | |
193 | snd_iprintf(buffer, "sync\n"); | |
194 | else | |
195 | snd_iprintf(buffer, "lock\n"); | |
196 | } else { | |
197 | snd_iprintf(buffer, "none\n"); | |
198 | } | |
199 | ||
200 | snd_iprintf(buffer, "S/PDIF:"); | |
201 | if ((data >> 16) & 0x10) { | |
202 | if ((data >> 16) & 0x04) | |
203 | snd_iprintf(buffer, "sync\n"); | |
204 | else | |
205 | snd_iprintf(buffer, "lock\n"); | |
206 | } else { | |
207 | snd_iprintf(buffer, "none\n"); | |
208 | } | |
209 | ||
210 | snd_iprintf(buffer, "ADAT:"); | |
211 | if ((data >> 8) & 0x04) { | |
212 | if ((data >> 8) & 0x10) | |
213 | snd_iprintf(buffer, "sync\n"); | |
214 | else | |
215 | snd_iprintf(buffer, "lock\n"); | |
216 | } else { | |
217 | snd_iprintf(buffer, "none\n"); | |
218 | } | |
219 | ||
220 | snd_iprintf(buffer, "\nUsed external source:\n"); | |
221 | ||
222 | if (((data >> 22) & 0x07) == 0x07) { | |
223 | snd_iprintf(buffer, "None\n"); | |
224 | } else { | |
225 | switch ((data >> 22) & 0x07) { | |
226 | case 0x00: | |
227 | snd_iprintf(buffer, "ADAT:"); | |
228 | break; | |
229 | case 0x03: | |
230 | snd_iprintf(buffer, "S/PDIF:"); | |
231 | break; | |
232 | case 0x04: | |
233 | snd_iprintf(buffer, "Word:"); | |
234 | break; | |
235 | case 0x07: | |
236 | snd_iprintf(buffer, "Nothing:"); | |
237 | break; | |
238 | case 0x01: | |
239 | case 0x02: | |
240 | case 0x05: | |
241 | case 0x06: | |
242 | default: | |
243 | snd_iprintf(buffer, "unknown:"); | |
244 | break; | |
245 | } | |
246 | ||
247 | if ((data >> 25) & 0x07) { | |
248 | switch ((data >> 25) & 0x07) { | |
249 | case 0x01: | |
250 | snd_iprintf(buffer, "32000\n"); | |
251 | break; | |
252 | case 0x02: | |
253 | snd_iprintf(buffer, "44100\n"); | |
254 | break; | |
255 | case 0x03: | |
256 | snd_iprintf(buffer, "48000\n"); | |
257 | break; | |
258 | case 0x04: | |
259 | snd_iprintf(buffer, "64000\n"); | |
260 | break; | |
261 | case 0x05: | |
262 | snd_iprintf(buffer, "88200\n"); | |
263 | break; | |
264 | case 0x06: | |
265 | snd_iprintf(buffer, "96000\n"); | |
266 | break; | |
267 | case 0x07: | |
268 | snd_iprintf(buffer, "128000\n"); | |
269 | break; | |
270 | case 0x08: | |
271 | snd_iprintf(buffer, "176400\n"); | |
272 | break; | |
273 | case 0x09: | |
274 | snd_iprintf(buffer, "192000\n"); | |
275 | break; | |
276 | case 0x00: | |
277 | snd_iprintf(buffer, "unknown\n"); | |
278 | break; | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 | snd_iprintf(buffer, "Multiplied:"); | |
284 | snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250); | |
285 | } | |
286 | ||
287 | static void ff400_dump_clock_config(struct snd_ff *ff, | |
288 | struct snd_info_buffer *buffer) | |
289 | { | |
290 | __le32 reg; | |
291 | u32 data; | |
292 | unsigned int rate; | |
293 | const char *src; | |
294 | int err; | |
295 | ||
296 | err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST, | |
297 | FF400_CLOCK_CONFIG, ®, sizeof(reg), 0); | |
298 | if (err < 0) | |
299 | return; | |
300 | ||
301 | data = le32_to_cpu(reg); | |
302 | ||
303 | snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", | |
304 | (data & 0x20) ? "Professional" : "Consumer", | |
305 | (data & 0x40) ? "on" : "off"); | |
306 | ||
307 | snd_iprintf(buffer, "Optical output interface format: %s\n", | |
308 | ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT"); | |
309 | ||
310 | snd_iprintf(buffer, "Word output single speed: %s\n", | |
311 | ((data >> 8) & 0x20) ? "on" : "off"); | |
312 | ||
313 | snd_iprintf(buffer, "S/PDIF input interface: %s\n", | |
314 | ((data >> 8) & 0x02) ? "Optical" : "Coaxial"); | |
315 | ||
316 | switch ((data >> 1) & 0x03) { | |
317 | case 0x01: | |
318 | rate = 32000; | |
319 | break; | |
320 | case 0x00: | |
321 | rate = 44100; | |
322 | break; | |
323 | case 0x03: | |
324 | rate = 48000; | |
325 | break; | |
326 | case 0x02: | |
327 | default: | |
328 | return; | |
329 | } | |
330 | ||
331 | if (data & 0x08) | |
332 | rate *= 2; | |
333 | else if (data & 0x10) | |
334 | rate *= 4; | |
335 | ||
336 | snd_iprintf(buffer, "Sampling rate: %d\n", rate); | |
337 | ||
338 | if (data & 0x01) { | |
339 | src = "Internal"; | |
340 | } else { | |
341 | switch ((data >> 10) & 0x07) { | |
342 | case 0x00: | |
343 | src = "ADAT"; | |
344 | break; | |
345 | case 0x03: | |
346 | src = "S/PDIF"; | |
347 | break; | |
348 | case 0x04: | |
349 | src = "Word"; | |
350 | break; | |
351 | case 0x05: | |
352 | src = "LTC"; | |
353 | break; | |
354 | default: | |
355 | return; | |
356 | } | |
357 | } | |
358 | ||
359 | snd_iprintf(buffer, "Sync to clock source: %s\n", src); | |
360 | } | |
361 | ||
782fbec7 | 362 | const struct snd_ff_protocol snd_ff_protocol_ff400 = { |
76fdb3a9 TS |
363 | .get_clock = ff400_get_clock, |
364 | .begin_session = ff400_begin_session, | |
365 | .finish_session = ff400_finish_session, | |
366 | .switch_fetching_mode = ff400_switch_fetching_mode, | |
367 | ||
368 | .dump_sync_status = ff400_dump_sync_status, | |
369 | .dump_clock_config = ff400_dump_clock_config, | |
370 | ||
371 | .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR, | |
372 | .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0, | |
373 | .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1, | |
374 | }; |