linux/sound/firewire/tascam/tascam-pcm.c
<<
>>
Prefs
   1/*
   2 * tascam-pcm.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 "tascam.h"
  10
  11static void set_buffer_params(struct snd_pcm_hardware *hw)
  12{
  13        hw->period_bytes_min = 4 * hw->channels_min;
  14        hw->period_bytes_max = hw->period_bytes_min * 2048;
  15        hw->buffer_bytes_max = hw->period_bytes_max * 2;
  16
  17        hw->periods_min = 2;
  18        hw->periods_max = UINT_MAX;
  19}
  20
  21static int pcm_init_hw_params(struct snd_tscm *tscm,
  22                              struct snd_pcm_substream *substream)
  23{
  24        static const struct snd_pcm_hardware hardware = {
  25                .info = SNDRV_PCM_INFO_BATCH |
  26                        SNDRV_PCM_INFO_BLOCK_TRANSFER |
  27                        SNDRV_PCM_INFO_INTERLEAVED |
  28                        SNDRV_PCM_INFO_JOINT_DUPLEX |
  29                        SNDRV_PCM_INFO_MMAP |
  30                        SNDRV_PCM_INFO_MMAP_VALID,
  31                .rates = SNDRV_PCM_RATE_44100 |
  32                         SNDRV_PCM_RATE_48000 |
  33                         SNDRV_PCM_RATE_88200 |
  34                         SNDRV_PCM_RATE_96000,
  35                .rate_min = 44100,
  36                .rate_max = 96000,
  37                .channels_min = 10,
  38                .channels_max = 18,
  39        };
  40        struct snd_pcm_runtime *runtime = substream->runtime;
  41        struct amdtp_stream *stream;
  42        unsigned int pcm_channels;
  43
  44        runtime->hw = hardware;
  45
  46        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  47                runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
  48                stream = &tscm->tx_stream;
  49                pcm_channels = tscm->spec->pcm_capture_analog_channels;
  50        } else {
  51                runtime->hw.formats =
  52                                SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
  53                stream = &tscm->rx_stream;
  54                pcm_channels = tscm->spec->pcm_playback_analog_channels;
  55        }
  56
  57        if (tscm->spec->has_adat)
  58                pcm_channels += 8;
  59        if (tscm->spec->has_spdif)
  60                pcm_channels += 2;
  61        runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
  62
  63        set_buffer_params(&runtime->hw);
  64
  65        return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
  66}
  67
  68static int pcm_open(struct snd_pcm_substream *substream)
  69{
  70        struct snd_tscm *tscm = substream->private_data;
  71        enum snd_tscm_clock clock;
  72        unsigned int rate;
  73        int err;
  74
  75        err = snd_tscm_stream_lock_try(tscm);
  76        if (err < 0)
  77                goto end;
  78
  79        err = pcm_init_hw_params(tscm, substream);
  80        if (err < 0)
  81                goto err_locked;
  82
  83        err = snd_tscm_stream_get_clock(tscm, &clock);
  84        if (clock != SND_TSCM_CLOCK_INTERNAL ||
  85            amdtp_stream_pcm_running(&tscm->rx_stream) ||
  86            amdtp_stream_pcm_running(&tscm->tx_stream)) {
  87                err = snd_tscm_stream_get_rate(tscm, &rate);
  88                if (err < 0)
  89                        goto err_locked;
  90                substream->runtime->hw.rate_min = rate;
  91                substream->runtime->hw.rate_max = rate;
  92        }
  93
  94        snd_pcm_set_sync(substream);
  95end:
  96        return err;
  97err_locked:
  98        snd_tscm_stream_lock_release(tscm);
  99        return err;
 100}
 101
 102static int pcm_close(struct snd_pcm_substream *substream)
 103{
 104        struct snd_tscm *tscm = substream->private_data;
 105
 106        snd_tscm_stream_lock_release(tscm);
 107
 108        return 0;
 109}
 110
 111static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
 112                                 struct snd_pcm_hw_params *hw_params)
 113{
 114        struct snd_tscm *tscm = substream->private_data;
 115        int err;
 116
 117        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 118                                               params_buffer_bytes(hw_params));
 119        if (err < 0)
 120                return err;
 121
 122        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 123                mutex_lock(&tscm->mutex);
 124                tscm->substreams_counter++;
 125                mutex_unlock(&tscm->mutex);
 126        }
 127
 128        amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
 129
 130        return 0;
 131}
 132
 133static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
 134                                  struct snd_pcm_hw_params *hw_params)
 135{
 136        struct snd_tscm *tscm = substream->private_data;
 137        int err;
 138
 139        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 140                                               params_buffer_bytes(hw_params));
 141        if (err < 0)
 142                return err;
 143
 144        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 145                mutex_lock(&tscm->mutex);
 146                tscm->substreams_counter++;
 147                mutex_unlock(&tscm->mutex);
 148        }
 149
 150        amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
 151
 152        return 0;
 153}
 154
 155static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
 156{
 157        struct snd_tscm *tscm = substream->private_data;
 158
 159        mutex_lock(&tscm->mutex);
 160
 161        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
 162                tscm->substreams_counter--;
 163
 164        snd_tscm_stream_stop_duplex(tscm);
 165
 166        mutex_unlock(&tscm->mutex);
 167
 168        return snd_pcm_lib_free_vmalloc_buffer(substream);
 169}
 170
 171static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
 172{
 173        struct snd_tscm *tscm = substream->private_data;
 174
 175        mutex_lock(&tscm->mutex);
 176
 177        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
 178                tscm->substreams_counter--;
 179
 180        snd_tscm_stream_stop_duplex(tscm);
 181
 182        mutex_unlock(&tscm->mutex);
 183
 184        return snd_pcm_lib_free_vmalloc_buffer(substream);
 185}
 186
 187static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 188{
 189        struct snd_tscm *tscm = substream->private_data;
 190        struct snd_pcm_runtime *runtime = substream->runtime;
 191        int err;
 192
 193        mutex_lock(&tscm->mutex);
 194
 195        err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
 196        if (err >= 0)
 197                amdtp_stream_pcm_prepare(&tscm->tx_stream);
 198
 199        mutex_unlock(&tscm->mutex);
 200
 201        return err;
 202}
 203
 204static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 205{
 206        struct snd_tscm *tscm = substream->private_data;
 207        struct snd_pcm_runtime *runtime = substream->runtime;
 208        int err;
 209
 210        mutex_lock(&tscm->mutex);
 211
 212        err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
 213        if (err >= 0)
 214                amdtp_stream_pcm_prepare(&tscm->rx_stream);
 215
 216        mutex_unlock(&tscm->mutex);
 217
 218        return err;
 219}
 220
 221static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 222{
 223        struct snd_tscm *tscm = substream->private_data;
 224
 225        switch (cmd) {
 226        case SNDRV_PCM_TRIGGER_START:
 227                amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
 228                break;
 229        case SNDRV_PCM_TRIGGER_STOP:
 230                amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
 231                break;
 232        default:
 233                return -EINVAL;
 234        }
 235
 236        return 0;
 237}
 238
 239static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 240{
 241        struct snd_tscm *tscm = substream->private_data;
 242
 243        switch (cmd) {
 244        case SNDRV_PCM_TRIGGER_START:
 245                amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
 246                break;
 247        case SNDRV_PCM_TRIGGER_STOP:
 248                amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
 249                break;
 250        default:
 251                return -EINVAL;
 252        }
 253
 254        return 0;
 255}
 256
 257static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
 258{
 259        struct snd_tscm *tscm = sbstrm->private_data;
 260
 261        return amdtp_stream_pcm_pointer(&tscm->tx_stream);
 262}
 263
 264static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
 265{
 266        struct snd_tscm *tscm = sbstrm->private_data;
 267
 268        return amdtp_stream_pcm_pointer(&tscm->rx_stream);
 269}
 270
 271static const struct snd_pcm_ops pcm_capture_ops = {
 272        .open           = pcm_open,
 273        .close          = pcm_close,
 274        .ioctl          = snd_pcm_lib_ioctl,
 275        .hw_params      = pcm_capture_hw_params,
 276        .hw_free        = pcm_capture_hw_free,
 277        .prepare        = pcm_capture_prepare,
 278        .trigger        = pcm_capture_trigger,
 279        .pointer        = pcm_capture_pointer,
 280        .page           = snd_pcm_lib_get_vmalloc_page,
 281};
 282
 283static const struct snd_pcm_ops pcm_playback_ops = {
 284        .open           = pcm_open,
 285        .close          = pcm_close,
 286        .ioctl          = snd_pcm_lib_ioctl,
 287        .hw_params      = pcm_playback_hw_params,
 288        .hw_free        = pcm_playback_hw_free,
 289        .prepare        = pcm_playback_prepare,
 290        .trigger        = pcm_playback_trigger,
 291        .pointer        = pcm_playback_pointer,
 292        .page           = snd_pcm_lib_get_vmalloc_page,
 293        .mmap           = snd_pcm_lib_mmap_vmalloc,
 294};
 295
 296int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
 297{
 298        struct snd_pcm *pcm;
 299        int err;
 300
 301        err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
 302        if (err < 0)
 303                return err;
 304
 305        pcm->private_data = tscm;
 306        snprintf(pcm->name, sizeof(pcm->name),
 307                 "%s PCM", tscm->card->shortname);
 308        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
 309        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
 310
 311        return 0;
 312}
 313