1eaf01ee5SSarah Walker // SPDX-License-Identifier: GPL-2.0-only OR MIT
2eaf01ee5SSarah Walker /* Copyright (c) 2023 Imagination Technologies Ltd. */
3eaf01ee5SSarah Walker 
4eaf01ee5SSarah Walker #include <drm/drm_managed.h>
5eaf01ee5SSarah Walker #include <drm/gpu_scheduler.h>
6eaf01ee5SSarah Walker 
7eaf01ee5SSarah Walker #include "pvr_cccb.h"
8eaf01ee5SSarah Walker #include "pvr_context.h"
9eaf01ee5SSarah Walker #include "pvr_device.h"
10eaf01ee5SSarah Walker #include "pvr_drv.h"
11eaf01ee5SSarah Walker #include "pvr_job.h"
12eaf01ee5SSarah Walker #include "pvr_queue.h"
13eaf01ee5SSarah Walker #include "pvr_vm.h"
14eaf01ee5SSarah Walker 
15eaf01ee5SSarah Walker #include "pvr_rogue_fwif_client.h"
16eaf01ee5SSarah Walker 
17eaf01ee5SSarah Walker #define MAX_DEADLINE_MS 30000
18eaf01ee5SSarah Walker 
19eaf01ee5SSarah Walker #define CTX_COMPUTE_CCCB_SIZE_LOG2 15
20eaf01ee5SSarah Walker #define CTX_FRAG_CCCB_SIZE_LOG2 15
21eaf01ee5SSarah Walker #define CTX_GEOM_CCCB_SIZE_LOG2 15
22eaf01ee5SSarah Walker #define CTX_TRANSFER_CCCB_SIZE_LOG2 15
23eaf01ee5SSarah Walker 
get_xfer_ctx_state_size(struct pvr_device * pvr_dev)24eaf01ee5SSarah Walker static int get_xfer_ctx_state_size(struct pvr_device *pvr_dev)
25eaf01ee5SSarah Walker {
26eaf01ee5SSarah Walker 	u32 num_isp_store_registers;
27eaf01ee5SSarah Walker 
28eaf01ee5SSarah Walker 	if (PVR_HAS_FEATURE(pvr_dev, xe_memory_hierarchy)) {
29eaf01ee5SSarah Walker 		num_isp_store_registers = 1;
30eaf01ee5SSarah Walker 	} else {
31eaf01ee5SSarah Walker 		int err;
32eaf01ee5SSarah Walker 
33eaf01ee5SSarah Walker 		err = PVR_FEATURE_VALUE(pvr_dev, num_isp_ipp_pipes, &num_isp_store_registers);
34eaf01ee5SSarah Walker 		if (WARN_ON(err))
35eaf01ee5SSarah Walker 			return err;
36eaf01ee5SSarah Walker 	}
37eaf01ee5SSarah Walker 
38eaf01ee5SSarah Walker 	return sizeof(struct rogue_fwif_frag_ctx_state) +
39eaf01ee5SSarah Walker 	       (num_isp_store_registers *
40eaf01ee5SSarah Walker 		sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0]));
41eaf01ee5SSarah Walker }
42eaf01ee5SSarah Walker 
get_frag_ctx_state_size(struct pvr_device * pvr_dev)43eaf01ee5SSarah Walker static int get_frag_ctx_state_size(struct pvr_device *pvr_dev)
44eaf01ee5SSarah Walker {
45eaf01ee5SSarah Walker 	u32 num_isp_store_registers;
46eaf01ee5SSarah Walker 	int err;
47eaf01ee5SSarah Walker 
48eaf01ee5SSarah Walker 	if (PVR_HAS_FEATURE(pvr_dev, xe_memory_hierarchy)) {
49eaf01ee5SSarah Walker 		err = PVR_FEATURE_VALUE(pvr_dev, num_raster_pipes, &num_isp_store_registers);
50eaf01ee5SSarah Walker 		if (WARN_ON(err))
51eaf01ee5SSarah Walker 			return err;
52eaf01ee5SSarah Walker 
53eaf01ee5SSarah Walker 		if (PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support)) {
54eaf01ee5SSarah Walker 			u32 xpu_max_slaves;
55eaf01ee5SSarah Walker 
56eaf01ee5SSarah Walker 			err = PVR_FEATURE_VALUE(pvr_dev, xpu_max_slaves, &xpu_max_slaves);
57eaf01ee5SSarah Walker 			if (WARN_ON(err))
58eaf01ee5SSarah Walker 				return err;
59eaf01ee5SSarah Walker 
60eaf01ee5SSarah Walker 			num_isp_store_registers *= (1 + xpu_max_slaves);
61eaf01ee5SSarah Walker 		}
62eaf01ee5SSarah Walker 	} else {
63eaf01ee5SSarah Walker 		err = PVR_FEATURE_VALUE(pvr_dev, num_isp_ipp_pipes, &num_isp_store_registers);
64eaf01ee5SSarah Walker 		if (WARN_ON(err))
65eaf01ee5SSarah Walker 			return err;
66eaf01ee5SSarah Walker 	}
67eaf01ee5SSarah Walker 
68eaf01ee5SSarah Walker 	return sizeof(struct rogue_fwif_frag_ctx_state) +
69eaf01ee5SSarah Walker 	       (num_isp_store_registers *
70eaf01ee5SSarah Walker 		sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0]));
71eaf01ee5SSarah Walker }
72eaf01ee5SSarah Walker 
get_ctx_state_size(struct pvr_device * pvr_dev,enum drm_pvr_job_type type)73eaf01ee5SSarah Walker static int get_ctx_state_size(struct pvr_device *pvr_dev, enum drm_pvr_job_type type)
74eaf01ee5SSarah Walker {
75eaf01ee5SSarah Walker 	switch (type) {
76eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_GEOMETRY:
77eaf01ee5SSarah Walker 		return sizeof(struct rogue_fwif_geom_ctx_state);
78eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_FRAGMENT:
79eaf01ee5SSarah Walker 		return get_frag_ctx_state_size(pvr_dev);
80eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_COMPUTE:
81eaf01ee5SSarah Walker 		return sizeof(struct rogue_fwif_compute_ctx_state);
82eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
83eaf01ee5SSarah Walker 		return get_xfer_ctx_state_size(pvr_dev);
84eaf01ee5SSarah Walker 	}
85eaf01ee5SSarah Walker 
86eaf01ee5SSarah Walker 	WARN(1, "Invalid queue type");
87eaf01ee5SSarah Walker 	return -EINVAL;
88eaf01ee5SSarah Walker }
89eaf01ee5SSarah Walker 
get_ctx_offset(enum drm_pvr_job_type type)90eaf01ee5SSarah Walker static u32 get_ctx_offset(enum drm_pvr_job_type type)
91eaf01ee5SSarah Walker {
92eaf01ee5SSarah Walker 	switch (type) {
93eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_GEOMETRY:
94eaf01ee5SSarah Walker 		return offsetof(struct rogue_fwif_fwrendercontext, geom_context);
95eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_FRAGMENT:
96eaf01ee5SSarah Walker 		return offsetof(struct rogue_fwif_fwrendercontext, frag_context);
97eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_COMPUTE:
98eaf01ee5SSarah Walker 		return offsetof(struct rogue_fwif_fwcomputecontext, cdm_context);
99eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
100eaf01ee5SSarah Walker 		return offsetof(struct rogue_fwif_fwtransfercontext, tq_context);
101eaf01ee5SSarah Walker 	}
102eaf01ee5SSarah Walker 
103eaf01ee5SSarah Walker 	return 0;
104eaf01ee5SSarah Walker }
105eaf01ee5SSarah Walker 
106eaf01ee5SSarah Walker static const char *
pvr_queue_fence_get_driver_name(struct dma_fence * f)107eaf01ee5SSarah Walker pvr_queue_fence_get_driver_name(struct dma_fence *f)
108eaf01ee5SSarah Walker {
109eaf01ee5SSarah Walker 	return PVR_DRIVER_NAME;
110eaf01ee5SSarah Walker }
111eaf01ee5SSarah Walker 
pvr_queue_fence_release_work(struct work_struct * w)112df1a1ed5SBrendan King static void pvr_queue_fence_release_work(struct work_struct *w)
113df1a1ed5SBrendan King {
114df1a1ed5SBrendan King 	struct pvr_queue_fence *fence = container_of(w, struct pvr_queue_fence, release_work);
115df1a1ed5SBrendan King 
116df1a1ed5SBrendan King 	pvr_context_put(fence->queue->ctx);
117df1a1ed5SBrendan King 	dma_fence_free(&fence->base);
118df1a1ed5SBrendan King }
119df1a1ed5SBrendan King 
pvr_queue_fence_release(struct dma_fence * f)120eaf01ee5SSarah Walker static void pvr_queue_fence_release(struct dma_fence *f)
121eaf01ee5SSarah Walker {
122eaf01ee5SSarah Walker 	struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base);
123df1a1ed5SBrendan King 	struct pvr_device *pvr_dev = fence->queue->ctx->pvr_dev;
124eaf01ee5SSarah Walker 
125df1a1ed5SBrendan King 	queue_work(pvr_dev->sched_wq, &fence->release_work);
126eaf01ee5SSarah Walker }
127eaf01ee5SSarah Walker 
128eaf01ee5SSarah Walker static const char *
pvr_queue_job_fence_get_timeline_name(struct dma_fence * f)129eaf01ee5SSarah Walker pvr_queue_job_fence_get_timeline_name(struct dma_fence *f)
130eaf01ee5SSarah Walker {
131eaf01ee5SSarah Walker 	struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base);
132eaf01ee5SSarah Walker 
133eaf01ee5SSarah Walker 	switch (fence->queue->type) {
134eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_GEOMETRY:
135eaf01ee5SSarah Walker 		return "geometry";
136eaf01ee5SSarah Walker 
137eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_FRAGMENT:
138eaf01ee5SSarah Walker 		return "fragment";
139eaf01ee5SSarah Walker 
140eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_COMPUTE:
141eaf01ee5SSarah Walker 		return "compute";
142eaf01ee5SSarah Walker 
143eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
144eaf01ee5SSarah Walker 		return "transfer";
145eaf01ee5SSarah Walker 	}
146eaf01ee5SSarah Walker 
147eaf01ee5SSarah Walker 	WARN(1, "Invalid queue type");
148eaf01ee5SSarah Walker 	return "invalid";
149eaf01ee5SSarah Walker }
150eaf01ee5SSarah Walker 
151eaf01ee5SSarah Walker static const char *
pvr_queue_cccb_fence_get_timeline_name(struct dma_fence * f)152eaf01ee5SSarah Walker pvr_queue_cccb_fence_get_timeline_name(struct dma_fence *f)
153eaf01ee5SSarah Walker {
154eaf01ee5SSarah Walker 	struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base);
155eaf01ee5SSarah Walker 
156eaf01ee5SSarah Walker 	switch (fence->queue->type) {
157eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_GEOMETRY:
158eaf01ee5SSarah Walker 		return "geometry-cccb";
159eaf01ee5SSarah Walker 
160eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_FRAGMENT:
161eaf01ee5SSarah Walker 		return "fragment-cccb";
162eaf01ee5SSarah Walker 
163eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_COMPUTE:
164eaf01ee5SSarah Walker 		return "compute-cccb";
165eaf01ee5SSarah Walker 
166eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
167eaf01ee5SSarah Walker 		return "transfer-cccb";
168eaf01ee5SSarah Walker 	}
169eaf01ee5SSarah Walker 
170eaf01ee5SSarah Walker 	WARN(1, "Invalid queue type");
171eaf01ee5SSarah Walker 	return "invalid";
172eaf01ee5SSarah Walker }
173eaf01ee5SSarah Walker 
174eaf01ee5SSarah Walker static const struct dma_fence_ops pvr_queue_job_fence_ops = {
175eaf01ee5SSarah Walker 	.get_driver_name = pvr_queue_fence_get_driver_name,
176eaf01ee5SSarah Walker 	.get_timeline_name = pvr_queue_job_fence_get_timeline_name,
177eaf01ee5SSarah Walker 	.release = pvr_queue_fence_release,
178eaf01ee5SSarah Walker };
179eaf01ee5SSarah Walker 
180eaf01ee5SSarah Walker /**
181eaf01ee5SSarah Walker  * to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is
182eaf01ee5SSarah Walker  * backed by a UFO.
183eaf01ee5SSarah Walker  * @f: The dma_fence to turn into a pvr_queue_fence.
184eaf01ee5SSarah Walker  *
185eaf01ee5SSarah Walker  * Return:
186eaf01ee5SSarah Walker  *  * A non-NULL pvr_queue_fence object if the dma_fence is backed by a UFO, or
187eaf01ee5SSarah Walker  *  * NULL otherwise.
188eaf01ee5SSarah Walker  */
189eaf01ee5SSarah Walker static struct pvr_queue_fence *
to_pvr_queue_job_fence(struct dma_fence * f)190eaf01ee5SSarah Walker to_pvr_queue_job_fence(struct dma_fence *f)
191eaf01ee5SSarah Walker {
192eaf01ee5SSarah Walker 	struct drm_sched_fence *sched_fence = to_drm_sched_fence(f);
193eaf01ee5SSarah Walker 
194eaf01ee5SSarah Walker 	if (sched_fence)
195eaf01ee5SSarah Walker 		f = sched_fence->parent;
196eaf01ee5SSarah Walker 
197eaf01ee5SSarah Walker 	if (f && f->ops == &pvr_queue_job_fence_ops)
198eaf01ee5SSarah Walker 		return container_of(f, struct pvr_queue_fence, base);
199eaf01ee5SSarah Walker 
200eaf01ee5SSarah Walker 	return NULL;
201eaf01ee5SSarah Walker }
202eaf01ee5SSarah Walker 
203eaf01ee5SSarah Walker static const struct dma_fence_ops pvr_queue_cccb_fence_ops = {
204eaf01ee5SSarah Walker 	.get_driver_name = pvr_queue_fence_get_driver_name,
205eaf01ee5SSarah Walker 	.get_timeline_name = pvr_queue_cccb_fence_get_timeline_name,
206eaf01ee5SSarah Walker 	.release = pvr_queue_fence_release,
207eaf01ee5SSarah Walker };
208eaf01ee5SSarah Walker 
209eaf01ee5SSarah Walker /**
210eaf01ee5SSarah Walker  * pvr_queue_fence_put() - Put wrapper for pvr_queue_fence objects.
211eaf01ee5SSarah Walker  * @f: The dma_fence object to put.
212eaf01ee5SSarah Walker  *
213eaf01ee5SSarah Walker  * If the pvr_queue_fence has been initialized, we call dma_fence_put(),
214eaf01ee5SSarah Walker  * otherwise we free the object with dma_fence_free(). This allows us
215eaf01ee5SSarah Walker  * to do the right thing before and after pvr_queue_fence_init() had been
216eaf01ee5SSarah Walker  * called.
217eaf01ee5SSarah Walker  */
pvr_queue_fence_put(struct dma_fence * f)218eaf01ee5SSarah Walker static void pvr_queue_fence_put(struct dma_fence *f)
219eaf01ee5SSarah Walker {
220eaf01ee5SSarah Walker 	if (!f)
221eaf01ee5SSarah Walker 		return;
222eaf01ee5SSarah Walker 
223eaf01ee5SSarah Walker 	if (WARN_ON(f->ops &&
224eaf01ee5SSarah Walker 		    f->ops != &pvr_queue_cccb_fence_ops &&
225eaf01ee5SSarah Walker 		    f->ops != &pvr_queue_job_fence_ops))
226eaf01ee5SSarah Walker 		return;
227eaf01ee5SSarah Walker 
228eaf01ee5SSarah Walker 	/* If the fence hasn't been initialized yet, free the object directly. */
229eaf01ee5SSarah Walker 	if (f->ops)
230eaf01ee5SSarah Walker 		dma_fence_put(f);
231eaf01ee5SSarah Walker 	else
232eaf01ee5SSarah Walker 		dma_fence_free(f);
233eaf01ee5SSarah Walker }
234eaf01ee5SSarah Walker 
235eaf01ee5SSarah Walker /**
236eaf01ee5SSarah Walker  * pvr_queue_fence_alloc() - Allocate a pvr_queue_fence fence object
237eaf01ee5SSarah Walker  *
238eaf01ee5SSarah Walker  * Call this function to allocate job CCCB and done fences. This only
239eaf01ee5SSarah Walker  * allocates the objects. Initialization happens when the underlying
240eaf01ee5SSarah Walker  * dma_fence object is to be returned to drm_sched (in prepare_job() or
241eaf01ee5SSarah Walker  * run_job()).
242eaf01ee5SSarah Walker  *
243eaf01ee5SSarah Walker  * Return:
244eaf01ee5SSarah Walker  *  * A valid pointer if the allocation succeeds, or
245eaf01ee5SSarah Walker  *  * NULL if the allocation fails.
246eaf01ee5SSarah Walker  */
247eaf01ee5SSarah Walker static struct dma_fence *
pvr_queue_fence_alloc(void)248eaf01ee5SSarah Walker pvr_queue_fence_alloc(void)
249eaf01ee5SSarah Walker {
250eaf01ee5SSarah Walker 	struct pvr_queue_fence *fence;
251eaf01ee5SSarah Walker 
252eaf01ee5SSarah Walker 	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
253eaf01ee5SSarah Walker 	if (!fence)
254eaf01ee5SSarah Walker 		return NULL;
255eaf01ee5SSarah Walker 
256eaf01ee5SSarah Walker 	return &fence->base;
257eaf01ee5SSarah Walker }
258eaf01ee5SSarah Walker 
259eaf01ee5SSarah Walker /**
260eaf01ee5SSarah Walker  * pvr_queue_fence_init() - Initializes a pvr_queue_fence object.
261eaf01ee5SSarah Walker  * @f: The fence to initialize
262eaf01ee5SSarah Walker  * @queue: The queue this fence belongs to.
263eaf01ee5SSarah Walker  * @fence_ops: The fence operations.
264eaf01ee5SSarah Walker  * @fence_ctx: The fence context.
265eaf01ee5SSarah Walker  *
266eaf01ee5SSarah Walker  * Wrapper around dma_fence_init() that takes care of initializing the
267eaf01ee5SSarah Walker  * pvr_queue_fence::queue field too.
268eaf01ee5SSarah Walker  */
269eaf01ee5SSarah Walker static void
pvr_queue_fence_init(struct dma_fence * f,struct pvr_queue * queue,const struct dma_fence_ops * fence_ops,struct pvr_queue_fence_ctx * fence_ctx)270eaf01ee5SSarah Walker pvr_queue_fence_init(struct dma_fence *f,
271eaf01ee5SSarah Walker 		     struct pvr_queue *queue,
272eaf01ee5SSarah Walker 		     const struct dma_fence_ops *fence_ops,
273eaf01ee5SSarah Walker 		     struct pvr_queue_fence_ctx *fence_ctx)
274eaf01ee5SSarah Walker {
275eaf01ee5SSarah Walker 	struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base);
276eaf01ee5SSarah Walker 
277eaf01ee5SSarah Walker 	pvr_context_get(queue->ctx);
278eaf01ee5SSarah Walker 	fence->queue = queue;
279df1a1ed5SBrendan King 	INIT_WORK(&fence->release_work, pvr_queue_fence_release_work);
280eaf01ee5SSarah Walker 	dma_fence_init(&fence->base, fence_ops,
281eaf01ee5SSarah Walker 		       &fence_ctx->lock, fence_ctx->id,
282eaf01ee5SSarah Walker 		       atomic_inc_return(&fence_ctx->seqno));
283eaf01ee5SSarah Walker }
284eaf01ee5SSarah Walker 
285eaf01ee5SSarah Walker /**
286eaf01ee5SSarah Walker  * pvr_queue_cccb_fence_init() - Initializes a CCCB fence object.
287eaf01ee5SSarah Walker  * @fence: The fence to initialize.
288eaf01ee5SSarah Walker  * @queue: The queue this fence belongs to.
289eaf01ee5SSarah Walker  *
290eaf01ee5SSarah Walker  * Initializes a fence that can be used to wait for CCCB space.
291eaf01ee5SSarah Walker  *
292eaf01ee5SSarah Walker  * Should be called in the ::prepare_job() path, so the fence returned to
293eaf01ee5SSarah Walker  * drm_sched is valid.
294eaf01ee5SSarah Walker  */
295eaf01ee5SSarah Walker static void
pvr_queue_cccb_fence_init(struct dma_fence * fence,struct pvr_queue * queue)296eaf01ee5SSarah Walker pvr_queue_cccb_fence_init(struct dma_fence *fence, struct pvr_queue *queue)
297eaf01ee5SSarah Walker {
298eaf01ee5SSarah Walker 	pvr_queue_fence_init(fence, queue, &pvr_queue_cccb_fence_ops,
299eaf01ee5SSarah Walker 			     &queue->cccb_fence_ctx.base);
300eaf01ee5SSarah Walker }
301eaf01ee5SSarah Walker 
302eaf01ee5SSarah Walker /**
303eaf01ee5SSarah Walker  * pvr_queue_job_fence_init() - Initializes a job done fence object.
304eaf01ee5SSarah Walker  * @fence: The fence to initialize.
305eaf01ee5SSarah Walker  * @queue: The queue this fence belongs to.
306eaf01ee5SSarah Walker  *
307eaf01ee5SSarah Walker  * Initializes a fence that will be signaled when the GPU is done executing
308eaf01ee5SSarah Walker  * a job.
309eaf01ee5SSarah Walker  *
310eaf01ee5SSarah Walker  * Should be called *before* the ::run_job() path, so the fence is initialised
311eaf01ee5SSarah Walker  * before being placed in the pending_list.
312eaf01ee5SSarah Walker  */
313eaf01ee5SSarah Walker static void
pvr_queue_job_fence_init(struct dma_fence * fence,struct pvr_queue * queue)314eaf01ee5SSarah Walker pvr_queue_job_fence_init(struct dma_fence *fence, struct pvr_queue *queue)
315eaf01ee5SSarah Walker {
31668c3de7fSBrendan King 	if (!fence->ops)
317eaf01ee5SSarah Walker 		pvr_queue_fence_init(fence, queue, &pvr_queue_job_fence_ops,
318eaf01ee5SSarah Walker 				     &queue->job_fence_ctx);
319eaf01ee5SSarah Walker }
320eaf01ee5SSarah Walker 
321eaf01ee5SSarah Walker /**
322eaf01ee5SSarah Walker  * pvr_queue_fence_ctx_init() - Queue fence context initialization.
323eaf01ee5SSarah Walker  * @fence_ctx: The context to initialize
324eaf01ee5SSarah Walker  */
325eaf01ee5SSarah Walker static void
pvr_queue_fence_ctx_init(struct pvr_queue_fence_ctx * fence_ctx)326eaf01ee5SSarah Walker pvr_queue_fence_ctx_init(struct pvr_queue_fence_ctx *fence_ctx)
327eaf01ee5SSarah Walker {
328eaf01ee5SSarah Walker 	spin_lock_init(&fence_ctx->lock);
329eaf01ee5SSarah Walker 	fence_ctx->id = dma_fence_context_alloc(1);
330eaf01ee5SSarah Walker 	atomic_set(&fence_ctx->seqno, 0);
331eaf01ee5SSarah Walker }
332eaf01ee5SSarah Walker 
ufo_cmds_size(u32 elem_count)333eaf01ee5SSarah Walker static u32 ufo_cmds_size(u32 elem_count)
334eaf01ee5SSarah Walker {
335eaf01ee5SSarah Walker 	/* We can pass at most ROGUE_FWIF_CCB_CMD_MAX_UFOS per UFO-related command. */
336eaf01ee5SSarah Walker 	u32 full_cmd_count = elem_count / ROGUE_FWIF_CCB_CMD_MAX_UFOS;
337eaf01ee5SSarah Walker 	u32 remaining_elems = elem_count % ROGUE_FWIF_CCB_CMD_MAX_UFOS;
338eaf01ee5SSarah Walker 	u32 size = full_cmd_count *
339eaf01ee5SSarah Walker 		   pvr_cccb_get_size_of_cmd_with_hdr(ROGUE_FWIF_CCB_CMD_MAX_UFOS *
340eaf01ee5SSarah Walker 						     sizeof(struct rogue_fwif_ufo));
341eaf01ee5SSarah Walker 
342eaf01ee5SSarah Walker 	if (remaining_elems) {
343eaf01ee5SSarah Walker 		size += pvr_cccb_get_size_of_cmd_with_hdr(remaining_elems *
344eaf01ee5SSarah Walker 							  sizeof(struct rogue_fwif_ufo));
345eaf01ee5SSarah Walker 	}
346eaf01ee5SSarah Walker 
347eaf01ee5SSarah Walker 	return size;
348eaf01ee5SSarah Walker }
349eaf01ee5SSarah Walker 
job_cmds_size(struct pvr_job * job,u32 ufo_wait_count)350eaf01ee5SSarah Walker static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count)
351eaf01ee5SSarah Walker {
352eaf01ee5SSarah Walker 	/* One UFO cmd for the fence signaling, one UFO cmd per native fence native,
353eaf01ee5SSarah Walker 	 * and a command for the job itself.
354eaf01ee5SSarah Walker 	 */
355eaf01ee5SSarah Walker 	return ufo_cmds_size(1) + ufo_cmds_size(ufo_wait_count) +
356eaf01ee5SSarah Walker 	       pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len);
357eaf01ee5SSarah Walker }
358eaf01ee5SSarah Walker 
359eaf01ee5SSarah Walker /**
360eaf01ee5SSarah Walker  * job_count_remaining_native_deps() - Count the number of non-signaled native dependencies.
361eaf01ee5SSarah Walker  * @job: Job to operate on.
362eaf01ee5SSarah Walker  *
363eaf01ee5SSarah Walker  * Returns: Number of non-signaled native deps remaining.
364eaf01ee5SSarah Walker  */
job_count_remaining_native_deps(struct pvr_job * job)365eaf01ee5SSarah Walker static unsigned long job_count_remaining_native_deps(struct pvr_job *job)
366eaf01ee5SSarah Walker {
367eaf01ee5SSarah Walker 	unsigned long remaining_count = 0;
368eaf01ee5SSarah Walker 	struct dma_fence *fence = NULL;
369eaf01ee5SSarah Walker 	unsigned long index;
370eaf01ee5SSarah Walker 
371eaf01ee5SSarah Walker 	xa_for_each(&job->base.dependencies, index, fence) {
372eaf01ee5SSarah Walker 		struct pvr_queue_fence *jfence;
373eaf01ee5SSarah Walker 
374eaf01ee5SSarah Walker 		jfence = to_pvr_queue_job_fence(fence);
375eaf01ee5SSarah Walker 		if (!jfence)
376eaf01ee5SSarah Walker 			continue;
377eaf01ee5SSarah Walker 
378eaf01ee5SSarah Walker 		if (!dma_fence_is_signaled(&jfence->base))
379eaf01ee5SSarah Walker 			remaining_count++;
380eaf01ee5SSarah Walker 	}
381eaf01ee5SSarah Walker 
382eaf01ee5SSarah Walker 	return remaining_count;
383eaf01ee5SSarah Walker }
384eaf01ee5SSarah Walker 
385eaf01ee5SSarah Walker /**
386eaf01ee5SSarah Walker  * pvr_queue_get_job_cccb_fence() - Get the CCCB fence attached to a job.
387eaf01ee5SSarah Walker  * @queue: The queue this job will be submitted to.
388eaf01ee5SSarah Walker  * @job: The job to get the CCCB fence on.
389eaf01ee5SSarah Walker  *
390eaf01ee5SSarah Walker  * The CCCB fence is a synchronization primitive allowing us to delay job
391eaf01ee5SSarah Walker  * submission until there's enough space in the CCCB to submit the job.
392eaf01ee5SSarah Walker  *
393eaf01ee5SSarah Walker  * Return:
394eaf01ee5SSarah Walker  *  * NULL if there's enough space in the CCCB to submit this job, or
395eaf01ee5SSarah Walker  *  * A valid dma_fence object otherwise.
396eaf01ee5SSarah Walker  */
397eaf01ee5SSarah Walker static struct dma_fence *
pvr_queue_get_job_cccb_fence(struct pvr_queue * queue,struct pvr_job * job)398eaf01ee5SSarah Walker pvr_queue_get_job_cccb_fence(struct pvr_queue *queue, struct pvr_job *job)
399eaf01ee5SSarah Walker {
400eaf01ee5SSarah Walker 	struct pvr_queue_fence *cccb_fence;
401eaf01ee5SSarah Walker 	unsigned int native_deps_remaining;
402eaf01ee5SSarah Walker 
403eaf01ee5SSarah Walker 	/* If the fence is NULL, that means we already checked that we had
404eaf01ee5SSarah Walker 	 * enough space in the cccb for our job.
405eaf01ee5SSarah Walker 	 */
406eaf01ee5SSarah Walker 	if (!job->cccb_fence)
407eaf01ee5SSarah Walker 		return NULL;
408eaf01ee5SSarah Walker 
409eaf01ee5SSarah Walker 	mutex_lock(&queue->cccb_fence_ctx.job_lock);
410eaf01ee5SSarah Walker 
411eaf01ee5SSarah Walker 	/* Count remaining native dependencies and check if the job fits in the CCCB. */
412eaf01ee5SSarah Walker 	native_deps_remaining = job_count_remaining_native_deps(job);
413eaf01ee5SSarah Walker 	if (pvr_cccb_cmdseq_fits(&queue->cccb, job_cmds_size(job, native_deps_remaining))) {
414eaf01ee5SSarah Walker 		pvr_queue_fence_put(job->cccb_fence);
415eaf01ee5SSarah Walker 		job->cccb_fence = NULL;
416eaf01ee5SSarah Walker 		goto out_unlock;
417eaf01ee5SSarah Walker 	}
418eaf01ee5SSarah Walker 
419eaf01ee5SSarah Walker 	/* There should be no job attached to the CCCB fence context:
420eaf01ee5SSarah Walker 	 * drm_sched_entity guarantees that jobs are submitted one at a time.
421eaf01ee5SSarah Walker 	 */
422eaf01ee5SSarah Walker 	if (WARN_ON(queue->cccb_fence_ctx.job))
423eaf01ee5SSarah Walker 		pvr_job_put(queue->cccb_fence_ctx.job);
424eaf01ee5SSarah Walker 
425eaf01ee5SSarah Walker 	queue->cccb_fence_ctx.job = pvr_job_get(job);
426eaf01ee5SSarah Walker 
427eaf01ee5SSarah Walker 	/* Initialize the fence before returning it. */
428eaf01ee5SSarah Walker 	cccb_fence = container_of(job->cccb_fence, struct pvr_queue_fence, base);
429eaf01ee5SSarah Walker 	if (!WARN_ON(cccb_fence->queue))
430eaf01ee5SSarah Walker 		pvr_queue_cccb_fence_init(job->cccb_fence, queue);
431eaf01ee5SSarah Walker 
432eaf01ee5SSarah Walker out_unlock:
433eaf01ee5SSarah Walker 	mutex_unlock(&queue->cccb_fence_ctx.job_lock);
434eaf01ee5SSarah Walker 
435eaf01ee5SSarah Walker 	return dma_fence_get(job->cccb_fence);
436eaf01ee5SSarah Walker }
437eaf01ee5SSarah Walker 
438eaf01ee5SSarah Walker /**
439eaf01ee5SSarah Walker  * pvr_queue_get_job_kccb_fence() - Get the KCCB fence attached to a job.
440eaf01ee5SSarah Walker  * @queue: The queue this job will be submitted to.
441eaf01ee5SSarah Walker  * @job: The job to get the KCCB fence on.
442eaf01ee5SSarah Walker  *
443eaf01ee5SSarah Walker  * The KCCB fence is a synchronization primitive allowing us to delay job
444eaf01ee5SSarah Walker  * submission until there's enough space in the KCCB to submit the job.
445eaf01ee5SSarah Walker  *
446eaf01ee5SSarah Walker  * Return:
447eaf01ee5SSarah Walker  *  * NULL if there's enough space in the KCCB to submit this job, or
448eaf01ee5SSarah Walker  *  * A valid dma_fence object otherwise.
449eaf01ee5SSarah Walker  */
450eaf01ee5SSarah Walker static struct dma_fence *
pvr_queue_get_job_kccb_fence(struct pvr_queue * queue,struct pvr_job * job)451eaf01ee5SSarah Walker pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job)
452eaf01ee5SSarah Walker {
453eaf01ee5SSarah Walker 	struct pvr_device *pvr_dev = queue->ctx->pvr_dev;
454eaf01ee5SSarah Walker 	struct dma_fence *kccb_fence = NULL;
455eaf01ee5SSarah Walker 
456eaf01ee5SSarah Walker 	/* If the fence is NULL, that means we already checked that we had
457eaf01ee5SSarah Walker 	 * enough space in the KCCB for our job.
458eaf01ee5SSarah Walker 	 */
459eaf01ee5SSarah Walker 	if (!job->kccb_fence)
460eaf01ee5SSarah Walker 		return NULL;
461eaf01ee5SSarah Walker 
462eaf01ee5SSarah Walker 	if (!WARN_ON(job->kccb_fence->ops)) {
463eaf01ee5SSarah Walker 		kccb_fence = pvr_kccb_reserve_slot(pvr_dev, job->kccb_fence);
464eaf01ee5SSarah Walker 		job->kccb_fence = NULL;
465eaf01ee5SSarah Walker 	}
466eaf01ee5SSarah Walker 
467eaf01ee5SSarah Walker 	return kccb_fence;
468eaf01ee5SSarah Walker }
469eaf01ee5SSarah Walker 
470eaf01ee5SSarah Walker static struct dma_fence *
pvr_queue_get_paired_frag_job_dep(struct pvr_queue * queue,struct pvr_job * job)471eaf01ee5SSarah Walker pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job)
472eaf01ee5SSarah Walker {
473eaf01ee5SSarah Walker 	struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ?
474eaf01ee5SSarah Walker 				   job->paired_job : NULL;
475eaf01ee5SSarah Walker 	struct dma_fence *f;
476eaf01ee5SSarah Walker 	unsigned long index;
477eaf01ee5SSarah Walker 
478eaf01ee5SSarah Walker 	if (!frag_job)
479eaf01ee5SSarah Walker 		return NULL;
480eaf01ee5SSarah Walker 
481eaf01ee5SSarah Walker 	xa_for_each(&frag_job->base.dependencies, index, f) {
482eaf01ee5SSarah Walker 		/* Skip already signaled fences. */
483eaf01ee5SSarah Walker 		if (dma_fence_is_signaled(f))
484eaf01ee5SSarah Walker 			continue;
485eaf01ee5SSarah Walker 
486eaf01ee5SSarah Walker 		/* Skip our own fence. */
487eaf01ee5SSarah Walker 		if (f == &job->base.s_fence->scheduled)
488eaf01ee5SSarah Walker 			continue;
489eaf01ee5SSarah Walker 
490eaf01ee5SSarah Walker 		return dma_fence_get(f);
491eaf01ee5SSarah Walker 	}
492eaf01ee5SSarah Walker 
493eaf01ee5SSarah Walker 	return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity);
494eaf01ee5SSarah Walker }
495eaf01ee5SSarah Walker 
496eaf01ee5SSarah Walker /**
497eaf01ee5SSarah Walker  * pvr_queue_prepare_job() - Return the next internal dependencies expressed as a dma_fence.
498eaf01ee5SSarah Walker  * @sched_job: The job to query the next internal dependency on
499eaf01ee5SSarah Walker  * @s_entity: The entity this job is queue on.
500eaf01ee5SSarah Walker  *
501eaf01ee5SSarah Walker  * After iterating over drm_sched_job::dependencies, drm_sched let the driver return
502eaf01ee5SSarah Walker  * its own internal dependencies. We use this function to return our internal dependencies.
503eaf01ee5SSarah Walker  */
504eaf01ee5SSarah Walker static struct dma_fence *
pvr_queue_prepare_job(struct drm_sched_job * sched_job,struct drm_sched_entity * s_entity)505eaf01ee5SSarah Walker pvr_queue_prepare_job(struct drm_sched_job *sched_job,
506eaf01ee5SSarah Walker 		      struct drm_sched_entity *s_entity)
507eaf01ee5SSarah Walker {
508eaf01ee5SSarah Walker 	struct pvr_job *job = container_of(sched_job, struct pvr_job, base);
509eaf01ee5SSarah Walker 	struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity);
510eaf01ee5SSarah Walker 	struct dma_fence *internal_dep = NULL;
511eaf01ee5SSarah Walker 
512eaf01ee5SSarah Walker 	/*
513eaf01ee5SSarah Walker 	 * Initialize the done_fence, so we can signal it. This must be done
514eaf01ee5SSarah Walker 	 * here because otherwise by the time of run_job() the job will end up
515eaf01ee5SSarah Walker 	 * in the pending list without a valid fence.
516eaf01ee5SSarah Walker 	 */
517eaf01ee5SSarah Walker 	if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
518eaf01ee5SSarah Walker 		/*
519eaf01ee5SSarah Walker 		 * This will be called on a paired fragment job after being
520eaf01ee5SSarah Walker 		 * submitted to firmware. We can tell if this is the case and
521eaf01ee5SSarah Walker 		 * bail early from whether run_job() has been called on the
522eaf01ee5SSarah Walker 		 * geometry job, which would issue a pm ref.
523eaf01ee5SSarah Walker 		 */
524eaf01ee5SSarah Walker 		if (job->paired_job->has_pm_ref)
525eaf01ee5SSarah Walker 			return NULL;
526eaf01ee5SSarah Walker 
527eaf01ee5SSarah Walker 		/*
528eaf01ee5SSarah Walker 		 * In this case we need to use the job's own ctx to initialise
529eaf01ee5SSarah Walker 		 * the done_fence.  The other steps are done in the ctx of the
530eaf01ee5SSarah Walker 		 * paired geometry job.
531eaf01ee5SSarah Walker 		 */
532eaf01ee5SSarah Walker 		pvr_queue_job_fence_init(job->done_fence,
533eaf01ee5SSarah Walker 					 job->ctx->queues.fragment);
534eaf01ee5SSarah Walker 	} else {
535eaf01ee5SSarah Walker 		pvr_queue_job_fence_init(job->done_fence, queue);
536eaf01ee5SSarah Walker 	}
537eaf01ee5SSarah Walker 
538eaf01ee5SSarah Walker 	/* CCCB fence is used to make sure we have enough space in the CCCB to
539eaf01ee5SSarah Walker 	 * submit our commands.
540eaf01ee5SSarah Walker 	 */
541eaf01ee5SSarah Walker 	internal_dep = pvr_queue_get_job_cccb_fence(queue, job);
542eaf01ee5SSarah Walker 
543eaf01ee5SSarah Walker 	/* KCCB fence is used to make sure we have a KCCB slot to queue our
544eaf01ee5SSarah Walker 	 * CMD_KICK.
545eaf01ee5SSarah Walker 	 */
546eaf01ee5SSarah Walker 	if (!internal_dep)
547eaf01ee5SSarah Walker 		internal_dep = pvr_queue_get_job_kccb_fence(queue, job);
548eaf01ee5SSarah Walker 
549eaf01ee5SSarah Walker 	/* Any extra internal dependency should be added here, using the following
550eaf01ee5SSarah Walker 	 * pattern:
551eaf01ee5SSarah Walker 	 *
552eaf01ee5SSarah Walker 	 *	if (!internal_dep)
553eaf01ee5SSarah Walker 	 *		internal_dep = pvr_queue_get_job_xxxx_fence(queue, job);
554eaf01ee5SSarah Walker 	 */
555eaf01ee5SSarah Walker 
556eaf01ee5SSarah Walker 	/* The paired job fence should come last, when everything else is ready. */
557eaf01ee5SSarah Walker 	if (!internal_dep)
558eaf01ee5SSarah Walker 		internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job);
559eaf01ee5SSarah Walker 
560eaf01ee5SSarah Walker 	return internal_dep;
561eaf01ee5SSarah Walker }
562eaf01ee5SSarah Walker 
563eaf01ee5SSarah Walker /**
564eaf01ee5SSarah Walker  * pvr_queue_update_active_state_locked() - Update the queue active state.
565eaf01ee5SSarah Walker  * @queue: Queue to update the state on.
566eaf01ee5SSarah Walker  *
567eaf01ee5SSarah Walker  * Locked version of pvr_queue_update_active_state(). Must be called with
568eaf01ee5SSarah Walker  * pvr_device::queue::lock held.
569eaf01ee5SSarah Walker  */
pvr_queue_update_active_state_locked(struct pvr_queue * queue)570eaf01ee5SSarah Walker static void pvr_queue_update_active_state_locked(struct pvr_queue *queue)
571eaf01ee5SSarah Walker {
572eaf01ee5SSarah Walker 	struct pvr_device *pvr_dev = queue->ctx->pvr_dev;
573eaf01ee5SSarah Walker 
574eaf01ee5SSarah Walker 	lockdep_assert_held(&pvr_dev->queues.lock);
575eaf01ee5SSarah Walker 
576eaf01ee5SSarah Walker 	/* The queue is temporary out of any list when it's being reset,
577eaf01ee5SSarah Walker 	 * we don't want a call to pvr_queue_update_active_state_locked()
578eaf01ee5SSarah Walker 	 * to re-insert it behind our back.
579eaf01ee5SSarah Walker 	 */
580eaf01ee5SSarah Walker 	if (list_empty(&queue->node))
581eaf01ee5SSarah Walker 		return;
582eaf01ee5SSarah Walker 
583eaf01ee5SSarah Walker 	if (!atomic_read(&queue->in_flight_job_count))
584eaf01ee5SSarah Walker 		list_move_tail(&queue->node, &pvr_dev->queues.idle);
585eaf01ee5SSarah Walker 	else
586eaf01ee5SSarah Walker 		list_move_tail(&queue->node, &pvr_dev->queues.active);
587eaf01ee5SSarah Walker }
588eaf01ee5SSarah Walker 
589eaf01ee5SSarah Walker /**
590eaf01ee5SSarah Walker  * pvr_queue_update_active_state() - Update the queue active state.
591eaf01ee5SSarah Walker  * @queue: Queue to update the state on.
592eaf01ee5SSarah Walker  *
593eaf01ee5SSarah Walker  * Active state is based on the in_flight_job_count value.
594eaf01ee5SSarah Walker  *
595eaf01ee5SSarah Walker  * Updating the active state implies moving the queue in or out of the
596eaf01ee5SSarah Walker  * active queue list, which also defines whether the queue is checked
597eaf01ee5SSarah Walker  * or not when a FW event is received.
598eaf01ee5SSarah Walker  *
599eaf01ee5SSarah Walker  * This function should be called any time a job is submitted or it done
600eaf01ee5SSarah Walker  * fence is signaled.
601eaf01ee5SSarah Walker  */
pvr_queue_update_active_state(struct pvr_queue * queue)602eaf01ee5SSarah Walker static void pvr_queue_update_active_state(struct pvr_queue *queue)
603eaf01ee5SSarah Walker {
604eaf01ee5SSarah Walker 	struct pvr_device *pvr_dev = queue->ctx->pvr_dev;
605eaf01ee5SSarah Walker 
606eaf01ee5SSarah Walker 	mutex_lock(&pvr_dev->queues.lock);
607eaf01ee5SSarah Walker 	pvr_queue_update_active_state_locked(queue);
608eaf01ee5SSarah Walker 	mutex_unlock(&pvr_dev->queues.lock);
609eaf01ee5SSarah Walker }
610eaf01ee5SSarah Walker 
pvr_queue_submit_job_to_cccb(struct pvr_job * job)611eaf01ee5SSarah Walker static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
612eaf01ee5SSarah Walker {
613eaf01ee5SSarah Walker 	struct pvr_queue *queue = container_of(job->base.sched, struct pvr_queue, scheduler);
614eaf01ee5SSarah Walker 	struct rogue_fwif_ufo ufos[ROGUE_FWIF_CCB_CMD_MAX_UFOS];
615eaf01ee5SSarah Walker 	struct pvr_cccb *cccb = &queue->cccb;
616eaf01ee5SSarah Walker 	struct pvr_queue_fence *jfence;
617eaf01ee5SSarah Walker 	struct dma_fence *fence;
618eaf01ee5SSarah Walker 	unsigned long index;
619eaf01ee5SSarah Walker 	u32 ufo_count = 0;
620eaf01ee5SSarah Walker 
621eaf01ee5SSarah Walker 	/* We need to add the queue to the active list before updating the CCCB,
622eaf01ee5SSarah Walker 	 * otherwise we might miss the FW event informing us that something
623eaf01ee5SSarah Walker 	 * happened on this queue.
624eaf01ee5SSarah Walker 	 */
625eaf01ee5SSarah Walker 	atomic_inc(&queue->in_flight_job_count);
626eaf01ee5SSarah Walker 	pvr_queue_update_active_state(queue);
627eaf01ee5SSarah Walker 
628eaf01ee5SSarah Walker 	xa_for_each(&job->base.dependencies, index, fence) {
629eaf01ee5SSarah Walker 		jfence = to_pvr_queue_job_fence(fence);
630eaf01ee5SSarah Walker 		if (!jfence)
631eaf01ee5SSarah Walker 			continue;
632eaf01ee5SSarah Walker 
633eaf01ee5SSarah Walker 		/* Skip the partial render fence, we will place it at the end. */
634eaf01ee5SSarah Walker 		if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job &&
635eaf01ee5SSarah Walker 		    &job->paired_job->base.s_fence->scheduled == fence)
636eaf01ee5SSarah Walker 			continue;
637eaf01ee5SSarah Walker 
638eaf01ee5SSarah Walker 		if (dma_fence_is_signaled(&jfence->base))
639eaf01ee5SSarah Walker 			continue;
640eaf01ee5SSarah Walker 
641eaf01ee5SSarah Walker 		pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj,
642eaf01ee5SSarah Walker 					  &ufos[ufo_count].addr);
643eaf01ee5SSarah Walker 		ufos[ufo_count++].value = jfence->base.seqno;
644eaf01ee5SSarah Walker 
645eaf01ee5SSarah Walker 		if (ufo_count == ARRAY_SIZE(ufos)) {
646eaf01ee5SSarah Walker 			pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_FENCE_PR,
647eaf01ee5SSarah Walker 							   sizeof(ufos), ufos, 0, 0);
648eaf01ee5SSarah Walker 			ufo_count = 0;
649eaf01ee5SSarah Walker 		}
650eaf01ee5SSarah Walker 	}
651eaf01ee5SSarah Walker 
652eaf01ee5SSarah Walker 	/* Partial render fence goes last. */
653eaf01ee5SSarah Walker 	if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
654eaf01ee5SSarah Walker 		jfence = to_pvr_queue_job_fence(job->paired_job->done_fence);
655eaf01ee5SSarah Walker 		if (!WARN_ON(!jfence)) {
656eaf01ee5SSarah Walker 			pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj,
657eaf01ee5SSarah Walker 						  &ufos[ufo_count].addr);
658eaf01ee5SSarah Walker 			ufos[ufo_count++].value = job->paired_job->done_fence->seqno;
659eaf01ee5SSarah Walker 		}
660eaf01ee5SSarah Walker 	}
661eaf01ee5SSarah Walker 
662eaf01ee5SSarah Walker 	if (ufo_count) {
663eaf01ee5SSarah Walker 		pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_FENCE_PR,
664eaf01ee5SSarah Walker 						   sizeof(ufos[0]) * ufo_count, ufos, 0, 0);
665eaf01ee5SSarah Walker 	}
666eaf01ee5SSarah Walker 
667eaf01ee5SSarah Walker 	if (job->type == DRM_PVR_JOB_TYPE_GEOMETRY && job->paired_job) {
668eaf01ee5SSarah Walker 		struct rogue_fwif_cmd_geom *cmd = job->cmd;
669eaf01ee5SSarah Walker 
670eaf01ee5SSarah Walker 		/* Reference value for the partial render test is the current queue fence
671eaf01ee5SSarah Walker 		 * seqno minus one.
672eaf01ee5SSarah Walker 		 */
673eaf01ee5SSarah Walker 		pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj,
674eaf01ee5SSarah Walker 					  &cmd->partial_render_geom_frag_fence.addr);
675eaf01ee5SSarah Walker 		cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1;
676eaf01ee5SSarah Walker 	}
677eaf01ee5SSarah Walker 
678eaf01ee5SSarah Walker 	/* Submit job to FW */
679eaf01ee5SSarah Walker 	pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd,
680eaf01ee5SSarah Walker 					   job->id, job->id);
681eaf01ee5SSarah Walker 
682eaf01ee5SSarah Walker 	/* Signal the job fence. */
683eaf01ee5SSarah Walker 	pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj, &ufos[0].addr);
684eaf01ee5SSarah Walker 	ufos[0].value = job->done_fence->seqno;
685eaf01ee5SSarah Walker 	pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_UPDATE,
686eaf01ee5SSarah Walker 					   sizeof(ufos[0]), ufos, 0, 0);
687eaf01ee5SSarah Walker }
688eaf01ee5SSarah Walker 
689eaf01ee5SSarah Walker /**
690eaf01ee5SSarah Walker  * pvr_queue_run_job() - Submit a job to the FW.
691eaf01ee5SSarah Walker  * @sched_job: The job to submit.
692eaf01ee5SSarah Walker  *
693eaf01ee5SSarah Walker  * This function is called when all non-native dependencies have been met and
694eaf01ee5SSarah Walker  * when the commands resulting from this job are guaranteed to fit in the CCCB.
695eaf01ee5SSarah Walker  */
pvr_queue_run_job(struct drm_sched_job * sched_job)696eaf01ee5SSarah Walker static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
697eaf01ee5SSarah Walker {
698eaf01ee5SSarah Walker 	struct pvr_job *job = container_of(sched_job, struct pvr_job, base);
699eaf01ee5SSarah Walker 	struct pvr_device *pvr_dev = job->pvr_dev;
700eaf01ee5SSarah Walker 	int err;
701eaf01ee5SSarah Walker 
702eaf01ee5SSarah Walker 	/* The fragment job is issued along the geometry job when we use combined
703eaf01ee5SSarah Walker 	 * geom+frag kicks. When we get there, we should simply return the
704eaf01ee5SSarah Walker 	 * done_fence that's been initialized earlier.
705eaf01ee5SSarah Walker 	 */
706eaf01ee5SSarah Walker 	if (job->paired_job && job->type == DRM_PVR_JOB_TYPE_FRAGMENT &&
707eaf01ee5SSarah Walker 	    job->done_fence->ops) {
708eaf01ee5SSarah Walker 		return dma_fence_get(job->done_fence);
709eaf01ee5SSarah Walker 	}
710eaf01ee5SSarah Walker 
711eaf01ee5SSarah Walker 	/* The only kind of jobs that can be paired are geometry and fragment, and
712eaf01ee5SSarah Walker 	 * we bail out early if we see a fragment job that's paired with a geomtry
713eaf01ee5SSarah Walker 	 * job.
714eaf01ee5SSarah Walker 	 * Paired jobs must also target the same context and point to the same
715eaf01ee5SSarah Walker 	 * HWRT.
716eaf01ee5SSarah Walker 	 */
717eaf01ee5SSarah Walker 	if (WARN_ON(job->paired_job &&
718eaf01ee5SSarah Walker 		    (job->type != DRM_PVR_JOB_TYPE_GEOMETRY ||
719eaf01ee5SSarah Walker 		     job->paired_job->type != DRM_PVR_JOB_TYPE_FRAGMENT ||
720eaf01ee5SSarah Walker 		     job->hwrt != job->paired_job->hwrt ||
721eaf01ee5SSarah Walker 		     job->ctx != job->paired_job->ctx)))
722eaf01ee5SSarah Walker 		return ERR_PTR(-EINVAL);
723eaf01ee5SSarah Walker 
724eaf01ee5SSarah Walker 	err = pvr_job_get_pm_ref(job);
725eaf01ee5SSarah Walker 	if (WARN_ON(err))
726eaf01ee5SSarah Walker 		return ERR_PTR(err);
727eaf01ee5SSarah Walker 
728eaf01ee5SSarah Walker 	if (job->paired_job) {
729eaf01ee5SSarah Walker 		err = pvr_job_get_pm_ref(job->paired_job);
730eaf01ee5SSarah Walker 		if (WARN_ON(err))
731eaf01ee5SSarah Walker 			return ERR_PTR(err);
732eaf01ee5SSarah Walker 	}
733eaf01ee5SSarah Walker 
734eaf01ee5SSarah Walker 	/* Submit our job to the CCCB */
735eaf01ee5SSarah Walker 	pvr_queue_submit_job_to_cccb(job);
736eaf01ee5SSarah Walker 
737eaf01ee5SSarah Walker 	if (job->paired_job) {
738eaf01ee5SSarah Walker 		struct pvr_job *geom_job = job;
739eaf01ee5SSarah Walker 		struct pvr_job *frag_job = job->paired_job;
740eaf01ee5SSarah Walker 		struct pvr_queue *geom_queue = job->ctx->queues.geometry;
741eaf01ee5SSarah Walker 		struct pvr_queue *frag_queue = job->ctx->queues.fragment;
742eaf01ee5SSarah Walker 
743eaf01ee5SSarah Walker 		/* Submit the fragment job along the geometry job and send a combined kick. */
744eaf01ee5SSarah Walker 		pvr_queue_submit_job_to_cccb(frag_job);
745eaf01ee5SSarah Walker 		pvr_cccb_send_kccb_combined_kick(pvr_dev,
746eaf01ee5SSarah Walker 						 &geom_queue->cccb, &frag_queue->cccb,
747eaf01ee5SSarah Walker 						 pvr_context_get_fw_addr(geom_job->ctx) +
748eaf01ee5SSarah Walker 						 geom_queue->ctx_offset,
749eaf01ee5SSarah Walker 						 pvr_context_get_fw_addr(frag_job->ctx) +
750eaf01ee5SSarah Walker 						 frag_queue->ctx_offset,
751eaf01ee5SSarah Walker 						 job->hwrt,
752eaf01ee5SSarah Walker 						 frag_job->fw_ccb_cmd_type ==
753eaf01ee5SSarah Walker 						 ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR);
754eaf01ee5SSarah Walker 	} else {
755eaf01ee5SSarah Walker 		struct pvr_queue *queue = container_of(job->base.sched,
756eaf01ee5SSarah Walker 						       struct pvr_queue, scheduler);
757eaf01ee5SSarah Walker 
758eaf01ee5SSarah Walker 		pvr_cccb_send_kccb_kick(pvr_dev, &queue->cccb,
759eaf01ee5SSarah Walker 					pvr_context_get_fw_addr(job->ctx) + queue->ctx_offset,
760eaf01ee5SSarah Walker 					job->hwrt);
761eaf01ee5SSarah Walker 	}
762eaf01ee5SSarah Walker 
763eaf01ee5SSarah Walker 	return dma_fence_get(job->done_fence);
764eaf01ee5SSarah Walker }
765eaf01ee5SSarah Walker 
pvr_queue_stop(struct pvr_queue * queue,struct pvr_job * bad_job)766eaf01ee5SSarah Walker static void pvr_queue_stop(struct pvr_queue *queue, struct pvr_job *bad_job)
767eaf01ee5SSarah Walker {
768eaf01ee5SSarah Walker 	drm_sched_stop(&queue->scheduler, bad_job ? &bad_job->base : NULL);
769eaf01ee5SSarah Walker }
770eaf01ee5SSarah Walker 
pvr_queue_start(struct pvr_queue * queue)771eaf01ee5SSarah Walker static void pvr_queue_start(struct pvr_queue *queue)
772eaf01ee5SSarah Walker {
773eaf01ee5SSarah Walker 	struct pvr_job *job;
774eaf01ee5SSarah Walker 
775eaf01ee5SSarah Walker 	/* Make sure we CPU-signal the UFO object, so other queues don't get
776eaf01ee5SSarah Walker 	 * blocked waiting on it.
777eaf01ee5SSarah Walker 	 */
778eaf01ee5SSarah Walker 	*queue->timeline_ufo.value = atomic_read(&queue->job_fence_ctx.seqno);
779eaf01ee5SSarah Walker 
780eaf01ee5SSarah Walker 	list_for_each_entry(job, &queue->scheduler.pending_list, base.list) {
781eaf01ee5SSarah Walker 		if (dma_fence_is_signaled(job->done_fence)) {
782eaf01ee5SSarah Walker 			/* Jobs might have completed after drm_sched_stop() was called.
783eaf01ee5SSarah Walker 			 * In that case, re-assign the parent field to the done_fence.
784eaf01ee5SSarah Walker 			 */
785eaf01ee5SSarah Walker 			WARN_ON(job->base.s_fence->parent);
786eaf01ee5SSarah Walker 			job->base.s_fence->parent = dma_fence_get(job->done_fence);
787eaf01ee5SSarah Walker 		} else {
788eaf01ee5SSarah Walker 			/* If we had unfinished jobs, flag the entity as guilty so no
789eaf01ee5SSarah Walker 			 * new job can be submitted.
790eaf01ee5SSarah Walker 			 */
791eaf01ee5SSarah Walker 			atomic_set(&queue->ctx->faulty, 1);
792eaf01ee5SSarah Walker 		}
793eaf01ee5SSarah Walker 	}
794eaf01ee5SSarah Walker 
795b2ef8087SChristian König 	drm_sched_start(&queue->scheduler, 0);
796eaf01ee5SSarah Walker }
797eaf01ee5SSarah Walker 
798eaf01ee5SSarah Walker /**
799eaf01ee5SSarah Walker  * pvr_queue_timedout_job() - Handle a job timeout event.
800eaf01ee5SSarah Walker  * @s_job: The job this timeout occurred on.
801eaf01ee5SSarah Walker  *
802eaf01ee5SSarah Walker  * FIXME: We don't do anything here to unblock the situation, we just stop+start
803eaf01ee5SSarah Walker  * the scheduler, and re-assign parent fences in the middle.
804eaf01ee5SSarah Walker  *
805eaf01ee5SSarah Walker  * Return:
806eaf01ee5SSarah Walker  *  * DRM_GPU_SCHED_STAT_NOMINAL.
807eaf01ee5SSarah Walker  */
808eaf01ee5SSarah Walker static enum drm_gpu_sched_stat
pvr_queue_timedout_job(struct drm_sched_job * s_job)809eaf01ee5SSarah Walker pvr_queue_timedout_job(struct drm_sched_job *s_job)
810eaf01ee5SSarah Walker {
811eaf01ee5SSarah Walker 	struct drm_gpu_scheduler *sched = s_job->sched;
812eaf01ee5SSarah Walker 	struct pvr_queue *queue = container_of(sched, struct pvr_queue, scheduler);
813eaf01ee5SSarah Walker 	struct pvr_device *pvr_dev = queue->ctx->pvr_dev;
814eaf01ee5SSarah Walker 	struct pvr_job *job;
815eaf01ee5SSarah Walker 	u32 job_count = 0;
816eaf01ee5SSarah Walker 
817eaf01ee5SSarah Walker 	dev_err(sched->dev, "Job timeout\n");
818eaf01ee5SSarah Walker 
819eaf01ee5SSarah Walker 	/* Before we stop the scheduler, make sure the queue is out of any list, so
820eaf01ee5SSarah Walker 	 * any call to pvr_queue_update_active_state_locked() that might happen
821eaf01ee5SSarah Walker 	 * until the scheduler is really stopped doesn't end up re-inserting the
822eaf01ee5SSarah Walker 	 * queue in the active list. This would cause
823eaf01ee5SSarah Walker 	 * pvr_queue_signal_done_fences() and drm_sched_stop() to race with each
824eaf01ee5SSarah Walker 	 * other when accessing the pending_list, since drm_sched_stop() doesn't
825eaf01ee5SSarah Walker 	 * grab the job_list_lock when modifying the list (it's assuming the
826eaf01ee5SSarah Walker 	 * only other accessor is the scheduler, and it's safe to not grab the
827eaf01ee5SSarah Walker 	 * lock since it's stopped).
828eaf01ee5SSarah Walker 	 */
829eaf01ee5SSarah Walker 	mutex_lock(&pvr_dev->queues.lock);
830eaf01ee5SSarah Walker 	list_del_init(&queue->node);
831eaf01ee5SSarah Walker 	mutex_unlock(&pvr_dev->queues.lock);
832eaf01ee5SSarah Walker 
833eaf01ee5SSarah Walker 	drm_sched_stop(sched, s_job);
834eaf01ee5SSarah Walker 
835eaf01ee5SSarah Walker 	/* Re-assign job parent fences. */
836eaf01ee5SSarah Walker 	list_for_each_entry(job, &sched->pending_list, base.list) {
837eaf01ee5SSarah Walker 		job->base.s_fence->parent = dma_fence_get(job->done_fence);
838eaf01ee5SSarah Walker 		job_count++;
839eaf01ee5SSarah Walker 	}
840eaf01ee5SSarah Walker 	WARN_ON(atomic_read(&queue->in_flight_job_count) != job_count);
841eaf01ee5SSarah Walker 
842eaf01ee5SSarah Walker 	/* Re-insert the queue in the proper list, and kick a queue processing
843eaf01ee5SSarah Walker 	 * operation if there were jobs pending.
844eaf01ee5SSarah Walker 	 */
845eaf01ee5SSarah Walker 	mutex_lock(&pvr_dev->queues.lock);
846eaf01ee5SSarah Walker 	if (!job_count) {
847eaf01ee5SSarah Walker 		list_move_tail(&queue->node, &pvr_dev->queues.idle);
848eaf01ee5SSarah Walker 	} else {
849eaf01ee5SSarah Walker 		atomic_set(&queue->in_flight_job_count, job_count);
850eaf01ee5SSarah Walker 		list_move_tail(&queue->node, &pvr_dev->queues.active);
851eaf01ee5SSarah Walker 		pvr_queue_process(queue);
852eaf01ee5SSarah Walker 	}
853eaf01ee5SSarah Walker 	mutex_unlock(&pvr_dev->queues.lock);
854eaf01ee5SSarah Walker 
855b2ef8087SChristian König 	drm_sched_start(sched, 0);
856eaf01ee5SSarah Walker 
857eaf01ee5SSarah Walker 	return DRM_GPU_SCHED_STAT_NOMINAL;
858eaf01ee5SSarah Walker }
859eaf01ee5SSarah Walker 
860eaf01ee5SSarah Walker /**
861eaf01ee5SSarah Walker  * pvr_queue_free_job() - Release the reference the scheduler had on a job object.
862eaf01ee5SSarah Walker  * @sched_job: Job object to free.
863eaf01ee5SSarah Walker  */
pvr_queue_free_job(struct drm_sched_job * sched_job)864eaf01ee5SSarah Walker static void pvr_queue_free_job(struct drm_sched_job *sched_job)
865eaf01ee5SSarah Walker {
866eaf01ee5SSarah Walker 	struct pvr_job *job = container_of(sched_job, struct pvr_job, base);
867eaf01ee5SSarah Walker 
868eaf01ee5SSarah Walker 	drm_sched_job_cleanup(sched_job);
869*4ba2abe1SBrendan King 
870*4ba2abe1SBrendan King 	if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job)
871*4ba2abe1SBrendan King 		pvr_job_put(job->paired_job);
872*4ba2abe1SBrendan King 
873eaf01ee5SSarah Walker 	job->paired_job = NULL;
874eaf01ee5SSarah Walker 	pvr_job_put(job);
875eaf01ee5SSarah Walker }
876eaf01ee5SSarah Walker 
877eaf01ee5SSarah Walker static const struct drm_sched_backend_ops pvr_queue_sched_ops = {
878eaf01ee5SSarah Walker 	.prepare_job = pvr_queue_prepare_job,
879eaf01ee5SSarah Walker 	.run_job = pvr_queue_run_job,
880eaf01ee5SSarah Walker 	.timedout_job = pvr_queue_timedout_job,
881eaf01ee5SSarah Walker 	.free_job = pvr_queue_free_job,
882eaf01ee5SSarah Walker };
883eaf01ee5SSarah Walker 
884eaf01ee5SSarah Walker /**
885eaf01ee5SSarah Walker  * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO object
886eaf01ee5SSarah Walker  * @f: Fence to test.
887eaf01ee5SSarah Walker  *
888eaf01ee5SSarah Walker  * A UFO-backed fence is a fence that can be signaled or waited upon FW-side.
889eaf01ee5SSarah Walker  * pvr_job::done_fence objects are backed by the timeline UFO attached to the queue
890eaf01ee5SSarah Walker  * they are pushed to, but those fences are not directly exposed to the outside
891eaf01ee5SSarah Walker  * world, so we also need to check if the fence we're being passed is a
892eaf01ee5SSarah Walker  * drm_sched_fence that was coming from our driver.
893eaf01ee5SSarah Walker  */
pvr_queue_fence_is_ufo_backed(struct dma_fence * f)894eaf01ee5SSarah Walker bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
895eaf01ee5SSarah Walker {
896eaf01ee5SSarah Walker 	struct drm_sched_fence *sched_fence = f ? to_drm_sched_fence(f) : NULL;
897eaf01ee5SSarah Walker 
898eaf01ee5SSarah Walker 	if (sched_fence &&
899eaf01ee5SSarah Walker 	    sched_fence->sched->ops == &pvr_queue_sched_ops)
900eaf01ee5SSarah Walker 		return true;
901eaf01ee5SSarah Walker 
902eaf01ee5SSarah Walker 	if (f && f->ops == &pvr_queue_job_fence_ops)
903eaf01ee5SSarah Walker 		return true;
904eaf01ee5SSarah Walker 
905eaf01ee5SSarah Walker 	return false;
906eaf01ee5SSarah Walker }
907eaf01ee5SSarah Walker 
908eaf01ee5SSarah Walker /**
909eaf01ee5SSarah Walker  * pvr_queue_signal_done_fences() - Signal done fences.
910eaf01ee5SSarah Walker  * @queue: Queue to check.
911eaf01ee5SSarah Walker  *
912eaf01ee5SSarah Walker  * Signal done fences of jobs whose seqno is less than the current value of
913eaf01ee5SSarah Walker  * the UFO object attached to the queue.
914eaf01ee5SSarah Walker  */
915eaf01ee5SSarah Walker static void
pvr_queue_signal_done_fences(struct pvr_queue * queue)916eaf01ee5SSarah Walker pvr_queue_signal_done_fences(struct pvr_queue *queue)
917eaf01ee5SSarah Walker {
918eaf01ee5SSarah Walker 	struct pvr_job *job, *tmp_job;
919eaf01ee5SSarah Walker 	u32 cur_seqno;
920eaf01ee5SSarah Walker 
921eaf01ee5SSarah Walker 	spin_lock(&queue->scheduler.job_list_lock);
922eaf01ee5SSarah Walker 	cur_seqno = *queue->timeline_ufo.value;
923eaf01ee5SSarah Walker 	list_for_each_entry_safe(job, tmp_job, &queue->scheduler.pending_list, base.list) {
924eaf01ee5SSarah Walker 		if ((int)(cur_seqno - lower_32_bits(job->done_fence->seqno)) < 0)
925eaf01ee5SSarah Walker 			break;
926eaf01ee5SSarah Walker 
927eaf01ee5SSarah Walker 		if (!dma_fence_is_signaled(job->done_fence)) {
928eaf01ee5SSarah Walker 			dma_fence_signal(job->done_fence);
929eaf01ee5SSarah Walker 			pvr_job_release_pm_ref(job);
930eaf01ee5SSarah Walker 			atomic_dec(&queue->in_flight_job_count);
931eaf01ee5SSarah Walker 		}
932eaf01ee5SSarah Walker 	}
933eaf01ee5SSarah Walker 	spin_unlock(&queue->scheduler.job_list_lock);
934eaf01ee5SSarah Walker }
935eaf01ee5SSarah Walker 
936eaf01ee5SSarah Walker /**
937eaf01ee5SSarah Walker  * pvr_queue_check_job_waiting_for_cccb_space() - Check if the job waiting for CCCB space
938eaf01ee5SSarah Walker  * can be unblocked
939eaf01ee5SSarah Walker  * pushed to the CCCB
940eaf01ee5SSarah Walker  * @queue: Queue to check
941eaf01ee5SSarah Walker  *
942eaf01ee5SSarah Walker  * If we have a job waiting for CCCB, and this job now fits in the CCCB, we signal
943eaf01ee5SSarah Walker  * its CCCB fence, which should kick drm_sched.
944eaf01ee5SSarah Walker  */
945eaf01ee5SSarah Walker static void
pvr_queue_check_job_waiting_for_cccb_space(struct pvr_queue * queue)946eaf01ee5SSarah Walker pvr_queue_check_job_waiting_for_cccb_space(struct pvr_queue *queue)
947eaf01ee5SSarah Walker {
948eaf01ee5SSarah Walker 	struct pvr_queue_fence *cccb_fence;
949eaf01ee5SSarah Walker 	u32 native_deps_remaining;
950eaf01ee5SSarah Walker 	struct pvr_job *job;
951eaf01ee5SSarah Walker 
952eaf01ee5SSarah Walker 	mutex_lock(&queue->cccb_fence_ctx.job_lock);
953eaf01ee5SSarah Walker 	job = queue->cccb_fence_ctx.job;
954eaf01ee5SSarah Walker 	if (!job)
955eaf01ee5SSarah Walker 		goto out_unlock;
956eaf01ee5SSarah Walker 
957eaf01ee5SSarah Walker 	/* If we have a job attached to the CCCB fence context, its CCCB fence
958eaf01ee5SSarah Walker 	 * shouldn't be NULL.
959eaf01ee5SSarah Walker 	 */
960eaf01ee5SSarah Walker 	if (WARN_ON(!job->cccb_fence)) {
961eaf01ee5SSarah Walker 		job = NULL;
962eaf01ee5SSarah Walker 		goto out_unlock;
963eaf01ee5SSarah Walker 	}
964eaf01ee5SSarah Walker 
965eaf01ee5SSarah Walker 	/* If we get there, CCCB fence has to be initialized. */
966eaf01ee5SSarah Walker 	cccb_fence = container_of(job->cccb_fence, struct pvr_queue_fence, base);
967eaf01ee5SSarah Walker 	if (WARN_ON(!cccb_fence->queue)) {
968eaf01ee5SSarah Walker 		job = NULL;
969eaf01ee5SSarah Walker 		goto out_unlock;
970eaf01ee5SSarah Walker 	}
971eaf01ee5SSarah Walker 
972eaf01ee5SSarah Walker 	/* Evict signaled dependencies before checking for CCCB space.
973eaf01ee5SSarah Walker 	 * If the job fits, signal the CCCB fence, this should unblock
974eaf01ee5SSarah Walker 	 * the drm_sched_entity.
975eaf01ee5SSarah Walker 	 */
976eaf01ee5SSarah Walker 	native_deps_remaining = job_count_remaining_native_deps(job);
977eaf01ee5SSarah Walker 	if (!pvr_cccb_cmdseq_fits(&queue->cccb, job_cmds_size(job, native_deps_remaining))) {
978eaf01ee5SSarah Walker 		job = NULL;
979eaf01ee5SSarah Walker 		goto out_unlock;
980eaf01ee5SSarah Walker 	}
981eaf01ee5SSarah Walker 
982eaf01ee5SSarah Walker 	dma_fence_signal(job->cccb_fence);
983eaf01ee5SSarah Walker 	pvr_queue_fence_put(job->cccb_fence);
984eaf01ee5SSarah Walker 	job->cccb_fence = NULL;
985eaf01ee5SSarah Walker 	queue->cccb_fence_ctx.job = NULL;
986eaf01ee5SSarah Walker 
987eaf01ee5SSarah Walker out_unlock:
988eaf01ee5SSarah Walker 	mutex_unlock(&queue->cccb_fence_ctx.job_lock);
989eaf01ee5SSarah Walker 
990eaf01ee5SSarah Walker 	pvr_job_put(job);
991eaf01ee5SSarah Walker }
992eaf01ee5SSarah Walker 
993eaf01ee5SSarah Walker /**
994eaf01ee5SSarah Walker  * pvr_queue_process() - Process events that happened on a queue.
995eaf01ee5SSarah Walker  * @queue: Queue to check
996eaf01ee5SSarah Walker  *
997eaf01ee5SSarah Walker  * Signal job fences and check if jobs waiting for CCCB space can be unblocked.
998eaf01ee5SSarah Walker  */
pvr_queue_process(struct pvr_queue * queue)999eaf01ee5SSarah Walker void pvr_queue_process(struct pvr_queue *queue)
1000eaf01ee5SSarah Walker {
1001eaf01ee5SSarah Walker 	lockdep_assert_held(&queue->ctx->pvr_dev->queues.lock);
1002eaf01ee5SSarah Walker 
1003eaf01ee5SSarah Walker 	pvr_queue_check_job_waiting_for_cccb_space(queue);
1004eaf01ee5SSarah Walker 	pvr_queue_signal_done_fences(queue);
1005eaf01ee5SSarah Walker 	pvr_queue_update_active_state_locked(queue);
1006eaf01ee5SSarah Walker }
1007eaf01ee5SSarah Walker 
get_dm_type(struct pvr_queue * queue)1008eaf01ee5SSarah Walker static u32 get_dm_type(struct pvr_queue *queue)
1009eaf01ee5SSarah Walker {
1010eaf01ee5SSarah Walker 	switch (queue->type) {
1011eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_GEOMETRY:
1012eaf01ee5SSarah Walker 		return PVR_FWIF_DM_GEOM;
1013eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
1014eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_FRAGMENT:
1015eaf01ee5SSarah Walker 		return PVR_FWIF_DM_FRAG;
1016eaf01ee5SSarah Walker 	case DRM_PVR_JOB_TYPE_COMPUTE:
1017eaf01ee5SSarah Walker 		return PVR_FWIF_DM_CDM;
1018eaf01ee5SSarah Walker 	}
1019eaf01ee5SSarah Walker 
1020eaf01ee5SSarah Walker 	return ~0;
1021eaf01ee5SSarah Walker }
1022eaf01ee5SSarah Walker 
1023eaf01ee5SSarah Walker /**
1024eaf01ee5SSarah Walker  * init_fw_context() - Initializes the queue part of a FW context.
1025eaf01ee5SSarah Walker  * @queue: Queue object to initialize the FW context for.
1026eaf01ee5SSarah Walker  * @fw_ctx_map: The FW context CPU mapping.
1027eaf01ee5SSarah Walker  *
1028eaf01ee5SSarah Walker  * FW contexts are containing various states, one of them being a per-queue state
1029eaf01ee5SSarah Walker  * that needs to be initialized for each queue being exposed by a context. This
1030eaf01ee5SSarah Walker  * function takes care of that.
1031eaf01ee5SSarah Walker  */
init_fw_context(struct pvr_queue * queue,void * fw_ctx_map)1032eaf01ee5SSarah Walker static void init_fw_context(struct pvr_queue *queue, void *fw_ctx_map)
1033eaf01ee5SSarah Walker {
1034eaf01ee5SSarah Walker 	struct pvr_context *ctx = queue->ctx;
1035eaf01ee5SSarah Walker 	struct pvr_fw_object *fw_mem_ctx_obj = pvr_vm_get_fw_mem_context(ctx->vm_ctx);
1036eaf01ee5SSarah Walker 	struct rogue_fwif_fwcommoncontext *cctx_fw;
1037eaf01ee5SSarah Walker 	struct pvr_cccb *cccb = &queue->cccb;
1038eaf01ee5SSarah Walker 
1039eaf01ee5SSarah Walker 	cctx_fw = fw_ctx_map + queue->ctx_offset;
1040eaf01ee5SSarah Walker 	cctx_fw->ccbctl_fw_addr = cccb->ctrl_fw_addr;
1041eaf01ee5SSarah Walker 	cctx_fw->ccb_fw_addr = cccb->cccb_fw_addr;
1042eaf01ee5SSarah Walker 
1043eaf01ee5SSarah Walker 	cctx_fw->dm = get_dm_type(queue);
1044eaf01ee5SSarah Walker 	cctx_fw->priority = ctx->priority;
1045eaf01ee5SSarah Walker 	cctx_fw->priority_seq_num = 0;
1046eaf01ee5SSarah Walker 	cctx_fw->max_deadline_ms = MAX_DEADLINE_MS;
1047eaf01ee5SSarah Walker 	cctx_fw->pid = task_tgid_nr(current);
1048eaf01ee5SSarah Walker 	cctx_fw->server_common_context_id = ctx->ctx_id;
1049eaf01ee5SSarah Walker 
1050eaf01ee5SSarah Walker 	pvr_fw_object_get_fw_addr(fw_mem_ctx_obj, &cctx_fw->fw_mem_context_fw_addr);
1051eaf01ee5SSarah Walker 
1052eaf01ee5SSarah Walker 	pvr_fw_object_get_fw_addr(queue->reg_state_obj, &cctx_fw->context_state_addr);
1053eaf01ee5SSarah Walker }
1054eaf01ee5SSarah Walker 
1055eaf01ee5SSarah Walker /**
1056eaf01ee5SSarah Walker  * pvr_queue_cleanup_fw_context() - Wait for the FW context to be idle and clean it up.
1057eaf01ee5SSarah Walker  * @queue: Queue on FW context to clean up.
1058eaf01ee5SSarah Walker  *
1059eaf01ee5SSarah Walker  * Return:
1060eaf01ee5SSarah Walker  *  * 0 on success,
1061eaf01ee5SSarah Walker  *  * Any error returned by pvr_fw_structure_cleanup() otherwise.
1062eaf01ee5SSarah Walker  */
pvr_queue_cleanup_fw_context(struct pvr_queue * queue)1063eaf01ee5SSarah Walker static int pvr_queue_cleanup_fw_context(struct pvr_queue *queue)
1064eaf01ee5SSarah Walker {
1065eaf01ee5SSarah Walker 	if (!queue->ctx->fw_obj)
1066eaf01ee5SSarah Walker 		return 0;
1067eaf01ee5SSarah Walker 
1068eaf01ee5SSarah Walker 	return pvr_fw_structure_cleanup(queue->ctx->pvr_dev,
1069eaf01ee5SSarah Walker 					ROGUE_FWIF_CLEANUP_FWCOMMONCONTEXT,
1070eaf01ee5SSarah Walker 					queue->ctx->fw_obj, queue->ctx_offset);
1071eaf01ee5SSarah Walker }
1072eaf01ee5SSarah Walker 
1073eaf01ee5SSarah Walker /**
1074eaf01ee5SSarah Walker  * pvr_queue_job_init() - Initialize queue related fields in a pvr_job object.
1075eaf01ee5SSarah Walker  * @job: The job to initialize.
1076eaf01ee5SSarah Walker  *
1077eaf01ee5SSarah Walker  * Bind the job to a queue and allocate memory to guarantee pvr_queue_job_arm()
1078eaf01ee5SSarah Walker  * and pvr_queue_job_push() can't fail. We also make sure the context type is
1079eaf01ee5SSarah Walker  * valid and the job can fit in the CCCB.
1080eaf01ee5SSarah Walker  *
1081eaf01ee5SSarah Walker  * Return:
1082eaf01ee5SSarah Walker  *  * 0 on success, or
1083eaf01ee5SSarah Walker  *  * An error code if something failed.
1084eaf01ee5SSarah Walker  */
pvr_queue_job_init(struct pvr_job * job)1085eaf01ee5SSarah Walker int pvr_queue_job_init(struct pvr_job *job)
1086eaf01ee5SSarah Walker {
1087eaf01ee5SSarah Walker 	/* Fragment jobs need at least one native fence wait on the geometry job fence. */
1088eaf01ee5SSarah Walker 	u32 min_native_dep_count = job->type == DRM_PVR_JOB_TYPE_FRAGMENT ? 1 : 0;
1089eaf01ee5SSarah Walker 	struct pvr_queue *queue;
1090eaf01ee5SSarah Walker 	int err;
1091eaf01ee5SSarah Walker 
1092eaf01ee5SSarah Walker 	if (atomic_read(&job->ctx->faulty))
1093eaf01ee5SSarah Walker 		return -EIO;
1094eaf01ee5SSarah Walker 
1095eaf01ee5SSarah Walker 	queue = pvr_context_get_queue_for_job(job->ctx, job->type);
1096eaf01ee5SSarah Walker 	if (!queue)
1097eaf01ee5SSarah Walker 		return -EINVAL;
1098eaf01ee5SSarah Walker 
1099eaf01ee5SSarah Walker 	if (!pvr_cccb_cmdseq_can_fit(&queue->cccb, job_cmds_size(job, min_native_dep_count)))
1100eaf01ee5SSarah Walker 		return -E2BIG;
1101eaf01ee5SSarah Walker 
1102eaf01ee5SSarah Walker 	err = drm_sched_job_init(&job->base, &queue->entity, 1, THIS_MODULE);
1103eaf01ee5SSarah Walker 	if (err)
1104eaf01ee5SSarah Walker 		return err;
1105eaf01ee5SSarah Walker 
1106eaf01ee5SSarah Walker 	job->cccb_fence = pvr_queue_fence_alloc();
1107eaf01ee5SSarah Walker 	job->kccb_fence = pvr_kccb_fence_alloc();
1108eaf01ee5SSarah Walker 	job->done_fence = pvr_queue_fence_alloc();
1109eaf01ee5SSarah Walker 	if (!job->cccb_fence || !job->kccb_fence || !job->done_fence)
1110eaf01ee5SSarah Walker 		return -ENOMEM;
1111eaf01ee5SSarah Walker 
1112eaf01ee5SSarah Walker 	return 0;
1113eaf01ee5SSarah Walker }
1114eaf01ee5SSarah Walker 
1115eaf01ee5SSarah Walker /**
1116eaf01ee5SSarah Walker  * pvr_queue_job_arm() - Arm a job object.
1117eaf01ee5SSarah Walker  * @job: The job to arm.
1118eaf01ee5SSarah Walker  *
1119eaf01ee5SSarah Walker  * Initializes fences and return the drm_sched finished fence so it can
1120eaf01ee5SSarah Walker  * be exposed to the outside world. Once this function is called, you should
1121eaf01ee5SSarah Walker  * make sure the job is pushed using pvr_queue_job_push(), or guarantee that
1122eaf01ee5SSarah Walker  * no one grabbed a reference to the returned fence. The latter can happen if
1123eaf01ee5SSarah Walker  * we do multi-job submission, and something failed when creating/initializing
1124eaf01ee5SSarah Walker  * a job. In that case, we know the fence didn't leave the driver, and we
1125eaf01ee5SSarah Walker  * can thus guarantee nobody will wait on an dead fence object.
1126eaf01ee5SSarah Walker  *
1127eaf01ee5SSarah Walker  * Return:
1128eaf01ee5SSarah Walker  *  * A dma_fence object.
1129eaf01ee5SSarah Walker  */
pvr_queue_job_arm(struct pvr_job * job)1130eaf01ee5SSarah Walker struct dma_fence *pvr_queue_job_arm(struct pvr_job *job)
1131eaf01ee5SSarah Walker {
1132eaf01ee5SSarah Walker 	drm_sched_job_arm(&job->base);
1133eaf01ee5SSarah Walker 
1134eaf01ee5SSarah Walker 	return &job->base.s_fence->finished;
1135eaf01ee5SSarah Walker }
1136eaf01ee5SSarah Walker 
1137eaf01ee5SSarah Walker /**
1138eaf01ee5SSarah Walker  * pvr_queue_job_cleanup() - Cleanup fence/scheduler related fields in the job object.
1139eaf01ee5SSarah Walker  * @job: The job to cleanup.
1140eaf01ee5SSarah Walker  *
1141eaf01ee5SSarah Walker  * Should be called in the job release path.
1142eaf01ee5SSarah Walker  */
pvr_queue_job_cleanup(struct pvr_job * job)1143eaf01ee5SSarah Walker void pvr_queue_job_cleanup(struct pvr_job *job)
1144eaf01ee5SSarah Walker {
1145eaf01ee5SSarah Walker 	pvr_queue_fence_put(job->done_fence);
1146eaf01ee5SSarah Walker 	pvr_queue_fence_put(job->cccb_fence);
1147eaf01ee5SSarah Walker 	pvr_kccb_fence_put(job->kccb_fence);
1148eaf01ee5SSarah Walker 
1149eaf01ee5SSarah Walker 	if (job->base.s_fence)
1150eaf01ee5SSarah Walker 		drm_sched_job_cleanup(&job->base);
1151eaf01ee5SSarah Walker }
1152eaf01ee5SSarah Walker 
1153eaf01ee5SSarah Walker /**
1154eaf01ee5SSarah Walker  * pvr_queue_job_push() - Push a job to its queue.
1155eaf01ee5SSarah Walker  * @job: The job to push.
1156eaf01ee5SSarah Walker  *
1157eaf01ee5SSarah Walker  * Must be called after pvr_queue_job_init() and after all dependencies
1158eaf01ee5SSarah Walker  * have been added to the job. This will effectively queue the job to
1159eaf01ee5SSarah Walker  * the drm_sched_entity attached to the queue. We grab a reference on
1160eaf01ee5SSarah Walker  * the job object, so the caller is free to drop its reference when it's
1161eaf01ee5SSarah Walker  * done accessing the job object.
1162eaf01ee5SSarah Walker  */
pvr_queue_job_push(struct pvr_job * job)1163eaf01ee5SSarah Walker void pvr_queue_job_push(struct pvr_job *job)
1164eaf01ee5SSarah Walker {
1165eaf01ee5SSarah Walker 	struct pvr_queue *queue = container_of(job->base.sched, struct pvr_queue, scheduler);
1166eaf01ee5SSarah Walker 
1167eaf01ee5SSarah Walker 	/* Keep track of the last queued job scheduled fence for combined submit. */
1168eaf01ee5SSarah Walker 	dma_fence_put(queue->last_queued_job_scheduled_fence);
1169eaf01ee5SSarah Walker 	queue->last_queued_job_scheduled_fence = dma_fence_get(&job->base.s_fence->scheduled);
1170eaf01ee5SSarah Walker 
1171eaf01ee5SSarah Walker 	pvr_job_get(job);
1172eaf01ee5SSarah Walker 	drm_sched_entity_push_job(&job->base);
1173eaf01ee5SSarah Walker }
1174eaf01ee5SSarah Walker 
reg_state_init(void * cpu_ptr,void * priv)1175eaf01ee5SSarah Walker static void reg_state_init(void *cpu_ptr, void *priv)
1176eaf01ee5SSarah Walker {
1177eaf01ee5SSarah Walker 	struct pvr_queue *queue = priv;
1178eaf01ee5SSarah Walker 
1179eaf01ee5SSarah Walker 	if (queue->type == DRM_PVR_JOB_TYPE_GEOMETRY) {
1180eaf01ee5SSarah Walker 		struct rogue_fwif_geom_ctx_state *geom_ctx_state_fw = cpu_ptr;
1181eaf01ee5SSarah Walker 
1182eaf01ee5SSarah Walker 		geom_ctx_state_fw->geom_core[0].geom_reg_vdm_call_stack_pointer_init =
1183eaf01ee5SSarah Walker 			queue->callstack_addr;
1184eaf01ee5SSarah Walker 	}
1185eaf01ee5SSarah Walker }
1186eaf01ee5SSarah Walker 
1187eaf01ee5SSarah Walker /**
1188eaf01ee5SSarah Walker  * pvr_queue_create() - Create a queue object.
1189eaf01ee5SSarah Walker  * @ctx: The context this queue will be attached to.
1190eaf01ee5SSarah Walker  * @type: The type of jobs being pushed to this queue.
1191eaf01ee5SSarah Walker  * @args: The arguments passed to the context creation function.
1192eaf01ee5SSarah Walker  * @fw_ctx_map: CPU mapping of the FW context object.
1193eaf01ee5SSarah Walker  *
1194eaf01ee5SSarah Walker  * Create a queue object that will be used to queue and track jobs.
1195eaf01ee5SSarah Walker  *
1196eaf01ee5SSarah Walker  * Return:
1197eaf01ee5SSarah Walker  *  * A valid pointer to a pvr_queue object, or
1198eaf01ee5SSarah Walker  *  * An error pointer if the creation/initialization failed.
1199eaf01ee5SSarah Walker  */
pvr_queue_create(struct pvr_context * ctx,enum drm_pvr_job_type type,struct drm_pvr_ioctl_create_context_args * args,void * fw_ctx_map)1200eaf01ee5SSarah Walker struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
1201eaf01ee5SSarah Walker 				   enum drm_pvr_job_type type,
1202eaf01ee5SSarah Walker 				   struct drm_pvr_ioctl_create_context_args *args,
1203eaf01ee5SSarah Walker 				   void *fw_ctx_map)
1204eaf01ee5SSarah Walker {
1205eaf01ee5SSarah Walker 	static const struct {
1206eaf01ee5SSarah Walker 		u32 cccb_size;
1207eaf01ee5SSarah Walker 		const char *name;
1208eaf01ee5SSarah Walker 	} props[] = {
1209eaf01ee5SSarah Walker 		[DRM_PVR_JOB_TYPE_GEOMETRY] = {
1210eaf01ee5SSarah Walker 			.cccb_size = CTX_GEOM_CCCB_SIZE_LOG2,
1211eaf01ee5SSarah Walker 			.name = "geometry",
1212eaf01ee5SSarah Walker 		},
1213eaf01ee5SSarah Walker 		[DRM_PVR_JOB_TYPE_FRAGMENT] = {
1214eaf01ee5SSarah Walker 			.cccb_size = CTX_FRAG_CCCB_SIZE_LOG2,
1215eaf01ee5SSarah Walker 			.name = "fragment"
1216eaf01ee5SSarah Walker 		},
1217eaf01ee5SSarah Walker 		[DRM_PVR_JOB_TYPE_COMPUTE] = {
1218eaf01ee5SSarah Walker 			.cccb_size = CTX_COMPUTE_CCCB_SIZE_LOG2,
1219eaf01ee5SSarah Walker 			.name = "compute"
1220eaf01ee5SSarah Walker 		},
1221eaf01ee5SSarah Walker 		[DRM_PVR_JOB_TYPE_TRANSFER_FRAG] = {
1222eaf01ee5SSarah Walker 			.cccb_size = CTX_TRANSFER_CCCB_SIZE_LOG2,
1223eaf01ee5SSarah Walker 			.name = "transfer_frag"
1224eaf01ee5SSarah Walker 		},
1225eaf01ee5SSarah Walker 	};
1226eaf01ee5SSarah Walker 	struct pvr_device *pvr_dev = ctx->pvr_dev;
1227eaf01ee5SSarah Walker 	const struct drm_sched_init_args sched_args = {
1228eaf01ee5SSarah Walker 		.ops = &pvr_queue_sched_ops,
1229eaf01ee5SSarah Walker 		.submit_wq = pvr_dev->sched_wq,
1230eaf01ee5SSarah Walker 		.num_rqs = 1,
1231eaf01ee5SSarah Walker 		.credit_limit = 64 * 1024,
1232eaf01ee5SSarah Walker 		.hang_limit = 1,
1233eaf01ee5SSarah Walker 		.timeout = msecs_to_jiffies(500),
1234eaf01ee5SSarah Walker 		.timeout_wq = pvr_dev->sched_wq,
1235eaf01ee5SSarah Walker 		.name = "pvr-queue",
1236eaf01ee5SSarah Walker 		.dev = pvr_dev->base.dev,
1237eaf01ee5SSarah Walker 	};
1238eaf01ee5SSarah Walker 	struct drm_gpu_scheduler *sched;
1239eaf01ee5SSarah Walker 	struct pvr_queue *queue;
1240eaf01ee5SSarah Walker 	int ctx_state_size, err;
1241eaf01ee5SSarah Walker 	void *cpu_map;
1242eaf01ee5SSarah Walker 
1243eaf01ee5SSarah Walker 	if (WARN_ON(type >= sizeof(props)))
1244eaf01ee5SSarah Walker 		return ERR_PTR(-EINVAL);
1245eaf01ee5SSarah Walker 
1246eaf01ee5SSarah Walker 	switch (ctx->type) {
1247eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_RENDER:
1248eaf01ee5SSarah Walker 		if (type != DRM_PVR_JOB_TYPE_GEOMETRY &&
1249eaf01ee5SSarah Walker 		    type != DRM_PVR_JOB_TYPE_FRAGMENT)
1250eaf01ee5SSarah Walker 			return ERR_PTR(-EINVAL);
1251eaf01ee5SSarah Walker 		break;
1252eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_COMPUTE:
1253eaf01ee5SSarah Walker 		if (type != DRM_PVR_JOB_TYPE_COMPUTE)
1254eaf01ee5SSarah Walker 			return ERR_PTR(-EINVAL);
1255eaf01ee5SSarah Walker 		break;
1256eaf01ee5SSarah Walker 	case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
1257eaf01ee5SSarah Walker 		if (type != DRM_PVR_JOB_TYPE_TRANSFER_FRAG)
1258eaf01ee5SSarah Walker 			return ERR_PTR(-EINVAL);
1259eaf01ee5SSarah Walker 		break;
1260eaf01ee5SSarah Walker 	default:
1261eaf01ee5SSarah Walker 		return ERR_PTR(-EINVAL);
1262eaf01ee5SSarah Walker 	}
1263eaf01ee5SSarah Walker 
1264eaf01ee5SSarah Walker 	ctx_state_size = get_ctx_state_size(pvr_dev, type);
1265eaf01ee5SSarah Walker 	if (ctx_state_size < 0)
1266eaf01ee5SSarah Walker 		return ERR_PTR(ctx_state_size);
1267eaf01ee5SSarah Walker 
1268eaf01ee5SSarah Walker 	queue = kzalloc(sizeof(*queue), GFP_KERNEL);
1269eaf01ee5SSarah Walker 	if (!queue)
1270eaf01ee5SSarah Walker 		return ERR_PTR(-ENOMEM);
1271eaf01ee5SSarah Walker 
1272eaf01ee5SSarah Walker 	queue->type = type;
1273eaf01ee5SSarah Walker 	queue->ctx_offset = get_ctx_offset(type);
1274eaf01ee5SSarah Walker 	queue->ctx = ctx;
1275eaf01ee5SSarah Walker 	queue->callstack_addr = args->callstack_addr;
1276eaf01ee5SSarah Walker 	sched = &queue->scheduler;
1277eaf01ee5SSarah Walker 	INIT_LIST_HEAD(&queue->node);
1278eaf01ee5SSarah Walker 	mutex_init(&queue->cccb_fence_ctx.job_lock);
1279eaf01ee5SSarah Walker 	pvr_queue_fence_ctx_init(&queue->cccb_fence_ctx.base);
1280eaf01ee5SSarah Walker 	pvr_queue_fence_ctx_init(&queue->job_fence_ctx);
1281eaf01ee5SSarah Walker 
1282eaf01ee5SSarah Walker 	err = pvr_cccb_init(pvr_dev, &queue->cccb, props[type].cccb_size, props[type].name);
1283eaf01ee5SSarah Walker 	if (err)
1284eaf01ee5SSarah Walker 		goto err_free_queue;
1285eaf01ee5SSarah Walker 
1286eaf01ee5SSarah Walker 	err = pvr_fw_object_create(pvr_dev, ctx_state_size,
1287eaf01ee5SSarah Walker 				   PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
1288eaf01ee5SSarah Walker 				   reg_state_init, queue, &queue->reg_state_obj);
1289eaf01ee5SSarah Walker 	if (err)
1290eaf01ee5SSarah Walker 		goto err_cccb_fini;
1291eaf01ee5SSarah Walker 
1292eaf01ee5SSarah Walker 	init_fw_context(queue, fw_ctx_map);
1293eaf01ee5SSarah Walker 
1294eaf01ee5SSarah Walker 	if (type != DRM_PVR_JOB_TYPE_GEOMETRY && type != DRM_PVR_JOB_TYPE_FRAGMENT &&
1295eaf01ee5SSarah Walker 	    args->callstack_addr) {
1296eaf01ee5SSarah Walker 		err = -EINVAL;
1297eaf01ee5SSarah Walker 		goto err_release_reg_state;
1298eaf01ee5SSarah Walker 	}
1299eaf01ee5SSarah Walker 
1300eaf01ee5SSarah Walker 	cpu_map = pvr_fw_object_create_and_map(pvr_dev, sizeof(*queue->timeline_ufo.value),
1301eaf01ee5SSarah Walker 					       PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
1302eaf01ee5SSarah Walker 					       NULL, NULL, &queue->timeline_ufo.fw_obj);
1303eaf01ee5SSarah Walker 	if (IS_ERR(cpu_map)) {
1304eaf01ee5SSarah Walker 		err = PTR_ERR(cpu_map);
1305eaf01ee5SSarah Walker 		goto err_release_reg_state;
1306eaf01ee5SSarah Walker 	}
1307eaf01ee5SSarah Walker 
1308eaf01ee5SSarah Walker 	queue->timeline_ufo.value = cpu_map;
130919b4c60cSLuben Tuikov 
1310eaf01ee5SSarah Walker 	err = drm_sched_init(&queue->scheduler, &sched_args);
1311eaf01ee5SSarah Walker 	if (err)
1312eaf01ee5SSarah Walker 		goto err_release_ufo;
1313eaf01ee5SSarah Walker 
1314eaf01ee5SSarah Walker 	err = drm_sched_entity_init(&queue->entity,
1315eaf01ee5SSarah Walker 				    DRM_SCHED_PRIORITY_KERNEL,
1316eaf01ee5SSarah Walker 				    &sched, 1, &ctx->faulty);
1317eaf01ee5SSarah Walker 	if (err)
1318eaf01ee5SSarah Walker 		goto err_sched_fini;
1319eaf01ee5SSarah Walker 
1320eaf01ee5SSarah Walker 	mutex_lock(&pvr_dev->queues.lock);
1321eaf01ee5SSarah Walker 	list_add_tail(&queue->node, &pvr_dev->queues.idle);
1322eaf01ee5SSarah Walker 	mutex_unlock(&pvr_dev->queues.lock);
1323eaf01ee5SSarah Walker 
1324eaf01ee5SSarah Walker 	return queue;
1325eaf01ee5SSarah Walker 
1326eaf01ee5SSarah Walker err_sched_fini:
1327eaf01ee5SSarah Walker 	drm_sched_fini(&queue->scheduler);
1328eaf01ee5SSarah Walker 
1329eaf01ee5SSarah Walker err_release_ufo:
1330eaf01ee5SSarah Walker 	pvr_fw_object_unmap_and_destroy(queue->timeline_ufo.fw_obj);
1331eaf01ee5SSarah Walker 
1332eaf01ee5SSarah Walker err_release_reg_state:
1333eaf01ee5SSarah Walker 	pvr_fw_object_destroy(queue->reg_state_obj);
1334eaf01ee5SSarah Walker 
1335eaf01ee5SSarah Walker err_cccb_fini:
1336eaf01ee5SSarah Walker 	pvr_cccb_fini(&queue->cccb);
1337eaf01ee5SSarah Walker 
1338eaf01ee5SSarah Walker err_free_queue:
1339eaf01ee5SSarah Walker 	mutex_destroy(&queue->cccb_fence_ctx.job_lock);
1340eaf01ee5SSarah Walker 	kfree(queue);
1341eaf01ee5SSarah Walker 
1342eaf01ee5SSarah Walker 	return ERR_PTR(err);
1343eaf01ee5SSarah Walker }
1344eaf01ee5SSarah Walker 
pvr_queue_device_pre_reset(struct pvr_device * pvr_dev)1345eaf01ee5SSarah Walker void pvr_queue_device_pre_reset(struct pvr_device *pvr_dev)
1346eaf01ee5SSarah Walker {
1347eaf01ee5SSarah Walker 	struct pvr_queue *queue;
1348eaf01ee5SSarah Walker 
1349eaf01ee5SSarah Walker 	mutex_lock(&pvr_dev->queues.lock);
1350eaf01ee5SSarah Walker 	list_for_each_entry(queue, &pvr_dev->queues.idle, node)
1351eaf01ee5SSarah Walker 		pvr_queue_stop(queue, NULL);
1352eaf01ee5SSarah Walker 	list_for_each_entry(queue, &pvr_dev->queues.active, node)
1353eaf01ee5SSarah Walker 		pvr_queue_stop(queue, NULL);
1354eaf01ee5SSarah Walker 	mutex_unlock(&pvr_dev->queues.lock);
1355eaf01ee5SSarah Walker }
1356eaf01ee5SSarah Walker 
pvr_queue_device_post_reset(struct pvr_device * pvr_dev)1357eaf01ee5SSarah Walker void pvr_queue_device_post_reset(struct pvr_device *pvr_dev)
1358eaf01ee5SSarah Walker {
1359eaf01ee5SSarah Walker 	struct pvr_queue *queue;
1360eaf01ee5SSarah Walker 
1361eaf01ee5SSarah Walker 	mutex_lock(&pvr_dev->queues.lock);
1362eaf01ee5SSarah Walker 	list_for_each_entry(queue, &pvr_dev->queues.active, node)
1363eaf01ee5SSarah Walker 		pvr_queue_start(queue);
1364eaf01ee5SSarah Walker 	list_for_each_entry(queue, &pvr_dev->queues.idle, node)
1365eaf01ee5SSarah Walker 		pvr_queue_start(queue);
1366eaf01ee5SSarah Walker 	mutex_unlock(&pvr_dev->queues.lock);
1367eaf01ee5SSarah Walker }
1368eaf01ee5SSarah Walker 
1369eaf01ee5SSarah Walker /**
1370eaf01ee5SSarah Walker  * pvr_queue_kill() - Kill a queue.
1371eaf01ee5SSarah Walker  * @queue: The queue to kill.
1372eaf01ee5SSarah Walker  *
1373eaf01ee5SSarah Walker  * Kill the queue so no new jobs can be pushed. Should be called when the
1374eaf01ee5SSarah Walker  * context handle is destroyed. The queue object might last longer if jobs
1375eaf01ee5SSarah Walker  * are still in flight and holding a reference to the context this queue
1376eaf01ee5SSarah Walker  * belongs to.
1377eaf01ee5SSarah Walker  */
pvr_queue_kill(struct pvr_queue * queue)1378eaf01ee5SSarah Walker void pvr_queue_kill(struct pvr_queue *queue)
1379eaf01ee5SSarah Walker {
1380eaf01ee5SSarah Walker 	drm_sched_entity_destroy(&queue->entity);
1381eaf01ee5SSarah Walker 	dma_fence_put(queue->last_queued_job_scheduled_fence);
1382eaf01ee5SSarah Walker 	queue->last_queued_job_scheduled_fence = NULL;
1383eaf01ee5SSarah Walker }
1384eaf01ee5SSarah Walker 
1385eaf01ee5SSarah Walker /**
1386eaf01ee5SSarah Walker  * pvr_queue_destroy() - Destroy a queue.
1387eaf01ee5SSarah Walker  * @queue: The queue to destroy.
1388eaf01ee5SSarah Walker  *
1389eaf01ee5SSarah Walker  * Cleanup the queue and free the resources attached to it. Should be
1390eaf01ee5SSarah Walker  * called from the context release function.
1391eaf01ee5SSarah Walker  */
pvr_queue_destroy(struct pvr_queue * queue)1392eaf01ee5SSarah Walker void pvr_queue_destroy(struct pvr_queue *queue)
1393eaf01ee5SSarah Walker {
1394eaf01ee5SSarah Walker 	if (!queue)
1395eaf01ee5SSarah Walker 		return;
1396eaf01ee5SSarah Walker 
1397eaf01ee5SSarah Walker 	mutex_lock(&queue->ctx->pvr_dev->queues.lock);
1398eaf01ee5SSarah Walker 	list_del_init(&queue->node);
1399eaf01ee5SSarah Walker 	mutex_unlock(&queue->ctx->pvr_dev->queues.lock);
1400eaf01ee5SSarah Walker 
1401eaf01ee5SSarah Walker 	drm_sched_fini(&queue->scheduler);
1402eaf01ee5SSarah Walker 	drm_sched_entity_fini(&queue->entity);
1403eaf01ee5SSarah Walker 
1404eaf01ee5SSarah Walker 	if (WARN_ON(queue->last_queued_job_scheduled_fence))
1405eaf01ee5SSarah Walker 		dma_fence_put(queue->last_queued_job_scheduled_fence);
1406eaf01ee5SSarah Walker 
1407eaf01ee5SSarah Walker 	pvr_queue_cleanup_fw_context(queue);
1408eaf01ee5SSarah Walker 
1409eaf01ee5SSarah Walker 	pvr_fw_object_unmap_and_destroy(queue->timeline_ufo.fw_obj);
1410eaf01ee5SSarah Walker 	pvr_fw_object_destroy(queue->reg_state_obj);
1411eaf01ee5SSarah Walker 	pvr_cccb_fini(&queue->cccb);
1412eaf01ee5SSarah Walker 	mutex_destroy(&queue->cccb_fence_ctx.job_lock);
1413eaf01ee5SSarah Walker 	kfree(queue);
1414eaf01ee5SSarah Walker }
1415eaf01ee5SSarah Walker 
1416eaf01ee5SSarah Walker /**
1417eaf01ee5SSarah Walker  * pvr_queue_device_init() - Device-level initialization of queue related fields.
1418eaf01ee5SSarah Walker  * @pvr_dev: The device to initialize.
1419eaf01ee5SSarah Walker  *
1420eaf01ee5SSarah Walker  * Initializes all fields related to queue management in pvr_device.
1421eaf01ee5SSarah Walker  *
1422eaf01ee5SSarah Walker  * Return:
1423eaf01ee5SSarah Walker  *  * 0 on success, or
1424eaf01ee5SSarah Walker  *  * An error code on failure.
1425eaf01ee5SSarah Walker  */
pvr_queue_device_init(struct pvr_device * pvr_dev)1426eaf01ee5SSarah Walker int pvr_queue_device_init(struct pvr_device *pvr_dev)
1427eaf01ee5SSarah Walker {
1428eaf01ee5SSarah Walker 	int err;
1429eaf01ee5SSarah Walker 
1430eaf01ee5SSarah Walker 	INIT_LIST_HEAD(&pvr_dev->queues.active);
1431eaf01ee5SSarah Walker 	INIT_LIST_HEAD(&pvr_dev->queues.idle);
1432eaf01ee5SSarah Walker 	err = drmm_mutex_init(from_pvr_device(pvr_dev), &pvr_dev->queues.lock);
1433eaf01ee5SSarah Walker 	if (err)
1434eaf01ee5SSarah Walker 		return err;
1435eaf01ee5SSarah Walker 
1436eaf01ee5SSarah Walker 	pvr_dev->sched_wq = alloc_workqueue("powervr-sched", WQ_UNBOUND, 0);
1437eaf01ee5SSarah Walker 	if (!pvr_dev->sched_wq)
1438eaf01ee5SSarah Walker 		return -ENOMEM;
1439eaf01ee5SSarah Walker 
1440eaf01ee5SSarah Walker 	return 0;
1441eaf01ee5SSarah Walker }
1442eaf01ee5SSarah Walker 
1443eaf01ee5SSarah Walker /**
1444eaf01ee5SSarah Walker  * pvr_queue_device_fini() - Device-level cleanup of queue related fields.
1445eaf01ee5SSarah Walker  * @pvr_dev: The device to cleanup.
1446eaf01ee5SSarah Walker  *
1447  * Cleanup/free all queue-related resources attached to a pvr_device object.
1448  */
pvr_queue_device_fini(struct pvr_device * pvr_dev)1449 void pvr_queue_device_fini(struct pvr_device *pvr_dev)
1450 {
1451 	destroy_workqueue(pvr_dev->sched_wq);
1452 }
1453