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 READY_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
 188                err = ff->spec->protocol->begin_session(ff, rate);
 189                if (err < 0)
 190                        goto error;
 191
 192                err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
 193                                              ff->rx_resources.channel, spd);
 194                if (err < 0)
 195                        goto error;
 196
 197                err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
 198                                              ff->tx_resources.channel, spd);
 199                if (err < 0)
 200                        goto error;
 201
 202                // NOTE: The device doesn't transfer packets unless receiving any packet. The
 203                // sequence of tx packets includes cycle skip corresponding to empty packet or
 204                // NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per
 205                // packet is important for media clock recovery.
 206                err = amdtp_domain_start(&ff->domain, 0, true, true);
 207                if (err < 0)
 208                        goto error;
 209
 210                if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) {
 211                        err = -ETIMEDOUT;
 212                        goto error;
 213                }
 214
 215                err = ff->spec->protocol->switch_fetching_mode(ff, true);
 216                if (err < 0)
 217                        goto error;
 218        }
 219
 220        return 0;
 221error:
 222        amdtp_domain_stop(&ff->domain);
 223        finish_session(ff);
 224
 225        return err;
 226}
 227
 228void snd_ff_stream_stop_duplex(struct snd_ff *ff)
 229{
 230        if (ff->substreams_counter == 0) {
 231                amdtp_domain_stop(&ff->domain);
 232                finish_session(ff);
 233
 234                fw_iso_resources_free(&ff->tx_resources);
 235                fw_iso_resources_free(&ff->rx_resources);
 236        }
 237}
 238
 239void snd_ff_stream_update_duplex(struct snd_ff *ff)
 240{
 241        amdtp_domain_stop(&ff->domain);
 242
 243        // The device discontinue to transfer packets.
 244        amdtp_stream_pcm_abort(&ff->tx_stream);
 245        amdtp_stream_pcm_abort(&ff->rx_stream);
 246}
 247
 248void snd_ff_stream_lock_changed(struct snd_ff *ff)
 249{
 250        ff->dev_lock_changed = true;
 251        wake_up(&ff->hwdep_wait);
 252}
 253
 254int snd_ff_stream_lock_try(struct snd_ff *ff)
 255{
 256        int err;
 257
 258        spin_lock_irq(&ff->lock);
 259
 260        /* user land lock this */
 261        if (ff->dev_lock_count < 0) {
 262                err = -EBUSY;
 263                goto end;
 264        }
 265
 266        /* this is the first time */
 267        if (ff->dev_lock_count++ == 0)
 268                snd_ff_stream_lock_changed(ff);
 269        err = 0;
 270end:
 271        spin_unlock_irq(&ff->lock);
 272        return err;
 273}
 274
 275void snd_ff_stream_lock_release(struct snd_ff *ff)
 276{
 277        spin_lock_irq(&ff->lock);
 278
 279        if (WARN_ON(ff->dev_lock_count <= 0))
 280                goto end;
 281        if (--ff->dev_lock_count == 0)
 282                snd_ff_stream_lock_changed(ff);
 283end:
 284        spin_unlock_irq(&ff->lock);
 285}
 286