Commit | Line | Data |
---|---|---|
da607e19 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5992e300 TS |
2 | /* |
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> | |
5992e300 TS |
6 | */ |
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 | |
7203233e TS |
16 | #define V3_CLOCK_SRC_INTERNAL 0x00 |
17 | #define V3_CLOCK_SRC_WORD_ON_BNC 0x01 | |
18 | #define V3_CLOCK_SRC_SPH 0x02 | |
bf868be7 | 19 | #define V3_CLOCK_SRC_AESEBU_ON_XLR 0x08 |
7203233e TS |
20 | #define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10 |
21 | #define V3_CLOCK_SRC_OPT_IFACE_A 0x18 | |
22 | #define V3_CLOCK_SRC_OPT_IFACE_B 0x19 | |
5992e300 TS |
23 | |
24 | #define V3_OPT_IFACE_MODE_OFFSET 0x0c94 | |
25 | #define V3_ENABLE_OPT_IN_IFACE_A 0x00000001 | |
26 | #define V3_ENABLE_OPT_IN_IFACE_B 0x00000002 | |
27 | #define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100 | |
28 | #define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200 | |
29 | #define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000 | |
30 | #define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000 | |
31 | #define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000 | |
32 | #define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000 | |
33 | ||
67539867 TS |
34 | #define V3_MSG_FLAG_CLK_CHANGED 0x00000002 |
35 | #define V3_CLK_WAIT_MSEC 4000 | |
36 | ||
ff222b7e TS |
37 | int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu, |
38 | unsigned int *rate) | |
5992e300 TS |
39 | { |
40 | __be32 reg; | |
41 | u32 data; | |
42 | int err; | |
43 | ||
44 | err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, | |
45 | sizeof(reg)); | |
46 | if (err < 0) | |
47 | return err; | |
48 | data = be32_to_cpu(reg); | |
49 | ||
50 | data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT; | |
51 | if (data >= ARRAY_SIZE(snd_motu_clock_rates)) | |
52 | return -EIO; | |
53 | ||
54 | *rate = snd_motu_clock_rates[data]; | |
55 | ||
56 | return 0; | |
57 | } | |
58 | ||
ff222b7e TS |
59 | int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu, |
60 | unsigned int rate) | |
5992e300 TS |
61 | { |
62 | __be32 reg; | |
63 | u32 data; | |
64 | bool need_to_wait; | |
65 | int i, err; | |
66 | ||
67 | for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { | |
68 | if (snd_motu_clock_rates[i] == rate) | |
69 | break; | |
70 | } | |
71 | if (i == ARRAY_SIZE(snd_motu_clock_rates)) | |
72 | return -EINVAL; | |
73 | ||
74 | err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, | |
75 | sizeof(reg)); | |
76 | if (err < 0) | |
77 | return err; | |
78 | data = be32_to_cpu(reg); | |
79 | ||
80 | data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES); | |
81 | data |= i << V3_CLOCK_RATE_SHIFT; | |
82 | ||
83 | need_to_wait = data != be32_to_cpu(reg); | |
84 | ||
85 | reg = cpu_to_be32(data); | |
86 | err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®, | |
87 | sizeof(reg)); | |
88 | if (err < 0) | |
89 | return err; | |
90 | ||
91 | if (need_to_wait) { | |
67539867 TS |
92 | int result; |
93 | ||
94 | motu->msg = 0; | |
95 | result = wait_event_interruptible_timeout(motu->hwdep_wait, | |
96 | motu->msg & V3_MSG_FLAG_CLK_CHANGED, | |
97 | msecs_to_jiffies(V3_CLK_WAIT_MSEC)); | |
98 | if (result < 0) | |
99 | return result; | |
100 | if (result == 0) | |
101 | return -ETIMEDOUT; | |
5992e300 TS |
102 | } |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
ef8f14ad TS |
107 | int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, |
108 | enum snd_motu_clock_source *src) | |
5992e300 | 109 | { |
ef8f14ad TS |
110 | __be32 reg; |
111 | u32 data; | |
112 | int err; | |
113 | ||
114 | err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, | |
115 | sizeof(reg)); | |
116 | if (err < 0) | |
117 | return err; | |
118 | data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK; | |
119 | ||
7b47c0d7 | 120 | switch (data) { |
7203233e | 121 | case V3_CLOCK_SRC_INTERNAL: |
5992e300 | 122 | *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; |
7b47c0d7 | 123 | break; |
7203233e | 124 | case V3_CLOCK_SRC_WORD_ON_BNC: |
5992e300 | 125 | *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; |
7b47c0d7 | 126 | break; |
7203233e | 127 | case V3_CLOCK_SRC_SPH: |
3f58f004 | 128 | *src = SND_MOTU_CLOCK_SOURCE_SPH; |
7b47c0d7 | 129 | break; |
bf868be7 TS |
130 | case V3_CLOCK_SRC_AESEBU_ON_XLR: |
131 | *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; | |
132 | break; | |
7203233e | 133 | case V3_CLOCK_SRC_SPDIF_ON_COAX: |
5992e300 | 134 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; |
7b47c0d7 | 135 | break; |
7203233e TS |
136 | case V3_CLOCK_SRC_OPT_IFACE_A: |
137 | case V3_CLOCK_SRC_OPT_IFACE_B: | |
7b47c0d7 TS |
138 | { |
139 | __be32 reg; | |
140 | u32 options; | |
7b47c0d7 TS |
141 | |
142 | err = snd_motu_transaction_read(motu, | |
143 | V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg)); | |
5992e300 TS |
144 | if (err < 0) |
145 | return err; | |
7b47c0d7 | 146 | options = be32_to_cpu(reg); |
5992e300 | 147 | |
7203233e | 148 | if (data == V3_CLOCK_SRC_OPT_IFACE_A) { |
7b47c0d7 | 149 | if (options & V3_NO_ADAT_OPT_IN_IFACE_A) |
5992e300 TS |
150 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A; |
151 | else | |
152 | *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A; | |
153 | } else { | |
7b47c0d7 | 154 | if (options & V3_NO_ADAT_OPT_IN_IFACE_B) |
5992e300 TS |
155 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B; |
156 | else | |
157 | *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B; | |
158 | } | |
7b47c0d7 TS |
159 | break; |
160 | } | |
161 | default: | |
5992e300 | 162 | *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; |
7b47c0d7 | 163 | break; |
5992e300 TS |
164 | } |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
ff222b7e TS |
169 | int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, |
170 | bool enable) | |
5992e300 TS |
171 | { |
172 | __be32 reg; | |
173 | u32 data; | |
174 | int err; | |
175 | ||
176 | err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, | |
177 | sizeof(reg)); | |
178 | if (err < 0) | |
179 | return 0; | |
180 | data = be32_to_cpu(reg); | |
181 | ||
182 | if (enable) | |
183 | data |= V3_FETCH_PCM_FRAMES; | |
184 | else | |
185 | data &= ~V3_FETCH_PCM_FRAMES; | |
186 | ||
187 | reg = cpu_to_be32(data); | |
188 | return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®, | |
189 | sizeof(reg)); | |
190 | } | |
191 | ||
411ac298 | 192 | static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data) |
0090c1c5 TS |
193 | { |
194 | if (data & V3_ENABLE_OPT_IN_IFACE_A) { | |
195 | if (data & V3_NO_ADAT_OPT_IN_IFACE_A) { | |
196 | motu->tx_packet_formats.pcm_chunks[0] += 4; | |
197 | motu->tx_packet_formats.pcm_chunks[1] += 4; | |
198 | } else { | |
199 | motu->tx_packet_formats.pcm_chunks[0] += 8; | |
200 | motu->tx_packet_formats.pcm_chunks[1] += 4; | |
201 | } | |
202 | } | |
203 | ||
204 | if (data & V3_ENABLE_OPT_IN_IFACE_B) { | |
205 | if (data & V3_NO_ADAT_OPT_IN_IFACE_B) { | |
206 | motu->tx_packet_formats.pcm_chunks[0] += 4; | |
207 | motu->tx_packet_formats.pcm_chunks[1] += 4; | |
208 | } else { | |
209 | motu->tx_packet_formats.pcm_chunks[0] += 8; | |
210 | motu->tx_packet_formats.pcm_chunks[1] += 4; | |
211 | } | |
212 | } | |
213 | ||
214 | if (data & V3_ENABLE_OPT_OUT_IFACE_A) { | |
215 | if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) { | |
216 | motu->rx_packet_formats.pcm_chunks[0] += 4; | |
217 | motu->rx_packet_formats.pcm_chunks[1] += 4; | |
218 | } else { | |
219 | motu->rx_packet_formats.pcm_chunks[0] += 8; | |
220 | motu->rx_packet_formats.pcm_chunks[1] += 4; | |
221 | } | |
222 | } | |
223 | ||
224 | if (data & V3_ENABLE_OPT_OUT_IFACE_B) { | |
225 | if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) { | |
226 | motu->rx_packet_formats.pcm_chunks[0] += 4; | |
227 | motu->rx_packet_formats.pcm_chunks[1] += 4; | |
228 | } else { | |
229 | motu->rx_packet_formats.pcm_chunks[0] += 8; | |
230 | motu->rx_packet_formats.pcm_chunks[1] += 4; | |
231 | } | |
232 | } | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
ff222b7e | 237 | int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) |
5992e300 TS |
238 | { |
239 | __be32 reg; | |
240 | u32 data; | |
241 | int err; | |
242 | ||
0090c1c5 TS |
243 | motu->tx_packet_formats.pcm_byte_offset = 10; |
244 | motu->rx_packet_formats.pcm_byte_offset = 10; | |
245 | ||
246 | motu->tx_packet_formats.msg_chunks = 2; | |
247 | motu->rx_packet_formats.msg_chunks = 2; | |
248 | ||
5992e300 TS |
249 | err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, ®, |
250 | sizeof(reg)); | |
251 | if (err < 0) | |
252 | return err; | |
253 | data = be32_to_cpu(reg); | |
254 | ||
0090c1c5 TS |
255 | memcpy(motu->tx_packet_formats.pcm_chunks, |
256 | motu->spec->tx_fixed_pcm_chunks, | |
257 | sizeof(motu->tx_packet_formats.pcm_chunks)); | |
258 | memcpy(motu->rx_packet_formats.pcm_chunks, | |
259 | motu->spec->rx_fixed_pcm_chunks, | |
260 | sizeof(motu->rx_packet_formats.pcm_chunks)); | |
5992e300 | 261 | |
bf868be7 TS |
262 | if (motu->spec == &snd_motu_spec_828mk3_fw || |
263 | motu->spec == &snd_motu_spec_828mk3_hybrid || | |
6d5a2dda | 264 | motu->spec == &snd_motu_spec_896mk3 || |
411ac298 TS |
265 | motu->spec == &snd_motu_spec_traveler_mk3 || |
266 | motu->spec == &snd_motu_spec_track16) | |
267 | return detect_packet_formats_with_opt_ifaces(motu, data); | |
0090c1c5 TS |
268 | else |
269 | return 0; | |
5992e300 TS |
270 | } |
271 | ||
5b24119e | 272 | const struct snd_motu_spec snd_motu_spec_828mk3_fw = { |
c806a0e2 | 273 | .name = "828mk3", |
61d79c70 | 274 | .protocol_version = SND_MOTU_PROTOCOL_V3, |
739bdbae | 275 | .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | |
90b28f3b TS |
276 | SND_MOTU_SPEC_TX_MIDI_3RD_Q | |
277 | SND_MOTU_SPEC_COMMAND_DSP, | |
dfbaa4dc TS |
278 | .tx_fixed_pcm_chunks = {18, 18, 14}, |
279 | .rx_fixed_pcm_chunks = {14, 14, 10}, | |
c806a0e2 TS |
280 | }; |
281 | ||
5b24119e TS |
282 | const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = { |
283 | .name = "828mk3", | |
284 | .protocol_version = SND_MOTU_PROTOCOL_V3, | |
285 | .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | | |
90b28f3b TS |
286 | SND_MOTU_SPEC_TX_MIDI_3RD_Q | |
287 | SND_MOTU_SPEC_COMMAND_DSP, | |
5b24119e TS |
288 | .tx_fixed_pcm_chunks = {18, 18, 14}, |
289 | .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate. | |
290 | }; | |
291 | ||
6d5a2dda TS |
292 | const struct snd_motu_spec snd_motu_spec_896mk3 = { |
293 | .name = "896mk3", | |
294 | .protocol_version = SND_MOTU_PROTOCOL_V3, | |
295 | .flags = SND_MOTU_SPEC_COMMAND_DSP, | |
296 | .tx_fixed_pcm_chunks = {18, 14, 10}, | |
297 | .rx_fixed_pcm_chunks = {18, 14, 10}, | |
298 | }; | |
299 | ||
bf868be7 TS |
300 | const struct snd_motu_spec snd_motu_spec_traveler_mk3 = { |
301 | .name = "TravelerMk3", | |
302 | .protocol_version = SND_MOTU_PROTOCOL_V3, | |
303 | .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | | |
304 | SND_MOTU_SPEC_TX_MIDI_3RD_Q | | |
305 | SND_MOTU_SPEC_COMMAND_DSP, | |
306 | .tx_fixed_pcm_chunks = {18, 14, 10}, | |
307 | .rx_fixed_pcm_chunks = {14, 14, 10}, | |
308 | }; | |
309 | ||
e0b2db35 TS |
310 | const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = { |
311 | .name = "UltraLiteMk3", | |
312 | .protocol_version = SND_MOTU_PROTOCOL_V3, | |
313 | .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | | |
90b28f3b TS |
314 | SND_MOTU_SPEC_TX_MIDI_3RD_Q | |
315 | SND_MOTU_SPEC_COMMAND_DSP, | |
e0b2db35 TS |
316 | .tx_fixed_pcm_chunks = {18, 14, 10}, |
317 | .rx_fixed_pcm_chunks = {14, 14, 14}, | |
318 | }; | |
319 | ||
c806a0e2 TS |
320 | const struct snd_motu_spec snd_motu_spec_audio_express = { |
321 | .name = "AudioExpress", | |
61d79c70 | 322 | .protocol_version = SND_MOTU_PROTOCOL_V3, |
739bdbae | 323 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
bea36afa TS |
324 | SND_MOTU_SPEC_TX_MIDI_3RD_Q | |
325 | SND_MOTU_SPEC_REGISTER_DSP, | |
dfbaa4dc TS |
326 | .tx_fixed_pcm_chunks = {10, 10, 0}, |
327 | .rx_fixed_pcm_chunks = {10, 10, 0}, | |
c806a0e2 TS |
328 | }; |
329 | ||
411ac298 TS |
330 | const struct snd_motu_spec snd_motu_spec_track16 = { |
331 | .name = "Track16", | |
332 | .protocol_version = SND_MOTU_PROTOCOL_V3, | |
333 | .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | | |
334 | SND_MOTU_SPEC_TX_MIDI_3RD_Q | | |
335 | SND_MOTU_SPEC_COMMAND_DSP, | |
336 | .tx_fixed_pcm_chunks = {14, 14, 14}, | |
337 | .rx_fixed_pcm_chunks = {6, 6, 6}, | |
338 | }; | |
339 | ||
c806a0e2 TS |
340 | const struct snd_motu_spec snd_motu_spec_4pre = { |
341 | .name = "4pre", | |
61d79c70 | 342 | .protocol_version = SND_MOTU_PROTOCOL_V3, |
bea36afa | 343 | .flags = SND_MOTU_SPEC_REGISTER_DSP, |
dfbaa4dc TS |
344 | .tx_fixed_pcm_chunks = {10, 10, 0}, |
345 | .rx_fixed_pcm_chunks = {10, 10, 0}, | |
c806a0e2 | 346 | }; |