Browse Source

disp: msm: sde: add support for hwfence profiling

This change adds hwfence input and output fence profiling
registers and debugfs to enable them.
To enable input hw fences timestamps:
echo 0x1 > /d/dri/0/debug/hw_fence_status
To enable output hw fences timestamps:
echo 0x2 > /d/dri/0/debug/hw_fence_status
To enable both, input and output hw fences timestamps:
echo 0x3 > /d/dri/0/debug/hw_fence_status.

Change-Id: I269a38f3843a01ec8c0816890e50bb7d847a4ed9
Signed-off-by: Christina Oliveira <[email protected]>
Christina Oliveira 3 years ago
parent
commit
21ca2acab9

+ 6 - 4
msm/sde/sde_crtc.c

@@ -3801,22 +3801,23 @@ static bool _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
 	int num_hw_fences = 0;
 	struct sde_hw_ctl *hw_ctl;
 	bool input_hw_fences_enable;
+	struct sde_kms *sde_kms = _sde_crtc_get_kms(crtc);
 	int ret;
 
 	SDE_DEBUG("\n");
 
-	if (!crtc || !crtc->state) {
+	if (!crtc || !crtc->state || !sde_kms) {
 		SDE_ERROR("invalid crtc/state %pK\n", crtc);
 		return false;
 	}
-
 	hw_ctl = _sde_crtc_get_hw_ctl(crtc);
 
 	SDE_ATRACE_BEGIN("plane_wait_input_fence");
 
 	/* update ctl hw to wait for ipcc input signal before fetch */
 	if (test_bit(HW_FENCE_IN_FENCES_ENABLE, sde_crtc->hwfence_features_mask) &&
-			!sde_fence_update_input_hw_fence_signal(hw_ctl))
+			!sde_fence_update_input_hw_fence_signal(hw_ctl, sde_kms->debugfs_hw_fence,
+			sde_kms->hw_mdp))
 		ipcc_input_signal_wait = true;
 
 	/* avoid hw-fences in first frame after timing engine enable */
@@ -4626,7 +4627,8 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
 	 * condition between txq update and the hw signal during ctl-done for partial updates
 	 */
 	if (test_bit(HW_FENCE_OUT_FENCES_ENABLE, sde_crtc->hwfence_features_mask) && !is_vid)
-		sde_fence_update_hw_fences_txq(sde_crtc->output_fence, false, 0);
+		sde_fence_update_hw_fences_txq(sde_crtc->output_fence, false, 0,
+			sde_kms->debugfs_hw_fence);
 
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->crtc != crtc)

+ 50 - 9
msm/sde/sde_encoder.c

@@ -3433,6 +3433,35 @@ static enum sde_wb sde_encoder_get_wb(struct sde_mdss_cfg *catalog,
 	return WB_MAX;
 }
 
+void sde_encoder_hw_fence_status(struct sde_kms *sde_kms,
+	struct drm_crtc *crtc, struct sde_hw_ctl *hw_ctl)
+{
+	u64 start_timestamp, end_timestamp;
+
+	if (!sde_kms || !hw_ctl || !sde_kms->hw_mdp) {
+		SDE_ERROR("invalid inputs\n");
+		return;
+	}
+
+	if ((sde_kms->debugfs_hw_fence & SDE_INPUT_HW_FENCE_TIMESTAMP)
+		&& sde_kms->hw_mdp->ops.hw_fence_input_status) {
+
+		sde_kms->hw_mdp->ops.hw_fence_input_status(sde_kms->hw_mdp,
+			&start_timestamp, &end_timestamp);
+		trace_sde_hw_fence_status(crtc->base.id, "input",
+			start_timestamp, end_timestamp);
+	}
+
+	if ((sde_kms->debugfs_hw_fence & SDE_OUTPUT_HW_FENCE_TIMESTAMP)
+		&& hw_ctl->ops.hw_fence_output_status) {
+
+		hw_ctl->ops.hw_fence_output_status(hw_ctl,
+			&start_timestamp, &end_timestamp);
+		trace_sde_hw_fence_status(crtc->base.id, "output",
+			start_timestamp, end_timestamp);
+	}
+}
+
 void sde_encoder_perf_uidle_status(struct sde_kms *sde_kms,
 	struct drm_crtc *crtc)
 {
@@ -3492,7 +3521,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
 	unsigned long lock_flags;
 	ktime_t ts = 0;
 
-	if (!drm_enc || !phy_enc)
+	if (!drm_enc || !phy_enc || !phy_enc->sde_kms)
 		return;
 
 	SDE_ATRACE_BEGIN("encoder_vblank_callback");
@@ -3502,8 +3531,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
 	 * calculate accurate vsync timestamp when available
 	 * set current time otherwise
 	 */
-	if (phy_enc->sde_kms && test_bit(SDE_FEATURE_HW_VSYNC_TS,
-					 phy_enc->sde_kms->catalog->features))
+	if (test_bit(SDE_FEATURE_HW_VSYNC_TS, phy_enc->sde_kms->catalog->features))
 		ts = sde_encoder_calc_last_vsync_timestamp(drm_enc);
 	if (!ts)
 		ts = ktime_get();
@@ -3515,10 +3543,12 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
 		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)
+	if (phy_enc->sde_kms->catalog->uidle_cfg.debugfs_perf)
 		sde_encoder_perf_uidle_status(phy_enc->sde_kms, sde_enc->crtc);
 
+	if (phy_enc->sde_kms->debugfs_hw_fence)
+		sde_encoder_hw_fence_status(phy_enc->sde_kms, sde_enc->crtc, phy_enc->hw_ctl);
+
 	SDE_ATRACE_END("encoder_vblank_callback");
 }
 
@@ -3706,7 +3736,8 @@ int sde_encoder_idle_request(struct drm_encoder *drm_enc)
 * phys: Pointer to physical encoder structure
 *
 */
-static inline void _sde_encoder_update_retire_txq(struct sde_encoder_phys *phys)
+static inline void _sde_encoder_update_retire_txq(struct sde_encoder_phys *phys,
+	struct sde_kms *sde_kms)
 {
 	struct sde_connector *c_conn;
 	int line_count;
@@ -3720,7 +3751,8 @@ static inline void _sde_encoder_update_retire_txq(struct sde_encoder_phys *phys)
 	line_count = sde_connector_get_property(phys->connector->state,
 			CONNECTOR_PROP_EARLY_FENCE_LINE);
 	if (c_conn->hwfence_wb_retire_fences_enable)
-		sde_fence_update_hw_fences_txq(c_conn->retire_fence, false, line_count);
+		sde_fence_update_hw_fences_txq(c_conn->retire_fence, false, line_count,
+			sde_kms->debugfs_hw_fence);
 }
 
 /**
@@ -3933,16 +3965,23 @@ void sde_encoder_helper_hw_reset(struct sde_encoder_phys *phys_enc)
 void sde_encoder_helper_update_out_fence_txq(struct sde_encoder_virt *sde_enc, bool is_vid)
 {
 	struct sde_crtc *sde_crtc;
+	struct sde_kms *sde_kms = NULL;
 
 	if (!sde_enc || !sde_enc->crtc) {
 		SDE_ERROR("invalid encoder %d\n", !sde_enc);
 		return;
 	}
+	sde_kms = sde_encoder_get_kms(&sde_enc->base);
+	if (!sde_kms) {
+		SDE_ERROR("invalid kms\n");
+		return;
+	}
 
 	sde_crtc = to_sde_crtc(sde_enc->crtc);
 
 	SDE_EVT32(DRMID(sde_enc->crtc), is_vid);
-	sde_fence_update_hw_fences_txq(sde_crtc->output_fence, is_vid, 0);
+	sde_fence_update_hw_fences_txq(sde_crtc->output_fence, is_vid, 0, sde_kms ?
+		sde_kms->debugfs_hw_fence : 0);
 }
 
 /**
@@ -4594,6 +4633,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool config_changed)
 {
 	struct sde_encoder_virt *sde_enc;
 	struct sde_encoder_phys *phys;
+	struct sde_kms *sde_kms;
 	unsigned int i;
 
 	if (!drm_enc) {
@@ -4619,8 +4659,9 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool config_changed)
 	}
 
 	/* update txq for any output retire hw-fence (wb-path) */
+	sde_kms = sde_encoder_get_kms(&sde_enc->base);
 	if (sde_enc->cur_master)
-		_sde_encoder_update_retire_txq(sde_enc->cur_master);
+		_sde_encoder_update_retire_txq(sde_enc->cur_master, sde_kms);
 
 	/* All phys encs are ready to go, trigger the kickoff */
 	_sde_encoder_kickoff_phys(sde_enc, config_changed);

+ 18 - 7
msm/sde/sde_fence.c

@@ -251,7 +251,7 @@ int sde_fence_register_hw_fences_wait(struct sde_hw_ctl *hw_ctl, struct dma_fenc
 	return ret;
 }
 
-static int _arm_output_hw_fence(struct sde_hw_ctl *hw_ctl, u32 line_count)
+static int _arm_output_hw_fence(struct sde_hw_ctl *hw_ctl, u32 line_count, u32 debugfs_hw_fence)
 {
 	struct sde_hw_fence_data *data;
 	u32 ipcc_out_signal;
@@ -279,6 +279,10 @@ static int _arm_output_hw_fence(struct sde_hw_ctl *hw_ctl, u32 line_count)
 	SDE_DEBUG("out-fence ctl_id:%d out_signal:%d hw_fence_client:%s\n",
 		ctl_id, ipcc_out_signal, _get_client_id_name(data->hw_fence_client_id));
 
+	if ((debugfs_hw_fence & SDE_OUTPUT_HW_FENCE_TIMESTAMP) &&
+			hw_ctl->ops.hw_fence_output_timestamp_ctrl)
+		hw_ctl->ops.hw_fence_output_timestamp_ctrl(hw_ctl, true, false);
+
 	/* update client/signal output fence */
 	hw_ctl->ops.hw_fence_update_output_fence(hw_ctl, data->ipcc_out_client, ipcc_out_signal);
 	SDE_EVT32_VERBOSE(ctl_id, ipcc_out_signal);
@@ -293,7 +297,8 @@ static int _arm_output_hw_fence(struct sde_hw_ctl *hw_ctl, u32 line_count)
 	return 0;
 }
 
-static int _sde_fence_arm_output_hw_fence(struct sde_fence_context *ctx, u32 line_count)
+static int _sde_fence_arm_output_hw_fence(struct sde_fence_context *ctx, u32 line_count,
+		u32 debugfs_hw_fence)
 {
 	struct sde_hw_ctl *hw_ctl = NULL;
 	struct sde_fence *fc, *next;
@@ -330,13 +335,14 @@ static int _sde_fence_arm_output_hw_fence(struct sde_fence_context *ctx, u32 lin
 
 	/* arm dpu to trigger output hw-fence ipcc signal upon completion */
 	if (hw_ctl)
-		_arm_output_hw_fence(hw_ctl, line_count);
+		_arm_output_hw_fence(hw_ctl, line_count, debugfs_hw_fence);
 
 	return 0;
 }
 
 /* update output hw_fences txq */
-int sde_fence_update_hw_fences_txq(struct sde_fence_context *ctx, bool vid_mode, u32 line_count)
+int sde_fence_update_hw_fences_txq(struct sde_fence_context *ctx, bool vid_mode, u32 line_count,
+		u32 debugfs_hw_fence)
 {
 	int ret = 0;
 	struct sde_hw_fence_data *data;
@@ -409,7 +415,7 @@ exit:
 
 	/* arm dpu to trigger output hw-fence ipcc signal upon completion in vid-mode */
 	if ((txq_updated && hw_ctl) || line_count)
-		_sde_fence_arm_output_hw_fence(ctx, line_count);
+		_sde_fence_arm_output_hw_fence(ctx, line_count, debugfs_hw_fence);
 
 	return ret;
 }
@@ -462,7 +468,8 @@ static int _reset_hw_fence_timeline(struct sde_hw_ctl *hw_ctl, u32 flags)
 	return ret;
 }
 
-int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *hw_ctl)
+int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *hw_ctl, u32 debugfs_hw_fence,
+		struct sde_hw_mdp *hw_mdp)
 {
 	struct sde_hw_fence_data *data;
 	u32 ipcc_signal_id;
@@ -470,7 +477,7 @@ int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *hw_ctl)
 	int ctl_id;
 
 	/* we must support sw_override as well, so check both functions */
-	if (!hw_ctl || !hw_ctl->ops.hw_fence_update_input_fence ||
+	if (!hw_mdp || !hw_ctl || !hw_ctl->ops.hw_fence_update_input_fence ||
 			!hw_ctl->ops.hw_fence_trigger_sw_override) {
 		SDE_ERROR("missing ctl/override/update fence %d\n", !hw_ctl);
 		return -EINVAL;
@@ -479,6 +486,10 @@ int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *hw_ctl)
 	ctl_id = hw_ctl->idx - CTL_0;
 	data = &hw_ctl->hwfence_data;
 
+	if ((debugfs_hw_fence & SDE_INPUT_HW_FENCE_TIMESTAMP)
+			&& hw_mdp->ops.hw_fence_input_timestamp_ctrl)
+		hw_mdp->ops.hw_fence_input_timestamp_ctrl(hw_mdp, true, false);
+
 	ipcc_signal_id = data->ipcc_in_signal;
 	ipcc_client_id = data->ipcc_in_client;
 

+ 9 - 2
msm/sde/sde_fence.h

@@ -19,6 +19,9 @@
 #define HW_FENCE_TRIGGER_SEL_CTRL_DONE       0x0
 #define HW_FENCE_TRIGGER_SEL_PROG_LINE_COUNT 0x1
 
+#define SDE_INPUT_HW_FENCE_TIMESTAMP         BIT(0)
+#define SDE_OUTPUT_HW_FENCE_TIMESTAMP        BIT(1)
+
 #define SDE_FENCE_NAME_SIZE	24
 
 #define MAX_SDE_HFENCE_OUT_SIGNAL_PING_PONG 2
@@ -179,17 +182,21 @@ int sde_fence_register_hw_fences_wait(struct sde_hw_ctl *hw_ctl, struct dma_fenc
  *
  * Returns: Zero on success, otherwise returns an error code.
  */
-int sde_fence_update_hw_fences_txq(struct sde_fence_context *ctx, bool vid_mode, u32 line_count);
+int sde_fence_update_hw_fences_txq(struct sde_fence_context *ctx, bool vid_mode, u32 line_count,
+	u32 debugfs_hw_fence);
 
 /**
  * sde_fence_update_input_hw_fence_signal - updates input-fence ipcc signal in dpu and enables
  *                                  hw-fences for the ctl.
  *
  * @ctl: hw ctl to update the input-fence and enable hw-fences
+ * @debugfs_hw_fence: hw-fence timestamp debugfs value
+ * @hw_mdp: pointer to hw_mdp to get timestamp registers
  *
  * Returns: Zero on success, otherwise returns an error code.
  */
-int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *ctl);
+int sde_fence_update_input_hw_fence_signal(struct sde_hw_ctl *ctl, u32 debugfs_hw_fence,
+	struct sde_hw_mdp *hw_mdp);
 
 /**
  * sde_fence_deinit - deinit fence container

+ 47 - 0
msm/sde/sde_hw_ctl.c

@@ -61,6 +61,11 @@
 #define CTL_OUTPUT_FENCE_ID           0x260
 #define CTL_HW_FENCE_STATUS           0x278
 #define CTL_OUTPUT_FENCE_SW_OVERRIDE  0x27C
+#define CTL_TIMESTAMP_CTRL            0x264
+#define CTL_OUTPUT_FENCE_START_TIMESTAMP0 0x268
+#define CTL_OUTPUT_FENCE_START_TIMESTAMP1 0x26C
+#define CTL_OUTPUT_FENCE_END_TIMESTAMP0 0x270
+#define CTL_OUTPUT_FENCE_END_TIMESTAMP1 0x274
 
 #define CTL_MIXER_BORDER_OUT            BIT(24)
 #define CTL_FLUSH_MASK_ROT              BIT(27)
@@ -377,6 +382,46 @@ static inline void sde_hw_ctl_trigger_output_fence_override(struct sde_hw_ctl *c
 	SDE_REG_WRITE(&ctx->hw, CTL_OUTPUT_FENCE_SW_OVERRIDE, 0x1);
 }
 
+static inline void sde_hw_ctl_fence_timestamp_ctrl(struct sde_hw_ctl *ctx, bool enable, bool clear)
+{
+	u32 val;
+
+	val = SDE_REG_READ(&ctx->hw, CTL_TIMESTAMP_CTRL);
+	if (enable)
+		val |= BIT(0);
+	else
+		val &= ~BIT(0);
+	if (clear)
+		val |= BIT(1);
+	else
+		val &= ~BIT(1);
+
+	SDE_REG_WRITE(&ctx->hw, CTL_TIMESTAMP_CTRL, val);
+	wmb(); /* make sure the ctrl is written */
+}
+
+static inline int sde_hw_ctl_output_fence_timestamps(struct sde_hw_ctl *ctx,
+			u64 *val_start, u64 *val_end)
+{
+	u32 start_l, start_h, end_l, end_h;
+
+	if (!ctx || IS_ERR_OR_NULL(val_start) || IS_ERR_OR_NULL(val_end))
+		return -EINVAL;
+
+	start_l = SDE_REG_READ(&ctx->hw, CTL_OUTPUT_FENCE_START_TIMESTAMP0);
+	start_h = SDE_REG_READ(&ctx->hw, CTL_OUTPUT_FENCE_START_TIMESTAMP1);
+	*val_start = (u64)start_h << 32 | start_l;
+
+	end_l = SDE_REG_READ(&ctx->hw, CTL_OUTPUT_FENCE_END_TIMESTAMP0);
+	end_h = SDE_REG_READ(&ctx->hw, CTL_OUTPUT_FENCE_END_TIMESTAMP1);
+	*val_end = (u64)end_h << 32 | end_l;
+
+	/* clear timestamps */
+	sde_hw_ctl_fence_timestamp_ctrl(ctx, false, true);
+
+	return 0;
+}
+
 static inline int sde_hw_ctl_trigger_start(struct sde_hw_ctl *ctx)
 {
 	if (!ctx)
@@ -1433,6 +1478,8 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
 		ops->hw_fence_trigger_sw_override = sde_hw_ctl_trigger_sw_override;
 		ops->get_hw_fence_status = sde_hw_ctl_get_hw_fence_status;
 		ops->trigger_output_fence_override = sde_hw_ctl_trigger_output_fence_override;
+		ops->hw_fence_output_status = sde_hw_ctl_output_fence_timestamps;
+		ops->hw_fence_output_timestamp_ctrl = sde_hw_ctl_fence_timestamp_ctrl;
 	}
 
 	if (cap & BIT(SDE_CTL_UIDLE))

+ 17 - 0
msm/sde/sde_hw_ctl.h

@@ -189,6 +189,23 @@ struct sde_hw_ctl_ops {
 	 */
 	void (*hw_fence_trigger_sw_override)(struct sde_hw_ctl *ctx);
 
+	/**
+	 * enable or clear hw fence output fence timestamps
+	 * @ctx         : ctl path ctx pointer
+	 * @enable      : indicates if timestamps should be enabled
+	 * @clear       : indicates if timestamps should be cleared
+	 */
+	void (*hw_fence_output_timestamp_ctrl)(struct sde_hw_ctl *ctx, bool enable, bool clear);
+
+	/**
+	 * get hw fence output fence timestamps and clear them
+	 * @ctx              : ctl path ctx pointer
+	 * @val_start        : pointer to start timestamp value
+	 * @val_end          : pointer to end timestamp value
+	 * @Return: error code
+	 */
+	int (*hw_fence_output_status)(struct sde_hw_ctl *ctx, u64 *val_start, u64 *val_end);
+
 	/**
 	 * configure output hw fence trigger
 	 * @ctx         : ctl path ctx pointer

+ 58 - 1
msm/sde/sde_hw_top.c

@@ -593,6 +593,60 @@ static u32 sde_hw_get_autorefresh_status(struct sde_hw_mdp *mdp, u32 intf_idx)
 	return autorefresh_status;
 }
 
+static void sde_hw_hw_fence_timestamp_ctrl(struct sde_hw_mdp *mdp, bool enable, bool clear)
+{
+	struct sde_hw_blk_reg_map c;
+	u32 val;
+
+	if (!mdp) {
+		SDE_ERROR("invalid mdp, won't enable hw-fence timestamping\n");
+		return;
+	}
+
+	/* start from the base-address of the mdss */
+	c = mdp->hw;
+	c.blk_off = 0x0;
+
+	val = SDE_REG_READ(&c, MDP_CTL_HW_FENCE_ID_TIMESTAMP_CTRL);
+	if (enable)
+		val |= BIT(0);
+	else
+		val &= ~BIT(0);
+	if (clear)
+		val |= BIT(1);
+	else
+		val &= ~BIT(1);
+	SDE_REG_WRITE(&c, MDP_CTL_HW_FENCE_ID_TIMESTAMP_CTRL, val);
+}
+
+static void sde_hw_input_hw_fence_status(struct sde_hw_mdp *mdp, u64 *s_val, u64 *e_val)
+{
+	u32 start_h, start_l, end_h, end_l;
+	struct sde_hw_blk_reg_map c;
+
+	if (!mdp || IS_ERR_OR_NULL(s_val) || IS_ERR_OR_NULL(e_val)) {
+		SDE_ERROR("invalid mdp\n");
+		return;
+	}
+
+	/* start from the base-address of the mdss */
+	c = mdp->hw;
+	c.blk_off = 0x0;
+
+	start_l = SDE_REG_READ(&c, MDP_CTL_HW_FENCE_INPUT_START_TIMESTAMP0);
+	start_h = SDE_REG_READ(&c, MDP_CTL_HW_FENCE_INPUT_START_TIMESTAMP1);
+	*s_val = (u64)start_h << 32 | start_l;
+
+	end_l = SDE_REG_READ(&c, MDP_CTL_HW_FENCE_INPUT_END_TIMESTAMP0);
+	end_h = SDE_REG_READ(&c, MDP_CTL_HW_FENCE_INPUT_END_TIMESTAMP1);
+	*e_val = (u64)end_h << 32 | end_l;
+
+	/* clear the timestamps */
+	sde_hw_hw_fence_timestamp_ctrl(mdp, false, true);
+
+	wmb(); /* make sure the timestamps are cleared */
+}
+
 static void sde_hw_setup_hw_fences_config(struct sde_hw_mdp *mdp, u32 protocol_id,
 	unsigned long ipcc_base_addr)
 {
@@ -711,8 +765,11 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, unsigned long cap, u32 hw
 		ops->set_hdr_plus_metadata = sde_hw_set_hdr_plus_metadata;
 	ops->get_autorefresh_status = sde_hw_get_autorefresh_status;
 
-	if (hw_fence_rev)
+	if (hw_fence_rev) {
 		ops->setup_hw_fences = sde_hw_setup_hw_fences_config;
+		ops->hw_fence_input_timestamp_ctrl = sde_hw_hw_fence_timestamp_ctrl;
+		ops->hw_fence_input_status = sde_hw_input_hw_fence_status;
+	}
 }
 
 static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,

+ 15 - 0
msm/sde/sde_hw_top.h

@@ -215,6 +215,21 @@ struct sde_hw_mdp_ops {
 	void (*setup_hw_fences)(struct sde_hw_mdp *mdp, u32 protocol_id,
 			unsigned long ipcc_base_addr);
 
+	/**
+	 * hw_fence_input_status - get hw_fence input fence timestamps and clear them
+	 * @mdp:       mdp top context driver
+	 * @s_val:     pointer to start timestamp value to populate
+	 * @e_val:     pointer to end timestamp value to populate
+	 */
+	void (*hw_fence_input_status)(struct sde_hw_mdp *mdp, u64 *s_val, u64 *e_val);
+
+	/**
+	 * hw_fence_input_timestamp_ctrl - enable or clear input fence timestamps
+	 * @mdp:       mdp top context driver
+	 * @enable:    indicates if timestamps should be enabled
+	 * @enable:    indicates if timestamps should be cleared
+	 */
+	void (*hw_fence_input_timestamp_ctrl)(struct sde_hw_mdp *mdp, bool enable, bool clear);
 };
 
 struct sde_hw_mdp {

+ 2 - 0
msm/sde/sde_kms.c

@@ -172,6 +172,8 @@ static int _sde_debugfs_init(struct sde_kms *sde_kms)
 
 	debugfs_create_u32("pm_suspend_clk_dump", 0600, debugfs_root,
 			(u32 *)&sde_kms->pm_suspend_clk_dump);
+	debugfs_create_u32("hw_fence_status", 0600, debugfs_root,
+			(u32 *)&sde_kms->debugfs_hw_fence);
 
 	return 0;
 }

+ 1 - 0
msm/sde/sde_kms.h

@@ -318,6 +318,7 @@ struct sde_kms {
 	struct sde_vm *vm;
 
 	unsigned long ipcc_base_addr;
+	u32 debugfs_hw_fence;
 };
 
 struct vsync_info {

+ 29 - 0
msm/sde/sde_trace.h

@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
  */
 
@@ -420,6 +421,34 @@ TRACE_EVENT(sde_perf_uidle_status,
 			)
 );
 
+TRACE_EVENT(sde_hw_fence_status,
+	TP_PROTO(u32 crtc,
+			char *fence,
+			u32 hw_fence_start_timestamp,
+			u32 hw_fence_end_timestamp),
+	TP_ARGS(crtc,
+			fence,
+			hw_fence_start_timestamp,
+			hw_fence_end_timestamp),
+	TP_STRUCT__entry(
+			__field(u32, crtc)
+			__field(char *, fence)
+			__field(u32, hw_fence_start_timestamp)
+			__field(u32, hw_fence_end_timestamp)),
+	TP_fast_assign(
+			__entry->crtc = crtc;
+			__entry->fence = fence;
+			__entry->hw_fence_start_timestamp = hw_fence_start_timestamp;
+			__entry->hw_fence_end_timestamp = hw_fence_end_timestamp;),
+	TP_printk(
+		"crtc:%d %s hw-fence start timestamp:%llu end timestamp:%llu",
+			__entry->crtc,
+			__entry->fence,
+			__entry->hw_fence_start_timestamp,
+			__entry->hw_fence_end_timestamp
+			)
+);
+
 #define sde_atrace trace_tracing_mark_write
 
 #define SDE_ATRACE_END(name) sde_atrace('E', current, name, 0)