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