From bb846fab11ed6dcc2b315f520ccc78dcd8ad6bcf Mon Sep 17 00:00:00 2001 From: Christina Oliveira Date: Tue, 24 Jan 2023 14:52:23 -0800 Subject: [PATCH] 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 --- msm/sde/sde_crtc.c | 14 +++++++- msm/sde/sde_crtc.h | 9 ++++- msm/sde/sde_encoder.c | 61 ++++++++++++++++++++++++++++++++++ msm/sde/sde_encoder_phys.h | 22 ++++++++++++ msm/sde/sde_encoder_phys_cmd.c | 13 ++++++++ msm/sde/sde_encoder_phys_vid.c | 12 +++++++ msm/sde/sde_encoder_phys_wb.c | 17 +++++++++- msm/sde/sde_plane.c | 25 +++++++++++++- msm/sde/sde_plane.h | 9 ++++- 9 files changed, 177 insertions(+), 5 deletions(-) diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 075366f901..334de2ac99 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -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 @@ -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 diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index e3f24fb9c1..8294bc453b 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -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 @@ -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 diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 3ba95f1a76..72d222734a 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -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; diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index f2f99efc24..7530b320d9 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -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 diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index d14e5c2d74..4ff2ebce2e 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -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; diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 5cf699b074..fd541000cb 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -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); } diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index d45167cb62..fc421216c3 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -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); diff --git a/msm/sde/sde_plane.c b/msm/sde/sde_plane.c index 36416ebaca..a875ec125d 100644 --- a/msm/sde/sde_plane.c +++ b/msm/sde/sde_plane.c @@ -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 @@ -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; diff --git a/msm/sde/sde_plane.h b/msm/sde/sde_plane.h index 769b1b1737..cc140812ae 100644 --- a/msm/sde/sde_plane.h +++ b/msm/sde/sde_plane.h @@ -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 @@ -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_ */