1e93b6deeSLucas Stach /*
2e93b6deeSLucas Stach  * Copyright (C) 2017 Etnaviv Project
3e93b6deeSLucas Stach  *
4e93b6deeSLucas Stach  * This program is free software; you can redistribute it and/or modify it
5e93b6deeSLucas Stach  * under the terms of the GNU General Public License version 2 as published by
6e93b6deeSLucas Stach  * the Free Software Foundation.
7e93b6deeSLucas Stach  *
8e93b6deeSLucas Stach  * This program is distributed in the hope that it will be useful, but WITHOUT
9e93b6deeSLucas Stach  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10e93b6deeSLucas Stach  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11e93b6deeSLucas Stach  * more details.
12e93b6deeSLucas Stach  *
13e93b6deeSLucas Stach  * You should have received a copy of the GNU General Public License along with
14e93b6deeSLucas Stach  * this program.  If not, see <http://www.gnu.org/licenses/>.
15e93b6deeSLucas Stach  */
16e93b6deeSLucas Stach 
17e93b6deeSLucas Stach #include <drm/gpu_scheduler.h>
18e93b6deeSLucas Stach #include <linux/kthread.h>
19e93b6deeSLucas Stach 
20e93b6deeSLucas Stach #include "etnaviv_drv.h"
21e93b6deeSLucas Stach #include "etnaviv_gem.h"
22e93b6deeSLucas Stach #include "etnaviv_gpu.h"
23e93b6deeSLucas Stach 
24e93b6deeSLucas Stach static int etnaviv_job_hang_limit = 0;
25e93b6deeSLucas Stach module_param_named(job_hang_limit, etnaviv_job_hang_limit, int , 0444);
26e93b6deeSLucas Stach static int etnaviv_hw_jobs_limit = 2;
27e93b6deeSLucas Stach module_param_named(hw_job_limit, etnaviv_hw_jobs_limit, int , 0444);
28e93b6deeSLucas Stach 
29e93b6deeSLucas Stach static inline
30e93b6deeSLucas Stach struct etnaviv_gem_submit *to_etnaviv_submit(struct drm_sched_job *sched_job)
31e93b6deeSLucas Stach {
32e93b6deeSLucas Stach 	return container_of(sched_job, struct etnaviv_gem_submit, sched_job);
33e93b6deeSLucas Stach }
34e93b6deeSLucas Stach 
35e93b6deeSLucas Stach struct dma_fence *etnaviv_sched_dependency(struct drm_sched_job *sched_job,
36e93b6deeSLucas Stach 					   struct drm_sched_entity *entity)
37e93b6deeSLucas Stach {
38683da226SLucas Stach 	struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job);
39683da226SLucas Stach 	struct dma_fence *fence;
40683da226SLucas Stach 	int i;
41683da226SLucas Stach 
42683da226SLucas Stach 	if (unlikely(submit->in_fence)) {
43683da226SLucas Stach 		fence = submit->in_fence;
44683da226SLucas Stach 		submit->in_fence = NULL;
45683da226SLucas Stach 
46683da226SLucas Stach 		if (!dma_fence_is_signaled(fence))
47683da226SLucas Stach 			return fence;
48683da226SLucas Stach 
49683da226SLucas Stach 		dma_fence_put(fence);
50683da226SLucas Stach 	}
51683da226SLucas Stach 
52683da226SLucas Stach 	for (i = 0; i < submit->nr_bos; i++) {
53683da226SLucas Stach 		struct etnaviv_gem_submit_bo *bo = &submit->bos[i];
54683da226SLucas Stach 		int j;
55683da226SLucas Stach 
56683da226SLucas Stach 		if (bo->excl) {
57683da226SLucas Stach 			fence = bo->excl;
58683da226SLucas Stach 			bo->excl = NULL;
59683da226SLucas Stach 
60683da226SLucas Stach 			if (!dma_fence_is_signaled(fence))
61683da226SLucas Stach 				return fence;
62683da226SLucas Stach 
63683da226SLucas Stach 			dma_fence_put(fence);
64683da226SLucas Stach 		}
65683da226SLucas Stach 
66683da226SLucas Stach 		for (j = 0; j < bo->nr_shared; j++) {
67683da226SLucas Stach 			if (!bo->shared[j])
68683da226SLucas Stach 				continue;
69683da226SLucas Stach 
70683da226SLucas Stach 			fence = bo->shared[j];
71683da226SLucas Stach 			bo->shared[j] = NULL;
72683da226SLucas Stach 
73683da226SLucas Stach 			if (!dma_fence_is_signaled(fence))
74683da226SLucas Stach 				return fence;
75683da226SLucas Stach 
76683da226SLucas Stach 			dma_fence_put(fence);
77683da226SLucas Stach 		}
78683da226SLucas Stach 		kfree(bo->shared);
79683da226SLucas Stach 		bo->nr_shared = 0;
80683da226SLucas Stach 		bo->shared = NULL;
81683da226SLucas Stach 	}
82683da226SLucas Stach 
83e93b6deeSLucas Stach 	return NULL;
84e93b6deeSLucas Stach }
85e93b6deeSLucas Stach 
86e93b6deeSLucas Stach struct dma_fence *etnaviv_sched_run_job(struct drm_sched_job *sched_job)
87e93b6deeSLucas Stach {
88e93b6deeSLucas Stach 	struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job);
89e93b6deeSLucas Stach 	struct dma_fence *fence;
90e93b6deeSLucas Stach 
91e93b6deeSLucas Stach 	mutex_lock(&submit->gpu->lock);
92e93b6deeSLucas Stach 	list_add_tail(&submit->node, &submit->gpu->active_submit_list);
93e93b6deeSLucas Stach 	mutex_unlock(&submit->gpu->lock);
94e93b6deeSLucas Stach 
95e93b6deeSLucas Stach 	fence = etnaviv_gpu_submit(submit);
96e93b6deeSLucas Stach 	if (!fence) {
97e93b6deeSLucas Stach 		etnaviv_submit_put(submit);
98e93b6deeSLucas Stach 		return NULL;
99e93b6deeSLucas Stach 	}
100e93b6deeSLucas Stach 
101e93b6deeSLucas Stach 	return fence;
102e93b6deeSLucas Stach }
103e93b6deeSLucas Stach 
104e93b6deeSLucas Stach static void etnaviv_sched_timedout_job(struct drm_sched_job *sched_job)
105e93b6deeSLucas Stach {
106e93b6deeSLucas Stach 	/* this replaces the hangcheck */
107e93b6deeSLucas Stach }
108e93b6deeSLucas Stach 
109e93b6deeSLucas Stach static void etnaviv_sched_free_job(struct drm_sched_job *sched_job)
110e93b6deeSLucas Stach {
111e93b6deeSLucas Stach 	struct etnaviv_gem_submit *submit = to_etnaviv_submit(sched_job);
112e93b6deeSLucas Stach 
113e93b6deeSLucas Stach 	mutex_lock(&submit->gpu->lock);
114e93b6deeSLucas Stach 	list_del(&submit->node);
115e93b6deeSLucas Stach 	mutex_unlock(&submit->gpu->lock);
116e93b6deeSLucas Stach 
117e93b6deeSLucas Stach 	etnaviv_submit_put(submit);
118e93b6deeSLucas Stach }
119e93b6deeSLucas Stach 
120e93b6deeSLucas Stach static const struct drm_sched_backend_ops etnaviv_sched_ops = {
121e93b6deeSLucas Stach 	.dependency = etnaviv_sched_dependency,
122e93b6deeSLucas Stach 	.run_job = etnaviv_sched_run_job,
123e93b6deeSLucas Stach 	.timedout_job = etnaviv_sched_timedout_job,
124e93b6deeSLucas Stach 	.free_job = etnaviv_sched_free_job,
125e93b6deeSLucas Stach };
126e93b6deeSLucas Stach 
127e93b6deeSLucas Stach int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity,
128e93b6deeSLucas Stach 			   struct etnaviv_gem_submit *submit)
129e93b6deeSLucas Stach {
130e93b6deeSLucas Stach 	int ret;
131e93b6deeSLucas Stach 
132e93b6deeSLucas Stach 	ret = drm_sched_job_init(&submit->sched_job, &submit->gpu->sched,
133e93b6deeSLucas Stach 				 sched_entity, submit->cmdbuf.ctx);
134e93b6deeSLucas Stach 	if (ret)
135e93b6deeSLucas Stach 		return ret;
136e93b6deeSLucas Stach 
137e93b6deeSLucas Stach 	submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished);
138e93b6deeSLucas Stach 	mutex_lock(&submit->gpu->fence_idr_lock);
139e93b6deeSLucas Stach 	submit->out_fence_id = idr_alloc_cyclic(&submit->gpu->fence_idr,
140e93b6deeSLucas Stach 						submit->out_fence, 0,
141e93b6deeSLucas Stach 						INT_MAX, GFP_KERNEL);
142e93b6deeSLucas Stach 	mutex_unlock(&submit->gpu->fence_idr_lock);
143e93b6deeSLucas Stach 	if (submit->out_fence_id < 0)
144e93b6deeSLucas Stach 		return -ENOMEM;
145e93b6deeSLucas Stach 
146e93b6deeSLucas Stach 	/* the scheduler holds on to the job now */
147e93b6deeSLucas Stach 	kref_get(&submit->refcount);
148e93b6deeSLucas Stach 
149e93b6deeSLucas Stach 	drm_sched_entity_push_job(&submit->sched_job, sched_entity);
150e93b6deeSLucas Stach 
151e93b6deeSLucas Stach 	return 0;
152e93b6deeSLucas Stach }
153e93b6deeSLucas Stach 
154e93b6deeSLucas Stach int etnaviv_sched_init(struct etnaviv_gpu *gpu)
155e93b6deeSLucas Stach {
156e93b6deeSLucas Stach 	int ret;
157e93b6deeSLucas Stach 
158e93b6deeSLucas Stach 	ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops,
159e93b6deeSLucas Stach 			     etnaviv_hw_jobs_limit, etnaviv_job_hang_limit,
160e93b6deeSLucas Stach 			     msecs_to_jiffies(500), dev_name(gpu->dev));
161e93b6deeSLucas Stach 	if (ret)
162e93b6deeSLucas Stach 		return ret;
163e93b6deeSLucas Stach 
164e93b6deeSLucas Stach 	return 0;
165e93b6deeSLucas Stach }
166e93b6deeSLucas Stach 
167e93b6deeSLucas Stach void etnaviv_sched_fini(struct etnaviv_gpu *gpu)
168e93b6deeSLucas Stach {
169e93b6deeSLucas Stach 	drm_sched_fini(&gpu->sched);
170e93b6deeSLucas Stach }
171