Browse Source

disp: msm: sde: enable support to get accurate vsync timestamp

From MDSS 8.x, vsync timestamp counter register is added in all the
interfaces. Add interface to get the vsync counter and use the global
qtmr reference counter to get the counter delta. This can be used
with reference to the curret ktime to deduce the accurate vsync
timestamp. This utility is intended to be used for setting the vblank
and retire fence timestamps which would be notified to user-mode.

Change-Id: I608a284c035cda50053eedbb311f1f54b3d3d557
Signed-off-by: Veera Sundaram Sankaran <[email protected]>
Veera Sundaram Sankaran 4 years ago
parent
commit
b554f01a10

+ 67 - 0
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)
 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);
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);

+ 8 - 0
msm/sde/sde_encoder.h

@@ -592,6 +592,14 @@ static inline u32 sde_encoder_get_dfps_maxfps(struct drm_encoder *drm_enc)
  */
  */
 void sde_encoder_virt_reset(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
  * sde_encoder_get_kms - retrieve the kms from encoder
  * @drm_enc:    Pointer to drm encoder structure
  * @drm_enc:    Pointer to drm encoder structure

+ 1 - 0
msm/sde/sde_hw_catalog.c

@@ -2450,6 +2450,7 @@ static int sde_intf_parse_dt(struct device_node *np,
 				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_WD_TIMER, &intf->features);
 			set_bit(SDE_INTF_RESET_COUNTER, &intf->features);
 			set_bit(SDE_INTF_RESET_COUNTER, &intf->features);
+			set_bit(SDE_INTF_VSYNC_TIMESTAMP, &intf->features);
 		}
 		}
 	}
 	}
 
 

+ 2 - 0
msm/sde/sde_hw_catalog.h

@@ -499,6 +499,7 @@ enum {
  * @SDE_INTF_WD_TIMER          INTF block has WD Timer support
  * @SDE_INTF_WD_TIMER          INTF block has WD Timer support
  * @SDE_INTF_STATUS             INTF block has INTF_STATUS register
  * @SDE_INTF_STATUS             INTF block has INTF_STATUS register
  * @SDE_INTF_RESET_COUNTER      INTF block has frame/line counter reset support
  * @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
  * @SDE_INTF_MAX
  */
  */
 enum {
 enum {
@@ -508,6 +509,7 @@ enum {
 	SDE_INTF_WD_TIMER,
 	SDE_INTF_WD_TIMER,
 	SDE_INTF_STATUS,
 	SDE_INTF_STATUS,
 	SDE_INTF_RESET_COUNTER,
 	SDE_INTF_RESET_COUNTER,
+	SDE_INTF_VSYNC_TIMESTAMP,
 	SDE_INTF_MAX
 	SDE_INTF_MAX
 };
 };
 
 

+ 29 - 0
msm/sde/sde_hw_intf.c

@@ -61,6 +61,9 @@
 #define INTF_MISR_CTRL                  0x180
 #define INTF_MISR_CTRL                  0x180
 #define INTF_MISR_SIGNATURE             0x184
 #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_CTL             0x230
 #define INTF_WD_TIMER_0_CTL2            0x234
 #define INTF_WD_TIMER_0_CTL2            0x234
 #define INTF_WD_TIMER_0_LOAD_VALUE      0x238
 #define INTF_WD_TIMER_0_LOAD_VALUE      0x238
@@ -199,6 +202,20 @@ static void sde_hw_intf_reset_counter(struct sde_hw_intf *ctx)
 	SDE_REG_WRITE(c, INTF_LINE_COUNT, BIT(31));
 	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,
 static void sde_hw_intf_setup_timing_engine(struct sde_hw_intf *ctx,
 		const struct intf_timing_params *p,
 		const struct intf_timing_params *p,
 		const struct sde_format *fmt)
 		const struct sde_format *fmt)
@@ -378,8 +395,12 @@ static void sde_hw_intf_enable_timing_engine(
 		u8 enable)
 		u8 enable)
 {
 {
 	struct sde_hw_blk_reg_map *c = &intf->hw;
 	struct sde_hw_blk_reg_map *c = &intf->hw;
+
 	/* Note: Display interface select is handled in top block hw layer */
 	/* Note: Display interface select is handled in top block hw layer */
 	SDE_REG_WRITE(c, INTF_TIMING_ENGINE_EN, enable != 0);
 	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(
 static void sde_hw_intf_setup_prg_fetch(
@@ -479,6 +500,7 @@ static void sde_hw_intf_v1_get_status(
 	struct sde_hw_blk_reg_map *c = &intf->hw;
 	struct sde_hw_blk_reg_map *c = &intf->hw;
 
 
 	s->is_en = SDE_REG_READ(c, INTF_STATUS) & BIT(0);
 	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) {
 	if (s->is_en) {
 		s->frame_count = SDE_REG_READ(c, INTF_FRAME_COUNT);
 		s->frame_count = SDE_REG_READ(c, INTF_FRAME_COUNT);
 		s->line_count = SDE_REG_READ(c, INTF_LINE_COUNT) & 0xffff;
 		s->line_count = SDE_REG_READ(c, INTF_LINE_COUNT) & 0xffff;
@@ -669,6 +691,10 @@ static int sde_hw_intf_enable_te(struct sde_hw_intf *intf, bool enable)
 
 
 	c = &intf->hw;
 	c = &intf->hw;
 	SDE_REG_WRITE(c, INTF_TEAR_TEAR_CHECK_EN, enable);
 	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;
 	return 0;
 }
 }
 
 
@@ -857,6 +883,9 @@ static void _setup_intf_ops(struct sde_hw_intf_ops *ops,
 
 
 	if (cap & BIT(SDE_INTF_RESET_COUNTER))
 	if (cap & BIT(SDE_INTF_RESET_COUNTER))
 		ops->reset_counter = sde_hw_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 = {
 static struct sde_hw_blk_ops sde_hw_ops = {

+ 6 - 0
msm/sde/sde_hw_intf.h

@@ -50,6 +50,7 @@ struct intf_prog_fetch {
 
 
 struct intf_status {
 struct intf_status {
 	u8 is_en;		/* interface timing engine is enabled or not */
 	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 frame_count;	/* frame count since timing engine enabled */
 	u32 line_count;		/* current line count including blanking */
 	u32 line_count;		/* current line count including blanking */
 };
 };
@@ -210,6 +211,11 @@ struct sde_hw_intf_ops {
 	 */
 	 */
 	void (*reset_counter)(struct sde_hw_intf *intf);
 	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
 	 * Enable processing of 2 pixels per clock
 	 */
 	 */