فهرست منبع

disp: msm: sde: restore qsync read pointer after IPC

Currently, when there is an idle power collapse HW resets
the internal read pointer value to 0. This causes the
trigger window to be out of sync when power is restored
until the next vsync is received.

This change reads the panel read pointer and overrrides
the internal register to allow a frame to be picked up in
the current vsync cycle, but defers it to next vsync if it
comes later than the safe trigger window.

Change-Id: I741a91edcddc105eda34d875e8e1c32933b83d71
Signed-off-by: Amine Najahi <[email protected]>
Amine Najahi 2 سال پیش
والد
کامیت
386e77f95f
3فایلهای تغییر یافته به همراه93 افزوده شده و 1 حذف شده
  1. 3 0
      msm/sde/sde_encoder.h
  2. 8 0
      msm/sde/sde_encoder_phys.h
  3. 82 1
      msm/sde/sde_encoder_phys_cmd.c

+ 3 - 0
msm/sde/sde_encoder.h

@@ -155,6 +155,8 @@ enum sde_enc_rc_states {
  * @misr_frame_count:		misr frame count before start capturing the data
  * @idle_pc_enabled:		indicate if idle power collapse is enabled
  *				currently. This can be controlled by user-mode
+ * @restore_te_rd_ptr:          flag to indicate that te read pointer value must
+ *                              be restored after idle power collapse
  * @rc_lock:			resource control mutex lock to protect
  *				virt encoder over various state changes
  * @rc_state:			resource controller state
@@ -242,6 +244,7 @@ struct sde_encoder_virt {
 	struct input_handler *input_handler;
 	bool vblank_enabled;
 	bool idle_pc_restore;
+	bool restore_te_rd_ptr;
 	enum frame_trigger_mode_type frame_trigger_mode;
 	bool dynamic_hdr_updated;
 

+ 8 - 0
msm/sde/sde_encoder_phys.h

@@ -785,6 +785,14 @@ int sde_encoder_helper_unregister_irq(struct sde_encoder_phys *phys_enc,
 void sde_encoder_helper_update_intf_cfg(
 		struct sde_encoder_phys *phys_enc);
 
+/**
+ * sde_encoder_restore_tearcheck_rd_ptr - restore interface rd_ptr configuration
+ *	This function reads the panel scan line value using a DCS command
+ *	and overrides the internal interface read pointer configuration.
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void sde_encoder_restore_tearcheck_rd_ptr(struct sde_encoder_phys *phys_enc);
+
 /**
  * _sde_encoder_phys_is_dual_ctl - check if encoder needs dual ctl path.
  * @phys_enc: Pointer to physical encoder structure

+ 82 - 1
msm/sde/sde_encoder_phys_cmd.c

@@ -202,6 +202,80 @@ static void sde_encoder_override_tearcheck_rd_ptr(struct sde_encoder_phys *phys_
 		info[1].rd_ptr_line_count, info[1].rd_ptr_frame_count, info[1].wr_ptr_line_count);
 }
 
+void sde_encoder_restore_tearcheck_rd_ptr(struct sde_encoder_phys *phys_enc)
+{
+	struct sde_hw_intf *hw_intf;
+	struct sde_hw_pp_vsync_info info[MAX_CHANNELS_PER_ENC] = {{0}};
+	struct drm_display_mode *mode;
+	struct sde_encoder_phys_cmd *cmd_enc;
+	struct sde_encoder_virt *sde_enc;
+	struct sde_connector *c_conn;
+	ktime_t nominal_period_ns, nominal_line_time_ns, panel_scan_line_ts_ns = 0;
+	ktime_t qsync_period_ns, time_into_frame_ns;
+	u32 qsync_timeout_lines, latency_margin_lines = 0, restored_rd_ptr_lines;
+	u16 panel_scan_line;
+	int rc;
+
+	if (!phys_enc || !phys_enc->connector) {
+		SDE_ERROR("invalid arguments\n");
+		return;
+	}
+
+	cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
+	mode = &phys_enc->cached_mode;
+	hw_intf = phys_enc->hw_intf;
+	c_conn = to_sde_connector(phys_enc->connector);
+	sde_enc = to_sde_encoder_virt(phys_enc->parent);
+
+	nominal_period_ns = mult_frac(1000000000, 1, drm_mode_vrefresh(mode));
+	qsync_period_ns = mult_frac(1000000000, 1, sde_enc->mode_info.qsync_min_fps);
+	nominal_line_time_ns = mult_frac(1, nominal_period_ns, mode->vtotal);
+	qsync_timeout_lines = mode->vtotal + cmd_enc->qsync_threshold_lines + 1;
+
+	/*
+	 * First read panel scan line value using a DCS command.
+	 * If the functionality is not supported or there is an error, defer trigger to
+	 * next TE by setting panel_scan_line to qsync_timeout_lines.
+	 */
+	if (c_conn->ops.get_panel_scan_line) {
+		rc = c_conn->ops.get_panel_scan_line(c_conn->display, &panel_scan_line,
+				&panel_scan_line_ts_ns);
+		if (rc || panel_scan_line >= qsync_timeout_lines) {
+			SDE_DEBUG_CMDENC(cmd_enc, "failed to get panel scan line, rc=%d\n", rc);
+			panel_scan_line = qsync_timeout_lines;
+		}
+	} else {
+		panel_scan_line = qsync_timeout_lines;
+	}
+
+	/* Compensate the latency from DCS scan line response*/
+	spin_lock(phys_enc->enc_spinlock);
+	sde_encoder_helper_get_pp_line_count(phys_enc->parent, info);
+	time_into_frame_ns = ktime_sub(ktime_get(), phys_enc->last_vsync_timestamp);
+
+	if (panel_scan_line_ts_ns)
+		latency_margin_lines = mult_frac(1, ktime_sub(ktime_get(), panel_scan_line_ts_ns),
+				nominal_line_time_ns);
+
+	restored_rd_ptr_lines = panel_scan_line + latency_margin_lines;
+	if (restored_rd_ptr_lines >= qsync_timeout_lines)
+		restored_rd_ptr_lines = qsync_timeout_lines;
+
+	if (hw_intf && hw_intf->ops.override_tear_rd_ptr_val)
+		hw_intf->ops.override_tear_rd_ptr_val(hw_intf, restored_rd_ptr_lines);
+	spin_unlock(phys_enc->enc_spinlock);
+
+	SDE_EVT32(DRMID(phys_enc->parent), drm_mode_vrefresh(mode),
+			sde_enc->mode_info.qsync_min_fps,
+			mode->vtotal, panel_scan_line, qsync_timeout_lines, latency_margin_lines,
+			restored_rd_ptr_lines, info[0].rd_ptr_line_count - mode->vdisplay,
+			ktime_to_us(time_into_frame_ns));
+	SDE_DEBUG_CMDENC(cmd_enc, "scan_line:%u rest_rd_ptr:%u rd_ptr:%u frame_ns:%u\n",
+			panel_scan_line, restored_rd_ptr_lines,
+			info[0].rd_ptr_line_count - mode->vdisplay,
+			ktime_to_us(time_into_frame_ns));
+}
+
 static void _sde_encoder_phys_signal_frame_done(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_encoder_phys_cmd *cmd_enc;
@@ -1374,7 +1448,7 @@ static void sde_encoder_phys_cmd_enable_helper(
 	if (sde_enc->idle_pc_restore) {
 		qsync_mode = sde_connector_get_qsync_mode(phys_enc->connector);
 		if (qsync_mode)
-			sde_encoder_override_tearcheck_rd_ptr(phys_enc);
+			sde_enc->restore_te_rd_ptr = true;
 	}
 
 	/*
@@ -1575,6 +1649,7 @@ static int sde_encoder_phys_cmd_prepare_for_kickoff(
 	struct sde_hw_tear_check tc_cfg = {0};
 	struct sde_encoder_phys_cmd *cmd_enc =
 			to_sde_encoder_phys_cmd(phys_enc);
+	struct sde_encoder_virt *sde_enc;
 	int ret = 0;
 	bool recovery_events;
 
@@ -1632,6 +1707,12 @@ static int sde_encoder_phys_cmd_prepare_for_kickoff(
 		SDE_EVT32(DRMID(phys_enc->parent), tc_cfg.sync_threshold_start);
 	}
 
+	sde_enc = to_sde_encoder_virt(phys_enc->parent);
+	if (sde_enc->restore_te_rd_ptr) {
+		sde_encoder_restore_tearcheck_rd_ptr(phys_enc);
+		sde_enc->restore_te_rd_ptr = false;
+	}
+
 	SDE_DEBUG_CMDENC(cmd_enc, "pp:%d pending_cnt %d\n",
 			phys_enc->hw_pp->idx - PINGPONG_0,
 			atomic_read(&phys_enc->pending_kickoff_cnt));