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        ff->spec->protocol->finish_session(ff);
  36        ff->spec->protocol->switch_fetching_mode(ff, false);
  37}
  38
  39static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
  40{
  41        struct fw_iso_resources *resources;
  42        enum amdtp_stream_direction dir;
  43        int err;
  44
  45        if (s == &ff->tx_stream) {
  46                resources = &ff->tx_resources;
  47                dir = AMDTP_IN_STREAM;
  48        } else {
  49                resources = &ff->rx_resources;
  50                dir = AMDTP_OUT_STREAM;
  51        }
  52
  53        err = fw_iso_resources_init(resources, ff->unit);
  54        if (err < 0)
  55                return err;
  56
  57        err = amdtp_ff_init(s, ff->unit, dir);
  58        if (err < 0)
  59                fw_iso_resources_destroy(resources);
  60
  61        return err;
  62}
  63
  64static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
  65{
  66        amdtp_stream_destroy(s);
  67
  68        if (s == &ff->tx_stream)
  69                fw_iso_resources_destroy(&ff->tx_resources);
  70        else
  71                fw_iso_resources_destroy(&ff->rx_resources);
  72}
  73
  74int snd_ff_stream_init_duplex(struct snd_ff *ff)
  75{
  76        int err;
  77
  78        err = init_stream(ff, &ff->rx_stream);
  79        if (err < 0)
  80                return err;
  81
  82        err = init_stream(ff, &ff->tx_stream);
  83        if (err < 0) {
  84                destroy_stream(ff, &ff->rx_stream);
  85                return err;
  86        }
  87
  88        err = amdtp_domain_init(&ff->domain);
  89        if (err < 0) {
  90                destroy_stream(ff, &ff->rx_stream);
  91                destroy_stream(ff, &ff->tx_stream);
  92        }
  93
  94        return err;
  95}
  96
  97/*
  98 * This function should be called before starting streams or after stopping
  99 * streams.
 100 */
 101void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
 102{
 103        amdtp_domain_destroy(&ff->domain);
 104
 105        destroy_stream(ff, &ff->rx_stream);
 106        destroy_stream(ff, &ff->tx_stream);
 107}
 108
 109int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
 110                                 unsigned int frames_per_period,
 111                                 unsigned int frames_per_buffer)
 112{
 113        unsigned int curr_rate;
 114        enum snd_ff_clock_src src;
 115        int err;
 116
 117        err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
 118        if (err < 0)
 119                return err;
 120
 121        if (ff->substreams_counter == 0 || curr_rate != rate) {
 122                enum snd_ff_stream_mode mode;
 123                int i;
 124
 125                amdtp_domain_stop(&ff->domain);
 126                finish_session(ff);
 127
 128                fw_iso_resources_free(&ff->tx_resources);
 129                fw_iso_resources_free(&ff->rx_resources);
 130
 131                for (i = 0; i < CIP_SFC_COUNT; ++i) {
 132                        if (amdtp_rate_table[i] == rate)
 133                                break;
 134                }
 135                if (i >= CIP_SFC_COUNT)
 136                        return -EINVAL;
 137
 138                err = snd_ff_stream_get_multiplier_mode(i, &mode);
 139                if (err < 0)
 140                        return err;
 141
 142                err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
 143                                        ff->spec->pcm_capture_channels[mode]);
 144                if (err < 0)
 145                        return err;
 146
 147                err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
 148                                        ff->spec->pcm_playback_channels[mode]);
 149                if (err < 0)
 150                        return err;
 151
 152                err = ff->spec->protocol->allocate_resources(ff, rate);
 153                if (err < 0)
 154                        return err;
 155
 156                err = amdtp_domain_set_events_per_period(&ff->domain,
 157                                        frames_per_period, frames_per_buffer);
 158                if (err < 0) {
 159                        fw_iso_resources_free(&ff->tx_resources);
 160                        fw_iso_resources_free(&ff->rx_resources);
 161                        return err;
 162                }
 163        }
 164
 165        return 0;
 166}
 167
 168int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
 169{
 170        int err;
 171
 172        if (ff->substreams_counter == 0)
 173                return 0;
 174
 175        if (amdtp_streaming_error(&ff->tx_stream) ||
 176            amdtp_streaming_error(&ff->rx_stream)) {
 177                amdtp_domain_stop(&ff->domain);
 178                finish_session(ff);
 179        }
 180
 181        /*
 182         * Regardless of current source of clock signal, drivers transfer some
 183         * packets. Then, the device transfers packets.
 184         */
 185        if (!amdtp_stream_running(&ff->rx_stream)) {
 186                int spd = fw_parent_device(ff->unit)->max_speed;
 187                unsigned int ir_delay_cycle;
 188
 189                err = ff->spec->protocol->begin_session(ff, rate);
 190                if (err < 0)
 191                        goto error;
 192
 193                err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
 194                                              ff->rx_resources.channel, spd);
 195                if (err < 0)
 196                        goto error;
 197
 198                err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
 199                                              ff->tx_resources.channel, spd);
 200                if (err < 0)
 201                        goto error;
 202
 203                // The device postpones start of transmission mostly for several
 204                // cycles after receiving packets firstly.
 205                if (ff->spec->protocol == &snd_ff_protocol_ff800)
 206                        ir_delay_cycle = 800;   // = 100 msec
 207                else
 208                        ir_delay_cycle = 16;    // = 2 msec
 209
 210                err = amdtp_domain_start(&ff->domain, ir_delay_cycle);
 211                if (err < 0)
 212                        goto error;
 213
 214                if (!amdtp_stream_wait_callback(&ff->rx_stream,
 215                                                CALLBACK_TIMEOUT_MS) ||
 216                    !amdtp_stream_wait_callback(&ff->tx_stream,
 217                                                CALLBACK_TIMEOUT_MS)) {
 218                        err = -ETIMEDOUT;
 219                        goto error;
 220                }
 221
 222                err = ff->spec->protocol->switch_fetching_mode(ff, true);
 223                if (err < 0)
 224                        goto error;
 225        }
 226
 227        return 0;
 228error:
 229        amdtp_domain_stop(&ff->domain);
 230        finish_session(ff);
 231
 232        return err;
 233}
 234
 235void snd_ff_stream_stop_duplex(struct snd_ff *ff)
 236{
 237        if (ff->substreams_counter == 0) {
 238                amdtp_domain_stop(&ff->domain);
 239                finish_session(ff);
 240
 241                fw_iso_resources_free(&ff->tx_resources);
 242                fw_iso_resources_free(&ff->rx_resources);
 243        }
 244}
 245
 246void snd_ff_stream_update_duplex(struct snd_ff *ff)
 247{
 248        amdtp_domain_stop(&ff->domain);
 249
 250        // The device discontinue to transfer packets.
 251        amdtp_stream_pcm_abort(&ff->tx_stream);
 252        amdtp_stream_pcm_abort(&ff->rx_stream);
 253}
 254
 255void snd_ff_stream_lock_changed(struct snd_ff *ff)
 256{
 257        ff->dev_lock_changed = true;
 258        wake_up(&ff->hwdep_wait);
 259}
 260
 261int snd_ff_stream_lock_try(struct snd_ff *ff)
 262{
 263        int err;
 264
 265        spin_lock_irq(&ff->lock);
 266
 267        /* user land lock this */
 268        if (ff->dev_lock_count < 0) {
 269                err = -EBUSY;
 270                goto end;
 271        }
 272
 273        /* this is the first time */
 274        if (ff->dev_lock_count++ == 0)
 275                snd_ff_stream_lock_changed(ff);
 276        err = 0;
 277end:
 278        spin_unlock_irq(&ff->lock);
 279        return err;
 280}
 281
 282void snd_ff_stream_lock_release(struct snd_ff *ff)
 283{
 284        spin_lock_irq(&ff->lock);
 285
 286        if (WARN_ON(ff->dev_lock_count <= 0))
 287                goto end;
 288        if (--ff->dev_lock_count == 0)
 289                snd_ff_stream_lock_changed(ff);
 290end:
 291        spin_unlock_irq(&ff->lock);
 292}
 293