]> asedeno.scripts.mit.edu Git - linux.git/blob - sound/firewire/motu/motu-protocol-v2.c
Merge tag 'mmc-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
[linux.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 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_SEPARETED_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 };