1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * motu-protocol-v2.c - a part of driver for MOTU FireWire series |
4 | * |
5 | * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> |
6 | */ |
7 | |
8 | #include "motu.h" |
9 | |
10 | #define V2_CLOCK_STATUS_OFFSET 0x0b14 |
11 | #define V2_CLOCK_RATE_MASK 0x00000038 |
12 | #define V2_CLOCK_RATE_SHIFT 3 |
13 | #define V2_CLOCK_SRC_MASK 0x00000007 |
14 | #define V2_CLOCK_SRC_SHIFT 0 |
15 | #define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07 // In Traveler. |
16 | #define V2_CLOCK_SRC_ADAT_ON_DSUB 0x05 |
17 | #define V2_CLOCK_SRC_WORD_ON_BNC 0x04 |
18 | #define V2_CLOCK_SRC_SPH 0x03 |
19 | #define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical. AES/EBU in 896HD. |
20 | #define V2_CLOCK_SRC_ADAT_ON_OPT 0x01 |
21 | #define V2_CLOCK_SRC_INTERNAL 0x00 |
22 | #define V2_CLOCK_FETCH_ENABLE 0x02000000 |
23 | #define V2_CLOCK_MODEL_SPECIFIC 0x04000000 |
24 | |
25 | #define V2_IN_OUT_CONF_OFFSET 0x0c04 |
26 | #define V2_OPT_OUT_IFACE_MASK 0x00000c00 |
27 | #define V2_OPT_OUT_IFACE_SHIFT 10 |
28 | #define V2_OPT_IN_IFACE_MASK 0x00000300 |
29 | #define V2_OPT_IN_IFACE_SHIFT 8 |
30 | #define V2_OPT_IFACE_MODE_NONE 0 |
31 | #define V2_OPT_IFACE_MODE_ADAT 1 |
32 | #define V2_OPT_IFACE_MODE_SPDIF 2 |
33 | |
34 | static int get_clock_rate(u32 data, unsigned int *rate) |
35 | { |
36 | unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT; |
37 | if (index >= ARRAY_SIZE(snd_motu_clock_rates)) |
38 | return -EIO; |
39 | |
40 | *rate = snd_motu_clock_rates[index]; |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, |
46 | unsigned int *rate) |
47 | { |
48 | __be32 reg; |
49 | int err; |
50 | |
51 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, reg: ®, |
52 | size: sizeof(reg)); |
53 | if (err < 0) |
54 | return err; |
55 | |
56 | return get_clock_rate(be32_to_cpu(reg), rate); |
57 | } |
58 | |
59 | int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, |
60 | unsigned int rate) |
61 | { |
62 | __be32 reg; |
63 | u32 data; |
64 | int i; |
65 | int 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, V2_CLOCK_STATUS_OFFSET, reg: ®, |
75 | size: sizeof(reg)); |
76 | if (err < 0) |
77 | return err; |
78 | data = be32_to_cpu(reg); |
79 | |
80 | data &= ~V2_CLOCK_RATE_MASK; |
81 | data |= i << V2_CLOCK_RATE_SHIFT; |
82 | |
83 | reg = cpu_to_be32(data); |
84 | return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, reg: ®, |
85 | size: sizeof(reg)); |
86 | } |
87 | |
88 | static int get_clock_source(struct snd_motu *motu, u32 data, |
89 | enum snd_motu_clock_source *src) |
90 | { |
91 | switch (data & V2_CLOCK_SRC_MASK) { |
92 | case V2_CLOCK_SRC_INTERNAL: |
93 | *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; |
94 | break; |
95 | case V2_CLOCK_SRC_ADAT_ON_OPT: |
96 | *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; |
97 | break; |
98 | case V2_CLOCK_SRC_SPDIF: |
99 | { |
100 | bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 || |
101 | motu->spec == &snd_motu_spec_traveler); |
102 | |
103 | if (motu->spec == &snd_motu_spec_896hd) { |
104 | *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; |
105 | } else if (!support_iec60958_on_opt) { |
106 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; |
107 | } else { |
108 | __be32 reg; |
109 | |
110 | // To check the configuration of optical interface. |
111 | int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, reg: ®, |
112 | size: sizeof(reg)); |
113 | if (err < 0) |
114 | return err; |
115 | |
116 | if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == |
117 | V2_OPT_IFACE_MODE_SPDIF) |
118 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT; |
119 | else |
120 | *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; |
121 | } |
122 | break; |
123 | } |
124 | case V2_CLOCK_SRC_SPH: |
125 | *src = SND_MOTU_CLOCK_SOURCE_SPH; |
126 | break; |
127 | case V2_CLOCK_SRC_WORD_ON_BNC: |
128 | *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; |
129 | break; |
130 | case V2_CLOCK_SRC_ADAT_ON_DSUB: |
131 | *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; |
132 | break; |
133 | case V2_CLOCK_SRC_AESEBU_ON_XLR: |
134 | // For Traveler. |
135 | *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; |
136 | break; |
137 | default: |
138 | *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; |
139 | break; |
140 | } |
141 | |
142 | return 0; |
143 | } |
144 | |
145 | int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, |
146 | enum snd_motu_clock_source *src) |
147 | { |
148 | __be32 reg; |
149 | int err; |
150 | |
151 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, reg: ®, |
152 | size: sizeof(reg)); |
153 | if (err < 0) |
154 | return err; |
155 | |
156 | return get_clock_source(motu, be32_to_cpu(reg), src); |
157 | } |
158 | |
159 | // Expected for Traveler, which implements Altera Cyclone EP1C3. |
160 | static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data, |
161 | bool enable) |
162 | { |
163 | *data |= V2_CLOCK_MODEL_SPECIFIC; |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | // For UltraLite and 8pre, which implements Xilinx Spartan XC3S200. |
169 | static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data, |
170 | bool enable) |
171 | { |
172 | unsigned int rate; |
173 | enum snd_motu_clock_source src; |
174 | int err; |
175 | |
176 | err = get_clock_source(motu, data: *data, src: &src); |
177 | if (err < 0) |
178 | return err; |
179 | |
180 | err = get_clock_rate(data: *data, rate: &rate); |
181 | if (err < 0) |
182 | return err; |
183 | |
184 | if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) |
185 | *data |= V2_CLOCK_MODEL_SPECIFIC; |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, |
191 | bool enable) |
192 | { |
193 | if (motu->spec == &snd_motu_spec_828mk2) { |
194 | // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. |
195 | return 0; |
196 | } else if (motu->spec == &snd_motu_spec_896hd) { |
197 | // 896HD implements Altera Cyclone EP1C3 but nothing to do. |
198 | return 0; |
199 | } else { |
200 | __be32 reg; |
201 | u32 data; |
202 | int err; |
203 | |
204 | err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, |
205 | reg: ®, size: sizeof(reg)); |
206 | if (err < 0) |
207 | return err; |
208 | data = be32_to_cpu(reg); |
209 | |
210 | data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); |
211 | if (enable) |
212 | data |= V2_CLOCK_FETCH_ENABLE; |
213 | |
214 | if (motu->spec == &snd_motu_spec_traveler) |
215 | err = switch_fetching_mode_cyclone(motu, data: &data, enable); |
216 | else |
217 | err = switch_fetching_mode_spartan(motu, data: &data, enable); |
218 | if (err < 0) |
219 | return err; |
220 | |
221 | reg = cpu_to_be32(data); |
222 | return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, |
223 | reg: ®, size: sizeof(reg)); |
224 | } |
225 | } |
226 | |
227 | int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu) |
228 | { |
229 | bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre); |
230 | __be32 reg; |
231 | u32 data; |
232 | int err; |
233 | |
234 | motu->tx_packet_formats.pcm_byte_offset = 10; |
235 | motu->rx_packet_formats.pcm_byte_offset = 10; |
236 | |
237 | motu->tx_packet_formats.msg_chunks = 2; |
238 | motu->rx_packet_formats.msg_chunks = 2; |
239 | |
240 | err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, reg: ®, |
241 | size: sizeof(reg)); |
242 | if (err < 0) |
243 | return err; |
244 | data = be32_to_cpu(reg); |
245 | |
246 | memcpy(motu->tx_packet_formats.pcm_chunks, |
247 | motu->spec->tx_fixed_pcm_chunks, |
248 | sizeof(motu->tx_packet_formats.pcm_chunks)); |
249 | memcpy(motu->rx_packet_formats.pcm_chunks, |
250 | motu->spec->rx_fixed_pcm_chunks, |
251 | sizeof(motu->rx_packet_formats.pcm_chunks)); |
252 | |
253 | if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) { |
254 | motu->tx_packet_formats.pcm_chunks[0] += 8; |
255 | |
256 | if (!has_two_opt_ifaces) |
257 | motu->tx_packet_formats.pcm_chunks[1] += 4; |
258 | else |
259 | motu->tx_packet_formats.pcm_chunks[1] += 8; |
260 | } |
261 | |
262 | if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) { |
263 | motu->rx_packet_formats.pcm_chunks[0] += 8; |
264 | |
265 | if (!has_two_opt_ifaces) |
266 | motu->rx_packet_formats.pcm_chunks[1] += 4; |
267 | else |
268 | motu->rx_packet_formats.pcm_chunks[1] += 8; |
269 | } |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | const struct snd_motu_spec snd_motu_spec_828mk2 = { |
275 | .name = "828mk2" , |
276 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
277 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
278 | SND_MOTU_SPEC_TX_MIDI_2ND_Q | |
279 | SND_MOTU_SPEC_REGISTER_DSP, |
280 | .tx_fixed_pcm_chunks = {14, 14, 0}, |
281 | .rx_fixed_pcm_chunks = {14, 14, 0}, |
282 | }; |
283 | |
284 | const struct snd_motu_spec snd_motu_spec_896hd = { |
285 | .name = "896HD" , |
286 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
287 | .flags = SND_MOTU_SPEC_REGISTER_DSP, |
288 | .tx_fixed_pcm_chunks = {14, 14, 8}, |
289 | .rx_fixed_pcm_chunks = {14, 14, 8}, |
290 | }; |
291 | |
292 | const struct snd_motu_spec snd_motu_spec_traveler = { |
293 | .name = "Traveler" , |
294 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
295 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
296 | SND_MOTU_SPEC_TX_MIDI_2ND_Q | |
297 | SND_MOTU_SPEC_REGISTER_DSP, |
298 | .tx_fixed_pcm_chunks = {14, 14, 8}, |
299 | .rx_fixed_pcm_chunks = {14, 14, 8}, |
300 | }; |
301 | |
302 | const struct snd_motu_spec snd_motu_spec_ultralite = { |
303 | .name = "UltraLite" , |
304 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
305 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
306 | SND_MOTU_SPEC_TX_MIDI_2ND_Q | |
307 | SND_MOTU_SPEC_REGISTER_DSP, |
308 | .tx_fixed_pcm_chunks = {14, 14, 0}, |
309 | .rx_fixed_pcm_chunks = {14, 14, 0}, |
310 | }; |
311 | |
312 | const struct snd_motu_spec snd_motu_spec_8pre = { |
313 | .name = "8pre" , |
314 | .protocol_version = SND_MOTU_PROTOCOL_V2, |
315 | .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | |
316 | SND_MOTU_SPEC_TX_MIDI_2ND_Q | |
317 | SND_MOTU_SPEC_REGISTER_DSP, |
318 | // Two dummy chunks always in the end of data block. |
319 | .tx_fixed_pcm_chunks = {10, 10, 0}, |
320 | .rx_fixed_pcm_chunks = {6, 6, 0}, |
321 | }; |
322 | |