On 26-10-2026 15:37, Dmitry Baryshkov wrote:
On Mon, Oct 26, 2026 at 11:51:29AM +0530, Jayant Shekhar wrote:
Add the Qualcomm-specific VirtIO transport required by DCP firmware.

The transport sends an IPCC power notification before accessing the
VirtIO MMIO registers, then registers a VirtIO device directly.

Implement MMIO configuration access, feature negotiation, virtqueue
setup, interrupt handling, reset, DMA setup, and suspend/resume
sequencing in the transport. Keep power-management ordering in the
transport so VirtIO queues are quiesced before DCP power changes.

Assisted-by: LLM
Signed-off-by: Jayant Shekhar <[email protected]>
---
  drivers/gpu/drm/msm/Makefile                   |   1 +
  drivers/gpu/drm/msm/dcp/dcp_virtio.h           |  23 +
  drivers/gpu/drm/msm/dcp/dcp_virtio_transport.c | 643 +++++++++++++++++++++++++
This is a separate VirtIO host driver. Please move it to drivers/virtio
like the rest of the VirtIO hosts. It has nothing to do with the drm
subsystem now.


Will be moving this into drivers/virtio in v3.


  3 files changed, 667 insertions(+)

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 9f0a9bfeb2a7..e1a995a8c980 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -85,6 +85,7 @@ msm-display-$(CONFIG_DRM_MSM_DCP) += \
        dcp/dcp_hfi_plane.o \
        dcp/dcp_ipcc.o \
        dcp/dcp_encoder.o \
+       dcp/dcp_virtio_transport.o \
        dcp/hfi/hfi_packer.o \
        dcp/hfi/hfi_unpacker.o \
        dcp/hfi/hfi_utils.o
diff --git a/drivers/gpu/drm/msm/dcp/dcp_virtio.h 
b/drivers/gpu/drm/msm/dcp/dcp_virtio.h
new file mode 100644
index 000000000000..a9c3b9aff61d
--- /dev/null
+++ b/drivers/gpu/drm/msm/dcp/dcp_virtio.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __DCP_VIRTIO_H__
+#define __DCP_VIRTIO_H__
+
+#include <linux/device.h>
+#include <linux/virtio.h>
+
+struct dcp_drv;
+struct dcp_ipcc;
+
+struct dcp_ipcc *dcp_virtio_get_ipcc(struct virtio_device *vdev);
What is the use for it? The DCP driver should not communicate to the
IPCC, it's a part of the underlying VirtIO mechanism. If we export DCP
to the VM there will be no IPCC anymore.

+
+int dcp_virtio_transport_register(void);
+void dcp_virtio_transport_unregister(void);
+
+int dcp_virtio_register(void);
+void dcp_virtio_unregister(void);
Unused, remove these two.

+
+#endif /* __DCP_VIRTIO_H__ */
diff --git a/drivers/gpu/drm/msm/dcp/dcp_virtio_transport.c 
b/drivers/gpu/drm/msm/dcp/dcp_virtio_transport.c
new file mode 100644
index 000000000000..02c0e2a9fafe
--- /dev/null
+++ b/drivers/gpu/drm/msm/dcp/dcp_virtio_transport.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Qualcomm DCP VirtIO transport.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <uapi/linux/virtio_mmio.h>
+#include <linux/virtio_ring.h>
+
+#include "dcp_ipcc.h"
+#include "dcp_virtio.h"
+
+#define VIRTIO_MMIO_VRING_ALIGN                PAGE_SIZE
+
+#define to_dcp_virtio_transport(_plat_dev) \
+       container_of(_plat_dev, struct dcp_virtio_transport, vdev)
+
+struct dcp_virtio_transport {
+       struct virtio_device vdev;
+       struct platform_device *pdev;
+
+       void __iomem *base;
+       unsigned long version;
+
+       bool wake_irq_enabled;
+       struct dcp_ipcc ipcc;
+};
+
+static u64 dcp_virtio_get_features(struct virtio_device *vdev)
+{
+       struct dcp_virtio_transport *transport = to_dcp_virtio_transport(vdev);
+       u64 features;
+
+       writel(1, transport->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
+       features = readl(transport->base + VIRTIO_MMIO_DEVICE_FEATURES);
+       features <<= 32;
+
+       writel(0, transport->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
+       features |= readl(transport->base + VIRTIO_MMIO_DEVICE_FEATURES);
Can we turn virtio-mmio into the library code instead of copy-pasting
everything here?

+
+       return features;
+}
+
[...]

+
+       magic = readl(transport->base + VIRTIO_MMIO_MAGIC_VALUE);
+       if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
Is it really a correct magic here? should it be a different magic as you
are not just a virtio-mmio?

+               dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic);
+               ret = -ENODEV;
+               goto err_node;
+       }
+
+       transport->version = readl(transport->base + VIRTIO_MMIO_VERSION);
+       if (transport->version < 1 || transport->version > 3) {
Isn't transport version fixed for the DCP purposes? Why do we need to
determine it again at the runtime?

+               dev_err(&pdev->dev, "Version %ld not supported!\n",
+                       transport->version);
+               ret = -ENXIO;
+               goto err_node;
+       }
+
+       transport->vdev.id.device =
+               readl(transport->base + VIRTIO_MMIO_DEVICE_ID);
+       if (!transport->vdev.id.device) {
+               ret = -ENODEV;
+               goto err_node;
+       }
+       transport->vdev.id.vendor =
+               readl(transport->base + VIRTIO_MMIO_VENDOR_ID);
+
+       if (transport->version == 1) {
+               writel(PAGE_SIZE,
+                      transport->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
+               ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+               if (!ret)
+                       ret = dma_set_coherent_mask(&pdev->dev,
+                                                   DMA_BIT_MASK(32 + 
PAGE_SHIFT));
+       } else {
+               ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+       }
+       if (ret)
+               ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+       if (ret)
+               dev_warn(&pdev->dev,
+                        "Failed to enable 64-bit or 32-bit DMA. Trying to continue, 
but this might not work.\n");
+
+       ret = register_virtio_device(&transport->vdev);
+       if (ret) {
+               put_device(&transport->vdev.dev);
+               dcp_ipcc_deinit(&transport->ipcc);
+               platform_set_drvdata(pdev, NULL);
+               return ret;
+       }
+
+       return 0;
+
+err_node:
+err_ipcc:
+       dcp_ipcc_deinit(&transport->ipcc);
+err_free:
+       platform_set_drvdata(pdev, NULL);
+       return ret;
+}
+
+static void dcp_virtio_transport_remove(struct platform_device *pdev)
+{
+       struct dcp_virtio_transport *transport = platform_get_drvdata(pdev);
+
+       unregister_virtio_device(&transport->vdev);
+       platform_set_drvdata(pdev, NULL);
+       dcp_ipcc_deinit(&transport->ipcc);
+}
+
+static int dcp_virtio_transport_suspend(struct device *dev)
+{
+       struct dcp_virtio_transport *transport = dev_get_drvdata(dev);
+       int ret;
+
+       ret = virtio_device_freeze(&transport->vdev);
+       if (ret)
+               return ret;
+
+       ret = dcp_ipcc_enable_irq_wake(&transport->ipcc);
+       if (ret)
+               goto err_restore;
+
+       ret = dcp_ipcc_send_power_notify(&transport->ipcc);
+       if (ret) {
+               dcp_ipcc_disable_irq_wake(&transport->ipcc);
+               goto err_restore;
+       }
+
+       return 0;
+
+err_restore:
+       virtio_device_restore(&transport->vdev);
+       return ret;
+}
+
+static int dcp_virtio_transport_resume(struct device *dev)
+{
+       struct dcp_virtio_transport *transport = dev_get_drvdata(dev);
+       int ret;
+
+       ret = dcp_ipcc_send_power_notify(&transport->ipcc);
+       if (ret)
+               return ret;
+
+       dcp_ipcc_disable_irq_wake(&transport->ipcc);
+       if (transport->version == 1)
+               writel(PAGE_SIZE,
+                      transport->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
+
+       return virtio_device_restore(&transport->vdev);
+}
+
+static const struct dev_pm_ops dcp_virtio_transport_pm_ops = {
+       .suspend = dcp_virtio_transport_suspend,
+       .resume = dcp_virtio_transport_resume,
+};
+
+static const struct of_device_id dcp_virtio_transport_of_match[] = {
+       { .compatible = "qcom,dcp-virtio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, dcp_virtio_transport_of_match);
+
+static struct platform_driver dcp_virtio_transport_driver = {
+       .probe = dcp_virtio_transport_probe,
+       .remove = dcp_virtio_transport_remove,
+       .driver = {
+               .name = "qcom-dcp-virtio",
+               .of_match_table = dcp_virtio_transport_of_match,
+               .pm = &dcp_virtio_transport_pm_ops,
+       },
+};
+
+int dcp_virtio_transport_register(void)
+{
+       return platform_driver_register(&dcp_virtio_transport_driver);
+}
+
+void dcp_virtio_transport_unregister(void)
+{
+       platform_driver_unregister(&dcp_virtio_transport_driver);
+}
Turn it into a separate driver, use module_platform_driver().


Reply via email to