linux/sound/firewire/bebob/bebob_pcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * bebob_pcm.c - a part of driver for BeBoB based devices
   4 *
   5 * Copyright (c) 2013-2014 Takashi Sakamoto
   6 */
   7
   8#include "./bebob.h"
   9
  10static int
  11hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
  12{
  13        struct snd_bebob_stream_formation *formations = 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 < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
  24                /* entry is invalid */
  25                if (formations[i].pcm == 0)
  26                        continue;
  27
  28                if (!snd_interval_test(c, formations[i].pcm))
  29                        continue;
  30
  31                t.min = min(t.min, snd_bebob_rate_table[i]);
  32                t.max = max(t.max, snd_bebob_rate_table[i]);
  33
  34        }
  35        return snd_interval_refine(r, &t);
  36}
  37
  38static int
  39hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
  40{
  41        struct snd_bebob_stream_formation *formations = rule->private;
  42        struct snd_interval *c =
  43                hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
  44        const struct snd_interval *r =
  45                hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
  46        struct snd_interval t = {
  47                .min = UINT_MAX, .max = 0, .integer = 1
  48        };
  49
  50        unsigned int i;
  51
  52        for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
  53                /* entry is invalid */
  54                if (formations[i].pcm == 0)
  55                        continue;
  56
  57                if (!snd_interval_test(r, snd_bebob_rate_table[i]))
  58                        continue;
  59
  60                t.min = min(t.min, formations[i].pcm);
  61                t.max = max(t.max, formations[i].pcm);
  62        }
  63
  64        return snd_interval_refine(c, &t);
  65}
  66
  67static void
  68limit_channels_and_rates(struct snd_pcm_hardware *hw,
  69                         struct snd_bebob_stream_formation *formations)
  70{
  71        unsigned int i;
  72
  73        hw->channels_min = UINT_MAX;
  74        hw->channels_max = 0;
  75
  76        hw->rate_min = UINT_MAX;
  77        hw->rate_max = 0;
  78        hw->rates = 0;
  79
  80        for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
  81                /* entry has no PCM channels */
  82                if (formations[i].pcm == 0)
  83                        continue;
  84
  85                hw->channels_min = min(hw->channels_min, formations[i].pcm);
  86                hw->channels_max = max(hw->channels_max, formations[i].pcm);
  87
  88                hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
  89                hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
  90                hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
  91        }
  92}
  93
  94static int
  95pcm_init_hw_params(struct snd_bebob *bebob,
  96                   struct snd_pcm_substream *substream)
  97{
  98        struct snd_pcm_runtime *runtime = substream->runtime;
  99        struct amdtp_stream *s;
 100        struct snd_bebob_stream_formation *formations;
 101        int err;
 102
 103        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 104                runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
 105                s = &bebob->tx_stream;
 106                formations = bebob->tx_stream_formations;
 107        } else {
 108                runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
 109                s = &bebob->rx_stream;
 110                formations = bebob->rx_stream_formations;
 111        }
 112
 113        limit_channels_and_rates(&runtime->hw, formations);
 114
 115        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 116                                  hw_rule_channels, formations,
 117                                  SNDRV_PCM_HW_PARAM_RATE, -1);
 118        if (err < 0)
 119                goto end;
 120
 121        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 122                                  hw_rule_rate, formations,
 123                                  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 124        if (err < 0)
 125                goto end;
 126
 127        err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
 128end:
 129        return err;
 130}
 131
 132static int pcm_open(struct snd_pcm_substream *substream)
 133{
 134        struct snd_bebob *bebob = substream->private_data;
 135        const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
 136        struct amdtp_domain *d = &bebob->domain;
 137        enum snd_bebob_clock_type src;
 138        int err;
 139
 140        err = snd_bebob_stream_lock_try(bebob);
 141        if (err < 0)
 142                return err;
 143
 144        err = pcm_init_hw_params(bebob, substream);
 145        if (err < 0)
 146                goto err_locked;
 147
 148        err = snd_bebob_stream_get_clock_src(bebob, &src);
 149        if (err < 0)
 150                goto err_locked;
 151
 152        mutex_lock(&bebob->mutex);
 153
 154        // When source of clock is not internal or any stream is reserved for
 155        // transmission of PCM frames, the available sampling rate is limited
 156        // at current one.
 157        if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
 158            (bebob->substreams_counter > 0 && d->events_per_period > 0)) {
 159                unsigned int frames_per_period = d->events_per_period;
 160                unsigned int frames_per_buffer = d->events_per_buffer;
 161                unsigned int sampling_rate;
 162
 163                err = spec->get(bebob, &sampling_rate);
 164                if (err < 0) {
 165                        mutex_unlock(&bebob->mutex);
 166                        dev_err(&bebob->unit->device,
 167                                "fail to get sampling rate: %d\n", err);
 168                        goto err_locked;
 169                }
 170
 171                substream->runtime->hw.rate_min = sampling_rate;
 172                substream->runtime->hw.rate_max = sampling_rate;
 173
 174                if (frames_per_period > 0) {
 175                        err = snd_pcm_hw_constraint_minmax(substream->runtime,
 176                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
 177                                        frames_per_period, frames_per_period);
 178                        if (err < 0) {
 179                                mutex_unlock(&bebob->mutex);
 180                                goto err_locked;
 181                        }
 182
 183                        err = snd_pcm_hw_constraint_minmax(substream->runtime,
 184                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
 185                                        frames_per_buffer, frames_per_buffer);
 186                        if (err < 0) {
 187                                mutex_unlock(&bebob->mutex);
 188                                goto err_locked;
 189                        }
 190                }
 191        }
 192
 193        mutex_unlock(&bebob->mutex);
 194
 195        snd_pcm_set_sync(substream);
 196
 197        return 0;
 198err_locked:
 199        snd_bebob_stream_lock_release(bebob);
 200        return err;
 201}
 202
 203static int
 204pcm_close(struct snd_pcm_substream *substream)
 205{
 206        struct snd_bebob *bebob = substream->private_data;
 207        snd_bebob_stream_lock_release(bebob);
 208        return 0;
 209}
 210
 211static int pcm_hw_params(struct snd_pcm_substream *substream,
 212                         struct snd_pcm_hw_params *hw_params)
 213{
 214        struct snd_bebob *bebob = substream->private_data;
 215        int err = 0;
 216
 217        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
 218                unsigned int rate = params_rate(hw_params);
 219                unsigned int frames_per_period = params_period_size(hw_params);
 220                unsigned int frames_per_buffer = params_buffer_size(hw_params);
 221
 222                mutex_lock(&bebob->mutex);
 223                err = snd_bebob_stream_reserve_duplex(bebob, rate,
 224                                        frames_per_period, frames_per_buffer);
 225                if (err >= 0)
 226                        ++bebob->substreams_counter;
 227                mutex_unlock(&bebob->mutex);
 228        }
 229
 230        return err;
 231}
 232
 233static int pcm_hw_free(struct snd_pcm_substream *substream)
 234{
 235        struct snd_bebob *bebob = substream->private_data;
 236
 237        mutex_lock(&bebob->mutex);
 238
 239        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
 240                bebob->substreams_counter--;
 241
 242        snd_bebob_stream_stop_duplex(bebob);
 243
 244        mutex_unlock(&bebob->mutex);
 245
 246        return 0;
 247}
 248
 249static int
 250pcm_capture_prepare(struct snd_pcm_substream *substream)
 251{
 252        struct snd_bebob *bebob = substream->private_data;
 253        int err;
 254
 255        err = snd_bebob_stream_start_duplex(bebob);
 256        if (err >= 0)
 257                amdtp_stream_pcm_prepare(&bebob->tx_stream);
 258
 259        return err;
 260}
 261static int
 262pcm_playback_prepare(struct snd_pcm_substream *substream)
 263{
 264        struct snd_bebob *bebob = substream->private_data;
 265        int err;
 266
 267        err = snd_bebob_stream_start_duplex(bebob);
 268        if (err >= 0)
 269                amdtp_stream_pcm_prepare(&bebob->rx_stream);
 270
 271        return err;
 272}
 273
 274static int
 275pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 276{
 277        struct snd_bebob *bebob = substream->private_data;
 278
 279        switch (cmd) {
 280        case SNDRV_PCM_TRIGGER_START:
 281                amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
 282                break;
 283        case SNDRV_PCM_TRIGGER_STOP:
 284                amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
 285                break;
 286        default:
 287                return -EINVAL;
 288        }
 289
 290        return 0;
 291}
 292static int
 293pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 294{
 295        struct snd_bebob *bebob = substream->private_data;
 296
 297        switch (cmd) {
 298        case SNDRV_PCM_TRIGGER_START:
 299                amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
 300                break;
 301        case SNDRV_PCM_TRIGGER_STOP:
 302                amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
 303                break;
 304        default:
 305                return -EINVAL;
 306        }
 307
 308        return 0;
 309}
 310
 311static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
 312{
 313        struct snd_bebob *bebob = sbstrm->private_data;
 314
 315        return amdtp_domain_stream_pcm_pointer(&bebob->domain,
 316                                               &bebob->tx_stream);
 317}
 318static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
 319{
 320        struct snd_bebob *bebob = sbstrm->private_data;
 321
 322        return amdtp_domain_stream_pcm_pointer(&bebob->domain,
 323                                               &bebob->rx_stream);
 324}
 325
 326static int pcm_capture_ack(struct snd_pcm_substream *substream)
 327{
 328        struct snd_bebob *bebob = substream->private_data;
 329
 330        return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);
 331}
 332
 333static int pcm_playback_ack(struct snd_pcm_substream *substream)
 334{
 335        struct snd_bebob *bebob = substream->private_data;
 336
 337        return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);
 338}
 339
 340int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
 341{
 342        static const struct snd_pcm_ops capture_ops = {
 343                .open           = pcm_open,
 344                .close          = pcm_close,
 345                .hw_params      = pcm_hw_params,
 346                .hw_free        = pcm_hw_free,
 347                .prepare        = pcm_capture_prepare,
 348                .trigger        = pcm_capture_trigger,
 349                .pointer        = pcm_capture_pointer,
 350                .ack            = pcm_capture_ack,
 351        };
 352        static const struct snd_pcm_ops playback_ops = {
 353                .open           = pcm_open,
 354                .close          = pcm_close,
 355                .hw_params      = pcm_hw_params,
 356                .hw_free        = pcm_hw_free,
 357                .prepare        = pcm_playback_prepare,
 358                .trigger        = pcm_playback_trigger,
 359                .pointer        = pcm_playback_pointer,
 360                .ack            = pcm_playback_ack,
 361        };
 362        struct snd_pcm *pcm;
 363        int err;
 364
 365        err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
 366        if (err < 0)
 367                goto end;
 368
 369        pcm->private_data = bebob;
 370        snprintf(pcm->name, sizeof(pcm->name),
 371                 "%s PCM", bebob->card->shortname);
 372        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
 373        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
 374        snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
 375end:
 376        return err;
 377}
 378