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

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

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ 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-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_SRC_AESEBU_ON_XLR            0x07    // In Traveler.
 16 #define   V2_CLOCK_SRC_ADAT_ON_DSUB             0x05
 17 #define   V2_CLOCK_SRC_WORD_ON_BNC              0x04
 18 #define   V2_CLOCK_SRC_SPH                      0x03
 19 #define   V2_CLOCK_SRC_SPDIF                    0x02    // on either coaxial or optical. AES/EBU in 896HD.
 20 #define   V2_CLOCK_SRC_ADAT_ON_OPT              0x01
 21 #define   V2_CLOCK_SRC_INTERNAL                 0x00
 22 #define  V2_CLOCK_FETCH_ENABLE                  0x02000000
 23 #define  V2_CLOCK_MODEL_SPECIFIC                0x04000000
 24 
 25 #define V2_IN_OUT_CONF_OFFSET                   0x0c04
 26 #define  V2_OPT_OUT_IFACE_MASK                  0x00000c00
 27 #define  V2_OPT_OUT_IFACE_SHIFT                 10
 28 #define  V2_OPT_IN_IFACE_MASK                   0x00000300
 29 #define  V2_OPT_IN_IFACE_SHIFT                  8
 30 #define  V2_OPT_IFACE_MODE_NONE                 0
 31 #define  V2_OPT_IFACE_MODE_ADAT                 1
 32 #define  V2_OPT_IFACE_MODE_SPDIF                2
 33 
 34 static int get_clock_rate(u32 data, unsigned int *rate)
 35 {
 36         unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
 37         if (index >= ARRAY_SIZE(snd_motu_clock_rates))
 38                 return -EIO;
 39 
 40         *rate = snd_motu_clock_rates[index];
 41 
 42         return 0;
 43 }
 44 
 45 int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
 46                                         unsigned int *rate)
 47 {
 48         __be32 reg;
 49         int err;
 50 
 51         err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
 52                                         sizeof(reg));
 53         if (err < 0)
 54                 return err;
 55 
 56         return get_clock_rate(be32_to_cpu(reg), rate);
 57 }
 58 
 59 int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
 60                                         unsigned int rate)
 61 {
 62         __be32 reg;
 63         u32 data;
 64         int i;
 65         int 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, V2_CLOCK_STATUS_OFFSET, &reg,
 75                                         sizeof(reg));
 76         if (err < 0)
 77                 return err;
 78         data = be32_to_cpu(reg);
 79 
 80         data &= ~V2_CLOCK_RATE_MASK;
 81         data |= i << V2_CLOCK_RATE_SHIFT;
 82 
 83         reg = cpu_to_be32(data);
 84         return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
 85                                           sizeof(reg));
 86 }
 87 
 88 static int get_clock_source(struct snd_motu *motu, u32 data,
 89                             enum snd_motu_clock_source *src)
 90 {
 91         switch (data & V2_CLOCK_SRC_MASK) {
 92         case V2_CLOCK_SRC_INTERNAL:
 93                 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
 94                 break;
 95         case V2_CLOCK_SRC_ADAT_ON_OPT:
 96                 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
 97                 break;
 98         case V2_CLOCK_SRC_SPDIF:
 99         {
100                 bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||
101                                                 motu->spec == &snd_motu_spec_traveler);
102 
103                 if (motu->spec == &snd_motu_spec_896hd) {
104                         *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
105                 } else if (!support_iec60958_on_opt) {
106                         *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
107                 } else {
108                         __be32 reg;
109 
110                         // To check the configuration of optical interface.
111                         int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
112                                                             sizeof(reg));
113                         if (err < 0)
114                                 return err;
115 
116                         if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
117                             V2_OPT_IFACE_MODE_SPDIF)
118                                 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
119                         else
120                                 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
121                 }
122                 break;
123         }
124         case V2_CLOCK_SRC_SPH:
125                 *src = SND_MOTU_CLOCK_SOURCE_SPH;
126                 break;
127         case V2_CLOCK_SRC_WORD_ON_BNC:
128                 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
129                 break;
130         case V2_CLOCK_SRC_ADAT_ON_DSUB:
131                 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
132                 break;
133         case V2_CLOCK_SRC_AESEBU_ON_XLR:
134                 // For Traveler.
135                 *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
136                 break;
137         default:
138                 *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
139                 break;
140         }
141 
142         return 0;
143 }
144 
145 int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
146                                           enum snd_motu_clock_source *src)
147 {
148         __be32 reg;
149         int err;
150 
151         err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
152                                         sizeof(reg));
153         if (err < 0)
154                 return err;
155 
156         return get_clock_source(motu, be32_to_cpu(reg), src);
157 }
158 
159 // Expected for Traveler, which implements Altera Cyclone EP1C3.
160 static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
161                                         bool enable)
162 {
163         *data |= V2_CLOCK_MODEL_SPECIFIC;
164 
165         return 0;
166 }
167 
168 // For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
169 static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
170                                         bool enable)
171 {
172         unsigned int rate;
173         enum snd_motu_clock_source src;
174         int err;
175 
176         err = get_clock_source(motu, *data, &src);
177         if (err < 0)
178                 return err;
179 
180         err = get_clock_rate(*data, &rate);
181         if (err < 0)
182                 return err;
183 
184         if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
185                 *data |= V2_CLOCK_MODEL_SPECIFIC;
186 
187         return 0;
188 }
189 
190 int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
191                                               bool enable)
192 {
193         if (motu->spec == &snd_motu_spec_828mk2) {
194                 // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
195                 return 0;
196         } else if (motu->spec == &snd_motu_spec_896hd) {
197                 // 896HD implements Altera Cyclone EP1C3 but nothing to do.
198                 return 0;
199         } else {
200                 __be32 reg;
201                 u32 data;
202                 int err;
203 
204                 err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
205                                                 &reg, sizeof(reg));
206                 if (err < 0)
207                         return err;
208                 data = be32_to_cpu(reg);
209 
210                 data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
211                 if (enable)
212                         data |= V2_CLOCK_FETCH_ENABLE;
213 
214                 if (motu->spec == &snd_motu_spec_traveler)
215                         err = switch_fetching_mode_cyclone(motu, &data, enable);
216                 else
217                         err = switch_fetching_mode_spartan(motu, &data, enable);
218                 if (err < 0)
219                         return err;
220 
221                 reg = cpu_to_be32(data);
222                 return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
223                                                   &reg, sizeof(reg));
224         }
225 }
226 
227 int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
228 {
229         bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);
230         __be32 reg;
231         u32 data;
232         int err;
233 
234         motu->tx_packet_formats.pcm_byte_offset = 10;
235         motu->rx_packet_formats.pcm_byte_offset = 10;
236 
237         motu->tx_packet_formats.msg_chunks = 2;
238         motu->rx_packet_formats.msg_chunks = 2;
239 
240         err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
241                                         sizeof(reg));
242         if (err < 0)
243                 return err;
244         data = be32_to_cpu(reg);
245 
246         memcpy(motu->tx_packet_formats.pcm_chunks,
247                motu->spec->tx_fixed_pcm_chunks,
248                sizeof(motu->tx_packet_formats.pcm_chunks));
249         memcpy(motu->rx_packet_formats.pcm_chunks,
250                motu->spec->rx_fixed_pcm_chunks,
251                sizeof(motu->rx_packet_formats.pcm_chunks));
252 
253         if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
254                 motu->tx_packet_formats.pcm_chunks[0] += 8;
255 
256                 if (!has_two_opt_ifaces)
257                         motu->tx_packet_formats.pcm_chunks[1] += 4;
258                 else
259                         motu->tx_packet_formats.pcm_chunks[1] += 8;
260         }
261 
262         if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
263                 motu->rx_packet_formats.pcm_chunks[0] += 8;
264 
265                 if (!has_two_opt_ifaces)
266                         motu->rx_packet_formats.pcm_chunks[1] += 4;
267                 else
268                         motu->rx_packet_formats.pcm_chunks[1] += 8;
269         }
270 
271         return 0;
272 }
273 
274 const struct snd_motu_spec snd_motu_spec_828mk2 = {
275         .name = "828mk2",
276         .protocol_version = SND_MOTU_PROTOCOL_V2,
277         .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
278                  SND_MOTU_SPEC_TX_MIDI_2ND_Q |
279                  SND_MOTU_SPEC_REGISTER_DSP,
280         .tx_fixed_pcm_chunks = {14, 14, 0},
281         .rx_fixed_pcm_chunks = {14, 14, 0},
282 };
283 
284 const struct snd_motu_spec snd_motu_spec_896hd = {
285         .name = "896HD",
286         .protocol_version = SND_MOTU_PROTOCOL_V2,
287         .flags = SND_MOTU_SPEC_REGISTER_DSP,
288         .tx_fixed_pcm_chunks = {14, 14, 8},
289         .rx_fixed_pcm_chunks = {14, 14, 8},
290 };
291 
292 const struct snd_motu_spec snd_motu_spec_traveler = {
293         .name = "Traveler",
294         .protocol_version = SND_MOTU_PROTOCOL_V2,
295         .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
296                  SND_MOTU_SPEC_TX_MIDI_2ND_Q |
297                  SND_MOTU_SPEC_REGISTER_DSP,
298         .tx_fixed_pcm_chunks = {14, 14, 8},
299         .rx_fixed_pcm_chunks = {14, 14, 8},
300 };
301 
302 const struct snd_motu_spec snd_motu_spec_ultralite = {
303         .name = "UltraLite",
304         .protocol_version = SND_MOTU_PROTOCOL_V2,
305         .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
306                  SND_MOTU_SPEC_TX_MIDI_2ND_Q |
307                  SND_MOTU_SPEC_REGISTER_DSP,
308         .tx_fixed_pcm_chunks = {14, 14, 0},
309         .rx_fixed_pcm_chunks = {14, 14, 0},
310 };
311 
312 const struct snd_motu_spec snd_motu_spec_8pre = {
313         .name = "8pre",
314         .protocol_version = SND_MOTU_PROTOCOL_V2,
315         .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
316                  SND_MOTU_SPEC_TX_MIDI_2ND_Q |
317                  SND_MOTU_SPEC_REGISTER_DSP,
318         // Two dummy chunks always in the end of data block.
319         .tx_fixed_pcm_chunks = {10, 10, 0},
320         .rx_fixed_pcm_chunks = {6, 6, 0},
321 };
322 

~ [ 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