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_FETCH_ENABLE			0x02000000
16 #define  V2_CLOCK_MODEL_SPECIFIC		0x04000000
17 
18 #define V2_IN_OUT_CONF_OFFSET			0x0c04
19 #define  V2_OPT_OUT_IFACE_MASK			0x00000c00
20 #define  V2_OPT_OUT_IFACE_SHIFT			10
21 #define  V2_OPT_IN_IFACE_MASK			0x00000300
22 #define  V2_OPT_IN_IFACE_SHIFT			8
23 #define  V2_OPT_IFACE_MODE_NONE			0
24 #define  V2_OPT_IFACE_MODE_ADAT			1
25 #define  V2_OPT_IFACE_MODE_SPDIF		2
26 
27 static int get_clock_rate(u32 data, unsigned int *rate)
28 {
29 	unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
30 	if (index >= ARRAY_SIZE(snd_motu_clock_rates))
31 		return -EIO;
32 
33 	*rate = snd_motu_clock_rates[index];
34 
35 	return 0;
36 }
37 
38 static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
39 {
40 	__be32 reg;
41 	int err;
42 
43 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
44 					sizeof(reg));
45 	if (err < 0)
46 		return err;
47 
48 	return get_clock_rate(be32_to_cpu(reg), rate);
49 }
50 
51 static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
52 {
53 	__be32 reg;
54 	u32 data;
55 	int i;
56 	int err;
57 
58 	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
59 		if (snd_motu_clock_rates[i] == rate)
60 			break;
61 	}
62 	if (i == ARRAY_SIZE(snd_motu_clock_rates))
63 		return -EINVAL;
64 
65 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
66 					sizeof(reg));
67 	if (err < 0)
68 		return err;
69 	data = be32_to_cpu(reg);
70 
71 	data &= ~V2_CLOCK_RATE_MASK;
72 	data |= i << V2_CLOCK_RATE_SHIFT;
73 
74 	reg = cpu_to_be32(data);
75 	return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
76 					  sizeof(reg));
77 }
78 
79 static int get_clock_source(struct snd_motu *motu, u32 data,
80 			    enum snd_motu_clock_source *src)
81 {
82 	unsigned int index = data & V2_CLOCK_SRC_MASK;
83 	if (index > 5)
84 		return -EIO;
85 
86 	switch (index) {
87 	case 0:
88 		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
89 		break;
90 	case 1:
91 	{
92 		__be32 reg;
93 
94 		// To check the configuration of optical interface.
95 		int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET,
96 						    &reg, sizeof(reg));
97 		if (err < 0)
98 			return err;
99 
100 		if (be32_to_cpu(reg) & 0x00000200)
101 			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
102 		else
103 			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
104 		break;
105 	}
106 	case 2:
107 		*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
108 		break;
109 	case 3:
110 		*src = SND_MOTU_CLOCK_SOURCE_SPH;
111 		break;
112 	case 4:
113 		*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
114 		break;
115 	case 5:
116 		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
117 		break;
118 	default:
119 		return -EIO;
120 	}
121 
122 	return 0;
123 }
124 
125 static int v2_get_clock_source(struct snd_motu *motu,
126 			       enum snd_motu_clock_source *src)
127 {
128 	__be32 reg;
129 	int err;
130 
131 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
132 					sizeof(reg));
133 	if (err < 0)
134 		return err;
135 
136 	return get_clock_source(motu, be32_to_cpu(reg), src);
137 }
138 
139 static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
140 {
141 	enum snd_motu_clock_source src;
142 	__be32 reg;
143 	u32 data;
144 	int err = 0;
145 
146 	// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
147 	if (motu->spec == &snd_motu_spec_828mk2)
148 		return 0;
149 
150 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
151 					sizeof(reg));
152 	if (err < 0)
153 		return err;
154 	data = be32_to_cpu(reg);
155 
156 	err = get_clock_source(motu, data, &src);
157 	if (err < 0)
158 		return err;
159 
160 	data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
161 	if (enable)
162 		data |= V2_CLOCK_FETCH_ENABLE;
163 
164 	if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) {
165 		// Expected for Traveler and 896HD, which implements Altera
166 		// Cyclone EP1C3.
167 		data |= V2_CLOCK_MODEL_SPECIFIC;
168 	} else {
169 		// For UltraLite and 8pre, which implements Xilinx Spartan
170 		// XC3S200.
171 		unsigned int rate;
172 
173 		err = get_clock_rate(data, &rate);
174 		if (err < 0)
175 			return err;
176 
177 		if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
178 			data |= V2_CLOCK_MODEL_SPECIFIC;
179 	}
180 
181 	reg = cpu_to_be32(data);
182 	return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
183 					  sizeof(reg));
184 }
185 
186 static void calculate_fixed_part(struct snd_motu_packet_format *formats,
187 				 enum amdtp_stream_direction dir,
188 				 enum snd_motu_spec_flags flags,
189 				 unsigned char analog_ports)
190 {
191 	unsigned char pcm_chunks[3] = {0, 0, 0};
192 
193 	formats->msg_chunks = 2;
194 
195 	pcm_chunks[0] = analog_ports;
196 	pcm_chunks[1] = analog_ports;
197 	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
198 		pcm_chunks[2] = analog_ports;
199 
200 	if (dir == AMDTP_IN_STREAM) {
201 		if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
202 			pcm_chunks[0] += 2;
203 			pcm_chunks[1] += 2;
204 		}
205 		if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
206 			pcm_chunks[0] += 2;
207 			pcm_chunks[1] += 2;
208 		}
209 	} else {
210 		if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
211 			pcm_chunks[0] += 2;
212 			pcm_chunks[1] += 2;
213 		}
214 
215 		// Packets to v2 units include 2 chunks for phone 1/2, except
216 		// for 176.4/192.0 kHz.
217 		pcm_chunks[0] += 2;
218 		pcm_chunks[1] += 2;
219 	}
220 
221 	if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
222 		pcm_chunks[0] += 2;
223 		pcm_chunks[1] += 2;
224 	}
225 
226 	/*
227 	 * All of v2 models have a pair of coaxial interfaces for digital in/out
228 	 * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
229 	 * ports.
230 	 */
231 	pcm_chunks[0] += 2;
232 	pcm_chunks[1] += 2;
233 
234 	formats->fixed_part_pcm_chunks[0] = pcm_chunks[0];
235 	formats->fixed_part_pcm_chunks[1] = pcm_chunks[1];
236 	formats->fixed_part_pcm_chunks[2] = pcm_chunks[2];
237 }
238 
239 static void calculate_differed_part(struct snd_motu_packet_format *formats,
240 				    enum snd_motu_spec_flags flags,
241 				    u32 data, u32 mask, u32 shift)
242 {
243 	unsigned char pcm_chunks[2] = {0, 0};
244 
245 	/*
246 	 * When optical interfaces are configured for S/PDIF (TOSLINK),
247 	 * the above PCM frames come from them, instead of coaxial
248 	 * interfaces.
249 	 */
250 	data = (data & mask) >> shift;
251 	if (data == V2_OPT_IFACE_MODE_ADAT) {
252 		if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) {
253 			pcm_chunks[0] += 8;
254 			pcm_chunks[1] += 4;
255 		}
256 		// 8pre has two sets of optical interface and doesn't reduce
257 		// chunks for ADAT signals.
258 		if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) {
259 			pcm_chunks[1] += 4;
260 		}
261 	}
262 
263 	/* At mode x4, no data chunks are supported in this part. */
264 	formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
265 	formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
266 }
267 
268 static int v2_cache_packet_formats(struct snd_motu *motu)
269 {
270 	__be32 reg;
271 	u32 data;
272 	int err;
273 
274 	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
275 					sizeof(reg));
276 	if (err < 0)
277 		return err;
278 	data = be32_to_cpu(reg);
279 
280 	calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
281 			     motu->spec->flags, motu->spec->analog_in_ports);
282 	calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
283 			data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
284 
285 	calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
286 			     motu->spec->flags, motu->spec->analog_out_ports);
287 	calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
288 			data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
289 
290 	motu->tx_packet_formats.pcm_byte_offset = 10;
291 	motu->rx_packet_formats.pcm_byte_offset = 10;
292 
293 	return 0;
294 }
295 
296 const struct snd_motu_protocol snd_motu_protocol_v2 = {
297 	.get_clock_rate		= v2_get_clock_rate,
298 	.set_clock_rate		= v2_set_clock_rate,
299 	.get_clock_source	= v2_get_clock_source,
300 	.switch_fetching_mode	= v2_switch_fetching_mode,
301 	.cache_packet_formats	= v2_cache_packet_formats,
302 };
303 
304 const struct snd_motu_spec snd_motu_spec_828mk2 = {
305 	.name = "828mk2",
306 	.protocol = &snd_motu_protocol_v2,
307 	.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
308 		 SND_MOTU_SPEC_TX_MICINST_CHUNK |
309 		 SND_MOTU_SPEC_TX_RETURN_CHUNK |
310 		 SND_MOTU_SPEC_RX_SEPARATED_MAIN |
311 		 SND_MOTU_SPEC_HAS_OPT_IFACE_A |
312 		 SND_MOTU_SPEC_RX_MIDI_2ND_Q |
313 		 SND_MOTU_SPEC_TX_MIDI_2ND_Q,
314 
315 	.analog_in_ports = 8,
316 	.analog_out_ports = 8,
317 };
318 
319 const struct snd_motu_spec snd_motu_spec_traveler = {
320 	.name = "Traveler",
321 	.protocol = &snd_motu_protocol_v2,
322 	.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
323 		 SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
324 		 SND_MOTU_SPEC_TX_RETURN_CHUNK |
325 		 SND_MOTU_SPEC_HAS_AESEBU_IFACE |
326 		 SND_MOTU_SPEC_HAS_OPT_IFACE_A |
327 		 SND_MOTU_SPEC_RX_MIDI_2ND_Q |
328 		 SND_MOTU_SPEC_TX_MIDI_2ND_Q,
329 
330 	.analog_in_ports = 8,
331 	.analog_out_ports = 8,
332 };
333 
334 const struct snd_motu_spec snd_motu_spec_ultralite = {
335 	.name = "UltraLite",
336 	.protocol = &snd_motu_protocol_v2,
337 	.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
338 		 SND_MOTU_SPEC_TX_MICINST_CHUNK | // padding.
339 		 SND_MOTU_SPEC_TX_RETURN_CHUNK |
340 		 SND_MOTU_SPEC_RX_MIDI_2ND_Q |
341 		 SND_MOTU_SPEC_TX_MIDI_2ND_Q |
342 		 SND_MOTU_SPEC_RX_SEPARATED_MAIN,
343 	.analog_in_ports = 8,
344 	.analog_out_ports = 8,
345 };
346 
347 const struct snd_motu_spec snd_motu_spec_8pre = {
348 	.name = "8pre",
349 	.protocol = &snd_motu_protocol_v2,
350 	// In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for
351 	// dummy 1/2.
352 	.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
353 		 SND_MOTU_SPEC_HAS_OPT_IFACE_A |
354 		 SND_MOTU_SPEC_HAS_OPT_IFACE_B |
355 		 SND_MOTU_SPEC_RX_MIDI_2ND_Q |
356 		 SND_MOTU_SPEC_TX_MIDI_2ND_Q,
357 	.analog_in_ports = 8,
358 	.analog_out_ports = 2,
359 };
360