From f86e196de099d9d3165e638fb55bcc7150d15455 Mon Sep 17 00:00:00 2001 From: Sandeep Gangadharaiah Date: Tue, 9 Aug 2022 09:03:24 -0700 Subject: [PATCH 01/20] disp: msm: dp: remove disconnect call for downstream port status change During MST scenario, plugging out all the downstream monitors connected to the MST hub would trigger a disconnect handler which would cleanup display structure. This isn't required since MST hub is still connected and the display cleanup would be taken care during the actual MST hub disconnect. Also, handling the disconnect immediately on port status notification leaves the usermode in an invalid state where it assumes the display is still enabled and results in commit errors. Change-Id: Ia9a58fadd89bd05746da25f142b54b31e8567258 Signed-off-by: Sandeep Gangadharaiah --- msm/dp/dp_display.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 25205014..a7413b55 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1738,15 +1738,16 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) { SDE_EVT32_EXTERNAL(dp->state, DS_PORT_STATUS_CHANGED); - if (dp_display_is_sink_count_zero(dp)) { - dp_display_handle_disconnect(dp); - } else { - /* - * connect work should take care of sending - * the HPD notification. - */ - if (!dp->mst.mst_active) + if (!dp->mst.mst_active) { + if (dp_display_is_sink_count_zero(dp)) { + dp_display_handle_disconnect(dp); + } else { + /* + * connect work should take care of sending + * the HPD notification. + */ queue_work(dp->wq, &dp->connect_work); + } } goto mst_attention; From 046b2d1e356691bb875a355bf6a032b1c538a32f Mon Sep 17 00:00:00 2001 From: Alisha Thapaliya Date: Tue, 9 Aug 2022 12:03:11 -0700 Subject: [PATCH 02/20] disp: msm: sde: disable spr and demura for secondary panel in trusted vm When spr and demura init config function pointers are not null, then only enable those features. For secondary panel in dual display for trusted ui, these features will be disabled. Change-Id: Idcbc672d9da62664bdbaa9489dbfac9f6ab80ec1 Signed-off-by: Alisha Thapaliya --- msm/sde/sde_color_processing.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 1f7059ce..42181db2 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -896,13 +896,15 @@ static int _set_spr_init_feature(struct sde_hw_dspp *hw_dspp, { int ret = 0; - if (!sde_crtc || !hw_dspp || !hw_dspp->ops.setup_spr_init_config) { + if (!sde_crtc || !hw_dspp) { DRM_ERROR("invalid arguments\n"); ret = -EINVAL; } else { - hw_dspp->ops.setup_spr_init_config(hw_dspp, hw_cfg); - _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_SPR_PU, + if (hw_dspp->ops.setup_spr_init_config) { + hw_dspp->ops.setup_spr_init_config(hw_dspp, hw_cfg); + _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_SPR_PU, hw_cfg->payload != NULL); + } } return ret; @@ -914,12 +916,14 @@ static int _set_demura_feature(struct sde_hw_dspp *hw_dspp, { int ret = 0; - if (!hw_dspp || !hw_dspp->ops.setup_demura_cfg) { + if (!hw_dspp) { ret = -EINVAL; } else { - hw_dspp->ops.setup_demura_cfg(hw_dspp, hw_cfg); - _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_DEMURA_PU, + if (hw_dspp->ops.setup_demura_cfg) { + hw_dspp->ops.setup_demura_cfg(hw_dspp, hw_cfg); + _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_DEMURA_PU, hw_cfg->payload != NULL); + } } return ret; From e60e9026fca071e9f11f91606eb1ced919138320 Mon Sep 17 00:00:00 2001 From: Sandeep Gangadharaiah Date: Mon, 8 Aug 2022 14:27:38 -0700 Subject: [PATCH 03/20] disp: msm: dp: remove register call for regdump framework for DP domains Currently regdump framework for all the DP domains are registered during init. But, unlike other modules in DP each SWI module is controlled by its own clock and cannot be read without turning on the corresponding clocks. Trying to do so might lead to unexpected behavior. This change removes registering these nodes. Change-Id: Ib20d7212bde24f3858558009e1679661731d16df Signed-off-by: Sandeep Gangadharaiah --- msm/dp/dp_display.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 141bf9ba..c9468873 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -2240,37 +2240,6 @@ error: return rc; } -static void dp_display_dbg_reister(struct dp_display_private *dp) -{ - struct dp_parser *parser = dp->parser; - struct dss_io_data *io; - - io = &parser->get_io(parser, "dp_ahb")->io; - if (io) - sde_dbg_reg_register_base("dp_ahb", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_ahb"), SDE_DBG_DP); - - io = &parser->get_io(parser, "dp_aux")->io; - if (io) - sde_dbg_reg_register_base("dp_aux", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_aux"), SDE_DBG_DP); - - io = &parser->get_io(parser, "dp_link")->io; - if (io) - sde_dbg_reg_register_base("dp_link", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_link"), SDE_DBG_DP); - - io = &parser->get_io(parser, "dp_p0")->io; - if (io) - sde_dbg_reg_register_base("dp_p0", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_p0"), SDE_DBG_DP); - - io = &parser->get_io(parser, "hdcp_physical")->io; - if (io) - sde_dbg_reg_register_base("hdcp_physical", io->base, io->len, - msm_get_phys_addr(dp->pdev, "hdcp_physical"), SDE_DBG_DP); -} - static int dp_display_post_init(struct dp_display *dp_display) { int rc = 0; @@ -2293,8 +2262,6 @@ static int dp_display_post_init(struct dp_display *dp_display) if (rc) goto end; - dp_display_dbg_reister(dp); - dp_display->post_init = NULL; end: DP_DEBUG("%s\n", rc ? "failed" : "success"); From d65c12ca5a124754b87818847a8199444654e629 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Wed, 10 Aug 2022 11:33:26 -0700 Subject: [PATCH 04/20] disp: msm: sde: add check to avoid NULL WB output fb Change the debug message to error during the writeback encoder validate for wb output buffer. The output buffer can be NULL only during disable frame and all other frames need to have a valid output buffer. Change-Id: I4d6fecfeaf863e56fe25e17ab1200849003b3309 Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_encoder_phys_wb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index 44e713a5..1a180733 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -1024,8 +1024,8 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc, /* bypass check if commit with no framebuffer */ fb = sde_wb_connector_state_get_output_fb(conn_state); if (!fb) { - SDE_DEBUG("[enc:%d wb:%d] no out fb\n", DRMID(phys_enc->parent), WBID(wb_enc)); - return 0; + SDE_ERROR("[enc:%d wb:%d] no out fb\n", DRMID(phys_enc->parent), WBID(wb_enc)); + return -EINVAL; } fmt = sde_get_sde_format_ext(fb->format->format, fb->modifier); From 5196a85f67dfdda35d5b1efbfbce16e7346e1ca3 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Tue, 9 Aug 2022 22:58:36 -0700 Subject: [PATCH 05/20] disp: msm: sde: update hw configs on dnsc_blur disable Currently, dnsc_blur hardware block is not updated when the connector dnsc_blur property is set to NULL or when dnsc_blur_count is 0. Update the dnsc_blur hw block configs to avoid stale configs affecting the current frame. Signed-off-by: Veera Sundaram Sankaran Change-Id: If64dc5548b03edba401fb7f40edf3419dbe57ca3 --- msm/sde/sde_encoder_phys_wb.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index 44e713a5..cd7ce4b0 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -1430,23 +1430,32 @@ static void _sde_encoder_phys_wb_setup_dnsc_blur(struct sde_encoder_phys *phys_e int i; bool enable; - if (!sde_kms->catalog->dnsc_blur_count || !hw_dnsc_blur || !hw_pp - || !hw_dnsc_blur->ops.setup_dnsc_blur) + if (!sde_kms->catalog->dnsc_blur_count || !hw_pp) return; sde_conn = to_sde_connector(wb_dev->connector); sde_conn_state = to_sde_connector_state(wb_dev->connector->state); - if (sde_conn_state->dnsc_blur_count && !hw_dnsc_blur) { + if (sde_conn_state->dnsc_blur_count + && (!hw_dnsc_blur || !hw_dnsc_blur->ops.setup_dnsc_blur)) { SDE_ERROR("[enc:%d wb:%d] invalid config - dnsc_blur block not reserved\n", DRMID(phys_enc->parent), WBID(wb_enc)); - sde_kms->catalog->dnsc_blur_count = 0; return; } /* swap between 0 & 1 lut idx on each config change for gaussian lut */ sde_conn_state->dnsc_blur_lut = 1 - sde_conn_state->dnsc_blur_lut; + /* + * disable dnsc_blur case - safe to update the opmode as dynamic switching of + * dnsc_blur hw block between WBs are not supported currently. + */ + if (hw_dnsc_blur && !sde_conn_state->dnsc_blur_count) { + hw_dnsc_blur->ops.setup_dnsc_blur(hw_dnsc_blur, NULL, 0); + SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), SDE_EVTLOG_FUNC_CASE1); + return; + } + for (i = 0; i < sde_conn_state->dnsc_blur_count; i++) { cfg = &sde_conn_state->dnsc_blur_cfg[i]; From eab3fd66db309a5de1d321939a393ca20b9f9e1c Mon Sep 17 00:00:00 2001 From: Nilaan Gunabalachandran Date: Mon, 6 Jun 2022 10:48:57 -0400 Subject: [PATCH 06/20] disp: msm: sde: convert ubwc stats roi into blob property This change converts the ubwc stats roi into a blob property. This allows for the roi data structure to be passed into kernel. Change-Id: I4b30dcc16bcbd152428861444ff321add860942f Signed-off-by: Nilaan Gunabalachandran --- msm/msm_drv.h | 2 +- msm/sde/sde_crtc.c | 2 +- msm/sde/sde_plane.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 39f35fa0..2a7c0010 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -117,6 +117,7 @@ enum msm_mdp_plane_property { PLANE_PROP_DMA_GC, PLANE_PROP_FP16_GC, PLANE_PROP_FP16_CSC, + PLANE_PROP_UBWC_STATS_ROI, /* # of blob properties */ PLANE_PROP_BLOBCOUNT, @@ -140,7 +141,6 @@ enum msm_mdp_plane_property { PLANE_PROP_INVERSE_PMA, PLANE_PROP_FP16_IGC, PLANE_PROP_FP16_UNMULT, - PLANE_PROP_UBWC_STATS_ROI, /* enum/bitmask properties */ PLANE_PROP_BLEND_OP, diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index ae47ea51..e6620476 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -2764,7 +2764,7 @@ void sde_crtc_get_frame_data(struct drm_crtc *crtc) /* Collect plane specific data */ drm_for_each_plane_mask(plane, crtc->dev, sde_crtc->plane_mask_old) - sde_plane_get_frame_data(plane, &data->plane_frame_data[i]); + sde_plane_get_frame_data(plane, &data->plane_frame_data[i++]); if (frame_data->cnt) _sde_crtc_frame_data_notify(crtc, data); diff --git a/msm/sde/sde_plane.c b/msm/sde/sde_plane.c index cb7ddb50..09f90d6f 100644 --- a/msm/sde/sde_plane.c +++ b/msm/sde/sde_plane.c @@ -3992,8 +3992,8 @@ static void _sde_plane_install_properties(struct drm_plane *plane, PLANE_PROP_FB_TRANSLATION_MODE); if (psde->pipe_hw->ops.set_ubwc_stats_roi) - msm_property_install_range(&psde->property_info, "ubwc_stats_roi", - 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_UBWC_STATS_ROI); + msm_property_install_volatile_range(&psde->property_info, "ubwc_stats_roi", + 0, 0, ~0, 0, PLANE_PROP_UBWC_STATS_ROI); vfree(info); } From 3d82106dee72db98eeb6b473a4aec4091f0a5744 Mon Sep 17 00:00:00 2001 From: Vara Reddy Date: Thu, 11 Aug 2022 14:25:43 -0700 Subject: [PATCH 07/20] disp: msm: dsi: increase cmd dma timeout to 1200 milliseconds Change increases cmd dma timeout to 1200 milliseconds from 200 milliseconds. There are video mode panels which can support one frame per second, if pixel data transfer is active, then our command transfer timeout should be atleast 1000 msec. Change-Id: I3e8269febe3ed6e55ac9381a8de35e7d19fa3160 Signed-off-by: Vara Reddy --- msm/dsi/dsi_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 120f15fc..6a76189b 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -25,7 +25,7 @@ #define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL" -#define DSI_CTRL_TX_TO_MS 200 +#define DSI_CTRL_TX_TO_MS 1200 #define TO_ON_OFF(x) ((x) ? "ON" : "OFF") From cf264d1a939dbd9514e1ba6ea55c5a92d1dc8cc0 Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Thu, 21 Jul 2022 14:34:28 -0700 Subject: [PATCH 08/20] disp: msm: dsi: reorder various resets of DSI PHY DSI PHY has various resets defined to reset analog, PLL and digital portions. In current sequence, these resets happen after PLL is locked which can result in introduction of jitter on PHY lanes.Reordering these resets to happen before PLL is programmed to have intended clean start of DSI PHY. Change-Id: I4eb5c05ea0e6015a5447728b2845b49817411c50 Signed-off-by: Shashank Babu Chinta Venkata --- msm/dsi/dsi_pll_4nm.c | 56 +++++++++++++++++++++++++++++++++---------- msm/dsi/dsi_pll_4nm.h | 1 + 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/msm/dsi/dsi_pll_4nm.c b/msm/dsi/dsi_pll_4nm.c index adcf0a12..80a94f2b 100644 --- a/msm/dsi/dsi_pll_4nm.c +++ b/msm/dsi/dsi_pll_4nm.c @@ -597,11 +597,11 @@ static void dsi_pll_enable_global_clk(struct dsi_pll_resource *rsc) DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_CLK_CFG1, (data | BIT(5) | BIT(4))); } -static void dsi_pll_phy_dig_reset(struct dsi_pll_resource *rsc) +static void dsi_pll_phy_analog_reset(struct dsi_pll_resource *rsc) { /* - * Reset the PHY digital domain. This would be needed when - * coming out of a CX or analog rail power collapse while + * Reset the PHY analog domain. This would be needed when + * coming out of a 0p9 power collapse while * ensuring that the pads maintain LP00 or LP11 state */ DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_GLBL_DIGTOP_SPARE4, BIT(0)); @@ -1391,15 +1391,6 @@ static int dsi_pll_4nm_enable(struct dsi_pll_resource *rsc) goto error; } - /* - * assert power on reset for PHY digital in case the PLL is - * enabled after CX of analog domain power collapse. This needs - * to be done before enabling the global clk. - */ - dsi_pll_phy_dig_reset(rsc); - if (rsc->slave) - dsi_pll_phy_dig_reset(rsc->slave); - dsi_pll_enable_global_clk(rsc); if (rsc->slave) dsi_pll_enable_global_clk(rsc->slave); @@ -1436,12 +1427,53 @@ static int dsi_pll_4nm_disable(struct dsi_pll_resource *rsc) return rc; } +void dsi_pll_assert_pll_reset(struct dsi_pll_resource *rsc) +{ + u32 data = 0; + + DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_1, data | BIT(7)); + + /* Ensure Assert is through */ + wmb(); + + DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_1, data & ~BIT(7)); + + /* Ensure deassert is through */ + wmb(); +} + +void dsi_pll_4nm_trigger_resets_pre_enable(struct dsi_pll_resource *rsc) +{ + /* + * Assert power on reset on DSI PHY Analog immeditately + * after 0P9 resume to make sure PHY starts in a + * clean state + */ + dsi_pll_phy_analog_reset(rsc); + if (rsc->slave) + dsi_pll_phy_analog_reset(rsc->slave); + + /* + * Trigger PLL reset as well to clear out any jitter + * introduced as result of 0p9 collapse + */ + dsi_pll_assert_pll_reset(rsc); + if (rsc->slave) + dsi_pll_assert_pll_reset(rsc->slave); +} + int dsi_pll_4nm_configure(void *pll, bool commit) { int rc = 0; struct dsi_pll_resource *rsc = (struct dsi_pll_resource *)pll; + /* These resets are needed for resetting Analog and PLL portions + * of DSI PHY before PLL is enabled and locked + */ + if (commit) + dsi_pll_4nm_trigger_resets_pre_enable(rsc); + dsi_pll_config_slave(rsc); /* PLL power needs to be enabled before accessing PLL registers */ diff --git a/msm/dsi/dsi_pll_4nm.h b/msm/dsi/dsi_pll_4nm.h index 052a2808..80056cd5 100644 --- a/msm/dsi/dsi_pll_4nm.h +++ b/msm/dsi/dsi_pll_4nm.h @@ -167,6 +167,7 @@ #define PHY_CMN_GLBL_CTRL 0x018 #define PHY_CMN_RBUF_CTRL 0x01C #define PHY_CMN_CTRL_0 0x024 +#define PHY_CMN_CTRL_1 0x028 #define PHY_CMN_CTRL_2 0x02C #define PHY_CMN_CTRL_3 0x030 #define PHY_CMN_PLL_CNTRL 0x03C From d3d31ac4185a3dbb26bd4d629452127564fbf2a3 Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Fri, 22 Jul 2022 15:54:42 -0700 Subject: [PATCH 09/20] disp: msm: dsi: avoid DSI PHY shutdown during idle Avoid shutting down DSI PHY and lanes before entering into idle collapse. Change-Id: I62fb40c2398e544b08b8cb8788ac2dc1143a82ce Signed-off-by: Shashank Babu Chinta Venkata --- msm/dsi/dsi_catalog.h | 3 ++- msm/dsi/dsi_phy.c | 5 +---- msm/dsi/dsi_phy_hw.h | 4 +++- msm/dsi/dsi_phy_hw_v5_0.c | 15 ++++++++++++++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index a364f36e..83f377de 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -312,7 +312,8 @@ void dsi_phy_hw_v5_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, int dsi_phy_hw_v5_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, u32 *dst, u32 size); -void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy); +void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg); void dsi_ctrl_hw_22_configure_cmddma_window(struct dsi_ctrl_hw *ctrl, struct dsi_ctrl_cmd_dma_info *cmd, u32 line_no, u32 window); diff --git a/msm/dsi/dsi_phy.c b/msm/dsi/dsi_phy.c index 00448bd2..6f7e1d2f 100644 --- a/msm/dsi/dsi_phy.c +++ b/msm/dsi/dsi_phy.c @@ -1167,11 +1167,8 @@ int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable) } else { phy->dsi_phy_state = DSI_PHY_ENGINE_OFF; - if (phy->hw.ops.disable) - phy->hw.ops.disable(&phy->hw, &phy->cfg); - if (phy->hw.ops.phy_idle_off) - phy->hw.ops.phy_idle_off(&phy->hw); + phy->hw.ops.phy_idle_off(&phy->hw, &phy->cfg); } mutex_unlock(&phy->phy_lock); diff --git a/msm/dsi/dsi_phy_hw.h b/msm/dsi/dsi_phy_hw.h index c747b294..d74e2ad7 100644 --- a/msm/dsi/dsi_phy_hw.h +++ b/msm/dsi/dsi_phy_hw.h @@ -291,8 +291,10 @@ struct dsi_phy_hw_ops { /** * phy_idle_off() - Disable PHY hardware when exiting idle screen * @phy: Pointer to DSI PHY hardware object. + * @cfg: Per lane configurations for timing, strength and lane + * configurations. */ - void (*phy_idle_off)(struct dsi_phy_hw *phy); + void (*phy_idle_off)(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg); /** * calculate_timing_params() - calculates timing parameters. diff --git a/msm/dsi/dsi_phy_hw_v5_0.c b/msm/dsi/dsi_phy_hw_v5_0.c index 7562d7b4..698e82c1 100644 --- a/msm/dsi/dsi_phy_hw_v5_0.c +++ b/msm/dsi/dsi_phy_hw_v5_0.c @@ -880,10 +880,23 @@ void dsi_phy_hw_v5_0_set_continuous_clk(struct dsi_phy_hw *phy, bool enable) wmb(); /* make sure request is set */ } -void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy) +void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg) { + if (dsi_phy_hw_v5_0_is_pll_on(phy)) + DSI_PHY_WARN(phy, "Turning OFF PHY while PLL is on\n"); + /* enable clamping of PADS */ DSI_W32(phy, DSIPHY_CMN_CTRL_4, 0x1); DSI_W32(phy, DSIPHY_CMN_CTRL_3, 0x0); wmb(); + + dsi_phy_hw_v5_0_config_lpcdrx(phy, cfg, false); + + /* Turn off REFGEN Vote */ + DSI_W32(phy, DSIPHY_CMN_GLBL_DIGTOP_SPARE10, 0x0); + /* make sure request is set */ + wmb(); + /* Delay to ensure HW removes vote*/ + udelay(2); } From 3550ca8f9f11f2ca00e822b3ff3bc70f0e7ad79e Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Tue, 16 Aug 2022 12:13:40 -0700 Subject: [PATCH 10/20] disp: msm: sde: add crtc width restriction when 3d-merge is enabled Add validation during crtc_atomic_check to have crtc width as multiple of 4 when dualpipe 3d-mux is enabled and multiple of 8 when quadpipe 3d-mux is enabled. This ensures each layer mixer is having an even width. Change-Id: I5dc173c1b0349430a8e12a7b1c9440c7854e7ecd Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_connector.h | 27 +++++++++++++++++++++++---- msm/sde/sde_crtc.c | 19 +++++++++++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 786f9464..d5d8c91c 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -1217,7 +1217,22 @@ static inline int sde_connector_state_get_compression_info( return 0; } -static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector *conn) +static inline bool sde_connector_is_quadpipe_3d_merge_enabled(struct drm_connector *conn) +{ + enum sde_rm_topology_name topology; + + if (!conn) + return false; + + topology = sde_connector_get_topology_name(conn); + if ((topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE) + || (topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE_DSC)) + return true; + + return false; +} + +static inline bool sde_connector_is_dualpipe_3d_merge_enabled(struct drm_connector *conn) { enum sde_rm_topology_name topology; @@ -1227,14 +1242,18 @@ static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector *conn) topology = sde_connector_get_topology_name(conn); if ((topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) - || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC) - || (topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE) - || (topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE_DSC)) + || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC)) return true; return false; } +static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector *conn) +{ + return sde_connector_is_dualpipe_3d_merge_enabled(conn) + || sde_connector_is_quadpipe_3d_merge_enabled(conn); +} + /** * sde_connector_set_msm_mode - set msm_mode for connector state * @conn_state: Pointer to drm connector state structure diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index e6620476..d476bd4b 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1364,6 +1364,8 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, u32 crtc_width, crtc_height, mixer_width, mixer_height; struct drm_display_mode *adj_mode; int rc = 0, lm_idx, i; + struct drm_connector *conn; + struct drm_connector_state *conn_state; if (!crtc || !state) return -EINVAL; @@ -1379,14 +1381,27 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, sde_crtc_get_resolution(crtc, state, adj_mode, &crtc_width, &crtc_height); sde_crtc_get_mixer_resolution(crtc, state, adj_mode, &mixer_width, &mixer_height); /* check cumulative mixer w/h is equal full crtc w/h */ - if (sde_crtc->num_mixers - && (((mixer_width * sde_crtc->num_mixers) != crtc_width) + if (sde_crtc->num_mixers && (((mixer_width * sde_crtc->num_mixers) != crtc_width) || (mixer_height != crtc_height))) { SDE_ERROR("%s: invalid w/h crtc:%d,%d, mixer:%d,%d, num_mixers:%d\n", sde_crtc->name, crtc_width, crtc_height, mixer_width, mixer_height, sde_crtc->num_mixers); rc = -EINVAL; goto end; + } else if (state->state) { + for_each_new_connector_in_state(state->state, conn, conn_state, i) { + if (conn_state && (conn_state->crtc == crtc) + && ((sde_connector_is_dualpipe_3d_merge_enabled(conn) + && (crtc_width % 4)) + || (sde_connector_is_quadpipe_3d_merge_enabled(conn) + && (crtc_width % 8)))) { + SDE_ERROR( + "%s: invalid 3d-merge_w - mixer_w:%d, crtc_w:%d, num_mixers:%d\n", + sde_crtc->name, mixer_width, + crtc_width, sde_crtc->num_mixers); + return -EINVAL; + } + } } /* From 8a4d70c9ca4ed5f072fc1fc563066487e2a2af81 Mon Sep 17 00:00:00 2001 From: Amine Najahi Date: Tue, 16 Aug 2022 23:11:57 -0400 Subject: [PATCH 11/20] disp: msm: sde: bound event log traversal to allocated memory in coredump Currently, driver is determining the amount of memory to allocate based on the event log object indexes (first, last). The last index can change if there is additional logging done during the coredump phase and potentially cause an out-of-bound memory access during buffer traversal. This change restrict the event log object traversal to a maximum of the output buffer size. Change-Id: I91e5734362d2d7a796129fce85e27611bab2245f Signed-off-by: Amine Najahi --- msm/sde_dbg.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/msm/sde_dbg.c b/msm/sde_dbg.c index 2eacf1f8..7aef13e9 100644 --- a/msm/sde_dbg.c +++ b/msm/sde_dbg.c @@ -1191,7 +1191,6 @@ void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) char buf[SDE_EVTLOG_BUF_MAX]; bool update_last_entry = true; u32 in_log, in_mem, in_dump; - u32 log_size = 0; char *dump_addr = NULL; int i; @@ -1202,21 +1201,22 @@ void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) in_mem = evtlog->dump_mode & SDE_DBG_DUMP_IN_MEM; in_dump = evtlog->dump_mode & SDE_DBG_DUMP_IN_COREDUMP; - log_size = sde_evtlog_count(evtlog); - if (!log_size) - return; - if (!evtlog->dumped_evtlog) { - if (in_mem) - log_size = SDE_EVTLOG_ENTRY; - evtlog->dumped_evtlog = kvzalloc((log_size * SDE_EVTLOG_BUF_MAX), GFP_KERNEL); - evtlog->log_size = log_size; + evtlog->dumped_evtlog = kvzalloc((SDE_EVTLOG_ENTRY * SDE_EVTLOG_BUF_MAX), + GFP_KERNEL); + if (!evtlog->dumped_evtlog) + return; + + evtlog->log_size = SDE_EVTLOG_ENTRY; } dump_addr = evtlog->dumped_evtlog; if ((in_mem || in_dump) && dump_addr && (!sde_dbg_base.coredump_reading)) { - while (sde_evtlog_dump_to_buffer(evtlog, dump_addr, SDE_EVTLOG_BUF_MAX, - update_last_entry, true)) { + for (i = 0; i < evtlog->log_size; i++) { + if (!sde_evtlog_dump_to_buffer(evtlog, dump_addr, SDE_EVTLOG_BUF_MAX, + update_last_entry, true)) + break; + dump_addr += SDE_EVTLOG_BUF_MAX; update_last_entry = false; } @@ -1232,8 +1232,11 @@ void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) } if (in_log) { - while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf), - update_last_entry, false)) { + for (i = 0; i < evtlog->log_size; i++) { + if (!sde_evtlog_dump_to_buffer(evtlog, buf, SDE_EVTLOG_BUF_MAX, + update_last_entry, false)) + break; + pr_info("%s\n", buf); update_last_entry = false; } From 182aac6040a752624958a9b8522b242f3fbf4e32 Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Wed, 16 Feb 2022 19:50:08 +0530 Subject: [PATCH 12/20] disp: msm: cancel all delayed_works before triggering msm_lastclose This patch cancels all the delayed_off_works if scheduled and flushes the display threads for completion during msm_lastclose. The commit from msm_lastclose client modeset to disable any crtcs if enabled is always scheduled on primary crtc_commit thread. In the current issue, delayed_off_work is scheduled on secondary display crtc_commit thread and primary crtc_commit thread is scheduled to turn off active crtcs from msm_lastclose leading to null dereference access of sde_enc's cur_master. This race is avoided by serializing the operations in msm_lastclose. Change-Id: I30cc95b925c8134f0064816ebe2cfdb86a49fb36 Signed-off-by: Jayaprakash Madisetty --- msm/msm_drv.c | 21 +++++++++++++++++++++ msm/msm_drv.h | 2 ++ msm/sde/sde_kms.c | 22 ++++++++++++---------- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 70b803da..0b0d7071 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -1040,6 +1040,25 @@ mdss_init_fail: return ret; } +void msm_atomic_flush_display_threads(struct msm_drm_private *priv) +{ + int i; + + if (!priv) { + SDE_ERROR("invalid private data\n"); + return; + } + + for (i = 0; i < priv->num_crtcs; i++) { + if (priv->disp_thread[i].thread) + kthread_flush_worker(&priv->disp_thread[i].worker); + if (priv->event_thread[i].thread) + kthread_flush_worker(&priv->event_thread[i].worker); + } + + kthread_flush_worker(&priv->pp_event_worker); +} + /* * DRM operations: */ @@ -1165,6 +1184,8 @@ static void msm_lastclose(struct drm_device *dev) DRM_INFO("wait for crtc mask 0x%x failed, commit anyway...\n", priv->pending_crtcs); + msm_atomic_flush_display_threads(priv); + if (priv->fbdev) { rc = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); if (rc) diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 2a7c0010..16e91cb0 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -1115,6 +1115,8 @@ struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev); void msm_atomic_state_clear(struct drm_atomic_state *state); void msm_atomic_state_free(struct drm_atomic_state *state); +void msm_atomic_flush_display_threads(struct msm_drm_private *priv); + int msm_gem_init_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, int npages); void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index e176b1aa..45cd994f 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2655,6 +2655,11 @@ error: drm_framebuffer_put(fb); } + drm_for_each_crtc(crtc, dev) { + if (!ret && crtc_mask & drm_crtc_mask(crtc)) + sde_kms_cancel_delayed_work(crtc); + } + end: return ret; } @@ -3881,6 +3886,7 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) { struct sde_kms *sde_kms; struct sde_splash_display *splash_display; + struct drm_crtc *crtc; int i, rc = 0; if (!kms) { @@ -3898,10 +3904,14 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) splash_display = &sde_kms->splash_data.splash_display[i]; if (splash_display->cont_splash_enabled && splash_display->encoder) { + crtc = splash_display->encoder->crtc; SDE_DEBUG("triggering null commit on enc:%d\n", DRMID(splash_display->encoder)); SDE_EVT32(DRMID(splash_display->encoder), SDE_EVTLOG_FUNC_ENTRY); rc = _sde_kms_null_commit(sde_kms->dev, splash_display->encoder); + + if (!rc && crtc) + sde_kms_cancel_delayed_work(crtc); } } @@ -3911,7 +3921,7 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) static void _sde_kms_pm_suspend_idle_helper(struct sde_kms *sde_kms, struct device *dev) { - int i, ret, crtc_id = 0; + int ret, crtc_id = 0; struct drm_device *ddev = dev_get_drvdata(dev); struct drm_connector *conn; struct drm_connector_list_iter conn_iter; @@ -3948,15 +3958,7 @@ static void _sde_kms_pm_suspend_idle_helper(struct sde_kms *sde_kms, } drm_connector_list_iter_end(&conn_iter); - for (i = 0; i < priv->num_crtcs; i++) { - if (priv->disp_thread[i].thread) - kthread_flush_worker( - &priv->disp_thread[i].worker); - if (priv->event_thread[i].thread) - kthread_flush_worker( - &priv->event_thread[i].worker); - } - kthread_flush_worker(&priv->pp_event_worker); + msm_atomic_flush_display_threads(priv); } struct msm_display_mode *sde_kms_get_msm_mode(struct drm_connector_state *conn_state) From f9578b89c97537cc030ad74792f54aaecff1eb43 Mon Sep 17 00:00:00 2001 From: Jayaprakash Madisetty Date: Thu, 14 Apr 2022 18:09:07 +0530 Subject: [PATCH 13/20] disp: msm: sde: skip msm_lastclose if display is stuck in splash This change skips msm_lastclose, when splash enabled builtin-displays equals number of actual displays and are stuck in continuous splash. It fixes the issue seen with change commit 548b17185e95 ("disp: msm: send power_on event in dual display composer kill scenario"). Change-Id: I1f5417d8945db621dc20ab0a9cc0146eabae5e22 Signed-off-by: Jayaprakash Madisetty --- msm/msm_drv.c | 4 +--- msm/sde/sde_kms.c | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 0b0d7071..d43673cd 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -1157,10 +1157,8 @@ static void msm_lastclose(struct drm_device *dev) priv->pending_crtcs); rc = kms->funcs->trigger_null_flush(kms); - if (rc) { - DRM_ERROR("null flush commit failure during lastclose\n"); + if (rc) return; - } } /* diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 45cd994f..bea70fb8 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -3896,10 +3896,18 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) sde_kms = to_sde_kms(kms); - if (!sde_kms->splash_data.num_splash_displays || - sde_kms->dsi_display_count == sde_kms->splash_data.num_splash_displays) - return rc; + /* If splash handoff is done, early return*/ + if (!sde_kms->splash_data.num_splash_displays) + return 0; + /* If all builtin-displays are having cont splash enabled, ignore lastclose*/ + if (sde_kms->dsi_display_count == sde_kms->splash_data.num_splash_displays) + return -EINVAL; + + /* + * Trigger NULL flush if built-in secondary/primary is stuck in splash + * while the primary/secondary is running respectively before lastclose. + */ for (i = 0; i < MAX_DSI_DISPLAYS; i++) { splash_display = &sde_kms->splash_data.splash_display[i]; @@ -3912,10 +3920,12 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) if (!rc && crtc) sde_kms_cancel_delayed_work(crtc); + if (rc) + DRM_ERROR("null flush commit failure during lastclose\n"); } } - return rc; + return 0; } static void _sde_kms_pm_suspend_idle_helper(struct sde_kms *sde_kms, From 51775dd093a3b376e3aa9bdfa6f508ac799c9e53 Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Fri, 19 Aug 2022 12:25:26 -0700 Subject: [PATCH 14/20] disp: msm: uapi: increase SDE_FRAME_DATA_MAX_PLANES size MDSS 9.0.0 supports 10 pipes, so modify the max_planes accordingly. This is used for the frame_data transfer between user/kernel and since its a new feature added there is no backward compatibility that needs to be handled for this uapi change. Add corresponding bound check during the usage. Change-Id: I0853fcc55395855d798f2c1b03cf9bf7b4bd3c96 Signed-off-by: Veera Sundaram Sankaran --- include/uapi/display/drm/sde_drm.h | 2 +- msm/sde/sde_crtc.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/uapi/display/drm/sde_drm.h b/include/uapi/display/drm/sde_drm.h index 8cb12843..5eee6bd8 100644 --- a/include/uapi/display/drm/sde_drm.h +++ b/include/uapi/display/drm/sde_drm.h @@ -580,7 +580,7 @@ struct sde_drm_ubwc_stats_data { */ #define SDE_FRAME_DATA_BUFFER_MAX 0x3 #define SDE_FRAME_DATA_GUARD_BYTES 0xFF -#define SDE_FRAME_DATA_MAX_PLANES 0x10 +#define SDE_FRAME_DATA_MAX_PLANES 0x14 /** * struct sde_drm_frame_data_buffers_ctrl - control frame data buffers diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index d476bd4b..b63dc387 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -2778,8 +2778,10 @@ void sde_crtc_get_frame_data(struct drm_crtc *crtc) data->frame_count = sde_crtc->fps_info.frame_count; /* Collect plane specific data */ - drm_for_each_plane_mask(plane, crtc->dev, sde_crtc->plane_mask_old) - sde_plane_get_frame_data(plane, &data->plane_frame_data[i++]); + drm_for_each_plane_mask(plane, crtc->dev, sde_crtc->plane_mask_old) { + if (i < SDE_FRAME_DATA_MAX_PLANES) + sde_plane_get_frame_data(plane, &data->plane_frame_data[i++]); + } if (frame_data->cnt) _sde_crtc_frame_data_notify(crtc, data); From d44f0ff715dbc5ff0ad453fd1e085e0e59fc2e6f Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Mon, 22 Aug 2022 14:29:16 -0700 Subject: [PATCH 15/20] disp: msm: sde: use new connector state for topology checks Use with the new connector state during validation phase for checking the 3d-merge topology, since this is the state that needs to be validated. Change-Id: Ie212f948affa4dc439ef508363bac6713e560006 Signed-off-by: Veera Sundaram Sankaran --- msm/sde/sde_connector.h | 20 +++++++++++--------- msm/sde/sde_crtc.c | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index d5d8c91c..86ff2ace 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -1217,14 +1217,15 @@ static inline int sde_connector_state_get_compression_info( return 0; } -static inline bool sde_connector_is_quadpipe_3d_merge_enabled(struct drm_connector *conn) +static inline bool sde_connector_is_quadpipe_3d_merge_enabled( + struct drm_connector_state *conn_state) { enum sde_rm_topology_name topology; - if (!conn) + if (!conn_state) return false; - topology = sde_connector_get_topology_name(conn); + topology = sde_connector_get_property(conn_state, CONNECTOR_PROP_TOPOLOGY_NAME); if ((topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE) || (topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE_DSC)) return true; @@ -1232,14 +1233,15 @@ static inline bool sde_connector_is_quadpipe_3d_merge_enabled(struct drm_connect return false; } -static inline bool sde_connector_is_dualpipe_3d_merge_enabled(struct drm_connector *conn) +static inline bool sde_connector_is_dualpipe_3d_merge_enabled( + struct drm_connector_state *conn_state) { enum sde_rm_topology_name topology; - if (!conn) + if (!conn_state) return false; - topology = sde_connector_get_topology_name(conn); + topology = sde_connector_get_property(conn_state, CONNECTOR_PROP_TOPOLOGY_NAME); if ((topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC)) @@ -1248,10 +1250,10 @@ static inline bool sde_connector_is_dualpipe_3d_merge_enabled(struct drm_connect return false; } -static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector *conn) +static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector_state *conn_state) { - return sde_connector_is_dualpipe_3d_merge_enabled(conn) - || sde_connector_is_quadpipe_3d_merge_enabled(conn); + return sde_connector_is_dualpipe_3d_merge_enabled(conn_state) + || sde_connector_is_quadpipe_3d_merge_enabled(conn_state); } /** diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index b63dc387..fa50d111 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1391,9 +1391,9 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, } else if (state->state) { for_each_new_connector_in_state(state->state, conn, conn_state, i) { if (conn_state && (conn_state->crtc == crtc) - && ((sde_connector_is_dualpipe_3d_merge_enabled(conn) + && ((sde_connector_is_dualpipe_3d_merge_enabled(conn_state) && (crtc_width % 4)) - || (sde_connector_is_quadpipe_3d_merge_enabled(conn) + || (sde_connector_is_quadpipe_3d_merge_enabled(conn_state) && (crtc_width % 8)))) { SDE_ERROR( "%s: invalid 3d-merge_w - mixer_w:%d, crtc_w:%d, num_mixers:%d\n", @@ -1421,7 +1421,7 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, goto end; } - if (sde_connector_is_3d_merge_enabled(conn) && (mixer_width % 2)) { + if (sde_connector_is_3d_merge_enabled(conn->state) && (mixer_width % 2)) { SDE_ERROR( "%s: invalid width w/ 3d-merge - mixer_w:%d, crtc_w:%d, num_mixers:%d\n", sde_crtc->name, crtc_width, mixer_width, sde_crtc->num_mixers); From 7eef92843d7ef8c956bdd51f0440950cc04478dd Mon Sep 17 00:00:00 2001 From: Rajkumar Subbiah Date: Tue, 12 Apr 2022 11:13:55 -0400 Subject: [PATCH 16/20] disp: msm: dp: add debug node to capture source and sink crc This change adds a debug node named 'crc' to drm_dp to read the frame CRC values for DP controller and DP Sink. In order to facilitate the immediate read of the CRC values when accessed, it enables the CRC calculation on the controller and sink automatically when the stream is enabled. In addition to the frame CRC values it also reads the MISR values from controller and PHY to validate the data flow from controller to PHY. Change-Id: I1acee2dba931e4635caf4a400e336a72c86e88bf Signed-off-by: Rajkumar Subbiah --- msm/dp/dp_catalog.c | 107 ++++++++++++++++++++++++++++++++++++++++++++ msm/dp/dp_catalog.h | 8 ++++ msm/dp/dp_ctrl.c | 27 +++++++++++ msm/dp/dp_ctrl.h | 3 ++ msm/dp/dp_debug.c | 98 +++++++++++++++++++++++++++++++++++++++- msm/dp/dp_link.h | 2 + msm/dp/dp_panel.c | 71 +++++++++++++++++++++++++++++ msm/dp/dp_panel.h | 3 ++ msm/dp/dp_reg.h | 14 ++++++ 9 files changed, 332 insertions(+), 1 deletion(-) diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index e6dfae0a..8482f949 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -1118,6 +1118,7 @@ static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel, struct dp_catalog_private *catalog; struct dp_io_data *io_data; u32 strm_reg_off = 0, mainlink_ctrl; + u32 reg; if (!panel) { DP_ERR("invalid input\n"); @@ -1150,6 +1151,10 @@ static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel, dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x01); else dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x00); + + reg = dp_read(MMSS_DP_TIMING_ENGINE_EN); + reg |= BIT(8); + dp_write(MMSS_DP_TIMING_ENGINE_EN, reg); } static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel, @@ -1419,6 +1424,77 @@ static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip) wmb(); } +static int dp_catalog_ctrl_setup_misr(struct dp_catalog_ctrl *ctrl) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 val; + + if (!ctrl) { + DP_ERR("invalid input\n"); + return -EINVAL; + } + + catalog = dp_catalog_get_priv(ctrl); + + io_data = catalog->io.dp_phy; + dp_write(DP_PHY_MISR_CTRL, 0x3); + /* make sure misr hw is reset */ + wmb(); + dp_write(DP_PHY_MISR_CTRL, 0x1); + /* make sure misr is brought out of reset */ + wmb(); + + io_data = catalog->io.dp_link; + val = 1; // frame count + val |= BIT(10); // clear status + val |= BIT(8); // enable + dp_write(DP_MISR40_CTRL, val); + /* make sure misr control is applied */ + wmb(); + + return 0; +} + +static int dp_catalog_ctrl_read_misr(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 val; + int i, j; + u32 addr; + + if (!ctrl) { + DP_ERR("invalid input\n"); + return -EINVAL; + } + + catalog = dp_catalog_get_priv(ctrl); + + io_data = catalog->io.dp_phy; + val = dp_read(DP_PHY_MISR_STATUS); + if (!val) { + DP_WARN("phy misr not ready!"); + return -EAGAIN; + } + + addr = DP_PHY_MISR_TX0; + for (i = 0; i < 8; i++) { + data->phy_misr[i] = 0; + for (j = 0; j < 4; j++) { + val = dp_read(addr) & 0xff; + data->phy_misr[i] |= val << (j * 8); + addr += 4; + } + } + + io_data = catalog->io.dp_link; + for (i = 0; i < 8; i++) + data->ctrl_misr[i] = dp_read(DP_MISR40_TX0 + (i * 4)); + + return 0; +} + static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel, u32 pattern) { struct dp_catalog_private *catalog; @@ -1627,6 +1703,34 @@ static bool dp_catalog_panel_dhdr_busy(struct dp_catalog_panel *panel) return dp_flush & BIT(DP_DHDR_FLUSH) ? true : false; } +static int dp_catalog_panel_get_src_crc(struct dp_catalog_panel *panel, u16 *crc) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 offset; + u32 reg; + + if (panel->stream_id >= DP_STREAM_MAX) { + DP_ERR("invalid stream_id:%d\n", panel->stream_id); + return -EINVAL; + } + + catalog = dp_catalog_get_priv(panel); + io_data = catalog->io.dp_link; + + if (panel->stream_id == DP_STREAM_0) + offset = MMSS_DP_PSR_CRC_RG; + else + offset = MMSS_DP1_CRC_RG; + + reg = dp_read(offset); //GR + crc[0] = reg & 0xffff; + crc[1] = reg >> 16; + crc[2] = dp_read(offset + 4); //B + + return 0; +} + static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl) { u32 sw_reset; @@ -2896,6 +3000,8 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .fec_config = dp_catalog_ctrl_fec_config, .mainlink_levels = dp_catalog_ctrl_mainlink_levels, .late_phy_init = dp_catalog_ctrl_late_phy_init, + .setup_misr = dp_catalog_ctrl_setup_misr, + .read_misr = dp_catalog_ctrl_read_misr, }; struct dp_catalog_hpd hpd = { .config_hpd = dp_catalog_hpd_config_hpd, @@ -2925,6 +3031,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .pps_flush = dp_catalog_panel_pps_flush, .dhdr_flush = dp_catalog_panel_dhdr_flush, .dhdr_busy = dp_catalog_panel_dhdr_busy, + .get_src_crc = dp_catalog_panel_get_src_crc, }; if (!dev || !parser) { diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index fea23658..dad18559 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -46,6 +46,11 @@ struct dp_catalog_vsc_sdp_colorimetry { u8 data[32]; }; +struct dp_misr40_data { + u32 ctrl_misr[8]; + u32 phy_misr[8]; +}; + struct dp_catalog_aux { u32 data; u32 isr; @@ -103,6 +108,8 @@ struct dp_catalog_ctrl { int (*late_phy_init)(struct dp_catalog_ctrl *ctrl, u8 lane_cnt, bool flipped); + int (*setup_misr)(struct dp_catalog_ctrl *ctrl); + int (*read_misr)(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data); }; struct dp_catalog_hpd { @@ -221,6 +228,7 @@ struct dp_catalog_panel { void (*pps_flush)(struct dp_catalog_panel *panel); void (*dhdr_flush)(struct dp_catalog_panel *panel); bool (*dhdr_busy)(struct dp_catalog_panel *panel); + int (*get_src_crc)(struct dp_catalog_panel *panel, u16 *crc); }; struct dp_catalog; diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index b8cad19e..2c6b6a1c 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -1308,6 +1308,7 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) /* wait for link training completion before fec config as per spec */ dp_ctrl_fec_setup(ctrl); dp_ctrl_dsc_setup(ctrl, panel); + panel->sink_crc_enable(panel, true); return rc; } @@ -1515,6 +1516,30 @@ void dp_ctrl_set_sim_mode(struct dp_ctrl *dp_ctrl, bool en) DP_INFO("sim_mode=%d\n", ctrl->sim_mode); } +int dp_ctrl_setup_misr(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + + if (!dp_ctrl) + return -EINVAL; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + return ctrl->catalog->setup_misr(ctrl->catalog); +} + +int dp_ctrl_read_misr(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data) +{ + struct dp_ctrl_private *ctrl; + + if (!dp_ctrl) + return -EINVAL; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + return ctrl->catalog->read_misr(ctrl->catalog, data); +} + struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) { int rc = 0; @@ -1565,6 +1590,8 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) dp_ctrl->stream_pre_off = dp_ctrl_stream_pre_off; dp_ctrl->set_mst_channel_info = dp_ctrl_set_mst_channel_info; dp_ctrl->set_sim_mode = dp_ctrl_set_sim_mode; + dp_ctrl->setup_misr = dp_ctrl_setup_misr; + dp_ctrl->read_misr = dp_ctrl_read_misr; return dp_ctrl; error: diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index ded1d8eb..37f78d04 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ @@ -32,6 +33,8 @@ struct dp_ctrl { enum dp_stream_id strm, u32 ch_start_slot, u32 ch_tot_slots); void (*set_sim_mode)(struct dp_ctrl *dp_ctrl, bool en); + int (*setup_misr)(struct dp_ctrl *dp_ctrl); + int (*read_misr)(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data); }; struct dp_ctrl_in { diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 75b5e0ff..1e1cb53b 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -333,7 +333,7 @@ static ssize_t dp_debug_read_dpcd(struct file *file, } } - len += scnprintf(buf + len , buf_size - len, "%04x: ", debug->dpcd_offset); + len += scnprintf(buf + len, buf_size - len, "%04x: ", debug->dpcd_offset); while (offset < debug->dpcd_size) len += scnprintf(buf + len, buf_size - len, "%02x ", dpcd[offset++]); @@ -351,6 +351,90 @@ bail: return len; } +static ssize_t dp_debug_read_crc(struct file *file, char __user *user_buff, size_t count, + loff_t *ppos) +{ + struct dp_debug_private *debug = file->private_data; + char *buf; + int const buf_size = SZ_4K; + u32 len = 0; + u16 src_crc[3] = {0}; + u16 sink_crc[3] = {0}; + struct dp_misr40_data misr40 = {0}; + u32 retries = 2; + struct drm_connector *drm_conn; + struct sde_connector *sde_conn; + struct dp_panel *panel; + int i; + int rc; + + if (!debug || !debug->aux) + return -ENODEV; + + if (*ppos) + return 0; + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&debug->lock); + + if (!debug->panel || !debug->ctrl) + goto bail; + + if (debug->panel->mst_state) { + drm_conn = drm_connector_lookup((*debug->connector)->dev, NULL, debug->mst_con_id); + if (!drm_conn) { + DP_ERR("connector %u not in mst list\n", debug->mst_con_id); + goto bail; + } + + sde_conn = to_sde_connector(drm_conn); + panel = sde_conn->drv_panel; + } else { + panel = debug->panel; + } + + panel->get_src_crc(panel, src_crc); + panel->get_sink_crc(panel, sink_crc); + + len += scnprintf(buf + len, buf_size - len, "FRAME_CRC:\nSource vs Sink\n"); + + len += scnprintf(buf + len, buf_size - len, "CRC_R: %04X %04X\n", src_crc[0], sink_crc[0]); + len += scnprintf(buf + len, buf_size - len, "CRC_G: %04X %04X\n", src_crc[1], sink_crc[1]); + len += scnprintf(buf + len, buf_size - len, "CRC_B: %04X %04X\n", src_crc[2], sink_crc[2]); + + debug->ctrl->setup_misr(debug->ctrl); + + while (retries--) { + mutex_unlock(&debug->lock); + msleep(30); + mutex_lock(&debug->lock); + + rc = debug->ctrl->read_misr(debug->ctrl, &misr40); + if (rc != -EAGAIN) + break; + } + + len += scnprintf(buf + len, buf_size - len, "\nMISR40:\nCTLR vs PHY\n"); + for (i = 0; i < 4; i++) { + len += scnprintf(buf + len, buf_size - len, "Lane%d %08X%08X %08X%08X\n", i, + misr40.ctrl_misr[2 * i], misr40.ctrl_misr[(2 * i) + 1], + misr40.phy_misr[2 * i], misr40.phy_misr[(2 * i) + 1]); + } + + len = min_t(size_t, count, len); + if (!copy_to_user(user_buff, buf, len)) + *ppos += len; + +bail: + mutex_unlock(&debug->lock); + kfree(buf); + + return len; +} + static ssize_t dp_debug_write_hpd(struct file *file, const char __user *user_buff, size_t count, loff_t *ppos) { @@ -1871,6 +1955,11 @@ static const struct file_operations dpcd_fops = { .read = dp_debug_read_dpcd, }; +static const struct file_operations crc_fops = { + .open = simple_open, + .read = dp_debug_read_crc, +}; + static const struct file_operations connected_fops = { .open = simple_open, .read = dp_debug_read_connected, @@ -2104,6 +2193,13 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug, return rc; } + file = debugfs_create_file("crc", 0644, dir, debug, &crc_fops); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + DP_ERR("[%s] debugfs crc failed, rc=%d\n", DEBUG_NAME, rc); + return rc; + } + return rc; } diff --git a/msm/dp/dp_link.h b/msm/dp/dp_link.h index 50da7ca9..ca419f13 100644 --- a/msm/dp/dp_link.h +++ b/msm/dp/dp_link.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. * * Copyright (c) 2008 Keith Packard @@ -38,6 +39,7 @@ struct drm_dp_aux; #define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) +#define DP_LINK_CAP_CRC (1 << 1) struct drm_dp_link { unsigned char revision; diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index ace9b8ab..b9862f89 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.c @@ -1682,6 +1682,10 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func) if (drm_dp_enhanced_frame_cap(dpcd)) link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; + rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_TEST_SINK_MISC, &temp, 1); + if ((rlen == 1) && (temp & DP_TEST_CRC_SUPPORTED)) + link_info->capabilities |= DP_LINK_CAP_CRC; + dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_DOWN_STREAM_PORT_COUNT; @@ -3050,6 +3054,70 @@ static void dp_panel_update_pps(struct dp_panel *dp_panel, char *pps_cmd) catalog->pps_flush(catalog); } +int dp_panel_get_src_crc(struct dp_panel *dp_panel, u16 *crc) +{ + struct dp_catalog_panel *catalog; + struct dp_panel_private *panel; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + + catalog = panel->catalog; + return catalog->get_src_crc(catalog, crc); +} + +int dp_panel_get_sink_crc(struct dp_panel *dp_panel, u16 *crc) +{ + int rc = 0; + struct dp_panel_private *panel; + struct drm_dp_aux *drm_aux; + u8 crc_bytes[6]; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + drm_aux = panel->aux->drm_aux; + + /* + * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes + * per component (RGB or CrYCb). + */ + rc = drm_dp_dpcd_read(drm_aux, DP_TEST_CRC_R_CR, crc_bytes, 6); + if (rc < 0) + return rc; + + rc = 0; + crc[0] = crc_bytes[0] | crc_bytes[1] << 8; + crc[1] = crc_bytes[2] | crc_bytes[3] << 8; + crc[2] = crc_bytes[4] | crc_bytes[5] << 8; + + return rc; +} + +int dp_panel_sink_crc_enable(struct dp_panel *dp_panel, bool enable) +{ + int rc = 0; + struct dp_panel_private *panel; + struct drm_dp_aux *drm_aux; + ssize_t ret; + u8 buf; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + drm_aux = panel->aux->drm_aux; + + if (dp_panel->link_info.capabilities & DP_LINK_CAP_CRC) { + ret = drm_dp_dpcd_readb(drm_aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_writeb(drm_aux, DP_TEST_SINK, buf | DP_TEST_SINK_START); + if (ret < 0) + return ret; + + drm_dp_dpcd_readb(drm_aux, DP_TEST_SINK, &buf); + DP_DEBUG("Enabled CRC: %x\n", buf); + } + + return rc; +} + struct dp_panel *dp_panel_get(struct dp_panel_in *in) { int rc = 0; @@ -3122,6 +3190,9 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel->read_mst_cap = dp_panel_read_mst_cap; dp_panel->convert_to_dp_mode = dp_panel_convert_to_dp_mode; dp_panel->update_pps = dp_panel_update_pps; + dp_panel->get_src_crc = dp_panel_get_src_crc; + dp_panel->get_sink_crc = dp_panel_get_sink_crc; + dp_panel->sink_crc_enable = dp_panel_sink_crc_enable; sde_conn = to_sde_connector(dp_panel->connector); sde_conn->drv_panel = dp_panel; diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index 92b0743b..93e75901 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.h @@ -193,6 +193,9 @@ struct dp_panel { const struct drm_display_mode *drm_mode, struct dp_display_mode *dp_mode); void (*update_pps)(struct dp_panel *dp_panel, char *pps_cmd); + int (*sink_crc_enable)(struct dp_panel *dp_panel, bool enable); + int (*get_src_crc)(struct dp_panel *dp_panel, u16 *crc); + int (*get_sink_crc)(struct dp_panel *dp_panel, u16 *crc); }; struct dp_tu_calc_input { diff --git a/msm/dp/dp_reg.h b/msm/dp/dp_reg.h index f48eaf6c..851291bf 100644 --- a/msm/dp/dp_reg.h +++ b/msm/dp/dp_reg.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ @@ -86,9 +87,16 @@ #define MMSS_DP_AUDIO_TIMING_RBR_48 (0x00000094) #define MMSS_DP_AUDIO_TIMING_HBR_48 (0x00000098) +#define DP_MISR40_CTRL (0x000000D0) +#define DP_MISR40_TX0 (0x000000D4) +#define DP_MISR40_TX1 (0x000000DC) +#define DP_MISR40_TX2 (0x000000E4) +#define DP_MISR40_TX3 (0x000000EC) #define MMSS_DP_PSR_CRC_RG (0x00000154) #define MMSS_DP_PSR_CRC_B (0x00000158) +#define MMSS_DP1_CRC_RG (0x00000164) +#define MMSS_DP1_CRC_B (0x00000168) #define DP_COMPRESSION_MODE_CTRL (0x00000180) #define DP_PPS_HB_0_3 (0x00000184) #define DP_PPS_PB_0_3 (0x00000188) @@ -384,6 +392,12 @@ #define DP_PHY_AUX_INTERRUPT_STATUS_V420 (0x00D8) #define DP_PHY_AUX_INTERRUPT_STATUS_V600 (0x00E0) #define DP_PHY_SPARE0_V420 (0x00C8) +#define DP_PHY_MISR_CTRL (0x00C0) +#define DP_PHY_MISR_STATUS (0x010C) +#define DP_PHY_MISR_TX0 (0x0110) +#define DP_PHY_MISR_TX1 (0x0130) +#define DP_PHY_MISR_TX2 (0x0150) +#define DP_PHY_MISR_TX3 (0x0170) #define TXn_TX_DRV_LVL_V420 (0x0014) #define TXn_TRANSCEIVER_BIAS_EN_V420 (0x0054) #define TXn_HIGHZ_DRVR_EN_V420 (0x0058) From 001fa8da9048c72b08c44e28f02dd01bf5a94d2c Mon Sep 17 00:00:00 2001 From: Vara Reddy Date: Fri, 26 Aug 2022 17:48:29 -0700 Subject: [PATCH 17/20] Revert "disp: msm: dsi: increase cmd dma timeout to 1200 milliseconds" This reverts commit 14e7e9b409346aa77fd08cca6eab85252d9ccabe. Reverting this for now until we properly understand the reason for command transfer timeouts that we are hitting for 5k panel. Change-Id: I0390af66f9ca06abc1ebb81996bb683dea35beac Signed-off-by: Vara Reddy --- msm/dsi/dsi_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 6a76189b..120f15fc 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -25,7 +25,7 @@ #define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL" -#define DSI_CTRL_TX_TO_MS 1200 +#define DSI_CTRL_TX_TO_MS 200 #define TO_ON_OFF(x) ((x) ? "ON" : "OFF") From 2e9d68e174116394d0b11d17fcf8de5923698613 Mon Sep 17 00:00:00 2001 From: Sandeep Gangadharaiah Date: Mon, 29 Aug 2022 14:28:31 -0700 Subject: [PATCH 18/20] disp: msm: dp: free DP sim ports during DP sim disable DP sim ports created during DP sim enable aren't cleared during disable path. This would retain the last status of the DP sim port or the connector. This would impact the next iteration of DP sim test, if done without device reset. This change will set the port number to 0 during DP sim disable and clear the memory allocated for these ports. Change-Id: I386a62e87fcaf006db8dd18e5751b33bbe70fc9b Signed-off-by: Sandeep Gangadharaiah --- msm/dp/dp_debug.c | 2 ++ msm/dp/dp_mst_sim.c | 4 ++++ msm/dp/dp_mst_sim_helper.c | 13 ++++++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 1e1cb53b..c5cfb46e 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -121,6 +121,8 @@ static void dp_debug_disable_sim_mode(struct dp_debug_private *debug, debug->sim_mode &= ~mode_mask; dp_sim_set_sim_mode(debug->sim_bridge, debug->sim_mode); + dp_sim_update_port_num(debug->sim_bridge, 0); + /* switch to normal mode */ if (!debug->sim_mode) debug->aux->set_sim_mode(debug->aux, NULL); diff --git a/msm/dp/dp_mst_sim.c b/msm/dp/dp_mst_sim.c index 2ed82571..2e3eb403 100644 --- a/msm/dp/dp_mst_sim.c +++ b/msm/dp/dp_mst_sim.c @@ -342,6 +342,10 @@ 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; if (port_num > sim_dev->port_num) { ports = devm_kzalloc(sim_dev->dev, diff --git a/msm/dp/dp_mst_sim_helper.c b/msm/dp/dp_mst_sim_helper.c index 1fe4e916..1657c975 100644 --- a/msm/dp/dp_mst_sim_helper.c +++ b/msm/dp/dp_mst_sim_helper.c @@ -1086,13 +1086,15 @@ int dp_mst_sim_update(void *mst_sim_context, u32 port_num, kfree(ctx->ports); ctx->port_num = 0; - ctx->ports = kcalloc(port_num, sizeof(*ports), GFP_KERNEL); - if (!ctx->ports) { - rc = -ENOMEM; - goto fail; + if (port_num) { + ctx->ports = kcalloc(port_num, sizeof(*ports), GFP_KERNEL); + if (!ctx->ports) { + rc = -ENOMEM; + goto fail; + } + ctx->port_num = port_num; } - ctx->port_num = port_num; for (i = 0; i < port_num; i++) { ctx->ports[i] = ports[i]; if (ports[i].edid_size) { @@ -1118,6 +1120,7 @@ fail: for (i = 0; i < ctx->port_num; i++) kfree(ctx->ports[i].edid); kfree(ctx->ports); + ctx->port_num = 0; } mutex_unlock(&ctx->session_lock); From 5043291e1a969c82b76b639f107ac99e56e3be5b Mon Sep 17 00:00:00 2001 From: Rajkumar Subbiah Date: Fri, 26 Aug 2022 15:15:21 -0400 Subject: [PATCH 19/20] disp: msm: dp: skip waits when processing usb disconnect in sim mode With real DP over Type-C sinks, DP driver requests access to USB combo PHY from USB driver. But in DP SIM mode, there is no real sink and PD management, so the combo PHY is managed by USB driver and DP driver uses it without actually claiming it. If the USB cable is unplugged in this scenario, USB driver notifies the disconnection through an atomic notifier call. It does not expect the handler to go into sleep, but the disconnect handler inside DP driver has multiple wait for events and also sleeps to wait for HW state updates. This change passes a skip_wait flag to all the disable functions to complete disconnect processing by skipping all processor sleeps and event waits. Change-Id: Ia98de0e7fa6b0573e644615ee59015914a93f4cf Signed-off-by: Rajkumar Subbiah --- msm/dp/dp_audio.c | 11 +++++++---- msm/dp/dp_audio.h | 4 +++- msm/dp/dp_debug.c | 2 ++ msm/dp/dp_display.c | 48 ++++++++++++++++++++++++--------------------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/msm/dp/dp_audio.c b/msm/dp/dp_audio.c index 680f6d67..cd665aa4 100644 --- a/msm/dp/dp_audio.c +++ b/msm/dp/dp_audio.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-2021, The Linux Foundation. All rights reserved. */ @@ -769,7 +770,7 @@ end: return rc; } -static int dp_audio_off(struct dp_audio *dp_audio) +static int dp_audio_off(struct dp_audio *dp_audio, bool skip_wait) { int rc = 0; struct dp_audio_private *audio; @@ -794,9 +795,11 @@ static int dp_audio_off(struct dp_audio *dp_audio) if (work_pending) DP_DEBUG("pending notification work completed\n"); - rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT); - if (rc) - goto end; + if (!skip_wait) { + rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT); + if (rc) + goto end; + } DP_DEBUG("success\n"); end: diff --git a/msm/dp/dp_audio.h b/msm/dp/dp_audio.h index 19e207e0..f81f0079 100644 --- a/msm/dp/dp_audio.h +++ b/msm/dp/dp_audio.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-2020, The Linux Foundation. All rights reserved. */ @@ -41,10 +42,11 @@ struct dp_audio { * playback should be stopped on the external display. * * @dp_audio: an instance of struct dp_audio. + * @skip_wait: flag to skip any waits * * Returns the error code in case of failure, 0 in success case. */ - int (*off)(struct dp_audio *dp_audio); + int (*off)(struct dp_audio *dp_audio, bool skip_wait); }; /** diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 1e1cb53b..0696dd5a 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -2466,6 +2466,8 @@ static void dp_debug_abort(struct dp_debug *dp_debug) debug = container_of(dp_debug, struct dp_debug_private, dp_debug); mutex_lock(&debug->lock); + // disconnect has already been handled. so clear hotplug + debug->hotplug = false; dp_debug_set_sim_mode(debug, false); mutex_unlock(&debug->lock); } diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index dad28ce8..e723997e 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -293,7 +293,7 @@ static void dp_audio_enable(struct dp_display_private *dp, bool enable) dp->link->link_params.lane_count; dp_panel->audio->on(dp_panel->audio); } else { - dp_panel->audio->off(dp_panel->audio); + dp_panel->audio->off(dp_panel->audio, false); } } } @@ -917,7 +917,7 @@ static bool dp_display_send_hpd_event(struct dp_display_private *dp) return true; } -static int dp_display_send_hpd_notification(struct dp_display_private *dp) +static int dp_display_send_hpd_notification(struct dp_display_private *dp, bool skip_wait) { int ret = 0; bool hpd = !!dp_display_state_is(DP_STATE_CONNECTED); @@ -976,7 +976,7 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) goto skip_wait; } - if (hpd && dp->mst.mst_active) + if (skip_wait || (hpd && dp->mst.mst_active)) goto skip_wait; if (!dp->mst.mst_active && @@ -1321,7 +1321,7 @@ end: } if (!rc && !dp_display_state_is(DP_STATE_ABORTED)) - dp_display_send_hpd_notification(dp); + dp_display_send_hpd_notification(dp, false); skip_notify: SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, @@ -1329,7 +1329,7 @@ skip_notify: return rc; } -static void dp_display_process_mst_hpd_low(struct dp_display_private *dp) +static void dp_display_process_mst_hpd_low(struct dp_display_private *dp, bool skip_wait) { int rc = 0; @@ -1346,7 +1346,7 @@ static void dp_display_process_mst_hpd_low(struct dp_display_private *dp) if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) || dp_display_state_is(DP_STATE_ENABLED))) - rc = dp_display_send_hpd_notification(dp); + rc = dp_display_send_hpd_notification(dp, skip_wait); dp_display_set_mst_mgr_state(dp, false); dp_display_update_mst_state(dp, false); @@ -1355,7 +1355,7 @@ static void dp_display_process_mst_hpd_low(struct dp_display_private *dp) DP_MST_DEBUG("mst_hpd_low. mst_active:%d\n", dp->mst.mst_active); } -static int dp_display_process_hpd_low(struct dp_display_private *dp) +static int dp_display_process_hpd_low(struct dp_display_private *dp, bool skip_wait) { int rc = 0; @@ -1364,11 +1364,11 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) dp_audio_enable(dp, false); if (dp->mst.mst_active) { - dp_display_process_mst_hpd_low(dp); + dp_display_process_mst_hpd_low(dp, skip_wait); } else { if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) || dp_display_state_is(DP_STATE_ENABLED))) - rc = dp_display_send_hpd_notification(dp); + rc = dp_display_send_hpd_notification(dp, skip_wait); } mutex_lock(&dp->session_lock); @@ -1551,7 +1551,7 @@ static void dp_display_stream_disable(struct dp_display_private *dp, dp->active_stream_cnt--; } -static void dp_display_clean(struct dp_display_private *dp) +static void dp_display_clean(struct dp_display_private *dp, bool skip_wait) { int idx; struct dp_panel *dp_panel; @@ -1579,9 +1579,10 @@ static void dp_display_clean(struct dp_display_private *dp) dp_panel = dp->active_panels[idx]; if (dp_panel->audio_supported) - dp_panel->audio->off(dp_panel->audio); + dp_panel->audio->off(dp_panel->audio, skip_wait); - dp_display_stream_pre_disable(dp, dp_panel); + if (!skip_wait) + dp_display_stream_pre_disable(dp, dp_panel); dp_display_stream_disable(dp, dp_panel); dp_display_clear_reservation(&dp->dp_display, dp_panel); dp_panel->deinit(dp_panel, 0); @@ -1593,12 +1594,12 @@ static void dp_display_clean(struct dp_display_private *dp) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); } -static int dp_display_handle_disconnect(struct dp_display_private *dp) +static int dp_display_handle_disconnect(struct dp_display_private *dp, bool skip_wait) { int rc; SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); - rc = dp_display_process_hpd_low(dp); + rc = dp_display_process_hpd_low(dp, skip_wait); if (rc) { /* cancel any pending request */ dp->ctrl->abort(dp->ctrl, true); @@ -1607,7 +1608,7 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) mutex_lock(&dp->session_lock); if (dp_display_state_is(DP_STATE_ENABLED)) - dp_display_clean(dp); + dp_display_clean(dp, skip_wait); dp_display_host_unready(dp); @@ -1648,7 +1649,7 @@ static void dp_display_disconnect_sync(struct dp_display_private *dp) DP_DEBUG("disconnect delay = %d ms\n", disconnect_delay_ms); msleep(disconnect_delay_ms); - dp_display_handle_disconnect(dp); + dp_display_handle_disconnect(dp, false); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, disconnect_delay_ms); } @@ -1764,7 +1765,7 @@ static void dp_display_attention_work(struct work_struct *work) SDE_EVT32_EXTERNAL(dp->state, DS_PORT_STATUS_CHANGED); if (!dp->mst.mst_active) { if (dp_display_is_sink_count_zero(dp)) { - dp_display_handle_disconnect(dp); + dp_display_handle_disconnect(dp, false); } else { /* * connect work should take care of sending @@ -1779,7 +1780,7 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_VIDEO_PATTERN); - dp_display_handle_disconnect(dp); + dp_display_handle_disconnect(dp, false); dp->panel->video_test = true; /* @@ -1838,7 +1839,7 @@ cp_irq: * account for that. This is not needed if this * attention work was handling a test request */ - dp_display_send_hpd_notification(dp); + dp_display_send_hpd_notification(dp, false); } mst_attention: @@ -1939,7 +1940,10 @@ static int dp_display_usb_notifier(struct notifier_block *nb, SDE_EVT32_EXTERNAL(dp->state, dp->debug->sim_mode, action); if (!action && dp->debug->sim_mode) { DP_WARN("usb disconnected during simulation\n"); - dp_display_disconnect_sync(dp); + dp_display_state_add(DP_STATE_ABORTED); + dp->ctrl->abort(dp->ctrl, true); + dp->aux->abort(dp->aux, true); + dp_display_handle_disconnect(dp, true); dp->debug->abort(dp->debug); } @@ -1978,7 +1982,7 @@ int dp_display_mmrm_callback(struct mmrm_client_notifier_data *notifier_data) if (notifier_data->cb_type == MMRM_CLIENT_RESOURCE_VALUE_CHANGE && dp_display_state_is(DP_STATE_ENABLED) && !dp_display_state_is(DP_STATE_ABORTED)) { - ret = dp_display_handle_disconnect(dp); + ret = dp_display_handle_disconnect(dp, false); if (ret) DP_ERR("mmrm callback error reducing clk, ret:%d\n", ret); } @@ -2650,7 +2654,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) clean: if (dp_panel->audio_supported) - dp_panel->audio->off(dp_panel->audio); + dp_panel->audio->off(dp_panel->audio, false); rc = dp_display_stream_pre_disable(dp, dp_panel); From ecaabfefc9ffe33a0d77c2aff395dfe7ba67a4b4 Mon Sep 17 00:00:00 2001 From: Rajkumar Subbiah Date: Fri, 26 Aug 2022 15:37:26 -0400 Subject: [PATCH 20/20] disp: msm: dp: resend hpd notification to usermode This change adds robustness to hpd notification by resending it if there was no action in 2 seconds, just in case, the first notification didn't make it to the usermode. Change-Id: Iaf00669ec77e8c50618ee5618735a98518ad7f1a Signed-off-by: Rajkumar Subbiah --- msm/dp/dp_display.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index e723997e..fe073d4b 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -983,8 +983,18 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp, bool (!!dp_display_state_is(DP_STATE_ENABLED) == hpd)) goto skip_wait; - if (!wait_for_completion_timeout(&dp->notification_comp, - HZ * 5)) { + // wait 2 seconds + if (wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) + goto skip_wait; + + //resend notification + if (dp->mst.mst_active) + dp->mst.cbs.hpd(&dp->dp_display, hpd); + else + dp_display_send_hpd_event(dp); + + // wait another 3 seconds + if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 3)) { DP_WARN("%s timeout\n", hpd ? "connect" : "disconnect"); ret = -EINVAL; }