/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ /* Copyright (c) 2023 Imagination Technologies Ltd. */ #ifndef PVR_CONTEXT_H #define PVR_CONTEXT_H #include #include #include #include #include #include #include #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 */