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 <quic_coliveir@quicinc.com>
This commit is contained in:
@@ -3801,22 +3801,23 @@ static bool _sde_crtc_wait_for_fences(struct drm_crtc *crtc)
|
|||||||
int num_hw_fences = 0;
|
int num_hw_fences = 0;
|
||||||
struct sde_hw_ctl *hw_ctl;
|
struct sde_hw_ctl *hw_ctl;
|
||||||
bool input_hw_fences_enable;
|
bool input_hw_fences_enable;
|
||||||
|
struct sde_kms *sde_kms = _sde_crtc_get_kms(crtc);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
SDE_DEBUG("\n");
|
SDE_DEBUG("\n");
|
||||||
|
|
||||||
if (!crtc || !crtc->state) {
|
if (!crtc || !crtc->state || !sde_kms) {
|
||||||
SDE_ERROR("invalid crtc/state %pK\n", crtc);
|
SDE_ERROR("invalid crtc/state %pK\n", crtc);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hw_ctl = _sde_crtc_get_hw_ctl(crtc);
|
hw_ctl = _sde_crtc_get_hw_ctl(crtc);
|
||||||
|
|
||||||
SDE_ATRACE_BEGIN("plane_wait_input_fence");
|
SDE_ATRACE_BEGIN("plane_wait_input_fence");
|
||||||
|
|
||||||
/* update ctl hw to wait for ipcc input signal before fetch */
|
/* update ctl hw to wait for ipcc input signal before fetch */
|
||||||
if (test_bit(HW_FENCE_IN_FENCES_ENABLE, sde_crtc->hwfence_features_mask) &&
|
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;
|
ipcc_input_signal_wait = true;
|
||||||
|
|
||||||
/* avoid hw-fences in first frame after timing engine enable */
|
/* 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
|
* 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)
|
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) {
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||||
if (encoder->crtc != crtc)
|
if (encoder->crtc != crtc)
|
||||||
|
@@ -3433,6 +3433,35 @@ static enum sde_wb sde_encoder_get_wb(struct sde_mdss_cfg *catalog,
|
|||||||
return WB_MAX;
|
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,
|
void sde_encoder_perf_uidle_status(struct sde_kms *sde_kms,
|
||||||
struct drm_crtc *crtc)
|
struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
@@ -3492,7 +3521,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc,
|
|||||||
unsigned long lock_flags;
|
unsigned long lock_flags;
|
||||||
ktime_t ts = 0;
|
ktime_t ts = 0;
|
||||||
|
|
||||||
if (!drm_enc || !phy_enc)
|
if (!drm_enc || !phy_enc || !phy_enc->sde_kms)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SDE_ATRACE_BEGIN("encoder_vblank_callback");
|
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
|
* calculate accurate vsync timestamp when available
|
||||||
* set current time otherwise
|
* set current time otherwise
|
||||||
*/
|
*/
|
||||||
if (phy_enc->sde_kms && test_bit(SDE_FEATURE_HW_VSYNC_TS,
|
if (test_bit(SDE_FEATURE_HW_VSYNC_TS, phy_enc->sde_kms->catalog->features))
|
||||||
phy_enc->sde_kms->catalog->features))
|
|
||||||
ts = sde_encoder_calc_last_vsync_timestamp(drm_enc);
|
ts = sde_encoder_calc_last_vsync_timestamp(drm_enc);
|
||||||
if (!ts)
|
if (!ts)
|
||||||
ts = ktime_get();
|
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);
|
sde_enc->crtc_vblank_cb(sde_enc->crtc_vblank_cb_data, ts);
|
||||||
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
|
spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
|
||||||
|
|
||||||
if (phy_enc->sde_kms &&
|
if (phy_enc->sde_kms->catalog->uidle_cfg.debugfs_perf)
|
||||||
phy_enc->sde_kms->catalog->uidle_cfg.debugfs_perf)
|
|
||||||
sde_encoder_perf_uidle_status(phy_enc->sde_kms, sde_enc->crtc);
|
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");
|
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
|
* 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;
|
struct sde_connector *c_conn;
|
||||||
int line_count;
|
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,
|
line_count = sde_connector_get_property(phys->connector->state,
|
||||||
CONNECTOR_PROP_EARLY_FENCE_LINE);
|
CONNECTOR_PROP_EARLY_FENCE_LINE);
|
||||||
if (c_conn->hwfence_wb_retire_fences_enable)
|
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)
|
void sde_encoder_helper_update_out_fence_txq(struct sde_encoder_virt *sde_enc, bool is_vid)
|
||||||
{
|
{
|
||||||
struct sde_crtc *sde_crtc;
|
struct sde_crtc *sde_crtc;
|
||||||
|
struct sde_kms *sde_kms = NULL;
|
||||||
|
|
||||||
if (!sde_enc || !sde_enc->crtc) {
|
if (!sde_enc || !sde_enc->crtc) {
|
||||||
SDE_ERROR("invalid encoder %d\n", !sde_enc);
|
SDE_ERROR("invalid encoder %d\n", !sde_enc);
|
||||||
return;
|
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_crtc = to_sde_crtc(sde_enc->crtc);
|
||||||
|
|
||||||
SDE_EVT32(DRMID(sde_enc->crtc), is_vid);
|
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_virt *sde_enc;
|
||||||
struct sde_encoder_phys *phys;
|
struct sde_encoder_phys *phys;
|
||||||
|
struct sde_kms *sde_kms;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (!drm_enc) {
|
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) */
|
/* update txq for any output retire hw-fence (wb-path) */
|
||||||
|
sde_kms = sde_encoder_get_kms(&sde_enc->base);
|
||||||
if (sde_enc->cur_master)
|
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 */
|
/* All phys encs are ready to go, trigger the kickoff */
|
||||||
_sde_encoder_kickoff_phys(sde_enc, config_changed);
|
_sde_encoder_kickoff_phys(sde_enc, config_changed);
|
||||||
|
@@ -251,7 +251,7 @@ int sde_fence_register_hw_fences_wait(struct sde_hw_ctl *hw_ctl, struct dma_fenc
|
|||||||
return ret;
|
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;
|
struct sde_hw_fence_data *data;
|
||||||
u32 ipcc_out_signal;
|
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",
|
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));
|
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 */
|
/* update client/signal output fence */
|
||||||
hw_ctl->ops.hw_fence_update_output_fence(hw_ctl, data->ipcc_out_client, ipcc_out_signal);
|
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);
|
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;
|
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_hw_ctl *hw_ctl = NULL;
|
||||||
struct sde_fence *fc, *next;
|
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 */
|
/* arm dpu to trigger output hw-fence ipcc signal upon completion */
|
||||||
if (hw_ctl)
|
if (hw_ctl)
|
||||||
_arm_output_hw_fence(hw_ctl, line_count);
|
_arm_output_hw_fence(hw_ctl, line_count, debugfs_hw_fence);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update output hw_fences txq */
|
/* 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;
|
int ret = 0;
|
||||||
struct sde_hw_fence_data *data;
|
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 */
|
/* arm dpu to trigger output hw-fence ipcc signal upon completion in vid-mode */
|
||||||
if ((txq_updated && hw_ctl) || line_count)
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -462,7 +468,8 @@ static int _reset_hw_fence_timeline(struct sde_hw_ctl *hw_ctl, u32 flags)
|
|||||||
return ret;
|
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;
|
struct sde_hw_fence_data *data;
|
||||||
u32 ipcc_signal_id;
|
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;
|
int ctl_id;
|
||||||
|
|
||||||
/* we must support sw_override as well, so check both functions */
|
/* 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) {
|
!hw_ctl->ops.hw_fence_trigger_sw_override) {
|
||||||
SDE_ERROR("missing ctl/override/update fence %d\n", !hw_ctl);
|
SDE_ERROR("missing ctl/override/update fence %d\n", !hw_ctl);
|
||||||
return -EINVAL;
|
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;
|
ctl_id = hw_ctl->idx - CTL_0;
|
||||||
data = &hw_ctl->hwfence_data;
|
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_signal_id = data->ipcc_in_signal;
|
||||||
ipcc_client_id = data->ipcc_in_client;
|
ipcc_client_id = data->ipcc_in_client;
|
||||||
|
|
||||||
|
@@ -19,6 +19,9 @@
|
|||||||
#define HW_FENCE_TRIGGER_SEL_CTRL_DONE 0x0
|
#define HW_FENCE_TRIGGER_SEL_CTRL_DONE 0x0
|
||||||
#define HW_FENCE_TRIGGER_SEL_PROG_LINE_COUNT 0x1
|
#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 SDE_FENCE_NAME_SIZE 24
|
||||||
|
|
||||||
#define MAX_SDE_HFENCE_OUT_SIGNAL_PING_PONG 2
|
#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.
|
* 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
|
* sde_fence_update_input_hw_fence_signal - updates input-fence ipcc signal in dpu and enables
|
||||||
* hw-fences for the ctl.
|
* hw-fences for the ctl.
|
||||||
*
|
*
|
||||||
* @ctl: hw ctl to update the input-fence and enable hw-fences
|
* @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.
|
* 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
|
* sde_fence_deinit - deinit fence container
|
||||||
|
@@ -61,6 +61,11 @@
|
|||||||
#define CTL_OUTPUT_FENCE_ID 0x260
|
#define CTL_OUTPUT_FENCE_ID 0x260
|
||||||
#define CTL_HW_FENCE_STATUS 0x278
|
#define CTL_HW_FENCE_STATUS 0x278
|
||||||
#define CTL_OUTPUT_FENCE_SW_OVERRIDE 0x27C
|
#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_MIXER_BORDER_OUT BIT(24)
|
||||||
#define CTL_FLUSH_MASK_ROT BIT(27)
|
#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);
|
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)
|
static inline int sde_hw_ctl_trigger_start(struct sde_hw_ctl *ctx)
|
||||||
{
|
{
|
||||||
if (!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->hw_fence_trigger_sw_override = sde_hw_ctl_trigger_sw_override;
|
||||||
ops->get_hw_fence_status = sde_hw_ctl_get_hw_fence_status;
|
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->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))
|
if (cap & BIT(SDE_CTL_UIDLE))
|
||||||
|
@@ -189,6 +189,23 @@ struct sde_hw_ctl_ops {
|
|||||||
*/
|
*/
|
||||||
void (*hw_fence_trigger_sw_override)(struct sde_hw_ctl *ctx);
|
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
|
* configure output hw fence trigger
|
||||||
* @ctx : ctl path ctx pointer
|
* @ctx : ctl path ctx pointer
|
||||||
|
@@ -593,6 +593,60 @@ static u32 sde_hw_get_autorefresh_status(struct sde_hw_mdp *mdp, u32 intf_idx)
|
|||||||
return autorefresh_status;
|
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,
|
static void sde_hw_setup_hw_fences_config(struct sde_hw_mdp *mdp, u32 protocol_id,
|
||||||
unsigned long ipcc_base_addr)
|
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->set_hdr_plus_metadata = sde_hw_set_hdr_plus_metadata;
|
||||||
ops->get_autorefresh_status = sde_hw_get_autorefresh_status;
|
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->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,
|
static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,
|
||||||
|
@@ -215,6 +215,21 @@ struct sde_hw_mdp_ops {
|
|||||||
void (*setup_hw_fences)(struct sde_hw_mdp *mdp, u32 protocol_id,
|
void (*setup_hw_fences)(struct sde_hw_mdp *mdp, u32 protocol_id,
|
||||||
unsigned long ipcc_base_addr);
|
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 {
|
struct sde_hw_mdp {
|
||||||
|
@@ -172,6 +172,8 @@ static int _sde_debugfs_init(struct sde_kms *sde_kms)
|
|||||||
|
|
||||||
debugfs_create_u32("pm_suspend_clk_dump", 0600, debugfs_root,
|
debugfs_create_u32("pm_suspend_clk_dump", 0600, debugfs_root,
|
||||||
(u32 *)&sde_kms->pm_suspend_clk_dump);
|
(u32 *)&sde_kms->pm_suspend_clk_dump);
|
||||||
|
debugfs_create_u32("hw_fence_status", 0600, debugfs_root,
|
||||||
|
(u32 *)&sde_kms->debugfs_hw_fence);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -318,6 +318,7 @@ struct sde_kms {
|
|||||||
struct sde_vm *vm;
|
struct sde_vm *vm;
|
||||||
|
|
||||||
unsigned long ipcc_base_addr;
|
unsigned long ipcc_base_addr;
|
||||||
|
u32 debugfs_hw_fence;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vsync_info {
|
struct vsync_info {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
/* 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.
|
* 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 trace_tracing_mark_write
|
||||||
|
|
||||||
#define SDE_ATRACE_END(name) sde_atrace('E', current, name, 0)
|
#define SDE_ATRACE_END(name) sde_atrace('E', current, name, 0)
|
||||||
|
Reference in New Issue
Block a user