Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: virtio: PCM substream operators

Introduce the operators required for the operation of substreams.

Signed-off-by: Anton Yakovlev <anton.yakovlev@opensynergy.com>
Link: https://lore.kernel.org/r/20210302164709.3142702-7-anton.yakovlev@opensynergy.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Anton Yakovlev and committed by
Takashi Iwai
da76e9f3 f40a2867

+454 -1
+2 -1
sound/virtio/Makefile
··· 6 6 virtio_card.o \ 7 7 virtio_ctl_msg.o \ 8 8 virtio_pcm.o \ 9 - virtio_pcm_msg.o 9 + virtio_pcm_msg.o \ 10 + virtio_pcm_ops.o 10 11
+2
sound/virtio/virtio_pcm.c
··· 470 470 471 471 for (kss = ks->substream; kss; kss = kss->next) 472 472 vs->substreams[kss->number]->substream = kss; 473 + 474 + snd_pcm_set_ops(vpcm->pcm, i, &virtsnd_pcm_ops); 473 475 } 474 476 475 477 snd_pcm_set_managed_buffer_all(vpcm->pcm,
+5
sound/virtio/virtio_pcm.h
··· 29 29 * @hw_ptr: Substream hardware pointer value in bytes [0 ... buffer_bytes). 30 30 * @xfer_enabled: Data transfer state (0 - off, 1 - on). 31 31 * @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun). 32 + * @stopped: True if the substream is stopped and must be released on the device 33 + * side. 32 34 * @msgs: Allocated I/O messages. 33 35 * @nmsgs: Number of allocated I/O messages. 34 36 * @msg_last_enqueued: Index of the last I/O message added to the virtqueue. ··· 51 49 size_t hw_ptr; 52 50 bool xfer_enabled; 53 51 bool xfer_xrun; 52 + bool stopped; 54 53 struct virtio_pcm_msg **msgs; 55 54 unsigned int nmsgs; 56 55 int msg_last_enqueued; ··· 82 79 struct snd_pcm *pcm; 83 80 struct virtio_pcm_stream streams[SNDRV_PCM_STREAM_LAST + 1]; 84 81 }; 82 + 83 + extern const struct snd_pcm_ops virtsnd_pcm_ops; 85 84 86 85 int virtsnd_pcm_validate(struct virtio_device *vdev); 87 86
+445
sound/virtio/virtio_pcm_ops.c
··· 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. */ 40 + struct virtsnd_a2v_format { 41 + snd_pcm_format_t alsa_bit; 42 + unsigned int vio_bit; 43 + }; 44 + 45 + static 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. */ 75 + struct virtsnd_a2v_rate { 76 + unsigned int rate; 77 + unsigned int vio_bit; 78 + }; 79 + 80 + static 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 + 96 + static 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 + */ 105 + static 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 + 119 + /* 120 + * If the substream has already been used, then the I/O queue may be in 121 + * an invalid state. Just in case, we do a check and try to return the 122 + * queue to its original state, if necessary. 123 + */ 124 + return virtsnd_pcm_sync_stop(substream); 125 + } 126 + 127 + /** 128 + * virtsnd_pcm_close() - Close the PCM substream. 129 + * @substream: Kernel ALSA substream. 130 + * 131 + * Context: Process context. 132 + * Return: 0. 133 + */ 134 + static int virtsnd_pcm_close(struct snd_pcm_substream *substream) 135 + { 136 + return 0; 137 + } 138 + 139 + /** 140 + * virtsnd_pcm_dev_set_params() - Set the parameters of the PCM substream on 141 + * the device side. 142 + * @vss: VirtIO PCM substream. 143 + * @buffer_bytes: Size of the hardware buffer. 144 + * @period_bytes: Size of the hardware period. 145 + * @channels: Selected number of channels. 146 + * @format: Selected sample format (SNDRV_PCM_FORMAT_XXX). 147 + * @rate: Selected frame rate. 148 + * 149 + * Context: Any context that permits to sleep. 150 + * Return: 0 on success, -errno on failure. 151 + */ 152 + static int virtsnd_pcm_dev_set_params(struct virtio_pcm_substream *vss, 153 + unsigned int buffer_bytes, 154 + unsigned int period_bytes, 155 + unsigned int channels, 156 + snd_pcm_format_t format, 157 + unsigned int rate) 158 + { 159 + struct virtio_snd_msg *msg; 160 + struct virtio_snd_pcm_set_params *request; 161 + unsigned int i; 162 + int vformat = -1; 163 + int vrate = -1; 164 + 165 + for (i = 0; i < ARRAY_SIZE(g_a2v_format_map); ++i) 166 + if (g_a2v_format_map[i].alsa_bit == format) { 167 + vformat = g_a2v_format_map[i].vio_bit; 168 + 169 + break; 170 + } 171 + 172 + for (i = 0; i < ARRAY_SIZE(g_a2v_rate_map); ++i) 173 + if (g_a2v_rate_map[i].rate == rate) { 174 + vrate = g_a2v_rate_map[i].vio_bit; 175 + 176 + break; 177 + } 178 + 179 + if (vformat == -1 || vrate == -1) 180 + return -EINVAL; 181 + 182 + msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_SET_PARAMS, 183 + GFP_KERNEL); 184 + if (!msg) 185 + return -ENOMEM; 186 + 187 + request = virtsnd_ctl_msg_request(msg); 188 + request->buffer_bytes = cpu_to_le32(buffer_bytes); 189 + request->period_bytes = cpu_to_le32(period_bytes); 190 + request->channels = channels; 191 + request->format = vformat; 192 + request->rate = vrate; 193 + 194 + if (vss->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING)) 195 + request->features |= 196 + cpu_to_le32(1U << VIRTIO_SND_PCM_F_MSG_POLLING); 197 + 198 + if (vss->features & (1U << VIRTIO_SND_PCM_F_EVT_XRUNS)) 199 + request->features |= 200 + cpu_to_le32(1U << VIRTIO_SND_PCM_F_EVT_XRUNS); 201 + 202 + return virtsnd_ctl_msg_send_sync(vss->snd, msg); 203 + } 204 + 205 + /** 206 + * virtsnd_pcm_hw_params() - Set the parameters of the PCM substream. 207 + * @substream: Kernel ALSA substream. 208 + * @hw_params: Hardware parameters. 209 + * 210 + * Context: Process context. 211 + * Return: 0 on success, -errno on failure. 212 + */ 213 + static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream, 214 + struct snd_pcm_hw_params *hw_params) 215 + { 216 + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); 217 + struct virtio_device *vdev = vss->snd->vdev; 218 + int rc; 219 + 220 + if (virtsnd_pcm_msg_pending_num(vss)) { 221 + dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", 222 + vss->sid); 223 + return -EBADFD; 224 + } 225 + 226 + rc = virtsnd_pcm_dev_set_params(vss, params_buffer_bytes(hw_params), 227 + params_period_bytes(hw_params), 228 + params_channels(hw_params), 229 + params_format(hw_params), 230 + params_rate(hw_params)); 231 + if (rc) 232 + return rc; 233 + 234 + /* 235 + * Free previously allocated messages if ops->hw_params() is called 236 + * several times in a row, or if ops->hw_free() failed to free messages. 237 + */ 238 + virtsnd_pcm_msg_free(vss); 239 + 240 + return virtsnd_pcm_msg_alloc(vss, params_periods(hw_params), 241 + params_period_bytes(hw_params)); 242 + } 243 + 244 + /** 245 + * virtsnd_pcm_hw_free() - Reset the parameters of the PCM substream. 246 + * @substream: Kernel ALSA substream. 247 + * 248 + * Context: Process context. 249 + * Return: 0 250 + */ 251 + static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream) 252 + { 253 + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); 254 + 255 + /* If the queue is flushed, we can safely free the messages here. */ 256 + if (!virtsnd_pcm_msg_pending_num(vss)) 257 + virtsnd_pcm_msg_free(vss); 258 + 259 + return 0; 260 + } 261 + 262 + /** 263 + * virtsnd_pcm_prepare() - Prepare the PCM substream. 264 + * @substream: Kernel ALSA substream. 265 + * 266 + * Context: Process context. 267 + * Return: 0 on success, -errno on failure. 268 + */ 269 + static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream) 270 + { 271 + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); 272 + struct virtio_device *vdev = vss->snd->vdev; 273 + struct virtio_snd_msg *msg; 274 + 275 + if (virtsnd_pcm_msg_pending_num(vss)) { 276 + dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", 277 + vss->sid); 278 + return -EBADFD; 279 + } 280 + 281 + vss->buffer_bytes = snd_pcm_lib_buffer_bytes(substream); 282 + vss->hw_ptr = 0; 283 + vss->xfer_xrun = false; 284 + vss->msg_last_enqueued = -1; 285 + vss->msg_count = 0; 286 + 287 + msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_PREPARE, 288 + GFP_KERNEL); 289 + if (!msg) 290 + return -ENOMEM; 291 + 292 + return virtsnd_ctl_msg_send_sync(vss->snd, msg); 293 + } 294 + 295 + /** 296 + * virtsnd_pcm_trigger() - Process command for the PCM substream. 297 + * @substream: Kernel ALSA substream. 298 + * @command: Substream command (SNDRV_PCM_TRIGGER_XXX). 299 + * 300 + * Context: Any context. Takes and releases the VirtIO substream spinlock. 301 + * May take and release the tx/rx queue spinlock. 302 + * Return: 0 on success, -errno on failure. 303 + */ 304 + static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command) 305 + { 306 + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); 307 + struct virtio_snd *snd = vss->snd; 308 + struct virtio_snd_queue *queue; 309 + struct virtio_snd_msg *msg; 310 + unsigned long flags; 311 + int rc; 312 + 313 + switch (command) { 314 + case SNDRV_PCM_TRIGGER_START: 315 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 316 + queue = virtsnd_pcm_queue(vss); 317 + 318 + spin_lock_irqsave(&queue->lock, flags); 319 + spin_lock(&vss->lock); 320 + rc = virtsnd_pcm_msg_send(vss); 321 + if (!rc) 322 + vss->xfer_enabled = true; 323 + spin_unlock(&vss->lock); 324 + spin_unlock_irqrestore(&queue->lock, flags); 325 + if (rc) 326 + return rc; 327 + 328 + msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_START, 329 + GFP_KERNEL); 330 + if (!msg) { 331 + spin_lock_irqsave(&vss->lock, flags); 332 + vss->xfer_enabled = false; 333 + spin_unlock_irqrestore(&vss->lock, flags); 334 + 335 + return -ENOMEM; 336 + } 337 + 338 + return virtsnd_ctl_msg_send_sync(snd, msg); 339 + case SNDRV_PCM_TRIGGER_STOP: 340 + vss->stopped = true; 341 + fallthrough; 342 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 343 + spin_lock_irqsave(&vss->lock, flags); 344 + vss->xfer_enabled = false; 345 + spin_unlock_irqrestore(&vss->lock, flags); 346 + 347 + msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_STOP, 348 + GFP_KERNEL); 349 + if (!msg) 350 + return -ENOMEM; 351 + 352 + return virtsnd_ctl_msg_send_sync(snd, msg); 353 + default: 354 + return -EINVAL; 355 + } 356 + } 357 + 358 + /** 359 + * virtsnd_pcm_sync_stop() - Synchronous PCM substream stop. 360 + * @substream: Kernel ALSA substream. 361 + * 362 + * The function can be called both from the upper level or from the driver 363 + * itself. 364 + * 365 + * Context: Process context. Takes and releases the VirtIO substream spinlock. 366 + * Return: 0 on success, -errno on failure. 367 + */ 368 + static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream) 369 + { 370 + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); 371 + struct virtio_snd *snd = vss->snd; 372 + struct virtio_snd_msg *msg; 373 + unsigned int js = msecs_to_jiffies(virtsnd_msg_timeout_ms); 374 + int rc; 375 + 376 + cancel_work_sync(&vss->elapsed_period); 377 + 378 + if (!vss->stopped) 379 + return 0; 380 + 381 + msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_RELEASE, 382 + GFP_KERNEL); 383 + if (!msg) 384 + return -ENOMEM; 385 + 386 + rc = virtsnd_ctl_msg_send_sync(snd, msg); 387 + if (rc) 388 + return rc; 389 + 390 + /* 391 + * The spec states that upon receipt of the RELEASE command "the device 392 + * MUST complete all pending I/O messages for the specified stream ID". 393 + * Thus, we consider the absence of I/O messages in the queue as an 394 + * indication that the substream has been released. 395 + */ 396 + rc = wait_event_interruptible_timeout(vss->msg_empty, 397 + !virtsnd_pcm_msg_pending_num(vss), 398 + js); 399 + if (rc <= 0) { 400 + dev_warn(&snd->vdev->dev, "SID %u: failed to flush I/O queue\n", 401 + vss->sid); 402 + 403 + return !rc ? -ETIMEDOUT : rc; 404 + } 405 + 406 + vss->stopped = false; 407 + 408 + return 0; 409 + } 410 + 411 + /** 412 + * virtsnd_pcm_pointer() - Get the current hardware position for the PCM 413 + * substream. 414 + * @substream: Kernel ALSA substream. 415 + * 416 + * Context: Any context. Takes and releases the VirtIO substream spinlock. 417 + * Return: Hardware position in frames inside [0 ... buffer_size) range. 418 + */ 419 + static snd_pcm_uframes_t 420 + virtsnd_pcm_pointer(struct snd_pcm_substream *substream) 421 + { 422 + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); 423 + snd_pcm_uframes_t hw_ptr = SNDRV_PCM_POS_XRUN; 424 + unsigned long flags; 425 + 426 + spin_lock_irqsave(&vss->lock, flags); 427 + if (!vss->xfer_xrun) 428 + hw_ptr = bytes_to_frames(substream->runtime, vss->hw_ptr); 429 + spin_unlock_irqrestore(&vss->lock, flags); 430 + 431 + return hw_ptr; 432 + } 433 + 434 + /* PCM substream operators map. */ 435 + const struct snd_pcm_ops virtsnd_pcm_ops = { 436 + .open = virtsnd_pcm_open, 437 + .close = virtsnd_pcm_close, 438 + .ioctl = snd_pcm_lib_ioctl, 439 + .hw_params = virtsnd_pcm_hw_params, 440 + .hw_free = virtsnd_pcm_hw_free, 441 + .prepare = virtsnd_pcm_prepare, 442 + .trigger = virtsnd_pcm_trigger, 443 + .sync_stop = virtsnd_pcm_sync_stop, 444 + .pointer = virtsnd_pcm_pointer, 445 + };