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