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