1 /* 2 * motu-protocol-v3.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 <linux/delay.h> 10 #include "motu.h" 11 12 #define V3_CLOCK_STATUS_OFFSET 0x0b14 13 #define V3_FETCH_PCM_FRAMES 0x02000000 14 #define V3_CLOCK_RATE_MASK 0x0000ff00 15 #define V3_CLOCK_RATE_SHIFT 8 16 #define V3_CLOCK_SOURCE_MASK 0x000000ff 17 18 #define V3_OPT_IFACE_MODE_OFFSET 0x0c94 19 #define V3_ENABLE_OPT_IN_IFACE_A 0x00000001 20 #define V3_ENABLE_OPT_IN_IFACE_B 0x00000002 21 #define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100 22 #define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200 23 #define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000 24 #define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000 25 #define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000 26 #define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000 27 28 static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate) 29 { 30 __be32 reg; 31 u32 data; 32 int err; 33 34 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, 35 sizeof(reg)); 36 if (err < 0) 37 return err; 38 data = be32_to_cpu(reg); 39 40 data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT; 41 if (data >= ARRAY_SIZE(snd_motu_clock_rates)) 42 return -EIO; 43 44 *rate = snd_motu_clock_rates[data]; 45 46 return 0; 47 } 48 49 static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate) 50 { 51 __be32 reg; 52 u32 data; 53 bool need_to_wait; 54 int i, 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, V3_CLOCK_STATUS_OFFSET, ®, 64 sizeof(reg)); 65 if (err < 0) 66 return err; 67 data = be32_to_cpu(reg); 68 69 data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES); 70 data |= i << V3_CLOCK_RATE_SHIFT; 71 72 need_to_wait = data != be32_to_cpu(reg); 73 74 reg = cpu_to_be32(data); 75 err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®, 76 sizeof(reg)); 77 if (err < 0) 78 return err; 79 80 if (need_to_wait) { 81 /* Cost expensive. */ 82 if (msleep_interruptible(4000) > 0) 83 return -EINTR; 84 } 85 86 return 0; 87 } 88 89 static int v3_get_clock_source(struct snd_motu *motu, 90 enum snd_motu_clock_source *src) 91 { 92 __be32 reg; 93 u32 data; 94 unsigned int val; 95 int err; 96 97 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, 98 sizeof(reg)); 99 if (err < 0) 100 return err; 101 data = be32_to_cpu(reg); 102 103 val = data & V3_CLOCK_SOURCE_MASK; 104 if (val == 0x00) { 105 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; 106 } else if (val == 0x01) { 107 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; 108 } else if (val == 0x10) { 109 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; 110 } else if (val == 0x18 || val == 0x19) { 111 err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, 112 ®, sizeof(reg)); 113 if (err < 0) 114 return err; 115 data = be32_to_cpu(reg); 116 117 if (val == 0x18) { 118 if (data & V3_NO_ADAT_OPT_IN_IFACE_A) 119 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A; 120 else 121 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A; 122 } else { 123 if (data & V3_NO_ADAT_OPT_IN_IFACE_B) 124 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B; 125 else 126 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B; 127 } 128 } else { 129 *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; 130 } 131 132 return 0; 133 } 134 135 static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable) 136 { 137 __be32 reg; 138 u32 data; 139 int err; 140 141 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, 142 sizeof(reg)); 143 if (err < 0) 144 return 0; 145 data = be32_to_cpu(reg); 146 147 if (enable) 148 data |= V3_FETCH_PCM_FRAMES; 149 else 150 data &= ~V3_FETCH_PCM_FRAMES; 151 152 reg = cpu_to_be32(data); 153 return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®, 154 sizeof(reg)); 155 } 156 157 static void calculate_fixed_part(struct snd_motu_packet_format *formats, 158 enum amdtp_stream_direction dir, 159 enum snd_motu_spec_flags flags, 160 unsigned char analog_ports) 161 { 162 unsigned char pcm_chunks[3] = {0, 0, 0}; 163 164 formats->msg_chunks = 2; 165 166 pcm_chunks[0] = analog_ports; 167 pcm_chunks[1] = analog_ports; 168 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) 169 pcm_chunks[2] = analog_ports; 170 171 if (dir == AMDTP_IN_STREAM) { 172 if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) { 173 pcm_chunks[0] += 2; 174 pcm_chunks[1] += 2; 175 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) 176 pcm_chunks[2] += 2; 177 } 178 179 if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) { 180 pcm_chunks[0] += 2; 181 pcm_chunks[1] += 2; 182 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) 183 pcm_chunks[2] += 2; 184 } 185 186 if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) { 187 pcm_chunks[0] += 2; 188 pcm_chunks[1] += 2; 189 } 190 } else { 191 /* 192 * Packets to v2 units transfer main-out-1/2 and phone-out-1/2. 193 */ 194 pcm_chunks[0] += 4; 195 pcm_chunks[1] += 4; 196 } 197 198 /* 199 * At least, packets have two data chunks for S/PDIF on coaxial 200 * interface. 201 */ 202 pcm_chunks[0] += 2; 203 pcm_chunks[1] += 2; 204 205 /* 206 * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As 207 * a result, this part can includes empty data chunks. 208 */ 209 formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2; 210 formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2; 211 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) 212 formats->fixed_part_pcm_chunks[2] = 213 round_up(2 + pcm_chunks[2], 4) - 2; 214 } 215 216 static void calculate_differed_part(struct snd_motu_packet_format *formats, 217 enum snd_motu_spec_flags flags, u32 data, 218 u32 a_enable_mask, u32 a_no_adat_mask, 219 u32 b_enable_mask, u32 b_no_adat_mask) 220 { 221 unsigned char pcm_chunks[3] = {0, 0, 0}; 222 int i; 223 224 if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) { 225 if (data & a_no_adat_mask) { 226 /* 227 * Additional two data chunks for S/PDIF on optical 228 * interface A. This includes empty data chunks. 229 */ 230 pcm_chunks[0] += 4; 231 pcm_chunks[1] += 4; 232 } else { 233 /* 234 * Additional data chunks for ADAT on optical interface 235 * A. 236 */ 237 pcm_chunks[0] += 8; 238 pcm_chunks[1] += 4; 239 } 240 } 241 242 if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) { 243 if (data & b_no_adat_mask) { 244 /* 245 * Additional two data chunks for S/PDIF on optical 246 * interface B. This includes empty data chunks. 247 */ 248 pcm_chunks[0] += 4; 249 pcm_chunks[1] += 4; 250 } else { 251 /* 252 * Additional data chunks for ADAT on optical interface 253 * B. 254 */ 255 pcm_chunks[0] += 8; 256 pcm_chunks[1] += 4; 257 } 258 } 259 260 for (i = 0; i < 3; ++i) { 261 if (pcm_chunks[i] > 0) 262 pcm_chunks[i] = round_up(pcm_chunks[i], 4); 263 264 formats->differed_part_pcm_chunks[i] = pcm_chunks[i]; 265 } 266 } 267 268 static int v3_cache_packet_formats(struct snd_motu *motu) 269 { 270 __be32 reg; 271 u32 data; 272 int err; 273 274 err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, ®, 275 sizeof(reg)); 276 if (err < 0) 277 return err; 278 data = be32_to_cpu(reg); 279 280 calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM, 281 motu->spec->flags, motu->spec->analog_in_ports); 282 calculate_differed_part(&motu->tx_packet_formats, 283 motu->spec->flags, data, 284 V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A, 285 V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B); 286 287 calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM, 288 motu->spec->flags, motu->spec->analog_out_ports); 289 calculate_differed_part(&motu->rx_packet_formats, 290 motu->spec->flags, data, 291 V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A, 292 V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B); 293 294 motu->tx_packet_formats.pcm_byte_offset = 10; 295 motu->rx_packet_formats.pcm_byte_offset = 10; 296 297 return 0; 298 } 299 300 const struct snd_motu_protocol snd_motu_protocol_v3 = { 301 .get_clock_rate = v3_get_clock_rate, 302 .set_clock_rate = v3_set_clock_rate, 303 .get_clock_source = v3_get_clock_source, 304 .switch_fetching_mode = v3_switch_fetching_mode, 305 .cache_packet_formats = v3_cache_packet_formats, 306 }; 307