linux/sound/firewire/motu/motu-stream.c
<<
>>
Prefs
   1/*
   2 * motu-stream.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 CALLBACK_TIMEOUT        200
  12
  13#define ISOC_COMM_CONTROL_OFFSET                0x0b00
  14#define  ISOC_COMM_CONTROL_MASK                 0xffff0000
  15#define  CHANGE_RX_ISOC_COMM_STATE              0x80000000
  16#define  RX_ISOC_COMM_IS_ACTIVATED              0x40000000
  17#define  RX_ISOC_COMM_CHANNEL_MASK              0x3f000000
  18#define  RX_ISOC_COMM_CHANNEL_SHIFT             24
  19#define  CHANGE_TX_ISOC_COMM_STATE              0x00800000
  20#define  TX_ISOC_COMM_IS_ACTIVATED              0x00400000
  21#define  TX_ISOC_COMM_CHANNEL_MASK              0x003f0000
  22#define  TX_ISOC_COMM_CHANNEL_SHIFT             16
  23
  24#define PACKET_FORMAT_OFFSET                    0x0b10
  25#define  TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
  26#define  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
  27#define  TX_PACKET_TRANSMISSION_SPEED_MASK      0x0000000f
  28
  29static int start_both_streams(struct snd_motu *motu, unsigned int rate)
  30{
  31        unsigned int midi_ports = 0;
  32        __be32 reg;
  33        u32 data;
  34        int err;
  35
  36        if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI)
  37                midi_ports = 1;
  38
  39        /* Set packet formation to our packet streaming engine. */
  40        err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
  41                                        &motu->rx_packet_formats);
  42        if (err < 0)
  43                return err;
  44
  45        err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
  46                                        &motu->tx_packet_formats);
  47        if (err < 0)
  48                return err;
  49
  50        /* Get isochronous resources on the bus. */
  51        err = fw_iso_resources_allocate(&motu->rx_resources,
  52                                amdtp_stream_get_max_payload(&motu->rx_stream),
  53                                fw_parent_device(motu->unit)->max_speed);
  54        if (err < 0)
  55                return err;
  56
  57        err = fw_iso_resources_allocate(&motu->tx_resources,
  58                                amdtp_stream_get_max_payload(&motu->tx_stream),
  59                                fw_parent_device(motu->unit)->max_speed);
  60        if (err < 0)
  61                return err;
  62
  63        /* Configure the unit to start isochronous communication. */
  64        err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
  65                                        sizeof(reg));
  66        if (err < 0)
  67                return err;
  68        data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
  69
  70        data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
  71                (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
  72                CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
  73                (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
  74
  75        reg = cpu_to_be32(data);
  76        return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
  77                                          sizeof(reg));
  78}
  79
  80static void stop_both_streams(struct snd_motu *motu)
  81{
  82        __be32 reg;
  83        u32 data;
  84        int err;
  85
  86        err = motu->spec->protocol->switch_fetching_mode(motu, false);
  87        if (err < 0)
  88                return;
  89
  90        err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
  91                                        sizeof(reg));
  92        if (err < 0)
  93                return;
  94        data = be32_to_cpu(reg);
  95
  96        data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
  97        data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
  98
  99        reg = cpu_to_be32(data);
 100        snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
 101                                   sizeof(reg));
 102
 103        fw_iso_resources_free(&motu->tx_resources);
 104        fw_iso_resources_free(&motu->rx_resources);
 105}
 106
 107static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
 108{
 109        struct fw_iso_resources *resources;
 110        int err;
 111
 112        if (stream == &motu->rx_stream)
 113                resources = &motu->rx_resources;
 114        else
 115                resources = &motu->tx_resources;
 116
 117        err = amdtp_stream_start(stream, resources->channel,
 118                                 fw_parent_device(motu->unit)->max_speed);
 119        if (err < 0)
 120                return err;
 121
 122        if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
 123                amdtp_stream_stop(stream);
 124                fw_iso_resources_free(resources);
 125                return -ETIMEDOUT;
 126        }
 127
 128        return 0;
 129}
 130
 131static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
 132{
 133        struct fw_iso_resources *resources;
 134
 135        if (stream == &motu->rx_stream)
 136                resources = &motu->rx_resources;
 137        else
 138                resources = &motu->tx_resources;
 139
 140        amdtp_stream_stop(stream);
 141        fw_iso_resources_free(resources);
 142}
 143
 144static int ensure_packet_formats(struct snd_motu *motu)
 145{
 146        __be32 reg;
 147        u32 data;
 148        int err;
 149
 150        err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
 151                                        sizeof(reg));
 152        if (err < 0)
 153                return err;
 154        data = be32_to_cpu(reg);
 155
 156        data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
 157                  RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
 158                  TX_PACKET_TRANSMISSION_SPEED_MASK);
 159        if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
 160                data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
 161        if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
 162                data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
 163        data |= fw_parent_device(motu->unit)->max_speed;
 164
 165        reg = cpu_to_be32(data);
 166        return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
 167                                          sizeof(reg));
 168}
 169
 170int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
 171{
 172        const struct snd_motu_protocol *protocol = motu->spec->protocol;
 173        unsigned int curr_rate;
 174        int err = 0;
 175
 176        if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
 177                return 0;
 178
 179        /* Some packet queueing errors. */
 180        if (amdtp_streaming_error(&motu->rx_stream) ||
 181            amdtp_streaming_error(&motu->tx_stream)) {
 182                amdtp_stream_stop(&motu->rx_stream);
 183                amdtp_stream_stop(&motu->tx_stream);
 184                stop_both_streams(motu);
 185        }
 186
 187        err = protocol->cache_packet_formats(motu);
 188        if (err < 0)
 189                return err;
 190
 191        /* Stop stream if rate is different. */
 192        err = protocol->get_clock_rate(motu, &curr_rate);
 193        if (err < 0) {
 194                dev_err(&motu->unit->device,
 195                        "fail to get sampling rate: %d\n", err);
 196                return err;
 197        }
 198        if (rate == 0)
 199                rate = curr_rate;
 200        if (rate != curr_rate) {
 201                amdtp_stream_stop(&motu->rx_stream);
 202                amdtp_stream_stop(&motu->tx_stream);
 203                stop_both_streams(motu);
 204        }
 205
 206        if (!amdtp_stream_running(&motu->rx_stream)) {
 207                err = protocol->set_clock_rate(motu, rate);
 208                if (err < 0) {
 209                        dev_err(&motu->unit->device,
 210                                "fail to set sampling rate: %d\n", err);
 211                        return err;
 212                }
 213
 214                err = ensure_packet_formats(motu);
 215                if (err < 0)
 216                        return err;
 217
 218                err = start_both_streams(motu, rate);
 219                if (err < 0) {
 220                        dev_err(&motu->unit->device,
 221                                "fail to start isochronous comm: %d\n", err);
 222                        stop_both_streams(motu);
 223                        return err;
 224                }
 225
 226                err = start_isoc_ctx(motu, &motu->rx_stream);
 227                if (err < 0) {
 228                        dev_err(&motu->unit->device,
 229                                "fail to start IT context: %d\n", err);
 230                        stop_both_streams(motu);
 231                        return err;
 232                }
 233
 234                err = protocol->switch_fetching_mode(motu, true);
 235                if (err < 0) {
 236                        dev_err(&motu->unit->device,
 237                                "fail to enable frame fetching: %d\n", err);
 238                        stop_both_streams(motu);
 239                        return err;
 240                }
 241        }
 242
 243        if (!amdtp_stream_running(&motu->tx_stream) &&
 244            motu->capture_substreams > 0) {
 245                err = start_isoc_ctx(motu, &motu->tx_stream);
 246                if (err < 0) {
 247                        dev_err(&motu->unit->device,
 248                                "fail to start IR context: %d", err);
 249                        amdtp_stream_stop(&motu->rx_stream);
 250                        stop_both_streams(motu);
 251                        return err;
 252                }
 253        }
 254
 255        return 0;
 256}
 257
 258void snd_motu_stream_stop_duplex(struct snd_motu *motu)
 259{
 260        if (motu->capture_substreams == 0) {
 261                if (amdtp_stream_running(&motu->tx_stream))
 262                        stop_isoc_ctx(motu, &motu->tx_stream);
 263
 264                if (motu->playback_substreams == 0) {
 265                        if (amdtp_stream_running(&motu->rx_stream))
 266                                stop_isoc_ctx(motu, &motu->rx_stream);
 267                        stop_both_streams(motu);
 268                }
 269        }
 270}
 271
 272static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
 273{
 274        int err;
 275        struct amdtp_stream *stream;
 276        struct fw_iso_resources *resources;
 277
 278        if (dir == AMDTP_IN_STREAM) {
 279                stream = &motu->tx_stream;
 280                resources = &motu->tx_resources;
 281        } else {
 282                stream = &motu->rx_stream;
 283                resources = &motu->rx_resources;
 284        }
 285
 286        err = fw_iso_resources_init(resources, motu->unit);
 287        if (err < 0)
 288                return err;
 289
 290        err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
 291        if (err < 0) {
 292                amdtp_stream_destroy(stream);
 293                fw_iso_resources_destroy(resources);
 294        }
 295
 296        return err;
 297}
 298
 299static void destroy_stream(struct snd_motu *motu,
 300                           enum amdtp_stream_direction dir)
 301{
 302        struct amdtp_stream *stream;
 303        struct fw_iso_resources *resources;
 304
 305        if (dir == AMDTP_IN_STREAM) {
 306                stream = &motu->tx_stream;
 307                resources = &motu->tx_resources;
 308        } else {
 309                stream = &motu->rx_stream;
 310                resources = &motu->rx_resources;
 311        }
 312
 313        amdtp_stream_destroy(stream);
 314        fw_iso_resources_free(resources);
 315}
 316
 317int snd_motu_stream_init_duplex(struct snd_motu *motu)
 318{
 319        int err;
 320
 321        err = init_stream(motu, AMDTP_IN_STREAM);
 322        if (err < 0)
 323                return err;
 324
 325        err = init_stream(motu, AMDTP_OUT_STREAM);
 326        if (err < 0)
 327                destroy_stream(motu, AMDTP_IN_STREAM);
 328
 329        return err;
 330}
 331
 332/*
 333 * This function should be called before starting streams or after stopping
 334 * streams.
 335 */
 336void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
 337{
 338        destroy_stream(motu, AMDTP_IN_STREAM);
 339        destroy_stream(motu, AMDTP_OUT_STREAM);
 340
 341        motu->playback_substreams = 0;
 342        motu->capture_substreams = 0;
 343}
 344
 345static void motu_lock_changed(struct snd_motu *motu)
 346{
 347        motu->dev_lock_changed = true;
 348        wake_up(&motu->hwdep_wait);
 349}
 350
 351int snd_motu_stream_lock_try(struct snd_motu *motu)
 352{
 353        int err;
 354
 355        spin_lock_irq(&motu->lock);
 356
 357        if (motu->dev_lock_count < 0) {
 358                err = -EBUSY;
 359                goto out;
 360        }
 361
 362        if (motu->dev_lock_count++ == 0)
 363                motu_lock_changed(motu);
 364        err = 0;
 365out:
 366        spin_unlock_irq(&motu->lock);
 367        return err;
 368}
 369
 370void snd_motu_stream_lock_release(struct snd_motu *motu)
 371{
 372        spin_lock_irq(&motu->lock);
 373
 374        if (WARN_ON(motu->dev_lock_count <= 0))
 375                goto out;
 376
 377        if (--motu->dev_lock_count == 0)
 378                motu_lock_changed(motu);
 379out:
 380        spin_unlock_irq(&motu->lock);
 381}
 382