xref: /openbmc/linux/sound/virtio/virtio_card.c (revision f40a28679e0b7cb3a9cc6627a8dbb40961990f0a)
1de3a9980SAnton Yakovlev // SPDX-License-Identifier: GPL-2.0+
2de3a9980SAnton Yakovlev /*
3de3a9980SAnton Yakovlev  * virtio-snd: Virtio sound device
4de3a9980SAnton Yakovlev  * Copyright (C) 2021 OpenSynergy GmbH
5de3a9980SAnton Yakovlev  */
6de3a9980SAnton Yakovlev #include <linux/module.h>
7de3a9980SAnton Yakovlev #include <linux/moduleparam.h>
8de3a9980SAnton Yakovlev #include <linux/virtio_config.h>
9de3a9980SAnton Yakovlev #include <sound/initval.h>
10de3a9980SAnton Yakovlev #include <uapi/linux/virtio_ids.h>
11de3a9980SAnton Yakovlev 
12de3a9980SAnton Yakovlev #include "virtio_card.h"
13de3a9980SAnton Yakovlev 
149d45e514SAnton Yakovlev u32 virtsnd_msg_timeout_ms = MSEC_PER_SEC;
159d45e514SAnton Yakovlev module_param_named(msg_timeout_ms, virtsnd_msg_timeout_ms, uint, 0644);
169d45e514SAnton Yakovlev MODULE_PARM_DESC(msg_timeout_ms, "Message completion timeout in milliseconds");
179d45e514SAnton Yakovlev 
18de3a9980SAnton Yakovlev static void virtsnd_remove(struct virtio_device *vdev);
19de3a9980SAnton Yakovlev 
20de3a9980SAnton Yakovlev /**
21de3a9980SAnton Yakovlev  * virtsnd_event_send() - Add an event to the event queue.
22de3a9980SAnton Yakovlev  * @vqueue: Underlying event virtqueue.
23de3a9980SAnton Yakovlev  * @event: Event.
24de3a9980SAnton Yakovlev  * @notify: Indicates whether or not to send a notification to the device.
25de3a9980SAnton Yakovlev  * @gfp: Kernel flags for memory allocation.
26de3a9980SAnton Yakovlev  *
27de3a9980SAnton Yakovlev  * Context: Any context.
28de3a9980SAnton Yakovlev  */
29de3a9980SAnton Yakovlev static void virtsnd_event_send(struct virtqueue *vqueue,
30de3a9980SAnton Yakovlev 			       struct virtio_snd_event *event, bool notify,
31de3a9980SAnton Yakovlev 			       gfp_t gfp)
32de3a9980SAnton Yakovlev {
33de3a9980SAnton Yakovlev 	struct scatterlist sg;
34de3a9980SAnton Yakovlev 	struct scatterlist *psgs[1] = { &sg };
35de3a9980SAnton Yakovlev 
36de3a9980SAnton Yakovlev 	/* reset event content */
37de3a9980SAnton Yakovlev 	memset(event, 0, sizeof(*event));
38de3a9980SAnton Yakovlev 
39de3a9980SAnton Yakovlev 	sg_init_one(&sg, event, sizeof(*event));
40de3a9980SAnton Yakovlev 
41de3a9980SAnton Yakovlev 	if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify)
42de3a9980SAnton Yakovlev 		return;
43de3a9980SAnton Yakovlev 
44de3a9980SAnton Yakovlev 	if (virtqueue_kick_prepare(vqueue))
45de3a9980SAnton Yakovlev 		virtqueue_notify(vqueue);
46de3a9980SAnton Yakovlev }
47de3a9980SAnton Yakovlev 
48de3a9980SAnton Yakovlev /**
49de3a9980SAnton Yakovlev  * virtsnd_event_dispatch() - Dispatch an event from the device side.
50de3a9980SAnton Yakovlev  * @snd: VirtIO sound device.
51de3a9980SAnton Yakovlev  * @event: VirtIO sound event.
52de3a9980SAnton Yakovlev  *
53de3a9980SAnton Yakovlev  * Context: Any context.
54de3a9980SAnton Yakovlev  */
55de3a9980SAnton Yakovlev static void virtsnd_event_dispatch(struct virtio_snd *snd,
56de3a9980SAnton Yakovlev 				   struct virtio_snd_event *event)
57de3a9980SAnton Yakovlev {
58*f40a2867SAnton Yakovlev 	switch (le32_to_cpu(event->hdr.code)) {
59*f40a2867SAnton Yakovlev 	case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
60*f40a2867SAnton Yakovlev 	case VIRTIO_SND_EVT_PCM_XRUN:
61*f40a2867SAnton Yakovlev 		virtsnd_pcm_event(snd, event);
62*f40a2867SAnton Yakovlev 		break;
63*f40a2867SAnton Yakovlev 	}
64de3a9980SAnton Yakovlev }
65de3a9980SAnton Yakovlev 
66de3a9980SAnton Yakovlev /**
67de3a9980SAnton Yakovlev  * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
68de3a9980SAnton Yakovlev  * @vqueue: Underlying event virtqueue.
69de3a9980SAnton Yakovlev  *
70de3a9980SAnton Yakovlev  * This callback function is called upon a vring interrupt request from the
71de3a9980SAnton Yakovlev  * device.
72de3a9980SAnton Yakovlev  *
73de3a9980SAnton Yakovlev  * Context: Interrupt context.
74de3a9980SAnton Yakovlev  */
75de3a9980SAnton Yakovlev static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
76de3a9980SAnton Yakovlev {
77de3a9980SAnton Yakovlev 	struct virtio_snd *snd = vqueue->vdev->priv;
78de3a9980SAnton Yakovlev 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
79de3a9980SAnton Yakovlev 	struct virtio_snd_event *event;
80de3a9980SAnton Yakovlev 	u32 length;
81de3a9980SAnton Yakovlev 	unsigned long flags;
82de3a9980SAnton Yakovlev 
83de3a9980SAnton Yakovlev 	spin_lock_irqsave(&queue->lock, flags);
84de3a9980SAnton Yakovlev 	do {
85de3a9980SAnton Yakovlev 		virtqueue_disable_cb(vqueue);
86de3a9980SAnton Yakovlev 		while ((event = virtqueue_get_buf(vqueue, &length))) {
87de3a9980SAnton Yakovlev 			virtsnd_event_dispatch(snd, event);
88de3a9980SAnton Yakovlev 			virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
89de3a9980SAnton Yakovlev 		}
90de3a9980SAnton Yakovlev 		if (unlikely(virtqueue_is_broken(vqueue)))
91de3a9980SAnton Yakovlev 			break;
92de3a9980SAnton Yakovlev 	} while (!virtqueue_enable_cb(vqueue));
93de3a9980SAnton Yakovlev 	spin_unlock_irqrestore(&queue->lock, flags);
94de3a9980SAnton Yakovlev }
95de3a9980SAnton Yakovlev 
96de3a9980SAnton Yakovlev /**
97de3a9980SAnton Yakovlev  * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
98de3a9980SAnton Yakovlev  * @snd: VirtIO sound device.
99de3a9980SAnton Yakovlev  *
100de3a9980SAnton Yakovlev  * After calling this function, the event queue is disabled.
101de3a9980SAnton Yakovlev  *
102de3a9980SAnton Yakovlev  * Context: Any context.
103de3a9980SAnton Yakovlev  * Return: 0 on success, -errno on failure.
104de3a9980SAnton Yakovlev  */
105de3a9980SAnton Yakovlev static int virtsnd_find_vqs(struct virtio_snd *snd)
106de3a9980SAnton Yakovlev {
107de3a9980SAnton Yakovlev 	struct virtio_device *vdev = snd->vdev;
108de3a9980SAnton Yakovlev 	static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
1099d45e514SAnton Yakovlev 		[VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb,
110*f40a2867SAnton Yakovlev 		[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb,
111*f40a2867SAnton Yakovlev 		[VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb,
112*f40a2867SAnton Yakovlev 		[VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb
113de3a9980SAnton Yakovlev 	};
114de3a9980SAnton Yakovlev 	static const char *names[VIRTIO_SND_VQ_MAX] = {
1159d45e514SAnton Yakovlev 		[VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl",
116*f40a2867SAnton Yakovlev 		[VIRTIO_SND_VQ_EVENT] = "virtsnd-event",
117*f40a2867SAnton Yakovlev 		[VIRTIO_SND_VQ_TX] = "virtsnd-tx",
118*f40a2867SAnton Yakovlev 		[VIRTIO_SND_VQ_RX] = "virtsnd-rx"
119de3a9980SAnton Yakovlev 	};
120de3a9980SAnton Yakovlev 	struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
121de3a9980SAnton Yakovlev 	unsigned int i;
122de3a9980SAnton Yakovlev 	unsigned int n;
123de3a9980SAnton Yakovlev 	int rc;
124de3a9980SAnton Yakovlev 
125de3a9980SAnton Yakovlev 	rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
126de3a9980SAnton Yakovlev 			     NULL);
127de3a9980SAnton Yakovlev 	if (rc) {
128de3a9980SAnton Yakovlev 		dev_err(&vdev->dev, "failed to initialize virtqueues\n");
129de3a9980SAnton Yakovlev 		return rc;
130de3a9980SAnton Yakovlev 	}
131de3a9980SAnton Yakovlev 
132de3a9980SAnton Yakovlev 	for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
133de3a9980SAnton Yakovlev 		snd->queues[i].vqueue = vqs[i];
134de3a9980SAnton Yakovlev 
135de3a9980SAnton Yakovlev 	/* Allocate events and populate the event queue */
136de3a9980SAnton Yakovlev 	virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
137de3a9980SAnton Yakovlev 
138de3a9980SAnton Yakovlev 	n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
139de3a9980SAnton Yakovlev 
140de3a9980SAnton Yakovlev 	snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
141de3a9980SAnton Yakovlev 					GFP_KERNEL);
142de3a9980SAnton Yakovlev 	if (!snd->event_msgs)
143de3a9980SAnton Yakovlev 		return -ENOMEM;
144de3a9980SAnton Yakovlev 
145de3a9980SAnton Yakovlev 	for (i = 0; i < n; ++i)
146de3a9980SAnton Yakovlev 		virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
147de3a9980SAnton Yakovlev 				   &snd->event_msgs[i], false, GFP_KERNEL);
148de3a9980SAnton Yakovlev 
149de3a9980SAnton Yakovlev 	return 0;
150de3a9980SAnton Yakovlev }
151de3a9980SAnton Yakovlev 
152de3a9980SAnton Yakovlev /**
153de3a9980SAnton Yakovlev  * virtsnd_enable_event_vq() - Enable the event virtqueue.
154de3a9980SAnton Yakovlev  * @snd: VirtIO sound device.
155de3a9980SAnton Yakovlev  *
156de3a9980SAnton Yakovlev  * Context: Any context.
157de3a9980SAnton Yakovlev  */
158de3a9980SAnton Yakovlev static void virtsnd_enable_event_vq(struct virtio_snd *snd)
159de3a9980SAnton Yakovlev {
160de3a9980SAnton Yakovlev 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
161de3a9980SAnton Yakovlev 
162de3a9980SAnton Yakovlev 	if (!virtqueue_enable_cb(queue->vqueue))
163de3a9980SAnton Yakovlev 		virtsnd_event_notify_cb(queue->vqueue);
164de3a9980SAnton Yakovlev }
165de3a9980SAnton Yakovlev 
166de3a9980SAnton Yakovlev /**
167de3a9980SAnton Yakovlev  * virtsnd_disable_event_vq() - Disable the event virtqueue.
168de3a9980SAnton Yakovlev  * @snd: VirtIO sound device.
169de3a9980SAnton Yakovlev  *
170de3a9980SAnton Yakovlev  * Context: Any context.
171de3a9980SAnton Yakovlev  */
172de3a9980SAnton Yakovlev static void virtsnd_disable_event_vq(struct virtio_snd *snd)
173de3a9980SAnton Yakovlev {
174de3a9980SAnton Yakovlev 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
175de3a9980SAnton Yakovlev 	struct virtio_snd_event *event;
176de3a9980SAnton Yakovlev 	u32 length;
177de3a9980SAnton Yakovlev 	unsigned long flags;
178de3a9980SAnton Yakovlev 
179de3a9980SAnton Yakovlev 	if (queue->vqueue) {
180de3a9980SAnton Yakovlev 		spin_lock_irqsave(&queue->lock, flags);
181de3a9980SAnton Yakovlev 		virtqueue_disable_cb(queue->vqueue);
182de3a9980SAnton Yakovlev 		while ((event = virtqueue_get_buf(queue->vqueue, &length)))
183de3a9980SAnton Yakovlev 			virtsnd_event_dispatch(snd, event);
184de3a9980SAnton Yakovlev 		spin_unlock_irqrestore(&queue->lock, flags);
185de3a9980SAnton Yakovlev 	}
186de3a9980SAnton Yakovlev }
187de3a9980SAnton Yakovlev 
188de3a9980SAnton Yakovlev /**
189de3a9980SAnton Yakovlev  * virtsnd_build_devs() - Read configuration and build ALSA devices.
190de3a9980SAnton Yakovlev  * @snd: VirtIO sound device.
191de3a9980SAnton Yakovlev  *
192de3a9980SAnton Yakovlev  * Context: Any context that permits to sleep.
193de3a9980SAnton Yakovlev  * Return: 0 on success, -errno on failure.
194de3a9980SAnton Yakovlev  */
195de3a9980SAnton Yakovlev static int virtsnd_build_devs(struct virtio_snd *snd)
196de3a9980SAnton Yakovlev {
197de3a9980SAnton Yakovlev 	struct virtio_device *vdev = snd->vdev;
198de3a9980SAnton Yakovlev 	struct device *dev = &vdev->dev;
199de3a9980SAnton Yakovlev 	int rc;
200de3a9980SAnton Yakovlev 
201de3a9980SAnton Yakovlev 	rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
202de3a9980SAnton Yakovlev 			  THIS_MODULE, 0, &snd->card);
203de3a9980SAnton Yakovlev 	if (rc < 0)
204de3a9980SAnton Yakovlev 		return rc;
205de3a9980SAnton Yakovlev 
206de3a9980SAnton Yakovlev 	snd->card->private_data = snd;
207de3a9980SAnton Yakovlev 
208de3a9980SAnton Yakovlev 	strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
209de3a9980SAnton Yakovlev 		sizeof(snd->card->driver));
210de3a9980SAnton Yakovlev 	strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
211de3a9980SAnton Yakovlev 		sizeof(snd->card->shortname));
212de3a9980SAnton Yakovlev 	if (dev->parent->bus)
213de3a9980SAnton Yakovlev 		snprintf(snd->card->longname, sizeof(snd->card->longname),
214de3a9980SAnton Yakovlev 			 VIRTIO_SND_CARD_NAME " at %s/%s/%s",
215de3a9980SAnton Yakovlev 			 dev->parent->bus->name, dev_name(dev->parent),
216de3a9980SAnton Yakovlev 			 dev_name(dev));
217de3a9980SAnton Yakovlev 	else
218de3a9980SAnton Yakovlev 		snprintf(snd->card->longname, sizeof(snd->card->longname),
219de3a9980SAnton Yakovlev 			 VIRTIO_SND_CARD_NAME " at %s/%s",
220de3a9980SAnton Yakovlev 			 dev_name(dev->parent), dev_name(dev));
221de3a9980SAnton Yakovlev 
22229b96bf5SAnton Yakovlev 	rc = virtsnd_pcm_parse_cfg(snd);
22329b96bf5SAnton Yakovlev 	if (rc)
22429b96bf5SAnton Yakovlev 		return rc;
22529b96bf5SAnton Yakovlev 
22629b96bf5SAnton Yakovlev 	if (snd->nsubstreams) {
22729b96bf5SAnton Yakovlev 		rc = virtsnd_pcm_build_devs(snd);
22829b96bf5SAnton Yakovlev 		if (rc)
22929b96bf5SAnton Yakovlev 			return rc;
23029b96bf5SAnton Yakovlev 	}
23129b96bf5SAnton Yakovlev 
232de3a9980SAnton Yakovlev 	return snd_card_register(snd->card);
233de3a9980SAnton Yakovlev }
234de3a9980SAnton Yakovlev 
235de3a9980SAnton Yakovlev /**
236de3a9980SAnton Yakovlev  * virtsnd_validate() - Validate if the device can be started.
237de3a9980SAnton Yakovlev  * @vdev: VirtIO parent device.
238de3a9980SAnton Yakovlev  *
239de3a9980SAnton Yakovlev  * Context: Any context.
240de3a9980SAnton Yakovlev  * Return: 0 on success, -EINVAL on failure.
241de3a9980SAnton Yakovlev  */
242de3a9980SAnton Yakovlev static int virtsnd_validate(struct virtio_device *vdev)
243de3a9980SAnton Yakovlev {
244de3a9980SAnton Yakovlev 	if (!vdev->config->get) {
245de3a9980SAnton Yakovlev 		dev_err(&vdev->dev, "configuration access disabled\n");
246de3a9980SAnton Yakovlev 		return -EINVAL;
247de3a9980SAnton Yakovlev 	}
248de3a9980SAnton Yakovlev 
249de3a9980SAnton Yakovlev 	if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
250de3a9980SAnton Yakovlev 		dev_err(&vdev->dev,
251de3a9980SAnton Yakovlev 			"device does not comply with spec version 1.x\n");
252de3a9980SAnton Yakovlev 		return -EINVAL;
253de3a9980SAnton Yakovlev 	}
254de3a9980SAnton Yakovlev 
2559d45e514SAnton Yakovlev 	if (!virtsnd_msg_timeout_ms) {
2569d45e514SAnton Yakovlev 		dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n");
2579d45e514SAnton Yakovlev 		return -EINVAL;
2589d45e514SAnton Yakovlev 	}
2599d45e514SAnton Yakovlev 
26029b96bf5SAnton Yakovlev 	if (virtsnd_pcm_validate(vdev))
26129b96bf5SAnton Yakovlev 		return -EINVAL;
26229b96bf5SAnton Yakovlev 
263de3a9980SAnton Yakovlev 	return 0;
264de3a9980SAnton Yakovlev }
265de3a9980SAnton Yakovlev 
266de3a9980SAnton Yakovlev /**
267de3a9980SAnton Yakovlev  * virtsnd_probe() - Create and initialize the device.
268de3a9980SAnton Yakovlev  * @vdev: VirtIO parent device.
269de3a9980SAnton Yakovlev  *
270de3a9980SAnton Yakovlev  * Context: Any context that permits to sleep.
271de3a9980SAnton Yakovlev  * Return: 0 on success, -errno on failure.
272de3a9980SAnton Yakovlev  */
273de3a9980SAnton Yakovlev static int virtsnd_probe(struct virtio_device *vdev)
274de3a9980SAnton Yakovlev {
275de3a9980SAnton Yakovlev 	struct virtio_snd *snd;
276de3a9980SAnton Yakovlev 	unsigned int i;
277de3a9980SAnton Yakovlev 	int rc;
278de3a9980SAnton Yakovlev 
279de3a9980SAnton Yakovlev 	snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
280de3a9980SAnton Yakovlev 	if (!snd)
281de3a9980SAnton Yakovlev 		return -ENOMEM;
282de3a9980SAnton Yakovlev 
283de3a9980SAnton Yakovlev 	snd->vdev = vdev;
2849d45e514SAnton Yakovlev 	INIT_LIST_HEAD(&snd->ctl_msgs);
28529b96bf5SAnton Yakovlev 	INIT_LIST_HEAD(&snd->pcm_list);
286de3a9980SAnton Yakovlev 
287de3a9980SAnton Yakovlev 	vdev->priv = snd;
288de3a9980SAnton Yakovlev 
289de3a9980SAnton Yakovlev 	for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
290de3a9980SAnton Yakovlev 		spin_lock_init(&snd->queues[i].lock);
291de3a9980SAnton Yakovlev 
292de3a9980SAnton Yakovlev 	rc = virtsnd_find_vqs(snd);
293de3a9980SAnton Yakovlev 	if (rc)
294de3a9980SAnton Yakovlev 		goto on_exit;
295de3a9980SAnton Yakovlev 
296de3a9980SAnton Yakovlev 	virtio_device_ready(vdev);
297de3a9980SAnton Yakovlev 
298de3a9980SAnton Yakovlev 	rc = virtsnd_build_devs(snd);
299de3a9980SAnton Yakovlev 	if (rc)
300de3a9980SAnton Yakovlev 		goto on_exit;
301de3a9980SAnton Yakovlev 
302de3a9980SAnton Yakovlev 	virtsnd_enable_event_vq(snd);
303de3a9980SAnton Yakovlev 
304de3a9980SAnton Yakovlev on_exit:
305de3a9980SAnton Yakovlev 	if (rc)
306de3a9980SAnton Yakovlev 		virtsnd_remove(vdev);
307de3a9980SAnton Yakovlev 
308de3a9980SAnton Yakovlev 	return rc;
309de3a9980SAnton Yakovlev }
310de3a9980SAnton Yakovlev 
311de3a9980SAnton Yakovlev /**
312de3a9980SAnton Yakovlev  * virtsnd_remove() - Remove VirtIO and ALSA devices.
313de3a9980SAnton Yakovlev  * @vdev: VirtIO parent device.
314de3a9980SAnton Yakovlev  *
315de3a9980SAnton Yakovlev  * Context: Any context that permits to sleep.
316de3a9980SAnton Yakovlev  */
317de3a9980SAnton Yakovlev static void virtsnd_remove(struct virtio_device *vdev)
318de3a9980SAnton Yakovlev {
319de3a9980SAnton Yakovlev 	struct virtio_snd *snd = vdev->priv;
32029b96bf5SAnton Yakovlev 	unsigned int i;
321de3a9980SAnton Yakovlev 
322de3a9980SAnton Yakovlev 	virtsnd_disable_event_vq(snd);
3239d45e514SAnton Yakovlev 	virtsnd_ctl_msg_cancel_all(snd);
324de3a9980SAnton Yakovlev 
325de3a9980SAnton Yakovlev 	if (snd->card)
326de3a9980SAnton Yakovlev 		snd_card_free(snd->card);
327de3a9980SAnton Yakovlev 
328de3a9980SAnton Yakovlev 	vdev->config->del_vqs(vdev);
329de3a9980SAnton Yakovlev 	vdev->config->reset(vdev);
330de3a9980SAnton Yakovlev 
331*f40a2867SAnton Yakovlev 	for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
332*f40a2867SAnton Yakovlev 		struct virtio_pcm_substream *vss = &snd->substreams[i];
333*f40a2867SAnton Yakovlev 
334*f40a2867SAnton Yakovlev 		cancel_work_sync(&vss->elapsed_period);
335*f40a2867SAnton Yakovlev 		virtsnd_pcm_msg_free(vss);
336*f40a2867SAnton Yakovlev 	}
33729b96bf5SAnton Yakovlev 
338de3a9980SAnton Yakovlev 	kfree(snd->event_msgs);
339de3a9980SAnton Yakovlev }
340de3a9980SAnton Yakovlev 
341de3a9980SAnton Yakovlev static const struct virtio_device_id id_table[] = {
342de3a9980SAnton Yakovlev 	{ VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
343de3a9980SAnton Yakovlev 	{ 0 },
344de3a9980SAnton Yakovlev };
345de3a9980SAnton Yakovlev 
346de3a9980SAnton Yakovlev static struct virtio_driver virtsnd_driver = {
347de3a9980SAnton Yakovlev 	.driver.name = KBUILD_MODNAME,
348de3a9980SAnton Yakovlev 	.driver.owner = THIS_MODULE,
349de3a9980SAnton Yakovlev 	.id_table = id_table,
350de3a9980SAnton Yakovlev 	.validate = virtsnd_validate,
351de3a9980SAnton Yakovlev 	.probe = virtsnd_probe,
352de3a9980SAnton Yakovlev 	.remove = virtsnd_remove,
353de3a9980SAnton Yakovlev };
354de3a9980SAnton Yakovlev 
355de3a9980SAnton Yakovlev static int __init init(void)
356de3a9980SAnton Yakovlev {
357de3a9980SAnton Yakovlev 	return register_virtio_driver(&virtsnd_driver);
358de3a9980SAnton Yakovlev }
359de3a9980SAnton Yakovlev module_init(init);
360de3a9980SAnton Yakovlev 
361de3a9980SAnton Yakovlev static void __exit fini(void)
362de3a9980SAnton Yakovlev {
363de3a9980SAnton Yakovlev 	unregister_virtio_driver(&virtsnd_driver);
364de3a9980SAnton Yakovlev }
365de3a9980SAnton Yakovlev module_exit(fini);
366de3a9980SAnton Yakovlev 
367de3a9980SAnton Yakovlev MODULE_DEVICE_TABLE(virtio, id_table);
368de3a9980SAnton Yakovlev MODULE_DESCRIPTION("Virtio sound card driver");
369de3a9980SAnton Yakovlev MODULE_LICENSE("GPL");
370