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:
Christina Oliveira
2023-01-24 14:52:23 -08:00
부모 6ecd45a1dc
커밋 bb846fab11
9개의 변경된 파일177개의 추가작업 그리고 5개의 파일을 삭제

파일 보기

@@ -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_ */