2757e5b42b9b19c5eba8b5506d015d8f4c436824
[linux-2.6-block.git] / sound / firewire / motu / motu-protocol-v2.c
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 3:
118                 *src = SND_MOTU_CLOCK_SOURCE_SPH;
119                 break;
120         case 4:
121                 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
122                 break;
123         case 5:
124                 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
125                 break;
126         default:
127                 return -EIO;
128         }
129
130         return 0;
131 }
132
133 static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
134 {
135         __be32 reg;
136         u32 data;
137         int err = 0;
138
139         if (motu->spec == &snd_motu_spec_traveler ||
140             motu->spec == &snd_motu_spec_8pre) {
141                 err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
142                                                 &reg, sizeof(reg));
143                 if (err < 0)
144                         return err;
145                 data = be32_to_cpu(reg);
146
147                 if (motu->spec == &snd_motu_spec_traveler) {
148                         data &= ~(V2_CLOCK_TRAVELER_FETCH_DISABLE |
149                                   V2_CLOCK_TRAVELER_FETCH_ENABLE);
150
151                         if (enable)
152                                 data |= V2_CLOCK_TRAVELER_FETCH_ENABLE;
153                         else
154                                 data |= V2_CLOCK_TRAVELER_FETCH_DISABLE;
155                 } else if (motu->spec == &snd_motu_spec_8pre) {
156                         data &= ~(V2_CLOCK_8PRE_FETCH_DISABLE |
157                                   V2_CLOCK_8PRE_FETCH_ENABLE);
158
159                         if (enable)
160                                 data |= V2_CLOCK_8PRE_FETCH_DISABLE;
161                         else
162                                 data |= V2_CLOCK_8PRE_FETCH_ENABLE;
163                 }
164
165                 reg = cpu_to_be32(data);
166                 err = snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
167                                                  &reg, sizeof(reg));
168         }
169
170         return err;
171 }
172
173 static void calculate_fixed_part(struct snd_motu_packet_format *formats,
174                                  enum amdtp_stream_direction dir,
175                                  enum snd_motu_spec_flags flags,
176                                  unsigned char analog_ports)
177 {
178         unsigned char pcm_chunks[3] = {0, 0, 0};
179
180         formats->msg_chunks = 2;
181
182         pcm_chunks[0] = analog_ports;
183         pcm_chunks[1] = analog_ports;
184         if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
185                 pcm_chunks[2] = analog_ports;
186
187         if (dir == AMDTP_IN_STREAM) {
188                 if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
189                         pcm_chunks[0] += 2;
190                         pcm_chunks[1] += 2;
191                 }
192                 if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
193                         pcm_chunks[0] += 2;
194                         pcm_chunks[1] += 2;
195                 }
196         } else {
197                 if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) {
198                         pcm_chunks[0] += 2;
199                         pcm_chunks[1] += 2;
200                 }
201
202                 // Packets to v2 units include 2 chunks for phone 1/2, except
203                 // for 176.4/192.0 kHz.
204                 pcm_chunks[0] += 2;
205                 pcm_chunks[1] += 2;
206         }
207
208         if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) {
209                 pcm_chunks[0] += 2;
210                 pcm_chunks[1] += 2;
211         }
212
213         /*
214          * All of v2 models have a pair of coaxial interfaces for digital in/out
215          * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
216          * ports.
217          */
218         pcm_chunks[0] += 2;
219         pcm_chunks[1] += 2;
220
221         formats->fixed_part_pcm_chunks[0] = pcm_chunks[0];
222         formats->fixed_part_pcm_chunks[1] = pcm_chunks[1];
223         formats->fixed_part_pcm_chunks[2] = pcm_chunks[2];
224 }
225
226 static void calculate_differed_part(struct snd_motu_packet_format *formats,
227                                     enum snd_motu_spec_flags flags,
228                                     u32 data, u32 mask, u32 shift)
229 {
230         unsigned char pcm_chunks[2] = {0, 0};
231
232         /*
233          * When optical interfaces are configured for S/PDIF (TOSLINK),
234          * the above PCM frames come from them, instead of coaxial
235          * interfaces.
236          */
237         data = (data & mask) >> shift;
238         if (data == V2_OPT_IFACE_MODE_ADAT) {
239                 if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) {
240                         pcm_chunks[0] += 8;
241                         pcm_chunks[1] += 4;
242                 }
243                 // 8pre has two sets of optical interface and doesn't reduce
244                 // chunks for ADAT signals.
245                 if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) {
246                         pcm_chunks[1] += 4;
247                 }
248         }
249
250         /* At mode x4, no data chunks are supported in this part. */
251         formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
252         formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
253 }
254
255 static int v2_cache_packet_formats(struct snd_motu *motu)
256 {
257         __be32 reg;
258         u32 data;
259         int err;
260
261         err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
262                                         sizeof(reg));
263         if (err < 0)
264                 return err;
265         data = be32_to_cpu(reg);
266
267         calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
268                              motu->spec->flags, motu->spec->analog_in_ports);
269         calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
270                         data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
271
272         calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
273                              motu->spec->flags, motu->spec->analog_out_ports);
274         calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
275                         data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
276
277         motu->tx_packet_formats.pcm_byte_offset = 10;
278         motu->rx_packet_formats.pcm_byte_offset = 10;
279
280         return 0;
281 }
282
283 const struct snd_motu_protocol snd_motu_protocol_v2 = {
284         .get_clock_rate         = v2_get_clock_rate,
285         .set_clock_rate         = v2_set_clock_rate,
286         .get_clock_source       = v2_get_clock_source,
287         .switch_fetching_mode   = v2_switch_fetching_mode,
288         .cache_packet_formats   = v2_cache_packet_formats,
289 };