root/sound/firewire/fireface/ff-stream.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_ff_stream_get_multiplier_mode
  2. finish_session
  3. init_stream
  4. destroy_stream
  5. snd_ff_stream_init_duplex
  6. snd_ff_stream_destroy_duplex
  7. snd_ff_stream_reserve_duplex
  8. snd_ff_stream_start_duplex
  9. snd_ff_stream_stop_duplex
  10. snd_ff_stream_update_duplex
  11. snd_ff_stream_lock_changed
  12. snd_ff_stream_lock_try
  13. snd_ff_stream_lock_release

   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 
  12 int 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 
  33 static 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 
  39 static 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 
  64 static 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 
  74 int 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  */
 101 void 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 
 109 int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
 110 {
 111         unsigned int curr_rate;
 112         enum snd_ff_clock_src src;
 113         int err;
 114 
 115         err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
 116         if (err < 0)
 117                 return err;
 118 
 119         if (ff->substreams_counter == 0 || curr_rate != rate) {
 120                 enum snd_ff_stream_mode mode;
 121                 int i;
 122 
 123                 amdtp_domain_stop(&ff->domain);
 124                 finish_session(ff);
 125 
 126                 fw_iso_resources_free(&ff->tx_resources);
 127                 fw_iso_resources_free(&ff->rx_resources);
 128 
 129                 for (i = 0; i < CIP_SFC_COUNT; ++i) {
 130                         if (amdtp_rate_table[i] == rate)
 131                                 break;
 132                 }
 133                 if (i >= CIP_SFC_COUNT)
 134                         return -EINVAL;
 135 
 136                 err = snd_ff_stream_get_multiplier_mode(i, &mode);
 137                 if (err < 0)
 138                         return err;
 139 
 140                 err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
 141                                         ff->spec->pcm_capture_channels[mode]);
 142                 if (err < 0)
 143                         return err;
 144 
 145                 err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
 146                                         ff->spec->pcm_playback_channels[mode]);
 147                 if (err < 0)
 148                         return err;
 149 
 150                 err = ff->spec->protocol->allocate_resources(ff, rate);
 151                 if (err < 0)
 152                         return err;
 153         }
 154 
 155         return 0;
 156 }
 157 
 158 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
 159 {
 160         int err;
 161 
 162         if (ff->substreams_counter == 0)
 163                 return 0;
 164 
 165         if (amdtp_streaming_error(&ff->tx_stream) ||
 166             amdtp_streaming_error(&ff->rx_stream)) {
 167                 amdtp_domain_stop(&ff->domain);
 168                 finish_session(ff);
 169         }
 170 
 171         /*
 172          * Regardless of current source of clock signal, drivers transfer some
 173          * packets. Then, the device transfers packets.
 174          */
 175         if (!amdtp_stream_running(&ff->rx_stream)) {
 176                 int spd = fw_parent_device(ff->unit)->max_speed;
 177 
 178                 err = ff->spec->protocol->begin_session(ff, rate);
 179                 if (err < 0)
 180                         goto error;
 181 
 182                 err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
 183                                               ff->rx_resources.channel, spd);
 184                 if (err < 0)
 185                         goto error;
 186 
 187                 err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
 188                                               ff->tx_resources.channel, spd);
 189                 if (err < 0)
 190                         goto error;
 191 
 192                 err = amdtp_domain_start(&ff->domain);
 193                 if (err < 0)
 194                         goto error;
 195 
 196                 if (!amdtp_stream_wait_callback(&ff->rx_stream,
 197                                                 CALLBACK_TIMEOUT_MS) ||
 198                     !amdtp_stream_wait_callback(&ff->tx_stream,
 199                                                 CALLBACK_TIMEOUT_MS)) {
 200                         err = -ETIMEDOUT;
 201                         goto error;
 202                 }
 203 
 204                 err = ff->spec->protocol->switch_fetching_mode(ff, true);
 205                 if (err < 0)
 206                         goto error;
 207         }
 208 
 209         return 0;
 210 error:
 211         amdtp_domain_stop(&ff->domain);
 212         finish_session(ff);
 213 
 214         return err;
 215 }
 216 
 217 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
 218 {
 219         if (ff->substreams_counter == 0) {
 220                 amdtp_domain_stop(&ff->domain);
 221                 finish_session(ff);
 222 
 223                 fw_iso_resources_free(&ff->tx_resources);
 224                 fw_iso_resources_free(&ff->rx_resources);
 225         }
 226 }
 227 
 228 void snd_ff_stream_update_duplex(struct snd_ff *ff)
 229 {
 230         amdtp_domain_stop(&ff->domain);
 231 
 232         // The device discontinue to transfer packets.
 233         amdtp_stream_pcm_abort(&ff->tx_stream);
 234         amdtp_stream_pcm_abort(&ff->rx_stream);
 235 }
 236 
 237 void snd_ff_stream_lock_changed(struct snd_ff *ff)
 238 {
 239         ff->dev_lock_changed = true;
 240         wake_up(&ff->hwdep_wait);
 241 }
 242 
 243 int snd_ff_stream_lock_try(struct snd_ff *ff)
 244 {
 245         int err;
 246 
 247         spin_lock_irq(&ff->lock);
 248 
 249         /* user land lock this */
 250         if (ff->dev_lock_count < 0) {
 251                 err = -EBUSY;
 252                 goto end;
 253         }
 254 
 255         /* this is the first time */
 256         if (ff->dev_lock_count++ == 0)
 257                 snd_ff_stream_lock_changed(ff);
 258         err = 0;
 259 end:
 260         spin_unlock_irq(&ff->lock);
 261         return err;
 262 }
 263 
 264 void snd_ff_stream_lock_release(struct snd_ff *ff)
 265 {
 266         spin_lock_irq(&ff->lock);
 267 
 268         if (WARN_ON(ff->dev_lock_count <= 0))
 269                 goto end;
 270         if (--ff->dev_lock_count == 0)
 271                 snd_ff_stream_lock_changed(ff);
 272 end:
 273         spin_unlock_irq(&ff->lock);
 274 }

/* [<][>][^][v][top][bottom][index][help] */