summaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_driver.c')
-rw-r--r--meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_driver.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_driver.c b/meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_driver.c
new file mode 100644
index 00000000..734c5b73
--- /dev/null
+++ b/meta-egvirt/recipes-kernel/kernel-module-virtio-video/files/virtio_video_driver.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Driver for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)
+#include <linux/dma-map-ops.h>
+#else
+#include <linux/dma-mapping.h>
+#endif
+
+#include "virtio_video.h"
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+
+static unsigned int use_dma_mem;
+module_param(use_dma_mem, uint, 0644);
+MODULE_PARM_DESC(use_dma_mem, "Try to allocate buffers from the DMA zone");
+
+static int vid_nr_dec = -1;
+module_param(vid_nr_dec, int, 0644);
+MODULE_PARM_DESC(vid_nr_dec, "videoN start number, -1 is autodetect");
+
+static int vid_nr_enc = -1;
+module_param(vid_nr_enc, int, 0644);
+MODULE_PARM_DESC(vid_nr_enc, "videoN start number, -1 is autodetect");
+
+static int vid_nr_cam = -1;
+module_param(vid_nr_cam, int, 0644);
+MODULE_PARM_DESC(vid_nr_cam, "videoN start number, -1 is autodetect");
+
+static bool mplane_cam = true;
+module_param(mplane_cam, bool, 0644);
+MODULE_PARM_DESC(mplane_cam,
+ "1 (default) - multiplanar camera, 0 - single planar camera");
+
+static int virtio_video_probe(struct virtio_device *vdev)
+{
+ int ret;
+ struct virtio_video_device *vvd;
+ struct virtqueue *vqs[2];
+ struct device *dev = &vdev->dev;
+ struct device *pdev = dev->parent;
+
+ static const char * const names[] = { "commandq", "eventq" };
+ static vq_callback_t *callbacks[] = {
+ virtio_video_cmd_cb,
+ virtio_video_event_cb
+ };
+
+ if (!virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES)) {
+ dev_err(dev, "device must support guest allocated buffers\n");
+ return -ENODEV;
+ }
+
+ vvd = devm_kzalloc(dev, sizeof(*vvd), GFP_KERNEL);
+ if (!vvd)
+ return -ENOMEM;
+
+ vvd->is_m2m_dev = true;
+
+ switch (vdev->id.device) {
+ case VIRTIO_ID_VIDEO_CAM:
+ vvd->is_m2m_dev = false;
+ vvd->vid_dev_nr = vid_nr_cam;
+ vvd->is_mplane_cam = mplane_cam;
+ vvd->type = VIRTIO_VIDEO_DEVICE_CAMERA;
+ break;
+ case VIRTIO_ID_VIDEO_ENC:
+ vvd->vid_dev_nr = vid_nr_enc;
+ vvd->type = VIRTIO_VIDEO_DEVICE_ENCODER;
+ break;
+ case VIRTIO_ID_VIDEO_DEC:
+ default:
+ vvd->vid_dev_nr = vid_nr_dec;
+ vvd->type = VIRTIO_VIDEO_DEVICE_DECODER;
+ break;
+ }
+
+ vvd->vdev = vdev;
+ vvd->debug = debug;
+ vvd->use_dma_mem = use_dma_mem;
+ vdev->priv = vvd;
+
+ spin_lock_init(&vvd->pending_buf_list_lock);
+ spin_lock_init(&vvd->resource_idr_lock);
+ idr_init(&vvd->resource_idr);
+ spin_lock_init(&vvd->stream_idr_lock);
+ idr_init(&vvd->stream_idr);
+
+ init_waitqueue_head(&vvd->wq);
+
+ if (virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG))
+ vvd->supp_non_contig = true;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
+ vvd->has_iommu = !virtio_has_dma_quirk(vdev);
+#else
+ vvd->has_iommu = !virtio_has_iommu_quirk(vdev);
+#endif
+
+ if (!dev->dma_ops)
+ set_dma_ops(dev, pdev->dma_ops);
+
+ /*
+ * Set it to coherent_dma_mask by default if the architecture
+ * code has not set it.
+ */
+ if (!dev->dma_mask)
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+ dma_set_mask(dev, *pdev->dma_mask);
+
+ dev_set_name(dev, "%s.%i", DRIVER_NAME, vdev->index);
+ ret = v4l2_device_register(dev, &vvd->v4l2_dev);
+ if (ret)
+ goto err_v4l2_reg;
+
+ spin_lock_init(&vvd->commandq.qlock);
+ init_waitqueue_head(&vvd->commandq.reclaim_queue);
+
+ INIT_WORK(&vvd->eventq.work, virtio_video_process_events);
+
+ INIT_LIST_HEAD(&vvd->pending_vbuf_list);
+
+ ret = virtio_find_vqs(vdev, 2, vqs, callbacks, names, NULL);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to find virt queues\n");
+ goto err_vqs;
+ }
+
+ vvd->commandq.vq = vqs[0];
+ vvd->eventq.vq = vqs[1];
+
+ ret = virtio_video_alloc_vbufs(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to alloc vbufs\n");
+ goto err_vbufs;
+ }
+
+ virtio_cread(vdev, struct virtio_video_config, max_caps_length,
+ &vvd->max_caps_len);
+ if (!vvd->max_caps_len) {
+ v4l2_err(&vvd->v4l2_dev, "max_caps_len is zero\n");
+ ret = -EINVAL;
+ goto err_config;
+ }
+
+ virtio_cread(vdev, struct virtio_video_config, max_resp_length,
+ &vvd->max_resp_len);
+ if (!vvd->max_resp_len) {
+ v4l2_err(&vvd->v4l2_dev, "max_resp_len is zero\n");
+ ret = -EINVAL;
+ goto err_config;
+ }
+
+ ret = virtio_video_alloc_events(vvd);
+ if (ret)
+ goto err_events;
+
+ virtio_device_ready(vdev);
+ vvd->commandq.ready = true;
+ vvd->eventq.ready = true;
+
+ ret = virtio_video_device_init(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to init virtio video\n");
+ goto err_init;
+ }
+
+ return 0;
+
+err_init:
+err_events:
+err_config:
+ virtio_video_free_vbufs(vvd);
+err_vbufs:
+ vdev->config->del_vqs(vdev);
+err_vqs:
+ v4l2_device_unregister(&vvd->v4l2_dev);
+err_v4l2_reg:
+ devm_kfree(&vdev->dev, vvd);
+
+ return ret;
+}
+
+static void virtio_video_remove(struct virtio_device *vdev)
+{
+ struct virtio_video_device *vvd = vdev->priv;
+
+ virtio_video_device_deinit(vvd);
+ virtio_video_free_vbufs(vvd);
+ vdev->config->del_vqs(vdev);
+ v4l2_device_unregister(&vvd->v4l2_dev);
+ devm_kfree(&vdev->dev, vvd);
+}
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_VIDEO_DEC, VIRTIO_DEV_ANY_ID },
+ { VIRTIO_ID_VIDEO_ENC, VIRTIO_DEV_ANY_ID },
+ { VIRTIO_ID_VIDEO_CAM, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static unsigned int features[] = {
+ VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES,
+ VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG,
+};
+
+static struct virtio_driver virtio_video_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .driver.name = DRIVER_NAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = virtio_video_probe,
+ .remove = virtio_video_remove,
+};
+
+module_virtio_driver(virtio_video_driver);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("virtio video driver");
+MODULE_AUTHOR("Dmitry Sepp <dmitry.sepp@opensynergy.com>");
+MODULE_AUTHOR("Kiran Pawar <kiran.pawar@opensynergy.com>");
+MODULE_AUTHOR("Nikolay Martyanov <nikolay.martyanov@opensynergy.com>");
+MODULE_AUTHOR("Samiullah Khawaja <samiullah.khawaja@opensynergy.com>");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");