Browse Source

Merge "disp: msm: sde: enable precise vblank feature support for waipio"

qctecmdr 4 years ago
parent
commit
60471cb6a1

+ 70 - 10
msm/sde/sde_crtc.c

@@ -2288,7 +2288,7 @@ static void _sde_crtc_dest_scaler_setup(struct drm_crtc *crtc)
 	}
 }
 
-static void sde_crtc_frame_event_cb(void *data, u32 event)
+static void sde_crtc_frame_event_cb(void *data, u32 event, ktime_t ts)
 {
 	struct drm_crtc *crtc = (struct drm_crtc *)data;
 	struct sde_crtc *sde_crtc;
@@ -2358,9 +2358,9 @@ static void sde_crtc_frame_event_cb(void *data, u32 event)
 	}
 
 	fevent->event = event;
+	fevent->ts = ts;
 	fevent->crtc = crtc;
 	fevent->connector = cb_data->connector;
-	fevent->ts = ktime_get();
 	kthread_queue_work(&priv->event_thread[crtc_id].worker, &fevent->work);
 }
 
@@ -2508,23 +2508,23 @@ u32 sde_crtc_get_dfps_maxfps(struct drm_crtc *crtc)
 	return 0;
 }
 
-static void sde_crtc_vblank_cb(void *data)
+static void sde_crtc_vblank_cb(void *data, ktime_t ts)
 {
 	struct drm_crtc *crtc = (struct drm_crtc *)data;
 	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
 
 	/* keep statistics on vblank callback - with auto reset via debugfs */
 	if (ktime_compare(sde_crtc->vblank_cb_time, ktime_set(0, 0)) == 0)
-		sde_crtc->vblank_cb_time = ktime_get();
+		sde_crtc->vblank_cb_time = ts;
 	else
 		sde_crtc->vblank_cb_count++;
 
-	sde_crtc->vblank_last_cb_time = ktime_get();
+	sde_crtc->vblank_last_cb_time = ts;
 	sysfs_notify_dirent(sde_crtc->vsync_event_sf);
 
 	drm_crtc_handle_vblank(crtc);
-	DRM_DEBUG_VBL("crtc%d\n", crtc->base.id);
-	SDE_EVT32_VERBOSE(DRMID(crtc));
+	DRM_DEBUG_VBL("crtc%d, ts:%llu\n", crtc->base.id, ktime_to_us(ts));
+	SDE_EVT32_VERBOSE(DRMID(crtc), ktime_to_us(ts));
 }
 
 static void _sde_crtc_retire_event(struct drm_connector *connector,
@@ -4409,6 +4409,8 @@ static void sde_crtc_enable(struct drm_crtc *crtc,
 	if (!sde_crtc->enabled) {
 		/* cache the encoder mask now for vblank work */
 		sde_crtc->cached_encoder_mask = crtc->state->encoder_mask;
+		/* max possible vsync_cnt(atomic_t) soft counter */
+		drm_crtc_set_max_vblank_count(crtc, INT_MAX);
 		drm_crtc_vblank_on(crtc);
 	}
 
@@ -5281,6 +5283,47 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en)
 	return 0;
 }
 
+static u32 sde_crtc_get_vblank_counter(struct drm_crtc *crtc)
+{
+	struct drm_encoder *encoder;
+	struct sde_crtc *sde_crtc;
+
+	if (!crtc)
+		return 0;
+
+	sde_crtc = to_sde_crtc(crtc);
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, sde_crtc->cached_encoder_mask) {
+		if (sde_encoder_in_clone_mode(encoder))
+			continue;
+
+		return sde_encoder_get_frame_count(encoder);
+	}
+
+	return 0;
+}
+
+static bool sde_crtc_get_vblank_timestamp(struct drm_crtc *crtc, int *max_error,
+				ktime_t *tvblank, bool in_vblank_irq)
+{
+	struct drm_encoder *encoder;
+	struct sde_crtc *sde_crtc;
+
+	if (!crtc)
+		return false;
+
+	sde_crtc = to_sde_crtc(crtc);
+
+	drm_for_each_encoder_mask(encoder, crtc->dev, sde_crtc->cached_encoder_mask) {
+		if (sde_encoder_in_clone_mode(encoder))
+			continue;
+
+		return sde_encoder_get_vblank_timestamp(encoder, tvblank);
+	}
+
+	return false;
+}
+
 static void sde_crtc_install_dest_scale_properties(struct sde_crtc *sde_crtc,
 		struct sde_mdss_cfg *catalog, struct sde_kms_info *info)
 {
@@ -6531,6 +6574,23 @@ static const struct drm_crtc_funcs sde_crtc_funcs = {
 	.early_unregister = sde_crtc_early_unregister,
 };
 
+static const struct drm_crtc_funcs sde_crtc_funcs_v1 = {
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = sde_crtc_destroy,
+	.enable_vblank = sde_crtc_enable_vblank,
+	.disable_vblank = sde_crtc_disable_vblank,
+	.page_flip = drm_atomic_helper_page_flip,
+	.atomic_set_property = sde_crtc_atomic_set_property,
+	.atomic_get_property = sde_crtc_atomic_get_property,
+	.reset = sde_crtc_reset,
+	.atomic_duplicate_state = sde_crtc_duplicate_state,
+	.atomic_destroy_state = sde_crtc_destroy_state,
+	.late_register = sde_crtc_late_register,
+	.early_unregister = sde_crtc_early_unregister,
+	.get_vblank_timestamp = sde_crtc_get_vblank_timestamp,
+	.get_vblank_counter = sde_crtc_get_vblank_counter,
+};
+
 static const struct drm_crtc_helper_funcs sde_crtc_helper_funcs = {
 	.mode_fixup = sde_crtc_mode_fixup,
 	.disable = sde_crtc_disable,
@@ -6806,6 +6866,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 	struct sde_crtc *sde_crtc = NULL;
 	struct msm_drm_private *priv = NULL;
 	struct sde_kms *kms = NULL;
+	const struct drm_crtc_funcs *crtc_funcs;
 	int i, rc;
 
 	priv = dev->dev_private;
@@ -6845,9 +6906,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 				sde_crtc_frame_event_work);
 	}
 
-	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs,
-				NULL);
-
+	crtc_funcs = kms->catalog->has_precise_vsync_ts ? &sde_crtc_funcs_v1 : &sde_crtc_funcs;
+	drm_crtc_init_with_planes(dev, crtc, plane, NULL, crtc_funcs, NULL);
 	drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs);
 
 	/* save user friendly CRTC name for later */

+ 139 - 11
msm/sde/sde_encoder.c

@@ -146,6 +146,73 @@ void sde_encoder_uidle_enable(struct drm_encoder *drm_enc, bool enable)
 	}
 }
 
+ktime_t sde_encoder_calc_last_vsync_timestamp(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder_virt *sde_enc;
+	struct sde_encoder_phys *cur_master;
+	u64 vsync_counter, qtmr_counter, hw_diff, hw_diff_ns, frametime_ns;
+	ktime_t tvblank, cur_time;
+	struct intf_status intf_status = {0};
+	u32 fps;
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	cur_master = sde_enc->cur_master;
+	fps = sde_encoder_get_fps(drm_enc);
+
+	if (!cur_master || !cur_master->hw_intf || !fps
+		|| !cur_master->hw_intf->ops.get_vsync_timestamp
+		|| (!sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE)
+			&& !sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_VIDEO_MODE)))
+		return 0;
+
+	/*
+	 * avoid calculation and rely on ktime_get, if programmable fetch is enabled
+	 * as the HW VSYNC timestamp will be updated at panel vsync and not at MDP VSYNC
+	 */
+	if (cur_master->hw_intf->ops.get_status) {
+		cur_master->hw_intf->ops.get_status(cur_master->hw_intf, &intf_status);
+		if (intf_status.is_prog_fetch_en)
+			return 0;
+	}
+
+	vsync_counter = cur_master->hw_intf->ops.get_vsync_timestamp(cur_master->hw_intf);
+	qtmr_counter = arch_timer_read_counter();
+	cur_time = ktime_get_ns();
+
+	/* check for counter rollover between the two timestamps [56 bits] */
+	if (qtmr_counter < vsync_counter) {
+		hw_diff = (0xffffffffffffff - vsync_counter) + qtmr_counter;
+		SDE_EVT32(DRMID(drm_enc), vsync_counter >> 32, vsync_counter,
+				qtmr_counter >> 32, qtmr_counter, hw_diff,
+				fps, SDE_EVTLOG_FUNC_CASE1);
+	} else {
+		hw_diff = qtmr_counter - vsync_counter;
+	}
+
+	hw_diff_ns = DIV_ROUND_UP(hw_diff * 1000 * 10, 192); /* 19.2 MHz clock */
+	frametime_ns = DIV_ROUND_UP(1000000000, fps);
+
+	/* avoid setting timestamp, if diff is more than one vsync */
+	if (ktime_compare(hw_diff_ns, frametime_ns) > 0) {
+		tvblank = 0;
+		SDE_EVT32(DRMID(drm_enc), vsync_counter >> 32, vsync_counter,
+				qtmr_counter >> 32, qtmr_counter, ktime_to_us(hw_diff_ns),
+				fps, SDE_EVTLOG_ERROR);
+	} else {
+		tvblank = ktime_sub_ns(cur_time, hw_diff_ns);
+	}
+
+	SDE_DEBUG_ENC(sde_enc,
+			"vsync:%llu, qtmr:%llu, diff_ns:%llu, ts:%llu, cur_ts:%llu, fps:%d\n",
+			vsync_counter, qtmr_counter, ktime_to_us(hw_diff_ns),
+			ktime_to_us(tvblank), ktime_to_us(cur_time), fps);
+
+	SDE_EVT32_VERBOSE(DRMID(drm_enc), hw_diff >> 32, hw_diff, ktime_to_us(hw_diff_ns),
+			ktime_to_us(tvblank), ktime_to_us(cur_time), fps, SDE_EVTLOG_FUNC_CASE2);
+
+	return tvblank;
+}
+
 static void _sde_encoder_pm_qos_add_request(struct drm_encoder *drm_enc)
 {
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
@@ -3066,6 +3133,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
 {
 	struct sde_encoder_virt *sde_enc = NULL;
 	unsigned long lock_flags;
+	ktime_t ts = 0;
 
 	if (!drm_enc || !phy_enc)
 		return;
@@ -3073,16 +3141,26 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
 	SDE_ATRACE_BEGIN("encoder_vblank_callback");
 	sde_enc = to_sde_encoder_virt(drm_enc);
 
+	/*
+	 * calculate accurate vsync timestamp when available
+	 * set current time otherwise
+	 */
+	if (phy_enc->sde_kms && phy_enc->sde_kms->catalog->has_precise_vsync_ts)
+		ts = sde_encoder_calc_last_vsync_timestamp(drm_enc);
+	if (!ts)
+		ts = ktime_get();
+
 	spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
+	phy_enc->last_vsync_timestamp = ts;
+	atomic_inc(&phy_enc->vsync_cnt);
 	if (sde_enc->crtc_vblank_cb)
-		sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data);
+		sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data, ts);
 	spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
 
 	if (phy_enc->sde_kms &&
 			phy_enc->sde_kms->catalog->uidle_cfg.debugfs_perf)
 		sde_encoder_perf_uidle_status(phy_enc->sde_kms, sde_enc->crtc);
 
-	atomic_inc(&phy_enc->vsync_cnt);
 	SDE_ATRACE_END("encoder_vblank_callback");
 }
 
@@ -3111,7 +3189,7 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc,
 }
 
 void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
-		void (*vbl_cb)(void *), void *vbl_data)
+		void (*vbl_cb)(void *, ktime_t), void *vbl_data)
 {
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
 	unsigned long lock_flags;
@@ -3147,7 +3225,7 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
 }
 
 void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
-			void (*frame_event_cb)(void *, u32 event),
+			void (*frame_event_cb)(void *, u32 event, ktime_t ts),
 			struct drm_crtc *crtc)
 {
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
@@ -3174,14 +3252,16 @@ static void sde_encoder_frame_done_callback(
 		struct sde_encoder_phys *ready_phys, u32 event)
 {
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+	struct sde_kms *sde_kms = sde_encoder_get_kms(&sde_enc->base);
 	unsigned int i;
 	bool trigger = true;
 	bool is_cmd_mode = false;
 	enum sde_rm_topology_name topology = SDE_RM_TOPOLOGY_NONE;
+	ktime_t ts = 0;
 
-	if (!drm_enc || !sde_enc->cur_master) {
-		SDE_ERROR("invalid param: drm_enc %pK, cur_master %pK\n",
-				drm_enc, drm_enc ? sde_enc->cur_master : 0);
+	if (!sde_kms || !sde_enc->cur_master) {
+		SDE_ERROR("invalid param: sde_kms %pK, cur_master %pK\n",
+				sde_kms, sde_enc->cur_master);
 		return;
 	}
 
@@ -3190,6 +3270,19 @@ static void sde_encoder_frame_done_callback(
 	if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE))
 		is_cmd_mode = true;
 
+	/* get precise vsync timestamp for retire fence, if precise vsync timestamp is enabled */
+	if (sde_kms->catalog->has_precise_vsync_ts
+	    && (event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE)
+	    && (!(event & (SDE_ENCODER_FRAME_EVENT_ERROR | SDE_ENCODER_FRAME_EVENT_PANEL_DEAD))))
+		ts = sde_encoder_calc_last_vsync_timestamp(drm_enc);
+
+	/*
+	 * get current ktime for other events and when precise timestamp is not
+	 * available for retire-fence
+	 */
+	if (!ts)
+		ts = ktime_get();
+
 	if (event & (SDE_ENCODER_FRAME_EVENT_DONE
 			| SDE_ENCODER_FRAME_EVENT_ERROR
 			| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD) && is_cmd_mode) {
@@ -3223,15 +3316,13 @@ static void sde_encoder_frame_done_callback(
 		if (trigger) {
 			if (sde_enc->crtc_frame_event_cb)
 				sde_enc->crtc_frame_event_cb(
-					&sde_enc->crtc_frame_event_cb_data,
-					event);
+					&sde_enc->crtc_frame_event_cb_data, event, ts);
 			for (i = 0; i < sde_enc->num_phys_encs; i++)
 				atomic_add_unless(&sde_enc->frame_done_cnt[i],
 						-1, 0);
 		}
 	} else if (sde_enc->crtc_frame_event_cb) {
-		sde_enc->crtc_frame_event_cb(
-				&sde_enc->crtc_frame_event_cb_data, event);
+		sde_enc->crtc_frame_event_cb(&sde_enc->crtc_frame_event_cb_data, event, ts);
 	}
 }
 
@@ -5142,6 +5233,43 @@ enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder)
 	return INTF_MODE_NONE;
 }
 
+u32 sde_encoder_get_frame_count(struct drm_encoder *encoder)
+{
+	struct sde_encoder_virt *sde_enc = NULL;
+	struct sde_encoder_phys *phys;
+
+	if (!encoder) {
+		SDE_ERROR("invalid encoder\n");
+		return 0;
+	}
+	sde_enc = to_sde_encoder_virt(encoder);
+
+	phys = sde_enc->cur_master;
+
+	return phys ? atomic_read(&phys->vsync_cnt) : 0;
+}
+
+bool sde_encoder_get_vblank_timestamp(struct drm_encoder *encoder,
+		ktime_t *tvblank)
+{
+	struct sde_encoder_virt *sde_enc = NULL;
+	struct sde_encoder_phys *phys;
+
+	if (!encoder) {
+		SDE_ERROR("invalid encoder\n");
+		return false;
+	}
+	sde_enc = to_sde_encoder_virt(encoder);
+
+	phys = sde_enc->cur_master;
+	if (!phys)
+		return false;
+
+	*tvblank = phys->last_vsync_timestamp;
+
+	return *tvblank ? true : false;
+}
+
 static void _sde_encoder_cache_hw_res_cont_splash(
 		struct drm_encoder *encoder,
 		struct sde_kms *sde_kms)

+ 26 - 4
msm/sde/sde_encoder.h

@@ -220,13 +220,13 @@ struct sde_encoder_virt {
 	bool intfs_swapped;
 	bool qdss_status;
 
-	void (*crtc_vblank_cb)(void *data);
+	void (*crtc_vblank_cb)(void *data, ktime_t ts);
 	void *crtc_vblank_cb_data;
 
 	struct dentry *debugfs_root;
 	struct mutex enc_lock;
 	atomic_t frame_done_cnt[MAX_PHYS_ENCODERS_PER_VIRTUAL];
-	void (*crtc_frame_event_cb)(void *data, u32 event);
+	void (*crtc_frame_event_cb)(void *data, u32 event, ktime_t ts);
 	struct sde_kms_frame_event_cb_data crtc_frame_event_cb_data;
 
 	struct sde_rsc_client *rsc_client;
@@ -290,7 +290,7 @@ void sde_encoder_early_wakeup(struct drm_encoder *drm_enc);
  * @data:	user data provided to callback
  */
 void sde_encoder_register_vblank_callback(struct drm_encoder *encoder,
-		void (*cb)(void *), void *data);
+		void (*cb)(void *, ktime_t), void *data);
 
 /**
  * sde_encoder_register_frame_event_callback - provide callback to encoder that
@@ -300,7 +300,7 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *encoder,
  * @crtc:	pointer to drm_crtc object interested in frame events
  */
 void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
-		void (*cb)(void *, u32), struct drm_crtc *crtc);
+		void (*cb)(void *, u32, ktime_t), struct drm_crtc *crtc);
 
 /**
  * sde_encoder_get_rsc_client - gets the rsc client state for primary
@@ -386,6 +386,20 @@ u32 sde_encoder_get_fps(struct drm_encoder *encoder);
  */
 enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder);
 
+/*
+ * sde_encoder_get_frame_count - get hardware frame count of the given encoder
+ * @encoder: Pointer to drm encoder object
+ */
+u32 sde_encoder_get_frame_count(struct drm_encoder *encoder);
+
+/*
+ * sde_encoder_get_vblank_timestamp - get the last vsync timestamp
+ * @encoder: Pointer to drm encoder object
+ * @tvblank: vblank timestamp
+ */
+bool sde_encoder_get_vblank_timestamp(struct drm_encoder *encoder,
+		ktime_t *tvblank);
+
 /**
  * sde_encoder_control_te - control enabling/disabling VSYNC_IN_EN
  * @encoder:	encoder pointer
@@ -592,6 +606,14 @@ static inline u32 sde_encoder_get_dfps_maxfps(struct drm_encoder *drm_enc)
  */
 void sde_encoder_virt_reset(struct drm_encoder *drm_enc);
 
+/**
+ * sde_encoder_calc_last_vsync_timestamp - read last HW vsync timestamp counter
+ *         and calculate the corresponding vsync ktime. Return ktime_get
+ *         when HW support is not available
+ * @drm_enc:    Pointer to drm encoder structure
+ */
+ktime_t sde_encoder_calc_last_vsync_timestamp(struct drm_encoder *drm_enc);
+
 /**
  * sde_encoder_get_kms - retrieve the kms from encoder
  * @drm_enc:    Pointer to drm encoder structure

+ 2 - 0
msm/sde/sde_encoder_phys.h

@@ -279,6 +279,7 @@ struct sde_encoder_irq {
  * @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
+ * @last_vsync_timestamp:	store last vsync timestamp
  * @underrun_cnt:	Underrun count for the physical encoder
  * @pending_kickoff_cnt:	Atomic counter tracking the number of kickoffs
  *				vs. the number of done/vblank irqs. Should hover
@@ -329,6 +330,7 @@ struct sde_encoder_phys {
 	atomic_t vblank_cached_refcount;
 	atomic_t wbirq_refcount;
 	atomic_t vsync_cnt;
+	ktime_t last_vsync_timestamp;
 	atomic_t underrun_cnt;
 	atomic_t pending_kickoff_cnt;
 	atomic_t pending_retire_fence_cnt;

+ 9 - 6
msm/sde/sde_encoder_phys_cmd.c

@@ -439,6 +439,9 @@ static void sde_encoder_phys_cmd_cont_splash_mode_set(
 			hw_pp->ops.get_autorefresh(hw_pp,
 					&cmd_enc->autorefresh.cfg);
 		}
+
+		if (hw_intf->ops.reset_counter)
+			hw_intf->ops.reset_counter(hw_intf);
 	}
 
 	_sde_encoder_phys_cmd_setup_irq_hw_idx(phys_enc);
@@ -682,12 +685,9 @@ static bool _sde_encoder_phys_cmd_is_ongoing_pptx(
 		hw_pp->ops.get_vsync_info(hw_pp, &info);
 	}
 
-	SDE_EVT32(DRMID(phys_enc->parent),
-			phys_enc->hw_pp->idx - PINGPONG_0,
-			phys_enc->hw_intf->idx - INTF_0,
-			atomic_read(&phys_enc->pending_kickoff_cnt),
-			info.wr_ptr_line_count,
-			phys_enc->cached_mode.vdisplay);
+	SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0,
+		phys_enc->hw_intf->idx - INTF_0, atomic_read(&phys_enc->pending_kickoff_cnt),
+		info.wr_ptr_line_count, info.intf_frame_count, phys_enc->cached_mode.vdisplay);
 
 	if (info.wr_ptr_line_count > 0 && info.wr_ptr_line_count <
 			phys_enc->cached_mode.vdisplay)
@@ -1348,6 +1348,9 @@ static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc)
 			phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp,
 					false);
 		sde_encoder_helper_phys_disable(phys_enc, NULL);
+
+		if (phys_enc->hw_intf->ops.reset_counter)
+			phys_enc->hw_intf->ops.reset_counter(phys_enc->hw_intf);
 	}
 
 	phys_enc->enable_state = SDE_ENC_DISABLED;

+ 3 - 0
msm/sde/sde_encoder_phys_vid.c

@@ -1085,6 +1085,9 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
 	sde_encoder_phys_inc_pending(phys_enc);
 	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
 
+	if (phys_enc->hw_intf->ops.reset_counter)
+		phys_enc->hw_intf->ops.reset_counter(phys_enc->hw_intf);
+
 	sde_encoder_phys_vid_single_vblank_wait(phys_enc);
 	if (phys_enc->hw_intf->ops.get_status)
 		phys_enc->hw_intf->ops.get_status(phys_enc->hw_intf,

+ 30 - 4
msm/sde/sde_fence.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
  */
 
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
@@ -12,6 +12,30 @@
 
 #define TIMELINE_VAL_LENGTH		128
 
+int _dma_fence_signal_timestamp_locked(struct dma_fence *fence, ktime_t ts)
+{
+	struct dma_fence_cb *cur, *tmp;
+	struct list_head cb_list;
+
+	lockdep_assert_held(fence->lock);
+
+	if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)))
+		return -EINVAL;
+
+	/* Stash the cb_list before replacing it with the timestamp */
+	list_replace(&fence->cb_list, &cb_list);
+
+	fence->timestamp = ts;
+	set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
+
+	list_for_each_entry_safe(cur, tmp, &cb_list, node) {
+		INIT_LIST_HEAD(&cur->node);
+		cur->func(fence, cur);
+	}
+
+	return 0;
+}
+
 void *sde_sync_get(uint64_t fd)
 {
 	/* force signed compare, fdget accepts an int argument */
@@ -289,7 +313,7 @@ void sde_fence_prepare(struct sde_fence_context *ctx)
 	}
 }
 
-static void _sde_fence_trigger(struct sde_fence_context *ctx, bool error)
+static void _sde_fence_trigger(struct sde_fence_context *ctx, bool error, ktime_t ts)
 {
 	unsigned long flags;
 	struct sde_fence *fc, *next;
@@ -307,7 +331,9 @@ static void _sde_fence_trigger(struct sde_fence_context *ctx, bool error)
 		spin_lock_irqsave(&ctx->lock, flags);
 		if (error)
 			dma_fence_set_error(&fc->base, -EBUSY);
-		is_signaled = dma_fence_is_signaled_locked(&fc->base);
+		is_signaled = sde_fence_signaled(&fc->base);
+		if (is_signaled)
+			_dma_fence_signal_timestamp_locked(&fc->base, ts);
 		spin_unlock_irqrestore(&ctx->lock, flags);
 
 		if (is_signaled) {
@@ -397,7 +423,7 @@ void sde_fence_signal(struct sde_fence_context *ctx, ktime_t ts,
 	SDE_EVT32(ctx->drm_id, ctx->done_count, ctx->commit_count,
 			ktime_to_us(ts));
 
-	_sde_fence_trigger(ctx, (fence_event == SDE_FENCE_SIGNAL_ERROR));
+	_sde_fence_trigger(ctx, (fence_event == SDE_FENCE_SIGNAL_ERROR), ts);
 }
 
 void sde_fence_timeline_status(struct sde_fence_context *ctx,

+ 5 - 1
msm/sde/sde_hw_catalog.c

@@ -2447,8 +2447,11 @@ static int sde_intf_parse_dt(struct device_node *np,
 			set_bit(SDE_INTF_TE_ALIGN_VSYNC, &intf->features);
 
 		if (SDE_HW_MAJOR(sde_cfg->hwversion) >=
-				SDE_HW_MAJOR(SDE_HW_VER_810))
+				SDE_HW_MAJOR(SDE_HW_VER_810)) {
 			set_bit(SDE_INTF_WD_TIMER, &intf->features);
+			set_bit(SDE_INTF_RESET_COUNTER, &intf->features);
+			set_bit(SDE_INTF_VSYNC_TIMESTAMP, &intf->features);
+		}
 	}
 
 end:
@@ -5119,6 +5122,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
 		sde_cfg->sspp_multirect_error = true;
 		sde_cfg->has_fp16 = true;
 		set_bit(SDE_MDP_PERIPH_TOP_0_REMOVED, &sde_cfg->mdp[0].features);
+		sde_cfg->has_precise_vsync_ts = true;
 	} else {
 		SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
 		sde_cfg->perf.min_prefill_lines = 0xffff;

+ 6 - 0
msm/sde/sde_hw_catalog.h

@@ -498,6 +498,8 @@ enum {
  * @SDE_INTF_TE_ALIGN_VSYNC     INTF block has POMS Align vsync support
  * @SDE_INTF_WD_TIMER          INTF block has WD Timer support
  * @SDE_INTF_STATUS             INTF block has INTF_STATUS register
+ * @SDE_INTF_RESET_COUNTER      INTF block has frame/line counter reset support
+ * @SDE_INTF_VSYNC_TIMESTAMP    INTF block has vsync timestamp logged
  * @SDE_INTF_MAX
  */
 enum {
@@ -506,6 +508,8 @@ enum {
 	SDE_INTF_TE_ALIGN_VSYNC,
 	SDE_INTF_WD_TIMER,
 	SDE_INTF_STATUS,
+	SDE_INTF_RESET_COUNTER,
+	SDE_INTF_VSYNC_TIMESTAMP,
 	SDE_INTF_MAX
 };
 
@@ -1507,6 +1511,7 @@ struct sde_perf_cfg {
  * @has_cursor    indicates if hardware cursor is supported
  * @has_vig_p010  indicates if vig pipe supports p010 format
  * @has_fp16      indicates if FP16 format is supported on SSPP pipes
+ * @has_precise_vsync_ts  indicates if HW has vsyc timestamp logging capability
  * @mdss_hw_block_size  Max offset of MDSS_HW block (0 offset), used for debug
  * @inline_rot_formats formats supported by the inline rotator feature
  * @irq_offset_list     list of sde_intr_irq_offsets to initialize irq table
@@ -1588,6 +1593,7 @@ struct sde_mdss_cfg {
 	bool has_cursor;
 	bool has_vig_p010;
 	bool has_fp16;
+	bool has_precise_vsync_ts;
 	u32 mdss_hw_block_size;
 	u32 mdss_count;
 	struct sde_mdss_base_cfg mdss[MAX_BLOCKS];

+ 42 - 3
msm/sde/sde_hw_intf.c

@@ -61,6 +61,9 @@
 #define INTF_MISR_CTRL                  0x180
 #define INTF_MISR_SIGNATURE             0x184
 
+#define INTF_VSYNC_TIMESTAMP_CTRL       0x210
+#define INTF_VSYNC_TIMESTAMP0           0x214
+#define INTF_VSYNC_TIMESTAMP1           0x218
 #define INTF_WD_TIMER_0_CTL             0x230
 #define INTF_WD_TIMER_0_CTL2            0x234
 #define INTF_WD_TIMER_0_LOAD_VALUE      0x238
@@ -192,6 +195,27 @@ static inline void _check_and_set_comp_bit(struct sde_hw_intf *ctx,
 		(*intf_cfg2) |= BIT(12);
 }
 
+static void sde_hw_intf_reset_counter(struct sde_hw_intf *ctx)
+{
+	struct sde_hw_blk_reg_map *c = &ctx->hw;
+
+	SDE_REG_WRITE(c, INTF_LINE_COUNT, BIT(31));
+}
+
+static u64 sde_hw_intf_get_vsync_timestamp(struct sde_hw_intf *ctx)
+{
+	struct sde_hw_blk_reg_map *c = &ctx->hw;
+	u32 timestamp_lo, timestamp_hi;
+	u64 timestamp = 0;
+
+	timestamp_hi = SDE_REG_READ(c, INTF_VSYNC_TIMESTAMP1);
+	timestamp_lo = SDE_REG_READ(c, INTF_VSYNC_TIMESTAMP0);
+	timestamp = timestamp_hi;
+	timestamp = (timestamp << 32) | timestamp_lo;
+
+	return timestamp;
+}
+
 static void sde_hw_intf_setup_timing_engine(struct sde_hw_intf *ctx,
 		const struct intf_timing_params *p,
 		const struct sde_format *fmt)
@@ -371,8 +395,12 @@ static void sde_hw_intf_enable_timing_engine(
 		u8 enable)
 {
 	struct sde_hw_blk_reg_map *c = &intf->hw;
+
 	/* Note: Display interface select is handled in top block hw layer */
 	SDE_REG_WRITE(c, INTF_TIMING_ENGINE_EN, enable != 0);
+
+	if (enable && (intf->cap->features & BIT(SDE_INTF_VSYNC_TIMESTAMP)))
+		SDE_REG_WRITE(c, INTF_VSYNC_TIMESTAMP_CTRL, BIT(0));
 }
 
 static void sde_hw_intf_setup_prg_fetch(
@@ -458,7 +486,7 @@ static void sde_hw_intf_get_status(
 	s->is_en = SDE_REG_READ(c, INTF_TIMING_ENGINE_EN);
 	if (s->is_en) {
 		s->frame_count = SDE_REG_READ(c, INTF_FRAME_COUNT);
-		s->line_count = SDE_REG_READ(c, INTF_LINE_COUNT);
+		s->line_count = SDE_REG_READ(c, INTF_LINE_COUNT) & 0xffff;
 	} else {
 		s->line_count = 0;
 		s->frame_count = 0;
@@ -472,9 +500,10 @@ static void sde_hw_intf_v1_get_status(
 	struct sde_hw_blk_reg_map *c = &intf->hw;
 
 	s->is_en = SDE_REG_READ(c, INTF_STATUS) & BIT(0);
+	s->is_prog_fetch_en = (SDE_REG_READ(c, INTF_CONFIG) & BIT(31));
 	if (s->is_en) {
 		s->frame_count = SDE_REG_READ(c, INTF_FRAME_COUNT);
-		s->line_count = SDE_REG_READ(c, INTF_LINE_COUNT);
+		s->line_count = SDE_REG_READ(c, INTF_LINE_COUNT) & 0xffff;
 	} else {
 		s->line_count = 0;
 		s->frame_count = 0;
@@ -537,7 +566,7 @@ static u32 sde_hw_intf_get_line_count(struct sde_hw_intf *intf)
 
 	c = &intf->hw;
 
-	return SDE_REG_READ(c, INTF_LINE_COUNT);
+	return SDE_REG_READ(c, INTF_LINE_COUNT) & 0xffff;
 }
 
 static u32 sde_hw_intf_get_underrun_line_count(struct sde_hw_intf *intf)
@@ -662,6 +691,10 @@ static int sde_hw_intf_enable_te(struct sde_hw_intf *intf, bool enable)
 
 	c = &intf->hw;
 	SDE_REG_WRITE(c, INTF_TEAR_TEAR_CHECK_EN, enable);
+
+	if (enable && (intf->cap->features & BIT(SDE_INTF_VSYNC_TIMESTAMP)))
+		SDE_REG_WRITE(c, INTF_VSYNC_TIMESTAMP_CTRL, BIT(0));
+
 	return 0;
 }
 
@@ -847,6 +880,12 @@ static void _setup_intf_ops(struct sde_hw_intf_ops *ops,
 		ops->check_and_reset_tearcheck =
 			sde_hw_intf_v1_check_and_reset_tearcheck;
 	}
+
+	if (cap & BIT(SDE_INTF_RESET_COUNTER))
+		ops->reset_counter = sde_hw_intf_reset_counter;
+
+	if (cap & BIT(SDE_INTF_VSYNC_TIMESTAMP))
+		ops->get_vsync_timestamp = sde_hw_intf_get_vsync_timestamp;
 }
 
 static struct sde_hw_blk_ops sde_hw_ops = {

+ 11 - 0
msm/sde/sde_hw_intf.h

@@ -50,6 +50,7 @@ struct intf_prog_fetch {
 
 struct intf_status {
 	u8 is_en;		/* interface timing engine is enabled or not */
+	bool is_prog_fetch_en;	/* interface prog fetch counter is enabled or not */
 	u32 frame_count;	/* frame count since timing engine enabled */
 	u32 line_count;		/* current line count including blanking */
 };
@@ -205,6 +206,16 @@ struct sde_hw_intf_ops {
 	int (*check_and_reset_tearcheck)(struct sde_hw_intf *intf,
 			struct intf_tear_status *status);
 
+	/**
+	 * Reset the interface frame & line counter
+	 */
+	void (*reset_counter)(struct sde_hw_intf *intf);
+
+	/**
+	 * Get the HW vsync timestamp counter
+	 */
+	u64 (*get_vsync_timestamp)(struct sde_hw_intf *intf);
+
 	/**
 	 * Enable processing of 2 pixels per clock
 	 */

+ 8 - 0
msm/sde/sde_kms.c

@@ -4398,6 +4398,14 @@ static int _sde_kms_hw_init_blocks(struct sde_kms *sde_kms,
 		goto perf_err;
 	}
 
+	/*
+	 * set the disable_immediate flag when driver supports the precise vsync
+	 * timestamp as the DRM hooks for vblank timestamp/counters would be set
+	 * based on the feature
+	 */
+	if (sde_kms->catalog->has_precise_vsync_ts)
+		dev->vblank_disable_immediate = true;
+
 	/*
 	 * _sde_kms_drm_obj_init should create the DRM related objects
 	 * i.e. CRTCs, planes, encoders, connectors and so forth