~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/motu/motu-protocol-v3.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * motu-protocol-v3.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 <linux/delay.h>
  9 #include "motu.h"
 10 
 11 #define V3_CLOCK_STATUS_OFFSET          0x0b14
 12 #define  V3_FETCH_PCM_FRAMES            0x02000000
 13 #define  V3_CLOCK_RATE_MASK             0x0000ff00
 14 #define  V3_CLOCK_RATE_SHIFT            8
 15 #define  V3_CLOCK_SOURCE_MASK           0x000000ff
 16 #define   V3_CLOCK_SRC_INTERNAL         0x00
 17 #define   V3_CLOCK_SRC_WORD_ON_BNC      0x01
 18 #define   V3_CLOCK_SRC_SPH              0x02
 19 #define   V3_CLOCK_SRC_AESEBU_ON_XLR    0x08
 20 #define   V3_CLOCK_SRC_SPDIF_ON_COAX    0x10
 21 #define   V3_CLOCK_SRC_OPT_IFACE_A      0x18
 22 #define   V3_CLOCK_SRC_OPT_IFACE_B      0x19
 23 
 24 #define V3_OPT_IFACE_MODE_OFFSET        0x0c94
 25 #define  V3_ENABLE_OPT_IN_IFACE_A       0x00000001
 26 #define  V3_ENABLE_OPT_IN_IFACE_B       0x00000002
 27 #define  V3_ENABLE_OPT_OUT_IFACE_A      0x00000100
 28 #define  V3_ENABLE_OPT_OUT_IFACE_B      0x00000200
 29 #define  V3_NO_ADAT_OPT_IN_IFACE_A      0x00010000
 30 #define  V3_NO_ADAT_OPT_IN_IFACE_B      0x00100000
 31 #define  V3_NO_ADAT_OPT_OUT_IFACE_A     0x00040000
 32 #define  V3_NO_ADAT_OPT_OUT_IFACE_B     0x00400000
 33 
 34 #define V3_MSG_FLAG_CLK_CHANGED         0x00000002
 35 #define V3_CLK_WAIT_MSEC                4000
 36 
 37 int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
 38                                         unsigned int *rate)
 39 {
 40         __be32 reg;
 41         u32 data;
 42         int err;
 43 
 44         err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
 45                                         sizeof(reg));
 46         if (err < 0)
 47                 return err;
 48         data = be32_to_cpu(reg);
 49 
 50         data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
 51         if (data >= ARRAY_SIZE(snd_motu_clock_rates))
 52                 return -EIO;
 53 
 54         *rate = snd_motu_clock_rates[data];
 55 
 56         return 0;
 57 }
 58 
 59 int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
 60                                         unsigned int rate)
 61 {
 62         __be32 reg;
 63         u32 data;
 64         bool need_to_wait;
 65         int i, err;
 66 
 67         for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
 68                 if (snd_motu_clock_rates[i] == rate)
 69                         break;
 70         }
 71         if (i == ARRAY_SIZE(snd_motu_clock_rates))
 72                 return -EINVAL;
 73 
 74         err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
 75                                         sizeof(reg));
 76         if (err < 0)
 77                 return err;
 78         data = be32_to_cpu(reg);
 79 
 80         data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
 81         data |= i << V3_CLOCK_RATE_SHIFT;
 82 
 83         need_to_wait = data != be32_to_cpu(reg);
 84 
 85         reg = cpu_to_be32(data);
 86         err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
 87                                          sizeof(reg));
 88         if (err < 0)
 89                 return err;
 90 
 91         if (need_to_wait) {
 92                 int result;
 93 
 94                 motu->msg = 0;
 95                 result = wait_event_interruptible_timeout(motu->hwdep_wait,
 96                                         motu->msg & V3_MSG_FLAG_CLK_CHANGED,
 97                                         msecs_to_jiffies(V3_CLK_WAIT_MSEC));
 98                 if (result < 0)
 99                         return result;
100                 if (result == 0)
101                         return -ETIMEDOUT;
102         }
103 
104         return 0;
105 }
106 
107 int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
108                                           enum snd_motu_clock_source *src)
109 {
110         __be32 reg;
111         u32 data;
112         int err;
113 
114         err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
115                                         sizeof(reg));
116         if (err < 0)
117                 return err;
118         data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
119 
120         switch (data) {
121         case V3_CLOCK_SRC_INTERNAL:
122                 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
123                 break;
124         case V3_CLOCK_SRC_WORD_ON_BNC:
125                 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
126                 break;
127         case V3_CLOCK_SRC_SPH:
128                 *src = SND_MOTU_CLOCK_SOURCE_SPH;
129                 break;
130         case V3_CLOCK_SRC_AESEBU_ON_XLR:
131                 *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
132                 break;
133         case V3_CLOCK_SRC_SPDIF_ON_COAX:
134                 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
135                 break;
136         case V3_CLOCK_SRC_OPT_IFACE_A:
137         case V3_CLOCK_SRC_OPT_IFACE_B:
138         {
139                 __be32 reg;
140                 u32 options;
141 
142                 err = snd_motu_transaction_read(motu,
143                                 V3_OPT_IFACE_MODE_OFFSET, &reg, sizeof(reg));
144                 if (err < 0)
145                         return err;
146                 options = be32_to_cpu(reg);
147 
148                 if (data == V3_CLOCK_SRC_OPT_IFACE_A) {
149                         if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
150                                 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
151                         else
152                                 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
153                 } else {
154                         if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
155                                 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
156                         else
157                                 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
158                 }
159                 break;
160         }
161         default:
162                 *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
163                 break;
164         }
165 
166         return 0;
167 }
168 
169 int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
170                                               bool enable)
171 {
172         __be32 reg;
173         u32 data;
174         int err;
175 
176         err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
177                                         sizeof(reg));
178         if (err < 0)
179                 return 0;
180         data = be32_to_cpu(reg);
181 
182         if (enable)
183                 data |= V3_FETCH_PCM_FRAMES;
184         else
185                 data &= ~V3_FETCH_PCM_FRAMES;
186 
187         reg = cpu_to_be32(data);
188         return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
189                                           sizeof(reg));
190 }
191 
192 static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data)
193 {
194         if (data & V3_ENABLE_OPT_IN_IFACE_A) {
195                 if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
196                         motu->tx_packet_formats.pcm_chunks[0] += 4;
197                         motu->tx_packet_formats.pcm_chunks[1] += 4;
198                 } else {
199                         motu->tx_packet_formats.pcm_chunks[0] += 8;
200                         motu->tx_packet_formats.pcm_chunks[1] += 4;
201                 }
202         }
203 
204         if (data & V3_ENABLE_OPT_IN_IFACE_B) {
205                 if (data & V3_NO_ADAT_OPT_IN_IFACE_B) {
206                         motu->tx_packet_formats.pcm_chunks[0] += 4;
207                         motu->tx_packet_formats.pcm_chunks[1] += 4;
208                 } else {
209                         motu->tx_packet_formats.pcm_chunks[0] += 8;
210                         motu->tx_packet_formats.pcm_chunks[1] += 4;
211                 }
212         }
213 
214         if (data & V3_ENABLE_OPT_OUT_IFACE_A) {
215                 if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) {
216                         motu->rx_packet_formats.pcm_chunks[0] += 4;
217                         motu->rx_packet_formats.pcm_chunks[1] += 4;
218                 } else {
219                         motu->rx_packet_formats.pcm_chunks[0] += 8;
220                         motu->rx_packet_formats.pcm_chunks[1] += 4;
221                 }
222         }
223 
224         if (data & V3_ENABLE_OPT_OUT_IFACE_B) {
225                 if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) {
226                         motu->rx_packet_formats.pcm_chunks[0] += 4;
227                         motu->rx_packet_formats.pcm_chunks[1] += 4;
228                 } else {
229                         motu->rx_packet_formats.pcm_chunks[0] += 8;
230                         motu->rx_packet_formats.pcm_chunks[1] += 4;
231                 }
232         }
233 
234         return 0;
235 }
236 
237 int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
238 {
239         __be32 reg;
240         u32 data;
241         int err;
242 
243         motu->tx_packet_formats.pcm_byte_offset = 10;
244         motu->rx_packet_formats.pcm_byte_offset = 10;
245 
246         motu->tx_packet_formats.msg_chunks = 2;
247         motu->rx_packet_formats.msg_chunks = 2;
248 
249         err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
250                                         sizeof(reg));
251         if (err < 0)
252                 return err;
253         data = be32_to_cpu(reg);
254 
255         memcpy(motu->tx_packet_formats.pcm_chunks,
256                motu->spec->tx_fixed_pcm_chunks,
257                sizeof(motu->tx_packet_formats.pcm_chunks));
258         memcpy(motu->rx_packet_formats.pcm_chunks,
259                motu->spec->rx_fixed_pcm_chunks,
260                sizeof(motu->rx_packet_formats.pcm_chunks));
261 
262         if (motu->spec == &snd_motu_spec_828mk3_fw ||
263             motu->spec == &snd_motu_spec_828mk3_hybrid ||
264             motu->spec == &snd_motu_spec_896mk3 ||
265             motu->spec == &snd_motu_spec_traveler_mk3 ||
266             motu->spec == &snd_motu_spec_track16)
267                 return detect_packet_formats_with_opt_ifaces(motu, data);
268         else
269                 return 0;
270 }
271 
272 const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
273         .name = "828mk3",
274         .protocol_version = SND_MOTU_PROTOCOL_V3,
275         .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
276                  SND_MOTU_SPEC_TX_MIDI_3RD_Q |
277                  SND_MOTU_SPEC_COMMAND_DSP,
278         .tx_fixed_pcm_chunks = {18, 18, 14},
279         .rx_fixed_pcm_chunks = {14, 14, 10},
280 };
281 
282 const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
283         .name = "828mk3",
284         .protocol_version = SND_MOTU_PROTOCOL_V3,
285         .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
286                  SND_MOTU_SPEC_TX_MIDI_3RD_Q |
287                  SND_MOTU_SPEC_COMMAND_DSP,
288         .tx_fixed_pcm_chunks = {18, 18, 14},
289         .rx_fixed_pcm_chunks = {14, 14, 14},    // Additional 4 dummy chunks at higher rate.
290 };
291 
292 const struct snd_motu_spec snd_motu_spec_896mk3 = {
293         .name = "896mk3",
294         .protocol_version = SND_MOTU_PROTOCOL_V3,
295         .flags = SND_MOTU_SPEC_COMMAND_DSP,
296         .tx_fixed_pcm_chunks = {18, 14, 10},
297         .rx_fixed_pcm_chunks = {18, 14, 10},
298 };
299 
300 const struct snd_motu_spec snd_motu_spec_traveler_mk3 = {
301         .name = "TravelerMk3",
302         .protocol_version = SND_MOTU_PROTOCOL_V3,
303         .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
304                  SND_MOTU_SPEC_TX_MIDI_3RD_Q |
305                  SND_MOTU_SPEC_COMMAND_DSP,
306         .tx_fixed_pcm_chunks = {18, 14, 10},
307         .rx_fixed_pcm_chunks = {14, 14, 10},
308 };
309 
310 const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
311         .name = "UltraLiteMk3",
312         .protocol_version = SND_MOTU_PROTOCOL_V3,
313         .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
314                  SND_MOTU_SPEC_TX_MIDI_3RD_Q |
315                  SND_MOTU_SPEC_COMMAND_DSP,
316         .tx_fixed_pcm_chunks = {18, 14, 10},
317         .rx_fixed_pcm_chunks = {14, 14, 14},
318 };
319 
320 const struct snd_motu_spec snd_motu_spec_audio_express = {
321         .name = "AudioExpress",
322         .protocol_version = SND_MOTU_PROTOCOL_V3,
323         .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
324                  SND_MOTU_SPEC_TX_MIDI_3RD_Q |
325                  SND_MOTU_SPEC_REGISTER_DSP,
326         .tx_fixed_pcm_chunks = {10, 10, 0},
327         .rx_fixed_pcm_chunks = {10, 10, 0},
328 };
329 
330 const struct snd_motu_spec snd_motu_spec_track16 = {
331         .name = "Track16",
332         .protocol_version = SND_MOTU_PROTOCOL_V3,
333         .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
334                  SND_MOTU_SPEC_TX_MIDI_3RD_Q |
335                  SND_MOTU_SPEC_COMMAND_DSP,
336         .tx_fixed_pcm_chunks = {14, 14, 14},
337         .rx_fixed_pcm_chunks = {6, 6, 6},
338 };
339 
340 const struct snd_motu_spec snd_motu_spec_4pre = {
341         .name = "4pre",
342         .protocol_version = SND_MOTU_PROTOCOL_V3,
343         .flags = SND_MOTU_SPEC_REGISTER_DSP,
344         .tx_fixed_pcm_chunks = {10, 10, 0},
345         .rx_fixed_pcm_chunks = {10, 10, 0},
346 };
347 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php