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