disp: msm: sde: increase display kickoff timeout for hw-fences
Starting with HW-Fencing, the frames hw kickoff can take longer to trigger, given that HW will wait for the input fences signal. Therefore, this change increments the time-outs to wait up to ~10 secs, which corresponds to the current input dma-fences timeout. This ~10secs wait is given in intervals, where the dma-fence is also checked, so in case that the client producer of the fence signals the dma-fence, but misses the hw-fence signaling, Display driver can handle this case and do a sw-override to start the fetching of the incoming frame without waiting for the input hw-fence ipc signal. Change-Id: I6fcacbbaa79ca9847da616bd52efdda4bb8fccae Signed-off-by: Christina Oliveira <quic_coliveir@quicinc.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
@@ -4518,6 +4518,18 @@ void sde_crtc_dump_fences(struct drm_crtc *crtc)
|
||||
sde_plane_dump_input_fence(plane);
|
||||
}
|
||||
|
||||
bool sde_crtc_is_fence_signaled(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_plane *plane = NULL;
|
||||
|
||||
drm_atomic_crtc_for_each_plane(plane, crtc) {
|
||||
if (!sde_plane_is_sw_fence_signaled(plane))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sde_crtc_reset_hw - attempt hardware reset on errors
|
||||
* @crtc: Pointer to DRM crtc instance
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2015-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
@@ -653,6 +653,13 @@ int sde_crtc_reset_hw(struct drm_crtc *crtc, struct drm_crtc_state *old_state,
|
||||
*/
|
||||
void sde_crtc_dump_fences(struct drm_crtc *crtc);
|
||||
|
||||
/**
|
||||
* sde_crtc_is_fence_signaled - check if all fences have been signaled
|
||||
* @crtc: Pointer to DRM crtc instance
|
||||
* Returns: true if all fences are signaled, otherwise false.
|
||||
*/
|
||||
bool sde_crtc_is_fence_signaled(struct drm_crtc *crtc);
|
||||
|
||||
/**
|
||||
* sde_crtc_request_frame_reset - requests for next frame reset
|
||||
* @crtc: Pointer to drm crtc object
|
||||
|
@@ -384,6 +384,55 @@ static int _sde_encoder_wait_timeout(int32_t drm_id, int32_t hw_id,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sde_encoder_helper_hw_fence_extended_wait(struct sde_encoder_phys *phys_enc,
|
||||
struct sde_hw_ctl *ctl, struct sde_encoder_wait_info *wait_info, int wait_type)
|
||||
{
|
||||
int ret = -ETIMEDOUT;
|
||||
s64 standard_kickoff_timeout_ms = wait_info->timeout_ms;
|
||||
int timeout_iters = EXTENDED_KICKOFF_TIMEOUT_ITERS;
|
||||
|
||||
wait_info->timeout_ms = EXTENDED_KICKOFF_TIMEOUT_MS;
|
||||
|
||||
while (ret == -ETIMEDOUT && timeout_iters--) {
|
||||
ret = sde_encoder_helper_wait_for_irq(phys_enc, wait_type, wait_info);
|
||||
if (ret == -ETIMEDOUT) {
|
||||
/* if dma_fence is not signaled, keep waiting */
|
||||
if (!sde_crtc_is_fence_signaled(phys_enc->parent->crtc))
|
||||
continue;
|
||||
|
||||
/* timed-out waiting and no sw-override support for hw-fences */
|
||||
if (!ctl || !ctl->ops.hw_fence_trigger_sw_override) {
|
||||
SDE_ERROR("invalid argument(s)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case the sw and hw fences were triggered at the same time,
|
||||
* wait the standard kickoff time one more time. Only override if
|
||||
* we timeout again.
|
||||
*/
|
||||
wait_info->timeout_ms = standard_kickoff_timeout_ms;
|
||||
ret = sde_encoder_helper_wait_for_irq(phys_enc, wait_type, wait_info);
|
||||
if (ret == -ETIMEDOUT) {
|
||||
sde_encoder_helper_hw_fence_sw_override(phys_enc, ctl);
|
||||
|
||||
/*
|
||||
* wait the original timeout time again if we
|
||||
* did sw override due to fence being signaled
|
||||
*/
|
||||
ret = sde_encoder_helper_wait_for_irq(phys_enc, wait_type,
|
||||
wait_info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* reset the timeout value */
|
||||
wait_info->timeout_ms = standard_kickoff_timeout_ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool sde_encoder_is_primary_display(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
|
||||
@@ -4888,6 +4937,18 @@ int sde_encoder_helper_reset_mixers(struct sde_encoder_phys *phys_enc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sde_encoder_helper_hw_fence_sw_override(struct sde_encoder_phys *phys_enc,
|
||||
struct sde_hw_ctl *ctl)
|
||||
{
|
||||
if (!ctl || !ctl->ops.hw_fence_trigger_sw_override)
|
||||
return;
|
||||
|
||||
SDE_EVT32(DRMID(phys_enc->parent), ctl->idx, ctl->ops.get_hw_fence_status ?
|
||||
ctl->ops.get_hw_fence_status(ctl) : SDE_EVTLOG_ERROR);
|
||||
sde_encoder_helper_reset_mixers(phys_enc, NULL);
|
||||
ctl->ops.hw_fence_trigger_sw_override(ctl);
|
||||
}
|
||||
|
||||
int sde_encoder_prepare_commit(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct sde_encoder_virt *sde_enc;
|
||||
|
@@ -26,6 +26,10 @@
|
||||
/* wait for at most 2 vsync for lowest refresh rate (24hz) */
|
||||
#define DEFAULT_KICKOFF_TIMEOUT_MS 84
|
||||
|
||||
/* if default timeout fails wait additional time in 1s increments */
|
||||
#define EXTENDED_KICKOFF_TIMEOUT_MS 1000
|
||||
#define EXTENDED_KICKOFF_TIMEOUT_ITERS 10
|
||||
|
||||
/* wait 1 sec for the emulated targets */
|
||||
#define MAX_KICKOFF_TIMEOUT_MS 100000
|
||||
|
||||
@@ -726,6 +730,13 @@ void sde_encoder_helper_split_config(
|
||||
*/
|
||||
int sde_encoder_helper_reset_mixers(struct sde_encoder_phys *phys_enc,
|
||||
struct drm_framebuffer *fb);
|
||||
/**
|
||||
* sde_encoder_helper_hw_fence_sw_override - reset mixers and do hw-fence sw override
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
* @ctl: Pointer to hw_ctl structure
|
||||
*/
|
||||
void sde_encoder_helper_hw_fence_sw_override(struct sde_encoder_phys *phys_enc,
|
||||
struct sde_hw_ctl *ctl);
|
||||
|
||||
/**
|
||||
* sde_encoder_helper_report_irq_timeout - utility to report error that irq has
|
||||
@@ -839,6 +850,17 @@ static inline bool sde_encoder_phys_needs_single_flush(
|
||||
!_sde_encoder_phys_is_dual_ctl(phys_enc));
|
||||
}
|
||||
|
||||
/**
|
||||
* sde_encoder_helper_hw_fence_extended_wait - extended kickoff wait for hw-fence enabled case
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
* @ctl: Pointer to hw ctl structure
|
||||
* @wait_info: Pointer to wait_info structure
|
||||
* @wait_type: Enum indicating the irq to wait for
|
||||
* Returns: -ETIMEDOUT in the case that the extended wait times out, 0 otherwise
|
||||
*/
|
||||
int sde_encoder_helper_hw_fence_extended_wait(struct sde_encoder_phys *phys_enc,
|
||||
struct sde_hw_ctl *ctl, struct sde_encoder_wait_info *wait_info, int wait_type);
|
||||
|
||||
/**
|
||||
* sde_encoder_helper_phys_disable - helper function to disable virt encoder
|
||||
* @phys_enc: Pointer to physical encoder structure
|
||||
|
@@ -1725,6 +1725,15 @@ static int _sde_encoder_phys_cmd_wait_for_wr_ptr(
|
||||
|
||||
ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WRPTR,
|
||||
&wait_info);
|
||||
|
||||
/*
|
||||
* if hwfencing enabled, try again to wait for up to the extended timeout time in
|
||||
* increments as long as fence has not been signaled.
|
||||
*/
|
||||
if (ret == -ETIMEDOUT && phys_enc->sde_kms->catalog->hw_fence_rev)
|
||||
ret = sde_encoder_helper_hw_fence_extended_wait(phys_enc, ctl, &wait_info,
|
||||
INTR_IDX_WRPTR);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
struct sde_hw_ctl *ctl = phys_enc->hw_ctl;
|
||||
|
||||
@@ -1754,6 +1763,10 @@ static int _sde_encoder_phys_cmd_wait_for_wr_ptr(
|
||||
lock_flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* if we timeout after the extended wait, reset mixers and do sw override */
|
||||
if (ret && phys_enc->sde_kms->catalog->hw_fence_rev)
|
||||
sde_encoder_helper_hw_fence_sw_override(phys_enc, ctl);
|
||||
}
|
||||
|
||||
cmd_enc->wr_ptr_wait_success = (ret == 0) ? true : false;
|
||||
|
@@ -913,6 +913,14 @@ static int _sde_encoder_phys_vid_wait_for_vblank(
|
||||
ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_VSYNC,
|
||||
&wait_info);
|
||||
|
||||
/*
|
||||
* if hwfencing enabled, try again to wait for up to the extended timeout time in
|
||||
* increments as long as fence has not been signaled.
|
||||
*/
|
||||
if (ret == -ETIMEDOUT && phys_enc->sde_kms->catalog->hw_fence_rev)
|
||||
ret = sde_encoder_helper_hw_fence_extended_wait(phys_enc, phys_enc->hw_ctl,
|
||||
&wait_info, INTR_IDX_VSYNC);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
|
||||
timeout = true;
|
||||
@@ -926,6 +934,10 @@ static int _sde_encoder_phys_vid_wait_for_vblank(
|
||||
if (!flush_register)
|
||||
ret = 0;
|
||||
|
||||
/* if we timeout after the extended wait, reset mixers and do sw override */
|
||||
if (ret && phys_enc->sde_kms->catalog->hw_fence_rev)
|
||||
sde_encoder_helper_hw_fence_sw_override(phys_enc, hw_ctl);
|
||||
|
||||
SDE_EVT32(DRMID(phys_enc->parent), new_cnt, flush_register, ret,
|
||||
SDE_EVTLOG_FUNC_CASE1);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
@@ -2131,6 +2131,7 @@ static int _sde_encoder_phys_wb_wait_for_ctl_start(struct sde_encoder_phys *phys
|
||||
{
|
||||
struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
|
||||
struct sde_encoder_wait_info wait_info = {0};
|
||||
struct sde_hw_ctl *hw_ctl = phys_enc->hw_ctl;
|
||||
int rc = 0;
|
||||
|
||||
if (!atomic_read(&phys_enc->pending_ctl_start_cnt))
|
||||
@@ -2146,8 +2147,22 @@ static int _sde_encoder_phys_wb_wait_for_ctl_start(struct sde_encoder_phys *phys
|
||||
wait_info.timeout_ms = max_t(u32, wb_enc->wbdone_timeout, phys_enc->kickoff_timeout_ms);
|
||||
|
||||
rc = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_CTL_START, &wait_info);
|
||||
|
||||
/*
|
||||
* if hwfencing enabled, try again to wait for up to the extended timeout time in
|
||||
* increments as long as fence has not been signaled.
|
||||
*/
|
||||
if (rc == -ETIMEDOUT && phys_enc->sde_kms->catalog->hw_fence_rev && hw_ctl)
|
||||
rc = sde_encoder_helper_hw_fence_extended_wait(phys_enc, hw_ctl,
|
||||
&wait_info, INTR_IDX_CTL_START);
|
||||
|
||||
if (rc == -ETIMEDOUT) {
|
||||
atomic_add_unless(&phys_enc->pending_ctl_start_cnt, -1, 0);
|
||||
|
||||
/* if we timeout after the extended wait, reset mixers and do sw override */
|
||||
if (phys_enc->sde_kms->catalog->hw_fence_rev)
|
||||
sde_encoder_helper_hw_fence_sw_override(phys_enc, hw_ctl);
|
||||
|
||||
SDE_ERROR("[enc:%d wb:%d] ctl_start timed out\n",
|
||||
DRMID(phys_enc->parent), WBID(wb_enc));
|
||||
SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), SDE_EVTLOG_ERROR);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (C) 2014-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
@@ -614,6 +614,29 @@ void sde_plane_dump_input_fence(struct drm_plane *plane)
|
||||
}
|
||||
}
|
||||
|
||||
bool sde_plane_is_sw_fence_signaled(struct drm_plane *plane)
|
||||
{
|
||||
struct sde_plane *psde;
|
||||
struct sde_plane_state *pstate;
|
||||
struct dma_fence *fence;
|
||||
|
||||
if (!plane) {
|
||||
SDE_ERROR("invalid plane\n");
|
||||
} else if (!plane->state) {
|
||||
SDE_ERROR_PLANE(to_sde_plane(plane), "invalid state\n");
|
||||
} else {
|
||||
psde = to_sde_plane(plane);
|
||||
pstate = to_sde_plane_state(plane->state);
|
||||
|
||||
if (pstate->input_fence) {
|
||||
fence = (struct dma_fence *)pstate->input_fence;
|
||||
return dma_fence_is_signaled(fence);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms)
|
||||
{
|
||||
struct sde_plane *psde;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2013 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
@@ -431,4 +431,11 @@ void sde_plane_add_data_to_minidump_va(struct drm_plane *plane);
|
||||
* @plane: Pointer to drm plane structure with the input fence we want to dump
|
||||
*/
|
||||
void sde_plane_dump_input_fence(struct drm_plane *plane);
|
||||
|
||||
/**
|
||||
* sde_plane_is_sw_fence_signaled - determine if the sw input dma-fence is signaled
|
||||
* @plane: Pointer to drm plane structure with the input fence to check
|
||||
* Returns: true if the input sw fence is signaled, otherwise false.
|
||||
*/
|
||||
bool sde_plane_is_sw_fence_signaled(struct drm_plane *plane);
|
||||
#endif /* _SDE_PLANE_H_ */
|
||||
|
Reference in New Issue
Block a user