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_TRAVELER_FETCH_DISABLE	0x04000000
16 #define  V2_CLOCK_TRAVELER_FETCH_ENABLE		0x03000000
17 #define  V2_CLOCK_8PRE_FETCH_DISABLE		0x02000000
18 #define  V2_CLOCK_8PRE_FETCH_ENABLE		0x00000000
19 
20 #define V2_IN_OUT_CONF_OFFSET			0x0c04
21 #define  V2_OPT_OUT_IFACE_MASK			0x00000c00
22 #define  V2_OPT_OUT_IFACE_SHIFT			10
23 #define  V2_OPT_IN_IFACE_MASK			0x00000300
24 #define  V2_OPT_IN_IFACE_SHIFT			8
25 #define  V2_OPT_IFACE_MODE_NONE			0
26 #define  V2_OPT_IFACE_MODE_ADAT			1
27 #define  V2_OPT_IFACE_MODE_SPDIF		2
28 
29 static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
30 {
31 	__be32 reg;
32 	unsigned int index;
33 	int err;
34 
35 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
36 					sizeof(reg));
37 	if (err < 0)
38 		return err;
39 
40 	index = (be32_to_cpu(reg) & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
41 	if (index >= ARRAY_SIZE(snd_motu_clock_rates))
42 		return -EIO;
43 
44 	*rate = snd_motu_clock_rates[index];
45 
46 	return 0;
47 }
48 
49 static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
50 {
51 	__be32 reg;
52 	u32 data;
53 	int i;
54 	int err;
55 
56 	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
57 		if (snd_motu_clock_rates[i] == rate)
58 			break;
59 	}
60 	if (i == ARRAY_SIZE(snd_motu_clock_rates))
61 		return -EINVAL;
62 
63 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
64 					sizeof(reg));
65 	if (err < 0)
66 		return err;
67 	data = be32_to_cpu(reg);
68 
69 	data &= ~V2_CLOCK_RATE_MASK;
70 	data |= i << V2_CLOCK_RATE_SHIFT;
71 
72 	if (motu->spec == &snd_motu_spec_traveler) {
73 		data &= ~V2_CLOCK_TRAVELER_FETCH_ENABLE;
74 		data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
75 	}
76 
77 	reg = cpu_to_be32(data);
78 	return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
79 					  sizeof(reg));
80 }
81 
82 static int v2_get_clock_source(struct snd_motu *motu,
83 			       enum snd_motu_clock_source *src)
84 {
85 	__be32 reg;
86 	unsigned int index;
87 	int err;
88 
89 	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
90 					sizeof(reg));
91 	if (err < 0)
92 		return err;
93 
94 	index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK;
95 	if (index > 5)
96 		return -EIO;
97 
98 	/* To check the configuration of optical interface. */
99 	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
100 					sizeof(reg));
101 	if (err < 0)
102 		return err;
103 
104 	switch (index) {
105 	case 0:
106 		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
107 		break;
108 	case 1:
109 		if (be32_to_cpu(reg) & 0x00000200)
110 			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
111 		else
112 			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
113 		break;
114 	case 2:
115 		*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
116 		break;
117 	case 4:
118 		*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
119 		break;
120 	case 5:
121 		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
122 		break;
123 	default:
124 		return -EIO;
125 	}
126 
127 	return 0;
128 }
129 
130 static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
131 {
132 	__be32 reg;
133 	u32 data;
134 	int err = 0;
135 
136 	if (motu->spec == &snd_motu_spec_traveler ||
137 	    motu->spec == &snd_motu_spec_8pre) {
138 		err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
139 						&reg, sizeof(reg));
140 		if (err < 0)
141 			return err;
142 		data = be32_to_cpu(reg);
143 
144 		if (motu->spec == &snd_motu_spec_traveler) {
145 			data &= ~(V2_CLOCK_TRAVELER_FETCH_DISABLE |
146 				  V2_CLOCK_TRAVELER_FETCH_ENABLE);
147 
148 			if (enable)
149 				data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
150 			else
151 				data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
152 		} else if (motu->spec == &snd_motu_spec_8pre) {
153 			data &= ~(V2_CLOCK_8PRE_FETCH_DISABLE |
154 				  V2_CLOCK_8PRE_FETCH_ENABLE);
155 
156 			if (enable)
157 				data |= V2_CLOCK_8PRE_FETCH_DISABLE;
158 			else
159 				data |= V2_CLOCK_8PRE_FETCH_ENABLE;
160 		}
161 
162 		reg = cpu_to_be32(data);
163 		err = snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
164 						 &reg, sizeof(reg));
165 	}
166 
167 	return err;
168 }
169 
170 static void calculate_fixed_part(struct snd_motu_packet_format *formats,
171 				 enum amdtp_stream_direction dir,
172 				 enum snd_motu_spec_flags flags,
173 				 unsigned char analog_ports)
174 {
175 	unsigned char pcm_chunks[3] = {0, 0, 0};
176 
177 	formats->msg_chunks = 2;
178 
179 	pcm_chunks[0] = analog_ports;
180 	pcm_chunks[1] = analog_ports;
181 	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
182 		pcm_chunks[2] = analog_ports;
183 
184 	if (dir == AMDTP_IN_STREAM) {
185 		if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
186 			pcm_chunks[0] += 2;
187 			pcm_chunks[1] += 2;
188 		}
189 		if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
190 			pcm_chunks[0] += 2;
191 			pcm_chunks[1] += 2;
192 		}
193 	} else {
194 		if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
195 			pcm_chunks[0] += 2;
196 			pcm_chunks[1] += 2;
197 		}
198 
199 		// Packets to v2 units include 2 chunks for phone 1/2, except
200 		// for 176.4/192.0 kHz.
201 		pcm_chunks[0] += 2;
202 		pcm_chunks[1] += 2;
203 	}
204 
205 	if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
206 		pcm_chunks[0] += 2;
207 		pcm_chunks[1] += 2;
208 	}
209 
210 	/*
211 	 * All of v2 models have a pair of coaxial interfaces for digital in/out
212 	 * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
213 	 * ports.
214 	 */
215 	pcm_chunks[0] += 2;
216 	pcm_chunks[1] += 2;
217 
218 	formats->fixed_part_pcm_chunks[0] = pcm_chunks[0];
219 	formats->fixed_part_pcm_chunks[1] = pcm_chunks[1];
220 	formats->fixed_part_pcm_chunks[2] = pcm_chunks[2];
221 }
222 
223 static void calculate_differed_part(struct snd_motu_packet_format *formats,
224 				    enum snd_motu_spec_flags flags,
225 				    u32 data, u32 mask, u32 shift)
226 {
227 	unsigned char pcm_chunks[2] = {0, 0};
228 
229 	/*
230 	 * When optical interfaces are configured for S/PDIF (TOSLINK),
231 	 * the above PCM frames come from them, instead of coaxial
232 	 * interfaces.
233 	 */
234 	data = (data & mask) >> shift;
235 	if (data == V2_OPT_IFACE_MODE_ADAT) {
236 		if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) {
237 			pcm_chunks[0] += 8;
238 			pcm_chunks[1] += 4;
239 		}
240 		// 8pre has two sets of optical interface and doesn't reduce
241 		// chunks for ADAT signals.
242 		if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) {
243 			pcm_chunks[1] += 4;
244 		}
245 	}
246 
247 	/* At mode x4, no data chunks are supported in this part. */
248 	formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
249 	formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
250 }
251 
252 static int v2_cache_packet_formats(struct snd_motu *motu)
253 {
254 	__be32 reg;
255 	u32 data;
256 	int err;
257 
258 	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
259 					sizeof(reg));
260 	if (err < 0)
261 		return err;
262 	data = be32_to_cpu(reg);
263 
264 	calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
265 			     motu->spec->flags, motu->spec->analog_in_ports);
266 	calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
267 			data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
268 
269 	calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
270 			     motu->spec->flags, motu->spec->analog_out_ports);
271 	calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
272 			data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
273 
274 	motu->tx_packet_formats.pcm_byte_offset = 10;
275 	motu->rx_packet_formats.pcm_byte_offset = 10;
276 
277 	return 0;
278 }
279 
280 const struct snd_motu_protocol snd_motu_protocol_v2 = {
281 	.get_clock_rate		= v2_get_clock_rate,
282 	.set_clock_rate		= v2_set_clock_rate,
283 	.get_clock_source	= v2_get_clock_source,
284 	.switch_fetching_mode	= v2_switch_fetching_mode,
285 	.cache_packet_formats	= v2_cache_packet_formats,
286 };
287