diff options
author | Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com> | 2023-01-17 12:27:18 +0300 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2023-01-19 13:07:22 +0300 |
commit | 263b2ba5fc93c875129e0d2b4034d7d8a34b3d39 (patch) | |
tree | 2526b4b3c8f76eba922575cf4087a31e93d8bdbd /drivers/accel/ivpu/ivpu_drv.c | |
parent | 35b137630f08d913fc2e33df33ccc2570dff3f7d (diff) | |
download | linux-263b2ba5fc93c875129e0d2b4034d7d8a34b3d39.tar.xz |
accel/ivpu: Add Intel VPU MMU support
VPU Memory Management Unit is based on ARM MMU-600.
It allows the creation of multiple virtual address spaces for
the device and map noncontinuous host memory (there is no dedicated
memory on the VPU).
Address space is implemented as a struct ivpu_mmu_context, it has an ID,
drm_mm allocator for VPU addresses and struct ivpu_mmu_pgtable that
holds actual 3-level, 4KB page table.
Context with ID 0 (global context) is created upon driver initialization
and it's mainly used for mapping memory required to execute
the firmware.
Contexts with non-zero IDs are user contexts allocated each time
the devices is open()-ed and they map command buffers and other
workload-related memory.
Workloads executing in a given contexts have access only
to the memory mapped in this context.
This patch is has two main files:
- ivpu_mmu_context.c handles MMU page tables and memory mapping
- ivpu_mmu.c implements a driver that programs the MMU device
Co-developed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
Signed-off-by: Karol Wachowski <karol.wachowski@linux.intel.com>
Co-developed-by: Krystian Pradzynski <krystian.pradzynski@linux.intel.com>
Signed-off-by: Krystian Pradzynski <krystian.pradzynski@linux.intel.com>
Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Reviewed-by: Oded Gabbay <ogabbay@kernel.org>
Reviewed-by: Jeffrey Hugo <quic_jhugo@quicinc.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20230117092723.60441-3-jacek.lawrynowicz@linux.intel.com
Diffstat (limited to 'drivers/accel/ivpu/ivpu_drv.c')
-rw-r--r-- | drivers/accel/ivpu/ivpu_drv.c | 83 |
1 files changed, 81 insertions, 2 deletions
diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 1134fb0028ad..69ca7dae8b57 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -15,6 +15,8 @@ #include "ivpu_drv.h" #include "ivpu_hw.h" +#include "ivpu_mmu.h" +#include "ivpu_mmu_context.h" #ifndef DRIVER_VERSION_STR #define DRIVER_VERSION_STR __stringify(DRM_IVPU_DRIVER_MAJOR) "." \ @@ -37,23 +39,38 @@ MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set VPU frequency"); struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv) { + struct ivpu_device *vdev = file_priv->vdev; + kref_get(&file_priv->ref); + + ivpu_dbg(vdev, KREF, "file_priv get: ctx %u refcount %u\n", + file_priv->ctx.id, kref_read(&file_priv->ref)); + return file_priv; } static void file_priv_release(struct kref *ref) { struct ivpu_file_priv *file_priv = container_of(ref, struct ivpu_file_priv, ref); + struct ivpu_device *vdev = file_priv->vdev; + ivpu_dbg(vdev, FILE, "file_priv release: ctx %u\n", file_priv->ctx.id); + + ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); + WARN_ON(xa_erase_irq(&vdev->context_xa, file_priv->ctx.id) != file_priv); kfree(file_priv); } void ivpu_file_priv_put(struct ivpu_file_priv **link) { struct ivpu_file_priv *file_priv = *link; + struct ivpu_device *vdev = file_priv->vdev; WARN_ON(!file_priv); + ivpu_dbg(vdev, KREF, "file_priv put: ctx %u refcount %u\n", + file_priv->ctx.id, kref_read(&file_priv->ref)); + *link = NULL; kref_put(&file_priv->ref, file_priv_release); } @@ -88,6 +105,9 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f case DRM_IVPU_PARAM_CONTEXT_PRIORITY: args->value = file_priv->priority; break; + case DRM_IVPU_PARAM_CONTEXT_ID: + args->value = file_priv->ctx.id; + break; default: ret = -EINVAL; break; @@ -120,22 +140,59 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) { struct ivpu_device *vdev = to_ivpu_device(dev); struct ivpu_file_priv *file_priv; + u32 ctx_id; + void *old; + int ret; + + ret = xa_alloc_irq(&vdev->context_xa, &ctx_id, NULL, vdev->context_xa_limit, GFP_KERNEL); + if (ret) { + ivpu_err(vdev, "Failed to allocate context id: %d\n", ret); + return ret; + } file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); - if (!file_priv) - return -ENOMEM; + if (!file_priv) { + ret = -ENOMEM; + goto err_xa_erase; + } file_priv->vdev = vdev; file_priv->priority = DRM_IVPU_CONTEXT_PRIORITY_NORMAL; kref_init(&file_priv->ref); + ret = ivpu_mmu_user_context_init(vdev, &file_priv->ctx, ctx_id); + if (ret) + goto err_free_file_priv; + + old = xa_store_irq(&vdev->context_xa, ctx_id, file_priv, GFP_KERNEL); + if (xa_is_err(old)) { + ret = xa_err(old); + ivpu_err(vdev, "Failed to store context %u: %d\n", ctx_id, ret); + goto err_ctx_fini; + } + + ivpu_dbg(vdev, FILE, "file_priv create: ctx %u process %s pid %d\n", + ctx_id, current->comm, task_pid_nr(current)); + file->driver_priv = file_priv; return 0; + +err_ctx_fini: + ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); +err_free_file_priv: + kfree(file_priv); +err_xa_erase: + xa_erase_irq(&vdev->context_xa, ctx_id); + return ret; } static void ivpu_postclose(struct drm_device *dev, struct drm_file *file) { struct ivpu_file_priv *file_priv = file->driver_priv; + struct ivpu_device *vdev = to_ivpu_device(dev); + + ivpu_dbg(vdev, FILE, "file_priv close: ctx %u process %s pid %d\n", + file_priv->ctx.id, current->comm, task_pid_nr(current)); ivpu_file_priv_put(&file_priv); } @@ -150,6 +207,7 @@ int ivpu_shutdown(struct ivpu_device *vdev) int ret; ivpu_hw_irq_disable(vdev); + ivpu_mmu_disable(vdev); ret = ivpu_hw_power_down(vdev); if (ret) @@ -251,6 +309,10 @@ static int ivpu_dev_init(struct ivpu_device *vdev) if (!vdev->hw) return -ENOMEM; + vdev->mmu = drmm_kzalloc(&vdev->drm, sizeof(*vdev->mmu), GFP_KERNEL); + if (!vdev->mmu) + return -ENOMEM; + vdev->hw->ops = &ivpu_hw_mtl_ops; vdev->platform = IVPU_PLATFORM_INVALID; vdev->context_xa_limit.min = IVPU_GLOBAL_CONTEXT_MMU_SSID + 1; @@ -283,8 +345,24 @@ static int ivpu_dev_init(struct ivpu_device *vdev) goto err_xa_destroy; } + ret = ivpu_mmu_global_context_init(vdev); + if (ret) { + ivpu_err(vdev, "Failed to initialize global MMU context: %d\n", ret); + goto err_power_down; + } + + ret = ivpu_mmu_init(vdev); + if (ret) { + ivpu_err(vdev, "Failed to initialize MMU device: %d\n", ret); + goto err_mmu_gctx_fini; + } + return 0; +err_mmu_gctx_fini: + ivpu_mmu_global_context_fini(vdev); +err_power_down: + ivpu_hw_power_down(vdev); err_xa_destroy: xa_destroy(&vdev->context_xa); return ret; @@ -293,6 +371,7 @@ err_xa_destroy: static void ivpu_dev_fini(struct ivpu_device *vdev) { ivpu_shutdown(vdev); + ivpu_mmu_global_context_fini(vdev); drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->context_xa)); xa_destroy(&vdev->context_xa); |