linux/sound/firewire/fireface/ff-stream.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ff-stream.c - a part of driver for RME Fireface series
   4 *
   5 * Copyright (c) 2015-2017 Takashi Sakamoto
   6 */
   7
   8#include "ff.h"
   9
  10#define CALLBACK_TIMEOUT_MS     200
  11
  12int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
  13                                      enum snd_ff_stream_mode *mode)
  14{
  15        static const enum snd_ff_stream_mode modes[] = {
  16                [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW,
  17                [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW,
  18                [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW,
  19                [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID,
  20                [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID,
  21                [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH,
  22                [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH,
  23        };
  24
  25        if (sfc >= CIP_SFC_COUNT)
  26                return -EINVAL;
  27
  28        *mode = modes[sfc];
  29
  30        return 0;
  31}
  32
  33static inline void finish_session(struct snd_ff *ff)
  34{
  35        amdtp_stream_stop(&ff->tx_stream);
  36        amdtp_stream_stop(&ff->rx_stream);
  37
  38        ff->spec->protocol->finish_session(ff);
  39        ff->spec->protocol->switch_fetching_mode(ff, false);
  40}
  41
  42static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
  43{
  44        int err;
  45        struct fw_iso_resources *resources;
  46        struct amdtp_stream *stream;
  47
  48        if (dir == AMDTP_IN_STREAM) {
  49                resources = &ff->tx_resources;
  50                stream = &ff->tx_stream;
  51        } else {
  52                resources = &ff->rx_resources;
  53                stream = &ff->rx_stream;
  54        }
  55
  56        err = fw_iso_resources_init(resources, ff->unit);
  57        if (err < 0)
  58                return err;
  59
  60        err = amdtp_ff_init(stream, ff->unit, dir);
  61        if (err < 0)
  62                fw_iso_resources_destroy(resources);
  63
  64        return err;
  65}
  66
  67static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
  68{
  69        if (dir == AMDTP_IN_STREAM) {
  70                amdtp_stream_destroy(&ff->tx_stream);
  71                fw_iso_resources_destroy(&ff->tx_resources);
  72        } else {
  73                amdtp_stream_destroy(&ff->rx_stream);
  74                fw_iso_resources_destroy(&ff->rx_resources);
  75        }
  76}
  77
  78int snd_ff_stream_init_duplex(struct snd_ff *ff)
  79{
  80        int err;
  81
  82        err = init_stream(ff, AMDTP_OUT_STREAM);
  83        if (err < 0)
  84                goto end;
  85
  86        err = init_stream(ff, AMDTP_IN_STREAM);
  87        if (err < 0)
  88                destroy_stream(ff, AMDTP_OUT_STREAM);
  89end:
  90        return err;
  91}
  92
  93/*
  94 * This function should be called before starting streams or after stopping
  95 * streams.
  96 */
  97void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
  98{
  99        destroy_stream(ff, AMDTP_IN_STREAM);
 100        destroy_stream(ff, AMDTP_OUT_STREAM);
 101}
 102
 103int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
 104{
 105        unsigned int curr_rate;
 106        enum snd_ff_clock_src src;
 107        int err;
 108
 109        err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
 110        if (err < 0)
 111                return err;
 112
 113        if (ff->substreams_counter == 0 || curr_rate != rate) {
 114                enum snd_ff_stream_mode mode;
 115                int i;
 116
 117                finish_session(ff);
 118
 119                fw_iso_resources_free(&ff->tx_resources);
 120                fw_iso_resources_free(&ff->rx_resources);
 121
 122                for (i = 0; i < CIP_SFC_COUNT; ++i) {
 123                        if (amdtp_rate_table[i] == rate)
 124                                break;
 125                }
 126                if (i >= CIP_SFC_COUNT)
 127                        return -EINVAL;
 128
 129                err = snd_ff_stream_get_multiplier_mode(i, &mode);
 130                if (err < 0)
 131                        return err;
 132
 133                err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
 134                                        ff->spec->pcm_capture_channels[mode]);
 135                if (err < 0)
 136                        return err;
 137
 138                err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
 139                                        ff->spec->pcm_playback_channels[mode]);
 140                if (err < 0)
 141                        return err;
 142
 143                err = ff->spec->protocol->allocate_resources(ff, rate);
 144                if (err < 0)
 145                        return err;
 146        }
 147
 148        return 0;
 149}
 150
 151int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
 152{
 153        int err;
 154
 155        if (ff->substreams_counter == 0)
 156                return 0;
 157
 158        if (amdtp_streaming_error(&ff->tx_stream) ||
 159            amdtp_streaming_error(&ff->rx_stream))
 160                finish_session(ff);
 161
 162        /*
 163         * Regardless of current source of clock signal, drivers transfer some
 164         * packets. Then, the device transfers packets.
 165         */
 166        if (!amdtp_stream_running(&ff->rx_stream)) {
 167                err = ff->spec->protocol->begin_session(ff, rate);
 168                if (err < 0)
 169                        goto error;
 170
 171                err = amdtp_stream_start(&ff->rx_stream,
 172                                         ff->rx_resources.channel,
 173                                         fw_parent_device(ff->unit)->max_speed);
 174                if (err < 0)
 175                        goto error;
 176
 177                if (!amdtp_stream_wait_callback(&ff->rx_stream,
 178                                                CALLBACK_TIMEOUT_MS)) {
 179                        err = -ETIMEDOUT;
 180                        goto error;
 181                }
 182
 183                err = ff->spec->protocol->switch_fetching_mode(ff, true);
 184                if (err < 0)
 185                        goto error;
 186        }
 187
 188        if (!amdtp_stream_running(&ff->tx_stream)) {
 189                err = amdtp_stream_start(&ff->tx_stream,
 190                                         ff->tx_resources.channel,
 191                                         fw_parent_device(ff->unit)->max_speed);
 192                if (err < 0)
 193                        goto error;
 194
 195                if (!amdtp_stream_wait_callback(&ff->tx_stream,
 196                                                CALLBACK_TIMEOUT_MS)) {
 197                        err = -ETIMEDOUT;
 198                        goto error;
 199                }
 200        }
 201
 202        return 0;
 203error:
 204        finish_session(ff);
 205
 206        return err;
 207}
 208
 209void snd_ff_stream_stop_duplex(struct snd_ff *ff)
 210{
 211        if (ff->substreams_counter == 0) {
 212                finish_session(ff);
 213
 214                fw_iso_resources_free(&ff->tx_resources);
 215                fw_iso_resources_free(&ff->rx_resources);
 216        }
 217}
 218
 219void snd_ff_stream_update_duplex(struct snd_ff *ff)
 220{
 221        // The device discontinue to transfer packets.
 222        amdtp_stream_pcm_abort(&ff->tx_stream);
 223        amdtp_stream_stop(&ff->tx_stream);
 224
 225        amdtp_stream_pcm_abort(&ff->rx_stream);
 226        amdtp_stream_stop(&ff->rx_stream);
 227}
 228
 229void snd_ff_stream_lock_changed(struct snd_ff *ff)
 230{
 231        ff->dev_lock_changed = true;
 232        wake_up(&ff->hwdep_wait);
 233}
 234
 235int snd_ff_stream_lock_try(struct snd_ff *ff)
 236{
 237        int err;
 238
 239        spin_lock_irq(&ff->lock);
 240
 241        /* user land lock this */
 242        if (ff->dev_lock_count < 0) {
 243                err = -EBUSY;
 244                goto end;
 245        }
 246
 247        /* this is the first time */
 248        if (ff->dev_lock_count++ == 0)
 249                snd_ff_stream_lock_changed(ff);
 250        err = 0;
 251end:
 252        spin_unlock_irq(&ff->lock);
 253        return err;
 254}
 255
 256void snd_ff_stream_lock_release(struct snd_ff *ff)
 257{
 258        spin_lock_irq(&ff->lock);
 259
 260        if (WARN_ON(ff->dev_lock_count <= 0))
 261                goto end;
 262        if (--ff->dev_lock_count == 0)
 263                snd_ff_stream_lock_changed(ff);
 264end:
 265        spin_unlock_irq(&ff->lock);
 266}
 267