| /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ |
| /* Copyright (c) 2023 Imagination Technologies Ltd. */ |
| |
| #ifndef PVR_CONTEXT_H |
| #define PVR_CONTEXT_H |
| |
| #include <drm/gpu_scheduler.h> |
| |
| #include <linux/compiler_attributes.h> |
| #include <linux/dma-fence.h> |
| #include <linux/kref.h> |
| #include <linux/types.h> |
| #include <linux/xarray.h> |
| #include <uapi/drm/pvr_drm.h> |
| |
| #include "pvr_cccb.h" |
| #include "pvr_device.h" |
| #include "pvr_queue.h" |
| |
| /* Forward declaration from pvr_gem.h. */ |
| struct pvr_fw_object; |
| |
| enum pvr_context_priority { |
| PVR_CTX_PRIORITY_LOW = 0, |
| PVR_CTX_PRIORITY_MEDIUM, |
| PVR_CTX_PRIORITY_HIGH, |
| }; |
| |
| /** |
| * struct pvr_context - Context data |
| */ |
| struct pvr_context { |
| /** @ref_count: Refcount for context. */ |
| struct kref ref_count; |
| |
| /** @pvr_dev: Pointer to owning device. */ |
| struct pvr_device *pvr_dev; |
| |
| /** @vm_ctx: Pointer to associated VM context. */ |
| struct pvr_vm_context *vm_ctx; |
| |
| /** @type: Type of context. */ |
| enum drm_pvr_ctx_type type; |
| |
| /** @flags: Context flags. */ |
| u32 flags; |
| |
| /** @priority: Context priority*/ |
| enum pvr_context_priority priority; |
| |
| /** @fw_obj: FW object representing FW-side context data. */ |
| struct pvr_fw_object *fw_obj; |
| |
| /** @data: Pointer to local copy of FW context data. */ |
| void *data; |
| |
| /** @data_size: Size of FW context data, in bytes. */ |
| u32 data_size; |
| |
| /** @ctx_id: FW context ID. */ |
| u32 ctx_id; |
| |
| /** |
| * @faulty: Set to 1 when the context queues had unfinished job when |
| * a GPU reset happened. |
| * |
| * In that case, the context is in an inconsistent state and can't be |
| * used anymore. |
| */ |
| atomic_t faulty; |
| |
| /** @queues: Union containing all kind of queues. */ |
| union { |
| struct { |
| /** @geometry: Geometry queue. */ |
| struct pvr_queue *geometry; |
| |
| /** @fragment: Fragment queue. */ |
| struct pvr_queue *fragment; |
| }; |
| |
| /** @compute: Compute queue. */ |
| struct pvr_queue *compute; |
| |
| /** @compute: Transfer queue. */ |
| struct pvr_queue *transfer; |
| } queues; |
| |
| /** @file_link: pvr_file PVR context list link. */ |
| struct list_head file_link; |
| }; |
| |
| static __always_inline struct pvr_queue * |
| pvr_context_get_queue_for_job(struct pvr_context *ctx, enum drm_pvr_job_type type) |
| { |
| switch (type) { |
| case DRM_PVR_JOB_TYPE_GEOMETRY: |
| return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.geometry : NULL; |
| case DRM_PVR_JOB_TYPE_FRAGMENT: |
| return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.fragment : NULL; |
| case DRM_PVR_JOB_TYPE_COMPUTE: |
| return ctx->type == DRM_PVR_CTX_TYPE_COMPUTE ? ctx->queues.compute : NULL; |
| case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: |
| return ctx->type == DRM_PVR_CTX_TYPE_TRANSFER_FRAG ? ctx->queues.transfer : NULL; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * pvr_context_get() - Take additional reference on context. |
| * @ctx: Context pointer. |
| * |
| * Call pvr_context_put() to release. |
| * |
| * Returns: |
| * * The requested context on success, or |
| * * %NULL if no context pointer passed. |
| */ |
| static __always_inline struct pvr_context * |
| pvr_context_get(struct pvr_context *ctx) |
| { |
| if (ctx) |
| kref_get(&ctx->ref_count); |
| |
| return ctx; |
| } |
| |
| /** |
| * pvr_context_get_if_referenced() - Take an additional reference on a still |
| * referenced context. |
| * @ctx: Context pointer. |
| * |
| * Call pvr_context_put() to release. |
| * |
| * Returns: |
| * * True on success, or |
| * * false if no context pointer passed, or the context wasn't still |
| * * referenced. |
| */ |
| static __always_inline bool |
| pvr_context_get_if_referenced(struct pvr_context *ctx) |
| { |
| return ctx != NULL && kref_get_unless_zero(&ctx->ref_count) != 0; |
| } |
| |
| /** |
| * pvr_context_lookup() - Lookup context pointer from handle and file. |
| * @pvr_file: Pointer to pvr_file structure. |
| * @handle: Context handle. |
| * |
| * Takes reference on context. Call pvr_context_put() to release. |
| * |
| * Return: |
| * * The requested context on success, or |
| * * %NULL on failure (context does not exist, or does not belong to @pvr_file). |
| */ |
| static __always_inline struct pvr_context * |
| pvr_context_lookup(struct pvr_file *pvr_file, u32 handle) |
| { |
| struct pvr_context *ctx; |
| |
| /* Take the array lock to protect against context removal. */ |
| xa_lock(&pvr_file->ctx_handles); |
| ctx = pvr_context_get(xa_load(&pvr_file->ctx_handles, handle)); |
| xa_unlock(&pvr_file->ctx_handles); |
| |
| return ctx; |
| } |
| |
| /** |
| * pvr_context_lookup_id() - Lookup context pointer from ID. |
| * @pvr_dev: Device pointer. |
| * @id: FW context ID. |
| * |
| * Takes reference on context. Call pvr_context_put() to release. |
| * |
| * Return: |
| * * The requested context on success, or |
| * * %NULL on failure (context does not exist). |
| */ |
| static __always_inline struct pvr_context * |
| pvr_context_lookup_id(struct pvr_device *pvr_dev, u32 id) |
| { |
| struct pvr_context *ctx; |
| |
| /* Take the array lock to protect against context removal. */ |
| xa_lock(&pvr_dev->ctx_ids); |
| |
| /* Contexts are removed from the ctx_ids set in the context release path, |
| * meaning the ref_count reached zero before they get removed. We need |
| * to make sure we're not trying to acquire a context that's being |
| * destroyed. |
| */ |
| ctx = xa_load(&pvr_dev->ctx_ids, id); |
| if (!kref_get_unless_zero(&ctx->ref_count)) |
| ctx = NULL; |
| |
| xa_unlock(&pvr_dev->ctx_ids); |
| |
| return ctx; |
| } |
| |
| static __always_inline u32 |
| pvr_context_get_fw_addr(struct pvr_context *ctx) |
| { |
| u32 ctx_fw_addr = 0; |
| |
| pvr_fw_object_get_fw_addr(ctx->fw_obj, &ctx_fw_addr); |
| |
| return ctx_fw_addr; |
| } |
| |
| void pvr_context_put(struct pvr_context *ctx); |
| |
| int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_context_args *args); |
| |
| int pvr_context_destroy(struct pvr_file *pvr_file, u32 handle); |
| |
| void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file); |
| |
| void pvr_context_device_init(struct pvr_device *pvr_dev); |
| |
| void pvr_context_device_fini(struct pvr_device *pvr_dev); |
| |
| #endif /* PVR_CONTEXT_H */ |