From 3ccfb3948369ecd7424610b0f92f8c39d8ed7c14 Mon Sep 17 00:00:00 2001 From: Renchao Liu Date: Wed, 31 Aug 2022 17:34:18 +0800 Subject: [PATCH 01/10] disp: msm: sde: fix null pointer dereference issue This changes fixes null pointer dereference issue. Change-Id: I9a9628f1fb274aea86a15792ac85b8505f25d28f Signed-off-by: Renchao Liu --- msm/sde/sde_color_processing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 42181db2..cc9d10d4 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -1828,7 +1828,7 @@ static void _sde_cp_dspp_flush_helper(struct sde_crtc *sde_crtc, u32 feature) for (i = 0; i < num_mixers; i++) { ctl = sde_crtc->mixers[i].hw_ctl; dspp = sde_crtc->mixers[i].hw_dspp; - if (ctl && ctl->ops.update_bitmask_dspp_subblk) { + if (ctl && dspp && ctl->ops.update_bitmask_dspp_subblk) { if (feature == SDE_CP_CRTC_DSPP_SB) { if (!dspp->sb_dma_in_use) continue; From abf0fdd34122230f522e1c657800a35c1a239997 Mon Sep 17 00:00:00 2001 From: Amine Najahi Date: Wed, 31 Aug 2022 16:14:25 -0400 Subject: [PATCH 02/10] disp: msm: update RSC bandwidth during solver mode transition Currently when disconnecting a secondary monitor, RSC will transition to solver mode. If the bandwidth remains the same for primary display, SW will not update BW indication register causing stale TCS wait values. This change forces a register update when RSC mode is changed to solver mode. Change-Id: I99d2332621bad75a7b6abdb64d6aedd35c30ca63 Signed-off-by: Amine Najahi --- msm/sde_rsc_hw_v3.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/msm/sde_rsc_hw_v3.c b/msm/sde_rsc_hw_v3.c index ef6066de..12229f22 100644 --- a/msm/sde_rsc_hw_v3.c +++ b/msm/sde_rsc_hw_v3.c @@ -429,6 +429,11 @@ static int sde_rsc_state_update_v3(struct sde_rsc_priv *rsc, reg, rsc->debug_mode); wmb(); /* make sure that solver is enabled */ + if (rsc->hw_ops.bwi_status) { + rsc->bwi_update = BW_NO_CHANGE; + rsc->hw_ops.bwi_status(rsc); + } + break; case SDE_RSC_VID_STATE: From 0df777b98db2ad7257ca2e2b30fc3f386f769b26 Mon Sep 17 00:00:00 2001 From: Sandeep Gangadharaiah Date: Fri, 9 Sep 2022 13:54:06 -0700 Subject: [PATCH 03/10] disp: msm: dp: clear MST sim context during DP sim disable After freeing MST sim context memory the pointer isn't set to NULL leading to unauthorized memory access. Along with this fix, this change also defers checking sim device ports pointer at a more appropriate place in the function call. Change-Id: I20c09edbd454c9d491060815dc73bae34aab6b08 Signed-off-by: Sandeep Gangadharaiah --- msm/dp/dp_debug.c | 17 +++++++-------- msm/dp/dp_mst_sim.c | 8 ++----- msm/dp/dp_mst_sim_helper.c | 43 ++++++++++++++++++++++---------------- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 2897d96e..1ee39a68 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -74,19 +74,18 @@ static int dp_debug_attach_sim_bridge(struct dp_debug_private *debug) { int ret; - if (debug->sim_bridge) - return 0; + if (!debug->sim_bridge) { + ret = dp_sim_create_bridge(debug->dev, &debug->sim_bridge); + if (ret) + return ret; - ret = dp_sim_create_bridge(debug->dev, &debug->sim_bridge); - if (ret) - return ret; + if (debug->sim_bridge->register_hpd) + debug->sim_bridge->register_hpd(debug->sim_bridge, + dp_debug_sim_hpd_cb, debug); + } dp_sim_update_port_num(debug->sim_bridge, 1); - if (debug->sim_bridge->register_hpd) - debug->sim_bridge->register_hpd(debug->sim_bridge, - dp_debug_sim_hpd_cb, debug); - return 0; } diff --git a/msm/dp/dp_mst_sim.c b/msm/dp/dp_mst_sim.c index 2e3eb403..1be4422e 100644 --- a/msm/dp/dp_mst_sim.c +++ b/msm/dp/dp_mst_sim.c @@ -342,10 +342,7 @@ int dp_sim_update_port_num(struct dp_aux_bridge *bridge, u32 port_num) return -EINVAL; sim_dev = to_dp_sim_dev(bridge); - DP_INFO("Update port count from %d to %d\n", port_num, sim_dev->port_num); - - if (sim_dev->port_num > port_num && sim_dev->ports) - sim_dev->port_num = port_num; + DP_INFO("Update port count from %d to %d\n", sim_dev->port_num, port_num); if (port_num > sim_dev->port_num) { ports = devm_kzalloc(sim_dev->dev, @@ -365,10 +362,9 @@ int dp_sim_update_port_num(struct dp_aux_bridge *bridge, u32 port_num) memcpy(&ports[i], &output_port, sizeof(*ports)); ports[i].peer_guid[0] = i; } - - sim_dev->port_num = port_num; } + sim_dev->port_num = port_num; rc = dp_mst_sim_update(sim_dev->bridge.mst_ctx, port_num, sim_dev->ports); if (rc) diff --git a/msm/dp/dp_mst_sim_helper.c b/msm/dp/dp_mst_sim_helper.c index 1657c975..505a2de9 100644 --- a/msm/dp/dp_mst_sim_helper.c +++ b/msm/dp/dp_mst_sim_helper.c @@ -1055,6 +1055,18 @@ static void dp_mst_sim_notify(struct dp_mst_sim_context *ctx, queue_work(ctx->wq, &work->base); } +static void dp_mst_sim_free_ports(struct dp_mst_sim_context *ctx) +{ + u32 i; + + for (i = 0; i < ctx->port_num; i++) + kfree(ctx->ports[i].edid); + + kfree(ctx->ports); + ctx->ports = NULL; + ctx->port_num = 0; +} + int dp_mst_sim_update(void *mst_sim_context, u32 port_num, struct dp_mst_sim_port *ports) { @@ -1064,7 +1076,7 @@ int dp_mst_sim_update(void *mst_sim_context, u32 port_num, u32 update_mask = 0; u32 i; - if (!ctx || port_num >= 15) + if (!ctx || port_num >= 15 || !ports) return -EINVAL; mutex_lock(&ctx->session_lock); @@ -1081,19 +1093,17 @@ int dp_mst_sim_update(void *mst_sim_context, u32 port_num, } } - for (i = 0; i < ctx->port_num; i++) - kfree(ctx->ports[i].edid); - kfree(ctx->ports); - ctx->port_num = 0; + dp_mst_sim_free_ports(ctx); - if (port_num) { - ctx->ports = kcalloc(port_num, sizeof(*ports), GFP_KERNEL); - if (!ctx->ports) { - rc = -ENOMEM; - goto fail; - } - ctx->port_num = port_num; + if (!port_num) + goto end; + + ctx->ports = kcalloc(port_num, sizeof(*ports), GFP_KERNEL); + if (!ctx->ports) { + rc = -ENOMEM; + goto fail; } + ctx->port_num = port_num; for (i = 0; i < port_num; i++) { ctx->ports[i] = ports[i]; @@ -1116,13 +1126,10 @@ int dp_mst_sim_update(void *mst_sim_context, u32 port_num, } fail: - if (rc) { - for (i = 0; i < ctx->port_num; i++) - kfree(ctx->ports[i].edid); - kfree(ctx->ports); - ctx->port_num = 0; - } + if (rc) + dp_mst_sim_free_ports(ctx); +end: mutex_unlock(&ctx->session_lock); if (update_mask) From edea1840b62b85df1649b7f9c3dfeacef286c959 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 6 Sep 2022 13:35:07 -0700 Subject: [PATCH 04/10] drm/msm/dpu: merge dpu_hw_intr_get_interrupt_statuses into dpu_hw_intr_dispatch_irqs There is little sense in reading interrupt statuses and right after that going after the array of statuses to dispatch them. Merge both loops into single function doing read and dispatch. Change-Id: I1259476549bcaf9f9f4e12591a7e182796e150dd Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Git-commit: 0abdba47dc1df708c365421d481734d3f7fecb01 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_core_irq.c | 10 +----- msm/sde/sde_hw_interrupts.c | 66 +++++++++++-------------------------- msm/sde/sde_hw_interrupts.h | 10 +----- 3 files changed, 22 insertions(+), 64 deletions(-) diff --git a/msm/sde/sde_core_irq.c b/msm/sde/sde_core_irq.c index fff3920f..cb175962 100644 --- a/msm/sde/sde_core_irq.c +++ b/msm/sde/sde_core_irq.c @@ -616,15 +616,6 @@ int sde_core_irq_domain_fini(struct sde_kms *sde_kms) irqreturn_t sde_core_irq(struct sde_kms *sde_kms) { - /* - * Read interrupt status from all sources. Interrupt status are - * stored within hw_intr. - * Function will also clear the interrupt status after reading. - * Individual interrupt status bit will only get stored if it - * is enabled. - */ - sde_kms->hw_intr->ops.get_interrupt_statuses(sde_kms->hw_intr); - /* * Dispatch to HW driver to handle interrupt lookup that is being * fired. When matching interrupt is located, HW driver will call to @@ -632,6 +623,7 @@ irqreturn_t sde_core_irq(struct sde_kms *sde_kms) * sde_core_irq_callback_handler will perform the registered function * callback, and do the interrupt status clearing once the registered * callback is finished. + * Function will also clear the interrupt status after reading. */ sde_kms->hw_intr->ops.dispatch_irqs( sde_kms->hw_intr, diff --git a/msm/sde/sde_hw_interrupts.c b/msm/sde/sde_hw_interrupts.c index ceac3e02..a3da29b5 100644 --- a/msm/sde/sde_hw_interrupts.c +++ b/msm/sde/sde_hw_interrupts.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ @@ -415,6 +415,7 @@ static void sde_hw_intr_dispatch_irq(struct sde_hw_intr *intr, int start_idx; int end_idx; u32 irq_status; + u32 enable_mask; unsigned long irq_flags; if (!intr) @@ -427,8 +428,6 @@ static void sde_hw_intr_dispatch_irq(struct sde_hw_intr *intr, */ spin_lock_irqsave(&intr->irq_lock, irq_flags); for (reg_idx = 0; reg_idx < intr->sde_irq_size; reg_idx++) { - irq_status = intr->save_irq_status[reg_idx]; - /* * Each Interrupt register has dynamic range of indexes, * initialized during hw_intr_init when sde_irq_tbl is created. @@ -440,6 +439,20 @@ static void sde_hw_intr_dispatch_irq(struct sde_hw_intr *intr, end_idx > intr->sde_irq_map_size) continue; + /* Read interrupt status */ + irq_status = SDE_REG_READ(&intr->hw, intr->sde_irq_tbl[reg_idx].status_off); + + /* Read enable mask */ + enable_mask = SDE_REG_READ(&intr->hw, intr->sde_irq_tbl[reg_idx].en_off); + + /* and clear the interrupt */ + if (irq_status) + SDE_REG_WRITE(&intr->hw, intr->sde_irq_tbl[reg_idx].clr_off, + irq_status); + + /* Finally update IRQ status based on enable mask */ + irq_status &= enable_mask; + /* * Search through matching intr status from irq map. * start_idx and end_idx defined the search range in @@ -474,6 +487,10 @@ static void sde_hw_intr_dispatch_irq(struct sde_hw_intr *intr, ~intr->sde_irq_map[irq_idx].irq_mask; } } + + /* ensure register writes go through */ + wmb(); + spin_unlock_irqrestore(&intr->irq_lock, irq_flags); } @@ -620,40 +637,6 @@ static int sde_hw_intr_get_interrupt_sources(struct sde_hw_intr *intr, return 0; } -static void sde_hw_intr_get_interrupt_statuses(struct sde_hw_intr *intr) -{ - int i; - u32 enable_mask; - unsigned long irq_flags; - - if (!intr) - return; - - spin_lock_irqsave(&intr->irq_lock, irq_flags); - for (i = 0; i < intr->sde_irq_size; i++) { - /* Read interrupt status */ - intr->save_irq_status[i] = SDE_REG_READ(&intr->hw, - intr->sde_irq_tbl[i].status_off); - - /* Read enable mask */ - enable_mask = SDE_REG_READ(&intr->hw, - intr->sde_irq_tbl[i].en_off); - - /* and clear the interrupt */ - if (intr->save_irq_status[i]) - SDE_REG_WRITE(&intr->hw, intr->sde_irq_tbl[i].clr_off, - intr->save_irq_status[i]); - - /* Finally update IRQ status based on enable mask */ - intr->save_irq_status[i] &= enable_mask; - } - - /* ensure register writes go through */ - wmb(); - - spin_unlock_irqrestore(&intr->irq_lock, irq_flags); -} - static void sde_hw_intr_clear_intr_status_nolock(struct sde_hw_intr *intr, int irq_idx) { @@ -856,7 +839,6 @@ static void __setup_intr_ops(struct sde_hw_intr_ops *ops) ops->clear_all_irqs = sde_hw_intr_clear_irqs; ops->disable_all_irqs = sde_hw_intr_disable_irqs; ops->get_interrupt_sources = sde_hw_intr_get_interrupt_sources; - ops->get_interrupt_statuses = sde_hw_intr_get_interrupt_statuses; ops->clear_interrupt_status = sde_hw_intr_clear_interrupt_status; ops->clear_intr_status_nolock = sde_hw_intr_clear_intr_status_nolock; ops->get_interrupt_status = sde_hw_intr_get_interrupt_status; @@ -881,7 +863,6 @@ void sde_hw_intr_destroy(struct sde_hw_intr *intr) kfree(intr->sde_irq_tbl); kfree(intr->sde_irq_map); kfree(intr->cache_irq_mask); - kfree(intr->save_irq_status); kfree(intr); } } @@ -1151,13 +1132,6 @@ struct sde_hw_intr *sde_hw_intr_init(void __iomem *addr, goto exit; } - intr->save_irq_status = kcalloc(intr->sde_irq_size, - sizeof(*intr->save_irq_status), GFP_KERNEL); - if (intr->save_irq_status == NULL) { - ret = -ENOMEM; - goto exit; - } - spin_lock_init(&intr->irq_lock); exit: diff --git a/msm/sde/sde_hw_interrupts.h b/msm/sde/sde_hw_interrupts.h index aab65a51..71a40497 100644 --- a/msm/sde/sde_hw_interrupts.h +++ b/msm/sde/sde_hw_interrupts.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2019, 2021, The Linux Foundation. All rights reserved. */ @@ -170,14 +170,6 @@ struct sde_hw_intr_ops { void (*cbfunc)(void *arg, int irq_idx), void *arg); - /** - * get_interrupt_statuses - Gets and store value from all interrupt - * status registers that are currently fired. - * @intr: HW interrupt handle - */ - void (*get_interrupt_statuses)( - struct sde_hw_intr *intr); - /** * clear_interrupt_status - Clears HW interrupt status based on given * lookup IRQ index From 7f364193c1a998896c43d32d13df7a6a52e94fa7 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Tue, 6 Sep 2022 11:06:02 -0700 Subject: [PATCH 05/10] disp: msm: sde: expose cdm block count through connector Expose the number of cdm blocks available through the connector capabilities. Add CDM to the topology_control table, so usermode can use the property to reserve the CDM block during modeset. Additionally, fix a error code return during CDM block reservation failure in sde resource manager. Change-Id: Ib42ca4e8614076a8e5df77d8abc77a9e73674390 Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_connector.c | 1 + msm/sde/sde_rm.c | 6 +++++- msm/sde/sde_rm.h | 3 +++ msm/sde/sde_wb.c | 3 +++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 9a250086..22ee4e1f 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -57,6 +57,7 @@ static const struct drm_prop_enum_list e_topology_control[] = { {SDE_RM_TOPCTL_DSPP, "dspp"}, {SDE_RM_TOPCTL_DS, "ds"}, {SDE_RM_TOPCTL_DNSC_BLUR, "dnsc_blur"}, + {SDE_RM_TOPCTL_CDM, "cdm"}, }; static const struct drm_prop_enum_list e_power_mode[] = { {SDE_MODE_DPMS_ON, "ON"}, diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index fc2e2048..cbe5d3ea 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -37,6 +37,7 @@ #define RM_RQ_CWB(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_CWB)) #define RM_RQ_DCWB(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DCWB)) #define RM_RQ_DNSC_BLUR(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DNSC_BLUR)) +#define RM_RQ_CDM(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_CDM)) #define RM_IS_TOPOLOGY_MATCH(t, r) ((t).num_lm == (r).num_lm && \ (t).num_comp_enc == (r).num_enc && \ (t).num_intf == (r).num_intf && \ @@ -1882,8 +1883,11 @@ static int _sde_rm_reserve_intf_or_wb(struct sde_rm *rm, struct sde_rm_rsvp *rsv } /* Expected only one intf or wb will request cdm */ - if (hw_res->needs_cdm) + if (hw_res->needs_cdm || RM_RQ_CDM(reqs)) { ret = _sde_rm_reserve_cdm(rm, rsvp, id, type); + if (ret) + return ret; + } if (RM_RQ_DNSC_BLUR(reqs)) ret = _sde_rm_reserve_dnsc_blur(rm, rsvp, id, type); diff --git a/msm/sde/sde_rm.h b/msm/sde/sde_rm.h index dc39a955..7be8816a 100644 --- a/msm/sde/sde_rm.h +++ b/msm/sde/sde_rm.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-2021, The Linux Foundation. All rights reserved. */ @@ -114,6 +115,7 @@ enum sde_rm_topology_group { * @SDE_RM_TOPCTL_CWB : Require layer mixers with CWB capabilities * @SDE_RM_TOPCTL_DCWB : Require layer mixers with DCWB capabilities * @SDE_RM_TOPCTL_DNSC_BLUR : Require writeback with downscale blur capabilities + * @SDE_RM_TOPCTL_CDM : Require writeback with CDM capabilities */ enum sde_rm_topology_control { SDE_RM_TOPCTL_RESERVE_LOCK, @@ -123,6 +125,7 @@ enum sde_rm_topology_control { SDE_RM_TOPCTL_CWB, SDE_RM_TOPCTL_DCWB, SDE_RM_TOPCTL_DNSC_BLUR, + SDE_RM_TOPCTL_CDM, }; /** diff --git a/msm/sde/sde_wb.c b/msm/sde/sde_wb.c index a8f41fe7..27a1537b 100644 --- a/msm/sde/sde_wb.c +++ b/msm/sde/sde_wb.c @@ -545,6 +545,9 @@ int sde_wb_connector_set_info_blob(struct drm_connector *connector, sde_kms_info_add_keyint(info, "has_cwb_dither", test_bit(SDE_FEATURE_CWB_DITHER, catalog->features)); + if (catalog->cdm_count) + sde_kms_info_add_keyint(info, "cdm_count", catalog->cdm_count); + if (catalog->dnsc_blur_count && catalog->dnsc_blur_filters) { sde_kms_info_add_keyint(info, "dnsc_blur_count", catalog->dnsc_blur_count); From 62e5c8ddfcddc44363db54f0321cdc454ec786b8 Mon Sep 17 00:00:00 2001 From: Gopikrishnaiah Anandan Date: Thu, 8 Sep 2022 17:09:21 -0700 Subject: [PATCH 06/10] drm: msm: re-enable driver disabled color features When encoder is disabled, demura is disabled since pipes are disabled internally. Change marks the features which were active and disabled by driver as dirty so that it can be applied in the next commit. Change-Id: I805d17d673a8ff41f9bdb18ba7f2fd185b5ccb5a Signed-off-by: Gopikrishnaiah Anandan --- msm/sde/sde_color_processing.c | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 42181db2..4cb02f3d 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -112,6 +112,8 @@ static int _sde_cp_crtc_cache_property(struct drm_crtc *crtc, struct drm_property *property, uint64_t val); +static void _sde_cp_mark_active_dirty_internal(struct sde_crtc *crtc); + #define setup_dspp_prop_install_funcs(func) \ do { \ func[SDE_DSPP_PCC] = _dspp_pcc_install_property; \ @@ -2824,6 +2826,7 @@ void sde_cp_disable_features(struct drm_crtc *crtc) mutex_unlock(&sde_crtc->crtc_cp_lock); } + _sde_cp_mark_active_dirty_internal(sde_crtc); } void sde_cp_crtc_clear(struct drm_crtc *crtc) @@ -4751,6 +4754,19 @@ static bool _sde_cp_feature_in_activelist(u32 feature, struct list_head *list) return false; } +/* this func needs to be called within crtc_cp_lock mutex */ +static struct sde_cp_node *_sde_cp_feature_getnode_activelist(u32 feature, struct list_head *list) +{ + struct sde_cp_node *node = NULL; + + list_for_each_entry(node, list, cp_active_list) { + if (feature == node->feature) + return node; + } + + return NULL; +} + void sde_cp_crtc_vm_primary_handoff(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = NULL; @@ -5012,3 +5028,26 @@ void sde_cp_set_skip_blend_plane_info(struct drm_crtc *drm_crtc, mutex_unlock(&crtc->crtc_cp_lock); } +void _sde_cp_mark_active_dirty_internal(struct sde_crtc *crtc) +{ + struct sde_cp_node *prop_node; + u32 i; + enum sde_cp_crtc_features features[] = { + SDE_CP_CRTC_DSPP_DEMURA_INIT, + }; + + mutex_lock(&crtc->crtc_cp_lock); + + for (i = 0; i < ARRAY_SIZE(features); i++) { + if (_sde_cp_feature_in_dirtylist(features[i], + &crtc->cp_dirty_list)) + continue; + prop_node = _sde_cp_feature_getnode_activelist(features[i], + &crtc->cp_active_list); + if (prop_node) { + _sde_cp_update_list(prop_node, crtc, true); + list_del_init(&prop_node->cp_active_list); + } + } + mutex_unlock(&crtc->crtc_cp_lock); +} From 83883bd16d915b93c562752ab508720d64556572 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Thu, 1 Sep 2022 21:40:58 -0700 Subject: [PATCH 07/10] disp: msm: sde: move some frame_events from crtc commit to event thread Move frame data stats collection/notification during frame-done and retire fence sysfs notification to event thread. This will free up some interrupt time. Change-Id: I2648ac4287ce8712e9a059edd408a59753aa6d32 Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_crtc.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index fa50d111..1ebf361f 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -2823,24 +2823,11 @@ static void sde_crtc_frame_event_cb(void *data, u32 event, ktime_t ts) spin_unlock_irqrestore(&sde_crtc->fevent_spin_lock, flags); if (!fevent) { - SDE_ERROR("crtc%d event %d overflow\n", - crtc->base.id, event); + SDE_ERROR("crtc%d event %d overflow\n", DRMID(crtc), event); SDE_EVT32(DRMID(crtc), event); return; } - /* log and clear plane ubwc errors if any */ - if (event & (SDE_ENCODER_FRAME_EVENT_ERROR - | SDE_ENCODER_FRAME_EVENT_PANEL_DEAD - | SDE_ENCODER_FRAME_EVENT_DONE)) - sde_crtc_get_frame_data(crtc); - - if ((event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE) && - (sde_crtc && sde_crtc->retire_frame_event_sf)) { - sde_crtc->retire_frame_event_time = ktime_get(); - sysfs_notify_dirent(sde_crtc->retire_frame_event_sf); - } - fevent->event = event; fevent->ts = ts; fevent->crtc = crtc; @@ -3103,6 +3090,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) struct sde_kms *sde_kms; unsigned long flags; bool in_clone_mode = false; + int ret; if (!work) { SDE_ERROR("invalid work handle\n"); @@ -3137,6 +3125,17 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) if (!in_clone_mode && (fevent->event & (SDE_ENCODER_FRAME_EVENT_ERROR | SDE_ENCODER_FRAME_EVENT_PANEL_DEAD | SDE_ENCODER_FRAME_EVENT_DONE))) { + + ret = pm_runtime_resume_and_get(crtc->dev->dev); + if (ret < 0) { + SDE_ERROR("failed to enable power resource %d\n", ret); + SDE_EVT32(ret, SDE_EVTLOG_ERROR); + } else { + /* log and clear plane ubwc errors if any */ + sde_crtc_get_frame_data(crtc); + pm_runtime_put_sync(crtc->dev->dev); + } + if (atomic_read(&sde_crtc->frame_pending) < 1) { /* this should not happen */ SDE_ERROR("crtc%d ts:%lld invalid frame_pending:%d\n", @@ -3168,11 +3167,17 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) SDE_ATRACE_END("signal_release_fence"); } - if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE) + if (fevent->event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE) { + if (sde_crtc->retire_frame_event_sf) { + sde_crtc->retire_frame_event_time = fevent->ts; + sysfs_notify_dirent(sde_crtc->retire_frame_event_sf); + } + /* this api should be called without spin_lock */ _sde_crtc_retire_event(fevent->connector, fevent->ts, (fevent->event & SDE_ENCODER_FRAME_EVENT_ERROR) ? SDE_FENCE_SIGNAL_ERROR : SDE_FENCE_SIGNAL); + } if (fevent->event & SDE_ENCODER_FRAME_EVENT_PANEL_DEAD) SDE_ERROR("crtc%d ts:%lld received panel dead event\n", From b6466ca7f597396cd2ecb3623d059435dfb0e4c6 Mon Sep 17 00:00:00 2001 From: Sandeep Gangadharaiah Date: Tue, 13 Sep 2022 16:08:09 -0700 Subject: [PATCH 08/10] disp: msm: dp: disable aux switch at the start of the disconnect path Currently the aux switch is disabled at the end of the disconnect path which would include the wait time upto 5 secs becauase of usermode cleanup. However, the PMIC module is expecting the aux switch to be disabled within 400 msec after the disconnect is notified. If not, this would trigger an LPD failure. This change moves aux switch disable further up in the disconnect path, before waiting on completion of usermode cleanup. Change-Id: I42e0608f06127729a78de11631d16d0a3ca0d2b4 Signed-off-by: Sandeep Gangadharaiah --- msm/dp/dp_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index fe073d4b..6b61ea30 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1646,6 +1646,10 @@ static void dp_display_disconnect_sync(struct dp_display_private *dp) cancel_work_sync(&dp->attention_work); flush_workqueue(dp->wq); + if (!dp->debug->sim_mode && !dp->no_aux_switch + && !dp->parser->gpio_aux_switch) + dp->aux->aux_switch(dp->aux, false, ORIENTATION_NONE); + /* * Delay the teardown of the mainlink for better interop experience. * It is possible that certain sinks can issue an HPD high immediately @@ -1703,10 +1707,6 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) dp_display_state_remove(DP_STATE_CONFIGURED); mutex_unlock(&dp->session_lock); - if (!dp->debug->sim_mode && !dp->no_aux_switch - && !dp->parser->gpio_aux_switch) - dp->aux->aux_switch(dp->aux, false, ORIENTATION_NONE); - SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); end: return rc; From c78000540c15b9080d55ae2b38fd4c39c5666279 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Wed, 14 Sep 2022 14:07:53 -0700 Subject: [PATCH 09/10] disp: msm: sde: fix crtc count based on layer mixer Fix the max crtc count based on the number of real layer mixers available. Usermode can use the crtc count to derive the number of layer mixers. This will be used in usermode to check if a new DP/IWE/WB session can be supported by the HW, based on the existing displays at that point. This will avoid atomic_check validation failures in driver. Change-Id: I63b033604ac549fc01bccef2a9320e0befab5926 Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_kms.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index bea70fb8..1d243737 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2132,7 +2132,7 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) u32 sspp_id[MAX_PLANES]; u32 master_plane_id[MAX_PLANES]; - u32 num_virt_planes = 0; + u32 num_virt_planes = 0, dummy_mixer_count = 0; if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev) { SDE_ERROR("invalid sde_kms\n"); @@ -2153,7 +2153,11 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) if (!_sde_kms_get_displays(sde_kms)) (void)_sde_kms_setup_displays(dev, priv, sde_kms); - max_crtc_count = min(catalog->mixer_count, priv->num_encoders); + for (i = 0; i < catalog->mixer_count; i++) + if (catalog->mixer[i].dummy_mixer) + dummy_mixer_count++; + + max_crtc_count = catalog->mixer_count - dummy_mixer_count; /* Create the planes */ for (i = 0; i < catalog->sspp_count; i++) { From 00bc3bc06a54308ae92a0f8e623103b342ab9485 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Tue, 13 Sep 2022 12:17:58 -0700 Subject: [PATCH 10/10] disp: msm: sde: avoid connector remove in dual display recovery Add changes to get drm object reference for connector and remove out fb in dual display recovery case. Change-Id: I1fd0c4818575b3f532d51ad41285031e8320c5fe Signed-off-by: Raviteja Tamatam --- msm/sde/sde_kms.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index bea70fb8..cab71ff2 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2579,6 +2579,38 @@ static void _sde_kms_plane_force_remove(struct drm_plane *plane, drm_atomic_set_fb_for_plane(plane_state, NULL); } +static int _sde_kms_connector_add_refcount(struct sde_kms *sde_kms, + struct drm_atomic_state *state) +{ + struct drm_device *dev = sde_kms->dev; + struct drm_connector *conn; + struct drm_connector_state *conn_state; + struct drm_connector_list_iter conn_iter; + struct sde_connector_state *c_state; + int ret = 0; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { + /* + * Acquire a connector reference to avoid removing + * connector in drm_release for splash and recovery cases. + */ + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + SDE_ERROR("error %d getting connector %d state\n", + ret, DRMID(conn)); + return ret; + } + c_state = to_sde_connector_state(conn_state); + if (c_state->out_fb) + drm_framebuffer_put(c_state->out_fb); + } + drm_connector_list_iter_end(&conn_iter); + + return ret; +} + static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, struct drm_atomic_state *state) { @@ -2611,6 +2643,8 @@ static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file, if (list_empty(&fbs)) { SDE_DEBUG("skip commit as no fb(s)\n"); + if (sde_kms->dsi_display_count == sde_kms->splash_data.num_splash_displays) + _sde_kms_connector_add_refcount(sde_kms, state); return 0; }