xref: /openbmc/linux/sound/virtio/virtio_card.c (revision de3a9980)
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 static void virtsnd_remove(struct virtio_device *vdev);
15 
16 /**
17  * virtsnd_event_send() - Add an event to the event queue.
18  * @vqueue: Underlying event virtqueue.
19  * @event: Event.
20  * @notify: Indicates whether or not to send a notification to the device.
21  * @gfp: Kernel flags for memory allocation.
22  *
23  * Context: Any context.
24  */
25 static void virtsnd_event_send(struct virtqueue *vqueue,
26 			       struct virtio_snd_event *event, bool notify,
27 			       gfp_t gfp)
28 {
29 	struct scatterlist sg;
30 	struct scatterlist *psgs[1] = { &sg };
31 
32 	/* reset event content */
33 	memset(event, 0, sizeof(*event));
34 
35 	sg_init_one(&sg, event, sizeof(*event));
36 
37 	if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify)
38 		return;
39 
40 	if (virtqueue_kick_prepare(vqueue))
41 		virtqueue_notify(vqueue);
42 }
43 
44 /**
45  * virtsnd_event_dispatch() - Dispatch an event from the device side.
46  * @snd: VirtIO sound device.
47  * @event: VirtIO sound event.
48  *
49  * Context: Any context.
50  */
51 static void virtsnd_event_dispatch(struct virtio_snd *snd,
52 				   struct virtio_snd_event *event)
53 {
54 }
55 
56 /**
57  * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue.
58  * @vqueue: Underlying event virtqueue.
59  *
60  * This callback function is called upon a vring interrupt request from the
61  * device.
62  *
63  * Context: Interrupt context.
64  */
65 static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
66 {
67 	struct virtio_snd *snd = vqueue->vdev->priv;
68 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
69 	struct virtio_snd_event *event;
70 	u32 length;
71 	unsigned long flags;
72 
73 	spin_lock_irqsave(&queue->lock, flags);
74 	do {
75 		virtqueue_disable_cb(vqueue);
76 		while ((event = virtqueue_get_buf(vqueue, &length))) {
77 			virtsnd_event_dispatch(snd, event);
78 			virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
79 		}
80 		if (unlikely(virtqueue_is_broken(vqueue)))
81 			break;
82 	} while (!virtqueue_enable_cb(vqueue));
83 	spin_unlock_irqrestore(&queue->lock, flags);
84 }
85 
86 /**
87  * virtsnd_find_vqs() - Enumerate and initialize all virtqueues.
88  * @snd: VirtIO sound device.
89  *
90  * After calling this function, the event queue is disabled.
91  *
92  * Context: Any context.
93  * Return: 0 on success, -errno on failure.
94  */
95 static int virtsnd_find_vqs(struct virtio_snd *snd)
96 {
97 	struct virtio_device *vdev = snd->vdev;
98 	static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
99 		[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb
100 	};
101 	static const char *names[VIRTIO_SND_VQ_MAX] = {
102 		[VIRTIO_SND_VQ_EVENT] = "virtsnd-event"
103 	};
104 	struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
105 	unsigned int i;
106 	unsigned int n;
107 	int rc;
108 
109 	rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names,
110 			     NULL);
111 	if (rc) {
112 		dev_err(&vdev->dev, "failed to initialize virtqueues\n");
113 		return rc;
114 	}
115 
116 	for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
117 		snd->queues[i].vqueue = vqs[i];
118 
119 	/* Allocate events and populate the event queue */
120 	virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]);
121 
122 	n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]);
123 
124 	snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs),
125 					GFP_KERNEL);
126 	if (!snd->event_msgs)
127 		return -ENOMEM;
128 
129 	for (i = 0; i < n; ++i)
130 		virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT],
131 				   &snd->event_msgs[i], false, GFP_KERNEL);
132 
133 	return 0;
134 }
135 
136 /**
137  * virtsnd_enable_event_vq() - Enable the event virtqueue.
138  * @snd: VirtIO sound device.
139  *
140  * Context: Any context.
141  */
142 static void virtsnd_enable_event_vq(struct virtio_snd *snd)
143 {
144 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
145 
146 	if (!virtqueue_enable_cb(queue->vqueue))
147 		virtsnd_event_notify_cb(queue->vqueue);
148 }
149 
150 /**
151  * virtsnd_disable_event_vq() - Disable the event virtqueue.
152  * @snd: VirtIO sound device.
153  *
154  * Context: Any context.
155  */
156 static void virtsnd_disable_event_vq(struct virtio_snd *snd)
157 {
158 	struct virtio_snd_queue *queue = virtsnd_event_queue(snd);
159 	struct virtio_snd_event *event;
160 	u32 length;
161 	unsigned long flags;
162 
163 	if (queue->vqueue) {
164 		spin_lock_irqsave(&queue->lock, flags);
165 		virtqueue_disable_cb(queue->vqueue);
166 		while ((event = virtqueue_get_buf(queue->vqueue, &length)))
167 			virtsnd_event_dispatch(snd, event);
168 		spin_unlock_irqrestore(&queue->lock, flags);
169 	}
170 }
171 
172 /**
173  * virtsnd_build_devs() - Read configuration and build ALSA devices.
174  * @snd: VirtIO sound device.
175  *
176  * Context: Any context that permits to sleep.
177  * Return: 0 on success, -errno on failure.
178  */
179 static int virtsnd_build_devs(struct virtio_snd *snd)
180 {
181 	struct virtio_device *vdev = snd->vdev;
182 	struct device *dev = &vdev->dev;
183 	int rc;
184 
185 	rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
186 			  THIS_MODULE, 0, &snd->card);
187 	if (rc < 0)
188 		return rc;
189 
190 	snd->card->private_data = snd;
191 
192 	strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER,
193 		sizeof(snd->card->driver));
194 	strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME,
195 		sizeof(snd->card->shortname));
196 	if (dev->parent->bus)
197 		snprintf(snd->card->longname, sizeof(snd->card->longname),
198 			 VIRTIO_SND_CARD_NAME " at %s/%s/%s",
199 			 dev->parent->bus->name, dev_name(dev->parent),
200 			 dev_name(dev));
201 	else
202 		snprintf(snd->card->longname, sizeof(snd->card->longname),
203 			 VIRTIO_SND_CARD_NAME " at %s/%s",
204 			 dev_name(dev->parent), dev_name(dev));
205 
206 	return snd_card_register(snd->card);
207 }
208 
209 /**
210  * virtsnd_validate() - Validate if the device can be started.
211  * @vdev: VirtIO parent device.
212  *
213  * Context: Any context.
214  * Return: 0 on success, -EINVAL on failure.
215  */
216 static int virtsnd_validate(struct virtio_device *vdev)
217 {
218 	if (!vdev->config->get) {
219 		dev_err(&vdev->dev, "configuration access disabled\n");
220 		return -EINVAL;
221 	}
222 
223 	if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
224 		dev_err(&vdev->dev,
225 			"device does not comply with spec version 1.x\n");
226 		return -EINVAL;
227 	}
228 
229 	return 0;
230 }
231 
232 /**
233  * virtsnd_probe() - Create and initialize the device.
234  * @vdev: VirtIO parent device.
235  *
236  * Context: Any context that permits to sleep.
237  * Return: 0 on success, -errno on failure.
238  */
239 static int virtsnd_probe(struct virtio_device *vdev)
240 {
241 	struct virtio_snd *snd;
242 	unsigned int i;
243 	int rc;
244 
245 	snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL);
246 	if (!snd)
247 		return -ENOMEM;
248 
249 	snd->vdev = vdev;
250 
251 	vdev->priv = snd;
252 
253 	for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i)
254 		spin_lock_init(&snd->queues[i].lock);
255 
256 	rc = virtsnd_find_vqs(snd);
257 	if (rc)
258 		goto on_exit;
259 
260 	virtio_device_ready(vdev);
261 
262 	rc = virtsnd_build_devs(snd);
263 	if (rc)
264 		goto on_exit;
265 
266 	virtsnd_enable_event_vq(snd);
267 
268 on_exit:
269 	if (rc)
270 		virtsnd_remove(vdev);
271 
272 	return rc;
273 }
274 
275 /**
276  * virtsnd_remove() - Remove VirtIO and ALSA devices.
277  * @vdev: VirtIO parent device.
278  *
279  * Context: Any context that permits to sleep.
280  */
281 static void virtsnd_remove(struct virtio_device *vdev)
282 {
283 	struct virtio_snd *snd = vdev->priv;
284 
285 	virtsnd_disable_event_vq(snd);
286 
287 	if (snd->card)
288 		snd_card_free(snd->card);
289 
290 	vdev->config->del_vqs(vdev);
291 	vdev->config->reset(vdev);
292 
293 	kfree(snd->event_msgs);
294 }
295 
296 static const struct virtio_device_id id_table[] = {
297 	{ VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID },
298 	{ 0 },
299 };
300 
301 static struct virtio_driver virtsnd_driver = {
302 	.driver.name = KBUILD_MODNAME,
303 	.driver.owner = THIS_MODULE,
304 	.id_table = id_table,
305 	.validate = virtsnd_validate,
306 	.probe = virtsnd_probe,
307 	.remove = virtsnd_remove,
308 };
309 
310 static int __init init(void)
311 {
312 	return register_virtio_driver(&virtsnd_driver);
313 }
314 module_init(init);
315 
316 static void __exit fini(void)
317 {
318 	unregister_virtio_driver(&virtsnd_driver);
319 }
320 module_exit(fini);
321 
322 MODULE_DEVICE_TABLE(virtio, id_table);
323 MODULE_DESCRIPTION("Virtio sound card driver");
324 MODULE_LICENSE("GPL");
325