xref: /openbmc/linux/sound/virtio/virtio_card.c (revision f40a2867)
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_PCM_PERIOD_ELAPSED:
60 	case VIRTIO_SND_EVT_PCM_XRUN:
61 		virtsnd_pcm_event(snd, event);
62 		break;
63 	}
64 }
65 
66 /**
67  * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
68  * @vqueue: Underlying event virtqueue.
69  *
70  * This callback function is called upon a vring interrupt request from the
71  * device.
72  *
73  * Context: Interrupt context.
74  */
75 static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
76 {
77 	struct virtio_snd *snd = vqueue->vdev->priv;
78 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
79 	struct virtio_snd_event *event;
80 	u32 length;
81 	unsigned long flags;
82 
83 	spin_lock_irqsave(&queue->lock, flags);
84 	do {
85 		virtqueue_disable_cb(vqueue);
86 		while ((event = virtqueue_get_buf(vqueue, &length))) {
87 			virtsnd_event_dispatch(snd, event);
88 			virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
89 		}
90 		if (unlikely(virtqueue_is_broken(vqueue)))
91 			break;
92 	} while (!virtqueue_enable_cb(vqueue));
93 	spin_unlock_irqrestore(&queue->lock, flags);
94 }
95 
96 /**
97  * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
98  * @snd: VirtIO sound device.
99  *
100  * After calling this function, the event queue is disabled.
101  *
102  * Context: Any context.
103  * Return: 0 on success, -errno on failure.
104  */
105 static int virtsnd_find_vqs(struct virtio_snd *snd)
106 {
107 	struct virtio_device *vdev = snd->vdev;
108 	static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
109 		[VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb,
110 		[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb,
111 		[VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb,
112 		[VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb
113 	};
114 	static const char *names[VIRTIO_SND_VQ_MAX] = {
115 		[VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl",
116 		[VIRTIO_SND_VQ_EVENT] = "virtsnd-event",
117 		[VIRTIO_SND_VQ_TX] = "virtsnd-tx",
118 		[VIRTIO_SND_VQ_RX] = "virtsnd-rx"
119 	};
120 	struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
121 	unsigned int i;
122 	unsigned int n;
123 	int rc;
124 
125 	rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
126 			     NULL);
127 	if (rc) {
128 		dev_err(&vdev->dev, "failed to initialize virtqueues\n");
129 		return rc;
130 	}
131 
132 	for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
133 		snd->queues[i].vqueue = vqs[i];
134 
135 	/* Allocate events and populate the event queue */
136 	virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
137 
138 	n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
139 
140 	snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
141 					GFP_KERNEL);
142 	if (!snd->event_msgs)
143 		return -ENOMEM;
144 
145 	for (i = 0; i < n; ++i)
146 		virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
147 				   &snd->event_msgs[i], false, GFP_KERNEL);
148 
149 	return 0;
150 }
151 
152 /**
153  * virtsnd_enable_event_vq() - Enable the event virtqueue.
154  * @snd: VirtIO sound device.
155  *
156  * Context: Any context.
157  */
158 static void virtsnd_enable_event_vq(struct virtio_snd *snd)
159 {
160 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
161 
162 	if (!virtqueue_enable_cb(queue->vqueue))
163 		virtsnd_event_notify_cb(queue->vqueue);
164 }
165 
166 /**
167  * virtsnd_disable_event_vq() - Disable the event virtqueue.
168  * @snd: VirtIO sound device.
169  *
170  * Context: Any context.
171  */
172 static void virtsnd_disable_event_vq(struct virtio_snd *snd)
173 {
174 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
175 	struct virtio_snd_event *event;
176 	u32 length;
177 	unsigned long flags;
178 
179 	if (queue->vqueue) {
180 		spin_lock_irqsave(&queue->lock, flags);
181 		virtqueue_disable_cb(queue->vqueue);
182 		while ((event = virtqueue_get_buf(queue->vqueue, &length)))
183 			virtsnd_event_dispatch(snd, event);
184 		spin_unlock_irqrestore(&queue->lock, flags);
185 	}
186 }
187 
188 /**
189  * virtsnd_build_devs() - Read configuration and build ALSA devices.
190  * @snd: VirtIO sound device.
191  *
192  * Context: Any context that permits to sleep.
193  * Return: 0 on success, -errno on failure.
194  */
195 static int virtsnd_build_devs(struct virtio_snd *snd)
196 {
197 	struct virtio_device *vdev = snd->vdev;
198 	struct device *dev = &vdev->dev;
199 	int rc;
200 
201 	rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
202 			  THIS_MODULE, 0, &snd->card);
203 	if (rc < 0)
204 		return rc;
205 
206 	snd->card->private_data = snd;
207 
208 	strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
209 		sizeof(snd->card->driver));
210 	strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
211 		sizeof(snd->card->shortname));
212 	if (dev->parent->bus)
213 		snprintf(snd->card->longname, sizeof(snd->card->longname),
214 			 VIRTIO_SND_CARD_NAME " at %s/%s/%s",
215 			 dev->parent->bus->name, dev_name(dev->parent),
216 			 dev_name(dev));
217 	else
218 		snprintf(snd->card->longname, sizeof(snd->card->longname),
219 			 VIRTIO_SND_CARD_NAME " at %s/%s",
220 			 dev_name(dev->parent), dev_name(dev));
221 
222 	rc = virtsnd_pcm_parse_cfg(snd);
223 	if (rc)
224 		return rc;
225 
226 	if (snd->nsubstreams) {
227 		rc = virtsnd_pcm_build_devs(snd);
228 		if (rc)
229 			return rc;
230 	}
231 
232 	return snd_card_register(snd->card);
233 }
234 
235 /**
236  * virtsnd_validate() - Validate if the device can be started.
237  * @vdev: VirtIO parent device.
238  *
239  * Context: Any context.
240  * Return: 0 on success, -EINVAL on failure.
241  */
242 static int virtsnd_validate(struct virtio_device *vdev)
243 {
244 	if (!vdev->config->get) {
245 		dev_err(&vdev->dev, "configuration access disabled\n");
246 		return -EINVAL;
247 	}
248 
249 	if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
250 		dev_err(&vdev->dev,
251 			"device does not comply with spec version 1.x\n");
252 		return -EINVAL;
253 	}
254 
255 	if (!virtsnd_msg_timeout_ms) {
256 		dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n");
257 		return -EINVAL;
258 	}
259 
260 	if (virtsnd_pcm_validate(vdev))
261 		return -EINVAL;
262 
263 	return 0;
264 }
265 
266 /**
267  * virtsnd_probe() - Create and initialize the device.
268  * @vdev: VirtIO parent device.
269  *
270  * Context: Any context that permits to sleep.
271  * Return: 0 on success, -errno on failure.
272  */
273 static int virtsnd_probe(struct virtio_device *vdev)
274 {
275 	struct virtio_snd *snd;
276 	unsigned int i;
277 	int rc;
278 
279 	snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
280 	if (!snd)
281 		return -ENOMEM;
282 
283 	snd->vdev = vdev;
284 	INIT_LIST_HEAD(&snd->ctl_msgs);
285 	INIT_LIST_HEAD(&snd->pcm_list);
286 
287 	vdev->priv = snd;
288 
289 	for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
290 		spin_lock_init(&snd->queues[i].lock);
291 
292 	rc = virtsnd_find_vqs(snd);
293 	if (rc)
294 		goto on_exit;
295 
296 	virtio_device_ready(vdev);
297 
298 	rc = virtsnd_build_devs(snd);
299 	if (rc)
300 		goto on_exit;
301 
302 	virtsnd_enable_event_vq(snd);
303 
304 on_exit:
305 	if (rc)
306 		virtsnd_remove(vdev);
307 
308 	return rc;
309 }
310 
311 /**
312  * virtsnd_remove() - Remove VirtIO and ALSA devices.
313  * @vdev: VirtIO parent device.
314  *
315  * Context: Any context that permits to sleep.
316  */
317 static void virtsnd_remove(struct virtio_device *vdev)
318 {
319 	struct virtio_snd *snd = vdev->priv;
320 	unsigned int i;
321 
322 	virtsnd_disable_event_vq(snd);
323 	virtsnd_ctl_msg_cancel_all(snd);
324 
325 	if (snd->card)
326 		snd_card_free(snd->card);
327 
328 	vdev->config->del_vqs(vdev);
329 	vdev->config->reset(vdev);
330 
331 	for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
332 		struct virtio_pcm_substream *vss = &snd->substreams[i];
333 
334 		cancel_work_sync(&vss->elapsed_period);
335 		virtsnd_pcm_msg_free(vss);
336 	}
337 
338 	kfree(snd->event_msgs);
339 }
340 
341 static const struct virtio_device_id id_table[] = {
342 	{ VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
343 	{ 0 },
344 };
345 
346 static struct virtio_driver virtsnd_driver = {
347 	.driver.name = KBUILD_MODNAME,
348 	.driver.owner = THIS_MODULE,
349 	.id_table = id_table,
350 	.validate = virtsnd_validate,
351 	.probe = virtsnd_probe,
352 	.remove = virtsnd_remove,
353 };
354 
355 static int __init init(void)
356 {
357 	return register_virtio_driver(&virtsnd_driver);
358 }
359 module_init(init);
360 
361 static void __exit fini(void)
362 {
363 	unregister_virtio_driver(&virtsnd_driver);
364 }
365 module_exit(fini);
366 
367 MODULE_DEVICE_TABLE(virtio, id_table);
368 MODULE_DESCRIPTION("Virtio sound card driver");
369 MODULE_LICENSE("GPL");
370