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