virtio: Backport virtio sound driver.
[AGL/meta-agl.git] / meta-agl-bsp / virtualization-layer / recipes-kernel / linux / linux-yocto / virtio-kmeta / bsp / virtio / virtio-snd / 0002-ALSA-virtio-add-virtio-sound-driver.patch
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
5
6 Introduce skeleton of the virtio sound driver. The driver implements
7 the virtio sound device specification, which has become part of the
8 virtio standard.
9
10 Initial initialization of the device, virtqueues and creation of an
11 empty ALSA sound device.
12
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>
17 ---
18  MAINTAINERS                     |   9 +
19  include/uapi/linux/virtio_snd.h | 334 ++++++++++++++++++++++++++++++++
20  sound/Kconfig                   |   2 +
21  sound/Makefile                  |   3 +-
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
32
33 diff --git a/MAINTAINERS b/MAINTAINERS
34 index 407ae5c24566..49772b741967 100644
35 --- a/MAINTAINERS
36 +++ b/MAINTAINERS
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
40  
41 +VIRTIO SOUND DRIVER
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)
46 +S:     Maintained
47 +F:     include/uapi/linux/virtio_snd.h
48 +F:     sound/virtio/*
49 +
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
54 new file mode 100644
55 index 000000000000..dfe49547a7b0
56 --- /dev/null
57 +++ b/include/uapi/linux/virtio_snd.h
58 @@ -0,0 +1,334 @@
59 +/* SPDX-License-Identifier: BSD-3-Clause */
60 +/*
61 + * Copyright (C) 2021 OpenSynergy GmbH
62 + */
63 +#ifndef VIRTIO_SND_IF_H
64 +#define VIRTIO_SND_IF_H
65 +
66 +#include <linux/virtio_types.h>
67 +
68 +/*******************************************************************************
69 + * CONFIGURATION SPACE
70 + */
71 +struct virtio_snd_config {
72 +       /* # of available physical jacks */
73 +       __le32 jacks;
74 +       /* # of available PCM streams */
75 +       __le32 streams;
76 +       /* # of available channel maps */
77 +       __le32 chmaps;
78 +};
79 +
80 +enum {
81 +       /* device virtqueue indexes */
82 +       VIRTIO_SND_VQ_CONTROL = 0,
83 +       VIRTIO_SND_VQ_EVENT,
84 +       VIRTIO_SND_VQ_TX,
85 +       VIRTIO_SND_VQ_RX,
86 +       /* # of device virtqueues */
87 +       VIRTIO_SND_VQ_MAX
88 +};
89 +
90 +/*******************************************************************************
91 + * COMMON DEFINITIONS
92 + */
93 +
94 +/* supported dataflow directions */
95 +enum {
96 +       VIRTIO_SND_D_OUTPUT = 0,
97 +       VIRTIO_SND_D_INPUT
98 +};
99 +
100 +enum {
101 +       /* jack control request types */
102 +       VIRTIO_SND_R_JACK_INFO = 1,
103 +       VIRTIO_SND_R_JACK_REMAP,
104 +
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,
112 +
113 +       /* channel map control request types */
114 +       VIRTIO_SND_R_CHMAP_INFO = 0x0200,
115 +
116 +       /* jack event types */
117 +       VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000,
118 +       VIRTIO_SND_EVT_JACK_DISCONNECTED,
119 +
120 +       /* PCM event types */
121 +       VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100,
122 +       VIRTIO_SND_EVT_PCM_XRUN,
123 +
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
129 +};
130 +
131 +/* common header */
132 +struct virtio_snd_hdr {
133 +       __le32 code;
134 +};
135 +
136 +/* event notification */
137 +struct virtio_snd_event {
138 +       /* VIRTIO_SND_EVT_XXX */
139 +       struct virtio_snd_hdr hdr;
140 +       /* optional event data */
141 +       __le32 data;
142 +};
143 +
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 */
149 +       __le32 start_id;
150 +       /* item count to query */
151 +       __le32 count;
152 +       /* item information size in bytes */
153 +       __le32 size;
154 +};
155 +
156 +/* common item information header */
157 +struct virtio_snd_info {
158 +       /* function group node id (High Definition Audio Specification 7.1.2) */
159 +       __le32 hda_fn_nid;
160 +};
161 +
162 +/*******************************************************************************
163 + * JACK CONTROL MESSAGES
164 + */
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 */
169 +       __le32 jack_id;
170 +};
171 +
172 +/* supported jack features */
173 +enum {
174 +       VIRTIO_SND_JACK_F_REMAP = 0
175 +};
176 +
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) */
181 +       __le32 features;
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) */
187 +       __u8 connected;
188 +
189 +       __u8 padding[7];
190 +};
191 +
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 */
199 +       __le32 sequence;
200 +};
201 +
202 +/*******************************************************************************
203 + * PCM CONTROL MESSAGES
204 + */
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 */
209 +       __le32 stream_id;
210 +};
211 +
212 +/* supported PCM stream features */
213 +enum {
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
219 +};
220 +
221 +/* supported PCM sample formats */
222 +enum {
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 */
250 +};
251 +
252 +/* supported PCM frame rates */
253 +enum {
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
268 +};
269 +
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) */
274 +       __le32 features;
275 +       /* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */
276 +       __le64 formats;
277 +       /* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */
278 +       __le64 rates;
279 +       /* dataflow direction (VIRTIO_SND_D_XXX) */
280 +       __u8 direction;
281 +       /* minimum # of supported channels */
282 +       __u8 channels_min;
283 +       /* maximum # of supported channels */
284 +       __u8 channels_max;
285 +
286 +       __u8 padding[5];
287 +};
288 +
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) */
298 +       __le32 features;
299 +       /* selected # of channels */
300 +       __u8 channels;
301 +       /* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */
302 +       __u8 format;
303 +       /* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */
304 +       __u8 rate;
305 +
306 +       __u8 padding;
307 +};
308 +
309 +/*******************************************************************************
310 + * PCM I/O MESSAGES
311 + */
312 +
313 +/* I/O request header */
314 +struct virtio_snd_pcm_xfer {
315 +       /* 0 ... virtio_snd_config::streams - 1 */
316 +       __le32 stream_id;
317 +};
318 +
319 +/* I/O request status */
320 +struct virtio_snd_pcm_status {
321 +       /* VIRTIO_SND_S_XXX */
322 +       __le32 status;
323 +       /* current device latency */
324 +       __le32 latency_bytes;
325 +};
326 +
327 +/*******************************************************************************
328 + * CHANNEL MAP CONTROL MESSAGES
329 + */
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 */
334 +       __le32 chmap_id;
335 +};
336 +
337 +/* standard channel position definition */
338 +enum {
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 */
376 +};
377 +
378 +/* maximum possible number of channels */
379 +#define VIRTIO_SND_CHMAP_MAX_SIZE      18
380 +
381 +struct virtio_snd_chmap_info {
382 +       /* common header */
383 +       struct virtio_snd_info hdr;
384 +       /* dataflow direction (VIRTIO_SND_D_XXX) */
385 +       __u8 direction;
386 +       /* # of valid channel position values */
387 +       __u8 channels;
388 +       /* channel position values (VIRTIO_SND_CHMAP_XXX) */
389 +       __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE];
390 +};
391 +
392 +#endif /* VIRTIO_SND_IF_H */
393 diff --git a/sound/Kconfig b/sound/Kconfig
394 index 36785410fbe1..e56d96d2b11c 100644
395 --- a/sound/Kconfig
396 +++ b/sound/Kconfig
397 @@ -99,6 +99,8 @@ source "sound/synth/Kconfig"
398  
399  source "sound/xen/Kconfig"
400  
401 +source "sound/virtio/Kconfig"
402 +
403  endif # SND
404  
405  endif # !UML
406 diff --git a/sound/Makefile b/sound/Makefile
407 index 797ecdcd35e2..04ef04b1168f 100644
408 --- a/sound/Makefile
409 +++ b/sound/Makefile
410 @@ -5,7 +5,8 @@
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/ \
416 +       virtio/
417  obj-$(CONFIG_SND_AOA) += aoa/
418  
419  # This one must be compilable even if sound is configured out
420 diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig
421 new file mode 100644
422 index 000000000000..094cba24ee5b
423 --- /dev/null
424 +++ b/sound/virtio/Kconfig
425 @@ -0,0 +1,10 @@
426 +# SPDX-License-Identifier: GPL-2.0+
427 +# Sound card driver for virtio
428 +
429 +config SND_VIRTIO
430 +       tristate "Virtio sound driver"
431 +       depends on VIRTIO
432 +       select SND_PCM
433 +       select SND_JACK
434 +       help
435 +          This is the virtual sound driver for virtio. Say Y or M.
436 diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile
437 new file mode 100644
438 index 000000000000..8c87ebb9982b
439 --- /dev/null
440 +++ b/sound/virtio/Makefile
441 @@ -0,0 +1,7 @@
442 +# SPDX-License-Identifier: GPL-2.0+
443 +
444 +obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
445 +
446 +virtio_snd-objs := \
447 +       virtio_card.o
448 +
449 diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
450 new file mode 100644
451 index 000000000000..5a37056858e9
452 --- /dev/null
453 +++ b/sound/virtio/virtio_card.c
454 @@ -0,0 +1,324 @@
455 +// SPDX-License-Identifier: GPL-2.0+
456 +/*
457 + * virtio-snd: Virtio sound device
458 + * Copyright (C) 2021 OpenSynergy GmbH
459 + */
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>
465 +
466 +#include "virtio_card.h"
467 +
468 +static void virtsnd_remove(struct virtio_device *vdev);
469 +
470 +/**
471 + * virtsnd_event_send() - Add an event to the event queue.
472 + * @vqueue: Underlying event virtqueue.
473 + * @event: Event.
474 + * @notify: Indicates whether or not to send a notification to the device.
475 + * @gfp: Kernel flags for memory allocation.
476 + *
477 + * Context: Any context.
478 + */
479 +static void virtsnd_event_send(struct virtqueue *vqueue,
480 +                              struct virtio_snd_event *event, bool notify,
481 +                              gfp_t gfp)
482 +{
483 +       struct scatterlist sg;
484 +       struct scatterlist *psgs[1] = { &sg };
485 +
486 +       /* reset event content */
487 +       memset(event, 0, sizeof(*event));
488 +
489 +       sg_init_one(&sg, event, sizeof(*event));
490 +
491 +       if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify)
492 +               return;
493 +
494 +       if (virtqueue_kick_prepare(vqueue))
495 +               virtqueue_notify(vqueue);
496 +}
497 +
498 +/**
499 + * virtsnd_event_dispatch() - Dispatch an event from the device side.
500 + * @snd: VirtIO sound device.
501 + * @event: VirtIO sound event.
502 + *
503 + * Context: Any context.
504 + */
505 +static void virtsnd_event_dispatch(struct virtio_snd *snd,
506 +                                  struct virtio_snd_event *event)
507 +{
508 +}
509 +
510 +/**
511 + * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
512 + * @vqueue: Underlying event virtqueue.
513 + *
514 + * This callback function is called upon a vring interrupt request from the
515 + * device.
516 + *
517 + * Context: Interrupt context.
518 + */
519 +static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
520 +{
521 +       struct virtio_snd *snd = vqueue->vdev->priv;
522 +       struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
523 +       struct virtio_snd_event *event;
524 +       u32 length;
525 +       unsigned long flags;
526 +
527 +       spin_lock_irqsave(&queue->lock, flags);
528 +       do {
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);
533 +               }
534 +               if (unlikely(virtqueue_is_broken(vqueue)))
535 +                       break;
536 +       } while (!virtqueue_enable_cb(vqueue));
537 +       spin_unlock_irqrestore(&queue->lock, flags);
538 +}
539 +
540 +/**
541 + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
542 + * @snd: VirtIO sound device.
543 + *
544 + * After calling this function, the event queue is disabled.
545 + *
546 + * Context: Any context.
547 + * Return: 0 on success, -errno on failure.
548 + */
549 +static int virtsnd_find_vqs(struct virtio_snd *snd)
550 +{
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
554 +       };
555 +       static const char *names[VIRTIO_SND_VQ_MAX] = {
556 +               [VIRTIO_SND_VQ_EVENT] = "virtsnd-event"
557 +       };
558 +       struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
559 +       unsigned int i;
560 +       unsigned int n;
561 +       int rc;
562 +
563 +       rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
564 +                            NULL);
565 +       if (rc) {
566 +               dev_err(&vdev->dev, "failed to initialize virtqueues\n");
567 +               return rc;
568 +       }
569 +
570 +       for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
571 +               snd->queues[i].vqueue = vqs[i];
572 +
573 +       /* Allocate events and populate the event queue */
574 +       virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
575 +
576 +       n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
577 +
578 +       snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
579 +                                       GFP_KERNEL);
580 +       if (!snd->event_msgs)
581 +               return -ENOMEM;
582 +
583 +       for (i = 0; i < n; ++i)
584 +               virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
585 +                                  &snd->event_msgs[i], false, GFP_KERNEL);
586 +
587 +       return 0;
588 +}
589 +
590 +/**
591 + * virtsnd_enable_event_vq() - Enable the event virtqueue.
592 + * @snd: VirtIO sound device.
593 + *
594 + * Context: Any context.
595 + */
596 +static void virtsnd_enable_event_vq(struct virtio_snd *snd)
597 +{
598 +       struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
599 +
600 +       if (!virtqueue_enable_cb(queue->vqueue))
601 +               virtsnd_event_notify_cb(queue->vqueue);
602 +}
603 +
604 +/**
605 + * virtsnd_disable_event_vq() - Disable the event virtqueue.
606 + * @snd: VirtIO sound device.
607 + *
608 + * Context: Any context.
609 + */
610 +static void virtsnd_disable_event_vq(struct virtio_snd *snd)
611 +{
612 +       struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
613 +       struct virtio_snd_event *event;
614 +       u32 length;
615 +       unsigned long flags;
616 +
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);
623 +       }
624 +}
625 +
626 +/**
627 + * virtsnd_build_devs() - Read configuration and build ALSA devices.
628 + * @snd: VirtIO sound device.
629 + *
630 + * Context: Any context that permits to sleep.
631 + * Return: 0 on success, -errno on failure.
632 + */
633 +static int virtsnd_build_devs(struct virtio_snd *snd)
634 +{
635 +       struct virtio_device *vdev = snd->vdev;
636 +       struct device *dev = &vdev->dev;
637 +       int rc;
638 +
639 +       rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
640 +                         THIS_MODULE, 0, &snd->card);
641 +       if (rc < 0)
642 +               return rc;
643 +
644 +       snd->card->private_data = snd;
645 +
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),
654 +                        dev_name(dev));
655 +       else
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));
659 +
660 +       return snd_card_register(snd->card);
661 +}
662 +
663 +/**
664 + * virtsnd_validate() - Validate if the device can be started.
665 + * @vdev: VirtIO parent device.
666 + *
667 + * Context: Any context.
668 + * Return: 0 on success, -EINVAL on failure.
669 + */
670 +static int virtsnd_validate(struct virtio_device *vdev)
671 +{
672 +       if (!vdev->config->get) {
673 +               dev_err(&vdev->dev, "configuration access disabled\n");
674 +               return -EINVAL;
675 +       }
676 +
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");
680 +               return -EINVAL;
681 +       }
682 +
683 +       return 0;
684 +}
685 +
686 +/**
687 + * virtsnd_probe() - Create and initialize the device.
688 + * @vdev: VirtIO parent device.
689 + *
690 + * Context: Any context that permits to sleep.
691 + * Return: 0 on success, -errno on failure.
692 + */
693 +static int virtsnd_probe(struct virtio_device *vdev)
694 +{
695 +       struct virtio_snd *snd;
696 +       unsigned int i;
697 +       int rc;
698 +
699 +       snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
700 +       if (!snd)
701 +               return -ENOMEM;
702 +
703 +       snd->vdev = vdev;
704 +
705 +       vdev->priv = snd;
706 +
707 +       for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
708 +               spin_lock_init(&snd->queues[i].lock);
709 +
710 +       rc = virtsnd_find_vqs(snd);
711 +       if (rc)
712 +               goto on_exit;
713 +
714 +       virtio_device_ready(vdev);
715 +
716 +       rc = virtsnd_build_devs(snd);
717 +       if (rc)
718 +               goto on_exit;
719 +
720 +       virtsnd_enable_event_vq(snd);
721 +
722 +on_exit:
723 +       if (rc)
724 +               virtsnd_remove(vdev);
725 +
726 +       return rc;
727 +}
728 +
729 +/**
730 + * virtsnd_remove() - Remove VirtIO and ALSA devices.
731 + * @vdev: VirtIO parent device.
732 + *
733 + * Context: Any context that permits to sleep.
734 + */
735 +static void virtsnd_remove(struct virtio_device *vdev)
736 +{
737 +       struct virtio_snd *snd = vdev->priv;
738 +
739 +       virtsnd_disable_event_vq(snd);
740 +
741 +       if (snd->card)
742 +               snd_card_free(snd->card);
743 +
744 +       vdev->config->del_vqs(vdev);
745 +       vdev->config->reset(vdev);
746 +
747 +       kfree(snd->event_msgs);
748 +}
749 +
750 +static const struct virtio_device_id id_table[] = {
751 +       { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
752 +       { 0 },
753 +};
754 +
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,
762 +};
763 +
764 +static int __init init(void)
765 +{
766 +       return register_virtio_driver(&virtsnd_driver);
767 +}
768 +module_init(init);
769 +
770 +static void __exit fini(void)
771 +{
772 +       unregister_virtio_driver(&virtsnd_driver);
773 +}
774 +module_exit(fini);
775 +
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
780 new file mode 100644
781 index 000000000000..b903b1b12e90
782 --- /dev/null
783 +++ b/sound/virtio/virtio_card.h
784 @@ -0,0 +1,65 @@
785 +/* SPDX-License-Identifier: GPL-2.0+ */
786 +/*
787 + * virtio-snd: Virtio sound device
788 + * Copyright (C) 2021 OpenSynergy GmbH
789 + */
790 +#ifndef VIRTIO_SND_CARD_H
791 +#define VIRTIO_SND_CARD_H
792 +
793 +#include <linux/slab.h>
794 +#include <linux/virtio.h>
795 +#include <sound/core.h>
796 +#include <uapi/linux/virtio_snd.h>
797 +
798 +#define VIRTIO_SND_CARD_DRIVER "virtio-snd"
799 +#define VIRTIO_SND_CARD_NAME   "VirtIO SoundCard"
800 +
801 +/**
802 + * struct virtio_snd_queue - Virtqueue wrapper structure.
803 + * @lock: Used to synchronize access to a virtqueue.
804 + * @vqueue: Underlying virtqueue.
805 + */
806 +struct virtio_snd_queue {
807 +       spinlock_t lock;
808 +       struct virtqueue *vqueue;
809 +};
810 +
811 +/**
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.
817 + */
818 +struct virtio_snd {
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;
823 +};
824 +
825 +static inline struct virtio_snd_queue *
826 +virtsnd_control_queue(struct virtio_snd *snd)
827 +{
828 +       return &snd->queues[VIRTIO_SND_VQ_CONTROL];
829 +}
830 +
831 +static inline struct virtio_snd_queue *
832 +virtsnd_event_queue(struct virtio_snd *snd)
833 +{
834 +       return &snd->queues[VIRTIO_SND_VQ_EVENT];
835 +}
836 +
837 +static inline struct virtio_snd_queue *
838 +virtsnd_tx_queue(struct virtio_snd *snd)
839 +{
840 +       return &snd->queues[VIRTIO_SND_VQ_TX];
841 +}
842 +
843 +static inline struct virtio_snd_queue *
844 +virtsnd_rx_queue(struct virtio_snd *snd)
845 +{
846 +       return &snd->queues[VIRTIO_SND_VQ_RX];
847 +}
848 +
849 +#endif /* VIRTIO_SND_CARD_H */