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