linux/sound/virtio/virtio_pcm_ops.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * virtio-snd: Virtio sound device
   4 * Copyright (C) 2021 OpenSynergy GmbH
   5 */
   6#include <sound/pcm_params.h>
   7
   8#include "virtio_card.h"
   9
  10/*
  11 * I/O messages lifetime
  12 * ---------------------
  13 *
  14 * Allocation:
  15 *   Messages are initially allocated in the ops->hw_params() after the size and
  16 *   number of periods have been successfully negotiated.
  17 *
  18 * Freeing:
  19 *   Messages can be safely freed after the queue has been successfully flushed
  20 *   (RELEASE command in the ops->sync_stop()) and the ops->hw_free() has been
  21 *   called.
  22 *
  23 *   When the substream stops, the ops->sync_stop() waits until the device has
  24 *   completed all pending messages. This wait can be interrupted either by a
  25 *   signal or due to a timeout. In this case, the device can still access
  26 *   messages even after calling ops->hw_free(). It can also issue an interrupt,
  27 *   and the interrupt handler will also try to access message structures.
  28 *
  29 *   Therefore, freeing of already allocated messages occurs:
  30 *
  31 *   - in ops->hw_params(), if this operator was called several times in a row,
  32 *     or if ops->hw_free() failed to free messages previously;
  33 *
  34 *   - in ops->hw_free(), if the queue has been successfully flushed;
  35 *
  36 *   - in dev->release().
  37 */
  38
  39/* Map for converting ALSA format to VirtIO format. */
  40struct virtsnd_a2v_format {
  41        snd_pcm_format_t alsa_bit;
  42        unsigned int vio_bit;
  43};
  44
  45static const struct virtsnd_a2v_format g_a2v_format_map[] = {
  46        { SNDRV_PCM_FORMAT_IMA_ADPCM, VIRTIO_SND_PCM_FMT_IMA_ADPCM },
  47        { SNDRV_PCM_FORMAT_MU_LAW, VIRTIO_SND_PCM_FMT_MU_LAW },
  48        { SNDRV_PCM_FORMAT_A_LAW, VIRTIO_SND_PCM_FMT_A_LAW },
  49        { SNDRV_PCM_FORMAT_S8, VIRTIO_SND_PCM_FMT_S8 },
  50        { SNDRV_PCM_FORMAT_U8, VIRTIO_SND_PCM_FMT_U8 },
  51        { SNDRV_PCM_FORMAT_S16_LE, VIRTIO_SND_PCM_FMT_S16 },
  52        { SNDRV_PCM_FORMAT_U16_LE, VIRTIO_SND_PCM_FMT_U16 },
  53        { SNDRV_PCM_FORMAT_S18_3LE, VIRTIO_SND_PCM_FMT_S18_3 },
  54        { SNDRV_PCM_FORMAT_U18_3LE, VIRTIO_SND_PCM_FMT_U18_3 },
  55        { SNDRV_PCM_FORMAT_S20_3LE, VIRTIO_SND_PCM_FMT_S20_3 },
  56        { SNDRV_PCM_FORMAT_U20_3LE, VIRTIO_SND_PCM_FMT_U20_3 },
  57        { SNDRV_PCM_FORMAT_S24_3LE, VIRTIO_SND_PCM_FMT_S24_3 },
  58        { SNDRV_PCM_FORMAT_U24_3LE, VIRTIO_SND_PCM_FMT_U24_3 },
  59        { SNDRV_PCM_FORMAT_S20_LE, VIRTIO_SND_PCM_FMT_S20 },
  60        { SNDRV_PCM_FORMAT_U20_LE, VIRTIO_SND_PCM_FMT_U20 },
  61        { SNDRV_PCM_FORMAT_S24_LE, VIRTIO_SND_PCM_FMT_S24 },
  62        { SNDRV_PCM_FORMAT_U24_LE, VIRTIO_SND_PCM_FMT_U24 },
  63        { SNDRV_PCM_FORMAT_S32_LE, VIRTIO_SND_PCM_FMT_S32 },
  64        { SNDRV_PCM_FORMAT_U32_LE, VIRTIO_SND_PCM_FMT_U32 },
  65        { SNDRV_PCM_FORMAT_FLOAT_LE, VIRTIO_SND_PCM_FMT_FLOAT },
  66        { SNDRV_PCM_FORMAT_FLOAT64_LE, VIRTIO_SND_PCM_FMT_FLOAT64 },
  67        { SNDRV_PCM_FORMAT_DSD_U8, VIRTIO_SND_PCM_FMT_DSD_U8 },
  68        { SNDRV_PCM_FORMAT_DSD_U16_LE, VIRTIO_SND_PCM_FMT_DSD_U16 },
  69        { SNDRV_PCM_FORMAT_DSD_U32_LE, VIRTIO_SND_PCM_FMT_DSD_U32 },
  70        { SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE,
  71          VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME }
  72};
  73
  74/* Map for converting ALSA frame rate to VirtIO frame rate. */
  75struct virtsnd_a2v_rate {
  76        unsigned int rate;
  77        unsigned int vio_bit;
  78};
  79
  80static const struct virtsnd_a2v_rate g_a2v_rate_map[] = {
  81        { 5512, VIRTIO_SND_PCM_RATE_5512 },
  82        { 8000, VIRTIO_SND_PCM_RATE_8000 },
  83        { 11025, VIRTIO_SND_PCM_RATE_11025 },
  84        { 16000, VIRTIO_SND_PCM_RATE_16000 },
  85        { 22050, VIRTIO_SND_PCM_RATE_22050 },
  86        { 32000, VIRTIO_SND_PCM_RATE_32000 },
  87        { 44100, VIRTIO_SND_PCM_RATE_44100 },
  88        { 48000, VIRTIO_SND_PCM_RATE_48000 },
  89        { 64000, VIRTIO_SND_PCM_RATE_64000 },
  90        { 88200, VIRTIO_SND_PCM_RATE_88200 },
  91        { 96000, VIRTIO_SND_PCM_RATE_96000 },
  92        { 176400, VIRTIO_SND_PCM_RATE_176400 },
  93        { 192000, VIRTIO_SND_PCM_RATE_192000 }
  94};
  95
  96static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream);
  97
  98/**
  99 * virtsnd_pcm_open() - Open the PCM substream.
 100 * @substream: Kernel ALSA substream.
 101 *
 102 * Context: Process context.
 103 * Return: 0 on success, -errno on failure.
 104 */
 105static int virtsnd_pcm_open(struct snd_pcm_substream *substream)
 106{
 107        struct virtio_pcm *vpcm = snd_pcm_substream_chip(substream);
 108        struct virtio_pcm_stream *vs = &vpcm->streams[substream->stream];
 109        struct virtio_pcm_substream *vss = vs->substreams[substream->number];
 110
 111        substream->runtime->hw = vss->hw;
 112        substream->private_data = vss;
 113
 114        snd_pcm_hw_constraint_integer(substream->runtime,
 115                                      SNDRV_PCM_HW_PARAM_PERIODS);
 116
 117        vss->stopped = !!virtsnd_pcm_msg_pending_num(vss);
 118        vss->suspended = false;
 119
 120        /*
 121         * If the substream has already been used, then the I/O queue may be in
 122         * an invalid state. Just in case, we do a check and try to return the
 123         * queue to its original state, if necessary.
 124         */
 125        return virtsnd_pcm_sync_stop(substream);
 126}
 127
 128/**
 129 * virtsnd_pcm_close() - Close the PCM substream.
 130 * @substream: Kernel ALSA substream.
 131 *
 132 * Context: Process context.
 133 * Return: 0.
 134 */
 135static int virtsnd_pcm_close(struct snd_pcm_substream *substream)
 136{
 137        return 0;
 138}
 139
 140/**
 141 * virtsnd_pcm_dev_set_params() - Set the parameters of the PCM substream on
 142 *                                the device side.
 143 * @vss: VirtIO PCM substream.
 144 * @buffer_bytes: Size of the hardware buffer.
 145 * @period_bytes: Size of the hardware period.
 146 * @channels: Selected number of channels.
 147 * @format: Selected sample format (SNDRV_PCM_FORMAT_XXX).
 148 * @rate: Selected frame rate.
 149 *
 150 * Context: Any context that permits to sleep.
 151 * Return: 0 on success, -errno on failure.
 152 */
 153static int virtsnd_pcm_dev_set_params(struct virtio_pcm_substream *vss,
 154                                      unsigned int buffer_bytes,
 155                                      unsigned int period_bytes,
 156                                      unsigned int channels,
 157                                      snd_pcm_format_t format,
 158                                      unsigned int rate)
 159{
 160        struct virtio_snd_msg *msg;
 161        struct virtio_snd_pcm_set_params *request;
 162        unsigned int i;
 163        int vformat = -1;
 164        int vrate = -1;
 165
 166        for (i = 0; i < ARRAY_SIZE(g_a2v_format_map); ++i)
 167                if (g_a2v_format_map[i].alsa_bit == format) {
 168                        vformat = g_a2v_format_map[i].vio_bit;
 169
 170                        break;
 171                }
 172
 173        for (i = 0; i < ARRAY_SIZE(g_a2v_rate_map); ++i)
 174                if (g_a2v_rate_map[i].rate == rate) {
 175                        vrate = g_a2v_rate_map[i].vio_bit;
 176
 177                        break;
 178                }
 179
 180        if (vformat == -1 || vrate == -1)
 181                return -EINVAL;
 182
 183        msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_SET_PARAMS,
 184                                        GFP_KERNEL);
 185        if (!msg)
 186                return -ENOMEM;
 187
 188        request = virtsnd_ctl_msg_request(msg);
 189        request->buffer_bytes = cpu_to_le32(buffer_bytes);
 190        request->period_bytes = cpu_to_le32(period_bytes);
 191        request->channels = channels;
 192        request->format = vformat;
 193        request->rate = vrate;
 194
 195        if (vss->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING))
 196                request->features |=
 197                        cpu_to_le32(1U << VIRTIO_SND_PCM_F_MSG_POLLING);
 198
 199        if (vss->features & (1U << VIRTIO_SND_PCM_F_EVT_XRUNS))
 200                request->features |=
 201                        cpu_to_le32(1U << VIRTIO_SND_PCM_F_EVT_XRUNS);
 202
 203        return virtsnd_ctl_msg_send_sync(vss->snd, msg);
 204}
 205
 206/**
 207 * virtsnd_pcm_hw_params() - Set the parameters of the PCM substream.
 208 * @substream: Kernel ALSA substream.
 209 * @hw_params: Hardware parameters.
 210 *
 211 * Context: Process context.
 212 * Return: 0 on success, -errno on failure.
 213 */
 214static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream,
 215                                 struct snd_pcm_hw_params *hw_params)
 216{
 217        struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
 218        struct virtio_device *vdev = vss->snd->vdev;
 219        int rc;
 220
 221        if (virtsnd_pcm_msg_pending_num(vss)) {
 222                dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n",
 223                        vss->sid);
 224                return -EBADFD;
 225        }
 226
 227        rc = virtsnd_pcm_dev_set_params(vss, params_buffer_bytes(hw_params),
 228                                        params_period_bytes(hw_params),
 229                                        params_channels(hw_params),
 230                                        params_format(hw_params),
 231                                        params_rate(hw_params));
 232        if (rc)
 233                return rc;
 234
 235        /*
 236         * Free previously allocated messages if ops->hw_params() is called
 237         * several times in a row, or if ops->hw_free() failed to free messages.
 238         */
 239        virtsnd_pcm_msg_free(vss);
 240
 241        return virtsnd_pcm_msg_alloc(vss, params_periods(hw_params),
 242                                     params_period_bytes(hw_params));
 243}
 244
 245/**
 246 * virtsnd_pcm_hw_free() - Reset the parameters of the PCM substream.
 247 * @substream: Kernel ALSA substream.
 248 *
 249 * Context: Process context.
 250 * Return: 0
 251 */
 252static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream)
 253{
 254        struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
 255
 256        /* If the queue is flushed, we can safely free the messages here. */
 257        if (!virtsnd_pcm_msg_pending_num(vss))
 258                virtsnd_pcm_msg_free(vss);
 259
 260        return 0;
 261}
 262
 263/**
 264 * virtsnd_pcm_prepare() - Prepare the PCM substream.
 265 * @substream: Kernel ALSA substream.
 266 *
 267 * Context: Process context.
 268 * Return: 0 on success, -errno on failure.
 269 */
 270static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
 271{
 272        struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
 273        struct virtio_device *vdev = vss->snd->vdev;
 274        struct virtio_snd_msg *msg;
 275
 276        if (!vss->suspended) {
 277                if (virtsnd_pcm_msg_pending_num(vss)) {
 278                        dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n",
 279                                vss->sid);
 280                        return -EBADFD;
 281                }
 282
 283                vss->buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
 284                vss->hw_ptr = 0;
 285                vss->msg_last_enqueued = -1;
 286        } else {
 287                struct snd_pcm_runtime *runtime = substream->runtime;
 288                unsigned int buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
 289                unsigned int period_bytes = snd_pcm_lib_period_bytes(substream);
 290                int rc;
 291
 292                rc = virtsnd_pcm_dev_set_params(vss, buffer_bytes, period_bytes,
 293                                                runtime->channels,
 294                                                runtime->format, runtime->rate);
 295                if (rc)
 296                        return rc;
 297        }
 298
 299        vss->xfer_xrun = false;
 300        vss->suspended = false;
 301        vss->msg_count = 0;
 302
 303        msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_PREPARE,
 304                                        GFP_KERNEL);
 305        if (!msg)
 306                return -ENOMEM;
 307
 308        return virtsnd_ctl_msg_send_sync(vss->snd, msg);
 309}
 310
 311/**
 312 * virtsnd_pcm_trigger() - Process command for the PCM substream.
 313 * @substream: Kernel ALSA substream.
 314 * @command: Substream command (SNDRV_PCM_TRIGGER_XXX).
 315 *
 316 * Context: Any context. Takes and releases the VirtIO substream spinlock.
 317 *          May take and release the tx/rx queue spinlock.
 318 * Return: 0 on success, -errno on failure.
 319 */
 320static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command)
 321{
 322        struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
 323        struct virtio_snd *snd = vss->snd;
 324        struct virtio_snd_queue *queue;
 325        struct virtio_snd_msg *msg;
 326        unsigned long flags;
 327        int rc;
 328
 329        switch (command) {
 330        case SNDRV_PCM_TRIGGER_START:
 331        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 332                queue = virtsnd_pcm_queue(vss);
 333
 334                spin_lock_irqsave(&queue->lock, flags);
 335                spin_lock(&vss->lock);
 336                rc = virtsnd_pcm_msg_send(vss);
 337                if (!rc)
 338                        vss->xfer_enabled = true;
 339                spin_unlock(&vss->lock);
 340                spin_unlock_irqrestore(&queue->lock, flags);
 341                if (rc)
 342                        return rc;
 343
 344                msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_START,
 345                                                GFP_KERNEL);
 346                if (!msg) {
 347                        spin_lock_irqsave(&vss->lock, flags);
 348                        vss->xfer_enabled = false;
 349                        spin_unlock_irqrestore(&vss->lock, flags);
 350
 351                        return -ENOMEM;
 352                }
 353
 354                return virtsnd_ctl_msg_send_sync(snd, msg);
 355        case SNDRV_PCM_TRIGGER_SUSPEND:
 356                vss->suspended = true;
 357                fallthrough;
 358        case SNDRV_PCM_TRIGGER_STOP:
 359                vss->stopped = true;
 360                fallthrough;
 361        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 362                spin_lock_irqsave(&vss->lock, flags);
 363                vss->xfer_enabled = false;
 364                spin_unlock_irqrestore(&vss->lock, flags);
 365
 366                msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_STOP,
 367                                                GFP_KERNEL);
 368                if (!msg)
 369                        return -ENOMEM;
 370
 371                return virtsnd_ctl_msg_send_sync(snd, msg);
 372        default:
 373                return -EINVAL;
 374        }
 375}
 376
 377/**
 378 * virtsnd_pcm_sync_stop() - Synchronous PCM substream stop.
 379 * @substream: Kernel ALSA substream.
 380 *
 381 * The function can be called both from the upper level or from the driver
 382 * itself.
 383 *
 384 * Context: Process context. Takes and releases the VirtIO substream spinlock.
 385 * Return: 0 on success, -errno on failure.
 386 */
 387static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
 388{
 389        struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
 390        struct virtio_snd *snd = vss->snd;
 391        struct virtio_snd_msg *msg;
 392        unsigned int js = msecs_to_jiffies(virtsnd_msg_timeout_ms);
 393        int rc;
 394
 395        cancel_work_sync(&vss->elapsed_period);
 396
 397        if (!vss->stopped)
 398                return 0;
 399
 400        msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_RELEASE,
 401                                        GFP_KERNEL);
 402        if (!msg)
 403                return -ENOMEM;
 404
 405        rc = virtsnd_ctl_msg_send_sync(snd, msg);
 406        if (rc)
 407                return rc;
 408
 409        /*
 410         * The spec states that upon receipt of the RELEASE command "the device
 411         * MUST complete all pending I/O messages for the specified stream ID".
 412         * Thus, we consider the absence of I/O messages in the queue as an
 413         * indication that the substream has been released.
 414         */
 415        rc = wait_event_interruptible_timeout(vss->msg_empty,
 416                                              !virtsnd_pcm_msg_pending_num(vss),
 417                                              js);
 418        if (rc <= 0) {
 419                dev_warn(&snd->vdev->dev, "SID %u: failed to flush I/O queue\n",
 420                         vss->sid);
 421
 422                return !rc ? -ETIMEDOUT : rc;
 423        }
 424
 425        vss->stopped = false;
 426
 427        return 0;
 428}
 429
 430/**
 431 * virtsnd_pcm_pointer() - Get the current hardware position for the PCM
 432 *                         substream.
 433 * @substream: Kernel ALSA substream.
 434 *
 435 * Context: Any context. Takes and releases the VirtIO substream spinlock.
 436 * Return: Hardware position in frames inside [0 ... buffer_size) range.
 437 */
 438static snd_pcm_uframes_t
 439virtsnd_pcm_pointer(struct snd_pcm_substream *substream)
 440{
 441        struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
 442        snd_pcm_uframes_t hw_ptr = SNDRV_PCM_POS_XRUN;
 443        unsigned long flags;
 444
 445        spin_lock_irqsave(&vss->lock, flags);
 446        if (!vss->xfer_xrun)
 447                hw_ptr = bytes_to_frames(substream->runtime, vss->hw_ptr);
 448        spin_unlock_irqrestore(&vss->lock, flags);
 449
 450        return hw_ptr;
 451}
 452
 453/* PCM substream operators map. */
 454const struct snd_pcm_ops virtsnd_pcm_ops = {
 455        .open = virtsnd_pcm_open,
 456        .close = virtsnd_pcm_close,
 457        .ioctl = snd_pcm_lib_ioctl,
 458        .hw_params = virtsnd_pcm_hw_params,
 459        .hw_free = virtsnd_pcm_hw_free,
 460        .prepare = virtsnd_pcm_prepare,
 461        .trigger = virtsnd_pcm_trigger,
 462        .sync_stop = virtsnd_pcm_sync_stop,
 463        .pointer = virtsnd_pcm_pointer,
 464};
 465