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