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 17 #define V3_OPT_IFACE_MODE_OFFSET 0x0c94 18 #define V3_ENABLE_OPT_IN_IFACE_A 0x00000001 19 #define V3_ENABLE_OPT_IN_IFACE_B 0x00000002 20 #define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100 21 #define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200 22 #define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000 23 #define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000 24 #define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000 25 #define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000 26 27 int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu, 28 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 int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu, 50 unsigned int rate) 51 { 52 __be32 reg; 53 u32 data; 54 bool need_to_wait; 55 int i, err; 56 57 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { 58 if (snd_motu_clock_rates[i] == rate) 59 break; 60 } 61 if (i == ARRAY_SIZE(snd_motu_clock_rates)) 62 return -EINVAL; 63 64 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, 65 sizeof(reg)); 66 if (err < 0) 67 return err; 68 data = be32_to_cpu(reg); 69 70 data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES); 71 data |= i << V3_CLOCK_RATE_SHIFT; 72 73 need_to_wait = data != be32_to_cpu(reg); 74 75 reg = cpu_to_be32(data); 76 err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®, 77 sizeof(reg)); 78 if (err < 0) 79 return err; 80 81 if (need_to_wait) { 82 /* Cost expensive. */ 83 if (msleep_interruptible(4000) > 0) 84 return -EINTR; 85 } 86 87 return 0; 88 } 89 90 static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data, 91 enum snd_motu_clock_source *src) 92 { 93 switch (data) { 94 case 0x00: 95 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; 96 break; 97 case 0x01: 98 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; 99 break; 100 case 0x02: 101 *src = SND_MOTU_CLOCK_SOURCE_SPH; 102 break; 103 case 0x10: 104 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; 105 break; 106 case 0x18: 107 case 0x19: 108 { 109 __be32 reg; 110 u32 options; 111 int err; 112 113 err = snd_motu_transaction_read(motu, 114 V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg)); 115 if (err < 0) 116 return err; 117 options = be32_to_cpu(reg); 118 119 if (data == 0x18) { 120 if (options & V3_NO_ADAT_OPT_IN_IFACE_A) 121 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A; 122 else 123 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A; 124 } else { 125 if (options & V3_NO_ADAT_OPT_IN_IFACE_B) 126 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B; 127 else 128 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B; 129 } 130 131 break; 132 } 133 default: 134 *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; 135 break; 136 } 137 138 return 0; 139 } 140 141 static int v3_detect_clock_source(struct snd_motu *motu, u32 data, 142 enum snd_motu_clock_source *src) 143 { 144 switch (data) { 145 case 0x00: 146 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; 147 break; 148 case 0x01: 149 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; 150 break; 151 case 0x02: 152 *src = SND_MOTU_CLOCK_SOURCE_SPH; 153 break; 154 case 0x10: 155 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; 156 break; 157 default: 158 *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; 159 break; 160 } 161 162 return 0; 163 } 164 165 int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, 166 enum snd_motu_clock_source *src) 167 { 168 __be32 reg; 169 u32 data; 170 int err; 171 172 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, 173 sizeof(reg)); 174 if (err < 0) 175 return err; 176 data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK; 177 178 if (motu->spec == &snd_motu_spec_828mk3) 179 return detect_clock_source_828mk3(motu, data, src); 180 else 181 return v3_detect_clock_source(motu, data, src); 182 } 183 184 int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, 185 bool enable) 186 { 187 __be32 reg; 188 u32 data; 189 int err; 190 191 err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, 192 sizeof(reg)); 193 if (err < 0) 194 return 0; 195 data = be32_to_cpu(reg); 196 197 if (enable) 198 data |= V3_FETCH_PCM_FRAMES; 199 else 200 data &= ~V3_FETCH_PCM_FRAMES; 201 202 reg = cpu_to_be32(data); 203 return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, ®, 204 sizeof(reg)); 205 } 206 207 static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data) 208 { 209 if (data & V3_ENABLE_OPT_IN_IFACE_A) { 210 if (data & V3_NO_ADAT_OPT_IN_IFACE_A) { 211 motu->tx_packet_formats.pcm_chunks[0] += 4; 212 motu->tx_packet_formats.pcm_chunks[1] += 4; 213 } else { 214 motu->tx_packet_formats.pcm_chunks[0] += 8; 215 motu->tx_packet_formats.pcm_chunks[1] += 4; 216 } 217 } 218 219 if (data & V3_ENABLE_OPT_IN_IFACE_B) { 220 if (data & V3_NO_ADAT_OPT_IN_IFACE_B) { 221 motu->tx_packet_formats.pcm_chunks[0] += 4; 222 motu->tx_packet_formats.pcm_chunks[1] += 4; 223 } else { 224 motu->tx_packet_formats.pcm_chunks[0] += 8; 225 motu->tx_packet_formats.pcm_chunks[1] += 4; 226 } 227 } 228 229 if (data & V3_ENABLE_OPT_OUT_IFACE_A) { 230 if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) { 231 motu->rx_packet_formats.pcm_chunks[0] += 4; 232 motu->rx_packet_formats.pcm_chunks[1] += 4; 233 } else { 234 motu->rx_packet_formats.pcm_chunks[0] += 8; 235 motu->rx_packet_formats.pcm_chunks[1] += 4; 236 } 237 } 238 239 if (data & V3_ENABLE_OPT_OUT_IFACE_B) { 240 if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) { 241 motu->rx_packet_formats.pcm_chunks[0] += 4; 242 motu->rx_packet_formats.pcm_chunks[1] += 4; 243 } else { 244 motu->rx_packet_formats.pcm_chunks[0] += 8; 245 motu->rx_packet_formats.pcm_chunks[1] += 4; 246 } 247 } 248 249 return 0; 250 } 251 252 int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) 253 { 254 __be32 reg; 255 u32 data; 256 int err; 257 258 motu->tx_packet_formats.pcm_byte_offset = 10; 259 motu->rx_packet_formats.pcm_byte_offset = 10; 260 261 motu->tx_packet_formats.msg_chunks = 2; 262 motu->rx_packet_formats.msg_chunks = 2; 263 264 err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, ®, 265 sizeof(reg)); 266 if (err < 0) 267 return err; 268 data = be32_to_cpu(reg); 269 270 memcpy(motu->tx_packet_formats.pcm_chunks, 271 motu->spec->tx_fixed_pcm_chunks, 272 sizeof(motu->tx_packet_formats.pcm_chunks)); 273 memcpy(motu->rx_packet_formats.pcm_chunks, 274 motu->spec->rx_fixed_pcm_chunks, 275 sizeof(motu->rx_packet_formats.pcm_chunks)); 276 277 if (motu->spec == &snd_motu_spec_828mk3) 278 return detect_packet_formats_828mk3(motu, data); 279 else 280 return 0; 281 } 282 283 284 const struct snd_motu_spec snd_motu_spec_828mk3 = { 285 .name = "828mk3", 286 .protocol_version = SND_MOTU_PROTOCOL_V3, 287 .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | 288 SND_MOTU_SPEC_TX_MIDI_3RD_Q, 289 .tx_fixed_pcm_chunks = {18, 18, 14}, 290 .rx_fixed_pcm_chunks = {14, 14, 10}, 291 }; 292 293 const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = { 294 .name = "UltraLiteMk3", 295 .protocol_version = SND_MOTU_PROTOCOL_V3, 296 .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | 297 SND_MOTU_SPEC_TX_MIDI_3RD_Q, 298 .tx_fixed_pcm_chunks = {18, 14, 10}, 299 .rx_fixed_pcm_chunks = {14, 14, 14}, 300 }; 301 302 const struct snd_motu_spec snd_motu_spec_audio_express = { 303 .name = "AudioExpress", 304 .protocol_version = SND_MOTU_PROTOCOL_V3, 305 .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | 306 SND_MOTU_SPEC_TX_MIDI_3RD_Q, 307 .tx_fixed_pcm_chunks = {10, 10, 0}, 308 .rx_fixed_pcm_chunks = {10, 10, 0}, 309 }; 310 311 const struct snd_motu_spec snd_motu_spec_4pre = { 312 .name = "4pre", 313 .protocol_version = SND_MOTU_PROTOCOL_V3, 314 .tx_fixed_pcm_chunks = {10, 10, 0}, 315 .rx_fixed_pcm_chunks = {10, 10, 0}, 316 }; 317