1 /* 2 * motu-protocol-v2.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 "motu.h" 10 11 #define V2_CLOCK_STATUS_OFFSET 0x0b14 12 #define V2_CLOCK_RATE_MASK 0x00000038 13 #define V2_CLOCK_RATE_SHIFT 3 14 #define V2_CLOCK_SRC_MASK 0x00000007 15 #define V2_CLOCK_SRC_SHIFT 0 16 17 #define V2_IN_OUT_CONF_OFFSET 0x0c04 18 #define V2_OPT_OUT_IFACE_MASK 0x00000c00 19 #define V2_OPT_OUT_IFACE_SHIFT 10 20 #define V2_OPT_IN_IFACE_MASK 0x00000300 21 #define V2_OPT_IN_IFACE_SHIFT 8 22 #define V2_OPT_IFACE_MODE_NONE 0 23 #define V2_OPT_IFACE_MODE_ADAT 1 24 #define V2_OPT_IFACE_MODE_SPDIF 2 25 26 static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) 27 { 28 __be32 reg; 29 unsigned int index; 30 int err; 31 32 err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, 33 sizeof(reg)); 34 if (err < 0) 35 return err; 36 37 index = (be32_to_cpu(reg) & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT; 38 if (index >= ARRAY_SIZE(snd_motu_clock_rates)) 39 return -EIO; 40 41 *rate = snd_motu_clock_rates[index]; 42 43 return 0; 44 } 45 46 static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) 47 { 48 __be32 reg; 49 u32 data; 50 int i; 51 int err; 52 53 for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) { 54 if (snd_motu_clock_rates[i] == rate) 55 break; 56 } 57 if (i == ARRAY_SIZE(snd_motu_clock_rates)) 58 return -EINVAL; 59 60 err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, 61 sizeof(reg)); 62 if (err < 0) 63 return err; 64 data = be32_to_cpu(reg); 65 66 data &= ~V2_CLOCK_RATE_MASK; 67 data |= i << V2_CLOCK_RATE_SHIFT; 68 69 reg = cpu_to_be32(data); 70 return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®, 71 sizeof(reg)); 72 } 73 74 static int v2_get_clock_source(struct snd_motu *motu, 75 enum snd_motu_clock_source *src) 76 { 77 __be32 reg; 78 unsigned int index; 79 int err; 80 81 err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, 82 sizeof(reg)); 83 if (err < 0) 84 return err; 85 86 index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK; 87 if (index > 5) 88 return -EIO; 89 90 /* To check the configuration of optical interface. */ 91 err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, 92 sizeof(reg)); 93 if (err < 0) 94 return err; 95 96 switch (index) { 97 case 0: 98 *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; 99 break; 100 case 1: 101 if (be32_to_cpu(reg) & 0x00000200) 102 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT; 103 else 104 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT; 105 break; 106 case 2: 107 *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; 108 break; 109 case 4: 110 *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; 111 break; 112 case 5: 113 *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; 114 break; 115 default: 116 return -EIO; 117 } 118 119 return 0; 120 } 121 122 static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable) 123 { 124 /* V2 protocol doesn't have this feature. */ 125 return 0; 126 } 127 128 static void calculate_fixed_part(struct snd_motu_packet_format *formats, 129 enum amdtp_stream_direction dir, 130 enum snd_motu_spec_flags flags, 131 unsigned char analog_ports) 132 { 133 unsigned char pcm_chunks[3] = {0, 0, 0}; 134 135 formats->msg_chunks = 2; 136 137 pcm_chunks[0] = analog_ports; 138 pcm_chunks[1] = analog_ports; 139 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) 140 pcm_chunks[2] = analog_ports; 141 142 if (dir == AMDTP_IN_STREAM) { 143 if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) { 144 pcm_chunks[0] += 2; 145 pcm_chunks[1] += 2; 146 } 147 if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) { 148 pcm_chunks[0] += 2; 149 pcm_chunks[1] += 2; 150 } 151 } else { 152 /* 153 * Packets to v2 units transfer main-out-1/2 and phone-out-1/2. 154 */ 155 pcm_chunks[0] += 4; 156 pcm_chunks[1] += 4; 157 } 158 159 /* 160 * All of v2 models have a pair of coaxial interfaces for digital in/out 161 * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these 162 * ports. 163 */ 164 pcm_chunks[0] += 2; 165 pcm_chunks[1] += 2; 166 167 /* This part should be multiples of 4. */ 168 formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2; 169 formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2; 170 if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) 171 formats->fixed_part_pcm_chunks[2] = 172 round_up(2 + pcm_chunks[2], 4) - 2; 173 } 174 175 static void calculate_differed_part(struct snd_motu_packet_format *formats, 176 enum snd_motu_spec_flags flags, 177 u32 data, u32 mask, u32 shift) 178 { 179 unsigned char pcm_chunks[3] = {0, 0}; 180 181 /* 182 * When optical interfaces are configured for S/PDIF (TOSLINK), 183 * the above PCM frames come from them, instead of coaxial 184 * interfaces. 185 */ 186 data = (data & mask) >> shift; 187 if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && 188 data == V2_OPT_IFACE_MODE_ADAT) { 189 pcm_chunks[0] += 8; 190 pcm_chunks[1] += 4; 191 } 192 193 /* At mode x4, no data chunks are supported in this part. */ 194 formats->differed_part_pcm_chunks[0] = pcm_chunks[0]; 195 formats->differed_part_pcm_chunks[1] = pcm_chunks[1]; 196 } 197 198 static int v2_cache_packet_formats(struct snd_motu *motu) 199 { 200 __be32 reg; 201 u32 data; 202 int err; 203 204 err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, 205 sizeof(reg)); 206 if (err < 0) 207 return err; 208 data = be32_to_cpu(reg); 209 210 calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM, 211 motu->spec->flags, motu->spec->analog_in_ports); 212 calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags, 213 data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT); 214 215 calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM, 216 motu->spec->flags, motu->spec->analog_out_ports); 217 calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags, 218 data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT); 219 220 motu->tx_packet_formats.pcm_byte_offset = 10; 221 motu->rx_packet_formats.pcm_byte_offset = 10; 222 223 return 0; 224 } 225 226 const struct snd_motu_protocol snd_motu_protocol_v2 = { 227 .get_clock_rate = v2_get_clock_rate, 228 .set_clock_rate = v2_set_clock_rate, 229 .get_clock_source = v2_get_clock_source, 230 .switch_fetching_mode = v2_switch_fetching_mode, 231 .cache_packet_formats = v2_cache_packet_formats, 232 }; 233