Re: [PATCH v2 19/23] drm/msm/dcp: Add Qualcomm VirtIO transport wrapper

From: Dmitry Baryshkov

Date: Mon Oct 26 2026 - 06:13:14 EST


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 <jayant.shekhar@xxxxxxxxxxxxxxxx>
> ---
> 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.

> 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().

--
With best wishes
Dmitry