xref: /linux/drivers/gpu/drm/msm/adreno/a6xx_preempt.c (revision e7ae83da4a289d4bf3b0fb62aadbe8c81c0dbde7)
1*e7ae83daSAntonino Maniscalco // SPDX-License-Identifier: GPL-2.0
2*e7ae83daSAntonino Maniscalco /* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
3*e7ae83daSAntonino Maniscalco /* Copyright (c) 2023 Collabora, Ltd. */
4*e7ae83daSAntonino Maniscalco /* Copyright (c) 2024 Valve Corporation */
5*e7ae83daSAntonino Maniscalco 
6*e7ae83daSAntonino Maniscalco #include "msm_gem.h"
7*e7ae83daSAntonino Maniscalco #include "a6xx_gpu.h"
8*e7ae83daSAntonino Maniscalco #include "a6xx_gmu.xml.h"
9*e7ae83daSAntonino Maniscalco #include "msm_mmu.h"
10*e7ae83daSAntonino Maniscalco 
11*e7ae83daSAntonino Maniscalco /*
12*e7ae83daSAntonino Maniscalco  * Try to transition the preemption state from old to new. Return
13*e7ae83daSAntonino Maniscalco  * true on success or false if the original state wasn't 'old'
14*e7ae83daSAntonino Maniscalco  */
15*e7ae83daSAntonino Maniscalco static inline bool try_preempt_state(struct a6xx_gpu *a6xx_gpu,
16*e7ae83daSAntonino Maniscalco 		enum a6xx_preempt_state old, enum a6xx_preempt_state new)
17*e7ae83daSAntonino Maniscalco {
18*e7ae83daSAntonino Maniscalco 	enum a6xx_preempt_state cur = atomic_cmpxchg(&a6xx_gpu->preempt_state,
19*e7ae83daSAntonino Maniscalco 		old, new);
20*e7ae83daSAntonino Maniscalco 
21*e7ae83daSAntonino Maniscalco 	return (cur == old);
22*e7ae83daSAntonino Maniscalco }
23*e7ae83daSAntonino Maniscalco 
24*e7ae83daSAntonino Maniscalco /*
25*e7ae83daSAntonino Maniscalco  * Force the preemption state to the specified state.  This is used in cases
26*e7ae83daSAntonino Maniscalco  * where the current state is known and won't change
27*e7ae83daSAntonino Maniscalco  */
28*e7ae83daSAntonino Maniscalco static inline void set_preempt_state(struct a6xx_gpu *gpu,
29*e7ae83daSAntonino Maniscalco 		enum a6xx_preempt_state new)
30*e7ae83daSAntonino Maniscalco {
31*e7ae83daSAntonino Maniscalco 	/*
32*e7ae83daSAntonino Maniscalco 	 * preempt_state may be read by other cores trying to trigger a
33*e7ae83daSAntonino Maniscalco 	 * preemption or in the interrupt handler so barriers are needed
34*e7ae83daSAntonino Maniscalco 	 * before...
35*e7ae83daSAntonino Maniscalco 	 */
36*e7ae83daSAntonino Maniscalco 	smp_mb__before_atomic();
37*e7ae83daSAntonino Maniscalco 	atomic_set(&gpu->preempt_state, new);
38*e7ae83daSAntonino Maniscalco 	/* ... and after*/
39*e7ae83daSAntonino Maniscalco 	smp_mb__after_atomic();
40*e7ae83daSAntonino Maniscalco }
41*e7ae83daSAntonino Maniscalco 
42*e7ae83daSAntonino Maniscalco /* Write the most recent wptr for the given ring into the hardware */
43*e7ae83daSAntonino Maniscalco static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
44*e7ae83daSAntonino Maniscalco {
45*e7ae83daSAntonino Maniscalco 	unsigned long flags;
46*e7ae83daSAntonino Maniscalco 	uint32_t wptr;
47*e7ae83daSAntonino Maniscalco 
48*e7ae83daSAntonino Maniscalco 	spin_lock_irqsave(&ring->preempt_lock, flags);
49*e7ae83daSAntonino Maniscalco 
50*e7ae83daSAntonino Maniscalco 	if (ring->restore_wptr) {
51*e7ae83daSAntonino Maniscalco 		wptr = get_wptr(ring);
52*e7ae83daSAntonino Maniscalco 
53*e7ae83daSAntonino Maniscalco 		gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr);
54*e7ae83daSAntonino Maniscalco 
55*e7ae83daSAntonino Maniscalco 		ring->restore_wptr = false;
56*e7ae83daSAntonino Maniscalco 	}
57*e7ae83daSAntonino Maniscalco 
58*e7ae83daSAntonino Maniscalco 	spin_unlock_irqrestore(&ring->preempt_lock, flags);
59*e7ae83daSAntonino Maniscalco }
60*e7ae83daSAntonino Maniscalco 
61*e7ae83daSAntonino Maniscalco /* Return the highest priority ringbuffer with something in it */
62*e7ae83daSAntonino Maniscalco static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
63*e7ae83daSAntonino Maniscalco {
64*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
65*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
66*e7ae83daSAntonino Maniscalco 
67*e7ae83daSAntonino Maniscalco 	unsigned long flags;
68*e7ae83daSAntonino Maniscalco 	int i;
69*e7ae83daSAntonino Maniscalco 
70*e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++) {
71*e7ae83daSAntonino Maniscalco 		bool empty;
72*e7ae83daSAntonino Maniscalco 		struct msm_ringbuffer *ring = gpu->rb[i];
73*e7ae83daSAntonino Maniscalco 
74*e7ae83daSAntonino Maniscalco 		spin_lock_irqsave(&ring->preempt_lock, flags);
75*e7ae83daSAntonino Maniscalco 		empty = (get_wptr(ring) == gpu->funcs->get_rptr(gpu, ring));
76*e7ae83daSAntonino Maniscalco 		if (!empty && ring == a6xx_gpu->cur_ring)
77*e7ae83daSAntonino Maniscalco 			empty = ring->memptrs->fence == a6xx_gpu->last_seqno[i];
78*e7ae83daSAntonino Maniscalco 		spin_unlock_irqrestore(&ring->preempt_lock, flags);
79*e7ae83daSAntonino Maniscalco 
80*e7ae83daSAntonino Maniscalco 		if (!empty)
81*e7ae83daSAntonino Maniscalco 			return ring;
82*e7ae83daSAntonino Maniscalco 	}
83*e7ae83daSAntonino Maniscalco 
84*e7ae83daSAntonino Maniscalco 	return NULL;
85*e7ae83daSAntonino Maniscalco }
86*e7ae83daSAntonino Maniscalco 
87*e7ae83daSAntonino Maniscalco static void a6xx_preempt_timer(struct timer_list *t)
88*e7ae83daSAntonino Maniscalco {
89*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = from_timer(a6xx_gpu, t, preempt_timer);
90*e7ae83daSAntonino Maniscalco 	struct msm_gpu *gpu = &a6xx_gpu->base.base;
91*e7ae83daSAntonino Maniscalco 	struct drm_device *dev = gpu->dev;
92*e7ae83daSAntonino Maniscalco 
93*e7ae83daSAntonino Maniscalco 	if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
94*e7ae83daSAntonino Maniscalco 		return;
95*e7ae83daSAntonino Maniscalco 
96*e7ae83daSAntonino Maniscalco 	dev_err(dev->dev, "%s: preemption timed out\n", gpu->name);
97*e7ae83daSAntonino Maniscalco 	kthread_queue_work(gpu->worker, &gpu->recover_work);
98*e7ae83daSAntonino Maniscalco }
99*e7ae83daSAntonino Maniscalco 
100*e7ae83daSAntonino Maniscalco void a6xx_preempt_irq(struct msm_gpu *gpu)
101*e7ae83daSAntonino Maniscalco {
102*e7ae83daSAntonino Maniscalco 	uint32_t status;
103*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
104*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
105*e7ae83daSAntonino Maniscalco 	struct drm_device *dev = gpu->dev;
106*e7ae83daSAntonino Maniscalco 
107*e7ae83daSAntonino Maniscalco 	if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
108*e7ae83daSAntonino Maniscalco 		return;
109*e7ae83daSAntonino Maniscalco 
110*e7ae83daSAntonino Maniscalco 	/* Delete the preemption watchdog timer */
111*e7ae83daSAntonino Maniscalco 	del_timer(&a6xx_gpu->preempt_timer);
112*e7ae83daSAntonino Maniscalco 
113*e7ae83daSAntonino Maniscalco 	/*
114*e7ae83daSAntonino Maniscalco 	 * The hardware should be setting the stop bit of CP_CONTEXT_SWITCH_CNTL
115*e7ae83daSAntonino Maniscalco 	 * to zero before firing the interrupt, but there is a non zero chance
116*e7ae83daSAntonino Maniscalco 	 * of a hardware condition or a software race that could set it again
117*e7ae83daSAntonino Maniscalco 	 * before we have a chance to finish. If that happens, log and go for
118*e7ae83daSAntonino Maniscalco 	 * recovery
119*e7ae83daSAntonino Maniscalco 	 */
120*e7ae83daSAntonino Maniscalco 	status = gpu_read(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL);
121*e7ae83daSAntonino Maniscalco 	if (unlikely(status & A6XX_CP_CONTEXT_SWITCH_CNTL_STOP)) {
122*e7ae83daSAntonino Maniscalco 		DRM_DEV_ERROR(&gpu->pdev->dev,
123*e7ae83daSAntonino Maniscalco 					  "!!!!!!!!!!!!!!!! preemption faulted !!!!!!!!!!!!!! irq\n");
124*e7ae83daSAntonino Maniscalco 		set_preempt_state(a6xx_gpu, PREEMPT_FAULTED);
125*e7ae83daSAntonino Maniscalco 		dev_err(dev->dev, "%s: Preemption failed to complete\n",
126*e7ae83daSAntonino Maniscalco 			gpu->name);
127*e7ae83daSAntonino Maniscalco 		kthread_queue_work(gpu->worker, &gpu->recover_work);
128*e7ae83daSAntonino Maniscalco 		return;
129*e7ae83daSAntonino Maniscalco 	}
130*e7ae83daSAntonino Maniscalco 
131*e7ae83daSAntonino Maniscalco 	a6xx_gpu->cur_ring = a6xx_gpu->next_ring;
132*e7ae83daSAntonino Maniscalco 	a6xx_gpu->next_ring = NULL;
133*e7ae83daSAntonino Maniscalco 
134*e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_FINISH);
135*e7ae83daSAntonino Maniscalco 
136*e7ae83daSAntonino Maniscalco 	update_wptr(gpu, a6xx_gpu->cur_ring);
137*e7ae83daSAntonino Maniscalco 
138*e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_NONE);
139*e7ae83daSAntonino Maniscalco 
140*e7ae83daSAntonino Maniscalco 	/*
141*e7ae83daSAntonino Maniscalco 	 * Retrigger preemption to avoid a deadlock that might occur when preemption
142*e7ae83daSAntonino Maniscalco 	 * is skipped due to it being already in flight when requested.
143*e7ae83daSAntonino Maniscalco 	 */
144*e7ae83daSAntonino Maniscalco 	a6xx_preempt_trigger(gpu);
145*e7ae83daSAntonino Maniscalco }
146*e7ae83daSAntonino Maniscalco 
147*e7ae83daSAntonino Maniscalco void a6xx_preempt_hw_init(struct msm_gpu *gpu)
148*e7ae83daSAntonino Maniscalco {
149*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
150*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
151*e7ae83daSAntonino Maniscalco 	int i;
152*e7ae83daSAntonino Maniscalco 
153*e7ae83daSAntonino Maniscalco 	/* No preemption if we only have one ring */
154*e7ae83daSAntonino Maniscalco 	if (gpu->nr_rings == 1)
155*e7ae83daSAntonino Maniscalco 		return;
156*e7ae83daSAntonino Maniscalco 
157*e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++) {
158*e7ae83daSAntonino Maniscalco 		struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[i];
159*e7ae83daSAntonino Maniscalco 
160*e7ae83daSAntonino Maniscalco 		record_ptr->wptr = 0;
161*e7ae83daSAntonino Maniscalco 		record_ptr->rptr = 0;
162*e7ae83daSAntonino Maniscalco 		record_ptr->rptr_addr = shadowptr(a6xx_gpu, gpu->rb[i]);
163*e7ae83daSAntonino Maniscalco 		record_ptr->info = 0;
164*e7ae83daSAntonino Maniscalco 		record_ptr->data = 0;
165*e7ae83daSAntonino Maniscalco 		record_ptr->rbase = gpu->rb[i]->iova;
166*e7ae83daSAntonino Maniscalco 	}
167*e7ae83daSAntonino Maniscalco 
168*e7ae83daSAntonino Maniscalco 	/* Write a 0 to signal that we aren't switching pagetables */
169*e7ae83daSAntonino Maniscalco 	gpu_write64(gpu, REG_A6XX_CP_CONTEXT_SWITCH_SMMU_INFO, 0);
170*e7ae83daSAntonino Maniscalco 
171*e7ae83daSAntonino Maniscalco 	/* Enable the GMEM save/restore feature for preemption */
172*e7ae83daSAntonino Maniscalco 	gpu_write(gpu, REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE, 0x1);
173*e7ae83daSAntonino Maniscalco 
174*e7ae83daSAntonino Maniscalco 	/* Reset the preemption state */
175*e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_NONE);
176*e7ae83daSAntonino Maniscalco 
177*e7ae83daSAntonino Maniscalco 	spin_lock_init(&a6xx_gpu->eval_lock);
178*e7ae83daSAntonino Maniscalco 
179*e7ae83daSAntonino Maniscalco 	/* Always come up on rb 0 */
180*e7ae83daSAntonino Maniscalco 	a6xx_gpu->cur_ring = gpu->rb[0];
181*e7ae83daSAntonino Maniscalco }
182*e7ae83daSAntonino Maniscalco 
183*e7ae83daSAntonino Maniscalco void a6xx_preempt_trigger(struct msm_gpu *gpu)
184*e7ae83daSAntonino Maniscalco {
185*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
186*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
187*e7ae83daSAntonino Maniscalco 	unsigned long flags;
188*e7ae83daSAntonino Maniscalco 	struct msm_ringbuffer *ring;
189*e7ae83daSAntonino Maniscalco 	unsigned int cntl;
190*e7ae83daSAntonino Maniscalco 
191*e7ae83daSAntonino Maniscalco 	if (gpu->nr_rings == 1)
192*e7ae83daSAntonino Maniscalco 		return;
193*e7ae83daSAntonino Maniscalco 
194*e7ae83daSAntonino Maniscalco 	/*
195*e7ae83daSAntonino Maniscalco 	 * Lock to make sure another thread attempting preemption doesn't skip it
196*e7ae83daSAntonino Maniscalco 	 * while we are still evaluating the next ring. This makes sure the other
197*e7ae83daSAntonino Maniscalco 	 * thread does start preemption if we abort it and avoids a soft lock.
198*e7ae83daSAntonino Maniscalco 	 */
199*e7ae83daSAntonino Maniscalco 	spin_lock_irqsave(&a6xx_gpu->eval_lock, flags);
200*e7ae83daSAntonino Maniscalco 
201*e7ae83daSAntonino Maniscalco 	/*
202*e7ae83daSAntonino Maniscalco 	 * Try to start preemption by moving from NONE to START. If
203*e7ae83daSAntonino Maniscalco 	 * unsuccessful, a preemption is already in flight
204*e7ae83daSAntonino Maniscalco 	 */
205*e7ae83daSAntonino Maniscalco 	if (!try_preempt_state(a6xx_gpu, PREEMPT_NONE, PREEMPT_START)) {
206*e7ae83daSAntonino Maniscalco 		spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
207*e7ae83daSAntonino Maniscalco 		return;
208*e7ae83daSAntonino Maniscalco 	}
209*e7ae83daSAntonino Maniscalco 
210*e7ae83daSAntonino Maniscalco 	cntl = A6XX_CP_CONTEXT_SWITCH_CNTL_LEVEL(a6xx_gpu->preempt_level);
211*e7ae83daSAntonino Maniscalco 
212*e7ae83daSAntonino Maniscalco 	if (a6xx_gpu->skip_save_restore)
213*e7ae83daSAntonino Maniscalco 		cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_SKIP_SAVE_RESTORE;
214*e7ae83daSAntonino Maniscalco 
215*e7ae83daSAntonino Maniscalco 	if (a6xx_gpu->uses_gmem)
216*e7ae83daSAntonino Maniscalco 		cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_USES_GMEM;
217*e7ae83daSAntonino Maniscalco 
218*e7ae83daSAntonino Maniscalco 	cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_STOP;
219*e7ae83daSAntonino Maniscalco 
220*e7ae83daSAntonino Maniscalco 	/* Get the next ring to preempt to */
221*e7ae83daSAntonino Maniscalco 	ring = get_next_ring(gpu);
222*e7ae83daSAntonino Maniscalco 
223*e7ae83daSAntonino Maniscalco 	/*
224*e7ae83daSAntonino Maniscalco 	 * If no ring is populated or the highest priority ring is the current
225*e7ae83daSAntonino Maniscalco 	 * one do nothing except to update the wptr to the latest and greatest
226*e7ae83daSAntonino Maniscalco 	 */
227*e7ae83daSAntonino Maniscalco 	if (!ring || (a6xx_gpu->cur_ring == ring)) {
228*e7ae83daSAntonino Maniscalco 		set_preempt_state(a6xx_gpu, PREEMPT_FINISH);
229*e7ae83daSAntonino Maniscalco 		update_wptr(gpu, a6xx_gpu->cur_ring);
230*e7ae83daSAntonino Maniscalco 		set_preempt_state(a6xx_gpu, PREEMPT_NONE);
231*e7ae83daSAntonino Maniscalco 		spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
232*e7ae83daSAntonino Maniscalco 		return;
233*e7ae83daSAntonino Maniscalco 	}
234*e7ae83daSAntonino Maniscalco 
235*e7ae83daSAntonino Maniscalco 	spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
236*e7ae83daSAntonino Maniscalco 
237*e7ae83daSAntonino Maniscalco 	spin_lock_irqsave(&ring->preempt_lock, flags);
238*e7ae83daSAntonino Maniscalco 
239*e7ae83daSAntonino Maniscalco 	struct a7xx_cp_smmu_info *smmu_info_ptr =
240*e7ae83daSAntonino Maniscalco 		a6xx_gpu->preempt_smmu[ring->id];
241*e7ae83daSAntonino Maniscalco 	struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[ring->id];
242*e7ae83daSAntonino Maniscalco 	u64 ttbr0 = ring->memptrs->ttbr0;
243*e7ae83daSAntonino Maniscalco 	u32 context_idr = ring->memptrs->context_idr;
244*e7ae83daSAntonino Maniscalco 
245*e7ae83daSAntonino Maniscalco 	smmu_info_ptr->ttbr0 = ttbr0;
246*e7ae83daSAntonino Maniscalco 	smmu_info_ptr->context_idr = context_idr;
247*e7ae83daSAntonino Maniscalco 	record_ptr->wptr = get_wptr(ring);
248*e7ae83daSAntonino Maniscalco 
249*e7ae83daSAntonino Maniscalco 	/*
250*e7ae83daSAntonino Maniscalco 	 * The GPU will write the wptr we set above when we preempt. Reset
251*e7ae83daSAntonino Maniscalco 	 * restore_wptr to make sure that we don't write WPTR to the same
252*e7ae83daSAntonino Maniscalco 	 * thing twice. It's still possible subsequent submissions will update
253*e7ae83daSAntonino Maniscalco 	 * wptr again, in which case they will set the flag to true. This has
254*e7ae83daSAntonino Maniscalco 	 * to be protected by the lock for setting the flag and updating wptr
255*e7ae83daSAntonino Maniscalco 	 * to be atomic.
256*e7ae83daSAntonino Maniscalco 	 */
257*e7ae83daSAntonino Maniscalco 	ring->restore_wptr = false;
258*e7ae83daSAntonino Maniscalco 
259*e7ae83daSAntonino Maniscalco 	spin_unlock_irqrestore(&ring->preempt_lock, flags);
260*e7ae83daSAntonino Maniscalco 
261*e7ae83daSAntonino Maniscalco 	gpu_write64(gpu,
262*e7ae83daSAntonino Maniscalco 		REG_A6XX_CP_CONTEXT_SWITCH_SMMU_INFO,
263*e7ae83daSAntonino Maniscalco 		a6xx_gpu->preempt_smmu_iova[ring->id]);
264*e7ae83daSAntonino Maniscalco 
265*e7ae83daSAntonino Maniscalco 	gpu_write64(gpu,
266*e7ae83daSAntonino Maniscalco 		REG_A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR,
267*e7ae83daSAntonino Maniscalco 		a6xx_gpu->preempt_iova[ring->id]);
268*e7ae83daSAntonino Maniscalco 
269*e7ae83daSAntonino Maniscalco 	a6xx_gpu->next_ring = ring;
270*e7ae83daSAntonino Maniscalco 
271*e7ae83daSAntonino Maniscalco 	/* Start a timer to catch a stuck preemption */
272*e7ae83daSAntonino Maniscalco 	mod_timer(&a6xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000));
273*e7ae83daSAntonino Maniscalco 
274*e7ae83daSAntonino Maniscalco 	/* Set the preemption state to triggered */
275*e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED);
276*e7ae83daSAntonino Maniscalco 
277*e7ae83daSAntonino Maniscalco 	/* Trigger the preemption */
278*e7ae83daSAntonino Maniscalco 	gpu_write(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL, cntl);
279*e7ae83daSAntonino Maniscalco }
280*e7ae83daSAntonino Maniscalco 
281*e7ae83daSAntonino Maniscalco static int preempt_init_ring(struct a6xx_gpu *a6xx_gpu,
282*e7ae83daSAntonino Maniscalco 		struct msm_ringbuffer *ring)
283*e7ae83daSAntonino Maniscalco {
284*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
285*e7ae83daSAntonino Maniscalco 	struct msm_gpu *gpu = &adreno_gpu->base;
286*e7ae83daSAntonino Maniscalco 	struct drm_gem_object *bo = NULL;
287*e7ae83daSAntonino Maniscalco 	phys_addr_t ttbr;
288*e7ae83daSAntonino Maniscalco 	u64 iova = 0;
289*e7ae83daSAntonino Maniscalco 	void *ptr;
290*e7ae83daSAntonino Maniscalco 	int asid;
291*e7ae83daSAntonino Maniscalco 
292*e7ae83daSAntonino Maniscalco 	ptr = msm_gem_kernel_new(gpu->dev,
293*e7ae83daSAntonino Maniscalco 		PREEMPT_RECORD_SIZE(adreno_gpu),
294*e7ae83daSAntonino Maniscalco 		MSM_BO_WC | MSM_BO_MAP_PRIV, gpu->aspace, &bo, &iova);
295*e7ae83daSAntonino Maniscalco 
296*e7ae83daSAntonino Maniscalco 	if (IS_ERR(ptr))
297*e7ae83daSAntonino Maniscalco 		return PTR_ERR(ptr);
298*e7ae83daSAntonino Maniscalco 
299*e7ae83daSAntonino Maniscalco 	memset(ptr, 0, PREEMPT_RECORD_SIZE(adreno_gpu));
300*e7ae83daSAntonino Maniscalco 
301*e7ae83daSAntonino Maniscalco 	msm_gem_object_set_name(bo, "preempt_record ring%d", ring->id);
302*e7ae83daSAntonino Maniscalco 
303*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_bo[ring->id] = bo;
304*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_iova[ring->id] = iova;
305*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt[ring->id] = ptr;
306*e7ae83daSAntonino Maniscalco 
307*e7ae83daSAntonino Maniscalco 	struct a6xx_preempt_record *record_ptr = ptr;
308*e7ae83daSAntonino Maniscalco 
309*e7ae83daSAntonino Maniscalco 	ptr = msm_gem_kernel_new(gpu->dev,
310*e7ae83daSAntonino Maniscalco 		PREEMPT_SMMU_INFO_SIZE,
311*e7ae83daSAntonino Maniscalco 		MSM_BO_WC | MSM_BO_MAP_PRIV | MSM_BO_GPU_READONLY,
312*e7ae83daSAntonino Maniscalco 		gpu->aspace, &bo, &iova);
313*e7ae83daSAntonino Maniscalco 
314*e7ae83daSAntonino Maniscalco 	if (IS_ERR(ptr))
315*e7ae83daSAntonino Maniscalco 		return PTR_ERR(ptr);
316*e7ae83daSAntonino Maniscalco 
317*e7ae83daSAntonino Maniscalco 	memset(ptr, 0, PREEMPT_SMMU_INFO_SIZE);
318*e7ae83daSAntonino Maniscalco 
319*e7ae83daSAntonino Maniscalco 	msm_gem_object_set_name(bo, "preempt_smmu_info ring%d", ring->id);
320*e7ae83daSAntonino Maniscalco 
321*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_smmu_bo[ring->id] = bo;
322*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_smmu_iova[ring->id] = iova;
323*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_smmu[ring->id] = ptr;
324*e7ae83daSAntonino Maniscalco 
325*e7ae83daSAntonino Maniscalco 	struct a7xx_cp_smmu_info *smmu_info_ptr = ptr;
326*e7ae83daSAntonino Maniscalco 
327*e7ae83daSAntonino Maniscalco 	msm_iommu_pagetable_params(gpu->aspace->mmu, &ttbr, &asid);
328*e7ae83daSAntonino Maniscalco 
329*e7ae83daSAntonino Maniscalco 	smmu_info_ptr->magic = GEN7_CP_SMMU_INFO_MAGIC;
330*e7ae83daSAntonino Maniscalco 	smmu_info_ptr->ttbr0 = ttbr;
331*e7ae83daSAntonino Maniscalco 	smmu_info_ptr->asid = 0xdecafbad;
332*e7ae83daSAntonino Maniscalco 	smmu_info_ptr->context_idr = 0;
333*e7ae83daSAntonino Maniscalco 
334*e7ae83daSAntonino Maniscalco 	/* Set up the defaults on the preemption record */
335*e7ae83daSAntonino Maniscalco 	record_ptr->magic = A6XX_PREEMPT_RECORD_MAGIC;
336*e7ae83daSAntonino Maniscalco 	record_ptr->info = 0;
337*e7ae83daSAntonino Maniscalco 	record_ptr->data = 0;
338*e7ae83daSAntonino Maniscalco 	record_ptr->rptr = 0;
339*e7ae83daSAntonino Maniscalco 	record_ptr->wptr = 0;
340*e7ae83daSAntonino Maniscalco 	record_ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT;
341*e7ae83daSAntonino Maniscalco 	record_ptr->rbase = ring->iova;
342*e7ae83daSAntonino Maniscalco 	record_ptr->counter = 0;
343*e7ae83daSAntonino Maniscalco 	record_ptr->bv_rptr_addr = rbmemptr(ring, bv_rptr);
344*e7ae83daSAntonino Maniscalco 
345*e7ae83daSAntonino Maniscalco 	return 0;
346*e7ae83daSAntonino Maniscalco }
347*e7ae83daSAntonino Maniscalco 
348*e7ae83daSAntonino Maniscalco void a6xx_preempt_fini(struct msm_gpu *gpu)
349*e7ae83daSAntonino Maniscalco {
350*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
351*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
352*e7ae83daSAntonino Maniscalco 	int i;
353*e7ae83daSAntonino Maniscalco 
354*e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++)
355*e7ae83daSAntonino Maniscalco 		msm_gem_kernel_put(a6xx_gpu->preempt_bo[i], gpu->aspace);
356*e7ae83daSAntonino Maniscalco }
357*e7ae83daSAntonino Maniscalco 
358*e7ae83daSAntonino Maniscalco void a6xx_preempt_init(struct msm_gpu *gpu)
359*e7ae83daSAntonino Maniscalco {
360*e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
361*e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
362*e7ae83daSAntonino Maniscalco 	int i;
363*e7ae83daSAntonino Maniscalco 
364*e7ae83daSAntonino Maniscalco 	/* No preemption if we only have one ring */
365*e7ae83daSAntonino Maniscalco 	if (gpu->nr_rings <= 1)
366*e7ae83daSAntonino Maniscalco 		return;
367*e7ae83daSAntonino Maniscalco 
368*e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++) {
369*e7ae83daSAntonino Maniscalco 		if (preempt_init_ring(a6xx_gpu, gpu->rb[i]))
370*e7ae83daSAntonino Maniscalco 			goto fail;
371*e7ae83daSAntonino Maniscalco 	}
372*e7ae83daSAntonino Maniscalco 
373*e7ae83daSAntonino Maniscalco 	/* TODO: make this configurable? */
374*e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_level = 1;
375*e7ae83daSAntonino Maniscalco 	a6xx_gpu->uses_gmem = 1;
376*e7ae83daSAntonino Maniscalco 	a6xx_gpu->skip_save_restore = 1;
377*e7ae83daSAntonino Maniscalco 
378*e7ae83daSAntonino Maniscalco 	timer_setup(&a6xx_gpu->preempt_timer, a6xx_preempt_timer, 0);
379*e7ae83daSAntonino Maniscalco 
380*e7ae83daSAntonino Maniscalco 	return;
381*e7ae83daSAntonino Maniscalco fail:
382*e7ae83daSAntonino Maniscalco 	/*
383*e7ae83daSAntonino Maniscalco 	 * On any failure our adventure is over. Clean up and
384*e7ae83daSAntonino Maniscalco 	 * set nr_rings to 1 to force preemption off
385*e7ae83daSAntonino Maniscalco 	 */
386*e7ae83daSAntonino Maniscalco 	a6xx_preempt_fini(gpu);
387*e7ae83daSAntonino Maniscalco 	gpu->nr_rings = 1;
388*e7ae83daSAntonino Maniscalco 
389*e7ae83daSAntonino Maniscalco 	DRM_DEV_ERROR(&gpu->pdev->dev,
390*e7ae83daSAntonino Maniscalco 				  "preemption init failed, disabling preemption\n");
391*e7ae83daSAntonino Maniscalco 
392*e7ae83daSAntonino Maniscalco 	return;
393*e7ae83daSAntonino Maniscalco }
394