@@ -55,6 +55,8 @@ static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
5555/* Return the highest priority ringbuffer with something in it */
5656static struct msm_ringbuffer * get_next_ring (struct msm_gpu * gpu )
5757{
58+ struct adreno_gpu * adreno_gpu = to_adreno_gpu (gpu );
59+ struct a5xx_gpu * a5xx_gpu = to_a5xx_gpu (adreno_gpu );
5860 unsigned long flags ;
5961 int i ;
6062
@@ -64,6 +66,8 @@ static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
6466
6567 spin_lock_irqsave (& ring -> preempt_lock , flags );
6668 empty = (get_wptr (ring ) == gpu -> funcs -> get_rptr (gpu , ring ));
69+ if (!empty && ring == a5xx_gpu -> cur_ring )
70+ empty = ring -> memptrs -> fence == a5xx_gpu -> last_seqno [i ];
6771 spin_unlock_irqrestore (& ring -> preempt_lock , flags );
6872
6973 if (!empty )
@@ -97,12 +101,19 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
97101 if (gpu -> nr_rings == 1 )
98102 return ;
99103
104+ /*
105+ * Serialize preemption start to ensure that we always make
106+ * decision on latest state. Otherwise we can get stuck in
107+ * lower priority or empty ring.
108+ */
109+ spin_lock_irqsave (& a5xx_gpu -> preempt_start_lock , flags );
110+
100111 /*
101112 * Try to start preemption by moving from NONE to START. If
102113 * unsuccessful, a preemption is already in flight
103114 */
104115 if (!try_preempt_state (a5xx_gpu , PREEMPT_NONE , PREEMPT_START ))
105- return ;
116+ goto out ;
106117
107118 /* Get the next ring to preempt to */
108119 ring = get_next_ring (gpu );
@@ -127,9 +138,11 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
127138 set_preempt_state (a5xx_gpu , PREEMPT_ABORT );
128139 update_wptr (gpu , a5xx_gpu -> cur_ring );
129140 set_preempt_state (a5xx_gpu , PREEMPT_NONE );
130- return ;
141+ goto out ;
131142 }
132143
144+ spin_unlock_irqrestore (& a5xx_gpu -> preempt_start_lock , flags );
145+
133146 /* Make sure the wptr doesn't update while we're in motion */
134147 spin_lock_irqsave (& ring -> preempt_lock , flags );
135148 a5xx_gpu -> preempt [ring -> id ]-> wptr = get_wptr (ring );
@@ -152,6 +165,10 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu)
152165
153166 /* And actually start the preemption */
154167 gpu_write (gpu , REG_A5XX_CP_CONTEXT_SWITCH_CNTL , 1 );
168+ return ;
169+
170+ out :
171+ spin_unlock_irqrestore (& a5xx_gpu -> preempt_start_lock , flags );
155172}
156173
157174void a5xx_preempt_irq (struct msm_gpu * gpu )
@@ -188,6 +205,12 @@ void a5xx_preempt_irq(struct msm_gpu *gpu)
188205 update_wptr (gpu , a5xx_gpu -> cur_ring );
189206
190207 set_preempt_state (a5xx_gpu , PREEMPT_NONE );
208+
209+ /*
210+ * Try to trigger preemption again in case there was a submit or
211+ * retire during ring switch
212+ */
213+ a5xx_preempt_trigger (gpu );
191214}
192215
193216void a5xx_preempt_hw_init (struct msm_gpu * gpu )
@@ -204,6 +227,8 @@ void a5xx_preempt_hw_init(struct msm_gpu *gpu)
204227 return ;
205228
206229 for (i = 0 ; i < gpu -> nr_rings ; i ++ ) {
230+ a5xx_gpu -> preempt [i ]-> data = 0 ;
231+ a5xx_gpu -> preempt [i ]-> info = 0 ;
207232 a5xx_gpu -> preempt [i ]-> wptr = 0 ;
208233 a5xx_gpu -> preempt [i ]-> rptr = 0 ;
209234 a5xx_gpu -> preempt [i ]-> rbase = gpu -> rb [i ]-> iova ;
@@ -298,5 +323,6 @@ void a5xx_preempt_init(struct msm_gpu *gpu)
298323 }
299324 }
300325
326+ spin_lock_init (& a5xx_gpu -> preempt_start_lock );
301327 timer_setup (& a5xx_gpu -> preempt_timer , a5xx_preempt_timer , 0 );
302328}
0 commit comments