diff --git a/msm/dsi/dsi_catalog.c b/msm/dsi/dsi_catalog.c index fc686bd3b9..509469f491 100644 --- a/msm/dsi/dsi_catalog.c +++ b/msm/dsi/dsi_catalog.c @@ -201,6 +201,7 @@ static void dsi_catalog_phy_2_0_init(struct dsi_phy_hw *phy) dsi_phy_hw_v2_0_dyn_refresh_pipe_delay; phy->ops.dyn_refresh_ops.dyn_refresh_helper = dsi_phy_hw_v2_0_dyn_refresh_helper; + phy->ops.dyn_refresh_ops.dyn_refresh_trigger_sel = NULL; phy->ops.dyn_refresh_ops.cache_phy_timings = dsi_phy_hw_v2_0_cache_phy_timings; } @@ -236,6 +237,7 @@ static void dsi_catalog_phy_3_0_init(struct dsi_phy_hw *phy) dsi_phy_hw_v3_0_dyn_refresh_pipe_delay; phy->ops.dyn_refresh_ops.dyn_refresh_helper = dsi_phy_hw_v3_0_dyn_refresh_helper; + phy->ops.dyn_refresh_ops.dyn_refresh_trigger_sel = NULL; phy->ops.dyn_refresh_ops.cache_phy_timings = dsi_phy_hw_v3_0_cache_phy_timings; } @@ -272,6 +274,8 @@ static void dsi_catalog_phy_4_0_init(struct dsi_phy_hw *phy) dsi_phy_hw_v4_0_dyn_refresh_pipe_delay; phy->ops.dyn_refresh_ops.dyn_refresh_helper = dsi_phy_hw_v4_0_dyn_refresh_helper; + phy->ops.dyn_refresh_ops.dyn_refresh_trigger_sel = + dsi_phy_hw_v4_0_dyn_refresh_trigger_sel; phy->ops.dyn_refresh_ops.cache_phy_timings = dsi_phy_hw_v4_0_cache_phy_timings; phy->ops.set_continuous_clk = dsi_phy_hw_v4_0_set_continuous_clk; diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index cfd864bb59..3c64c7f0c0 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -260,6 +260,8 @@ int dsi_ctrl_hw_cmn_wait4dynamic_refresh_done(struct dsi_ctrl_hw *ctrl); int dsi_phy_hw_v3_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, u32 *dst, u32 size); +void dsi_phy_hw_v4_0_dyn_refresh_trigger_sel(struct dsi_phy_hw *phy, + bool is_master); void dsi_phy_hw_v4_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset); void dsi_phy_hw_v4_0_dyn_refresh_config(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg, bool is_master); diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 5984ea2621..263a55809c 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -4123,9 +4123,13 @@ static int _dsi_display_dyn_update_clks(struct dsi_display *display, struct link_clk_freq *bkp_freq) { int rc = 0, i; + u8 ctrl_version; struct dsi_display_ctrl *m_ctrl, *ctrl; + struct dsi_dyn_clk_caps *dyn_clk_caps; m_ctrl = &display->ctrl[display->clk_master_idx]; + dyn_clk_caps = &(display->panel->dyn_clk_caps); + ctrl_version = m_ctrl->ctrl->version; dsi_clk_prepare_enable(&display->clock_info.src_clks); @@ -4163,6 +4167,15 @@ static int _dsi_display_dyn_update_clks(struct dsi_display *display, } dsi_phy_dynamic_refresh_trigger(m_ctrl->phy, true); + /* + * Don't wait for dynamic refresh done for dsi ctrl greater than 2.5 + * and with constant fps, as dynamic refresh will applied with + * next mdp intf ctrl flush. + */ + if ((ctrl_version >= DSI_CTRL_VERSION_2_5) && + (dyn_clk_caps->maintain_const_fps)) + goto defer_dfps_wait; + /* wait for dynamic refresh done */ display_for_each_ctrl(i, display) { ctrl = &display->ctrl[i]; @@ -4181,6 +4194,7 @@ static int _dsi_display_dyn_update_clks(struct dsi_display *display, dsi_phy_dynamic_refresh_clear(ctrl->phy); } +defer_dfps_wait: rc = dsi_clk_update_parent(&display->clock_info.src_clks, &display->clock_info.mux_clks); if (rc) @@ -4580,8 +4594,10 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, int rc = 0, clk_rate = 0; int i; struct dsi_display_ctrl *ctrl; + struct dsi_display_ctrl *mctrl; struct dsi_display_mode_priv_info *priv_info; bool commit_phy_timing = false; + struct dsi_dyn_clk_caps *dyn_clk_caps; priv_info = mode->priv_info; if (!priv_info) { @@ -4607,8 +4623,26 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, memcpy(&display->config.lane_map, &display->lane_map, sizeof(display->lane_map)); + mctrl = &display->ctrl[display->clk_master_idx]; + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (mode->dsi_mode_flags & (DSI_MODE_FLAG_DFPS | DSI_MODE_FLAG_VRR)) { + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + ctrl->ctrl->hw.ops.set_timing_db(&ctrl->ctrl->hw, + true); + dsi_phy_dynamic_refresh_clear(ctrl->phy); + + if (!ctrl->ctrl || (ctrl != mctrl)) + continue; + + if ((ctrl->ctrl->version >= DSI_CTRL_VERSION_2_5) && + (dyn_clk_caps->maintain_const_fps)) { + dsi_phy_dynamic_refresh_trigger_sel(ctrl->phy, + true); + } + } rc = dsi_display_dfps_update(display, mode); if (rc) { DSI_ERR("[%s]DSI dfps update failed, rc=%d\n", diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index b9edd9b9c4..019c003a6d 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -990,8 +990,9 @@ int dsi_conn_post_kickoff(struct drm_connector *connector, struct dsi_display_mode adj_mode; struct dsi_display *display; struct dsi_display_ctrl *m_ctrl, *ctrl; - int i, rc = 0; + int i, rc = 0, ctrl_version; bool enable; + struct dsi_dyn_clk_caps *dyn_clk_caps; if (!connector || !connector->state) { DSI_ERR("invalid connector or connector state\n"); @@ -1007,9 +1008,11 @@ int dsi_conn_post_kickoff(struct drm_connector *connector, c_bridge = to_dsi_bridge(encoder->bridge); adj_mode = c_bridge->dsi_mode; display = c_bridge->display; + dyn_clk_caps = &(display->panel->dyn_clk_caps); if (adj_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR) { m_ctrl = &display->ctrl[display->clk_master_idx]; + ctrl_version = m_ctrl->ctrl->version; rc = dsi_ctrl_timing_db_update(m_ctrl->ctrl, false); if (rc) { DSI_ERR("[%s] failed to dfps update rc=%d\n", @@ -1017,6 +1020,17 @@ int dsi_conn_post_kickoff(struct drm_connector *connector, return -EINVAL; } + if ((ctrl_version >= DSI_CTRL_VERSION_2_5) && + (dyn_clk_caps->maintain_const_fps)) { + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + rc = dsi_ctrl_wait4dynamic_refresh_done( + ctrl->ctrl); + if (rc) + DSI_ERR("wait4dfps refresh failed\n"); + } + } + /* Update the rest of the controllers */ display_for_each_ctrl(i, display) { ctrl = &display->ctrl[i]; diff --git a/msm/dsi/dsi_phy.c b/msm/dsi/dsi_phy.c index db6aff7bd2..7cc2b38571 100644 --- a/msm/dsi/dsi_phy.c +++ b/msm/dsi/dsi_phy.c @@ -1196,6 +1196,32 @@ void dsi_phy_config_dynamic_refresh(struct msm_dsi_phy *phy, mutex_unlock(&phy->phy_lock); } +/** + * dsi_phy_dynamic_refresh_trigger_sel() - trigger dynamic refresh and + * update the video timings at next frame flush call. + * @phy: DSI PHY handle + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_dynamic_refresh_trigger_sel(struct msm_dsi_phy *phy, + bool is_master) +{ + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + /* + * program DYNAMIC_REFRESH_CTRL.TRIGGER_SEL for master. + */ + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_trigger_sel) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_trigger_sel + (&phy->hw, is_master); + phy->dfps_trigger_mdpintf_flush = true; + + SDE_EVT32(is_master, phy->index); + + mutex_unlock(&phy->phy_lock); +} + /** * dsi_phy_dynamic_refresh_trigger() - trigger dynamic refresh * @phy: DSI PHY handle diff --git a/msm/dsi/dsi_phy.h b/msm/dsi/dsi_phy.h index dc9d44be2f..70ce131591 100644 --- a/msm/dsi/dsi_phy.h +++ b/msm/dsi/dsi_phy.h @@ -75,6 +75,7 @@ enum phy_ulps_return_type { * @allow_phy_power_off: True if PHY is allowed to power off when idle * @regulator_min_datarate_bps: Minimum per lane data rate to turn on regulator * @regulator_required: True if phy regulator is required + * @dfps_trigger_mdpintf_flush: mdp intf flush controls dfps trigger. */ struct msm_dsi_phy { struct platform_device *pdev; @@ -102,6 +103,7 @@ struct msm_dsi_phy { bool allow_phy_power_off; u32 regulator_min_datarate_bps; bool regulator_required; + bool dfps_trigger_mdpintf_flush; }; /** @@ -323,6 +325,14 @@ int dsi_phy_update_phy_timings(struct msm_dsi_phy *phy, void dsi_phy_config_dynamic_refresh(struct msm_dsi_phy *phy, struct dsi_dyn_clk_delay *delay, bool is_master); +/** + * dsi_phy_dynamic_refresh_trigger_sel() - dynamic refresh trigger select. + * @phy: DSI PHY handle + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_dynamic_refresh_trigger_sel(struct msm_dsi_phy *phy, + bool is_master); + /** * dsi_phy_dynamic_refresh_trigger() - trigger dynamic refresh * @phy: DSI PHY handle diff --git a/msm/dsi/dsi_phy_hw.h b/msm/dsi/dsi_phy_hw.h index 45536843d2..62e9e01254 100644 --- a/msm/dsi/dsi_phy_hw.h +++ b/msm/dsi/dsi_phy_hw.h @@ -184,6 +184,14 @@ struct phy_dyn_refresh_ops { */ void (*dyn_refresh_helper)(struct dsi_phy_hw *phy, u32 offset); + /** + * dyn_refresh_trigger_sel - configure trigger_sel to frame flush + * @phy: Pointer to DSI PHY hardware instance. + * @is_master: Boolean to indicate whether master or slave. + */ + void (*dyn_refresh_trigger_sel)(struct dsi_phy_hw *phy, + bool is_master); + /** * dyn_refresh_config - configure dynamic refresh ctrl registers * @phy: Pointer to DSI PHY hardware instance. diff --git a/msm/dsi/dsi_phy_hw_v4_0.c b/msm/dsi/dsi_phy_hw_v4_0.c index 3f456736d4..e293f6b197 100644 --- a/msm/dsi/dsi_phy_hw_v4_0.c +++ b/msm/dsi/dsi_phy_hw_v4_0.c @@ -755,6 +755,23 @@ void dsi_phy_hw_v4_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, delay->pll_delay); } +void dsi_phy_hw_v4_0_dyn_refresh_trigger_sel(struct dsi_phy_hw *phy, + bool is_master) +{ + u32 reg; + + /* + * Dynamic refresh will take effect at next mdp flush event. + * This makes sure that any update to frame timings together + * with dfps will take effect in one vsync at next mdp flush. + */ + if (is_master) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(17); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } +} + void dsi_phy_hw_v4_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset) { u32 reg; @@ -766,7 +783,7 @@ void dsi_phy_hw_v4_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset) */ if (!offset) { reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); - reg &= ~(BIT(0) | BIT(8)); + reg &= ~(BIT(0) | BIT(8) | BIT(13) | BIT(16) | BIT(17)); DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); wmb(); /* ensure dynamic fps is cleared */ return;