From 9a1da8b4c3f7eda09f6a2ef3f605d98aab52a36b Mon Sep 17 00:00:00 2001 From: Prabhanjan Kandula Date: Fri, 31 Dec 2021 18:28:50 +0530 Subject: [PATCH 01/14] disp: msm: fix rsc static wakeup time calculation Currently RSC timer register programming is optimized for updating only during timing param changes and not during RSC state changes with same timing. Static wakeup time computation should consider panel jitter for RSC clk state too, else it can result in RSC hang. This change also removes extra logic for video mode prefil lines computation for rsc config as video mode does not enable RSC solver. Current issue scenario exposing the hang is in dual dsi display scenario where RSC is in clock state and static wakeup time is programmed by not considering panel jitter, after suspend/pmsuspend while waking up if RSC switches to command state if primary enabled first and vsync may arrive much early based on the panel jitter. RSC hw can not handle if TE arrives earlier than static wakeup time causing RSC hang. Change-Id: I1434fdd71eb04fdbe22b3601500493c818e9126d Signed-off-by: Prabhanjan Kandula Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 13 +------------ msm/sde_rsc.c | 6 +----- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 2f4ccf9ae938..190e2a473384 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1376,20 +1376,9 @@ static int _sde_encoder_update_rsc_client( (rsc_config->prefill_lines != mode_info->prefill_lines) || (rsc_config->jitter_numer != mode_info->jitter_numer) || (rsc_config->jitter_denom != mode_info->jitter_denom)) { - rsc_config->fps = mode_info->frame_rate; rsc_config->vtotal = mode_info->vtotal; - /* - * for video mode, prefill lines should not go beyond vertical - * front porch for RSCC configuration. This will ensure bw - * downvotes are not sent within the active region. Additional - * -1 is to give one line time for rscc mode min_threshold. - */ - if (is_vid_mode && (mode_info->prefill_lines >= v_front_porch)) - rsc_config->prefill_lines = v_front_porch - 1; - else - rsc_config->prefill_lines = mode_info->prefill_lines; - + rsc_config->prefill_lines = mode_info->prefill_lines; rsc_config->jitter_numer = mode_info->jitter_numer; rsc_config->jitter_denom = mode_info->jitter_denom; sde_enc->rsc_state_init = false; diff --git a/msm/sde_rsc.c b/msm/sde_rsc.c index 4b4c13bca0a5..14d180568115 100644 --- a/msm/sde_rsc.c +++ b/msm/sde_rsc.c @@ -331,11 +331,7 @@ static u32 sde_rsc_timer_calculate(struct sde_rsc_priv *rsc, line_time_ns = div_u64(line_time_ns, rsc->cmd_config.vtotal); prefill_time_ns = line_time_ns * rsc->cmd_config.prefill_lines; - /* only take jitter into account for CMD mode */ - if (state == SDE_RSC_CMD_STATE) - total = frame_time_ns - frame_jitter - prefill_time_ns; - else - total = frame_time_ns - prefill_time_ns; + total = frame_time_ns - frame_jitter - prefill_time_ns; if (total < 0) { pr_err("invalid total time period time:%llu jiter_time:%llu blanking time:%llu\n", From 205c579c9a61f758d5d29ae0cc136cd68e2b46ac Mon Sep 17 00:00:00 2001 From: Dhaval Patel Date: Thu, 21 Oct 2021 10:53:19 +0530 Subject: [PATCH 02/14] disp: msm: sde: avoid irq enable/disable during modeset Avoid irq enable/disable during modeset and trigger frame as posted start frame. This saves mode_set time and also avoids unbalanced vblank_irq in this usecase. Change-Id: I06958da5e52bc2aca0ddc60d2783615f80a839a4 Signed-off-by: Dhaval Patel Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 16 +---------- msm/sde/sde_encoder_phys.h | 3 +- msm/sde/sde_encoder_phys_cmd.c | 50 +++------------------------------- 3 files changed, 6 insertions(+), 63 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 2f4ccf9ae938..ec11d2d0091d 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 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 @@ -1873,19 +1874,6 @@ static int _sde_encoder_rc_pre_modeset(struct drm_encoder *drm_enc, sde_enc->rc_state = SDE_ENC_RC_STATE_ON; } - ret = sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE); - if (ret && ret != -EWOULDBLOCK) { - SDE_ERROR_ENC(sde_enc, - "wait for commit done returned %d\n", - ret); - SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, - ret, SDE_EVTLOG_ERROR); - ret = -EINVAL; - goto end; - } - - sde_encoder_irq_control(drm_enc, false); - SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, SDE_ENC_RC_STATE_MODESET, SDE_EVTLOG_FUNC_CASE5); @@ -1920,8 +1908,6 @@ static int _sde_encoder_rc_post_modeset(struct drm_encoder *drm_enc, goto end; } - sde_encoder_irq_control(drm_enc, true); - _sde_encoder_update_rsc_client(drm_enc, true); SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index 802140bd28c0..f3de4a43b8e3 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ @@ -275,7 +276,6 @@ struct sde_encoder_irq { * @enc_spinlock: Virtual-Encoder-Wide Spin Lock for IRQ purposes * @enable_state: Enable state tracking * @vblank_refcount: Reference count of vblank request - * @vblank_cached_refcount: Reference count of vblank cached request * @wbirq_refcount: Reference count of wb irq request * @vsync_cnt: Vsync count for the physical encoder * @underrun_cnt: Underrun count for the physical encoder @@ -325,7 +325,6 @@ struct sde_encoder_phys { enum sde_enc_enable_state enable_state; struct mutex *vblank_ctl_lock; atomic_t vblank_refcount; - atomic_t vblank_cached_refcount; atomic_t wbirq_refcount; atomic_t vsync_cnt; atomic_t underrun_cnt; diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index 8dd05021abac..403573cecd26 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ @@ -340,8 +341,6 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx( { struct sde_encoder_irq *irq; struct sde_kms *sde_kms; - int ret = 0; - u32 vblank_refcount; if (!phys_enc->sde_kms || !phys_enc->hw_pp || !phys_enc->hw_ctl) { SDE_ERROR("invalid args %d %d %d\n", !phys_enc->sde_kms, @@ -356,39 +355,13 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx( sde_kms = phys_enc->sde_kms; - mutex_lock(phys_enc->vblank_ctl_lock); - vblank_refcount = atomic_read(&phys_enc->vblank_refcount); - if (vblank_refcount) { - ret = sde_encoder_helper_unregister_irq(phys_enc, - INTR_IDX_RDPTR); - if (ret) - SDE_ERROR( - "control vblank irq registration error %d\n", - ret); - if (vblank_refcount > 1) - SDE_ERROR( - "vblank_refcount mismatch detected, try to reset %d\n", - atomic_read(&phys_enc->vblank_refcount)); - else - atomic_set(&phys_enc->vblank_cached_refcount, 1); - - SDE_EVT32(DRMID(phys_enc->parent), - phys_enc->hw_pp->idx - PINGPONG_0, vblank_refcount, - atomic_read(&phys_enc->vblank_cached_refcount)); - } - atomic_set(&phys_enc->vblank_refcount, 0); - mutex_unlock(phys_enc->vblank_ctl_lock); - irq = &phys_enc->irq[INTR_IDX_CTL_START]; irq->hw_idx = phys_enc->hw_ctl->idx; - irq->irq_idx = -EINVAL; irq = &phys_enc->irq[INTR_IDX_PINGPONG]; irq->hw_idx = phys_enc->hw_pp->idx; - irq->irq_idx = -EINVAL; irq = &phys_enc->irq[INTR_IDX_RDPTR]; - irq->irq_idx = -EINVAL; if (phys_enc->has_intf_te) irq->hw_idx = phys_enc->hw_intf->idx; else @@ -396,17 +369,14 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx( irq = &phys_enc->irq[INTR_IDX_UNDERRUN]; irq->hw_idx = phys_enc->intf_idx; - irq->irq_idx = -EINVAL; irq = &phys_enc->irq[INTR_IDX_AUTOREFRESH_DONE]; - irq->irq_idx = -EINVAL; if (phys_enc->has_intf_te) irq->hw_idx = phys_enc->hw_intf->idx; else irq->hw_idx = phys_enc->hw_pp->idx; irq = &phys_enc->irq[INTR_IDX_WRPTR]; - irq->irq_idx = -EINVAL; if (phys_enc->has_intf_te) irq->hw_idx = phys_enc->hw_intf->idx; else @@ -831,7 +801,7 @@ static int sde_encoder_phys_cmd_control_vblank_irq( struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); int ret = 0; - u32 refcount, cached_refcount; + u32 refcount; struct sde_kms *sde_kms; if (!phys_enc || !phys_enc->hw_pp) { @@ -846,17 +816,11 @@ static int sde_encoder_phys_cmd_control_vblank_irq( goto end; refcount = atomic_read(&phys_enc->vblank_refcount); - cached_refcount = atomic_read(&phys_enc->vblank_cached_refcount); /* protect against negative */ if (!enable && refcount == 0) { - if (cached_refcount == 1) { - atomic_set(&phys_enc->vblank_cached_refcount, 0); - goto end; - } else { - ret = -EINVAL; - goto end; - } + ret = -EINVAL; + goto end; } SDE_DEBUG_CMDENC(cmd_enc, "[%pS] enable=%d/%d\n", @@ -876,11 +840,6 @@ static int sde_encoder_phys_cmd_control_vblank_irq( atomic_inc_return(&phys_enc->vblank_refcount); } - if (enable && cached_refcount) { - atomic_inc(&phys_enc->vblank_refcount); - atomic_set(&phys_enc->vblank_cached_refcount, 0); - } - end: mutex_unlock(phys_enc->vblank_ctl_lock); if (ret) { @@ -2129,7 +2088,6 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( irq->cb.func = sde_encoder_phys_cmd_wr_ptr_irq; atomic_set(&phys_enc->vblank_refcount, 0); - atomic_set(&phys_enc->vblank_cached_refcount, 0); atomic_set(&phys_enc->pending_kickoff_cnt, 0); atomic_set(&phys_enc->pending_retire_fence_cnt, 0); atomic_set(&cmd_enc->pending_vblank_cnt, 0); From ff90d96806efc2e321ed14f9785166522513ba05 Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Mon, 10 Jan 2022 22:12:12 +0530 Subject: [PATCH 03/14] disp: msm: sde: avoid tx wait during DMS for targets with dsc rev2 This change removes tx_wait during DMS for targets which have dsc_hw_rev_2. For targets with dsc_hw_rev_1, during DMS the tx_wait is needed since DSC registers are not double buffered and frame trigger needs to be serialized to avoid pp_timeout issues. Change-Id: I46479ed8713602d167e57c9d9d0f800f544607f2 Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 12 ++++++++++++ msm/sde/sde_encoder_dce.c | 21 +++++++++++++++++++++ msm/sde/sde_encoder_dce.h | 7 +++++++ 3 files changed, 40 insertions(+) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index ec11d2d0091d..e7edc2033c10 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1874,6 +1874,18 @@ static int _sde_encoder_rc_pre_modeset(struct drm_encoder *drm_enc, sde_enc->rc_state = SDE_ENC_RC_STATE_ON; } + if (sde_encoder_has_dsc_hw_rev_2(sde_enc)) + goto skip_wait; + + ret = sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE); + if (ret && ret != -EWOULDBLOCK) { + SDE_ERROR_ENC(sde_enc, "wait for commit done returned %d\n", ret); + SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, ret, SDE_EVTLOG_ERROR); + ret = -EINVAL; + goto end; + } + +skip_wait: SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state, SDE_ENC_RC_STATE_MODESET, SDE_EVTLOG_FUNC_CASE5); diff --git a/msm/sde/sde_encoder_dce.c b/msm/sde/sde_encoder_dce.c index cde539fdd732..c299494c33df 100644 --- a/msm/sde/sde_encoder_dce.c +++ b/msm/sde/sde_encoder_dce.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. */ @@ -913,6 +914,26 @@ void sde_encoder_dce_set_bpp(struct msm_mode_info mode_info, sde_crtc->src_bpp, sde_crtc->target_bpp); } +bool sde_encoder_has_dsc_hw_rev_2(struct sde_encoder_virt *sde_enc) +{ + enum msm_display_compression_type comp_type; + int i; + + if (!sde_enc) + return false; + + comp_type = sde_enc->mode_info.comp_info.comp_type; + + if (comp_type != MSM_DISPLAY_COMPRESSION_DSC) + return false; + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) + if (sde_enc->hw_dsc[i]) + return test_bit(SDE_DSC_HW_REV_1_2, &sde_enc->hw_dsc[i]->caps->features); + + return false; +} + void sde_encoder_dce_disable(struct sde_encoder_virt *sde_enc) { enum msm_display_compression_type comp_type; diff --git a/msm/sde/sde_encoder_dce.h b/msm/sde/sde_encoder_dce.h index b217a03484fb..a406ee1c4d98 100644 --- a/msm/sde/sde_encoder_dce.h +++ b/msm/sde/sde_encoder_dce.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. */ @@ -36,4 +37,10 @@ int sde_encoder_dce_setup(struct sde_encoder_virt *sde_enc, */ void sde_encoder_dce_flush(struct sde_encoder_virt *sde_enc); +/** + * sde_encoder_has_dsc_hw_rev_2 :checks if dsc_hw_rev_1_2 feature is enabled + * @sde_enc: pointer to virtual encoder structure + */ +bool sde_encoder_has_dsc_hw_rev_2(struct sde_encoder_virt *sde_enc); + #endif /* __SDE_ENCODER_DCE_H__ */ From d55329a469cbe4a37f9042247d3d141733123c2d Mon Sep 17 00:00:00 2001 From: Dhaval Patel Date: Fri, 22 Oct 2021 12:13:56 +0530 Subject: [PATCH 04/14] disp: msm: sde: disable vsync_in to update tear check This change updates the single buffer tear check registers when vsync_in is disabled. It allows mode switch frame trigger as posted start frame trigger. Change-Id: I8068736b2ea01f6e4160e765fc39d7fc2a8590c9 Signed-off-by: Dhaval Patel Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index e7edc2033c10..767cc4c00bc5 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -2608,7 +2608,6 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc) &sde_enc->cur_master->intf_cfg_v1); _sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info, false); - sde_encoder_control_te(drm_enc, true); memset(&sde_enc->prv_conn_roi, 0, sizeof(sde_enc->prv_conn_roi)); memset(&sde_enc->cur_conn_roi, 0, sizeof(sde_enc->cur_conn_roi)); @@ -2711,6 +2710,7 @@ void sde_encoder_virt_restore(struct drm_encoder *drm_enc) sde_enc->cur_master->ops.restore(sde_enc->cur_master); _sde_encoder_virt_enable_helper(drm_enc); + sde_encoder_control_te(drm_enc, true); } static void sde_encoder_off_work(struct kthread_work *work) @@ -2792,6 +2792,9 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) return; } + /* turn off vsync_in to update tear check configuration */ + sde_encoder_control_te(drm_enc, false); + memset(&sde_enc->cur_master->intf_cfg_v1, 0, sizeof(sde_enc->cur_master->intf_cfg_v1)); @@ -2847,6 +2850,7 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) sde_enc->cur_master->ops.enable(sde_enc->cur_master); _sde_encoder_virt_enable_helper(drm_enc); + sde_encoder_control_te(drm_enc, true); } void sde_encoder_virt_reset(struct drm_encoder *drm_enc) From d32ac73186071dc6f025e47b60ddb37e3efe536a Mon Sep 17 00:00:00 2001 From: Dhaval Patel Date: Fri, 22 Oct 2021 12:17:56 +0530 Subject: [PATCH 05/14] disp: msm: sde: disable vsync counter before tear check update Disable vsync counter before single buffer tear check update. It allows to trigger the resolution switch frame as posted start frame. Change-Id: I2726372fd0e6d14ab0f79e3e3b0731a074158682 Signed-off-by: Dhaval Patel Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_hw_intf.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index 5c4bc917d654..1a97ef8d361e 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #include @@ -543,20 +544,20 @@ static int sde_hw_intf_setup_te_config(struct sde_hw_intf *intf, struct sde_hw_tear_check *te) { struct sde_hw_blk_reg_map *c; - int cfg; + u32 cfg = 0; if (!intf) return -EINVAL; c = &intf->hw; - cfg = BIT(19); /* VSYNC_COUNTER_EN */ if (te->hw_vsync_mode) cfg |= BIT(20); cfg |= te->vsync_count; SDE_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg); + wmb(); /* disable vsync counter before updating single buffer registers */ SDE_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); SDE_REG_WRITE(c, INTF_TEAR_VSYNC_INIT_VAL, te->vsync_init_val); SDE_REG_WRITE(c, INTF_TEAR_RD_PTR_IRQ, te->rd_ptr_irq); @@ -567,6 +568,8 @@ static int sde_hw_intf_setup_te_config(struct sde_hw_intf *intf, te->sync_threshold_start)); SDE_REG_WRITE(c, INTF_TEAR_SYNC_WRCOUNT, (te->start_pos + te->sync_threshold_start + 1)); + cfg |= BIT(19); /* VSYNC_COUNTER_EN */ + SDE_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg); return 0; } From c5ff99c3a31b75baf337a7f329903266a775ebae Mon Sep 17 00:00:00 2001 From: Yashwanth Date: Wed, 8 Dec 2021 11:18:32 +0530 Subject: [PATCH 06/14] disp: msm: sde: update TEAR_SYNC_WRCOUNT register before vsync counter During DMS, when tear check registers are updated near rd_ptr line count, it was resulting in a spurious rd_ptr_irq to which frame is getting latched and causing tearing on the screen. This change updates TEAR_SYNC_WRCOUNT register before disabling the vsync counter and adds a spinlock to avoid pre-emption. Change-Id: I986dc3ce6fb3da5fed758c2f50562df44f2ab557 Signed-off-by: Yashwanth --- msm/sde/sde_hw_intf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index 1a97ef8d361e..bc21321e3a9b 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -545,10 +545,12 @@ static int sde_hw_intf_setup_te_config(struct sde_hw_intf *intf, { struct sde_hw_blk_reg_map *c; u32 cfg = 0; + spinlock_t tearcheck_spinlock; if (!intf) return -EINVAL; + spin_lock_init(&tearcheck_spinlock); c = &intf->hw; if (te->hw_vsync_mode) @@ -556,6 +558,14 @@ static int sde_hw_intf_setup_te_config(struct sde_hw_intf *intf, cfg |= te->vsync_count; + /* + * Local spinlock is acquired here to avoid pre-emption + * as below register programming should be completed in + * less than 2^16 vsync clk cycles. + */ + spin_lock(&tearcheck_spinlock); + SDE_REG_WRITE(c, INTF_TEAR_SYNC_WRCOUNT, + (te->start_pos + te->sync_threshold_start + 1)); SDE_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg); wmb(); /* disable vsync counter before updating single buffer registers */ SDE_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_HEIGHT, te->sync_cfg_height); @@ -566,10 +576,9 @@ static int sde_hw_intf_setup_te_config(struct sde_hw_intf *intf, SDE_REG_WRITE(c, INTF_TEAR_SYNC_THRESH, ((te->sync_threshold_continue << 16) | te->sync_threshold_start)); - SDE_REG_WRITE(c, INTF_TEAR_SYNC_WRCOUNT, - (te->start_pos + te->sync_threshold_start + 1)); cfg |= BIT(19); /* VSYNC_COUNTER_EN */ SDE_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg); + spin_unlock(&tearcheck_spinlock); return 0; } From 8d57f083f2c20f9dfc28e9515d319d7bcff0fdf5 Mon Sep 17 00:00:00 2001 From: Lei Chen Date: Fri, 22 Oct 2021 12:21:19 +0530 Subject: [PATCH 07/14] disp: msm: sde: cancel delayed_off_work before reinitialization Canceling delayed_off_work in encoder pre_modeset might not be executed in all cases, but the following encoder enable might initialize the work. This will lead to list corruption as delayed_off work list node is reinitialized before removing from linked list. Move canceling delayed_off_work to start of encoder mode_set to ensure work is canceled before reinitialization. Change-Id: I38687604f2eedced308ea02019c162022725534e Signed-off-by: Lei Chen Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 767cc4c00bc5..c78066d282a0 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1842,9 +1842,6 @@ static int _sde_encoder_rc_pre_modeset(struct drm_encoder *drm_enc, { int ret = 0; - /* cancel delayed off work, if any */ - _sde_encoder_rc_cancel_delayed(sde_enc, sw_event); - mutex_lock(&sde_enc->rc_lock); if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF) { @@ -2371,6 +2368,9 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, sde_connector_state_get_mode_info(conn->state, &sde_enc->mode_info); sde_encoder_dce_set_bpp(sde_enc->mode_info, sde_enc->crtc); + /* cancel delayed off work, if any */ + kthread_cancel_delayed_work_sync(&sde_enc->delayed_off_work); + /* release resources before seamless mode change */ ret = sde_encoder_virt_modeset_rc(drm_enc, adj_mode, true); if (ret) From f77b7c533c0bd59b2a0b0e5bda4974f062a73a8d Mon Sep 17 00:00:00 2001 From: Lei Chen Date: Fri, 22 Oct 2021 12:25:08 +0530 Subject: [PATCH 08/14] disp: msm: sde: remove clearing cur_master in encoder enable function SDE IRQ callback can run in parallel thread to modeset after removing pp_done wait before pre_modeset. If cur_master is cleared in encoder enable function and irq callback is triggered at the same time, the irq callback could not be handled properly as cur_master is NULL.So remove clearing cur_master in encoder enable function to avoid the race condition between modeset and irq callback. Change-Id: I2059c699a68838b3c9f6a7dd658a35f178b18c42 Signed-off-by: Lei Chen Signed-off-by: Jayaprakash Madisetty --- msm/sde/sde_encoder.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index c78066d282a0..5605f5399db0 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -2733,6 +2733,7 @@ static void sde_encoder_off_work(struct kthread_work *work) static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; + bool has_master_enc = false; int i, ret = 0; struct msm_compression_info *comp_info = NULL; struct drm_display_mode *cur_mode = NULL; @@ -2759,18 +2760,19 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) SDE_DEBUG_ENC(sde_enc, "\n"); SDE_EVT32(DRMID(drm_enc), cur_mode->hdisplay, cur_mode->vdisplay); - sde_enc->cur_master = NULL; for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; if (phys && phys->ops.is_master && phys->ops.is_master(phys)) { SDE_DEBUG_ENC(sde_enc, "master is now idx %d\n", i); sde_enc->cur_master = phys; + has_master_enc = true; break; } } - if (!sde_enc->cur_master) { + if (!has_master_enc) { + sde_enc->cur_master = NULL; SDE_ERROR("virt encoder has no master! num_phys %d\n", i); return; } From bcb9619f9f536275baabefd097ae248f82360eaf Mon Sep 17 00:00:00 2001 From: Andhavarapu Karthik Date: Mon, 10 Jan 2022 12:33:39 +0530 Subject: [PATCH 09/14] disp: msm: sde: fix RM poll timeouts during PM suspend/resume usecase When PM resume commit occurs with mode_changed, enable flag set and active_changed flag not set, the RM reservation allocated is not cleared during crtc_destroy_state as encoder_mask in old_crtc_state is NULL. When there is resume commit from HAL, it polls for this pending reservation to be cleared causing poll timeouts. This change releases the pending reservation from the crtc->state->encoder mask, as the old_crtc_state encoder_mask will be reset with default values at start of PM resume. Change-Id: Ica1c90a6ea7ef7df08fcb976b6f1b54bbfeea357 Signed-off-by: Andhavarapu Karthik --- msm/sde/sde_crtc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index e6811e616a58..a17ffaeac08a 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 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 @@ -3482,6 +3483,7 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, struct sde_crtc_state *cstate; struct drm_encoder *enc; struct sde_kms *sde_kms; + u32 encoder_mask; if (!crtc || !state) { SDE_ERROR("invalid argument(s)\n"); @@ -3497,9 +3499,11 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, return; } - SDE_DEBUG("crtc%d\n", crtc->base.id); + encoder_mask = state->encoder_mask ? state->encoder_mask : + crtc->state->encoder_mask; + SDE_DEBUG("crtc%d\n, encoder_mask=%d", crtc->base.id, encoder_mask); - drm_for_each_encoder_mask(enc, crtc->dev, state->encoder_mask) + drm_for_each_encoder_mask(enc, crtc->dev, encoder_mask) sde_rm_release(&sde_kms->rm, enc, true); __drm_atomic_helper_crtc_destroy_state(state); From fb71974cf5e125f7811978af08f8c42b88b47070 Mon Sep 17 00:00:00 2001 From: Yahui Wang Date: Tue, 4 Jan 2022 13:49:19 +0800 Subject: [PATCH 10/14] disp: msm: sde: move sde power event call into kms post init The sde power event function needs to get actual sde kms irq number to handle irq update call, but it is not able to know the irq number before irq installation, so move sde power event call into kms post init to avoid unbalanced irq issues. Change-Id: Id262b86f98299fbb9a51c9ccb8e68c7bde7f57ed Signed-off-by: Yahui Wang --- msm/sde/sde_kms.c | 53 +++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 6785c65754dd..b9609ec6fc0f 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 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 @@ -111,6 +112,8 @@ static int _sde_kms_mmu_destroy(struct sde_kms *sde_kms); static int _sde_kms_mmu_init(struct sde_kms *sde_kms); static int _sde_kms_register_events(struct msm_kms *kms, struct drm_mode_object *obj, u32 event, bool en); +static void sde_kms_handle_power_event(u32 event_type, void *usr); + bool sde_is_custom_client(void) { return sdecustom; @@ -2178,14 +2181,38 @@ static int sde_kms_postinit(struct msm_kms *kms) struct drm_crtc *crtc; struct drm_connector *conn; struct drm_connector_list_iter conn_iter; - int rc; + struct msm_drm_private *priv; + int i, rc; - if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev) { + if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev || + !sde_kms->dev->dev_private) { SDE_ERROR("invalid sde_kms\n"); return -EINVAL; } dev = sde_kms->dev; + priv = sde_kms->dev->dev_private; + + /* + * Handle (re)initializations during power enable, the sde power + * event call has to be after drm_irq_install to handle irq update. + */ + sde_kms_handle_power_event(SDE_POWER_EVENT_POST_ENABLE, sde_kms); + sde_kms->power_event = sde_power_handle_register_event(&priv->phandle, + SDE_POWER_EVENT_POST_ENABLE | + SDE_POWER_EVENT_PRE_DISABLE, + sde_kms_handle_power_event, sde_kms, "kms"); + + if (sde_kms->splash_data.num_splash_displays) { + SDE_DEBUG("Skipping MDP Resources disable\n"); + } else { + for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) + sde_power_data_bus_set_quota(&priv->phandle, i, + SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA, + SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA); + + pm_runtime_put_sync(sde_kms->dev->dev); + } rc = _sde_debugfs_init(sde_kms); if (rc) @@ -4686,7 +4713,7 @@ static int sde_kms_hw_init(struct msm_kms *kms) struct drm_device *dev; struct msm_drm_private *priv; struct platform_device *platformdev; - int i, irq_num, rc = -EINVAL; + int irq_num, rc = -EINVAL; if (!kms) { SDE_ERROR("invalid kms\n"); @@ -4735,26 +4762,6 @@ static int sde_kms_hw_init(struct msm_kms *kms) */ dev->mode_config.allow_fb_modifiers = true; - /* - * Handle (re)initializations during power enable - */ - sde_kms_handle_power_event(SDE_POWER_EVENT_POST_ENABLE, sde_kms); - sde_kms->power_event = sde_power_handle_register_event(&priv->phandle, - SDE_POWER_EVENT_POST_ENABLE | - SDE_POWER_EVENT_PRE_DISABLE, - sde_kms_handle_power_event, sde_kms, "kms"); - - if (sde_kms->splash_data.num_splash_displays) { - SDE_DEBUG("Skipping MDP Resources disable\n"); - } else { - for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) - sde_power_data_bus_set_quota(&priv->phandle, i, - SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA, - SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA); - - pm_runtime_put_sync(sde_kms->dev->dev); - } - sde_kms->affinity_notify.notify = sde_kms_irq_affinity_notify; sde_kms->affinity_notify.release = sde_kms_irq_affinity_release; From 58fe91002a987158cccc9c43a88140c12fdaf26e Mon Sep 17 00:00:00 2001 From: Yashwanth Date: Mon, 17 Jan 2022 11:37:56 +0530 Subject: [PATCH 11/14] disp: msm: sde: always set CTL_x_UIDLE_ACTIVE register to "1" As per HW recommendation, FAL10_VETO_OVERRIDE register can be programmed to disable FAL10 in alternate to disabling uidle at the sspp level as disabling UIDLE controller will only disable DPU traffic shaping and will not stop the system from entering FAL10 state. This change programs FAL10_VETO_OVERRIDE register during uidle disable and also sets CTL_x_UIDLE_ACTIVE register to always one to avoid race condition between different CTL paths. Change-Id: I0361543e345bf6c237ad60560e2b11604f5abf92 Signed-off-by: Yashwanth --- msm/sde/sde_crtc.c | 7 +------ msm/sde/sde_crtc.h | 2 +- msm/sde/sde_hw_uidle.c | 6 +++++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index e6811e616a58..5bcbd67736a9 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 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 @@ -3324,11 +3325,6 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, if (crtc->state->mode_changed || sde_kms->perf.catalog->uidle_cfg.dirty) sde_core_perf_crtc_update_uidle(crtc, true); - else if (!test_bit(SDE_CRTC_DIRTY_UIDLE, &sde_crtc->revalidate_mask) && - !sde_kms->perf.uidle_enabled) - sde_core_uidle_setup_ctl(crtc, false); - - test_and_clear_bit(SDE_CRTC_DIRTY_UIDLE, &sde_crtc->revalidate_mask); /* * Since CP properties use AXI buffer to program the @@ -3999,7 +3995,6 @@ void sde_crtc_reset_sw_state(struct drm_crtc *crtc) /* mark other properties which need to be dirty for next update */ set_bit(SDE_CRTC_DIRTY_DIM_LAYERS, &sde_crtc->revalidate_mask); - set_bit(SDE_CRTC_DIRTY_UIDLE, &sde_crtc->revalidate_mask); if (cstate->num_ds_enabled) set_bit(SDE_CRTC_DIRTY_DEST_SCALER, cstate->dirty); } diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index 43db78485c69..4071e4841485 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022 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 @@ -399,7 +400,6 @@ struct sde_crtc { enum sde_crtc_dirty_flags { SDE_CRTC_DIRTY_DEST_SCALER, SDE_CRTC_DIRTY_DIM_LAYERS, - SDE_CRTC_DIRTY_UIDLE, SDE_CRTC_DIRTY_MAX, }; diff --git a/msm/sde/sde_hw_uidle.c b/msm/sde/sde_hw_uidle.c index bb6915205c7e..858cf42a9f1c 100644 --- a/msm/sde/sde_hw_uidle.c +++ b/msm/sde/sde_hw_uidle.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. * */ @@ -161,7 +162,7 @@ void sde_hw_uidle_setup_ctl(struct sde_hw_uidle *uidle, struct sde_uidle_ctl_cfg *cfg) { struct sde_hw_blk_reg_map *c = &uidle->hw; - u32 reg_val; + u32 reg_val, fal10_veto_regval = 0; reg_val = SDE_REG_READ(c, UIDLE_CTL); reg_val = (reg_val & ~BIT(31)) | (cfg->uidle_enable ? BIT(31) : 0); @@ -176,6 +177,9 @@ void sde_hw_uidle_setup_ctl(struct sde_hw_uidle *uidle, FAL10_EXIT_CNT_MSK); SDE_REG_WRITE(c, UIDLE_CTL, reg_val); + if (!cfg->uidle_enable) + fal10_veto_regval |= (BIT(31) | BIT(0)); + SDE_REG_WRITE(c, UIDLE_FAL10_VETO_OVERRIDE, fal10_veto_regval); } static void sde_hw_uilde_active_override(struct sde_hw_uidle *uidle, From 075e3e1fa1531eaff0927aea382818a09547544a Mon Sep 17 00:00:00 2001 From: Yahui Wang Date: Mon, 17 Jan 2022 16:53:25 +0800 Subject: [PATCH 12/14] disp: msm: sde: set NOAUTOEN for sde irq to match with power event If display cont-splash is enabled, then sde irq will be enabled after registration, but sde power event assumes irq to be disabled by default and will still try to enable irq with first power event call, then could cause unbalanced irq enable warning on boot up. Change-Id: Ic5482dd06501721664994f77cd5764140afb7a62 Signed-off-by: Yahui Wang --- msm/sde/sde_irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/msm/sde/sde_irq.c b/msm/sde/sde_irq.c index 974c163a6770..b83b07dc1f87 100644 --- a/msm/sde/sde_irq.c +++ b/msm/sde/sde_irq.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2019, 2021, The Linux Foundation. All rights reserved. */ @@ -103,7 +104,7 @@ void sde_irq_preinstall(struct msm_kms *kms) } /* disable irq until power event enables it */ - if (!sde_kms->splash_data.num_splash_displays && !sde_kms->irq_enabled) + if (!sde_kms->irq_enabled) irq_set_status_flags(sde_kms->irq_num, IRQ_NOAUTOEN); } From 986687d5d17e3f8a5056fc43924a17608c0e4c34 Mon Sep 17 00:00:00 2001 From: Sai Srujana Oruganti Date: Mon, 10 Jan 2022 15:22:46 +0530 Subject: [PATCH 13/14] disp: msm: sde: fix null pointer dereference Add condition to prevent null point dereference. Change-Id: If6019c0c7035a25ed87afa02c056044c8716bd64 Signed-off-by: Sai Srujana Oruganti --- msm/sde/sde_hw_reg_dma_v1.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/msm/sde/sde_hw_reg_dma_v1.c b/msm/sde/sde_hw_reg_dma_v1.c index 7b73dafecddc..917d76e0622b 100644 --- a/msm/sde/sde_hw_reg_dma_v1.c +++ b/msm/sde/sde_hw_reg_dma_v1.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1191,6 +1192,10 @@ static int last_cmd_v1(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q, //Lack of block support will be caught by kick_off memset(&hw, 0, sizeof(hw)); SET_UP_REG_DMA_REG(hw, reg_dma, kick_off.dma_type); + if (hw.hwversion == 0) { + DRM_ERROR("DMA type %d is unsupported\n", kick_off.dma_type); + return -EOPNOTSUPP; + } SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY, mode, ctl->idx, kick_off.queue_select, kick_off.dma_type, kick_off.op); From 3edfceab68d295c8a238aa8662012acee8482d99 Mon Sep 17 00:00:00 2001 From: Prabhanjan Kandula Date: Mon, 29 Nov 2021 17:28:03 -0800 Subject: [PATCH 14/14] disp: msm: sde: while timing engine enabling poll for active region DCS commands triggered right after timing engine enable can conflict with blanking period causing command transfer failures. Right after timing engine enable poll for frame start and line count reaching active region of display before any DCS commands. Change-Id: Ia3967e01c3bb5bc82aa3549c300fa8335e00210c Signed-off-by: Prabhanjan Kandula --- msm/sde/sde_encoder_phys_vid.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 7e444aa4dc79..2937de7088e9 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -1107,12 +1107,41 @@ exit: phys_enc->enable_state = SDE_ENC_DISABLED; } +static int sde_encoder_phys_vid_poll_for_active_region(struct sde_encoder_phys *phys_enc) +{ + struct sde_encoder_phys_vid *vid_enc; + struct intf_timing_params *timing; + u32 line_cnt, v_inactive, poll_time_us, trial = 0; + + if (!phys_enc || !phys_enc->hw_intf || !phys_enc->hw_intf->ops.get_line_count) + return -EINVAL; + + vid_enc = to_sde_encoder_phys_vid(phys_enc); + timing = &vid_enc->timing_params; + + /* if programmable fetch is not enabled return early */ + if (!programmable_fetch_get_num_lines(vid_enc, timing)) + return 0; + + poll_time_us = DIV_ROUND_UP(1000000, timing->vrefresh) / MAX_POLL_CNT; + v_inactive = timing->v_front_porch + timing->v_back_porch + timing->vsync_pulse_width; + + do { + usleep_range(poll_time_us, poll_time_us + 5); + line_cnt = phys_enc->hw_intf->ops.get_line_count(phys_enc->hw_intf); + trial++; + } while ((trial < MAX_POLL_CNT) || (line_cnt < v_inactive)); + + return (trial >= MAX_POLL_CNT) ? -ETIMEDOUT : 0; +} + static void sde_encoder_phys_vid_handle_post_kickoff( struct sde_encoder_phys *phys_enc) { unsigned long lock_flags; struct sde_encoder_phys_vid *vid_enc; u32 avr_mode; + u32 ret; if (!phys_enc) { SDE_ERROR("invalid encoder\n"); @@ -1135,6 +1164,10 @@ static void sde_encoder_phys_vid_handle_post_kickoff( 1); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); + + ret = sde_encoder_phys_vid_poll_for_active_region(phys_enc); + if (ret) + SDE_DEBUG_VIDENC(vid_enc, "poll for active failed ret:%d\n", ret); } phys_enc->enable_state = SDE_ENC_ENABLED; }