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 		/*
153 		 * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
154 		 */
155 		pcm_chunks[0] += 4;
156 		pcm_chunks[1] += 4;
157 	}
158 
159 	/*
160 	 * All of v2 models have a pair of coaxial interfaces for digital in/out
161 	 * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
162 	 * ports.
163 	 */
164 	pcm_chunks[0] += 2;
165 	pcm_chunks[1] += 2;
166 
167 	/* This part should be multiples of 4. */
168 	formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
169 	formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
170 	if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
171 		formats->fixed_part_pcm_chunks[2] =
172 					round_up(2 + pcm_chunks[2], 4) - 2;
173 }
174 
175 static void calculate_differed_part(struct snd_motu_packet_format *formats,
176 				    enum snd_motu_spec_flags flags,
177 				    u32 data, u32 mask, u32 shift)
178 {
179 	unsigned char pcm_chunks[3] = {0, 0};
180 
181 	/*
182 	 * When optical interfaces are configured for S/PDIF (TOSLINK),
183 	 * the above PCM frames come from them, instead of coaxial
184 	 * interfaces.
185 	 */
186 	data = (data & mask) >> shift;
187 	if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
188 	    data == V2_OPT_IFACE_MODE_ADAT) {
189 		pcm_chunks[0] += 8;
190 		pcm_chunks[1] += 4;
191 	}
192 
193 	/* At mode x4, no data chunks are supported in this part. */
194 	formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
195 	formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
196 }
197 
198 static int v2_cache_packet_formats(struct snd_motu *motu)
199 {
200 	__be32 reg;
201 	u32 data;
202 	int err;
203 
204 	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
205 					sizeof(reg));
206 	if (err < 0)
207 		return err;
208 	data = be32_to_cpu(reg);
209 
210 	calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
211 			     motu->spec->flags, motu->spec->analog_in_ports);
212 	calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
213 			data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
214 
215 	calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
216 			     motu->spec->flags, motu->spec->analog_out_ports);
217 	calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
218 			data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
219 
220 	motu->tx_packet_formats.pcm_byte_offset = 10;
221 	motu->rx_packet_formats.pcm_byte_offset = 10;
222 
223 	return 0;
224 }
225 
226 const struct snd_motu_protocol snd_motu_protocol_v2 = {
227 	.get_clock_rate		= v2_get_clock_rate,
228 	.set_clock_rate		= v2_set_clock_rate,
229 	.get_clock_source	= v2_get_clock_source,
230 	.switch_fetching_mode	= v2_switch_fetching_mode,
231 	.cache_packet_formats	= v2_cache_packet_formats,
232 };
233