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