소스 검색

disp: msm: sde: add support for qsync simulated panel logic

Currently, QSYNC sim panels are not fully emulating panel
side logic to allow different refresh rate depending on when
the frame is received by the panel.

This change adds the logic to reconfigure the TE watchdog at
different frame rate depending on when the frame is sent to the
simulated QSYNC panel.

Change-Id: I3f0de73976a0fc5748a76c4f7ab00205d1af9a1b
Signed-off-by: Amine Najahi <[email protected]>
Amine Najahi 2 년 전
부모
커밋
2d90a7d4a2
3개의 변경된 파일167개의 추가작업 그리고 0개의 파일을 삭제
  1. 27 0
      msm/sde/sde_encoder.h
  2. 2 0
      msm/sde/sde_encoder_phys.h
  3. 138 0
      msm/sde/sde_encoder_phys_cmd.c

+ 27 - 0
msm/sde/sde_encoder.h

@@ -111,6 +111,33 @@ enum sde_enc_rc_states {
 	SDE_ENC_RC_STATE_IDLE
 };
 
+/*
+ * enum sde_sim_qsync_frame - simulated QSYNC frame type
+ * @SDE_SIM_QSYNC_FRAME_NOMINAL: Frame is triggered early and TE must come at nominal frame rate.
+ * @SDE_SIM_QSYNC_FRAME_EARLY_OR_LATE: Frame could be triggered early or late and TE must adjust
+ *                                     accordingly.
+ * @SDE_SIM_QSYNC_FRAME_TIMEOUT: Frame is triggered too late and TE must adjust to the
+ *                               minimum QSYNC FPS.
+ */
+enum sde_sim_qsync_frame {
+	SDE_SIM_QSYNC_FRAME_NOMINAL,
+	SDE_SIM_QSYNC_FRAME_EARLY_OR_LATE,
+	SDE_SIM_QSYNC_FRAME_TIMEOUT
+};
+
+/*
+ * enum sde_sim_qsync_event - events that simulates a QSYNC panel
+ * @SDE_SIM_QSYNC_EVENT_FRAME_DETECTED: Event when DDIC is detecting a frame.
+ * @SDE_SIM_QSYNC_EVENT_TE_TRIGGER: Event when DDIC is triggering TE signal.
+ */
+enum sde_sim_qsync_event {
+	SDE_SIM_QSYNC_EVENT_FRAME_DETECTED,
+	SDE_SIM_QSYNC_EVENT_TE_TRIGGER
+};
+
+/* Frame rate value to trigger the watchdog TE in 200 us */
+#define SDE_SIM_QSYNC_IMMEDIATE_FPS 5000
+
 /**
  * struct sde_encoder_virt - virtual encoder. Container of one or more physical
  *	encoders. Virtual encoder manages one "logical" display. Physical

+ 2 - 0
msm/sde/sde_encoder_phys.h

@@ -340,6 +340,7 @@ struct sde_encoder_irq {
  *				mode display
  * @recovered:			flag set to true when recovered from pp timeout
  * @autorefresh_disable_trans:   flag set to true during autorefresh disable transition
+ * @sim_qsync_frame:            Current simulated qsync frame type
  */
 struct sde_encoder_phys {
 	struct drm_encoder *parent;
@@ -390,6 +391,7 @@ struct sde_encoder_phys {
 	enum frame_trigger_mode_type frame_trigger_mode;
 	bool recovered;
 	bool autorefresh_disable_trans;
+	enum sde_sim_qsync_frame sim_qsync_frame;
 };
 
 static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)

+ 138 - 0
msm/sde/sde_encoder_phys_cmd.c

@@ -276,6 +276,128 @@ void sde_encoder_restore_tearcheck_rd_ptr(struct sde_encoder_phys *phys_enc)
 			ktime_to_us(time_into_frame_ns));
 }
 
+static void _sde_encoder_phys_cmd_setup_sim_qsync_frame(struct sde_encoder_phys *phys_enc,
+		struct msm_display_info *disp_info, enum sde_sim_qsync_frame frame)
+{
+	struct sde_encoder_virt *sde_enc;
+	struct sde_connector *sde_conn;
+	struct drm_connector *conn;
+	u32 qsync_min_fps = 0, nominal_fps = 0, frame_rate = 0;
+	u32 nominal_period_us, qsync_min_period_us, time_since_vsync_us;
+	int time_before_nominal_vsync_us, time_before_timeout_vsync_us;
+	bool early_frame = false, late_frame = false, slow_frame = false;
+
+	if (!phys_enc || !phys_enc->hw_intf)
+		return;
+
+	sde_enc = to_sde_encoder_virt(phys_enc->parent);
+	sde_conn = to_sde_connector(phys_enc->connector);
+	conn = phys_enc->connector;
+	nominal_fps = sde_enc->mode_info.frame_rate;
+	qsync_min_fps = sde_enc->mode_info.qsync_min_fps;
+
+	if (!nominal_fps || !qsync_min_fps) {
+		SDE_ERROR("invalid fps values %d, %d\n", nominal_fps, qsync_min_fps);
+		return;
+	}
+
+	spin_lock(phys_enc->enc_spinlock);
+	switch (frame) {
+	case SDE_SIM_QSYNC_FRAME_NOMINAL:
+		frame_rate = nominal_fps;
+		break;
+
+	case SDE_SIM_QSYNC_FRAME_EARLY_OR_LATE:
+		time_since_vsync_us = ktime_to_us(ktime_sub(ktime_get(),
+				phys_enc->last_vsync_timestamp));
+
+		nominal_period_us = mult_frac(USEC_PER_SEC, 1, nominal_fps);
+		time_before_nominal_vsync_us = nominal_period_us - time_since_vsync_us;
+
+		qsync_min_period_us = mult_frac(USEC_PER_SEC, 1, qsync_min_fps);
+		time_before_timeout_vsync_us = qsync_min_period_us - time_since_vsync_us;
+
+		early_frame = (time_before_nominal_vsync_us > 0) ? true : false;
+		late_frame = (time_before_nominal_vsync_us <= 0) ? true : false;
+
+		/*
+		 * In simulation, a slow frame would happen if device enters idle power collapse
+		 * and wakes up after the QSYNC timeout period. In that case the last VSYNC time
+		 * stamp that was recorded when the device was up would not be a valid reference
+		 * to determine if the frame after idle power collapse is early or late and when
+		 * the next VSYNC should come.
+		 *
+		 * Thus, the simplest thing is to trigger the watchdog TE immediately and recover
+		 * in the next frame.
+		 */
+		slow_frame = (time_before_timeout_vsync_us <= 0) ? true : false;
+		if (early_frame)
+			frame_rate = mult_frac(USEC_PER_SEC, 1, time_before_nominal_vsync_us);
+		else if (late_frame || slow_frame)
+			frame_rate = SDE_SIM_QSYNC_IMMEDIATE_FPS;
+
+		SDE_EVT32(DRMID(phys_enc->parent), time_since_vsync_us, nominal_fps, qsync_min_fps,
+				nominal_period_us, qsync_min_period_us,
+				time_before_nominal_vsync_us, time_before_timeout_vsync_us,
+				early_frame, late_frame, slow_frame, frame_rate);
+		break;
+
+	case SDE_SIM_QSYNC_FRAME_TIMEOUT:
+		frame_rate = qsync_min_fps;
+		break;
+
+	default:
+		frame_rate = qsync_min_fps;
+		SDE_ERROR("invalid frame %d\n", frame);
+		break;
+	}
+
+	SDE_EVT32(DRMID(phys_enc->parent), frame, qsync_min_fps, frame_rate);
+	phys_enc->ops.control_te(phys_enc, false);
+	phys_enc->hw_intf->ops.setup_vsync_source(phys_enc->hw_intf, frame_rate);
+	phys_enc->hw_intf->ops.vsync_sel(phys_enc->hw_intf, SDE_VSYNC_SOURCE_WD_TIMER_0);
+	phys_enc->ops.control_te(phys_enc, true);
+	phys_enc->sim_qsync_frame = frame;
+	spin_unlock(phys_enc->enc_spinlock);
+}
+
+static void _sde_encoder_phys_cmd_process_sim_qsync_event(struct sde_encoder_phys *phys_enc,
+		enum sde_sim_qsync_event event)
+{
+	u32 qsync_mode = 0;
+	struct sde_encoder_virt *sde_enc;
+
+	sde_enc = to_sde_encoder_virt(phys_enc->parent);
+	if (!sde_enc->disp_info.is_te_using_watchdog_timer || !sde_enc->mode_info.qsync_min_fps)
+		return;
+
+	qsync_mode = sde_connector_get_qsync_mode(phys_enc->connector);
+	SDE_EVT32_IRQ(DRMID(phys_enc->parent),
+			ktime_to_us(ktime_get()) - ktime_to_us(phys_enc->last_vsync_timestamp),
+			qsync_mode, phys_enc->sim_qsync_frame, event);
+
+	switch (event) {
+	case SDE_SIM_QSYNC_EVENT_FRAME_DETECTED:
+		if (qsync_mode)
+			_sde_encoder_phys_cmd_setup_sim_qsync_frame(phys_enc, &sde_enc->disp_info,
+					SDE_SIM_QSYNC_FRAME_EARLY_OR_LATE);
+		break;
+
+	case SDE_SIM_QSYNC_EVENT_TE_TRIGGER:
+		if (qsync_mode)
+			_sde_encoder_phys_cmd_setup_sim_qsync_frame(phys_enc, &sde_enc->disp_info,
+					SDE_SIM_QSYNC_FRAME_TIMEOUT);
+		else if (phys_enc->sim_qsync_frame != SDE_SIM_QSYNC_FRAME_NOMINAL)
+			_sde_encoder_phys_cmd_setup_sim_qsync_frame(phys_enc, &sde_enc->disp_info,
+					SDE_SIM_QSYNC_FRAME_NOMINAL);
+		break;
+
+	default:
+		SDE_ERROR("invalid event %d\n", event);
+		break;
+	}
+}
+
 static void _sde_encoder_phys_signal_frame_done(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_encoder_phys_cmd *cmd_enc;
@@ -421,6 +543,8 @@ static void sde_encoder_phys_cmd_te_rd_ptr_irq(void *arg, int irq_idx)
 		info[0].rd_ptr_line_count, info[1].pp_idx, info[1].intf_idx,
 		info[1].intf_frame_count, info[1].wr_ptr_line_count, info[1].rd_ptr_line_count);
 
+	_sde_encoder_phys_cmd_process_sim_qsync_event(phys_enc, SDE_SIM_QSYNC_EVENT_TE_TRIGGER);
+
 	if (phys_enc->parent_ops.handle_vblank_virt)
 		phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
 			phys_enc);
@@ -2252,6 +2376,19 @@ static void sde_encoder_phys_cmd_trigger_start(
 	cmd_enc->wr_ptr_wait_success = false;
 }
 
+static void sde_encoder_phys_cmd_handle_post_kickoff(struct sde_encoder_phys *phys_enc)
+{
+	if (!phys_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+
+	if (sde_encoder_phys_cmd_is_master(phys_enc))
+		_sde_encoder_phys_cmd_process_sim_qsync_event(phys_enc,
+				SDE_SIM_QSYNC_EVENT_FRAME_DETECTED);
+
+}
+
 static void _sde_encoder_phys_cmd_calculate_wd_params(struct sde_encoder_phys *phys_enc)
 {
 	u32 nominal_te_value;
@@ -2364,6 +2501,7 @@ static void sde_encoder_phys_cmd_init_ops(struct sde_encoder_phys_ops *ops)
 	ops->add_to_minidump = sde_encoder_phys_cmd_add_enc_to_minidump;
 	ops->disable_autorefresh = _sde_encoder_phys_disable_autorefresh;
 	ops->idle_pc_cache_display_status = sde_encoder_phys_cmd_store_ltj_values;
+	ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff;
 }
 
 static inline bool sde_encoder_phys_cmd_intf_te_supported(