1 // SPDX-License-Identifier: GPL-2.0+
2 /* Driver for virtio video device.
4 * Copyright 2020 OpenSynergy GmbH.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 #include <linux/module.h>
20 #include <linux/version.h>
21 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)
22 #include <linux/dma-map-ops.h>
24 #include <linux/dma-mapping.h>
27 #include "virtio_video.h"
29 static unsigned int debug;
30 module_param(debug, uint, 0644);
32 static unsigned int use_dma_mem;
33 module_param(use_dma_mem, uint, 0644);
34 MODULE_PARM_DESC(use_dma_mem, "Try to allocate buffers from the DMA zone");
36 static int vid_nr_dec = -1;
37 module_param(vid_nr_dec, int, 0644);
38 MODULE_PARM_DESC(vid_nr_dec, "videoN start number, -1 is autodetect");
40 static int vid_nr_enc = -1;
41 module_param(vid_nr_enc, int, 0644);
42 MODULE_PARM_DESC(vid_nr_enc, "videoN start number, -1 is autodetect");
44 static int vid_nr_cam = -1;
45 module_param(vid_nr_cam, int, 0644);
46 MODULE_PARM_DESC(vid_nr_cam, "videoN start number, -1 is autodetect");
48 static bool mplane_cam = true;
49 module_param(mplane_cam, bool, 0644);
50 MODULE_PARM_DESC(mplane_cam,
51 "1 (default) - multiplanar camera, 0 - single planar camera");
53 static int virtio_video_probe(struct virtio_device *vdev)
56 struct virtio_video_device *vvd;
57 struct virtqueue *vqs[2];
58 struct device *dev = &vdev->dev;
59 struct device *pdev = dev->parent;
61 static const char * const names[] = { "commandq", "eventq" };
62 static vq_callback_t *callbacks[] = {
67 if (!virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES)) {
68 dev_err(dev, "device must support guest allocated buffers\n");
72 vvd = devm_kzalloc(dev, sizeof(*vvd), GFP_KERNEL);
76 vvd->is_m2m_dev = true;
78 switch (vdev->id.device) {
79 case VIRTIO_ID_VIDEO_CAM:
80 vvd->is_m2m_dev = false;
81 vvd->vid_dev_nr = vid_nr_cam;
82 vvd->is_mplane_cam = mplane_cam;
83 vvd->type = VIRTIO_VIDEO_DEVICE_CAMERA;
85 case VIRTIO_ID_VIDEO_ENC:
86 vvd->vid_dev_nr = vid_nr_enc;
87 vvd->type = VIRTIO_VIDEO_DEVICE_ENCODER;
89 case VIRTIO_ID_VIDEO_DEC:
91 vvd->vid_dev_nr = vid_nr_dec;
92 vvd->type = VIRTIO_VIDEO_DEVICE_DECODER;
98 vvd->use_dma_mem = use_dma_mem;
101 spin_lock_init(&vvd->pending_buf_list_lock);
102 spin_lock_init(&vvd->resource_idr_lock);
103 idr_init(&vvd->resource_idr);
104 spin_lock_init(&vvd->stream_idr_lock);
105 idr_init(&vvd->stream_idr);
107 init_waitqueue_head(&vvd->wq);
109 if (virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG))
110 vvd->supp_non_contig = true;
112 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
113 vvd->has_iommu = !virtio_has_dma_quirk(vdev);
115 vvd->has_iommu = !virtio_has_iommu_quirk(vdev);
119 set_dma_ops(dev, pdev->dma_ops);
122 * Set it to coherent_dma_mask by default if the architecture
123 * code has not set it.
126 dev->dma_mask = &dev->coherent_dma_mask;
128 dma_set_mask(dev, *pdev->dma_mask);
130 dev_set_name(dev, "%s.%i", DRIVER_NAME, vdev->index);
131 ret = v4l2_device_register(dev, &vvd->v4l2_dev);
135 spin_lock_init(&vvd->commandq.qlock);
136 init_waitqueue_head(&vvd->commandq.reclaim_queue);
138 INIT_WORK(&vvd->eventq.work, virtio_video_process_events);
140 INIT_LIST_HEAD(&vvd->pending_vbuf_list);
142 ret = virtio_find_vqs(vdev, 2, vqs, callbacks, names, NULL);
144 v4l2_err(&vvd->v4l2_dev, "failed to find virt queues\n");
148 vvd->commandq.vq = vqs[0];
149 vvd->eventq.vq = vqs[1];
151 ret = virtio_video_alloc_vbufs(vvd);
153 v4l2_err(&vvd->v4l2_dev, "failed to alloc vbufs\n");
157 virtio_cread(vdev, struct virtio_video_config, max_caps_length,
159 if (!vvd->max_caps_len) {
160 v4l2_err(&vvd->v4l2_dev, "max_caps_len is zero\n");
165 virtio_cread(vdev, struct virtio_video_config, max_resp_length,
167 if (!vvd->max_resp_len) {
168 v4l2_err(&vvd->v4l2_dev, "max_resp_len is zero\n");
173 ret = virtio_video_alloc_events(vvd);
177 virtio_device_ready(vdev);
178 vvd->commandq.ready = true;
179 vvd->eventq.ready = true;
181 ret = virtio_video_device_init(vvd);
183 v4l2_err(&vvd->v4l2_dev,
184 "failed to init virtio video\n");
193 virtio_video_free_vbufs(vvd);
195 vdev->config->del_vqs(vdev);
197 v4l2_device_unregister(&vvd->v4l2_dev);
199 devm_kfree(&vdev->dev, vvd);
204 static void virtio_video_remove(struct virtio_device *vdev)
206 struct virtio_video_device *vvd = vdev->priv;
208 virtio_video_device_deinit(vvd);
209 virtio_video_free_vbufs(vvd);
210 vdev->config->del_vqs(vdev);
211 v4l2_device_unregister(&vvd->v4l2_dev);
212 devm_kfree(&vdev->dev, vvd);
215 static struct virtio_device_id id_table[] = {
216 { VIRTIO_ID_VIDEO_DEC, VIRTIO_DEV_ANY_ID },
217 { VIRTIO_ID_VIDEO_ENC, VIRTIO_DEV_ANY_ID },
218 { VIRTIO_ID_VIDEO_CAM, VIRTIO_DEV_ANY_ID },
222 static unsigned int features[] = {
223 VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES,
224 VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG,
227 static struct virtio_driver virtio_video_driver = {
228 .feature_table = features,
229 .feature_table_size = ARRAY_SIZE(features),
230 .driver.name = DRIVER_NAME,
231 .driver.owner = THIS_MODULE,
232 .id_table = id_table,
233 .probe = virtio_video_probe,
234 .remove = virtio_video_remove,
237 module_virtio_driver(virtio_video_driver);
239 MODULE_DEVICE_TABLE(virtio, id_table);
240 MODULE_DESCRIPTION("virtio video driver");
241 MODULE_AUTHOR("Dmitry Sepp <dmitry.sepp@opensynergy.com>");
242 MODULE_AUTHOR("Kiran Pawar <kiran.pawar@opensynergy.com>");
243 MODULE_AUTHOR("Nikolay Martyanov <nikolay.martyanov@opensynergy.com>");
244 MODULE_AUTHOR("Samiullah Khawaja <samiullah.khawaja@opensynergy.com>");
245 MODULE_VERSION(DRIVER_VERSION);
246 MODULE_LICENSE("GPL");