浏览代码

disp: msm: sde: fix race between disable commit and vblank work

This patch addresses the following sequence:
1. DRM framework drops vblank reference,
	work is queued on event thread
2. Lastclose stages disable commit,
	swap-state occurs on display thread
3. vblank worker runs on event thread
	encoder cannot drop the FW vblank reference since it's
	no longer in crtc->encoder_mask

Instead, iterate through all encoders and match the enc->crtc
back-pointer to be sure the vblank vote is applied. Also, flush
the event thread in all cases during the CRTC disable to be sure
the vblank work is complete before the back-pointer is removed.

Change-Id: Ib307e9aefc2d4f729a3af7a6140bc887684f4489
Signed-off-by: Steve Cohen <[email protected]>
Steve Cohen 4 年之前
父节点
当前提交
31dd25c623
共有 1 个文件被更改,包括 11 次插入9 次删除
  1. 11 9
      msm/sde/sde_crtc.c

+ 11 - 9
msm/sde/sde_crtc.c

@@ -3850,8 +3850,10 @@ static int _sde_crtc_vblank_enable_no_lock(
 		if (ret < 0)
 			return ret;
 
-		drm_for_each_encoder_mask(enc, crtc->dev,
-			crtc->state->encoder_mask) {
+		drm_for_each_encoder(enc, crtc->dev) {
+			if (enc->crtc != crtc)
+				continue;
+
 			SDE_EVT32(DRMID(&sde_crtc->base), DRMID(enc), enable,
 					sde_crtc->enabled);
 
@@ -3859,8 +3861,10 @@ static int _sde_crtc_vblank_enable_no_lock(
 					sde_crtc_vblank_cb, (void *)crtc);
 		}
 	} else {
-		drm_for_each_encoder_mask(enc, crtc->dev,
-			crtc->state->encoder_mask) {
+		drm_for_each_encoder(enc, crtc->dev) {
+			if (enc->crtc != crtc)
+				continue;
+
 			SDE_EVT32(DRMID(&sde_crtc->base), DRMID(enc), enable,
 					sde_crtc->enabled);
 
@@ -4159,11 +4163,9 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
 	msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
 			(u8 *)&power_on);
 
-	if (atomic_read(&sde_crtc->frame_pending)) {
-		mutex_unlock(&sde_crtc->crtc_lock);
-		_sde_crtc_flush_event_thread(crtc);
-		mutex_lock(&sde_crtc->crtc_lock);
-	}
+	mutex_unlock(&sde_crtc->crtc_lock);
+	_sde_crtc_flush_event_thread(crtc);
+	mutex_lock(&sde_crtc->crtc_lock);
 
 	kthread_cancel_delayed_work_sync(&sde_crtc->static_cache_read_work);
 	kthread_cancel_delayed_work_sync(&sde_crtc->idle_notify_work);