linux/sound/firewire/tascam/tascam-stream.c
<<
>>
Prefs
   1/*
   2 * tascam-stream.c - a part of driver for TASCAM FireWire series
   3 *
   4 * Copyright (c) 2015 Takashi Sakamoto
   5 *
   6 * Licensed under the terms of the GNU General Public License, version 2.
   7 */
   8
   9#include <linux/delay.h>
  10#include "tascam.h"
  11
  12#define CALLBACK_TIMEOUT 500
  13
  14static int get_clock(struct snd_tscm *tscm, u32 *data)
  15{
  16        __be32 reg;
  17        int err;
  18
  19        err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
  20                                 TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
  21                                 &reg, sizeof(reg), 0);
  22        if (err >= 0)
  23                *data = be32_to_cpu(reg);
  24
  25        return err;
  26}
  27
  28static int set_clock(struct snd_tscm *tscm, unsigned int rate,
  29                     enum snd_tscm_clock clock)
  30{
  31        u32 data;
  32        __be32 reg;
  33        int err;
  34
  35        err = get_clock(tscm, &data);
  36        if (err < 0)
  37                return err;
  38        data &= 0x0000ffff;
  39
  40        if (rate > 0) {
  41                data &= 0x000000ff;
  42                /* Base rate. */
  43                if ((rate % 44100) == 0) {
  44                        data |= 0x00000100;
  45                        /* Multiplier. */
  46                        if (rate / 44100 == 2)
  47                                data |= 0x00008000;
  48                } else if ((rate % 48000) == 0) {
  49                        data |= 0x00000200;
  50                        /* Multiplier. */
  51                        if (rate / 48000 == 2)
  52                                data |= 0x00008000;
  53                } else {
  54                        return -EAGAIN;
  55                }
  56        }
  57
  58        if (clock != INT_MAX) {
  59                data &= 0x0000ff00;
  60                data |= clock + 1;
  61        }
  62
  63        reg = cpu_to_be32(data);
  64
  65        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
  66                                 TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
  67                                 &reg, sizeof(reg), 0);
  68        if (err < 0)
  69                return err;
  70
  71        if (data & 0x00008000)
  72                reg = cpu_to_be32(0x0000001a);
  73        else
  74                reg = cpu_to_be32(0x0000000d);
  75
  76        return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
  77                                  TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
  78                                  &reg, sizeof(reg), 0);
  79}
  80
  81int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
  82{
  83        u32 data = 0x0;
  84        unsigned int trials = 0;
  85        int err;
  86
  87        while (data == 0x0 || trials++ < 5) {
  88                err = get_clock(tscm, &data);
  89                if (err < 0)
  90                        return err;
  91
  92                data = (data & 0xff000000) >> 24;
  93        }
  94
  95        /* Check base rate. */
  96        if ((data & 0x0f) == 0x01)
  97                *rate = 44100;
  98        else if ((data & 0x0f) == 0x02)
  99                *rate = 48000;
 100        else
 101                return -EAGAIN;
 102
 103        /* Check multiplier. */
 104        if ((data & 0xf0) == 0x80)
 105                *rate *= 2;
 106        else if ((data & 0xf0) != 0x00)
 107                return -EAGAIN;
 108
 109        return err;
 110}
 111
 112int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
 113{
 114        u32 data;
 115        int err;
 116
 117        err = get_clock(tscm, &data);
 118        if (err < 0)
 119                return err;
 120
 121        *clock = ((data & 0x00ff0000) >> 16) - 1;
 122        if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
 123                return -EIO;
 124
 125        return 0;
 126}
 127
 128static int enable_data_channels(struct snd_tscm *tscm)
 129{
 130        __be32 reg;
 131        u32 data;
 132        unsigned int i;
 133        int err;
 134
 135        data = 0;
 136        for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
 137                data |= BIT(i);
 138        if (tscm->spec->has_adat)
 139                data |= 0x0000ff00;
 140        if (tscm->spec->has_spdif)
 141                data |= 0x00030000;
 142
 143        reg = cpu_to_be32(data);
 144        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 145                                 TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
 146                                 &reg, sizeof(reg), 0);
 147        if (err < 0)
 148                return err;
 149
 150        data = 0;
 151        for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
 152                data |= BIT(i);
 153        if (tscm->spec->has_adat)
 154                data |= 0x0000ff00;
 155        if (tscm->spec->has_spdif)
 156                data |= 0x00030000;
 157
 158        reg = cpu_to_be32(data);
 159        return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 160                                  TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
 161                                  &reg, sizeof(reg), 0);
 162}
 163
 164static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
 165{
 166        __be32 reg;
 167        int err;
 168
 169        /* Set an option for unknown purpose. */
 170        reg = cpu_to_be32(0x00200000);
 171        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 172                                 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
 173                                 &reg, sizeof(reg), 0);
 174        if (err < 0)
 175                return err;
 176
 177        err = enable_data_channels(tscm);
 178        if (err < 0)
 179                return err;
 180
 181        return set_clock(tscm, rate, INT_MAX);
 182}
 183
 184static void finish_session(struct snd_tscm *tscm)
 185{
 186        __be32 reg;
 187
 188        reg = 0;
 189        snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 190                           TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
 191                           &reg, sizeof(reg), 0);
 192
 193        reg = 0;
 194        snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 195                           TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
 196                           &reg, sizeof(reg), 0);
 197
 198}
 199
 200static int begin_session(struct snd_tscm *tscm)
 201{
 202        __be32 reg;
 203        int err;
 204
 205        reg = cpu_to_be32(0x00000001);
 206        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 207                                 TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
 208                                 &reg, sizeof(reg), 0);
 209        if (err < 0)
 210                return err;
 211
 212        reg = cpu_to_be32(0x00000001);
 213        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 214                                 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
 215                                 &reg, sizeof(reg), 0);
 216        if (err < 0)
 217                return err;
 218
 219        /* Set an option for unknown purpose. */
 220        reg = cpu_to_be32(0x00002000);
 221        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 222                                 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
 223                                 &reg, sizeof(reg), 0);
 224        if (err < 0)
 225                return err;
 226
 227        /* Start multiplexing PCM samples on packets. */
 228        reg = cpu_to_be32(0x00000001);
 229        return snd_fw_transaction(tscm->unit,
 230                                  TCODE_WRITE_QUADLET_REQUEST,
 231                                  TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
 232                                  &reg, sizeof(reg), 0);
 233}
 234
 235static void release_resources(struct snd_tscm *tscm)
 236{
 237        __be32 reg;
 238
 239        /* Unregister channels. */
 240        reg = cpu_to_be32(0x00000000);
 241        snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 242                           TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
 243                           &reg, sizeof(reg), 0);
 244        reg = cpu_to_be32(0x00000000);
 245        snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 246                           TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
 247                           &reg, sizeof(reg), 0);
 248        reg = cpu_to_be32(0x00000000);
 249        snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 250                           TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
 251                           &reg, sizeof(reg), 0);
 252
 253        /* Release isochronous resources. */
 254        fw_iso_resources_free(&tscm->tx_resources);
 255        fw_iso_resources_free(&tscm->rx_resources);
 256}
 257
 258static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
 259{
 260        __be32 reg;
 261        int err;
 262
 263        /* Keep resources for in-stream. */
 264        err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
 265        if (err < 0)
 266                return err;
 267        err = fw_iso_resources_allocate(&tscm->tx_resources,
 268                        amdtp_stream_get_max_payload(&tscm->tx_stream),
 269                        fw_parent_device(tscm->unit)->max_speed);
 270        if (err < 0)
 271                goto error;
 272
 273        /* Keep resources for out-stream. */
 274        err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
 275        if (err < 0)
 276                return err;
 277        err = fw_iso_resources_allocate(&tscm->rx_resources,
 278                        amdtp_stream_get_max_payload(&tscm->rx_stream),
 279                        fw_parent_device(tscm->unit)->max_speed);
 280        if (err < 0)
 281                return err;
 282
 283        /* Register the isochronous channel for transmitting stream. */
 284        reg = cpu_to_be32(tscm->tx_resources.channel);
 285        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 286                                 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
 287                                 &reg, sizeof(reg), 0);
 288        if (err < 0)
 289                goto error;
 290
 291        /* Unknown */
 292        reg = cpu_to_be32(0x00000002);
 293        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 294                                 TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
 295                                 &reg, sizeof(reg), 0);
 296        if (err < 0)
 297                goto error;
 298
 299        /* Register the isochronous channel for receiving stream. */
 300        reg = cpu_to_be32(tscm->rx_resources.channel);
 301        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 302                                 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
 303                                 &reg, sizeof(reg), 0);
 304        if (err < 0)
 305                goto error;
 306
 307        return 0;
 308error:
 309        release_resources(tscm);
 310        return err;
 311}
 312
 313int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
 314{
 315        unsigned int pcm_channels;
 316        int err;
 317
 318        /* For out-stream. */
 319        err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
 320        if (err < 0)
 321                return err;
 322        pcm_channels = tscm->spec->pcm_playback_analog_channels;
 323        if (tscm->spec->has_adat)
 324                pcm_channels += 8;
 325        if (tscm->spec->has_spdif)
 326                pcm_channels += 2;
 327        err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
 328                              pcm_channels);
 329        if (err < 0)
 330                return err;
 331
 332        /* For in-stream. */
 333        err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
 334        if (err < 0)
 335                return err;
 336        pcm_channels = tscm->spec->pcm_capture_analog_channels;
 337        if (tscm->spec->has_adat)
 338                pcm_channels += 8;
 339        if (tscm->spec->has_spdif)
 340                pcm_channels += 2;
 341        err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
 342                              pcm_channels);
 343        if (err < 0)
 344                amdtp_stream_destroy(&tscm->rx_stream);
 345
 346        return err;
 347}
 348
 349/* At bus reset, streaming is stopped and some registers are clear. */
 350void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
 351{
 352        amdtp_stream_pcm_abort(&tscm->tx_stream);
 353        amdtp_stream_stop(&tscm->tx_stream);
 354
 355        amdtp_stream_pcm_abort(&tscm->rx_stream);
 356        amdtp_stream_stop(&tscm->rx_stream);
 357}
 358
 359/*
 360 * This function should be called before starting streams or after stopping
 361 * streams.
 362 */
 363void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
 364{
 365        amdtp_stream_destroy(&tscm->rx_stream);
 366        amdtp_stream_destroy(&tscm->tx_stream);
 367
 368        fw_iso_resources_destroy(&tscm->rx_resources);
 369        fw_iso_resources_destroy(&tscm->tx_resources);
 370}
 371
 372int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
 373{
 374        unsigned int curr_rate;
 375        int err;
 376
 377        if (tscm->substreams_counter == 0)
 378                return 0;
 379
 380        err = snd_tscm_stream_get_rate(tscm, &curr_rate);
 381        if (err < 0)
 382                return err;
 383        if (curr_rate != rate ||
 384            amdtp_streaming_error(&tscm->rx_stream) ||
 385            amdtp_streaming_error(&tscm->tx_stream)) {
 386                finish_session(tscm);
 387
 388                amdtp_stream_stop(&tscm->rx_stream);
 389                amdtp_stream_stop(&tscm->tx_stream);
 390
 391                release_resources(tscm);
 392        }
 393
 394        if (!amdtp_stream_running(&tscm->rx_stream)) {
 395                err = keep_resources(tscm, rate);
 396                if (err < 0)
 397                        goto error;
 398
 399                err = set_stream_formats(tscm, rate);
 400                if (err < 0)
 401                        goto error;
 402
 403                err = begin_session(tscm);
 404                if (err < 0)
 405                        goto error;
 406
 407                err = amdtp_stream_start(&tscm->rx_stream,
 408                                tscm->rx_resources.channel,
 409                                fw_parent_device(tscm->unit)->max_speed);
 410                if (err < 0)
 411                        goto error;
 412
 413                if (!amdtp_stream_wait_callback(&tscm->rx_stream,
 414                                                CALLBACK_TIMEOUT)) {
 415                        err = -ETIMEDOUT;
 416                        goto error;
 417                }
 418        }
 419
 420        if (!amdtp_stream_running(&tscm->tx_stream)) {
 421                err = amdtp_stream_start(&tscm->tx_stream,
 422                                tscm->tx_resources.channel,
 423                                fw_parent_device(tscm->unit)->max_speed);
 424                if (err < 0)
 425                        goto error;
 426
 427                if (!amdtp_stream_wait_callback(&tscm->tx_stream,
 428                                                CALLBACK_TIMEOUT)) {
 429                        err = -ETIMEDOUT;
 430                        goto error;
 431                }
 432        }
 433
 434        return 0;
 435error:
 436        amdtp_stream_stop(&tscm->rx_stream);
 437        amdtp_stream_stop(&tscm->tx_stream);
 438
 439        finish_session(tscm);
 440        release_resources(tscm);
 441
 442        return err;
 443}
 444
 445void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
 446{
 447        if (tscm->substreams_counter > 0)
 448                return;
 449
 450        amdtp_stream_stop(&tscm->tx_stream);
 451        amdtp_stream_stop(&tscm->rx_stream);
 452
 453        finish_session(tscm);
 454        release_resources(tscm);
 455}
 456
 457void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
 458{
 459        tscm->dev_lock_changed = true;
 460        wake_up(&tscm->hwdep_wait);
 461}
 462
 463int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
 464{
 465        int err;
 466
 467        spin_lock_irq(&tscm->lock);
 468
 469        /* user land lock this */
 470        if (tscm->dev_lock_count < 0) {
 471                err = -EBUSY;
 472                goto end;
 473        }
 474
 475        /* this is the first time */
 476        if (tscm->dev_lock_count++ == 0)
 477                snd_tscm_stream_lock_changed(tscm);
 478        err = 0;
 479end:
 480        spin_unlock_irq(&tscm->lock);
 481        return err;
 482}
 483
 484void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
 485{
 486        spin_lock_irq(&tscm->lock);
 487
 488        if (WARN_ON(tscm->dev_lock_count <= 0))
 489                goto end;
 490        if (--tscm->dev_lock_count == 0)
 491                snd_tscm_stream_lock_changed(tscm);
 492end:
 493        spin_unlock_irq(&tscm->lock);
 494}
 495