Forráskód Böngészése

disp: msm: sde: delay frame trigger on esd failure

This change adds support for delaying kickoff in case of ESD error.
This delay is required to handle a race condition between esd
workqueue and display thread.
When ESD workqueue detects the esd and while handling the failure
notification if the new updates arrive on display thread, there
is a possibility that ESD workqueue will keep on waiting on pp_done.
This could happen if display thread keeps on incrementing the kickoff_cnt
before workqueue can check the condition. With this change the kickoff is
delayed, allowing the workqueue to get scheduled and avoiding the race
condition.

Change-Id: I8e6fff5ea5494ae801d1e60ae85b7ad19cc12961
Signed-off-by: Abhijit Kulkarni <[email protected]>
Abhijit Kulkarni 4 éve
szülő
commit
f1a277597f
2 módosított fájl, 25 hozzáadás és 1 törlés
  1. 20 1
      msm/sde/sde_encoder.c
  2. 5 0
      msm/sde/sde_encoder.h

+ 20 - 1
msm/sde/sde_encoder.c

@@ -68,6 +68,9 @@
 
 #define EVT_TIME_OUT_SPLIT 2
 
+/* worst case poll time for delay_kickoff to be cleared */
+#define DELAY_KICKOFF_POLL_TIMEOUT_US 100000
+
 /* Maximum number of VSYNC wait attempts for RSC state transition */
 #define MAX_RSC_WAIT	5
 
@@ -4173,6 +4176,19 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool is_error,
 	if (is_error)
 		_sde_encoder_reset_ctl_hw(drm_enc);
 
+	if (sde_enc->delay_kickoff) {
+		u32 loop_count = 20;
+		u32 sleep = DELAY_KICKOFF_POLL_TIMEOUT_US / loop_count;
+
+		for (i = 0; i < loop_count; i++) {
+			usleep_range(sleep, sleep * 2);
+			if (!sde_enc->delay_kickoff)
+				break;
+		}
+
+		SDE_EVT32(DRMID(drm_enc), i, SDE_EVTLOG_FUNC_CASE1);
+	}
+
 	/* All phys encs are ready to go, trigger the kickoff */
 	_sde_encoder_kickoff_phys(sde_enc, config_changed);
 
@@ -5360,6 +5376,7 @@ int sde_encoder_display_failure_notification(struct drm_encoder *enc,
 	event_thread = &priv->event_thread[sde_enc->crtc->index];
 
 	if (!skip_pre_kickoff) {
+		sde_enc->delay_kickoff = true;
 		kthread_queue_work(&event_thread->worker,
 				   &sde_enc->esd_trigger_work);
 		kthread_flush_work(&sde_enc->esd_trigger_work);
@@ -5372,8 +5389,10 @@ int sde_encoder_display_failure_notification(struct drm_encoder *enc,
 	 */
 	sde_encoder_helper_switch_vsync(enc, true);
 
-	if (!skip_pre_kickoff)
+	if (!skip_pre_kickoff) {
 		sde_encoder_wait_for_event(enc, MSM_ENC_TX_COMPLETE);
+		sde_enc->delay_kickoff = false;
+	}
 
 	return 0;
 }

+ 5 - 0
msm/sde/sde_encoder.h

@@ -189,6 +189,10 @@ struct sde_encoder_ops {
  * @valid_cpu_mask:		actual voted cpu core mask
  * @mode_info:                  stores the current mode and should be used
  *				only in commit phase
+ * @delay_kickoff		boolean to delay the kickoff, used in case
+ *				of esd attack to ensure esd workqueue detects
+ *				the previous frame transfer completion before
+ *				next update is triggered.
  */
 struct sde_encoder_virt {
 	struct drm_encoder base;
@@ -256,6 +260,7 @@ struct sde_encoder_virt {
 	struct dev_pm_qos_request pm_qos_cpu_req[NR_CPUS];
 	struct cpumask valid_cpu_mask;
 	struct msm_mode_info mode_info;
+	bool delay_kickoff;
 };
 
 #define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)