Ver Fonte

disp: msm: sde: manage vblank refcount concurrency

Vblank refcount can reach out of sync with below case
 1. event_thread triggers the vblank_enable
 2. commit_thread triggers the modeset
   2.a modeset resets the vblank refcount with mode_set
 3. event_thread triggers the vblank_disable

Event 2.a resets the vblank refcount and vblank disable
request after 2.a is going to fail. This can be fixed
by avoiding concurrency between mode_set call and vblank
request.

Change-Id: Ibb810ec90e81d63feee443f1c37dd736d5cfac0d
Signed-off-by: Dhaval Patel <[email protected]>
Dhaval Patel há 5 anos atrás
pai
commit
44cde01fc7
5 ficheiros alterados com 37 adições e 27 exclusões
  1. 1 9
      msm/msm_drv.c
  2. 2 0
      msm/sde/sde_encoder_phys.h
  3. 34 16
      msm/sde/sde_encoder_phys_cmd.c
  4. 0 1
      msm/sde/sde_kms.c
  5. 0 1
      msm/sde/sde_kms.h

+ 1 - 9
msm/msm_drv.c

@@ -365,15 +365,7 @@ static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
 	cur_work->crtc_id = crtc_id;
 	cur_work->enable = enable;
 	cur_work->priv = priv;
-
-	/* During modeset scenario, vblank request is queued to
-	 * display thread to avoid enabling irq resulting in
-	 * vblank refcount mismatch
-	 */
-	if (crtc->state && drm_atomic_crtc_needs_modeset(crtc->state))
-		worker = &priv->disp_thread[crtc_id].worker;
-	else
-		worker = &priv->event_thread[crtc_id].worker;
+	worker = &priv->event_thread[crtc_id].worker;
 
 	kthread_queue_work(worker, &cur_work->work);
 	return 0;

+ 2 - 0
msm/sde/sde_encoder_phys.h

@@ -275,6 +275,7 @@ struct sde_encoder_irq {
  * @enc_spinlock:	Virtual-Encoder-Wide Spin Lock for IRQ purposes
  * @enable_state:	Enable state tracking
  * @vblank_refcount:	Reference count of vblank request
+ * @vblank_cached_refcount:	Reference count of vblank cached request
  * @wbirq_refcount:	Reference count of wb irq request
  * @vsync_cnt:		Vsync count for the physical encoder
  * @underrun_cnt:	Underrun count for the physical encoder
@@ -324,6 +325,7 @@ struct sde_encoder_phys {
 	enum sde_enc_enable_state enable_state;
 	struct mutex *vblank_ctl_lock;
 	atomic_t vblank_refcount;
+	atomic_t vblank_cached_refcount;
 	atomic_t wbirq_refcount;
 	atomic_t vsync_cnt;
 	atomic_t underrun_cnt;

+ 34 - 16
msm/sde/sde_encoder_phys_cmd.c

@@ -328,6 +328,7 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
 	struct sde_encoder_irq *irq;
 	struct sde_kms *sde_kms;
 	int ret = 0;
+	u32 vblank_refcount;
 
 	if (!phys_enc->sde_kms || !phys_enc->hw_pp || !phys_enc->hw_ctl) {
 		SDE_ERROR("invalid args %d %d %d\n", !phys_enc->sde_kms,
@@ -342,20 +343,28 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
 
 	sde_kms = phys_enc->sde_kms;
 
-	mutex_lock(&sde_kms->vblank_ctl_global_lock);
-
-	if (atomic_read(&phys_enc->vblank_refcount)) {
-		SDE_ERROR(
-		"vblank_refcount mismatch detected, try to reset %d\n",
-				atomic_read(&phys_enc->vblank_refcount));
+	mutex_lock(phys_enc->vblank_ctl_lock);
+	vblank_refcount = atomic_read(&phys_enc->vblank_refcount);
+	if (vblank_refcount) {
 		ret = sde_encoder_helper_unregister_irq(phys_enc,
 				INTR_IDX_RDPTR);
 		if (ret)
 			SDE_ERROR(
 				"control vblank irq registration error %d\n",
 					ret);
+		if (vblank_refcount > 1)
+			SDE_ERROR(
+				"vblank_refcount mismatch detected, try to reset %d\n",
+				atomic_read(&phys_enc->vblank_refcount));
+		else
+			atomic_set(&phys_enc->vblank_cached_refcount, 1);
+
+		SDE_EVT32(DRMID(phys_enc->parent),
+			phys_enc->hw_pp->idx - PINGPONG_0, vblank_refcount,
+			atomic_read(&phys_enc->vblank_cached_refcount));
 	}
 	atomic_set(&phys_enc->vblank_refcount, 0);
+	mutex_unlock(phys_enc->vblank_ctl_lock);
 
 	irq = &phys_enc->irq[INTR_IDX_CTL_START];
 	irq->hw_idx = phys_enc->hw_ctl->idx;
@@ -389,9 +398,6 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
 		irq->hw_idx = phys_enc->hw_intf->idx;
 	else
 		irq->hw_idx = phys_enc->hw_pp->idx;
-
-	mutex_unlock(&sde_kms->vblank_ctl_global_lock);
-
 }
 
 static void sde_encoder_phys_cmd_cont_splash_mode_set(
@@ -811,7 +817,7 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
 	struct sde_encoder_phys_cmd *cmd_enc =
 		to_sde_encoder_phys_cmd(phys_enc);
 	int ret = 0;
-	int refcount;
+	u32 refcount, cached_refcount;
 	struct sde_kms *sde_kms;
 
 	if (!phys_enc || !phys_enc->hw_pp) {
@@ -820,17 +826,23 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
 	}
 	sde_kms = phys_enc->sde_kms;
 
-	mutex_lock(&sde_kms->vblank_ctl_global_lock);
-	refcount = atomic_read(&phys_enc->vblank_refcount);
-
+	mutex_lock(phys_enc->vblank_ctl_lock);
 	/* Slave encoders don't report vblank */
 	if (!sde_encoder_phys_cmd_is_master(phys_enc))
 		goto end;
 
+	refcount = atomic_read(&phys_enc->vblank_refcount);
+	cached_refcount = atomic_read(&phys_enc->vblank_cached_refcount);
+
 	/* protect against negative */
 	if (!enable && refcount == 0) {
-		ret = -EINVAL;
-		goto end;
+		if (cached_refcount == 1) {
+			atomic_set(&phys_enc->vblank_cached_refcount, 0);
+			goto end;
+		} else {
+			ret = -EINVAL;
+			goto end;
+		}
 	}
 
 	SDE_DEBUG_CMDENC(cmd_enc, "[%pS] enable=%d/%d\n",
@@ -850,7 +862,13 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
 			atomic_inc_return(&phys_enc->vblank_refcount);
 	}
 
+	if (enable && cached_refcount) {
+		atomic_inc(&phys_enc->vblank_refcount);
+		atomic_set(&phys_enc->vblank_cached_refcount, 0);
+	}
+
 end:
+	mutex_unlock(phys_enc->vblank_ctl_lock);
 	if (ret) {
 		SDE_ERROR_CMDENC(cmd_enc,
 				"control vblank irq error %d, enable %d, refcount %d\n",
@@ -860,7 +878,6 @@ end:
 				enable, refcount, SDE_EVTLOG_ERROR);
 	}
 
-	mutex_unlock(&sde_kms->vblank_ctl_global_lock);
 	return ret;
 }
 
@@ -2068,6 +2085,7 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init(
 	irq->cb.func = sde_encoder_phys_cmd_wr_ptr_irq;
 
 	atomic_set(&phys_enc->vblank_refcount, 0);
+	atomic_set(&phys_enc->vblank_cached_refcount, 0);
 	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
 	atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
 	atomic_set(&cmd_enc->pending_vblank_cnt, 0);

+ 0 - 1
msm/sde/sde_kms.c

@@ -3619,7 +3619,6 @@ static int sde_kms_hw_init(struct msm_kms *kms)
 	dev->mode_config.max_height = sde_kms->catalog->max_display_height;
 
 	mutex_init(&sde_kms->secure_transition_lock);
-	mutex_init(&sde_kms->vblank_ctl_global_lock);
 
 	atomic_set(&sde_kms->detach_sec_cb, 0);
 	atomic_set(&sde_kms->detach_all_cb, 0);

+ 0 - 1
msm/sde/sde_kms.h

@@ -302,7 +302,6 @@ struct sde_kms {
 	atomic_t detach_sec_cb;
 	atomic_t detach_all_cb;
 	struct mutex secure_transition_lock;
-	struct mutex vblank_ctl_global_lock;
 
 	bool first_kickoff;
 	bool qdss_enabled;