]> Git Repo - linux.git/blob - sound/virtio/virtio_card.c
Linux 6.14-rc3
[linux.git] / sound / virtio / virtio_card.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * virtio-snd: Virtio sound device
4  * Copyright (C) 2021 OpenSynergy GmbH
5  */
6 #include <linux/module.h>
7 #include <linux/moduleparam.h>
8 #include <linux/virtio_config.h>
9 #include <sound/initval.h>
10 #include <uapi/linux/virtio_ids.h>
11
12 #include "virtio_card.h"
13
14 u32 virtsnd_msg_timeout_ms = MSEC_PER_SEC;
15 module_param_named(msg_timeout_ms, virtsnd_msg_timeout_ms, uint, 0644);
16 MODULE_PARM_DESC(msg_timeout_ms, "Message completion timeout in milliseconds");
17
18 static void virtsnd_remove(struct virtio_device *vdev);
19
20 /**
21  * virtsnd_event_send() - Add an event to the event queue.
22  * @vqueue: Underlying event virtqueue.
23  * @event: Event.
24  * @notify: Indicates whether or not to send a notification to the device.
25  * @gfp: Kernel flags for memory allocation.
26  *
27  * Context: Any context.
28  */
29 static void virtsnd_event_send(struct virtqueue *vqueue,
30                                struct virtio_snd_event *event, bool notify,
31                                gfp_t gfp)
32 {
33         struct scatterlist sg;
34         struct scatterlist *psgs[1] = { &sg };
35
36         /* reset event content */
37         memset(event, 0, sizeof(*event));
38
39         sg_init_one(&sg, event, sizeof(*event));
40
41         if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify)
42                 return;
43
44         if (virtqueue_kick_prepare(vqueue))
45                 virtqueue_notify(vqueue);
46 }
47
48 /**
49  * virtsnd_event_dispatch() - Dispatch an event from the device side.
50  * @snd: VirtIO sound device.
51  * @event: VirtIO sound event.
52  *
53  * Context: Any context.
54  */
55 static void virtsnd_event_dispatch(struct virtio_snd *snd,
56                                    struct virtio_snd_event *event)
57 {
58         switch (le32_to_cpu(event->hdr.code)) {
59         case VIRTIO_SND_EVT_JACK_CONNECTED:
60         case VIRTIO_SND_EVT_JACK_DISCONNECTED:
61                 virtsnd_jack_event(snd, event);
62                 break;
63         case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
64         case VIRTIO_SND_EVT_PCM_XRUN:
65                 virtsnd_pcm_event(snd, event);
66                 break;
67         case VIRTIO_SND_EVT_CTL_NOTIFY:
68                 virtsnd_kctl_event(snd, event);
69                 break;
70         }
71 }
72
73 /**
74  * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
75  * @vqueue: Underlying event virtqueue.
76  *
77  * This callback function is called upon a vring interrupt request from the
78  * device.
79  *
80  * Context: Interrupt context.
81  */
82 static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
83 {
84         struct virtio_snd *snd = vqueue->vdev->priv;
85         struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
86         struct virtio_snd_event *event;
87         u32 length;
88         unsigned long flags;
89
90         spin_lock_irqsave(&queue->lock, flags);
91         do {
92                 virtqueue_disable_cb(vqueue);
93                 while ((event = virtqueue_get_buf(vqueue, &length))) {
94                         virtsnd_event_dispatch(snd, event);
95                         virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
96                 }
97         } while (!virtqueue_enable_cb(vqueue));
98         spin_unlock_irqrestore(&queue->lock, flags);
99 }
100
101 /**
102  * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
103  * @snd: VirtIO sound device.
104  *
105  * After calling this function, the event queue is disabled.
106  *
107  * Context: Any context.
108  * Return: 0 on success, -errno on failure.
109  */
110 static int virtsnd_find_vqs(struct virtio_snd *snd)
111 {
112         struct virtio_device *vdev = snd->vdev;
113         struct virtqueue_info vqs_info[VIRTIO_SND_VQ_MAX] = {
114                 [VIRTIO_SND_VQ_CONTROL] = { "virtsnd-ctl",
115                                             virtsnd_ctl_notify_cb },
116                 [VIRTIO_SND_VQ_EVENT] = { "virtsnd-event",
117                                           virtsnd_event_notify_cb },
118                 [VIRTIO_SND_VQ_TX] = { "virtsnd-tx",
119                                        virtsnd_pcm_tx_notify_cb },
120                 [VIRTIO_SND_VQ_RX] = { "virtsnd-rx",
121                                        virtsnd_pcm_rx_notify_cb },
122         };
123         struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
124         unsigned int i;
125         unsigned int n;
126         int rc;
127
128         rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, vqs_info, NULL);
129         if (rc) {
130                 dev_err(&vdev->dev, "failed to initialize virtqueues\n");
131                 return rc;
132         }
133
134         for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
135                 snd->queues[i].vqueue = vqs[i];
136
137         /* Allocate events and populate the event queue */
138         virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
139
140         n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
141
142         snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
143                                         GFP_KERNEL);
144         if (!snd->event_msgs)
145                 return -ENOMEM;
146
147         for (i = 0; i < n; ++i)
148                 virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
149                                    &snd->event_msgs[i], false, GFP_KERNEL);
150
151         return 0;
152 }
153
154 /**
155  * virtsnd_enable_event_vq() - Enable the event virtqueue.
156  * @snd: VirtIO sound device.
157  *
158  * Context: Any context.
159  */
160 static void virtsnd_enable_event_vq(struct virtio_snd *snd)
161 {
162         struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
163
164         if (!virtqueue_enable_cb(queue->vqueue))
165                 virtsnd_event_notify_cb(queue->vqueue);
166 }
167
168 /**
169  * virtsnd_disable_event_vq() - Disable the event virtqueue.
170  * @snd: VirtIO sound device.
171  *
172  * Context: Any context.
173  */
174 static void virtsnd_disable_event_vq(struct virtio_snd *snd)
175 {
176         struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
177         struct virtio_snd_event *event;
178         u32 length;
179         unsigned long flags;
180
181         if (queue->vqueue) {
182                 spin_lock_irqsave(&queue->lock, flags);
183                 virtqueue_disable_cb(queue->vqueue);
184                 while ((event = virtqueue_get_buf(queue->vqueue, &length)))
185                         virtsnd_event_dispatch(snd, event);
186                 spin_unlock_irqrestore(&queue->lock, flags);
187         }
188 }
189
190 /**
191  * virtsnd_build_devs() - Read configuration and build ALSA devices.
192  * @snd: VirtIO sound device.
193  *
194  * Context: Any context that permits to sleep.
195  * Return: 0 on success, -errno on failure.
196  */
197 static int virtsnd_build_devs(struct virtio_snd *snd)
198 {
199         struct virtio_device *vdev = snd->vdev;
200         struct device *dev = &vdev->dev;
201         int rc;
202
203         rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
204                           THIS_MODULE, 0, &snd->card);
205         if (rc < 0)
206                 return rc;
207
208         snd->card->private_data = snd;
209
210         strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
211                 sizeof(snd->card->driver));
212         strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
213                 sizeof(snd->card->shortname));
214         if (dev->parent->bus)
215                 snprintf(snd->card->longname, sizeof(snd->card->longname),
216                          VIRTIO_SND_CARD_NAME " at %s/%s/%s",
217                          dev->parent->bus->name, dev_name(dev->parent),
218                          dev_name(dev));
219         else
220                 snprintf(snd->card->longname, sizeof(snd->card->longname),
221                          VIRTIO_SND_CARD_NAME " at %s/%s",
222                          dev_name(dev->parent), dev_name(dev));
223
224         rc = virtsnd_jack_parse_cfg(snd);
225         if (rc)
226                 return rc;
227
228         rc = virtsnd_pcm_parse_cfg(snd);
229         if (rc)
230                 return rc;
231
232         rc = virtsnd_chmap_parse_cfg(snd);
233         if (rc)
234                 return rc;
235
236         if (virtio_has_feature(vdev, VIRTIO_SND_F_CTLS)) {
237                 rc = virtsnd_kctl_parse_cfg(snd);
238                 if (rc)
239                         return rc;
240         }
241
242         if (snd->njacks) {
243                 rc = virtsnd_jack_build_devs(snd);
244                 if (rc)
245                         return rc;
246         }
247
248         if (snd->nsubstreams) {
249                 rc = virtsnd_pcm_build_devs(snd);
250                 if (rc)
251                         return rc;
252         }
253
254         if (snd->nchmaps) {
255                 rc = virtsnd_chmap_build_devs(snd);
256                 if (rc)
257                         return rc;
258         }
259
260         if (snd->nkctls) {
261                 rc = virtsnd_kctl_build_devs(snd);
262                 if (rc)
263                         return rc;
264         }
265
266         return snd_card_register(snd->card);
267 }
268
269 /**
270  * virtsnd_validate() - Validate if the device can be started.
271  * @vdev: VirtIO parent device.
272  *
273  * Context: Any context.
274  * Return: 0 on success, -EINVAL on failure.
275  */
276 static int virtsnd_validate(struct virtio_device *vdev)
277 {
278         if (!vdev->config->get) {
279                 dev_err(&vdev->dev, "configuration access disabled\n");
280                 return -EINVAL;
281         }
282
283         if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
284                 dev_err(&vdev->dev,
285                         "device does not comply with spec version 1.x\n");
286                 return -EINVAL;
287         }
288
289         if (!virtsnd_msg_timeout_ms) {
290                 dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n");
291                 return -EINVAL;
292         }
293
294         if (virtsnd_pcm_validate(vdev))
295                 return -EINVAL;
296
297         return 0;
298 }
299
300 /**
301  * virtsnd_probe() - Create and initialize the device.
302  * @vdev: VirtIO parent device.
303  *
304  * Context: Any context that permits to sleep.
305  * Return: 0 on success, -errno on failure.
306  */
307 static int virtsnd_probe(struct virtio_device *vdev)
308 {
309         struct virtio_snd *snd;
310         unsigned int i;
311         int rc;
312
313         snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
314         if (!snd)
315                 return -ENOMEM;
316
317         snd->vdev = vdev;
318         INIT_LIST_HEAD(&snd->ctl_msgs);
319         INIT_LIST_HEAD(&snd->pcm_list);
320
321         vdev->priv = snd;
322
323         for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
324                 spin_lock_init(&snd->queues[i].lock);
325
326         rc = virtsnd_find_vqs(snd);
327         if (rc)
328                 goto on_exit;
329
330         virtio_device_ready(vdev);
331
332         rc = virtsnd_build_devs(snd);
333         if (rc)
334                 goto on_exit;
335
336         virtsnd_enable_event_vq(snd);
337
338 on_exit:
339         if (rc)
340                 virtsnd_remove(vdev);
341
342         return rc;
343 }
344
345 /**
346  * virtsnd_remove() - Remove VirtIO and ALSA devices.
347  * @vdev: VirtIO parent device.
348  *
349  * Context: Any context that permits to sleep.
350  */
351 static void virtsnd_remove(struct virtio_device *vdev)
352 {
353         struct virtio_snd *snd = vdev->priv;
354         unsigned int i;
355
356         virtsnd_disable_event_vq(snd);
357         virtsnd_ctl_msg_cancel_all(snd);
358
359         if (snd->card)
360                 snd_card_free(snd->card);
361
362         vdev->config->del_vqs(vdev);
363         virtio_reset_device(vdev);
364
365         for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
366                 struct virtio_pcm_substream *vss = &snd->substreams[i];
367
368                 cancel_work_sync(&vss->elapsed_period);
369                 virtsnd_pcm_msg_free(vss);
370         }
371
372         kfree(snd->event_msgs);
373 }
374
375 #ifdef CONFIG_PM_SLEEP
376 /**
377  * virtsnd_freeze() - Suspend device.
378  * @vdev: VirtIO parent device.
379  *
380  * Context: Any context.
381  * Return: 0 on success, -errno on failure.
382  */
383 static int virtsnd_freeze(struct virtio_device *vdev)
384 {
385         struct virtio_snd *snd = vdev->priv;
386         unsigned int i;
387
388         virtsnd_disable_event_vq(snd);
389         virtsnd_ctl_msg_cancel_all(snd);
390
391         vdev->config->del_vqs(vdev);
392         virtio_reset_device(vdev);
393
394         for (i = 0; i < snd->nsubstreams; ++i)
395                 cancel_work_sync(&snd->substreams[i].elapsed_period);
396
397         kfree(snd->event_msgs);
398         snd->event_msgs = NULL;
399
400         return 0;
401 }
402
403 /**
404  * virtsnd_restore() - Resume device.
405  * @vdev: VirtIO parent device.
406  *
407  * Context: Any context.
408  * Return: 0 on success, -errno on failure.
409  */
410 static int virtsnd_restore(struct virtio_device *vdev)
411 {
412         struct virtio_snd *snd = vdev->priv;
413         int rc;
414
415         rc = virtsnd_find_vqs(snd);
416         if (rc)
417                 return rc;
418
419         virtio_device_ready(vdev);
420
421         virtsnd_enable_event_vq(snd);
422
423         return 0;
424 }
425 #endif /* CONFIG_PM_SLEEP */
426
427 static const struct virtio_device_id id_table[] = {
428         { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
429         { 0 },
430 };
431
432 static unsigned int features[] = {
433         VIRTIO_SND_F_CTLS
434 };
435
436 static struct virtio_driver virtsnd_driver = {
437         .driver.name = KBUILD_MODNAME,
438         .id_table = id_table,
439         .feature_table = features,
440         .feature_table_size = ARRAY_SIZE(features),
441         .validate = virtsnd_validate,
442         .probe = virtsnd_probe,
443         .remove = virtsnd_remove,
444 #ifdef CONFIG_PM_SLEEP
445         .freeze = virtsnd_freeze,
446         .restore = virtsnd_restore,
447 #endif
448 };
449
450 module_virtio_driver(virtsnd_driver);
451
452 MODULE_DEVICE_TABLE(virtio, id_table);
453 MODULE_DESCRIPTION("Virtio sound card driver");
454 MODULE_LICENSE("GPL");
This page took 0.056182 seconds and 4 git commands to generate.