Bläddra i källkod

drm/msm/sde: fix race condition in vblank control interrupts

In Dual display concurrencies there are certain cases where
irq_idx and vblank_refcount state mismatch can occur.
To avoid it, during setup of irq_hw_idx, reset the vblank_refcount
and unregister read ptr irq if not yet done by then along with
maintaining global mutex lock for vblank_refcount.
Also, if register IRQ fails, correct vblank_refcount so
that IRQ registration can be tried again.

Change-Id: I06bcbf71c6a43bd161ff61093d9f6063a292d6bc
Signed-off-by: Raviteja Tamatam <[email protected]>
Signed-off-by: Samantha Tran <[email protected]>
Samantha Tran 5 år sedan
förälder
incheckning
0064a0227b
3 ändrade filer med 34 tillägg och 4 borttagningar
  1. 31 4
      msm/sde/sde_encoder_phys_cmd.c
  2. 2 0
      msm/sde/sde_kms.c
  3. 1 0
      msm/sde/sde_kms.h

+ 31 - 4
msm/sde/sde_encoder_phys_cmd.c

@@ -320,6 +320,8 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
 		struct sde_encoder_phys *phys_enc)
 {
 	struct sde_encoder_irq *irq;
+	struct sde_kms *sde_kms = phys_enc->sde_kms;
+	int ret = 0;
 
 	if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_ctl) {
 		SDE_ERROR("invalid args %d %d\n", !phys_enc,
@@ -332,6 +334,21 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx(
 		return;
 	}
 
+	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));
+		ret = sde_encoder_helper_unregister_irq(phys_enc,
+				INTR_IDX_RDPTR);
+		if (ret)
+			SDE_ERROR(
+				"control vblank irq registration error %d\n",
+					ret);
+	}
+	atomic_set(&phys_enc->vblank_refcount, 0);
+
 	irq = &phys_enc->irq[INTR_IDX_CTL_START];
 	irq->hw_idx = phys_enc->hw_ctl->idx;
 	irq->irq_idx = -EINVAL;
@@ -365,6 +382,8 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_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(
@@ -800,13 +819,15 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
 		to_sde_encoder_phys_cmd(phys_enc);
 	int ret = 0;
 	int refcount;
+	struct sde_kms *sde_kms;
 
 	if (!phys_enc || !phys_enc->hw_pp) {
 		SDE_ERROR("invalid encoder\n");
 		return -EINVAL;
 	}
+	sde_kms = phys_enc->sde_kms;
 
-	mutex_lock(phys_enc->vblank_ctl_lock);
+	mutex_lock(&sde_kms->vblank_ctl_global_lock);
 	refcount = atomic_read(&phys_enc->vblank_refcount);
 
 	/* Slave encoders don't report vblank */
@@ -824,11 +845,17 @@ static int sde_encoder_phys_cmd_control_vblank_irq(
 	SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0,
 			enable, refcount);
 
-	if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
+	if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1) {
 		ret = sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
-	else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
+		if (ret)
+			atomic_dec_return(&phys_enc->vblank_refcount);
+	} else if (!enable &&
+			atomic_dec_return(&phys_enc->vblank_refcount) == 0) {
 		ret = sde_encoder_helper_unregister_irq(phys_enc,
 				INTR_IDX_RDPTR);
+		if (ret)
+			atomic_inc_return(&phys_enc->vblank_refcount);
+	}
 
 end:
 	if (ret) {
@@ -840,7 +867,7 @@ end:
 				enable, refcount, SDE_EVTLOG_ERROR);
 	}
 
-	mutex_unlock(phys_enc->vblank_ctl_lock);
+	mutex_unlock(&sde_kms->vblank_ctl_global_lock);
 	return ret;
 }
 

+ 2 - 0
msm/sde/sde_kms.c

@@ -3418,6 +3418,8 @@ 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);
 

+ 1 - 0
msm/sde/sde_kms.h

@@ -299,6 +299,7 @@ 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;