linux/sound/firewire/digi00x/digi00x-stream.c
<<
>>
Prefs
   1/*
   2 * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
   3 *
   4 * Copyright (c) 2014-2015 Takashi Sakamoto
   5 *
   6 * Licensed under the terms of the GNU General Public License, version 2.
   7 */
   8
   9#include "digi00x.h"
  10
  11#define CALLBACK_TIMEOUT 500
  12
  13const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
  14        [SND_DG00X_RATE_44100] = 44100,
  15        [SND_DG00X_RATE_48000] = 48000,
  16        [SND_DG00X_RATE_88200] = 88200,
  17        [SND_DG00X_RATE_96000] = 96000,
  18};
  19
  20/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
  21const unsigned int
  22snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
  23        /* Analog/ADAT/SPDIF */
  24        [SND_DG00X_RATE_44100] = (8 + 8 + 2),
  25        [SND_DG00X_RATE_48000] = (8 + 8 + 2),
  26        /* Analog/SPDIF */
  27        [SND_DG00X_RATE_88200] = (8 + 2),
  28        [SND_DG00X_RATE_96000] = (8 + 2),
  29};
  30
  31int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
  32{
  33        u32 data;
  34        __be32 reg;
  35        int err;
  36
  37        err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
  38                                 DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
  39                                 &reg, sizeof(reg), 0);
  40        if (err < 0)
  41                return err;
  42
  43        data = be32_to_cpu(reg) & 0x0f;
  44        if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
  45                *rate = snd_dg00x_stream_rates[data];
  46        else
  47                err = -EIO;
  48
  49        return err;
  50}
  51
  52int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
  53{
  54        __be32 reg;
  55        unsigned int i;
  56
  57        for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
  58                if (rate == snd_dg00x_stream_rates[i])
  59                        break;
  60        }
  61        if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
  62                return -EINVAL;
  63
  64        reg = cpu_to_be32(i);
  65        return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
  66                                  DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
  67                                  &reg, sizeof(reg), 0);
  68}
  69
  70int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
  71                               enum snd_dg00x_clock *clock)
  72{
  73        __be32 reg;
  74        int err;
  75
  76        err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
  77                                 DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
  78                                 &reg, sizeof(reg), 0);
  79        if (err < 0)
  80                return err;
  81
  82        *clock = be32_to_cpu(reg) & 0x0f;
  83        if (*clock >= SND_DG00X_CLOCK_COUNT)
  84                err = -EIO;
  85
  86        return err;
  87}
  88
  89int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
  90{
  91        __be32 reg;
  92        int err;
  93
  94        err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
  95                                 DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
  96                                 &reg, sizeof(reg), 0);
  97        if (err >= 0)
  98                *detect = be32_to_cpu(reg) > 0;
  99
 100        return err;
 101}
 102
 103int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
 104                                       unsigned int *rate)
 105{
 106        u32 data;
 107        __be32 reg;
 108        int err;
 109
 110        err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
 111                                 DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
 112                                 &reg, sizeof(reg), 0);
 113        if (err < 0)
 114                return err;
 115
 116        data = be32_to_cpu(reg) & 0x0f;
 117        if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
 118                *rate = snd_dg00x_stream_rates[data];
 119        /* This means desync. */
 120        else
 121                err = -EBUSY;
 122
 123        return err;
 124}
 125
 126static void finish_session(struct snd_dg00x *dg00x)
 127{
 128        __be32 data = cpu_to_be32(0x00000003);
 129
 130        snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
 131                           DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
 132                           &data, sizeof(data), 0);
 133}
 134
 135static int begin_session(struct snd_dg00x *dg00x)
 136{
 137        __be32 data;
 138        u32 curr;
 139        int err;
 140
 141        err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
 142                                 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
 143                                 &data, sizeof(data), 0);
 144        if (err < 0)
 145                goto error;
 146        curr = be32_to_cpu(data);
 147
 148        if (curr == 0)
 149                curr = 2;
 150
 151        curr--;
 152        while (curr > 0) {
 153                data = cpu_to_be32(curr);
 154                err = snd_fw_transaction(dg00x->unit,
 155                                         TCODE_WRITE_QUADLET_REQUEST,
 156                                         DG00X_ADDR_BASE +
 157                                         DG00X_OFFSET_STREAMING_SET,
 158                                         &data, sizeof(data), 0);
 159                if (err < 0)
 160                        goto error;
 161
 162                msleep(20);
 163                curr--;
 164        }
 165
 166        return 0;
 167error:
 168        finish_session(dg00x);
 169        return err;
 170}
 171
 172static void release_resources(struct snd_dg00x *dg00x)
 173{
 174        __be32 data = 0;
 175
 176        /* Unregister isochronous channels for both direction. */
 177        snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
 178                           DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
 179                           &data, sizeof(data), 0);
 180
 181        /* Release isochronous resources. */
 182        fw_iso_resources_free(&dg00x->tx_resources);
 183        fw_iso_resources_free(&dg00x->rx_resources);
 184}
 185
 186static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
 187{
 188        unsigned int i;
 189        __be32 data;
 190        int err;
 191
 192        /* Check sampling rate. */
 193        for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
 194                if (snd_dg00x_stream_rates[i] == rate)
 195                        break;
 196        }
 197        if (i == SND_DG00X_RATE_COUNT)
 198                return -EINVAL;
 199
 200        /* Keep resources for out-stream. */
 201        err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
 202                                       snd_dg00x_stream_pcm_channels[i]);
 203        if (err < 0)
 204                return err;
 205        err = fw_iso_resources_allocate(&dg00x->rx_resources,
 206                                amdtp_stream_get_max_payload(&dg00x->rx_stream),
 207                                fw_parent_device(dg00x->unit)->max_speed);
 208        if (err < 0)
 209                return err;
 210
 211        /* Keep resources for in-stream. */
 212        err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
 213                                       snd_dg00x_stream_pcm_channels[i]);
 214        if (err < 0)
 215                return err;
 216        err = fw_iso_resources_allocate(&dg00x->tx_resources,
 217                                amdtp_stream_get_max_payload(&dg00x->tx_stream),
 218                                fw_parent_device(dg00x->unit)->max_speed);
 219        if (err < 0)
 220                goto error;
 221
 222        /* Register isochronous channels for both direction. */
 223        data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
 224                           dg00x->rx_resources.channel);
 225        err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
 226                                 DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
 227                                 &data, sizeof(data), 0);
 228        if (err < 0)
 229                goto error;
 230
 231        return 0;
 232error:
 233        release_resources(dg00x);
 234        return err;
 235}
 236
 237int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
 238{
 239        int err;
 240
 241        /* For out-stream. */
 242        err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
 243        if (err < 0)
 244                goto error;
 245        err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
 246        if (err < 0)
 247                goto error;
 248
 249        /* For in-stream. */
 250        err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
 251        if (err < 0)
 252                goto error;
 253        err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
 254        if (err < 0)
 255                goto error;
 256
 257        return 0;
 258error:
 259        snd_dg00x_stream_destroy_duplex(dg00x);
 260        return err;
 261}
 262
 263/*
 264 * This function should be called before starting streams or after stopping
 265 * streams.
 266 */
 267void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
 268{
 269        amdtp_stream_destroy(&dg00x->rx_stream);
 270        fw_iso_resources_destroy(&dg00x->rx_resources);
 271
 272        amdtp_stream_destroy(&dg00x->tx_stream);
 273        fw_iso_resources_destroy(&dg00x->tx_resources);
 274}
 275
 276int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
 277{
 278        unsigned int curr_rate;
 279        int err = 0;
 280
 281        if (dg00x->substreams_counter == 0)
 282                goto end;
 283
 284        /* Check current sampling rate. */
 285        err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
 286        if (err < 0)
 287                goto error;
 288        if (rate == 0)
 289                rate = curr_rate;
 290        if (curr_rate != rate ||
 291            amdtp_streaming_error(&dg00x->tx_stream) ||
 292            amdtp_streaming_error(&dg00x->rx_stream)) {
 293                finish_session(dg00x);
 294
 295                amdtp_stream_stop(&dg00x->tx_stream);
 296                amdtp_stream_stop(&dg00x->rx_stream);
 297                release_resources(dg00x);
 298        }
 299
 300        /*
 301         * No packets are transmitted without receiving packets, reagardless of
 302         * which source of clock is used.
 303         */
 304        if (!amdtp_stream_running(&dg00x->rx_stream)) {
 305                err = snd_dg00x_stream_set_local_rate(dg00x, rate);
 306                if (err < 0)
 307                        goto error;
 308
 309                err = keep_resources(dg00x, rate);
 310                if (err < 0)
 311                        goto error;
 312
 313                err = begin_session(dg00x);
 314                if (err < 0)
 315                        goto error;
 316
 317                err = amdtp_stream_start(&dg00x->rx_stream,
 318                                dg00x->rx_resources.channel,
 319                                fw_parent_device(dg00x->unit)->max_speed);
 320                if (err < 0)
 321                        goto error;
 322
 323                if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
 324                                              CALLBACK_TIMEOUT)) {
 325                        err = -ETIMEDOUT;
 326                        goto error;
 327                }
 328        }
 329
 330        /*
 331         * The value of SYT field in transmitted packets is always 0x0000. Thus,
 332         * duplex streams with timestamp synchronization cannot be built.
 333         */
 334        if (!amdtp_stream_running(&dg00x->tx_stream)) {
 335                err = amdtp_stream_start(&dg00x->tx_stream,
 336                                dg00x->tx_resources.channel,
 337                                fw_parent_device(dg00x->unit)->max_speed);
 338                if (err < 0)
 339                        goto error;
 340
 341                if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
 342                                              CALLBACK_TIMEOUT)) {
 343                        err = -ETIMEDOUT;
 344                        goto error;
 345                }
 346        }
 347end:
 348        return err;
 349error:
 350        finish_session(dg00x);
 351
 352        amdtp_stream_stop(&dg00x->tx_stream);
 353        amdtp_stream_stop(&dg00x->rx_stream);
 354        release_resources(dg00x);
 355
 356        return err;
 357}
 358
 359void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
 360{
 361        if (dg00x->substreams_counter > 0)
 362                return;
 363
 364        amdtp_stream_stop(&dg00x->tx_stream);
 365        amdtp_stream_stop(&dg00x->rx_stream);
 366        finish_session(dg00x);
 367        release_resources(dg00x);
 368
 369        /*
 370         * Just after finishing the session, the device may lost transmitting
 371         * functionality for a short time.
 372         */
 373        msleep(50);
 374}
 375
 376void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
 377{
 378        fw_iso_resources_update(&dg00x->tx_resources);
 379        fw_iso_resources_update(&dg00x->rx_resources);
 380
 381        amdtp_stream_update(&dg00x->tx_stream);
 382        amdtp_stream_update(&dg00x->rx_stream);
 383}
 384
 385void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
 386{
 387        dg00x->dev_lock_changed = true;
 388        wake_up(&dg00x->hwdep_wait);
 389}
 390
 391int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
 392{
 393        int err;
 394
 395        spin_lock_irq(&dg00x->lock);
 396
 397        /* user land lock this */
 398        if (dg00x->dev_lock_count < 0) {
 399                err = -EBUSY;
 400                goto end;
 401        }
 402
 403        /* this is the first time */
 404        if (dg00x->dev_lock_count++ == 0)
 405                snd_dg00x_stream_lock_changed(dg00x);
 406        err = 0;
 407end:
 408        spin_unlock_irq(&dg00x->lock);
 409        return err;
 410}
 411
 412void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
 413{
 414        spin_lock_irq(&dg00x->lock);
 415
 416        if (WARN_ON(dg00x->dev_lock_count <= 0))
 417                goto end;
 418        if (--dg00x->dev_lock_count == 0)
 419                snd_dg00x_stream_lock_changed(dg00x);
 420end:
 421        spin_unlock_irq(&dg00x->lock);
 422}
 423