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