video: rockchip: mpp: fix share iommu issue for rk3528 on Arch32

Issue:
1. On arm32 platform iommu use the mapping->domain, while use the
   group->domain on arm64 platform.
2. __iommu_attach_group use group->default to judge whether new device
   in kernel 5.10 branch, while use group->domain in kernel 6.1 branch.
   For this change, when two devices share the iommu, the first device
   attach success, while the second device failed in kernel 6.1 branch.

Method:
1. Set driver_managed_dma=true to ensure that during the IOMMU probe
phase,
   even if attach group failed, the mapping will not be released.
2. After the IOMMU probe succeeds, record info->domain=mapping->domain
and
   detach it, ensuring that group->domain is set to NULL. This prevents
the
   next device which sharing the IOMMU, not return -EBUSY when attach
the
   group.
3. Before hardware running, the shared IOMMU should check whether the
   device has been switched. If so, it needs to attach the current
   device's info->domain.
4. When removing a shared IOMMU, it must also ensure that group->domain
   corresponds to the current device.

Change-Id: If0fec01a0bcf9c49850129bfa5ac28484fece9a2
Signed-off-by: Ding Wei <leo.ding@rock-chips.com>
This commit is contained in:
Ding Wei
2025-02-10 11:18:30 +08:00
parent f346f25228
commit a6943befb9
5 changed files with 42 additions and 0 deletions

View File

@@ -2239,7 +2239,10 @@ int mpp_dev_probe(struct mpp_dev *mpp,
if (IS_ERR(mpp->iommu_info)) {
dev_err(dev, "failed to attach iommu\n");
mpp->iommu_info = NULL;
} else {
mpp->iommu_info->queue = mpp->queue;
}
if (mpp->hw_ops->init) {
ret = mpp->hw_ops->init(mpp);
if (ret)

View File

@@ -507,6 +507,9 @@ struct mpp_taskqueue {
u32 core_id_max;
u32 core_count;
unsigned long dev_active_flags;
/* for devices which share iommu, record last attach device */
struct mpp_iommu_info *last_iommu_info;
};
struct mpp_reset_group {
@@ -547,6 +550,9 @@ struct mpp_service {
/* global timing record flag */
u32 timing_en;
u32 load_interval;
/* bit mask for iommu shared */
u32 iommu_shared_mask;
};
/*

View File

@@ -17,6 +17,7 @@
#include <linux/kref.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#ifdef CONFIG_ARM_DMA_USE_IOMMU
#include <asm/dma-iommu.h>
@@ -451,9 +452,19 @@ int mpp_iommu_detach(struct mpp_iommu_info *info)
int mpp_iommu_attach(struct mpp_iommu_info *info)
{
struct mpp_iommu_info *last_info;
if (!info)
return 0;
/* if device changed, detach last first */
last_info = info->queue->last_iommu_info;
if (info->shared && last_info && last_info->shared
&& (info->dev != last_info->dev)) {
iommu_detach_group(last_info->domain, last_info->group);
}
info->queue->last_iommu_info = info;
if (info->domain == iommu_get_domain_for_dev(info->dev))
return 0;
@@ -561,6 +572,15 @@ mpp_iommu_probe(struct device *dev)
info->irq = platform_get_irq(pdev, 0);
info->got_irq = (info->irq < 0) ? false : true;
/* get shared flag, if true detach */
if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
struct platform_driver *drv = to_platform_driver(dev->driver);
info->shared = drv->driver_managed_dma;
if (info->shared)
iommu_detach_group(info->domain, info->group);
}
return info;
err_put_group:
@@ -578,6 +598,10 @@ int mpp_iommu_remove(struct mpp_iommu_info *info)
if (!info)
return 0;
/* if iommu shared, ensure current device's domain, then remove correctly */
if (info->shared)
mpp_iommu_attach(info);
iommu_group_put(info->group);
platform_device_put(info->pdev);

View File

@@ -102,6 +102,9 @@ struct mpp_iommu_info {
int irq;
int got_irq;
/* flag for mark iommu whether shared */
bool shared;
struct mpp_taskqueue *queue;
};
struct mpp_dma_session *

View File

@@ -100,6 +100,9 @@ static int mpp_add_driver(struct mpp_service *srv,
&srv->grf_infos[type],
grf_name);
if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU))
driver->driver_managed_dma = (srv->iommu_shared_mask & BIT(type)) ? true : false;
ret = platform_driver_register(driver);
if (ret)
return ret;
@@ -444,6 +447,9 @@ static int mpp_service_probe(struct platform_device *pdev)
}
}
of_property_read_u32(np, "rockchip,iommu-shared-mask",
&srv->iommu_shared_mask);
ret = mpp_register_service(srv, MPP_SERVICE_NAME);
if (ret) {
dev_err(dev, "register %s device\n", MPP_SERVICE_NAME);