linux/sound/firewire/fireworks/fireworks_stream.c
<<
>>
Prefs
   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
  11static 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
  64static 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.
  93static 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
 103static int
 104check_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
 127int 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
 158static 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
 184int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
 185                                  unsigned int frames_per_period,
 186                                  unsigned int frames_per_buffer)
 187{
 188        unsigned int curr_rate;
 189        int err;
 190
 191        // Considering JACK/FFADO streaming:
 192        // TODO: This can be removed hwdep functionality becomes popular.
 193        err = check_connection_used_by_others(efw, &efw->rx_stream);
 194        if (err < 0)
 195                return err;
 196
 197        // stop streams if rate is different.
 198        err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
 199        if (err < 0)
 200                return err;
 201        if (rate == 0)
 202                rate = curr_rate;
 203        if (rate != curr_rate) {
 204                amdtp_domain_stop(&efw->domain);
 205
 206                cmp_connection_break(&efw->out_conn);
 207                cmp_connection_break(&efw->in_conn);
 208
 209                cmp_connection_release(&efw->out_conn);
 210                cmp_connection_release(&efw->in_conn);
 211        }
 212
 213        if (efw->substreams_counter == 0 || rate != curr_rate) {
 214                unsigned int mode;
 215
 216                err = snd_efw_command_set_sampling_rate(efw, rate);
 217                if (err < 0)
 218                        return err;
 219
 220                err = snd_efw_get_multiplier_mode(rate, &mode);
 221                if (err < 0)
 222                        return err;
 223
 224                err = keep_resources(efw, &efw->tx_stream, rate, mode);
 225                if (err < 0)
 226                        return err;
 227
 228                err = keep_resources(efw, &efw->rx_stream, rate, mode);
 229                if (err < 0) {
 230                        cmp_connection_release(&efw->in_conn);
 231                        return err;
 232                }
 233
 234                err = amdtp_domain_set_events_per_period(&efw->domain,
 235                                        frames_per_period, frames_per_buffer);
 236                if (err < 0) {
 237                        cmp_connection_release(&efw->in_conn);
 238                        cmp_connection_release(&efw->out_conn);
 239                        return err;
 240                }
 241        }
 242
 243        return 0;
 244}
 245
 246int snd_efw_stream_start_duplex(struct snd_efw *efw)
 247{
 248        unsigned int rate;
 249        int err = 0;
 250
 251        // Need no substreams.
 252        if (efw->substreams_counter == 0)
 253                return -EIO;
 254
 255        if (amdtp_streaming_error(&efw->rx_stream) ||
 256            amdtp_streaming_error(&efw->tx_stream)) {
 257                amdtp_domain_stop(&efw->domain);
 258                cmp_connection_break(&efw->out_conn);
 259                cmp_connection_break(&efw->in_conn);
 260        }
 261
 262        err = snd_efw_command_get_sampling_rate(efw, &rate);
 263        if (err < 0)
 264                return err;
 265
 266        if (!amdtp_stream_running(&efw->rx_stream)) {
 267                err = start_stream(efw, &efw->rx_stream, rate);
 268                if (err < 0)
 269                        goto error;
 270
 271                err = start_stream(efw, &efw->tx_stream, rate);
 272                if (err < 0)
 273                        goto error;
 274
 275                err = amdtp_domain_start(&efw->domain, 0);
 276                if (err < 0)
 277                        goto error;
 278
 279                // Wait first callback.
 280                if (!amdtp_stream_wait_callback(&efw->rx_stream,
 281                                                CALLBACK_TIMEOUT) ||
 282                    !amdtp_stream_wait_callback(&efw->tx_stream,
 283                                                CALLBACK_TIMEOUT)) {
 284                        err = -ETIMEDOUT;
 285                        goto error;
 286                }
 287        }
 288
 289        return 0;
 290error:
 291        amdtp_domain_stop(&efw->domain);
 292
 293        cmp_connection_break(&efw->out_conn);
 294        cmp_connection_break(&efw->in_conn);
 295
 296        return err;
 297}
 298
 299void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 300{
 301        if (efw->substreams_counter == 0) {
 302                amdtp_domain_stop(&efw->domain);
 303
 304                cmp_connection_break(&efw->out_conn);
 305                cmp_connection_break(&efw->in_conn);
 306
 307                cmp_connection_release(&efw->out_conn);
 308                cmp_connection_release(&efw->in_conn);
 309        }
 310}
 311
 312void snd_efw_stream_update_duplex(struct snd_efw *efw)
 313{
 314        amdtp_domain_stop(&efw->domain);
 315
 316        cmp_connection_break(&efw->out_conn);
 317        cmp_connection_break(&efw->in_conn);
 318
 319        amdtp_stream_pcm_abort(&efw->rx_stream);
 320        amdtp_stream_pcm_abort(&efw->tx_stream);
 321}
 322
 323void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
 324{
 325        amdtp_domain_destroy(&efw->domain);
 326
 327        destroy_stream(efw, &efw->rx_stream);
 328        destroy_stream(efw, &efw->tx_stream);
 329}
 330
 331void snd_efw_stream_lock_changed(struct snd_efw *efw)
 332{
 333        efw->dev_lock_changed = true;
 334        wake_up(&efw->hwdep_wait);
 335}
 336
 337int snd_efw_stream_lock_try(struct snd_efw *efw)
 338{
 339        int err;
 340
 341        spin_lock_irq(&efw->lock);
 342
 343        /* user land lock this */
 344        if (efw->dev_lock_count < 0) {
 345                err = -EBUSY;
 346                goto end;
 347        }
 348
 349        /* this is the first time */
 350        if (efw->dev_lock_count++ == 0)
 351                snd_efw_stream_lock_changed(efw);
 352        err = 0;
 353end:
 354        spin_unlock_irq(&efw->lock);
 355        return err;
 356}
 357
 358void snd_efw_stream_lock_release(struct snd_efw *efw)
 359{
 360        spin_lock_irq(&efw->lock);
 361
 362        if (WARN_ON(efw->dev_lock_count <= 0))
 363                goto end;
 364        if (--efw->dev_lock_count == 0)
 365                snd_efw_stream_lock_changed(efw);
 366end:
 367        spin_unlock_irq(&efw->lock);
 368}
 369