root/sound/firewire/motu/motu-protocol-v3.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. v3_get_clock_rate
  2. v3_set_clock_rate
  3. v3_get_clock_source
  4. v3_switch_fetching_mode
  5. calculate_fixed_part
  6. calculate_differed_part
  7. v3_cache_packet_formats

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

/* [<][>][^][v][top][bottom][index][help] */