Kaynağa Gözat

disp: msm: sde: implement drm hooks to get precise vblank timestamp

Add precise vblank timestamp support through the DRM framework.
Implement the vblank related hooks to get the vblank count and
timestamp. Use MDSS 8.x, hardware feature that supports logging
of the vsync timestamp counter which can be used to derive the
accurate kernel timestamp. The current ktime would be returned
for older targets to support backward compatibility.

Change-Id: I2d35ed4a643a519e602278b6d16e67ccee16a60b
Signed-off-by: Veera Sundaram Sankaran <[email protected]>
Veera Sundaram Sankaran 4 yıl önce
ebeveyn
işleme
3cef1faa29

+ 68 - 8
msm/sde/sde_crtc.c

@@ -2499,23 +2499,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,
@@ -4400,6 +4400,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);
 	}
 
@@ -5269,6 +5271,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)
 {
@@ -6519,6 +6562,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,
@@ -6794,6 +6854,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;
@@ -6833,9 +6894,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 */

+ 51 - 3
msm/sde/sde_encoder.c

@@ -3133,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;
@@ -3140,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");
 }
 
@@ -3178,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;
@@ -5209,6 +5220,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)

+ 16 - 2
msm/sde/sde_encoder.h

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

+ 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;

+ 2 - 0
msm/sde/sde_hw_catalog.h

@@ -1511,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
@@ -1592,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];

+ 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