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