blob: d8b42d28304a9756c70a0086ad6820f0ada23e2c [file] [log] [blame]
Thomas Gleixnerda607e12019-05-29 16:57:59 -07001// SPDX-License-Identifier: GPL-2.0-only
Takashi Sakamoto5992e302017-03-22 21:30:28 +09002/*
3 * motu-protocol-v3.c - a part of driver for MOTU FireWire series
4 *
5 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Takashi Sakamoto5992e302017-03-22 21:30:28 +09006 */
7
8#include <linux/delay.h>
9#include "motu.h"
10
11#define V3_CLOCK_STATUS_OFFSET 0x0b14
12#define V3_FETCH_PCM_FRAMES 0x02000000
13#define V3_CLOCK_RATE_MASK 0x0000ff00
14#define V3_CLOCK_RATE_SHIFT 8
15#define V3_CLOCK_SOURCE_MASK 0x000000ff
Takashi Sakamoto5992e302017-03-22 21:30:28 +090016
17#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
18#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
19#define V3_ENABLE_OPT_IN_IFACE_B 0x00000002
20#define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100
21#define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200
22#define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000
23#define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000
24#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
25#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
26
Takashi Sakamotoff222b72020-05-19 20:16:31 +090027int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
28 unsigned int *rate)
Takashi Sakamoto5992e302017-03-22 21:30:28 +090029{
30 __be32 reg;
31 u32 data;
32 int err;
33
34 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
35 sizeof(reg));
36 if (err < 0)
37 return err;
38 data = be32_to_cpu(reg);
39
40 data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
41 if (data >= ARRAY_SIZE(snd_motu_clock_rates))
42 return -EIO;
43
44 *rate = snd_motu_clock_rates[data];
45
46 return 0;
47}
48
Takashi Sakamotoff222b72020-05-19 20:16:31 +090049int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
50 unsigned int rate)
Takashi Sakamoto5992e302017-03-22 21:30:28 +090051{
52 __be32 reg;
53 u32 data;
54 bool need_to_wait;
55 int i, err;
56
57 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
58 if (snd_motu_clock_rates[i] == rate)
59 break;
60 }
61 if (i == ARRAY_SIZE(snd_motu_clock_rates))
62 return -EINVAL;
63
64 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
65 sizeof(reg));
66 if (err < 0)
67 return err;
68 data = be32_to_cpu(reg);
69
70 data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
71 data |= i << V3_CLOCK_RATE_SHIFT;
72
73 need_to_wait = data != be32_to_cpu(reg);
74
75 reg = cpu_to_be32(data);
76 err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
77 sizeof(reg));
78 if (err < 0)
79 return err;
80
81 if (need_to_wait) {
82 /* Cost expensive. */
83 if (msleep_interruptible(4000) > 0)
84 return -EINTR;
85 }
86
87 return 0;
88}
89
Takashi Sakamotoff222b72020-05-19 20:16:31 +090090int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
91 enum snd_motu_clock_source *src)
Takashi Sakamoto5992e302017-03-22 21:30:28 +090092{
93 __be32 reg;
94 u32 data;
95 unsigned int val;
96 int err;
97
98 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
99 sizeof(reg));
100 if (err < 0)
101 return err;
102 data = be32_to_cpu(reg);
103
Takashi Sakamoto5b335042017-04-03 21:13:50 +0900104 val = data & V3_CLOCK_SOURCE_MASK;
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900105 if (val == 0x00) {
106 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
107 } else if (val == 0x01) {
108 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
Takashi Sakamoto3f58f002019-10-30 17:06:41 +0900109 } else if (val == 0x02) {
110 *src = SND_MOTU_CLOCK_SOURCE_SPH;
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900111 } else if (val == 0x10) {
112 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
113 } else if (val == 0x18 || val == 0x19) {
114 err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
115 &reg, sizeof(reg));
116 if (err < 0)
117 return err;
118 data = be32_to_cpu(reg);
119
120 if (val == 0x18) {
121 if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
122 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
123 else
124 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
125 } else {
126 if (data & V3_NO_ADAT_OPT_IN_IFACE_B)
127 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
128 else
129 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
130 }
131 } else {
132 *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
133 }
134
135 return 0;
136}
137
Takashi Sakamotoff222b72020-05-19 20:16:31 +0900138int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
139 bool enable)
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900140{
141 __be32 reg;
142 u32 data;
143 int err;
144
145 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
146 sizeof(reg));
147 if (err < 0)
148 return 0;
149 data = be32_to_cpu(reg);
150
151 if (enable)
152 data |= V3_FETCH_PCM_FRAMES;
153 else
154 data &= ~V3_FETCH_PCM_FRAMES;
155
156 reg = cpu_to_be32(data);
157 return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
158 sizeof(reg));
159}
160
161static void calculate_fixed_part(struct snd_motu_packet_format *formats,
162 enum amdtp_stream_direction dir,
163 enum snd_motu_spec_flags flags,
164 unsigned char analog_ports)
165{
166 unsigned char pcm_chunks[3] = {0, 0, 0};
167
168 formats->msg_chunks = 2;
169
170 pcm_chunks[0] = analog_ports;
171 pcm_chunks[1] = analog_ports;
172 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
173 pcm_chunks[2] = analog_ports;
174
175 if (dir == AMDTP_IN_STREAM) {
176 if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
177 pcm_chunks[0] += 2;
178 pcm_chunks[1] += 2;
179 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
180 pcm_chunks[2] += 2;
181 }
182
183 if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
184 pcm_chunks[0] += 2;
185 pcm_chunks[1] += 2;
186 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
187 pcm_chunks[2] += 2;
188 }
189
190 if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
191 pcm_chunks[0] += 2;
192 pcm_chunks[1] += 2;
193 }
194 } else {
Takashi Sakamoto2644df62019-10-30 17:06:39 +0900195 if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
Takashi Sakamoto81720c6d2018-06-18 21:07:52 +0900196 pcm_chunks[0] += 2;
197 pcm_chunks[1] += 2;
198 }
199
200 // Packets to v3 units include 2 chunks for phone 1/2, except
201 // for 176.4/192.0 kHz.
202 pcm_chunks[0] += 2;
203 pcm_chunks[1] += 2;
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900204 }
205
Takashi Sakamoto06ac0b62018-06-18 21:07:53 +0900206 if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
207 pcm_chunks[0] += 2;
208 pcm_chunks[1] += 2;
209 }
210
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900211 /*
212 * At least, packets have two data chunks for S/PDIF on coaxial
213 * interface.
214 */
215 pcm_chunks[0] += 2;
216 pcm_chunks[1] += 2;
217
218 /*
219 * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
220 * a result, this part can includes empty data chunks.
221 */
222 formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
223 formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
224 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
225 formats->fixed_part_pcm_chunks[2] =
226 round_up(2 + pcm_chunks[2], 4) - 2;
227}
228
229static void calculate_differed_part(struct snd_motu_packet_format *formats,
230 enum snd_motu_spec_flags flags, u32 data,
231 u32 a_enable_mask, u32 a_no_adat_mask,
232 u32 b_enable_mask, u32 b_no_adat_mask)
233{
234 unsigned char pcm_chunks[3] = {0, 0, 0};
235 int i;
236
237 if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
238 if (data & a_no_adat_mask) {
239 /*
240 * Additional two data chunks for S/PDIF on optical
241 * interface A. This includes empty data chunks.
242 */
243 pcm_chunks[0] += 4;
244 pcm_chunks[1] += 4;
245 } else {
246 /*
247 * Additional data chunks for ADAT on optical interface
248 * A.
249 */
250 pcm_chunks[0] += 8;
251 pcm_chunks[1] += 4;
252 }
253 }
254
255 if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
256 if (data & b_no_adat_mask) {
257 /*
258 * Additional two data chunks for S/PDIF on optical
259 * interface B. This includes empty data chunks.
260 */
261 pcm_chunks[0] += 4;
262 pcm_chunks[1] += 4;
263 } else {
264 /*
265 * Additional data chunks for ADAT on optical interface
266 * B.
267 */
268 pcm_chunks[0] += 8;
269 pcm_chunks[1] += 4;
270 }
271 }
272
273 for (i = 0; i < 3; ++i) {
274 if (pcm_chunks[i] > 0)
275 pcm_chunks[i] = round_up(pcm_chunks[i], 4);
276
277 formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
278 }
279}
280
Takashi Sakamotoff222b72020-05-19 20:16:31 +0900281int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900282{
283 __be32 reg;
284 u32 data;
285 int err;
286
287 err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
288 sizeof(reg));
289 if (err < 0)
290 return err;
291 data = be32_to_cpu(reg);
292
293 calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
294 motu->spec->flags, motu->spec->analog_in_ports);
295 calculate_differed_part(&motu->tx_packet_formats,
296 motu->spec->flags, data,
297 V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
298 V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
299
300 calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
301 motu->spec->flags, motu->spec->analog_out_ports);
302 calculate_differed_part(&motu->rx_packet_formats,
303 motu->spec->flags, data,
304 V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
305 V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
306
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900307 motu->tx_packet_formats.pcm_byte_offset = 10;
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900308 motu->rx_packet_formats.pcm_byte_offset = 10;
309
310 return 0;
311}
312
Takashi Sakamoto61d79c72020-05-19 20:16:30 +0900313static const struct snd_motu_protocol snd_motu_protocol_v3 = {
Takashi Sakamoto5992e302017-03-22 21:30:28 +0900314};
Takashi Sakamotoc806a0e2020-05-19 20:16:29 +0900315
316const struct snd_motu_spec snd_motu_spec_828mk3 = {
317 .name = "828mk3",
Takashi Sakamoto61d79c72020-05-19 20:16:30 +0900318 .protocol_version = SND_MOTU_PROTOCOL_V3,
Takashi Sakamotoc806a0e2020-05-19 20:16:29 +0900319 .protocol = &snd_motu_protocol_v3,
320 .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
321 SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
322 SND_MOTU_SPEC_TX_MICINST_CHUNK |
323 SND_MOTU_SPEC_TX_RETURN_CHUNK |
324 SND_MOTU_SPEC_TX_REVERB_CHUNK |
325 SND_MOTU_SPEC_RX_SEPARATED_MAIN |
326 SND_MOTU_SPEC_HAS_OPT_IFACE_A |
327 SND_MOTU_SPEC_HAS_OPT_IFACE_B |
328 SND_MOTU_SPEC_RX_MIDI_3RD_Q |
329 SND_MOTU_SPEC_TX_MIDI_3RD_Q,
330
331 .analog_in_ports = 8,
332 .analog_out_ports = 8,
333};
334
335const struct snd_motu_spec snd_motu_spec_audio_express = {
336 .name = "AudioExpress",
Takashi Sakamoto61d79c72020-05-19 20:16:30 +0900337 .protocol_version = SND_MOTU_PROTOCOL_V3,
Takashi Sakamotoc806a0e2020-05-19 20:16:29 +0900338 .protocol = &snd_motu_protocol_v3,
339 .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
340 SND_MOTU_SPEC_TX_MICINST_CHUNK |
341 SND_MOTU_SPEC_TX_RETURN_CHUNK |
342 SND_MOTU_SPEC_RX_SEPARATED_MAIN |
343 SND_MOTU_SPEC_RX_MIDI_2ND_Q |
344 SND_MOTU_SPEC_TX_MIDI_3RD_Q,
345 .analog_in_ports = 2,
346 .analog_out_ports = 4,
347};
348
349const struct snd_motu_spec snd_motu_spec_4pre = {
350 .name = "4pre",
Takashi Sakamoto61d79c72020-05-19 20:16:30 +0900351 .protocol_version = SND_MOTU_PROTOCOL_V3,
Takashi Sakamotoc806a0e2020-05-19 20:16:29 +0900352 .protocol = &snd_motu_protocol_v3,
353 .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
354 SND_MOTU_SPEC_TX_MICINST_CHUNK |
355 SND_MOTU_SPEC_TX_RETURN_CHUNK |
356 SND_MOTU_SPEC_RX_SEPARATED_MAIN,
357 .analog_in_ports = 2,
358 .analog_out_ports = 2,
359};