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 */
virtsnd_event_send(struct virtqueue * vqueue,struct virtio_snd_event * event,bool notify,gfp_t gfp)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 */
virtsnd_event_dispatch(struct virtio_snd * snd,struct virtio_snd_event * event)55de3a9980SAnton Yakovlev static void virtsnd_event_dispatch(struct virtio_snd *snd,
56de3a9980SAnton Yakovlev struct virtio_snd_event *event)
57de3a9980SAnton Yakovlev {
58f40a2867SAnton Yakovlev switch (le32_to_cpu(event->hdr.code)) {
59ca61a41fSAnton Yakovlev case VIRTIO_SND_EVT_JACK_CONNECTED:
60ca61a41fSAnton Yakovlev case VIRTIO_SND_EVT_JACK_DISCONNECTED:
61ca61a41fSAnton Yakovlev virtsnd_jack_event(snd, event);
62ca61a41fSAnton Yakovlev break;
63f40a2867SAnton Yakovlev case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
64f40a2867SAnton Yakovlev case VIRTIO_SND_EVT_PCM_XRUN:
65f40a2867SAnton Yakovlev virtsnd_pcm_event(snd, event);
66f40a2867SAnton Yakovlev break;
67f40a2867SAnton Yakovlev }
68de3a9980SAnton Yakovlev }
69de3a9980SAnton Yakovlev
70de3a9980SAnton Yakovlev /**
71de3a9980SAnton Yakovlev * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
72de3a9980SAnton Yakovlev * @vqueue: Underlying event virtqueue.
73de3a9980SAnton Yakovlev *
74de3a9980SAnton Yakovlev * This callback function is called upon a vring interrupt request from the
75de3a9980SAnton Yakovlev * device.
76de3a9980SAnton Yakovlev *
77de3a9980SAnton Yakovlev * Context: Interrupt context.
78de3a9980SAnton Yakovlev */
virtsnd_event_notify_cb(struct virtqueue * vqueue)79de3a9980SAnton Yakovlev static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
80de3a9980SAnton Yakovlev {
81de3a9980SAnton Yakovlev struct virtio_snd *snd = vqueue->vdev->priv;
82de3a9980SAnton Yakovlev struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
83de3a9980SAnton Yakovlev struct virtio_snd_event *event;
84de3a9980SAnton Yakovlev u32 length;
85de3a9980SAnton Yakovlev unsigned long flags;
86de3a9980SAnton Yakovlev
87de3a9980SAnton Yakovlev spin_lock_irqsave(&queue->lock, flags);
88de3a9980SAnton Yakovlev do {
89de3a9980SAnton Yakovlev virtqueue_disable_cb(vqueue);
90de3a9980SAnton Yakovlev while ((event = virtqueue_get_buf(vqueue, &length))) {
91de3a9980SAnton Yakovlev virtsnd_event_dispatch(snd, event);
92de3a9980SAnton Yakovlev virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
93de3a9980SAnton Yakovlev }
94de3a9980SAnton Yakovlev if (unlikely(virtqueue_is_broken(vqueue)))
95de3a9980SAnton Yakovlev break;
96de3a9980SAnton Yakovlev } while (!virtqueue_enable_cb(vqueue));
97de3a9980SAnton Yakovlev spin_unlock_irqrestore(&queue->lock, flags);
98de3a9980SAnton Yakovlev }
99de3a9980SAnton Yakovlev
100de3a9980SAnton Yakovlev /**
101de3a9980SAnton Yakovlev * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
102de3a9980SAnton Yakovlev * @snd: VirtIO sound device.
103de3a9980SAnton Yakovlev *
104de3a9980SAnton Yakovlev * After calling this function, the event queue is disabled.
105de3a9980SAnton Yakovlev *
106de3a9980SAnton Yakovlev * Context: Any context.
107de3a9980SAnton Yakovlev * Return: 0 on success, -errno on failure.
108de3a9980SAnton Yakovlev */
virtsnd_find_vqs(struct virtio_snd * snd)109de3a9980SAnton Yakovlev static int virtsnd_find_vqs(struct virtio_snd *snd)
110de3a9980SAnton Yakovlev {
111de3a9980SAnton Yakovlev struct virtio_device *vdev = snd->vdev;
112de3a9980SAnton Yakovlev static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
1139d45e514SAnton Yakovlev [VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb,
114f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb,
115f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb,
116f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb
117de3a9980SAnton Yakovlev };
118de3a9980SAnton Yakovlev static const char *names[VIRTIO_SND_VQ_MAX] = {
1199d45e514SAnton Yakovlev [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl",
120f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_EVENT] = "virtsnd-event",
121f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_TX] = "virtsnd-tx",
122f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_RX] = "virtsnd-rx"
123de3a9980SAnton Yakovlev };
124de3a9980SAnton Yakovlev struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
125de3a9980SAnton Yakovlev unsigned int i;
126de3a9980SAnton Yakovlev unsigned int n;
127de3a9980SAnton Yakovlev int rc;
128de3a9980SAnton Yakovlev
129de3a9980SAnton Yakovlev rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
130de3a9980SAnton Yakovlev NULL);
131de3a9980SAnton Yakovlev if (rc) {
132de3a9980SAnton Yakovlev dev_err(&vdev->dev, "failed to initialize virtqueues\n");
133de3a9980SAnton Yakovlev return rc;
134de3a9980SAnton Yakovlev }
135de3a9980SAnton Yakovlev
136de3a9980SAnton Yakovlev for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
137de3a9980SAnton Yakovlev snd->queues[i].vqueue = vqs[i];
138de3a9980SAnton Yakovlev
139de3a9980SAnton Yakovlev /* Allocate events and populate the event queue */
140de3a9980SAnton Yakovlev virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
141de3a9980SAnton Yakovlev
142de3a9980SAnton Yakovlev n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
143de3a9980SAnton Yakovlev
144de3a9980SAnton Yakovlev snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
145de3a9980SAnton Yakovlev GFP_KERNEL);
146de3a9980SAnton Yakovlev if (!snd->event_msgs)
147de3a9980SAnton Yakovlev return -ENOMEM;
148de3a9980SAnton Yakovlev
149de3a9980SAnton Yakovlev for (i = 0; i < n; ++i)
150de3a9980SAnton Yakovlev virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
151de3a9980SAnton Yakovlev &snd->event_msgs[i], false, GFP_KERNEL);
152de3a9980SAnton Yakovlev
153de3a9980SAnton Yakovlev return 0;
154de3a9980SAnton Yakovlev }
155de3a9980SAnton Yakovlev
156de3a9980SAnton Yakovlev /**
157de3a9980SAnton Yakovlev * virtsnd_enable_event_vq() - Enable the event virtqueue.
158de3a9980SAnton Yakovlev * @snd: VirtIO sound device.
159de3a9980SAnton Yakovlev *
160de3a9980SAnton Yakovlev * Context: Any context.
161de3a9980SAnton Yakovlev */
virtsnd_enable_event_vq(struct virtio_snd * snd)162de3a9980SAnton Yakovlev static void virtsnd_enable_event_vq(struct virtio_snd *snd)
163de3a9980SAnton Yakovlev {
164de3a9980SAnton Yakovlev struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
165de3a9980SAnton Yakovlev
166de3a9980SAnton Yakovlev if (!virtqueue_enable_cb(queue->vqueue))
167de3a9980SAnton Yakovlev virtsnd_event_notify_cb(queue->vqueue);
168de3a9980SAnton Yakovlev }
169de3a9980SAnton Yakovlev
170de3a9980SAnton Yakovlev /**
171de3a9980SAnton Yakovlev * virtsnd_disable_event_vq() - Disable the event virtqueue.
172de3a9980SAnton Yakovlev * @snd: VirtIO sound device.
173de3a9980SAnton Yakovlev *
174de3a9980SAnton Yakovlev * Context: Any context.
175de3a9980SAnton Yakovlev */
virtsnd_disable_event_vq(struct virtio_snd * snd)176de3a9980SAnton Yakovlev static void virtsnd_disable_event_vq(struct virtio_snd *snd)
177de3a9980SAnton Yakovlev {
178de3a9980SAnton Yakovlev struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
179de3a9980SAnton Yakovlev struct virtio_snd_event *event;
180de3a9980SAnton Yakovlev u32 length;
181de3a9980SAnton Yakovlev unsigned long flags;
182de3a9980SAnton Yakovlev
183de3a9980SAnton Yakovlev if (queue->vqueue) {
184de3a9980SAnton Yakovlev spin_lock_irqsave(&queue->lock, flags);
185de3a9980SAnton Yakovlev virtqueue_disable_cb(queue->vqueue);
186de3a9980SAnton Yakovlev while ((event = virtqueue_get_buf(queue->vqueue, &length)))
187de3a9980SAnton Yakovlev virtsnd_event_dispatch(snd, event);
188de3a9980SAnton Yakovlev spin_unlock_irqrestore(&queue->lock, flags);
189de3a9980SAnton Yakovlev }
190de3a9980SAnton Yakovlev }
191de3a9980SAnton Yakovlev
192de3a9980SAnton Yakovlev /**
193de3a9980SAnton Yakovlev * virtsnd_build_devs() - Read configuration and build ALSA devices.
194de3a9980SAnton Yakovlev * @snd: VirtIO sound device.
195de3a9980SAnton Yakovlev *
196de3a9980SAnton Yakovlev * Context: Any context that permits to sleep.
197de3a9980SAnton Yakovlev * Return: 0 on success, -errno on failure.
198de3a9980SAnton Yakovlev */
virtsnd_build_devs(struct virtio_snd * snd)199de3a9980SAnton Yakovlev static int virtsnd_build_devs(struct virtio_snd *snd)
200de3a9980SAnton Yakovlev {
201de3a9980SAnton Yakovlev struct virtio_device *vdev = snd->vdev;
202de3a9980SAnton Yakovlev struct device *dev = &vdev->dev;
203de3a9980SAnton Yakovlev int rc;
204de3a9980SAnton Yakovlev
205de3a9980SAnton Yakovlev rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
206de3a9980SAnton Yakovlev THIS_MODULE, 0, &snd->card);
207de3a9980SAnton Yakovlev if (rc < 0)
208de3a9980SAnton Yakovlev return rc;
209de3a9980SAnton Yakovlev
210de3a9980SAnton Yakovlev snd->card->private_data = snd;
211de3a9980SAnton Yakovlev
212de3a9980SAnton Yakovlev strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
213de3a9980SAnton Yakovlev sizeof(snd->card->driver));
214de3a9980SAnton Yakovlev strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
215de3a9980SAnton Yakovlev sizeof(snd->card->shortname));
216de3a9980SAnton Yakovlev if (dev->parent->bus)
217de3a9980SAnton Yakovlev snprintf(snd->card->longname, sizeof(snd->card->longname),
218de3a9980SAnton Yakovlev VIRTIO_SND_CARD_NAME " at %s/%s/%s",
219de3a9980SAnton Yakovlev dev->parent->bus->name, dev_name(dev->parent),
220de3a9980SAnton Yakovlev dev_name(dev));
221de3a9980SAnton Yakovlev else
222de3a9980SAnton Yakovlev snprintf(snd->card->longname, sizeof(snd->card->longname),
223de3a9980SAnton Yakovlev VIRTIO_SND_CARD_NAME " at %s/%s",
224de3a9980SAnton Yakovlev dev_name(dev->parent), dev_name(dev));
225de3a9980SAnton Yakovlev
226ca61a41fSAnton Yakovlev rc = virtsnd_jack_parse_cfg(snd);
227ca61a41fSAnton Yakovlev if (rc)
228ca61a41fSAnton Yakovlev return rc;
229ca61a41fSAnton Yakovlev
23029b96bf5SAnton Yakovlev rc = virtsnd_pcm_parse_cfg(snd);
23129b96bf5SAnton Yakovlev if (rc)
23229b96bf5SAnton Yakovlev return rc;
23329b96bf5SAnton Yakovlev
23419325fedSAnton Yakovlev rc = virtsnd_chmap_parse_cfg(snd);
23519325fedSAnton Yakovlev if (rc)
23619325fedSAnton Yakovlev return rc;
23719325fedSAnton Yakovlev
238ca61a41fSAnton Yakovlev if (snd->njacks) {
239ca61a41fSAnton Yakovlev rc = virtsnd_jack_build_devs(snd);
240ca61a41fSAnton Yakovlev if (rc)
241ca61a41fSAnton Yakovlev return rc;
242ca61a41fSAnton Yakovlev }
243ca61a41fSAnton Yakovlev
24429b96bf5SAnton Yakovlev if (snd->nsubstreams) {
24529b96bf5SAnton Yakovlev rc = virtsnd_pcm_build_devs(snd);
24629b96bf5SAnton Yakovlev if (rc)
24729b96bf5SAnton Yakovlev return rc;
24829b96bf5SAnton Yakovlev }
24929b96bf5SAnton Yakovlev
25019325fedSAnton Yakovlev if (snd->nchmaps) {
25119325fedSAnton Yakovlev rc = virtsnd_chmap_build_devs(snd);
25219325fedSAnton Yakovlev if (rc)
25319325fedSAnton Yakovlev return rc;
25419325fedSAnton Yakovlev }
25519325fedSAnton Yakovlev
256de3a9980SAnton Yakovlev return snd_card_register(snd->card);
257de3a9980SAnton Yakovlev }
258de3a9980SAnton Yakovlev
259de3a9980SAnton Yakovlev /**
260de3a9980SAnton Yakovlev * virtsnd_validate() - Validate if the device can be started.
261de3a9980SAnton Yakovlev * @vdev: VirtIO parent device.
262de3a9980SAnton Yakovlev *
263de3a9980SAnton Yakovlev * Context: Any context.
264de3a9980SAnton Yakovlev * Return: 0 on success, -EINVAL on failure.
265de3a9980SAnton Yakovlev */
virtsnd_validate(struct virtio_device * vdev)266de3a9980SAnton Yakovlev static int virtsnd_validate(struct virtio_device *vdev)
267de3a9980SAnton Yakovlev {
268de3a9980SAnton Yakovlev if (!vdev->config->get) {
269de3a9980SAnton Yakovlev dev_err(&vdev->dev, "configuration access disabled\n");
270de3a9980SAnton Yakovlev return -EINVAL;
271de3a9980SAnton Yakovlev }
272de3a9980SAnton Yakovlev
273de3a9980SAnton Yakovlev if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
274de3a9980SAnton Yakovlev dev_err(&vdev->dev,
275de3a9980SAnton Yakovlev "device does not comply with spec version 1.x\n");
276de3a9980SAnton Yakovlev return -EINVAL;
277de3a9980SAnton Yakovlev }
278de3a9980SAnton Yakovlev
2799d45e514SAnton Yakovlev if (!virtsnd_msg_timeout_ms) {
2809d45e514SAnton Yakovlev dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n");
2819d45e514SAnton Yakovlev return -EINVAL;
2829d45e514SAnton Yakovlev }
2839d45e514SAnton Yakovlev
28429b96bf5SAnton Yakovlev if (virtsnd_pcm_validate(vdev))
28529b96bf5SAnton Yakovlev return -EINVAL;
28629b96bf5SAnton Yakovlev
287de3a9980SAnton Yakovlev return 0;
288de3a9980SAnton Yakovlev }
289de3a9980SAnton Yakovlev
290de3a9980SAnton Yakovlev /**
291de3a9980SAnton Yakovlev * virtsnd_probe() - Create and initialize the device.
292de3a9980SAnton Yakovlev * @vdev: VirtIO parent device.
293de3a9980SAnton Yakovlev *
294de3a9980SAnton Yakovlev * Context: Any context that permits to sleep.
295de3a9980SAnton Yakovlev * Return: 0 on success, -errno on failure.
296de3a9980SAnton Yakovlev */
virtsnd_probe(struct virtio_device * vdev)297de3a9980SAnton Yakovlev static int virtsnd_probe(struct virtio_device *vdev)
298de3a9980SAnton Yakovlev {
299de3a9980SAnton Yakovlev struct virtio_snd *snd;
300de3a9980SAnton Yakovlev unsigned int i;
301de3a9980SAnton Yakovlev int rc;
302de3a9980SAnton Yakovlev
303de3a9980SAnton Yakovlev snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
304de3a9980SAnton Yakovlev if (!snd)
305de3a9980SAnton Yakovlev return -ENOMEM;
306de3a9980SAnton Yakovlev
307de3a9980SAnton Yakovlev snd->vdev = vdev;
3089d45e514SAnton Yakovlev INIT_LIST_HEAD(&snd->ctl_msgs);
30929b96bf5SAnton Yakovlev INIT_LIST_HEAD(&snd->pcm_list);
310de3a9980SAnton Yakovlev
311de3a9980SAnton Yakovlev vdev->priv = snd;
312de3a9980SAnton Yakovlev
313de3a9980SAnton Yakovlev for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
314de3a9980SAnton Yakovlev spin_lock_init(&snd->queues[i].lock);
315de3a9980SAnton Yakovlev
316de3a9980SAnton Yakovlev rc = virtsnd_find_vqs(snd);
317de3a9980SAnton Yakovlev if (rc)
318de3a9980SAnton Yakovlev goto on_exit;
319de3a9980SAnton Yakovlev
320de3a9980SAnton Yakovlev virtio_device_ready(vdev);
321de3a9980SAnton Yakovlev
322de3a9980SAnton Yakovlev rc = virtsnd_build_devs(snd);
323de3a9980SAnton Yakovlev if (rc)
324de3a9980SAnton Yakovlev goto on_exit;
325de3a9980SAnton Yakovlev
326de3a9980SAnton Yakovlev virtsnd_enable_event_vq(snd);
327de3a9980SAnton Yakovlev
328de3a9980SAnton Yakovlev on_exit:
329de3a9980SAnton Yakovlev if (rc)
330de3a9980SAnton Yakovlev virtsnd_remove(vdev);
331de3a9980SAnton Yakovlev
332de3a9980SAnton Yakovlev return rc;
333de3a9980SAnton Yakovlev }
334de3a9980SAnton Yakovlev
335de3a9980SAnton Yakovlev /**
336de3a9980SAnton Yakovlev * virtsnd_remove() - Remove VirtIO and ALSA devices.
337de3a9980SAnton Yakovlev * @vdev: VirtIO parent device.
338de3a9980SAnton Yakovlev *
339de3a9980SAnton Yakovlev * Context: Any context that permits to sleep.
340de3a9980SAnton Yakovlev */
virtsnd_remove(struct virtio_device * vdev)341de3a9980SAnton Yakovlev static void virtsnd_remove(struct virtio_device *vdev)
342de3a9980SAnton Yakovlev {
343de3a9980SAnton Yakovlev struct virtio_snd *snd = vdev->priv;
34429b96bf5SAnton Yakovlev unsigned int i;
345de3a9980SAnton Yakovlev
346de3a9980SAnton Yakovlev virtsnd_disable_event_vq(snd);
3479d45e514SAnton Yakovlev virtsnd_ctl_msg_cancel_all(snd);
348de3a9980SAnton Yakovlev
349de3a9980SAnton Yakovlev if (snd->card)
350de3a9980SAnton Yakovlev snd_card_free(snd->card);
351de3a9980SAnton Yakovlev
352de3a9980SAnton Yakovlev vdev->config->del_vqs(vdev);
353*d9679d00SMichael S. Tsirkin virtio_reset_device(vdev);
354de3a9980SAnton Yakovlev
355f40a2867SAnton Yakovlev for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
356f40a2867SAnton Yakovlev struct virtio_pcm_substream *vss = &snd->substreams[i];
357f40a2867SAnton Yakovlev
358f40a2867SAnton Yakovlev cancel_work_sync(&vss->elapsed_period);
359f40a2867SAnton Yakovlev virtsnd_pcm_msg_free(vss);
360f40a2867SAnton Yakovlev }
36129b96bf5SAnton Yakovlev
362de3a9980SAnton Yakovlev kfree(snd->event_msgs);
363de3a9980SAnton Yakovlev }
364de3a9980SAnton Yakovlev
365575483e9SAnton Yakovlev #ifdef CONFIG_PM_SLEEP
366575483e9SAnton Yakovlev /**
367575483e9SAnton Yakovlev * virtsnd_freeze() - Suspend device.
368575483e9SAnton Yakovlev * @vdev: VirtIO parent device.
369575483e9SAnton Yakovlev *
370575483e9SAnton Yakovlev * Context: Any context.
371575483e9SAnton Yakovlev * Return: 0 on success, -errno on failure.
372575483e9SAnton Yakovlev */
virtsnd_freeze(struct virtio_device * vdev)373575483e9SAnton Yakovlev static int virtsnd_freeze(struct virtio_device *vdev)
374575483e9SAnton Yakovlev {
375575483e9SAnton Yakovlev struct virtio_snd *snd = vdev->priv;
376575483e9SAnton Yakovlev unsigned int i;
377575483e9SAnton Yakovlev
378575483e9SAnton Yakovlev virtsnd_disable_event_vq(snd);
379575483e9SAnton Yakovlev virtsnd_ctl_msg_cancel_all(snd);
380575483e9SAnton Yakovlev
381575483e9SAnton Yakovlev vdev->config->del_vqs(vdev);
382*d9679d00SMichael S. Tsirkin virtio_reset_device(vdev);
383575483e9SAnton Yakovlev
384575483e9SAnton Yakovlev for (i = 0; i < snd->nsubstreams; ++i)
385575483e9SAnton Yakovlev cancel_work_sync(&snd->substreams[i].elapsed_period);
386575483e9SAnton Yakovlev
387575483e9SAnton Yakovlev kfree(snd->event_msgs);
388575483e9SAnton Yakovlev snd->event_msgs = NULL;
389575483e9SAnton Yakovlev
390575483e9SAnton Yakovlev return 0;
391575483e9SAnton Yakovlev }
392575483e9SAnton Yakovlev
393575483e9SAnton Yakovlev /**
394575483e9SAnton Yakovlev * virtsnd_restore() - Resume device.
395575483e9SAnton Yakovlev * @vdev: VirtIO parent device.
396575483e9SAnton Yakovlev *
397575483e9SAnton Yakovlev * Context: Any context.
398575483e9SAnton Yakovlev * Return: 0 on success, -errno on failure.
399575483e9SAnton Yakovlev */
virtsnd_restore(struct virtio_device * vdev)400575483e9SAnton Yakovlev static int virtsnd_restore(struct virtio_device *vdev)
401575483e9SAnton Yakovlev {
402575483e9SAnton Yakovlev struct virtio_snd *snd = vdev->priv;
403575483e9SAnton Yakovlev int rc;
404575483e9SAnton Yakovlev
405575483e9SAnton Yakovlev rc = virtsnd_find_vqs(snd);
406575483e9SAnton Yakovlev if (rc)
407575483e9SAnton Yakovlev return rc;
408575483e9SAnton Yakovlev
409575483e9SAnton Yakovlev virtio_device_ready(vdev);
410575483e9SAnton Yakovlev
411575483e9SAnton Yakovlev virtsnd_enable_event_vq(snd);
412575483e9SAnton Yakovlev
413575483e9SAnton Yakovlev return 0;
414575483e9SAnton Yakovlev }
415575483e9SAnton Yakovlev #endif /* CONFIG_PM_SLEEP */
416575483e9SAnton Yakovlev
417de3a9980SAnton Yakovlev static const struct virtio_device_id id_table[] = {
418de3a9980SAnton Yakovlev { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
419de3a9980SAnton Yakovlev { 0 },
420de3a9980SAnton Yakovlev };
421de3a9980SAnton Yakovlev
422de3a9980SAnton Yakovlev static struct virtio_driver virtsnd_driver = {
423de3a9980SAnton Yakovlev .driver.name = KBUILD_MODNAME,
424de3a9980SAnton Yakovlev .driver.owner = THIS_MODULE,
425de3a9980SAnton Yakovlev .id_table = id_table,
426de3a9980SAnton Yakovlev .validate = virtsnd_validate,
427de3a9980SAnton Yakovlev .probe = virtsnd_probe,
428de3a9980SAnton Yakovlev .remove = virtsnd_remove,
429575483e9SAnton Yakovlev #ifdef CONFIG_PM_SLEEP
430575483e9SAnton Yakovlev .freeze = virtsnd_freeze,
431575483e9SAnton Yakovlev .restore = virtsnd_restore,
432575483e9SAnton Yakovlev #endif
433de3a9980SAnton Yakovlev };
434de3a9980SAnton Yakovlev
435d91cbe83SChen Huang module_virtio_driver(virtsnd_driver);
436de3a9980SAnton Yakovlev
437de3a9980SAnton Yakovlev MODULE_DEVICE_TABLE(virtio, id_table);
438de3a9980SAnton Yakovlev MODULE_DESCRIPTION("Virtio sound card driver");
439de3a9980SAnton Yakovlev MODULE_LICENSE("GPL");
440