1 From a1cde5ccba57562aa77739b63b50586e6b197b52 Mon Sep 17 00:00:00 2001
2 From: Anton Yakovlev <anton.yakovlev@opensynergy.com>
3 Date: Tue, 2 Mar 2021 17:47:02 +0100
4 Subject: [PATCH] ALSA: virtio: add virtio sound driver
6 Introduce skeleton of the virtio sound driver. The driver implements
7 the virtio sound device specification, which has become part of the
10 Initial initialization of the device, virtqueues and creation of an
11 empty ALSA sound device.
13 Signed-off-by: Anton Yakovlev <anton.yakovlev@opensynergy.com>
14 Link: https://lore.kernel.org/r/20210302164709.3142702-3-anton.yakovlev@opensynergy.com
15 Signed-off-by: Takashi Iwai <tiwai@suse.de>
16 Signed-off-by: Vasyl Vavrychuk <vasyl.vavrychuk@opensynergy.com>
19 include/uapi/linux/virtio_snd.h | 334 ++++++++++++++++++++++++++++++++
22 sound/virtio/Kconfig | 10 +
23 sound/virtio/Makefile | 7 +
24 sound/virtio/virtio_card.c | 324 +++++++++++++++++++++++++++++++
25 sound/virtio/virtio_card.h | 65 +++++++
26 8 files changed, 753 insertions(+), 1 deletion(-)
27 create mode 100644 include/uapi/linux/virtio_snd.h
28 create mode 100644 sound/virtio/Kconfig
29 create mode 100644 sound/virtio/Makefile
30 create mode 100644 sound/virtio/virtio_card.c
31 create mode 100644 sound/virtio/virtio_card.h
33 diff --git a/MAINTAINERS b/MAINTAINERS
34 index 407ae5c24566..49772b741967 100644
37 @@ -18670,6 +18670,15 @@ W: https://virtio-mem.gitlab.io/
38 F: drivers/virtio/virtio_mem.c
39 F: include/uapi/linux/virtio_mem.h
42 +M: Anton Yakovlev <anton.yakovlev@opensynergy.com>
43 +M: "Michael S. Tsirkin" <mst@redhat.com>
44 +L: virtualization@lists.linux-foundation.org
45 +L: alsa-devel@alsa-project.org (moderated for non-subscribers)
47 +F: include/uapi/linux/virtio_snd.h
50 VIRTUAL BOX GUEST DEVICE DRIVER
51 M: Hans de Goede <hdegoede@redhat.com>
52 M: Arnd Bergmann <arnd@arndb.de>
53 diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h
55 index 000000000000..dfe49547a7b0
57 +++ b/include/uapi/linux/virtio_snd.h
59 +/* SPDX-License-Identifier: BSD-3-Clause */
61 + * Copyright (C) 2021 OpenSynergy GmbH
63 +#ifndef VIRTIO_SND_IF_H
64 +#define VIRTIO_SND_IF_H
66 +#include <linux/virtio_types.h>
68 +/*******************************************************************************
69 + * CONFIGURATION SPACE
71 +struct virtio_snd_config {
72 + /* # of available physical jacks */
74 + /* # of available PCM streams */
76 + /* # of available channel maps */
81 + /* device virtqueue indexes */
82 + VIRTIO_SND_VQ_CONTROL = 0,
83 + VIRTIO_SND_VQ_EVENT,
86 + /* # of device virtqueues */
90 +/*******************************************************************************
91 + * COMMON DEFINITIONS
94 +/* supported dataflow directions */
96 + VIRTIO_SND_D_OUTPUT = 0,
101 + /* jack control request types */
102 + VIRTIO_SND_R_JACK_INFO = 1,
103 + VIRTIO_SND_R_JACK_REMAP,
105 + /* PCM control request types */
106 + VIRTIO_SND_R_PCM_INFO = 0x0100,
107 + VIRTIO_SND_R_PCM_SET_PARAMS,
108 + VIRTIO_SND_R_PCM_PREPARE,
109 + VIRTIO_SND_R_PCM_RELEASE,
110 + VIRTIO_SND_R_PCM_START,
111 + VIRTIO_SND_R_PCM_STOP,
113 + /* channel map control request types */
114 + VIRTIO_SND_R_CHMAP_INFO = 0x0200,
116 + /* jack event types */
117 + VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000,
118 + VIRTIO_SND_EVT_JACK_DISCONNECTED,
120 + /* PCM event types */
121 + VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100,
122 + VIRTIO_SND_EVT_PCM_XRUN,
124 + /* common status codes */
125 + VIRTIO_SND_S_OK = 0x8000,
126 + VIRTIO_SND_S_BAD_MSG,
127 + VIRTIO_SND_S_NOT_SUPP,
128 + VIRTIO_SND_S_IO_ERR
132 +struct virtio_snd_hdr {
136 +/* event notification */
137 +struct virtio_snd_event {
138 + /* VIRTIO_SND_EVT_XXX */
139 + struct virtio_snd_hdr hdr;
140 + /* optional event data */
144 +/* common control request to query an item information */
145 +struct virtio_snd_query_info {
146 + /* VIRTIO_SND_R_XXX_INFO */
147 + struct virtio_snd_hdr hdr;
148 + /* item start identifier */
150 + /* item count to query */
152 + /* item information size in bytes */
156 +/* common item information header */
157 +struct virtio_snd_info {
158 + /* function group node id (High Definition Audio Specification 7.1.2) */
162 +/*******************************************************************************
163 + * JACK CONTROL MESSAGES
165 +struct virtio_snd_jack_hdr {
166 + /* VIRTIO_SND_R_JACK_XXX */
167 + struct virtio_snd_hdr hdr;
168 + /* 0 ... virtio_snd_config::jacks - 1 */
172 +/* supported jack features */
174 + VIRTIO_SND_JACK_F_REMAP = 0
177 +struct virtio_snd_jack_info {
178 + /* common header */
179 + struct virtio_snd_info hdr;
180 + /* supported feature bit map (1 << VIRTIO_SND_JACK_F_XXX) */
182 + /* pin configuration (High Definition Audio Specification 7.3.3.31) */
183 + __le32 hda_reg_defconf;
184 + /* pin capabilities (High Definition Audio Specification 7.3.4.9) */
185 + __le32 hda_reg_caps;
186 + /* current jack connection status (0: disconnected, 1: connected) */
192 +/* jack remapping control request */
193 +struct virtio_snd_jack_remap {
194 + /* .code = VIRTIO_SND_R_JACK_REMAP */
195 + struct virtio_snd_jack_hdr hdr;
196 + /* selected association number */
197 + __le32 association;
198 + /* selected sequence number */
202 +/*******************************************************************************
203 + * PCM CONTROL MESSAGES
205 +struct virtio_snd_pcm_hdr {
206 + /* VIRTIO_SND_R_PCM_XXX */
207 + struct virtio_snd_hdr hdr;
208 + /* 0 ... virtio_snd_config::streams - 1 */
212 +/* supported PCM stream features */
214 + VIRTIO_SND_PCM_F_SHMEM_HOST = 0,
215 + VIRTIO_SND_PCM_F_SHMEM_GUEST,
216 + VIRTIO_SND_PCM_F_MSG_POLLING,
217 + VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS,
218 + VIRTIO_SND_PCM_F_EVT_XRUNS
221 +/* supported PCM sample formats */
223 + /* analog formats (width / physical width) */
224 + VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0, /* 4 / 4 bits */
225 + VIRTIO_SND_PCM_FMT_MU_LAW, /* 8 / 8 bits */
226 + VIRTIO_SND_PCM_FMT_A_LAW, /* 8 / 8 bits */
227 + VIRTIO_SND_PCM_FMT_S8, /* 8 / 8 bits */
228 + VIRTIO_SND_PCM_FMT_U8, /* 8 / 8 bits */
229 + VIRTIO_SND_PCM_FMT_S16, /* 16 / 16 bits */
230 + VIRTIO_SND_PCM_FMT_U16, /* 16 / 16 bits */
231 + VIRTIO_SND_PCM_FMT_S18_3, /* 18 / 24 bits */
232 + VIRTIO_SND_PCM_FMT_U18_3, /* 18 / 24 bits */
233 + VIRTIO_SND_PCM_FMT_S20_3, /* 20 / 24 bits */
234 + VIRTIO_SND_PCM_FMT_U20_3, /* 20 / 24 bits */
235 + VIRTIO_SND_PCM_FMT_S24_3, /* 24 / 24 bits */
236 + VIRTIO_SND_PCM_FMT_U24_3, /* 24 / 24 bits */
237 + VIRTIO_SND_PCM_FMT_S20, /* 20 / 32 bits */
238 + VIRTIO_SND_PCM_FMT_U20, /* 20 / 32 bits */
239 + VIRTIO_SND_PCM_FMT_S24, /* 24 / 32 bits */
240 + VIRTIO_SND_PCM_FMT_U24, /* 24 / 32 bits */
241 + VIRTIO_SND_PCM_FMT_S32, /* 32 / 32 bits */
242 + VIRTIO_SND_PCM_FMT_U32, /* 32 / 32 bits */
243 + VIRTIO_SND_PCM_FMT_FLOAT, /* 32 / 32 bits */
244 + VIRTIO_SND_PCM_FMT_FLOAT64, /* 64 / 64 bits */
245 + /* digital formats (width / physical width) */
246 + VIRTIO_SND_PCM_FMT_DSD_U8, /* 8 / 8 bits */
247 + VIRTIO_SND_PCM_FMT_DSD_U16, /* 16 / 16 bits */
248 + VIRTIO_SND_PCM_FMT_DSD_U32, /* 32 / 32 bits */
249 + VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */
252 +/* supported PCM frame rates */
254 + VIRTIO_SND_PCM_RATE_5512 = 0,
255 + VIRTIO_SND_PCM_RATE_8000,
256 + VIRTIO_SND_PCM_RATE_11025,
257 + VIRTIO_SND_PCM_RATE_16000,
258 + VIRTIO_SND_PCM_RATE_22050,
259 + VIRTIO_SND_PCM_RATE_32000,
260 + VIRTIO_SND_PCM_RATE_44100,
261 + VIRTIO_SND_PCM_RATE_48000,
262 + VIRTIO_SND_PCM_RATE_64000,
263 + VIRTIO_SND_PCM_RATE_88200,
264 + VIRTIO_SND_PCM_RATE_96000,
265 + VIRTIO_SND_PCM_RATE_176400,
266 + VIRTIO_SND_PCM_RATE_192000,
267 + VIRTIO_SND_PCM_RATE_384000
270 +struct virtio_snd_pcm_info {
271 + /* common header */
272 + struct virtio_snd_info hdr;
273 + /* supported feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */
275 + /* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */
277 + /* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */
279 + /* dataflow direction (VIRTIO_SND_D_XXX) */
281 + /* minimum # of supported channels */
283 + /* maximum # of supported channels */
289 +/* set PCM stream format */
290 +struct virtio_snd_pcm_set_params {
291 + /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */
292 + struct virtio_snd_pcm_hdr hdr;
293 + /* size of the hardware buffer */
294 + __le32 buffer_bytes;
295 + /* size of the hardware period */
296 + __le32 period_bytes;
297 + /* selected feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */
299 + /* selected # of channels */
301 + /* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */
303 + /* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */
309 +/*******************************************************************************
313 +/* I/O request header */
314 +struct virtio_snd_pcm_xfer {
315 + /* 0 ... virtio_snd_config::streams - 1 */
319 +/* I/O request status */
320 +struct virtio_snd_pcm_status {
321 + /* VIRTIO_SND_S_XXX */
323 + /* current device latency */
324 + __le32 latency_bytes;
327 +/*******************************************************************************
328 + * CHANNEL MAP CONTROL MESSAGES
330 +struct virtio_snd_chmap_hdr {
331 + /* VIRTIO_SND_R_CHMAP_XXX */
332 + struct virtio_snd_hdr hdr;
333 + /* 0 ... virtio_snd_config::chmaps - 1 */
337 +/* standard channel position definition */
339 + VIRTIO_SND_CHMAP_NONE = 0, /* undefined */
340 + VIRTIO_SND_CHMAP_NA, /* silent */
341 + VIRTIO_SND_CHMAP_MONO, /* mono stream */
342 + VIRTIO_SND_CHMAP_FL, /* front left */
343 + VIRTIO_SND_CHMAP_FR, /* front right */
344 + VIRTIO_SND_CHMAP_RL, /* rear left */
345 + VIRTIO_SND_CHMAP_RR, /* rear right */
346 + VIRTIO_SND_CHMAP_FC, /* front center */
347 + VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */
348 + VIRTIO_SND_CHMAP_SL, /* side left */
349 + VIRTIO_SND_CHMAP_SR, /* side right */
350 + VIRTIO_SND_CHMAP_RC, /* rear center */
351 + VIRTIO_SND_CHMAP_FLC, /* front left center */
352 + VIRTIO_SND_CHMAP_FRC, /* front right center */
353 + VIRTIO_SND_CHMAP_RLC, /* rear left center */
354 + VIRTIO_SND_CHMAP_RRC, /* rear right center */
355 + VIRTIO_SND_CHMAP_FLW, /* front left wide */
356 + VIRTIO_SND_CHMAP_FRW, /* front right wide */
357 + VIRTIO_SND_CHMAP_FLH, /* front left high */
358 + VIRTIO_SND_CHMAP_FCH, /* front center high */
359 + VIRTIO_SND_CHMAP_FRH, /* front right high */
360 + VIRTIO_SND_CHMAP_TC, /* top center */
361 + VIRTIO_SND_CHMAP_TFL, /* top front left */
362 + VIRTIO_SND_CHMAP_TFR, /* top front right */
363 + VIRTIO_SND_CHMAP_TFC, /* top front center */
364 + VIRTIO_SND_CHMAP_TRL, /* top rear left */
365 + VIRTIO_SND_CHMAP_TRR, /* top rear right */
366 + VIRTIO_SND_CHMAP_TRC, /* top rear center */
367 + VIRTIO_SND_CHMAP_TFLC, /* top front left center */
368 + VIRTIO_SND_CHMAP_TFRC, /* top front right center */
369 + VIRTIO_SND_CHMAP_TSL, /* top side left */
370 + VIRTIO_SND_CHMAP_TSR, /* top side right */
371 + VIRTIO_SND_CHMAP_LLFE, /* left LFE */
372 + VIRTIO_SND_CHMAP_RLFE, /* right LFE */
373 + VIRTIO_SND_CHMAP_BC, /* bottom center */
374 + VIRTIO_SND_CHMAP_BLC, /* bottom left center */
375 + VIRTIO_SND_CHMAP_BRC /* bottom right center */
378 +/* maximum possible number of channels */
379 +#define VIRTIO_SND_CHMAP_MAX_SIZE 18
381 +struct virtio_snd_chmap_info {
382 + /* common header */
383 + struct virtio_snd_info hdr;
384 + /* dataflow direction (VIRTIO_SND_D_XXX) */
386 + /* # of valid channel position values */
388 + /* channel position values (VIRTIO_SND_CHMAP_XXX) */
389 + __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE];
392 +#endif /* VIRTIO_SND_IF_H */
393 diff --git a/sound/Kconfig b/sound/Kconfig
394 index 36785410fbe1..e56d96d2b11c 100644
397 @@ -99,6 +99,8 @@ source "sound/synth/Kconfig"
399 source "sound/xen/Kconfig"
401 +source "sound/virtio/Kconfig"
406 diff --git a/sound/Makefile b/sound/Makefile
407 index 797ecdcd35e2..04ef04b1168f 100644
411 obj-$(CONFIG_SOUND) += soundcore.o
412 obj-$(CONFIG_DMASOUND) += oss/dmasound/
413 obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
414 - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/
415 + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \
417 obj-$(CONFIG_SND_AOA) += aoa/
419 # This one must be compilable even if sound is configured out
420 diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig
422 index 000000000000..094cba24ee5b
424 +++ b/sound/virtio/Kconfig
426 +# SPDX-License-Identifier: GPL-2.0+
427 +# Sound card driver for virtio
430 + tristate "Virtio sound driver"
435 + This is the virtual sound driver for virtio. Say Y or M.
436 diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile
438 index 000000000000..8c87ebb9982b
440 +++ b/sound/virtio/Makefile
442 +# SPDX-License-Identifier: GPL-2.0+
444 +obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
446 +virtio_snd-objs := \
449 diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
451 index 000000000000..5a37056858e9
453 +++ b/sound/virtio/virtio_card.c
455 +// SPDX-License-Identifier: GPL-2.0+
457 + * virtio-snd: Virtio sound device
458 + * Copyright (C) 2021 OpenSynergy GmbH
460 +#include <linux/module.h>
461 +#include <linux/moduleparam.h>
462 +#include <linux/virtio_config.h>
463 +#include <sound/initval.h>
464 +#include <uapi/linux/virtio_ids.h>
466 +#include "virtio_card.h"
468 +static void virtsnd_remove(struct virtio_device *vdev);
471 + * virtsnd_event_send() - Add an event to the event queue.
472 + * @vqueue: Underlying event virtqueue.
474 + * @notify: Indicates whether or not to send a notification to the device.
475 + * @gfp: Kernel flags for memory allocation.
477 + * Context: Any context.
479 +static void virtsnd_event_send(struct virtqueue *vqueue,
480 + struct virtio_snd_event *event, bool notify,
483 + struct scatterlist sg;
484 + struct scatterlist *psgs[1] = { &sg };
486 + /* reset event content */
487 + memset(event, 0, sizeof(*event));
489 + sg_init_one(&sg, event, sizeof(*event));
491 + if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify)
494 + if (virtqueue_kick_prepare(vqueue))
495 + virtqueue_notify(vqueue);
499 + * virtsnd_event_dispatch() - Dispatch an event from the device side.
500 + * @snd: VirtIO sound device.
501 + * @event: VirtIO sound event.
503 + * Context: Any context.
505 +static void virtsnd_event_dispatch(struct virtio_snd *snd,
506 + struct virtio_snd_event *event)
511 + * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
512 + * @vqueue: Underlying event virtqueue.
514 + * This callback function is called upon a vring interrupt request from the
517 + * Context: Interrupt context.
519 +static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
521 + struct virtio_snd *snd = vqueue->vdev->priv;
522 + struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
523 + struct virtio_snd_event *event;
525 + unsigned long flags;
527 + spin_lock_irqsave(&queue->lock, flags);
529 + virtqueue_disable_cb(vqueue);
530 + while ((event = virtqueue_get_buf(vqueue, &length))) {
531 + virtsnd_event_dispatch(snd, event);
532 + virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
534 + if (unlikely(virtqueue_is_broken(vqueue)))
536 + } while (!virtqueue_enable_cb(vqueue));
537 + spin_unlock_irqrestore(&queue->lock, flags);
541 + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
542 + * @snd: VirtIO sound device.
544 + * After calling this function, the event queue is disabled.
546 + * Context: Any context.
547 + * Return: 0 on success, -errno on failure.
549 +static int virtsnd_find_vqs(struct virtio_snd *snd)
551 + struct virtio_device *vdev = snd->vdev;
552 + static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
553 + [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb
555 + static const char *names[VIRTIO_SND_VQ_MAX] = {
556 + [VIRTIO_SND_VQ_EVENT] = "virtsnd-event"
558 + struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
563 + rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
566 + dev_err(&vdev->dev, "failed to initialize virtqueues\n");
570 + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
571 + snd->queues[i].vqueue = vqs[i];
573 + /* Allocate events and populate the event queue */
574 + virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
576 + n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
578 + snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
580 + if (!snd->event_msgs)
583 + for (i = 0; i < n; ++i)
584 + virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
585 + &snd->event_msgs[i], false, GFP_KERNEL);
591 + * virtsnd_enable_event_vq() - Enable the event virtqueue.
592 + * @snd: VirtIO sound device.
594 + * Context: Any context.
596 +static void virtsnd_enable_event_vq(struct virtio_snd *snd)
598 + struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
600 + if (!virtqueue_enable_cb(queue->vqueue))
601 + virtsnd_event_notify_cb(queue->vqueue);
605 + * virtsnd_disable_event_vq() - Disable the event virtqueue.
606 + * @snd: VirtIO sound device.
608 + * Context: Any context.
610 +static void virtsnd_disable_event_vq(struct virtio_snd *snd)
612 + struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
613 + struct virtio_snd_event *event;
615 + unsigned long flags;
617 + if (queue->vqueue) {
618 + spin_lock_irqsave(&queue->lock, flags);
619 + virtqueue_disable_cb(queue->vqueue);
620 + while ((event = virtqueue_get_buf(queue->vqueue, &length)))
621 + virtsnd_event_dispatch(snd, event);
622 + spin_unlock_irqrestore(&queue->lock, flags);
627 + * virtsnd_build_devs() - Read configuration and build ALSA devices.
628 + * @snd: VirtIO sound device.
630 + * Context: Any context that permits to sleep.
631 + * Return: 0 on success, -errno on failure.
633 +static int virtsnd_build_devs(struct virtio_snd *snd)
635 + struct virtio_device *vdev = snd->vdev;
636 + struct device *dev = &vdev->dev;
639 + rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
640 + THIS_MODULE, 0, &snd->card);
644 + snd->card->private_data = snd;
646 + strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
647 + sizeof(snd->card->driver));
648 + strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
649 + sizeof(snd->card->shortname));
650 + if (dev->parent->bus)
651 + snprintf(snd->card->longname, sizeof(snd->card->longname),
652 + VIRTIO_SND_CARD_NAME " at %s/%s/%s",
653 + dev->parent->bus->name, dev_name(dev->parent),
656 + snprintf(snd->card->longname, sizeof(snd->card->longname),
657 + VIRTIO_SND_CARD_NAME " at %s/%s",
658 + dev_name(dev->parent), dev_name(dev));
660 + return snd_card_register(snd->card);
664 + * virtsnd_validate() - Validate if the device can be started.
665 + * @vdev: VirtIO parent device.
667 + * Context: Any context.
668 + * Return: 0 on success, -EINVAL on failure.
670 +static int virtsnd_validate(struct virtio_device *vdev)
672 + if (!vdev->config->get) {
673 + dev_err(&vdev->dev, "configuration access disabled\n");
677 + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
678 + dev_err(&vdev->dev,
679 + "device does not comply with spec version 1.x\n");
687 + * virtsnd_probe() - Create and initialize the device.
688 + * @vdev: VirtIO parent device.
690 + * Context: Any context that permits to sleep.
691 + * Return: 0 on success, -errno on failure.
693 +static int virtsnd_probe(struct virtio_device *vdev)
695 + struct virtio_snd *snd;
699 + snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
707 + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
708 + spin_lock_init(&snd->queues[i].lock);
710 + rc = virtsnd_find_vqs(snd);
714 + virtio_device_ready(vdev);
716 + rc = virtsnd_build_devs(snd);
720 + virtsnd_enable_event_vq(snd);
724 + virtsnd_remove(vdev);
730 + * virtsnd_remove() - Remove VirtIO and ALSA devices.
731 + * @vdev: VirtIO parent device.
733 + * Context: Any context that permits to sleep.
735 +static void virtsnd_remove(struct virtio_device *vdev)
737 + struct virtio_snd *snd = vdev->priv;
739 + virtsnd_disable_event_vq(snd);
742 + snd_card_free(snd->card);
744 + vdev->config->del_vqs(vdev);
745 + vdev->config->reset(vdev);
747 + kfree(snd->event_msgs);
750 +static const struct virtio_device_id id_table[] = {
751 + { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
755 +static struct virtio_driver virtsnd_driver = {
756 + .driver.name = KBUILD_MODNAME,
757 + .driver.owner = THIS_MODULE,
758 + .id_table = id_table,
759 + .validate = virtsnd_validate,
760 + .probe = virtsnd_probe,
761 + .remove = virtsnd_remove,
764 +static int __init init(void)
766 + return register_virtio_driver(&virtsnd_driver);
770 +static void __exit fini(void)
772 + unregister_virtio_driver(&virtsnd_driver);
776 +MODULE_DEVICE_TABLE(virtio, id_table);
777 +MODULE_DESCRIPTION("Virtio sound card driver");
778 +MODULE_LICENSE("GPL");
779 diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h
781 index 000000000000..b903b1b12e90
783 +++ b/sound/virtio/virtio_card.h
785 +/* SPDX-License-Identifier: GPL-2.0+ */
787 + * virtio-snd: Virtio sound device
788 + * Copyright (C) 2021 OpenSynergy GmbH
790 +#ifndef VIRTIO_SND_CARD_H
791 +#define VIRTIO_SND_CARD_H
793 +#include <linux/slab.h>
794 +#include <linux/virtio.h>
795 +#include <sound/core.h>
796 +#include <uapi/linux/virtio_snd.h>
798 +#define VIRTIO_SND_CARD_DRIVER "virtio-snd"
799 +#define VIRTIO_SND_CARD_NAME "VirtIO SoundCard"
802 + * struct virtio_snd_queue - Virtqueue wrapper structure.
803 + * @lock: Used to synchronize access to a virtqueue.
804 + * @vqueue: Underlying virtqueue.
806 +struct virtio_snd_queue {
808 + struct virtqueue *vqueue;
812 + * struct virtio_snd - VirtIO sound card device.
813 + * @vdev: Underlying virtio device.
814 + * @queues: Virtqueue wrappers.
815 + * @card: ALSA sound card.
816 + * @event_msgs: Device events.
819 + struct virtio_device *vdev;
820 + struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX];
821 + struct snd_card *card;
822 + struct virtio_snd_event *event_msgs;
825 +static inline struct virtio_snd_queue *
826 +virtsnd_control_queue(struct virtio_snd *snd)
828 + return &snd->queues[VIRTIO_SND_VQ_CONTROL];
831 +static inline struct virtio_snd_queue *
832 +virtsnd_event_queue(struct virtio_snd *snd)
834 + return &snd->queues[VIRTIO_SND_VQ_EVENT];
837 +static inline struct virtio_snd_queue *
838 +virtsnd_tx_queue(struct virtio_snd *snd)
840 + return &snd->queues[VIRTIO_SND_VQ_TX];
843 +static inline struct virtio_snd_queue *
844 +virtsnd_rx_queue(struct virtio_snd *snd)
846 + return &snd->queues[VIRTIO_SND_VQ_RX];
849 +#endif /* VIRTIO_SND_CARD_H */