summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/imagination/pvr_context.h
blob: 0c7b97dfa6bafdf303b75bb8c6d7a483018703cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* 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;
};

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_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 */