From 9c77f40c14bc397a759337e1880e0edf08a858df Mon Sep 17 00:00:00 2001 From: Steve Cohen Date: Fri, 9 Jul 2021 19:31:03 -0400 Subject: [PATCH] disp: msm: sde: cancel delayed work items during TUI transition Delayed work items may touch HW registers. If these work items run while HW is not owned by this VM it will lead to invalid access. This happens in video mode as HAL does not disable idle power-collapse in this mode. It can also happen with ESD status if lastclose or TUI transition failure occurs. Although there is a contract with user mode to turn off certain features, kernel cannot rely on it to always do the right thing. Prevent potential crashes from certain corner cases by cancelling all delayed work items when the HW ownership is transferred. Change-Id: I08da17f2ce72bf2fddf71924c3e8edd2e2715be8 Signed-off-by: Steve Cohen --- msm/sde/sde_crtc.c | 18 ++++++ msm/sde/sde_crtc.h | 8 ++- msm/sde/sde_encoder.c | 13 +++- msm/sde/sde_encoder.h | 7 +++ msm/sde/sde_kms.c | 143 +++++++++++++++++++++--------------------- 5 files changed, 116 insertions(+), 73 deletions(-) diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 9caa41328e80..24b773058f52 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -6626,6 +6626,24 @@ static void __sde_crtc_idle_notify_work(struct kthread_work *work) } } +void sde_crtc_cancel_delayed_work(struct drm_crtc *crtc) +{ + struct sde_crtc *sde_crtc; + struct sde_crtc_state *cstate; + bool idle_status; + bool cache_status; + + if (!crtc || !crtc->state) + return; + + sde_crtc = to_sde_crtc(crtc); + cstate = to_sde_crtc_state(crtc->state); + + idle_status = kthread_cancel_delayed_work_sync(&sde_crtc->idle_notify_work); + cache_status = kthread_cancel_delayed_work_sync(&sde_crtc->static_cache_read_work); + SDE_EVT32(DRMID(crtc), idle_status, cache_status); +} + /* initialize crtc */ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) { diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index 4071e4841485..75fb998ed06d 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.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 @@ -966,4 +966,10 @@ void sde_crtc_reset_sw_state(struct drm_crtc *crtc); */ void _sde_crtc_clear_dim_layers_v1(struct drm_crtc_state *state); +/** + * sde_crtc_cancel_delayed_work - cancel any pending work items for a given crtc + * @crtc: Pointer to DRM crtc object + */ +void sde_crtc_cancel_delayed_work(struct drm_crtc *crtc); + #endif /* _SDE_CRTC_H_ */ diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 3eeb518c1a63..9c103530d916 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -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) 2014-2021, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -1687,6 +1687,17 @@ static void _sde_encoder_rc_cancel_delayed(struct sde_encoder_virt *sde_enc, sw_event); } +void sde_encoder_cancel_delayed_work(struct drm_encoder *encoder) +{ + struct sde_encoder_virt *sde_enc; + + if (!encoder) + return; + + sde_enc = to_sde_encoder_virt(encoder); + _sde_encoder_rc_cancel_delayed(sde_enc, 0); +} + static void _sde_encoder_rc_kickoff_delayed(struct sde_encoder_virt *sde_enc, u32 sw_event) { diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index 3bd432072c8b..a1c138c16999 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 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 @@ -597,6 +598,12 @@ static inline u32 sde_encoder_get_dfps_maxfps(struct drm_encoder *drm_enc) */ void sde_encoder_virt_reset(struct drm_encoder *drm_enc); +/** + * sde_encoder_cancel_delayed_work - cancel delayed off work for encoder + * @drm_enc: Pointer to drm encoder structure + */ +void sde_encoder_cancel_delayed_work(struct drm_encoder *encoder); + /** * sde_encoder_get_kms - retrieve the kms from encoder * @drm_enc: Pointer to drm encoder structure diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 4c5705ce81b3..85c5e20efb82 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1034,6 +1034,7 @@ int sde_kms_vm_primary_prepare_commit(struct sde_kms *sde_kms, struct drm_connector *connector; struct sde_vm_ops *vm_ops; struct sde_crtc_state *cstate; + struct drm_connector_list_iter iter; enum sde_crtc_vm_req vm_req; int rc = 0; @@ -1070,9 +1071,11 @@ int sde_kms_vm_primary_prepare_commit(struct sde_kms *sde_kms, } /* Schedule ESD work */ - list_for_each_entry(connector, &ddev->mode_config.connector_list, head) + drm_connector_list_iter_begin(ddev, &iter); + drm_for_each_connector_iter(connector, &iter) if (drm_connector_mask(connector) & crtc->state->connector_mask) sde_connector_schedule_status_work(connector, true); + drm_connector_list_iter_end(&iter); /* enable vblank events */ drm_crtc_vblank_on(crtc); @@ -1282,6 +1285,72 @@ static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms, } } +static void sde_kms_cancel_delayed_work(struct drm_crtc *crtc) +{ + struct drm_connector *connector; + struct drm_connector_list_iter iter; + struct drm_encoder *encoder; + + /* Cancel CRTC work */ + sde_crtc_cancel_delayed_work(crtc); + + /* Cancel ESD work */ + drm_connector_list_iter_begin(crtc->dev, &iter); + drm_for_each_connector_iter(connector, &iter) + if (drm_connector_mask(connector) & crtc->state->connector_mask) + sde_connector_schedule_status_work(connector, false); + drm_connector_list_iter_end(&iter); + + /* Cancel Idle-PC work */ + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { + if (sde_encoder_in_clone_mode(encoder)) + continue; + + sde_encoder_cancel_delayed_work(encoder); + } +} + +int sde_kms_vm_pre_release(struct sde_kms *sde_kms, + struct drm_atomic_state *state, bool is_primary) +{ + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int rc = 0; + + crtc = sde_kms_vm_get_vm_crtc(state); + if (!crtc) + return 0; + + /* if vm_req is enabled, once CRTC on the commit is guaranteed */ + sde_kms_wait_for_frame_transfer_complete(&sde_kms->base, crtc); + + sde_kms_cancel_delayed_work(crtc); + + /* disable SDE irq's */ + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + if (sde_encoder_in_clone_mode(encoder)) + continue; + + sde_encoder_irq_control(encoder, false); + } + + if (is_primary) { + /* disable IRQ line */ + sde_irq_update(&sde_kms->base, false); + + /* disable vblank events */ + drm_crtc_vblank_off(crtc); + + /* reset sw state */ + sde_crtc_reset_sw_state(crtc); + } + + sde_dbg_set_hw_ownership_status(false); + + return rc; +} + int sde_kms_vm_trusted_post_commit(struct sde_kms *sde_kms, struct drm_atomic_state *state) { @@ -1289,7 +1358,6 @@ int sde_kms_vm_trusted_post_commit(struct sde_kms *sde_kms, struct drm_device *ddev; struct drm_crtc *crtc; struct drm_plane *plane; - struct drm_encoder *encoder; struct sde_crtc_state *cstate; struct drm_crtc_state *new_cstate; enum sde_crtc_vm_req vm_req; @@ -1311,24 +1379,13 @@ int sde_kms_vm_trusted_post_commit(struct sde_kms *sde_kms, if (vm_req != VM_REQ_RELEASE) return 0; - /* if vm_req is enabled, once CRTC on the commit is guaranteed */ - sde_kms_wait_for_frame_transfer_complete(&sde_kms->base, crtc); - - drm_for_each_encoder_mask(encoder, crtc->dev, - crtc->state->encoder_mask) { - if (sde_encoder_in_clone_mode(encoder)) - continue; - - sde_encoder_irq_control(encoder, false); - } + sde_kms_vm_pre_release(sde_kms, state, false); list_for_each_entry(plane, &ddev->mode_config.plane_list, head) sde_plane_set_sid(plane, 0); sde_hw_set_lutdma_sid(sde_kms->hw_sid, 0); - sde_dbg_set_hw_ownership_status(false); - sde_vm_lock(sde_kms); if (vm_ops->vm_release) @@ -1339,62 +1396,6 @@ int sde_kms_vm_trusted_post_commit(struct sde_kms *sde_kms, return rc; } -int sde_kms_vm_pre_release(struct sde_kms *sde_kms, - struct drm_atomic_state *state) -{ - struct drm_device *ddev; - struct drm_crtc *crtc; - struct drm_encoder *encoder; - struct drm_connector *connector; - int rc = 0; - struct msm_drm_private *priv; - - ddev = sde_kms->dev; - - crtc = sde_kms_vm_get_vm_crtc(state); - if (!crtc) - return 0; - - priv = crtc->dev->dev_private; - /* if vm_req is enabled, once CRTC on the commit is guaranteed */ - sde_kms_wait_for_frame_transfer_complete(&sde_kms->base, crtc); - - /* disable ESD work */ - list_for_each_entry(connector, - &ddev->mode_config.connector_list, head) { - if (drm_connector_mask(connector) & crtc->state->connector_mask) - sde_connector_schedule_status_work(connector, false); - } - - /* disable SDE irq's */ - drm_for_each_encoder_mask(encoder, crtc->dev, - crtc->state->encoder_mask) { - if (sde_encoder_in_clone_mode(encoder)) - continue; - - sde_encoder_irq_control(encoder, false); - } - - /* disable IRQ line */ - sde_irq_update(&sde_kms->base, false); - - /* disable vblank events */ - drm_crtc_vblank_off(crtc); - - /* - * Flush event thread queue for any pending events as vblank work - * might get scheduled from drm_crtc_vblank_off - */ - kthread_flush_worker(&priv->event_thread[crtc->index].worker); - - /* reset sw state */ - sde_crtc_reset_sw_state(crtc); - - sde_dbg_set_hw_ownership_status(false); - - return rc; -} - int sde_kms_vm_primary_post_commit(struct sde_kms *sde_kms, struct drm_atomic_state *state) { @@ -1421,7 +1422,7 @@ int sde_kms_vm_primary_post_commit(struct sde_kms *sde_kms, return 0; /* handle SDE pre-release */ - rc = sde_kms_vm_pre_release(sde_kms, state); + rc = sde_kms_vm_pre_release(sde_kms, state, true); if (rc) { SDE_ERROR("sde vm pre_release failed, rc=%d\n", rc); goto exit;