root/sound/firewire/fireworks/fireworks_stream.c

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

DEFINITIONS

This source file includes following definitions.
  1. init_stream
  2. start_stream
  3. destroy_stream
  4. check_connection_used_by_others
  5. snd_efw_stream_init_duplex
  6. keep_resources
  7. snd_efw_stream_reserve_duplex
  8. snd_efw_stream_start_duplex
  9. snd_efw_stream_stop_duplex
  10. snd_efw_stream_update_duplex
  11. snd_efw_stream_destroy_duplex
  12. snd_efw_stream_lock_changed
  13. snd_efw_stream_lock_try
  14. snd_efw_stream_lock_release

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * fireworks_stream.c - a part of driver for Fireworks based devices
   4  *
   5  * Copyright (c) 2013-2014 Takashi Sakamoto
   6  */
   7 #include "./fireworks.h"
   8 
   9 #define CALLBACK_TIMEOUT        100
  10 
  11 static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
  12 {
  13         struct cmp_connection *conn;
  14         enum cmp_direction c_dir;
  15         enum amdtp_stream_direction s_dir;
  16         int err;
  17 
  18         if (stream == &efw->tx_stream) {
  19                 conn = &efw->out_conn;
  20                 c_dir = CMP_OUTPUT;
  21                 s_dir = AMDTP_IN_STREAM;
  22         } else {
  23                 conn = &efw->in_conn;
  24                 c_dir = CMP_INPUT;
  25                 s_dir = AMDTP_OUT_STREAM;
  26         }
  27 
  28         err = cmp_connection_init(conn, efw->unit, c_dir, 0);
  29         if (err < 0)
  30                 return err;
  31 
  32         err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
  33         if (err < 0) {
  34                 amdtp_stream_destroy(stream);
  35                 cmp_connection_destroy(conn);
  36                 return err;
  37         }
  38 
  39         if (stream == &efw->tx_stream) {
  40                 // Fireworks transmits NODATA packets with TAG0.
  41                 efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
  42                 // Fireworks has its own meaning for dbc.
  43                 efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
  44                 // Fireworks reset dbc at bus reset.
  45                 efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
  46                 // But Recent firmwares starts packets with non-zero dbc.
  47                 // Driver version 5.7.6 installs firmware version 5.7.3.
  48                 if (efw->is_fireworks3 &&
  49                     (efw->firmware_version == 0x5070000 ||
  50                      efw->firmware_version == 0x5070300 ||
  51                      efw->firmware_version == 0x5080000))
  52                         efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
  53                 // AudioFire9 always reports wrong dbs.
  54                 if (efw->is_af9)
  55                         efw->tx_stream.flags |= CIP_WRONG_DBS;
  56                 // Firmware version 5.5 reports fixed interval for dbc.
  57                 if (efw->firmware_version == 0x5050000)
  58                         efw->tx_stream.ctx_data.tx.dbc_interval = 8;
  59         }
  60 
  61         return err;
  62 }
  63 
  64 static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
  65                         unsigned int rate)
  66 {
  67         struct cmp_connection *conn;
  68         int err;
  69 
  70         if (stream == &efw->tx_stream)
  71                 conn = &efw->out_conn;
  72         else
  73                 conn = &efw->in_conn;
  74 
  75         // Establish connection via CMP.
  76         err = cmp_connection_establish(conn);
  77         if (err < 0)
  78                 return err;
  79 
  80         // Start amdtp stream.
  81         err = amdtp_domain_add_stream(&efw->domain, stream,
  82                                       conn->resources.channel, conn->speed);
  83         if (err < 0) {
  84                 cmp_connection_break(conn);
  85                 return err;
  86         }
  87 
  88         return 0;
  89 }
  90 
  91 // This function should be called before starting the stream or after stopping
  92 // the streams.
  93 static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
  94 {
  95         amdtp_stream_destroy(stream);
  96 
  97         if (stream == &efw->tx_stream)
  98                 cmp_connection_destroy(&efw->out_conn);
  99         else
 100                 cmp_connection_destroy(&efw->in_conn);
 101 }
 102 
 103 static int
 104 check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
 105 {
 106         struct cmp_connection *conn;
 107         bool used;
 108         int err;
 109 
 110         if (s == &efw->tx_stream)
 111                 conn = &efw->out_conn;
 112         else
 113                 conn = &efw->in_conn;
 114 
 115         err = cmp_connection_check_used(conn, &used);
 116         if ((err >= 0) && used && !amdtp_stream_running(s)) {
 117                 dev_err(&efw->unit->device,
 118                         "Connection established by others: %cPCR[%d]\n",
 119                         (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
 120                         conn->pcr_index);
 121                 err = -EBUSY;
 122         }
 123 
 124         return err;
 125 }
 126 
 127 int snd_efw_stream_init_duplex(struct snd_efw *efw)
 128 {
 129         int err;
 130 
 131         err = init_stream(efw, &efw->tx_stream);
 132         if (err < 0)
 133                 return err;
 134 
 135         err = init_stream(efw, &efw->rx_stream);
 136         if (err < 0) {
 137                 destroy_stream(efw, &efw->tx_stream);
 138                 return err;
 139         }
 140 
 141         err = amdtp_domain_init(&efw->domain);
 142         if (err < 0) {
 143                 destroy_stream(efw, &efw->tx_stream);
 144                 destroy_stream(efw, &efw->rx_stream);
 145                 return err;
 146         }
 147 
 148         // set IEC61883 compliant mode (actually not fully compliant...).
 149         err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
 150         if (err < 0) {
 151                 destroy_stream(efw, &efw->tx_stream);
 152                 destroy_stream(efw, &efw->rx_stream);
 153         }
 154 
 155         return err;
 156 }
 157 
 158 static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
 159                           unsigned int rate, unsigned int mode)
 160 {
 161         unsigned int pcm_channels;
 162         unsigned int midi_ports;
 163         struct cmp_connection *conn;
 164         int err;
 165 
 166         if (stream == &efw->tx_stream) {
 167                 pcm_channels = efw->pcm_capture_channels[mode];
 168                 midi_ports = efw->midi_out_ports;
 169                 conn = &efw->out_conn;
 170         } else {
 171                 pcm_channels = efw->pcm_playback_channels[mode];
 172                 midi_ports = efw->midi_in_ports;
 173                 conn = &efw->in_conn;
 174         }
 175 
 176         err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
 177                                          midi_ports, false);
 178         if (err < 0)
 179                 return err;
 180 
 181         return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
 182 }
 183 
 184 int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
 185 {
 186         unsigned int curr_rate;
 187         int err;
 188 
 189         // Considering JACK/FFADO streaming:
 190         // TODO: This can be removed hwdep functionality becomes popular.
 191         err = check_connection_used_by_others(efw, &efw->rx_stream);
 192         if (err < 0)
 193                 return err;
 194 
 195         // stop streams if rate is different.
 196         err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
 197         if (err < 0)
 198                 return err;
 199         if (rate == 0)
 200                 rate = curr_rate;
 201         if (rate != curr_rate) {
 202                 amdtp_domain_stop(&efw->domain);
 203 
 204                 cmp_connection_break(&efw->out_conn);
 205                 cmp_connection_break(&efw->in_conn);
 206 
 207                 cmp_connection_release(&efw->out_conn);
 208                 cmp_connection_release(&efw->in_conn);
 209         }
 210 
 211         if (efw->substreams_counter == 0 || rate != curr_rate) {
 212                 unsigned int mode;
 213 
 214                 err = snd_efw_command_set_sampling_rate(efw, rate);
 215                 if (err < 0)
 216                         return err;
 217 
 218                 err = snd_efw_get_multiplier_mode(rate, &mode);
 219                 if (err < 0)
 220                         return err;
 221 
 222                 err = keep_resources(efw, &efw->tx_stream, rate, mode);
 223                 if (err < 0)
 224                         return err;
 225 
 226                 err = keep_resources(efw, &efw->rx_stream, rate, mode);
 227                 if (err < 0) {
 228                         cmp_connection_release(&efw->in_conn);
 229                         return err;
 230                 }
 231         }
 232 
 233         return 0;
 234 }
 235 
 236 int snd_efw_stream_start_duplex(struct snd_efw *efw)
 237 {
 238         unsigned int rate;
 239         int err = 0;
 240 
 241         // Need no substreams.
 242         if (efw->substreams_counter == 0)
 243                 return -EIO;
 244 
 245         if (amdtp_streaming_error(&efw->rx_stream) ||
 246             amdtp_streaming_error(&efw->tx_stream)) {
 247                 amdtp_domain_stop(&efw->domain);
 248                 cmp_connection_break(&efw->out_conn);
 249                 cmp_connection_break(&efw->in_conn);
 250         }
 251 
 252         err = snd_efw_command_get_sampling_rate(efw, &rate);
 253         if (err < 0)
 254                 return err;
 255 
 256         if (!amdtp_stream_running(&efw->rx_stream)) {
 257                 err = start_stream(efw, &efw->rx_stream, rate);
 258                 if (err < 0)
 259                         goto error;
 260 
 261                 err = start_stream(efw, &efw->tx_stream, rate);
 262                 if (err < 0)
 263                         goto error;
 264 
 265                 err = amdtp_domain_start(&efw->domain);
 266                 if (err < 0)
 267                         goto error;
 268 
 269                 // Wait first callback.
 270                 if (!amdtp_stream_wait_callback(&efw->rx_stream,
 271                                                 CALLBACK_TIMEOUT) ||
 272                     !amdtp_stream_wait_callback(&efw->tx_stream,
 273                                                 CALLBACK_TIMEOUT)) {
 274                         err = -ETIMEDOUT;
 275                         goto error;
 276                 }
 277         }
 278 
 279         return 0;
 280 error:
 281         amdtp_domain_stop(&efw->domain);
 282 
 283         cmp_connection_break(&efw->out_conn);
 284         cmp_connection_break(&efw->in_conn);
 285 
 286         return err;
 287 }
 288 
 289 void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 290 {
 291         if (efw->substreams_counter == 0) {
 292                 amdtp_domain_stop(&efw->domain);
 293 
 294                 cmp_connection_break(&efw->out_conn);
 295                 cmp_connection_break(&efw->in_conn);
 296 
 297                 cmp_connection_release(&efw->out_conn);
 298                 cmp_connection_release(&efw->in_conn);
 299         }
 300 }
 301 
 302 void snd_efw_stream_update_duplex(struct snd_efw *efw)
 303 {
 304         amdtp_domain_stop(&efw->domain);
 305 
 306         cmp_connection_break(&efw->out_conn);
 307         cmp_connection_break(&efw->in_conn);
 308 
 309         amdtp_stream_pcm_abort(&efw->rx_stream);
 310         amdtp_stream_pcm_abort(&efw->tx_stream);
 311 }
 312 
 313 void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
 314 {
 315         amdtp_domain_destroy(&efw->domain);
 316 
 317         destroy_stream(efw, &efw->rx_stream);
 318         destroy_stream(efw, &efw->tx_stream);
 319 }
 320 
 321 void snd_efw_stream_lock_changed(struct snd_efw *efw)
 322 {
 323         efw->dev_lock_changed = true;
 324         wake_up(&efw->hwdep_wait);
 325 }
 326 
 327 int snd_efw_stream_lock_try(struct snd_efw *efw)
 328 {
 329         int err;
 330 
 331         spin_lock_irq(&efw->lock);
 332 
 333         /* user land lock this */
 334         if (efw->dev_lock_count < 0) {
 335                 err = -EBUSY;
 336                 goto end;
 337         }
 338 
 339         /* this is the first time */
 340         if (efw->dev_lock_count++ == 0)
 341                 snd_efw_stream_lock_changed(efw);
 342         err = 0;
 343 end:
 344         spin_unlock_irq(&efw->lock);
 345         return err;
 346 }
 347 
 348 void snd_efw_stream_lock_release(struct snd_efw *efw)
 349 {
 350         spin_lock_irq(&efw->lock);
 351 
 352         if (WARN_ON(efw->dev_lock_count <= 0))
 353                 goto end;
 354         if (--efw->dev_lock_count == 0)
 355                 snd_efw_stream_lock_changed(efw);
 356 end:
 357         spin_unlock_irq(&efw->lock);
 358 }

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