linux/sound/firewire/digi00x/digi00x-pcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
   4 *
   5 * Copyright (c) 2014-2015 Takashi Sakamoto
   6 */
   7
   8#include "digi00x.h"
   9
  10static int hw_rule_rate(struct snd_pcm_hw_params *params,
  11                        struct snd_pcm_hw_rule *rule)
  12{
  13        struct snd_interval *r =
  14                hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
  15        const struct snd_interval *c =
  16                hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
  17        struct snd_interval t = {
  18                .min = UINT_MAX, .max = 0, .integer = 1,
  19        };
  20        unsigned int i;
  21
  22        for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
  23                if (!snd_interval_test(c,
  24                                       snd_dg00x_stream_pcm_channels[i]))
  25                        continue;
  26
  27                t.min = min(t.min, snd_dg00x_stream_rates[i]);
  28                t.max = max(t.max, snd_dg00x_stream_rates[i]);
  29        }
  30
  31        return snd_interval_refine(r, &t);
  32}
  33
  34static int hw_rule_channels(struct snd_pcm_hw_params *params,
  35                            struct snd_pcm_hw_rule *rule)
  36{
  37        struct snd_interval *c =
  38                hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
  39        const struct snd_interval *r =
  40                hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
  41        struct snd_interval t = {
  42                .min = UINT_MAX, .max = 0, .integer = 1,
  43        };
  44        unsigned int i;
  45
  46        for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
  47                if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
  48                        continue;
  49
  50                t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
  51                t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
  52        }
  53
  54        return snd_interval_refine(c, &t);
  55}
  56
  57static int pcm_init_hw_params(struct snd_dg00x *dg00x,
  58                              struct snd_pcm_substream *substream)
  59{
  60        struct snd_pcm_runtime *runtime = substream->runtime;
  61        struct snd_pcm_hardware *hw = &runtime->hw;
  62        struct amdtp_stream *s;
  63        int err;
  64
  65
  66        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  67                substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
  68                s = &dg00x->tx_stream;
  69        } else {
  70                substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
  71                s = &dg00x->rx_stream;
  72        }
  73
  74        hw->channels_min = 10;
  75        hw->channels_max = 18;
  76
  77        hw->rates = SNDRV_PCM_RATE_44100 |
  78                    SNDRV_PCM_RATE_48000 |
  79                    SNDRV_PCM_RATE_88200 |
  80                    SNDRV_PCM_RATE_96000;
  81        snd_pcm_limit_hw_rates(runtime);
  82
  83        err = snd_pcm_hw_rule_add(substream->runtime, 0,
  84                                  SNDRV_PCM_HW_PARAM_CHANNELS,
  85                                  hw_rule_channels, NULL,
  86                                  SNDRV_PCM_HW_PARAM_RATE, -1);
  87        if (err < 0)
  88                return err;
  89
  90        err = snd_pcm_hw_rule_add(substream->runtime, 0,
  91                                  SNDRV_PCM_HW_PARAM_RATE,
  92                                  hw_rule_rate, NULL,
  93                                  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
  94        if (err < 0)
  95                return err;
  96
  97        return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
  98}
  99
 100static int pcm_open(struct snd_pcm_substream *substream)
 101{
 102        struct snd_dg00x *dg00x = substream->private_data;
 103        enum snd_dg00x_clock clock;
 104        bool detect;
 105        unsigned int rate;
 106        int err;
 107
 108        err = snd_dg00x_stream_lock_try(dg00x);
 109        if (err < 0)
 110                goto end;
 111
 112        err = pcm_init_hw_params(dg00x, substream);
 113        if (err < 0)
 114                goto err_locked;
 115
 116        /* Check current clock source. */
 117        err = snd_dg00x_stream_get_clock(dg00x, &clock);
 118        if (err < 0)
 119                goto err_locked;
 120        if (clock != SND_DG00X_CLOCK_INTERNAL) {
 121                err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
 122                if (err < 0)
 123                        goto err_locked;
 124                if (!detect) {
 125                        err = -EBUSY;
 126                        goto err_locked;
 127                }
 128        }
 129
 130        if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
 131            amdtp_stream_pcm_running(&dg00x->rx_stream) ||
 132            amdtp_stream_pcm_running(&dg00x->tx_stream)) {
 133                err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
 134                if (err < 0)
 135                        goto err_locked;
 136                substream->runtime->hw.rate_min = rate;
 137                substream->runtime->hw.rate_max = rate;
 138        }
 139
 140        snd_pcm_set_sync(substream);
 141end:
 142        return err;
 143err_locked:
 144        snd_dg00x_stream_lock_release(dg00x);
 145        return err;
 146}
 147
 148static int pcm_close(struct snd_pcm_substream *substream)
 149{
 150        struct snd_dg00x *dg00x = substream->private_data;
 151
 152        snd_dg00x_stream_lock_release(dg00x);
 153
 154        return 0;
 155}
 156
 157static int pcm_hw_params(struct snd_pcm_substream *substream,
 158                         struct snd_pcm_hw_params *hw_params)
 159{
 160        struct snd_dg00x *dg00x = substream->private_data;
 161        int err;
 162
 163        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
 164                                               params_buffer_bytes(hw_params));
 165        if (err < 0)
 166                return err;
 167
 168        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 169                unsigned int rate = params_rate(hw_params);
 170
 171                mutex_lock(&dg00x->mutex);
 172                err = snd_dg00x_stream_reserve_duplex(dg00x, rate);
 173                if (err >= 0)
 174                        ++dg00x->substreams_counter;
 175                mutex_unlock(&dg00x->mutex);
 176        }
 177
 178        return err;
 179}
 180
 181static int pcm_hw_free(struct snd_pcm_substream *substream)
 182{
 183        struct snd_dg00x *dg00x = substream->private_data;
 184
 185        mutex_lock(&dg00x->mutex);
 186
 187        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
 188                --dg00x->substreams_counter;
 189
 190        snd_dg00x_stream_stop_duplex(dg00x);
 191
 192        mutex_unlock(&dg00x->mutex);
 193
 194        return snd_pcm_lib_free_vmalloc_buffer(substream);
 195}
 196
 197static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 198{
 199        struct snd_dg00x *dg00x = substream->private_data;
 200        int err;
 201
 202        mutex_lock(&dg00x->mutex);
 203
 204        err = snd_dg00x_stream_start_duplex(dg00x);
 205        if (err >= 0)
 206                amdtp_stream_pcm_prepare(&dg00x->tx_stream);
 207
 208        mutex_unlock(&dg00x->mutex);
 209
 210        return err;
 211}
 212
 213static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 214{
 215        struct snd_dg00x *dg00x = substream->private_data;
 216        int err;
 217
 218        mutex_lock(&dg00x->mutex);
 219
 220        err = snd_dg00x_stream_start_duplex(dg00x);
 221        if (err >= 0) {
 222                amdtp_stream_pcm_prepare(&dg00x->rx_stream);
 223                amdtp_dot_reset(&dg00x->rx_stream);
 224        }
 225
 226        mutex_unlock(&dg00x->mutex);
 227
 228        return err;
 229}
 230
 231static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 232{
 233        struct snd_dg00x *dg00x = substream->private_data;
 234
 235        switch (cmd) {
 236        case SNDRV_PCM_TRIGGER_START:
 237                amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
 238                break;
 239        case SNDRV_PCM_TRIGGER_STOP:
 240                amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
 241                break;
 242        default:
 243                return -EINVAL;
 244        }
 245
 246        return 0;
 247}
 248
 249static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 250{
 251        struct snd_dg00x *dg00x = substream->private_data;
 252
 253        switch (cmd) {
 254        case SNDRV_PCM_TRIGGER_START:
 255                amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
 256                break;
 257        case SNDRV_PCM_TRIGGER_STOP:
 258                amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
 259                break;
 260        default:
 261                return -EINVAL;
 262        }
 263
 264        return 0;
 265}
 266
 267static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
 268{
 269        struct snd_dg00x *dg00x = sbstrm->private_data;
 270
 271        return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
 272}
 273
 274static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
 275{
 276        struct snd_dg00x *dg00x = sbstrm->private_data;
 277
 278        return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
 279}
 280
 281static int pcm_capture_ack(struct snd_pcm_substream *substream)
 282{
 283        struct snd_dg00x *dg00x = substream->private_data;
 284
 285        return amdtp_stream_pcm_ack(&dg00x->tx_stream);
 286}
 287
 288static int pcm_playback_ack(struct snd_pcm_substream *substream)
 289{
 290        struct snd_dg00x *dg00x = substream->private_data;
 291
 292        return amdtp_stream_pcm_ack(&dg00x->rx_stream);
 293}
 294
 295int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
 296{
 297        static const struct snd_pcm_ops capture_ops = {
 298                .open           = pcm_open,
 299                .close          = pcm_close,
 300                .ioctl          = snd_pcm_lib_ioctl,
 301                .hw_params      = pcm_hw_params,
 302                .hw_free        = pcm_hw_free,
 303                .prepare        = pcm_capture_prepare,
 304                .trigger        = pcm_capture_trigger,
 305                .pointer        = pcm_capture_pointer,
 306                .ack            = pcm_capture_ack,
 307                .page           = snd_pcm_lib_get_vmalloc_page,
 308        };
 309        static const struct snd_pcm_ops playback_ops = {
 310                .open           = pcm_open,
 311                .close          = pcm_close,
 312                .ioctl          = snd_pcm_lib_ioctl,
 313                .hw_params      = pcm_hw_params,
 314                .hw_free        = pcm_hw_free,
 315                .prepare        = pcm_playback_prepare,
 316                .trigger        = pcm_playback_trigger,
 317                .pointer        = pcm_playback_pointer,
 318                .ack            = pcm_playback_ack,
 319                .page           = snd_pcm_lib_get_vmalloc_page,
 320        };
 321        struct snd_pcm *pcm;
 322        int err;
 323
 324        err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
 325        if (err < 0)
 326                return err;
 327
 328        pcm->private_data = dg00x;
 329        snprintf(pcm->name, sizeof(pcm->name),
 330                 "%s PCM", dg00x->card->shortname);
 331        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
 332        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
 333
 334        return 0;
 335}
 336