diff --git a/msm/dsi/dsi_catalog.c b/msm/dsi/dsi_catalog.c index c19372cf3c..484531345d 100644 --- a/msm/dsi/dsi_catalog.c +++ b/msm/dsi/dsi_catalog.c @@ -62,6 +62,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, dsi_ctrl_hw_cmn_wait_for_cmd_mode_mdp_idle; ctrl->ops.setup_avr = dsi_ctrl_hw_cmn_setup_avr; ctrl->ops.set_continuous_clk = dsi_ctrl_hw_cmn_set_continuous_clk; + ctrl->ops.wait4dynamic_refresh_done = + dsi_ctrl_hw_cmn_wait4dynamic_refresh_done; switch (version) { case DSI_CTRL_VERSION_1_4: @@ -219,6 +221,14 @@ static void dsi_catalog_phy_3_0_init(struct dsi_phy_hw *phy) phy->ops.clamp_ctrl = dsi_phy_hw_v3_0_clamp_ctrl; phy->ops.phy_lane_reset = dsi_phy_hw_v3_0_lane_reset; phy->ops.toggle_resync_fifo = dsi_phy_hw_v3_0_toggle_resync_fifo; + phy->ops.dyn_refresh_ops.dyn_refresh_config = + dsi_phy_hw_v3_0_dyn_refresh_config; + phy->ops.dyn_refresh_ops.dyn_refresh_pipe_delay = + 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.cache_phy_timings = + dsi_phy_hw_v3_0_cache_phy_timings; } /** diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index 08aa842ae8..99c2607c30 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -57,15 +57,17 @@ int dsi_phy_timing_calc_init(struct dsi_phy_hw *phy, * @mode: DSI mode information. * @host: DSI host configuration. * @timing: DSI phy lane configurations. + * @use_mode_bit_clk: Boolean to indicate whether to recalculate bit clk. * * This function setups the catalog information in the dsi_phy_hw object. * * return: error code for failure and 0 for success. */ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy, - struct dsi_mode_info *mode, - struct dsi_host_common_cfg *host, - struct dsi_phy_per_lane_cfgs *timing); + struct dsi_mode_info *mode, + struct dsi_host_common_cfg *host, + struct dsi_phy_per_lane_cfgs *timing, + bool use_mode_bit_clk); /* Definitions for 14nm PHY hardware driver */ void dsi_phy_hw_v2_0_regulator_enable(struct dsi_phy_hw *phy, @@ -239,4 +241,14 @@ void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable, void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable); +/* dynamic refresh specific functions */ +void dsi_phy_hw_v3_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset); +void dsi_phy_hw_v3_0_dyn_refresh_config(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master); +void dsi_phy_hw_v3_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay); + +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); #endif /* _DSI_CATALOG_H_ */ diff --git a/msm/dsi/dsi_clk.h b/msm/dsi/dsi_clk.h index a5b485b452..859d62f7ea 100644 --- a/msm/dsi/dsi_clk.h +++ b/msm/dsi/dsi_clk.h @@ -307,4 +307,18 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index); */ int dsi_clk_update_parent(struct dsi_clk_link_set *parent, struct dsi_clk_link_set *child); + +/** + * dsi_clk_prepare_enable() - prepare and enable dsi src clocks + * @clk: list of src clocks. + * + * @return: Zero on success and err no on failure + */ +int dsi_clk_prepare_enable(struct dsi_clk_link_set *clk); + +/** + * dsi_clk_disable_unprepare() - disable and unprepare dsi src clocks + * @clk: list of src clocks. + */ +void dsi_clk_disable_unprepare(struct dsi_clk_link_set *clk); #endif /* _DSI_CLK_H_ */ diff --git a/msm/dsi/dsi_clk_manager.c b/msm/dsi/dsi_clk_manager.c index 47881f1073..50276d8530 100644 --- a/msm/dsi/dsi_clk_manager.c +++ b/msm/dsi/dsi_clk_manager.c @@ -105,8 +105,9 @@ int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq, /** * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock - * @clks: DSI link clock information. - * @pixel_clk: Pixel clock rate in KHz. + * @clks: DSI link clock information. + * @pixel_clk: Pixel clock rate in KHz. + * @index: Index of the DSI controller. * * return: error code in case of failure or 0 for success. */ @@ -128,9 +129,9 @@ int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index) /** * dsi_clk_set_byte_clk_rate() - set frequency for byte clock - * @client: DSI clock client pointer. - * @byte_clk: Pixel clock rate in Hz. - * @index: Index of the DSI controller. + * @client: DSI clock client pointer. + * @byte_clk: Byte clock rate in Hz. + * @index: Index of the DSI controller. * return: error code in case of failure or 0 for success. */ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) @@ -138,6 +139,7 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) int rc = 0; struct dsi_clk_client_info *c = client; struct dsi_clk_mngr *mngr; + u64 byte_intf_rate; mngr = c->mngr; rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_clk, byte_clk); @@ -146,8 +148,16 @@ int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index) else mngr->link_clks[index].freq.byte_clk_rate = byte_clk; - return rc; + if (mngr->link_clks[index].hs_clks.byte_intf_clk) { + byte_intf_rate = mngr->link_clks[index].freq.byte_clk_rate / 2; + rc = clk_set_rate(mngr->link_clks[index].hs_clks.byte_intf_clk, + byte_intf_rate); + if (rc) + pr_err("failed to set clk rate for byte intf clk=%d\n", + rc); + } + return rc; } /** @@ -175,6 +185,41 @@ error: return rc; } +/** + * dsi_clk_prepare_enable() - prepare and enable dsi src clocks + * @clk: list of src clocks. + * + * @return: Zero on success and err no on failure. + */ +int dsi_clk_prepare_enable(struct dsi_clk_link_set *clk) +{ + int rc; + + rc = clk_prepare_enable(clk->byte_clk); + if (rc) { + pr_err("failed to enable byte src clk %d\n", rc); + return rc; + } + + rc = clk_prepare_enable(clk->pixel_clk); + if (rc) { + pr_err("failed to enable pixel src clk %d\n", rc); + return rc; + } + + return 0; +} + +/** + * dsi_clk_disable_unprepare() - disable and unprepare dsi src clocks + * @clk: list of src clocks. + */ +void dsi_clk_disable_unprepare(struct dsi_clk_link_set *clk) +{ + clk_disable_unprepare(clk->pixel_clk); + clk_disable_unprepare(clk->byte_clk); +} + int dsi_core_clk_start(struct dsi_core_clks *c_clks) { int rc = 0; diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index 6141760552..c72ad437af 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -2887,7 +2887,12 @@ int dsi_ctrl_update_host_config(struct dsi_ctrl *ctrl, goto error; } - if (!(flags & (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR))) { + if (!(flags & (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR | + DSI_MODE_FLAG_DYN_CLK))) { + /* + * for dynamic clk switch case link frequence would + * be updated dsi_display_dynamic_clk_switch(). + */ rc = dsi_ctrl_update_link_freqs(ctrl, config, clk_handle, mode); if (rc) { @@ -3603,6 +3608,27 @@ void dsi_ctrl_irq_update(struct dsi_ctrl *dsi_ctrl, bool enable) mutex_unlock(&dsi_ctrl->ctrl_lock); } +/** + * dsi_ctrl_wait4dynamic_refresh_done() - Poll for dynamci refresh + * done interrupt. + * @dsi_ctrl: DSI controller handle. + */ +int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl) +{ + int rc = 0; + + if (!ctrl) + return 0; + + mutex_lock(&ctrl->ctrl_lock); + + if (ctrl->hw.ops.wait4dynamic_refresh_done) + rc = ctrl->hw.ops.wait4dynamic_refresh_done(&ctrl->hw); + + mutex_unlock(&ctrl->ctrl_lock); + return rc; +} + /** * dsi_ctrl_drv_register() - register platform driver for dsi controller */ diff --git a/msm/dsi/dsi_ctrl.h b/msm/dsi/dsi_ctrl.h index 5192c2926e..0bdfc29387 100644 --- a/msm/dsi/dsi_ctrl.h +++ b/msm/dsi/dsi_ctrl.h @@ -796,4 +796,11 @@ int dsi_ctrl_pixel_format_to_bpp(enum dsi_pixel_format dst_format); * @enable: variable to control continuous clock. */ void dsi_ctrl_set_continuous_clk(struct dsi_ctrl *dsi_ctrl, bool enable); + +/** + * dsi_ctrl_wait4dynamic_refresh_done() - Poll for dynamic refresh done + * interrupt. + * @dsi_ctrl: DSI controller handle. + */ +int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl); #endif /* _DSI_CTRL_H_ */ diff --git a/msm/dsi/dsi_ctrl_hw.h b/msm/dsi/dsi_ctrl_hw.h index 2029c16f09..c2e08f2e97 100644 --- a/msm/dsi/dsi_ctrl_hw.h +++ b/msm/dsi/dsi_ctrl_hw.h @@ -823,6 +823,12 @@ struct dsi_ctrl_hw_ops { * @enable: Bool to control continuous clock request. */ void (*set_continuous_clk)(struct dsi_ctrl_hw *ctrl, bool enable); + + /** + * hw.ops.wait4dynamic_refresh_done() - Wait for dynamic refresh done + * @ctrl: Pointer to the controller host hardware. + */ + int (*wait4dynamic_refresh_done)(struct dsi_ctrl_hw *ctrl); }; /* diff --git a/msm/dsi/dsi_ctrl_hw_cmn.c b/msm/dsi/dsi_ctrl_hw_cmn.c index 4821fc4f51..177c00463b 100644 --- a/msm/dsi/dsi_ctrl_hw_cmn.c +++ b/msm/dsi/dsi_ctrl_hw_cmn.c @@ -1472,6 +1472,13 @@ void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx, bool en) } } + if (idx & BIT(DSI_PLL_UNLOCK_ERR)) { + if (en) + reg |= BIT(28); + else + reg &= ~BIT(28); + } + DSI_W32(ctrl, 0x10c, reg); wmb(); /* ensure error is masked */ } @@ -1538,3 +1545,25 @@ void dsi_ctrl_hw_cmn_set_continuous_clk(struct dsi_ctrl_hw *ctrl, bool enable) DSI_W32(ctrl, DSI_LANE_CTRL, reg); wmb(); /* make sure request is set */ } + +int dsi_ctrl_hw_cmn_wait4dynamic_refresh_done(struct dsi_ctrl_hw *ctrl) +{ + int rc; + u32 const sleep_us = 1000; + u32 const timeout_us = 84000; /* approximately 5 vsyncs */ + u32 reg = 0, dyn_refresh_done = BIT(28); + + rc = readl_poll_timeout(ctrl->base + DSI_INT_CTRL, reg, + (reg & dyn_refresh_done), sleep_us, timeout_us); + if (rc) { + pr_err("wait4dynamic refresh timedout %d\n", rc); + return rc; + } + + /* ack dynamic refresh done status */ + reg = DSI_R32(ctrl, DSI_INT_CTRL); + reg |= dyn_refresh_done; + DSI_W32(ctrl, DSI_INT_CTRL, reg); + + return 0; +} diff --git a/msm/dsi/dsi_ctrl_reg.h b/msm/dsi/dsi_ctrl_reg.h index b3cef04d71..bf93887fb5 100644 --- a/msm/dsi/dsi_ctrl_reg.h +++ b/msm/dsi/dsi_ctrl_reg.h @@ -129,44 +129,7 @@ #define DSI_SCRATCH_REGISTER_1 (0x01F8) #define DSI_SCRATCH_REGISTER_2 (0x01FC) #define DSI_DYNAMIC_REFRESH_CTRL (0x0200) -#define DSI_DYNAMIC_REFRESH_PIPE_DELAY (0x0204) -#define DSI_DYNAMIC_REFRESH_PIPE_DELAY2 (0x0208) -#define DSI_DYNAMIC_REFRESH_PLL_DELAY (0x020C) #define DSI_DYNAMIC_REFRESH_STATUS (0x0210) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL0 (0x0214) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL1 (0x0218) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL2 (0x021C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL3 (0x0220) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL4 (0x0224) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL5 (0x0228) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL6 (0x022C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL7 (0x0230) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL8 (0x0234) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL9 (0x0238) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL10 (0x023C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL11 (0x0240) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL12 (0x0244) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL13 (0x0248) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL14 (0x024C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL15 (0x0250) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL16 (0x0254) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL17 (0x0258) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL18 (0x025C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL19 (0x0260) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL20 (0x0264) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL21 (0x0268) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL22 (0x026C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL23 (0x0270) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL24 (0x0274) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL25 (0x0278) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL26 (0x027C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL27 (0x0280) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL28 (0x0284) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL29 (0x0288) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL30 (0x028C) -#define DSI_DYNAMIC_REFRESH_PLL_CTRL31 (0x0290) -#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR (0x0294) -#define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 (0x0298) #define DSI_VIDEO_COMPRESSION_MODE_CTRL (0x02A0) #define DSI_VIDEO_COMPRESSION_MODE_CTRL2 (0x02A4) #define DSI_COMMAND_COMPRESSION_MODE_CTRL (0x02A8) diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 0515201608..fed726c389 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -87,6 +87,7 @@ enum dsi_op_mode { * New timing values are sent from DAL. * @DSI_MODE_FLAG_POMS: * Seamless transition is dynamic panel operating mode switch + * @DSI_MODE_FLAG_DYN_CLK: Seamless transition is dynamic clock change */ enum dsi_mode_flags { DSI_MODE_FLAG_SEAMLESS = BIT(0), @@ -95,6 +96,7 @@ enum dsi_mode_flags { DSI_MODE_FLAG_DMS = BIT(3), DSI_MODE_FLAG_VRR = BIT(4), DSI_MODE_FLAG_POMS = BIT(5), + DSI_MODE_FLAG_DYN_CLK = BIT(6), }; /** @@ -646,12 +648,50 @@ struct dsi_event_cb_info { * @DSI_FIFO_OVERFLOW: DSI FIFO Overflow error * @DSI_FIFO_UNDERFLOW: DSI FIFO Underflow error * @DSI_LP_Rx_TIMEOUT: DSI LP/RX Timeout error + * @DSI_PLL_UNLOCK_ERR: DSI PLL unlock error */ enum dsi_error_status { DSI_FIFO_OVERFLOW = 1, DSI_FIFO_UNDERFLOW, DSI_LP_Rx_TIMEOUT, + DSI_PLL_UNLOCK_ERR, DSI_ERR_INTR_ALL, }; +/* structure containing the delays required for dynamic clk */ +struct dsi_dyn_clk_delay { + u32 pipe_delay; + u32 pipe_delay2; + u32 pll_delay; +}; + +/* dynamic refresh control bits */ +enum dsi_dyn_clk_control_bits { + DYN_REFRESH_INTF_SEL = 1, + DYN_REFRESH_SYNC_MODE, + DYN_REFRESH_SW_TRIGGER, + DYN_REFRESH_SWI_CTRL, +}; + +/* convert dsi pixel format into bits per pixel */ +static inline int dsi_pixel_format_to_bpp(enum dsi_pixel_format fmt) +{ + switch (fmt) { + case DSI_PIXEL_FORMAT_RGB888: + case DSI_PIXEL_FORMAT_MAX: + return 24; + case DSI_PIXEL_FORMAT_RGB666: + case DSI_PIXEL_FORMAT_RGB666_LOOSE: + return 18; + case DSI_PIXEL_FORMAT_RGB565: + return 16; + case DSI_PIXEL_FORMAT_RGB111: + return 3; + case DSI_PIXEL_FORMAT_RGB332: + return 8; + case DSI_PIXEL_FORMAT_RGB444: + return 12; + } + return 24; +} #endif /* _DSI_DEFS_H_ */ diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 10e5d5d014..b2007450ea 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -1643,14 +1643,12 @@ static int dsi_display_debugfs_deinit(struct dsi_display *display) static void adjust_timing_by_ctrl_count(const struct dsi_display *display, struct dsi_display_mode *mode) { - if (display->ctrl_count > 1) { - mode->timing.h_active /= display->ctrl_count; - mode->timing.h_front_porch /= display->ctrl_count; - mode->timing.h_sync_width /= display->ctrl_count; - mode->timing.h_back_porch /= display->ctrl_count; - mode->timing.h_skew /= display->ctrl_count; - mode->pixel_clk_khz /= display->ctrl_count; - } + mode->timing.h_active /= display->ctrl_count; + mode->timing.h_front_porch /= display->ctrl_count; + mode->timing.h_sync_width /= display->ctrl_count; + mode->timing.h_back_porch /= display->ctrl_count; + mode->timing.h_skew /= display->ctrl_count; + mode->pixel_clk_khz /= display->ctrl_count; } static int dsi_display_is_ulps_req_valid(struct dsi_display *display, @@ -2271,7 +2269,7 @@ static int dsi_display_set_clk_src(struct dsi_display *display) m_ctrl = &display->ctrl[display->clk_master_idx]; rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl, - &display->clock_info.src_clks); + &display->clock_info.mux_clks); if (rc) { pr_err("[%s] failed to set source clocks for master, rc=%d\n", display->name, rc); @@ -2285,7 +2283,7 @@ static int dsi_display_set_clk_src(struct dsi_display *display) continue; rc = dsi_ctrl_set_clock_source(ctrl->ctrl, - &display->clock_info.src_clks); + &display->clock_info.mux_clks); if (rc) { pr_err("[%s] failed to set source clocks, rc=%d\n", display->name, rc); @@ -2956,6 +2954,7 @@ static int dsi_display_clocks_init(struct dsi_display *display) struct dsi_clk_link_set *src = &display->clock_info.src_clks; struct dsi_clk_link_set *mux = &display->clock_info.mux_clks; struct dsi_clk_link_set *shadow = &display->clock_info.shadow_clks; + struct dsi_dyn_clk_caps *dyn_clk_caps = &(display->panel->dyn_clk_caps); char *dsi_clock_name; if (!strcmp(display->display_type, "primary")) @@ -2978,7 +2977,32 @@ static int dsi_display_clocks_init(struct dsi_display *display) rc = PTR_ERR(dsi_clk); pr_err("failed to get %s, rc=%d\n", clk_name, rc); - goto error; + + if (dsi_display_check_prefix(mux_byte, clk_name)) { + mux->byte_clk = NULL; + goto error; + } + if (dsi_display_check_prefix(mux_pixel, clk_name)) { + mux->pixel_clk = NULL; + goto error; + } + + if (dyn_clk_caps->dyn_clk_support) { + if (dsi_display_check_prefix(src_byte, + clk_name)) + src->byte_clk = NULL; + if (dsi_display_check_prefix(src_pixel, + clk_name)) + src->pixel_clk = NULL; + if (dsi_display_check_prefix(shadow_byte, + clk_name)) + shadow->byte_clk = NULL; + if (dsi_display_check_prefix(shadow_pixel, + clk_name)) + shadow->pixel_clk = NULL; + + dyn_clk_caps->dyn_clk_support = false; + } } if (dsi_display_check_prefix(src_byte, clk_name)) { @@ -3762,6 +3786,305 @@ static bool dsi_display_is_seamless_dfps_possible( return true; } +static int dsi_display_update_dsi_bitrate(struct dsi_display *display, + u32 bit_clk_rate) +{ + int rc = 0; + int i; + + pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate); + if (!display->panel) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + if (bit_clk_rate == 0) { + pr_err("Invalid bit clock rate\n"); + return -EINVAL; + } + + display->config.bit_clk_rate_hz = bit_clk_rate; + + for (i = 0; i < display->ctrl_count; i++) { + struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i]; + struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl; + u32 num_of_lanes = 0, bpp; + u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate; + struct dsi_host_common_cfg *host_cfg; + + mutex_lock(&ctrl->ctrl_lock); + + host_cfg = &display->panel->host_config; + if (host_cfg->data_lanes & DSI_DATA_LANE_0) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_1) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_2) + num_of_lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_3) + num_of_lanes++; + + if (num_of_lanes == 0) { + pr_err("Invalid lane count\n"); + rc = -EINVAL; + goto error; + } + + bpp = dsi_pixel_format_to_bpp(host_cfg->dst_format); + + bit_rate = display->config.bit_clk_rate_hz * num_of_lanes; + bit_rate_per_lane = bit_rate; + do_div(bit_rate_per_lane, num_of_lanes); + pclk_rate = bit_rate; + do_div(pclk_rate, bpp); + byte_clk_rate = bit_rate_per_lane; + do_div(byte_clk_rate, 8); + pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", + bit_rate, bit_rate_per_lane); + pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n", + byte_clk_rate, pclk_rate); + + ctrl->clk_freq.byte_clk_rate = byte_clk_rate; + ctrl->clk_freq.pix_clk_rate = pclk_rate; + rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle, + ctrl->clk_freq, ctrl->cell_index); + if (rc) { + pr_err("Failed to update link frequencies\n"); + goto error; + } + + ctrl->host_config.bit_clk_rate_hz = bit_clk_rate; +error: + mutex_unlock(&ctrl->ctrl_lock); + + /* TODO: recover ctrl->clk_freq in case of failure */ + if (rc) + return rc; + } + + return 0; +} + +static void _dsi_display_calc_pipe_delay(struct dsi_display *display, + struct dsi_dyn_clk_delay *delay, + struct dsi_display_mode *mode) +{ + u32 esc_clk_rate_hz; + u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio; + u32 hsync_period = 0; + struct dsi_display_ctrl *m_ctrl; + struct dsi_ctrl *dsi_ctrl; + struct dsi_phy_cfg *cfg; + + m_ctrl = &display->ctrl[display->clk_master_idx]; + dsi_ctrl = m_ctrl->ctrl; + + cfg = &(m_ctrl->phy->cfg); + + esc_clk_rate_hz = dsi_ctrl->clk_freq.esc_clk_rate * 1000; + pclk_to_esc_ratio = ((dsi_ctrl->clk_freq.pix_clk_rate * 1000) / + esc_clk_rate_hz); + byte_to_esc_ratio = ((dsi_ctrl->clk_freq.byte_clk_rate * 1000) / + esc_clk_rate_hz); + hr_bit_to_esc_ratio = ((dsi_ctrl->clk_freq.byte_clk_rate * 4 * 1000) / + esc_clk_rate_hz); + + hsync_period = DSI_H_TOTAL_DSC(&mode->timing); + delay->pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio; + if (!display->panel->video_config.eof_bllp_lp11_en) + delay->pipe_delay += (17 / pclk_to_esc_ratio) + + ((21 + (display->config.common_config.t_clk_pre + 1) + + (display->config.common_config.t_clk_post + 1)) / + byte_to_esc_ratio) + + ((((cfg->timing.lane_v3[8] >> 1) + 1) + + ((cfg->timing.lane_v3[6] >> 1) + 1) + + ((cfg->timing.lane_v3[3] * 4) + + (cfg->timing.lane_v3[5] >> 1) + 1) + + ((cfg->timing.lane_v3[7] >> 1) + 1) + + ((cfg->timing.lane_v3[1] >> 1) + 1) + + ((cfg->timing.lane_v3[4] >> 1) + 1)) / + hr_bit_to_esc_ratio); + + delay->pipe_delay2 = 0; + if (display->panel->host_config.force_hs_clk_lane) + delay->pipe_delay2 = (6 / byte_to_esc_ratio) + + ((((cfg->timing.lane_v3[1] >> 1) + 1) + + ((cfg->timing.lane_v3[4] >> 1) + 1)) / + hr_bit_to_esc_ratio); + + /* 130 us pll delay recommended by h/w doc */ + delay->pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2; +} + +static int _dsi_display_dyn_update_clks(struct dsi_display *display, + struct link_clk_freq *bkp_freq) +{ + int rc = 0, i; + struct dsi_display_ctrl *m_ctrl, *ctrl; + + m_ctrl = &display->ctrl[display->clk_master_idx]; + + dsi_clk_prepare_enable(&display->clock_info.src_clks); + + rc = dsi_clk_update_parent(&display->clock_info.shadow_clks, + &display->clock_info.mux_clks); + if (rc) { + pr_err("failed update mux parent to shadow\n"); + goto exit; + } + + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + rc = dsi_clk_set_byte_clk_rate(display->dsi_clk_handle, + ctrl->ctrl->clk_freq.byte_clk_rate, i); + if (rc) { + pr_err("failed to set byte rate for index:%d\n", i); + goto recover_byte_clk; + } + rc = dsi_clk_set_pixel_clk_rate(display->dsi_clk_handle, + ctrl->ctrl->clk_freq.pix_clk_rate, i); + if (rc) { + pr_err("failed to set pix rate for index:%d\n", i); + goto recover_pix_clk; + } + } + + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (ctrl == m_ctrl) + continue; + dsi_phy_dynamic_refresh_trigger(ctrl->phy, false); + } + dsi_phy_dynamic_refresh_trigger(m_ctrl->phy, true); + + /* wait for dynamic refresh done */ + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + rc = dsi_ctrl_wait4dynamic_refresh_done(ctrl->ctrl); + if (rc) { + pr_err("wait4dynamic refresh failed for dsi:%d\n", i); + goto recover_pix_clk; + } else { + pr_info("dynamic refresh done on dsi: %s\n", + i ? "slave" : "master"); + } + } + + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + dsi_phy_dynamic_refresh_clear(ctrl->phy); + } + + rc = dsi_clk_update_parent(&display->clock_info.src_clks, + &display->clock_info.mux_clks); + if (rc) + pr_err("could not switch back to src clks %d\n", rc); + + dsi_clk_disable_unprepare(&display->clock_info.src_clks); + + return rc; + +recover_pix_clk: + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + dsi_clk_set_pixel_clk_rate(display->dsi_clk_handle, + bkp_freq->pix_clk_rate, i); + } + +recover_byte_clk: + for (i = 0; (i < display->ctrl_count) && + (i < MAX_DSI_CTRLS_PER_DISPLAY); i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->ctrl) + continue; + dsi_clk_set_byte_clk_rate(display->dsi_clk_handle, + bkp_freq->byte_clk_rate, i); + } + +exit: + dsi_clk_disable_unprepare(&display->clock_info.src_clks); + + return rc; +} + +static int dsi_display_dynamic_clk_switch(struct dsi_display *display, + struct dsi_display_mode *mode) +{ + int rc = 0, mask, i; + struct dsi_display_ctrl *m_ctrl, *ctrl; + struct dsi_dyn_clk_delay delay; + struct link_clk_freq bkp_freq; + + dsi_panel_acquire_panel_lock(display->panel); + + m_ctrl = &display->ctrl[display->clk_master_idx]; + + dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON); + + /* mask PLL unlock, FIFO overflow and underflow errors */ + mask = BIT(DSI_PLL_UNLOCK_ERR) | BIT(DSI_FIFO_UNDERFLOW) | + BIT(DSI_FIFO_OVERFLOW); + dsi_display_mask_ctrl_error_interrupts(display, mask, true); + + /* update the phy timings based on new mode */ + for (i = 0; i < display->ctrl_count; i++) { + ctrl = &display->ctrl[i]; + dsi_phy_update_phy_timings(ctrl->phy, &display->config); + } + + /* back up existing rates to handle failure case */ + bkp_freq.byte_clk_rate = m_ctrl->ctrl->clk_freq.byte_clk_rate; + bkp_freq.pix_clk_rate = m_ctrl->ctrl->clk_freq.pix_clk_rate; + bkp_freq.esc_clk_rate = m_ctrl->ctrl->clk_freq.esc_clk_rate; + + rc = dsi_display_update_dsi_bitrate(display, mode->timing.clk_rate_hz); + if (rc) { + pr_err("failed set link frequencies %d\n", rc); + goto exit; + } + + /* calculate pipe delays */ + _dsi_display_calc_pipe_delay(display, &delay, mode); + + /* configure dynamic refresh ctrl registers */ + for (i = 0; i < display->ctrl_count; i++) { + ctrl = &display->ctrl[i]; + if (!ctrl->phy) + continue; + if (ctrl == m_ctrl) + dsi_phy_config_dynamic_refresh(ctrl->phy, &delay, true); + else + dsi_phy_config_dynamic_refresh(ctrl->phy, &delay, + false); + } + + rc = _dsi_display_dyn_update_clks(display, &bkp_freq); + +exit: + dsi_display_mask_ctrl_error_interrupts(display, mask, false); + + dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, + DSI_CLK_OFF); + + /* store newly calculated phy timings in mode private info */ + dsi_phy_dyn_refresh_cache_phy_timings(m_ctrl->phy, + mode->priv_info->phy_timing_val, + mode->priv_info->phy_timing_len); + + dsi_panel_release_panel_lock(display->panel); + + return rc; +} + static int dsi_display_dfps_update(struct dsi_display *display, struct dsi_display_mode *dsi_mode) { @@ -4031,6 +4354,16 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, display->name, rc); goto error; } + } else if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) { + rc = dsi_display_dynamic_clk_switch(display, mode); + if (rc) + pr_err("dynamic clk change failed %d\n", rc); + /* + * skip rest of the opearations since + * dsi_display_dynamic_clk_switch() already takes + * care of them. + */ + return rc; } display_for_each_ctrl(i, display) { @@ -4261,208 +4594,6 @@ static int dsi_display_force_update_dsi_clk(struct dsi_display *display) return rc; } -static int dsi_display_request_update_dsi_bitrate(struct dsi_display *display, - u32 bit_clk_rate) -{ - int rc = 0; - int i; - - pr_debug("%s:bit rate:%d\n", __func__, bit_clk_rate); - if (!display->panel) { - pr_err("Invalid params\n"); - return -EINVAL; - } - - if (bit_clk_rate == 0) { - pr_err("Invalid bit clock rate\n"); - return -EINVAL; - } - - display->config.bit_clk_rate_hz_override = bit_clk_rate; - - display_for_each_ctrl(i, display) { - struct dsi_display_ctrl *dsi_disp_ctrl = &display->ctrl[i]; - struct dsi_ctrl *ctrl = dsi_disp_ctrl->ctrl; - u32 num_of_lanes = 0; - u32 bpp = 3; - u64 bit_rate, pclk_rate, bit_rate_per_lane, byte_clk_rate; - struct dsi_host_common_cfg *host_cfg; - - mutex_lock(&ctrl->ctrl_lock); - - host_cfg = &display->panel->host_config; - if (host_cfg->data_lanes & DSI_DATA_LANE_0) - num_of_lanes++; - if (host_cfg->data_lanes & DSI_DATA_LANE_1) - num_of_lanes++; - if (host_cfg->data_lanes & DSI_DATA_LANE_2) - num_of_lanes++; - if (host_cfg->data_lanes & DSI_DATA_LANE_3) - num_of_lanes++; - - if (num_of_lanes == 0) { - pr_err("Invalid lane count\n"); - rc = -EINVAL; - goto error; - } - - bit_rate = display->config.bit_clk_rate_hz_override * - num_of_lanes; - bit_rate_per_lane = bit_rate; - do_div(bit_rate_per_lane, num_of_lanes); - pclk_rate = bit_rate; - do_div(pclk_rate, (8 * bpp)); - byte_clk_rate = bit_rate_per_lane; - do_div(byte_clk_rate, 8); - pr_debug("bit_clk_rate = %llu, bit_clk_rate_per_lane = %llu\n", - bit_rate, bit_rate_per_lane); - pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n", - byte_clk_rate, pclk_rate); - - ctrl->clk_freq.byte_clk_rate = byte_clk_rate; - ctrl->clk_freq.pix_clk_rate = pclk_rate; - rc = dsi_clk_set_link_frequencies(display->dsi_clk_handle, - ctrl->clk_freq, ctrl->cell_index); - if (rc) { - pr_err("Failed to update link frequencies\n"); - goto error; - } - - ctrl->host_config.bit_clk_rate_hz_override = bit_clk_rate; -error: - mutex_unlock(&ctrl->ctrl_lock); - - /* TODO: recover ctrl->clk_freq in case of failure */ - if (rc) - return rc; - } - - return 0; -} - -static ssize_t dynamic_dsi_clock_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int rc = 0; - struct dsi_display *display; - struct dsi_display_ctrl *m_ctrl; - struct dsi_ctrl *ctrl; - - display = dev_get_drvdata(dev); - if (!display) { - pr_err("Invalid display\n"); - return -EINVAL; - } - - mutex_lock(&display->display_lock); - - m_ctrl = &display->ctrl[display->cmd_master_idx]; - ctrl = m_ctrl->ctrl; - if (ctrl) - display->cached_clk_rate = ctrl->clk_freq.byte_clk_rate - * 8; - - rc = snprintf(buf, PAGE_SIZE, "%d\n", display->cached_clk_rate); - pr_debug("%s: read dsi clk rate %d\n", __func__, - display->cached_clk_rate); - - mutex_unlock(&display->display_lock); - - return rc; -} - -static ssize_t dynamic_dsi_clock_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int rc = 0; - int clk_rate; - struct dsi_display *display; - - display = dev_get_drvdata(dev); - if (!display) { - pr_err("Invalid display\n"); - return -EINVAL; - } - - rc = kstrtoint(buf, DSI_CLOCK_BITRATE_RADIX, &clk_rate); - if (rc) { - pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); - return rc; - } - - if (clk_rate <= 0) { - pr_err("%s: bitrate should be greater than 0\n", __func__); - return -EINVAL; - } - - if (clk_rate == display->cached_clk_rate) { - pr_info("%s: ignore duplicated DSI clk setting\n", __func__); - return count; - } - - pr_info("%s: bitrate param value: '%d'\n", __func__, clk_rate); - - mutex_lock(&display->display_lock); - - display->cached_clk_rate = clk_rate; - rc = dsi_display_request_update_dsi_bitrate(display, clk_rate); - if (!rc) { - pr_info("%s: bit clk is ready to be configured to '%d'\n", - __func__, clk_rate); - } else { - pr_err("%s: Failed to prepare to configure '%d'. rc = %d\n", - __func__, clk_rate, rc); - /*Caching clock failed, so don't go on doing so.*/ - atomic_set(&display->clkrate_change_pending, 0); - display->cached_clk_rate = 0; - - mutex_unlock(&display->display_lock); - - return rc; - } - atomic_set(&display->clkrate_change_pending, 1); - - mutex_unlock(&display->display_lock); - - return count; - -} - -static DEVICE_ATTR_RW(dynamic_dsi_clock); - -static struct attribute *dynamic_dsi_clock_fs_attrs[] = { - &dev_attr_dynamic_dsi_clock.attr, - NULL, -}; -static struct attribute_group dynamic_dsi_clock_fs_attrs_group = { - .attrs = dynamic_dsi_clock_fs_attrs, -}; - -static int dsi_display_sysfs_init(struct dsi_display *display) -{ - int rc = 0; - struct device *dev = &display->pdev->dev; - - if (display->panel->panel_mode == DSI_OP_CMD_MODE) - rc = sysfs_create_group(&dev->kobj, - &dynamic_dsi_clock_fs_attrs_group); - - return rc; - -} - -static int dsi_display_sysfs_deinit(struct dsi_display *display) -{ - struct device *dev = &display->pdev->dev; - - if (display->panel->panel_mode == DSI_OP_CMD_MODE) - sysfs_remove_group(&dev->kobj, - &dynamic_dsi_clock_fs_attrs_group); - - return 0; - -} - /** * dsi_display_bind - bind dsi device with controlling device * @dev: Pointer to base of platform device @@ -4514,12 +4645,6 @@ static int dsi_display_bind(struct device *dev, atomic_set(&display->clkrate_change_pending, 0); display->cached_clk_rate = 0; - rc = dsi_display_sysfs_init(display); - if (rc) { - pr_err("[%s] sysfs init failed, rc=%d\n", display->name, rc); - goto error; - } - memset(&info, 0x0, sizeof(info)); display_for_each_ctrl(i, display) { @@ -4665,7 +4790,6 @@ error_ctrl_deinit: (void)dsi_phy_drv_deinit(display_ctrl->phy); (void)dsi_ctrl_drv_deinit(display_ctrl->ctrl); } - (void)dsi_display_sysfs_deinit(display); (void)dsi_display_debugfs_deinit(display); error: mutex_unlock(&display->display_lock); @@ -4725,7 +4849,6 @@ static void dsi_display_unbind(struct device *dev, } atomic_set(&display->clkrate_change_pending, 0); - (void)dsi_display_sysfs_deinit(display); (void)dsi_display_debugfs_deinit(display); mutex_unlock(&display->display_lock); @@ -5480,7 +5603,8 @@ static int dsi_display_get_mode_count_no_lock(struct dsi_display *display, u32 *count) { struct dsi_dfps_capabilities dfps_caps; - int num_dfps_rates, rc = 0; + struct dsi_dyn_clk_caps *dyn_clk_caps; + int num_dfps_rates, num_bit_clks, rc = 0; if (!display || !display->panel) { pr_err("invalid display:%d panel:%d\n", display != NULL, @@ -5497,12 +5621,16 @@ static int dsi_display_get_mode_count_no_lock(struct dsi_display *display, return rc; } - num_dfps_rates = !dfps_caps.dfps_support ? 1 : - dfps_caps.max_refresh_rate - - dfps_caps.min_refresh_rate + 1; + num_dfps_rates = !dfps_caps.dfps_support ? 1 : dfps_caps.dfps_list_len; - /* Inflate num_of_modes by fps in dfps */ - *count = display->panel->num_timing_nodes * num_dfps_rates; + dyn_clk_caps = &(display->panel->dyn_clk_caps); + + num_bit_clks = !dyn_clk_caps->dyn_clk_support ? 1 : + dyn_clk_caps->bit_clk_list_len; + + /* Inflate num_of_modes by fps and bit clks in dfps */ + *count = display->panel->num_timing_nodes * + num_dfps_rates * num_bit_clks; return 0; } @@ -5525,6 +5653,73 @@ int dsi_display_get_mode_count(struct dsi_display *display, return 0; } +static void _dsi_display_populate_bit_clks(struct dsi_display *display, + int start, int end, u32 *mode_idx) +{ + struct dsi_dyn_clk_caps *dyn_clk_caps; + struct dsi_display_mode *src, *dst; + struct dsi_host_common_cfg *cfg; + int i, j, total_modes, bpp, lanes = 0; + + if (!display || !mode_idx) + return; + + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dyn_clk_caps->dyn_clk_support) + return; + + cfg = &(display->panel->host_config); + bpp = dsi_pixel_format_to_bpp(cfg->dst_format); + + if (cfg->data_lanes & DSI_DATA_LANE_0) + lanes++; + if (cfg->data_lanes & DSI_DATA_LANE_1) + lanes++; + if (cfg->data_lanes & DSI_DATA_LANE_2) + lanes++; + if (cfg->data_lanes & DSI_DATA_LANE_3) + lanes++; + + dsi_display_get_mode_count_no_lock(display, &total_modes); + + for (i = start; i < end; i++) { + src = &display->modes[i]; + if (!src) + return; + /* + * TODO: currently setting the first bit rate in + * the list as preferred rate. But ideally should + * be based on user or device tree preferrence. + */ + src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0]; + src->pixel_clk_khz = + div_u64(src->timing.clk_rate_hz * lanes, bpp); + src->pixel_clk_khz /= 1000; + src->pixel_clk_khz *= display->ctrl_count; + } + + for (i = 1; i < dyn_clk_caps->bit_clk_list_len; i++) { + if (*mode_idx >= total_modes) + return; + for (j = start; j < end; j++) { + src = &display->modes[j]; + dst = &display->modes[*mode_idx]; + + if (!src || !dst) { + pr_err("invalid mode index\n"); + return; + } + memcpy(dst, src, sizeof(struct dsi_display_mode)); + dst->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[i]; + dst->pixel_clk_khz = + div_u64(dst->timing.clk_rate_hz * lanes, bpp); + dst->pixel_clk_khz /= 1000; + dst->pixel_clk_khz *= display->ctrl_count; + (*mode_idx)++; + } + } +} + void dsi_display_put_mode(struct dsi_display *display, struct dsi_display_mode *mode) { @@ -5537,7 +5732,8 @@ int dsi_display_get_modes(struct dsi_display *display, struct dsi_dfps_capabilities dfps_caps; u32 num_dfps_rates, panel_mode_count, total_mode_count; u32 mode_idx, array_idx = 0; - int i, rc = -EINVAL; + struct dsi_dyn_clk_caps *dyn_clk_caps; + int i, start, end, rc = -EINVAL; if (!display || !out_modes) { pr_err("Invalid params\n"); @@ -5569,9 +5765,9 @@ int dsi_display_get_modes(struct dsi_display *display, goto error; } - num_dfps_rates = !dfps_caps.dfps_support ? 1 : - dfps_caps.max_refresh_rate - - dfps_caps.min_refresh_rate + 1; + dyn_clk_caps = &(display->panel->dyn_clk_caps); + + num_dfps_rates = !dfps_caps.dfps_support ? 1 : dfps_caps.dfps_list_len; panel_mode_count = display->panel->num_timing_nodes; @@ -5609,14 +5805,14 @@ int dsi_display_get_modes(struct dsi_display *display, panel_mode.timing.dsi_transfer_time_us; } - if (display->ctrl_count > 1) { /* TODO: remove if */ - panel_mode.timing.h_active *= display->ctrl_count; - panel_mode.timing.h_front_porch *= display->ctrl_count; - panel_mode.timing.h_sync_width *= display->ctrl_count; - panel_mode.timing.h_back_porch *= display->ctrl_count; - panel_mode.timing.h_skew *= display->ctrl_count; - panel_mode.pixel_clk_khz *= display->ctrl_count; - } + panel_mode.timing.h_active *= display->ctrl_count; + panel_mode.timing.h_front_porch *= display->ctrl_count; + panel_mode.timing.h_sync_width *= display->ctrl_count; + panel_mode.timing.h_back_porch *= display->ctrl_count; + panel_mode.timing.h_skew *= display->ctrl_count; + panel_mode.pixel_clk_khz *= display->ctrl_count; + + start = array_idx; for (i = 0; i < num_dfps_rates; i++) { struct dsi_display_mode *sub_mode = @@ -5630,24 +5826,23 @@ int dsi_display_get_modes(struct dsi_display *display, } memcpy(sub_mode, &panel_mode, sizeof(panel_mode)); - - if (dfps_caps.dfps_support) { - curr_refresh_rate = - sub_mode->timing.refresh_rate; - sub_mode->timing.refresh_rate = - dfps_caps.min_refresh_rate + - (i % num_dfps_rates); - - dsi_display_get_dfps_timing(display, - sub_mode, curr_refresh_rate); - - sub_mode->pixel_clk_khz = - (DSI_H_TOTAL_DSC(&sub_mode->timing) * - DSI_V_TOTAL(&sub_mode->timing) * - sub_mode->timing.refresh_rate) / 1000; - } array_idx++; + + if (!dfps_caps.dfps_support) + continue; + + curr_refresh_rate = sub_mode->timing.refresh_rate; + sub_mode->timing.refresh_rate = dfps_caps.dfps_list[i]; + + dsi_display_get_dfps_timing(display, sub_mode, + curr_refresh_rate); } + end = array_idx; + /* + * if dynamic clk switch is supported then update all the bit + * clk rates. + */ + _dsi_display_populate_bit_clks(display, start, end, &array_idx); } exit: @@ -5771,7 +5966,8 @@ int dsi_display_find_mode(struct dsi_display *display, if (cmp->timing.v_active == m->timing.v_active && cmp->timing.h_active == m->timing.h_active && cmp->timing.refresh_rate == m->timing.refresh_rate && - cmp->panel_mode == m->panel_mode) { + cmp->panel_mode == m->panel_mode && + cmp->pixel_clk_khz == m->pixel_clk_khz) { *out_mode = m; rc = 0; break; @@ -5780,9 +5976,10 @@ int dsi_display_find_mode(struct dsi_display *display, mutex_unlock(&display->display_lock); if (!*out_mode) { - pr_err("[%s] failed to find mode for v_active %u h_active %u rate %u\n", + pr_err("[%s] failed to find mode for v_active %u h_active %u fps %u pclk %u\n", display->name, cmp->timing.v_active, - cmp->timing.h_active, cmp->timing.refresh_rate); + cmp->timing.h_active, cmp->timing.refresh_rate, + cmp->pixel_clk_khz); rc = -ENOENT; } @@ -5790,7 +5987,7 @@ int dsi_display_find_mode(struct dsi_display *display, } /** - * dsi_display_validate_mode_vrr() - Validate if varaible refresh case. + * dsi_display_validate_mode_change() - Validate if varaible refresh case. * @display: DSI display handle. * @cur_dsi_mode: Current DSI mode. * @mode: Mode value structure to be validated. @@ -5798,16 +5995,15 @@ int dsi_display_find_mode(struct dsi_display *display, * is change in fps but vactive and hactive are same. * Return: error code. */ -int dsi_display_validate_mode_vrr(struct dsi_display *display, - struct dsi_display_mode *cur_dsi_mode, - struct dsi_display_mode *mode) +int dsi_display_validate_mode_change(struct dsi_display *display, + struct dsi_display_mode *cur_mode, + struct dsi_display_mode *adj_mode) { int rc = 0; - struct dsi_display_mode adj_mode, cur_mode; struct dsi_dfps_capabilities dfps_caps; - u32 curr_refresh_rate; + struct dsi_dyn_clk_caps *dyn_clk_caps; - if (!display || !mode) { + if (!display || !adj_mode) { pr_err("Invalid params\n"); return -EINVAL; } @@ -5819,65 +6015,43 @@ int dsi_display_validate_mode_vrr(struct dsi_display *display, mutex_lock(&display->display_lock); - adj_mode = *mode; - cur_mode = *cur_dsi_mode; - - if ((cur_mode.timing.refresh_rate != adj_mode.timing.refresh_rate) && - (cur_mode.timing.v_active == adj_mode.timing.v_active) && - (cur_mode.timing.h_active == adj_mode.timing.h_active)) { - - curr_refresh_rate = cur_mode.timing.refresh_rate; - rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps); - if (rc) { - pr_err("[%s] failed to get dfps caps from panel\n", - display->name); - goto error; + if ((cur_mode->timing.v_active == adj_mode->timing.v_active) && + (cur_mode->timing.h_active == adj_mode->timing.h_active)) { + /* dfps change use case */ + if (cur_mode->timing.refresh_rate != + adj_mode->timing.refresh_rate) { + dsi_panel_get_dfps_caps(display->panel, &dfps_caps); + if (!dfps_caps.dfps_support) { + pr_err("invalid mode dfps not supported\n"); + rc = -ENOTSUPP; + goto error; + } + pr_debug("Mode switch is seamless variable refresh\n"); + adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; + SDE_EVT32(cur_mode->timing.refresh_rate, + adj_mode->timing.refresh_rate, + cur_mode->timing.h_front_porch, + adj_mode->timing.h_front_porch); } - cur_mode.timing.refresh_rate = - adj_mode.timing.refresh_rate; - - rc = dsi_display_get_dfps_timing(display, - &cur_mode, curr_refresh_rate); - if (rc) { - pr_err("[%s] seamless vrr not possible rc=%d\n", - display->name, rc); - goto error; + /* dynamic clk change use case */ + if (cur_mode->pixel_clk_khz != adj_mode->pixel_clk_khz) { + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dyn_clk_caps->dyn_clk_support) { + pr_err("dyn clk change not supported\n"); + rc = -ENOTSUPP; + goto error; + } + if (adj_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) { + pr_err("dfps and dyn clk not supported in same commit\n"); + rc = -ENOTSUPP; + goto error; + } + pr_debug("dynamic clk change detected\n"); + adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_DYN_CLK; + SDE_EVT32(cur_mode->pixel_clk_khz, + adj_mode->pixel_clk_khz); } - switch (dfps_caps.type) { - /* - * Ignore any round off factors in porch calculation. - * Worse case is set to 5. - */ - case DSI_DFPS_IMMEDIATE_VFP: - if (abs(DSI_V_TOTAL(&cur_mode.timing) - - DSI_V_TOTAL(&adj_mode.timing)) > 5) - pr_err("Mismatch vfp fps:%d new:%d given:%d\n", - adj_mode.timing.refresh_rate, - cur_mode.timing.v_front_porch, - adj_mode.timing.v_front_porch); - break; - - case DSI_DFPS_IMMEDIATE_HFP: - if (abs(DSI_H_TOTAL_DSC(&cur_mode.timing) - - DSI_H_TOTAL_DSC(&adj_mode.timing)) > 5) - pr_err("Mismatch hfp fps:%d new:%d given:%d\n", - adj_mode.timing.refresh_rate, - cur_mode.timing.h_front_porch, - adj_mode.timing.h_front_porch); - break; - - default: - pr_err("Unsupported DFPS mode %d\n", - dfps_caps.type); - rc = -ENOTSUPP; - } - - pr_debug("Mode switch is seamless variable refresh\n"); - mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; - SDE_EVT32(curr_refresh_rate, adj_mode.timing.refresh_rate, - cur_mode.timing.h_front_porch, - adj_mode.timing.h_front_porch); } error: diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index 9a4740c469..29dab31095 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation.All rights reserved. + * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. */ #ifndef _DSI_DISPLAY_H_ @@ -410,13 +410,14 @@ int dsi_display_validate_mode(struct dsi_display *display, u32 flags); /** - * dsi_display_validate_mode_vrr() - validates mode if variable refresh case + * dsi_display_validate_mode_change() - validates mode if variable refresh case + * or dynamic clk change case * @display: Handle to display. * @mode: Mode to be validated.. * * Return: 0 if error code. */ -int dsi_display_validate_mode_vrr(struct dsi_display *display, +int dsi_display_validate_mode_change(struct dsi_display *display, struct dsi_display_mode *cur_dsi_mode, struct dsi_display_mode *mode); diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 6d5c25887f..decf9aa658 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -73,6 +73,8 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode, dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; if (msm_is_mode_seamless_poms(drm_mode)) dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_POMS; + if (msm_is_mode_seamless_dyn_clk(drm_mode)) + dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DYN_CLK; dsi_mode->timing.h_sync_polarity = !!(drm_mode->flags & DRM_MODE_FLAG_PHSYNC); @@ -88,6 +90,8 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode, void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, struct drm_display_mode *drm_mode) { + bool video_mode = (dsi_mode->panel_mode == DSI_OP_VIDEO_MODE); + memset(drm_mode, 0, sizeof(*drm_mode)); drm_mode->hdisplay = dsi_mode->timing.h_active; @@ -122,6 +126,8 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_VRR; if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_POMS; + if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) + drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DYN_CLK; if (dsi_mode->timing.h_sync_polarity) drm_mode->flags |= DRM_MODE_FLAG_PHSYNC; @@ -133,7 +139,11 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, if (dsi_mode->panel_mode == DSI_OP_CMD_MODE) drm_mode->flags |= DRM_MODE_FLAG_CMD_MODE_PANEL; - drm_mode_set_name(drm_mode); + /* set mode name */ + snprintf(drm_mode->name, DRM_DISPLAY_MODE_LEN, "%dx%dx%dx%d%s", + drm_mode->hdisplay, drm_mode->vdisplay, + drm_mode->vrefresh, drm_mode->clock, + video_mode ? "vid" : "cmd"); } static int dsi_bridge_attach(struct drm_bridge *bridge) @@ -178,7 +188,8 @@ static void dsi_bridge_pre_enable(struct drm_bridge *bridge) } if (c_bridge->dsi_mode.dsi_mode_flags & - (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR)) { + (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR | + DSI_MODE_FLAG_DYN_CLK)) { pr_debug("[%d] seamless pre-enable\n", c_bridge->id); return; } @@ -311,6 +322,12 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode)); convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode)); + + /* restore bit_clk_rate also for dynamic clk use cases */ + c_bridge->dsi_mode.timing.clk_rate_hz = + dsi_drm_find_bit_clk_rate(c_bridge->display, adjusted_mode); + + pr_debug("clk_rate: %llu\n", c_bridge->dsi_mode.timing.clk_rate_hz); } static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, @@ -379,11 +396,13 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, cur_dsi_mode.timing.dsc_enabled = dsi_mode.priv_info->dsc_enabled; cur_dsi_mode.timing.dsc = &dsi_mode.priv_info->dsc; - rc = dsi_display_validate_mode_vrr(c_bridge->display, + rc = dsi_display_validate_mode_change(c_bridge->display, &cur_dsi_mode, &dsi_mode); - if (rc) - pr_debug("[%s] vrr mode mismatch failure rc=%d\n", + if (rc) { + pr_err("[%s] seamless mode mismatch failure rc=%d\n", c_bridge->display->name, rc); + return false; + } cur_mode = crtc_state->crtc->mode; @@ -397,6 +416,7 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, if (!drm_mode_equal(&cur_mode, adjusted_mode) && (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) && (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_POMS)) && + (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK)) && (!crtc_state->active_changed || display->is_cont_splash_enabled)) dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS; @@ -408,6 +428,33 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, return true; } +u64 dsi_drm_find_bit_clk_rate(void *display, + const struct drm_display_mode *drm_mode) +{ + int i = 0, count = 0; + struct dsi_display *dsi_display = display; + struct dsi_display_mode *dsi_mode; + u64 bit_clk_rate = 0; + + if (!dsi_display || !drm_mode) + return 0; + + dsi_display_get_mode_count(dsi_display, &count); + + for (i = 0; i < count; i++) { + dsi_mode = &dsi_display->modes[i]; + if ((dsi_mode->timing.v_active == drm_mode->vdisplay) && + (dsi_mode->timing.h_active == drm_mode->hdisplay) && + (dsi_mode->pixel_clk_khz == drm_mode->clock) && + (dsi_mode->timing.refresh_rate == drm_mode->vrefresh)) { + bit_clk_rate = dsi_mode->timing.clk_rate_hz; + break; + } + } + + return bit_clk_rate; +} + int dsi_conn_get_mode_info(struct drm_connector *connector, const struct drm_display_mode *drm_mode, struct msm_mode_info *mode_info, @@ -432,7 +479,7 @@ int dsi_conn_get_mode_info(struct drm_connector *connector, mode_info->prefill_lines = dsi_mode.priv_info->panel_prefill_lines; mode_info->jitter_numer = dsi_mode.priv_info->panel_jitter_numer; mode_info->jitter_denom = dsi_mode.priv_info->panel_jitter_denom; - mode_info->clk_rate = dsi_mode.priv_info->clk_rate_hz; + mode_info->clk_rate = dsi_drm_find_bit_clk_rate(display, drm_mode); mode_info->mdp_transfer_time_us = dsi_mode.priv_info->mdp_transfer_time_us; @@ -538,6 +585,9 @@ int dsi_conn_set_info_blob(struct drm_connector *connector, panel->dfps_caps.max_refresh_rate); } + sde_kms_info_add_keystr(info, "dyn bitclk support", + panel->dyn_clk_caps.dyn_clk_support ? "true" : "false"); + switch (panel->phy_props.rotation) { case DSI_PANEL_ROTATE_NONE: sde_kms_info_add_keystr(info, "panel orientation", "none"); @@ -796,6 +846,9 @@ int dsi_connector_get_modes(struct drm_connector *connector, void *data) } m->width_mm = connector->display_info.width_mm; m->height_mm = connector->display_info.height_mm; + /* set the first mode in list as preferred */ + if (i == 0) + m->type |= DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, m); } @@ -915,6 +968,9 @@ int dsi_conn_post_kickoff(struct drm_connector *connector) c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_VRR; } + /* ensure dynamic clk switch flag is reset */ + c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_DYN_CLK; + return 0; } diff --git a/msm/dsi/dsi_drm.h b/msm/dsi/dsi_drm.h index d76b8c4c2c..8b0b135d72 100644 --- a/msm/dsi/dsi_drm.h +++ b/msm/dsi/dsi_drm.h @@ -130,4 +130,6 @@ int dsi_conn_post_kickoff(struct drm_connector *connector); void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode, struct drm_display_mode *drm_mode); +u64 dsi_drm_find_bit_clk_rate(void *display, + const struct drm_display_mode *drm_mode); #endif /* _DSI_DRM_H_ */ diff --git a/msm/dsi/dsi_hw.h b/msm/dsi/dsi_hw.h index cd2173c095..cf849a4ae7 100644 --- a/msm/dsi/dsi_hw.h +++ b/msm/dsi/dsi_hw.h @@ -44,4 +44,14 @@ #define DSI_R64(dsi_hw, off) readq_relaxed((dsi_hw)->base + (off)) #define DSI_W64(dsi_hw, off, val) writeq_relaxed((val), (dsi_hw)->base + (off)) +#define PLL_CALC_DATA(addr0, addr1, data0, data1) \ + (((data1) << 24) | ((((addr1)/4) & 0xFF) << 16) | \ + ((data0) << 8) | (((addr0)/4) & 0xFF)) + +#define DSI_DYN_REF_REG_W(base, offset, addr0, addr1, data0, data1) \ + writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \ + (base) + (offset)) + +#define DSI_GEN_R32(base, offset) readl_relaxed(base + (offset)) +#define DSI_GEN_W32(base, offset, val) writel_relaxed((val), base + (offset)) #endif /* _DSI_HW_H_ */ diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 8e6386a960..77ede1015d 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1155,6 +1155,48 @@ static int dsi_panel_parse_qsync_caps(struct dsi_panel *panel, return rc; } +static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) +{ + int rc = 0; + bool supported = false; + struct dsi_dyn_clk_caps *dyn_clk_caps = &panel->dyn_clk_caps; + struct dsi_parser_utils *utils = &panel->utils; + const char *name = panel->name; + + supported = utils->read_bool(utils->data, "qcom,dsi-dyn-clk-enable"); + + if (!supported) { + dyn_clk_caps->dyn_clk_support = false; + return rc; + } + + dyn_clk_caps->bit_clk_list_len = utils->count_u32_elems(utils->data, + "qcom,dsi-dyn-clk-list"); + + if (dyn_clk_caps->bit_clk_list_len < 1) { + pr_err("[%s] failed to get supported bit clk list\n", name); + return -EINVAL; + } + + dyn_clk_caps->bit_clk_list = kcalloc(dyn_clk_caps->bit_clk_list_len, + sizeof(u32), GFP_KERNEL); + if (!dyn_clk_caps->bit_clk_list) + return -ENOMEM; + + rc = utils->read_u32_array(utils->data, "qcom,dsi-dyn-clk-list", + dyn_clk_caps->bit_clk_list, + dyn_clk_caps->bit_clk_list_len); + + if (rc) { + pr_err("[%s] failed to parse supported bit clk list\n", name); + return -EINVAL; + } + + dyn_clk_caps->dyn_clk_support = true; + + return 0; +} + static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) { int rc = 0; @@ -1163,7 +1205,7 @@ static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) struct dsi_parser_utils *utils = &panel->utils; const char *name = panel->name; const char *type; - u32 val = 0; + u32 i; supported = utils->read_bool(utils->data, "qcom,mdss-dsi-pan-enable-dynamic-fps"); @@ -1171,66 +1213,64 @@ static int dsi_panel_parse_dfps_caps(struct dsi_panel *panel) if (!supported) { pr_debug("[%s] DFPS is not supported\n", name); dfps_caps->dfps_support = false; + return rc; + } + + type = utils->get_property(utils->data, + "qcom,mdss-dsi-pan-fps-update", NULL); + if (!type) { + pr_err("[%s] dfps type not defined\n", name); + rc = -EINVAL; + goto error; + } else if (!strcmp(type, "dfps_suspend_resume_mode")) { + dfps_caps->type = DSI_DFPS_SUSPEND_RESUME; + } else if (!strcmp(type, "dfps_immediate_clk_mode")) { + dfps_caps->type = DSI_DFPS_IMMEDIATE_CLK; + } else if (!strcmp(type, "dfps_immediate_porch_mode_hfp")) { + dfps_caps->type = DSI_DFPS_IMMEDIATE_HFP; + } else if (!strcmp(type, "dfps_immediate_porch_mode_vfp")) { + dfps_caps->type = DSI_DFPS_IMMEDIATE_VFP; } else { + pr_err("[%s] dfps type is not recognized\n", name); + rc = -EINVAL; + goto error; + } - type = utils->get_property(utils->data, - "qcom,mdss-dsi-pan-fps-update", - NULL); - if (!type) { - pr_err("[%s] dfps type not defined\n", name); - rc = -EINVAL; - goto error; - } else if (!strcmp(type, "dfps_suspend_resume_mode")) { - dfps_caps->type = DSI_DFPS_SUSPEND_RESUME; - } else if (!strcmp(type, "dfps_immediate_clk_mode")) { - dfps_caps->type = DSI_DFPS_IMMEDIATE_CLK; - } else if (!strcmp(type, "dfps_immediate_porch_mode_hfp")) { - dfps_caps->type = DSI_DFPS_IMMEDIATE_HFP; - } else if (!strcmp(type, "dfps_immediate_porch_mode_vfp")) { - dfps_caps->type = DSI_DFPS_IMMEDIATE_VFP; - } else { - pr_err("[%s] dfps type is not recognized\n", name); - rc = -EINVAL; - goto error; - } + dfps_caps->dfps_list_len = utils->count_u32_elems(utils->data, + "qcom,dsi-supported-dfps-list"); + if (dfps_caps->dfps_list_len < 1) { + pr_err("[%s] dfps refresh list not present\n", name); + rc = -EINVAL; + goto error; + } - rc = utils->read_u32(utils->data, - "qcom,mdss-dsi-min-refresh-rate", - &val); - if (rc) { - pr_err("[%s] Min refresh rate is not defined\n", name); - rc = -EINVAL; - goto error; - } - dfps_caps->min_refresh_rate = val; + dfps_caps->dfps_list = kcalloc(dfps_caps->dfps_list_len, sizeof(u32), + GFP_KERNEL); + if (!dfps_caps->dfps_list) { + rc = -ENOMEM; + goto error; + } - rc = utils->read_u32(utils->data, - "qcom,mdss-dsi-max-refresh-rate", - &val); - if (rc) { - pr_debug("[%s] Using default refresh rate\n", name); - rc = utils->read_u32(utils->data, - "qcom,mdss-dsi-panel-framerate", - &val); - if (rc) { - pr_err("[%s] max refresh rate is not defined\n", - name); - rc = -EINVAL; - goto error; - } - } - dfps_caps->max_refresh_rate = val; + rc = utils->read_u32_array(utils->data, + "qcom,dsi-supported-dfps-list", + dfps_caps->dfps_list, + dfps_caps->dfps_list_len); + if (rc) { + pr_err("[%s] dfps refresh rate list parse failed\n", name); + rc = -EINVAL; + goto error; + } + dfps_caps->dfps_support = true; - if (dfps_caps->min_refresh_rate > dfps_caps->max_refresh_rate) { - pr_err("[%s] min rate > max rate\n", name); - rc = -EINVAL; - } + /* calculate max and min fps */ + dfps_caps->max_refresh_rate = dfps_caps->dfps_list[0]; + dfps_caps->min_refresh_rate = dfps_caps->dfps_list[0]; - pr_debug("[%s] DFPS is supported %d-%d, mode %d\n", name, - dfps_caps->min_refresh_rate, - dfps_caps->max_refresh_rate, - dfps_caps->type); - dfps_caps->dfps_support = true; + for (i = 1; i < dfps_caps->dfps_list_len; i++) { + if (dfps_caps->dfps_list[i] < dfps_caps->min_refresh_rate) + dfps_caps->min_refresh_rate = dfps_caps->dfps_list[i]; + else if (dfps_caps->dfps_list[i] > dfps_caps->max_refresh_rate) + dfps_caps->max_refresh_rate = dfps_caps->dfps_list[i]; } error: @@ -3053,6 +3093,13 @@ struct dsi_panel *dsi_panel_get(struct device *parent, pr_err("failed to parse qsync features, rc=%d\n", rc); } + if (panel->panel_mode == DSI_OP_VIDEO_MODE) { + rc = dsi_panel_parse_dyn_clk_caps(panel); + if (rc) + pr_err("failed to parse dynamic clk config, rc=%d\n", + rc); + } + rc = dsi_panel_parse_phy_props(panel); if (rc) { pr_err("failed to parse panel physical dimension, rc=%d\n", rc); diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index f9ad4c257f..0348a71889 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -63,10 +63,18 @@ enum dsi_dms_mode { }; struct dsi_dfps_capabilities { - bool dfps_support; enum dsi_dfps_type type; u32 min_refresh_rate; u32 max_refresh_rate; + u32 *dfps_list; + u32 dfps_list_len; + bool dfps_support; +}; + +struct dsi_dyn_clk_caps { + bool dyn_clk_support; + u32 *bit_clk_list; + u32 bit_clk_list_len; }; struct dsi_pinctrl_info { @@ -159,6 +167,7 @@ struct dsi_panel { bool panel_mode_switch_enabled; struct dsi_dfps_capabilities dfps_caps; + struct dsi_dyn_clk_caps dyn_clk_caps; struct dsi_panel_phy_props phy_props; struct dsi_display_mode *cur_mode; diff --git a/msm/dsi/dsi_phy.c b/msm/dsi/dsi_phy.c index 4759b3d230..21755ec337 100644 --- a/msm/dsi/dsi_phy.c +++ b/msm/dsi/dsi_phy.c @@ -118,6 +118,9 @@ static int dsi_phy_regmap_init(struct platform_device *pdev, phy->hw.base = ptr; + ptr = msm_ioremap(pdev, "dyn_refresh_base", phy->name); + phy->hw.dyn_pll_base = ptr; + pr_debug("[%s] map dsi_phy registers to %pK\n", phy->name, phy->hw.base); @@ -641,11 +644,8 @@ int dsi_phy_validate_mode(struct msm_dsi_phy *dsi_phy, return -EINVAL; } - mutex_lock(&dsi_phy->phy_lock); - pr_debug("[PHY_%d] Skipping validation\n", dsi_phy->index); - mutex_unlock(&dsi_phy->phy_lock); return rc; } @@ -887,7 +887,7 @@ int dsi_phy_enable(struct msm_dsi_phy *phy, rc = phy->hw.ops.calculate_timing_params(&phy->hw, &phy->mode, &config->common_config, - &phy->cfg.timing); + &phy->cfg.timing, false); if (rc) { pr_err("[%s] failed to set timing, rc=%d\n", phy->name, rc); goto error; @@ -905,6 +905,27 @@ error: return rc; } +/* update dsi phy timings for dynamic clk switch use case */ +int dsi_phy_update_phy_timings(struct msm_dsi_phy *phy, + struct dsi_host_config *config) +{ + int rc = 0; + + if (!phy || !config) { + pr_err("invalid argument\n"); + return -EINVAL; + } + + memcpy(&phy->mode, &config->video_timing, sizeof(phy->mode)); + rc = phy->hw.ops.calculate_timing_params(&phy->hw, &phy->mode, + &config->common_config, + &phy->cfg.timing, true); + if (rc) + pr_err("failed to calculate phy timings %d\n", rc); + + return rc; +} + int dsi_phy_lane_reset(struct msm_dsi_phy *phy) { int ret = 0; @@ -1069,6 +1090,7 @@ int dsi_phy_set_timing_params(struct msm_dsi_phy *phy, rc = phy->hw.ops.phy_timing_val(&phy->cfg.timing, timing, size); if (!rc) phy->cfg.is_phy_timing_present = true; + mutex_unlock(&phy->phy_lock); return rc; } @@ -1117,6 +1139,106 @@ int dsi_phy_conv_logical_to_phy_lane( return i; } + +/** + * dsi_phy_config_dynamic_refresh() - Configure dynamic refresh registers + * @phy: DSI PHY handle + * @delay: pipe delays for dynamic refresh + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_config_dynamic_refresh(struct msm_dsi_phy *phy, + struct dsi_dyn_clk_delay *delay, + bool is_master) +{ + struct dsi_phy_cfg *cfg; + + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + + cfg = &phy->cfg; + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_config) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_config(&phy->hw, cfg, + is_master); + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_pipe_delay) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_pipe_delay( + &phy->hw, delay); + + mutex_unlock(&phy->phy_lock); +} + +/** + * dsi_phy_dynamic_refresh_trigger() - trigger dynamic refresh + * @phy: DSI PHY handle + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_dynamic_refresh_trigger(struct msm_dsi_phy *phy, bool is_master) +{ + u32 off; + + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + /* + * program PLL_SWI_INTF_SEL and SW_TRIGGER bit only for + * master and program SYNC_MODE bit only for slave. + */ + if (is_master) + off = BIT(DYN_REFRESH_INTF_SEL) | BIT(DYN_REFRESH_SWI_CTRL) | + BIT(DYN_REFRESH_SW_TRIGGER); + else + off = BIT(DYN_REFRESH_SYNC_MODE) | BIT(DYN_REFRESH_SWI_CTRL); + + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper(&phy->hw, off); + + mutex_unlock(&phy->phy_lock); +} + +/** + * dsi_phy_cache_phy_timings - cache the phy timings calculated as part of + * dynamic refresh. + * @phy: DSI PHY Handle. + * @dst: Pointer to cache location. + * @size: Number of phy lane settings. + */ +int dsi_phy_dyn_refresh_cache_phy_timings(struct msm_dsi_phy *phy, u32 *dst, + u32 size) +{ + int rc = 0; + + if (!phy || !dst || !size) + return -EINVAL; + + if (phy->hw.ops.dyn_refresh_ops.cache_phy_timings) + rc = phy->hw.ops.dyn_refresh_ops.cache_phy_timings( + &phy->cfg.timing, dst, size); + + if (rc) + pr_err("failed to cache phy timings %d\n", rc); + + return rc; +} + +/** + * dsi_phy_dynamic_refresh_clear() - clear dynamic refresh config + * @phy: DSI PHY handle + */ +void dsi_phy_dynamic_refresh_clear(struct msm_dsi_phy *phy) +{ + if (!phy) + return; + + mutex_lock(&phy->phy_lock); + + if (phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper) + phy->hw.ops.dyn_refresh_ops.dyn_refresh_helper(&phy->hw, 0); + + mutex_unlock(&phy->phy_lock); +} + void dsi_phy_drv_register(void) { platform_driver_register(&dsi_phy_platform_driver); diff --git a/msm/dsi/dsi_phy.h b/msm/dsi/dsi_phy.h index 875f2a9ed9..5e86d1f308 100644 --- a/msm/dsi/dsi_phy.h +++ b/msm/dsi/dsi_phy.h @@ -290,4 +290,45 @@ void dsi_phy_drv_register(void); */ void dsi_phy_drv_unregister(void); +/** + * dsi_phy_update_phy_timings() - Update dsi phy timings + * @phy: DSI PHY handle + * @config: DSI Host config parameters + * + * Return: error code. + */ +int dsi_phy_update_phy_timings(struct msm_dsi_phy *phy, + struct dsi_host_config *config); + +/** + * dsi_phy_config_dynamic_refresh() - Configure dynamic refresh registers + * @phy: DSI PHY handle + * @delay: pipe delays for dynamic refresh + * @is_master: Boolean to indicate if for master or slave + */ +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() - trigger dynamic refresh + * @phy: DSI PHY handle + * @is_master: Boolean to indicate if for master or slave. + */ +void dsi_phy_dynamic_refresh_trigger(struct msm_dsi_phy *phy, bool is_master); + +/** + * dsi_phy_dynamic_refresh_clear() - clear dynamic refresh config + * @phy: DSI PHY handle + */ +void dsi_phy_dynamic_refresh_clear(struct msm_dsi_phy *phy); + +/** + * dsi_phy_dyn_refresh_cache_phy_timings - cache the phy timings calculated + * as part of dynamic refresh. + * @phy: DSI PHY Handle. + * @dst: Pointer to cache location. + * @size: Number of phy lane settings. + */ +int dsi_phy_dyn_refresh_cache_phy_timings(struct msm_dsi_phy *phy, + u32 *dst, u32 size); #endif /* _DSI_PHY_H_ */ diff --git a/msm/dsi/dsi_phy_hw.h b/msm/dsi/dsi_phy_hw.h index 701435a488..7f58f4b80e 100644 --- a/msm/dsi/dsi_phy_hw.h +++ b/msm/dsi/dsi_phy_hw.h @@ -161,6 +161,43 @@ struct phy_ulps_config_ops { bool (*is_lanes_in_ulps)(u32 ulps, u32 ulps_lanes); }; +struct phy_dyn_refresh_ops { + /** + * dyn_refresh_helper - helper function to config particular registers + * @phy: Pointer to DSI PHY hardware instance. + * @offset: register offset to program. + */ + void (*dyn_refresh_helper)(struct dsi_phy_hw *phy, u32 offset); + + /** + * dyn_refresh_config - configure dynamic refresh ctrl registers + * @phy: Pointer to DSI PHY hardware instance. + * @cfg: Pointer to DSI PHY timings. + * @is_master: Boolean to indicate whether for master or slave. + */ + void (*dyn_refresh_config)(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master); + + /** + * dyn_refresh_pipe_delay - configure pipe delay registers for dynamic + * refresh. + * @phy: Pointer to DSI PHY hardware instance. + * @delay: structure containing all the delays to be programed. + */ + void (*dyn_refresh_pipe_delay)(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay); + + /** + * cache_phy_timings - cache the phy timings calculated as part of + * dynamic refresh. + * @timings: Pointer to calculated phy timing parameters. + * @dst: Pointer to cache location. + * @size: Number of phy lane settings. + */ + int (*cache_phy_timings)(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size); +}; + /** * struct dsi_phy_hw_ops - Operations for DSI PHY hardware. * @regulator_enable: Enable PHY regulators. @@ -220,11 +257,14 @@ struct dsi_phy_hw_ops { * @mode: Mode information for which timing has to be calculated. * @config: DSI host configuration for this mode. * @timing: Timing parameters for each lane which will be returned. + * @use_mode_bit_clk: Boolean to indicate whether reacalculate dsi + * bitclk or use the existing bitclk(for dynamic clk case). */ int (*calculate_timing_params)(struct dsi_phy_hw *phy, struct dsi_mode_info *mode, struct dsi_host_common_cfg *config, - struct dsi_phy_per_lane_cfgs *timing); + struct dsi_phy_per_lane_cfgs *timing, + bool use_mode_bit_clk); /** * phy_timing_val() - Gets PHY timing values. @@ -265,12 +305,15 @@ struct dsi_phy_hw_ops { void *timing_ops; struct phy_ulps_config_ops ulps_ops; + struct phy_dyn_refresh_ops dyn_refresh_ops; }; /** * struct dsi_phy_hw - DSI phy hardware object specific to an instance * @base: VA for the DSI PHY base address. * @length: Length of the DSI PHY register base map. + * @dyn_pll_base: VA for the DSI dynamic refresh base address. + * @length: Length of the DSI dynamic refresh register base map. * @index: Instance ID of the controller. * @version: DSI PHY version. * @phy_clamp_base: Base address of phy clamp register map. @@ -280,6 +323,8 @@ struct dsi_phy_hw_ops { struct dsi_phy_hw { void __iomem *base; u32 length; + void __iomem *dyn_pll_base; + u32 dyn_refresh_len; u32 index; enum dsi_phy_version version; diff --git a/msm/dsi/dsi_phy_hw_v3_0.c b/msm/dsi/dsi_phy_hw_v3_0.c index 19e1e9d215..29c6bb33e3 100644 --- a/msm/dsi/dsi_phy_hw_v3_0.c +++ b/msm/dsi/dsi_phy_hw_v3_0.c @@ -58,6 +58,47 @@ #define DSIPHY_LNX_LPRX_CTRL(n) (0x228 + (0x80 * (n))) #define DSIPHY_LNX_TX_DCTRL(n) (0x22C + (0x80 * (n))) +/* dynamic refresh control registers */ +#define DSI_DYN_REFRESH_CTRL (0x000) +#define DSI_DYN_REFRESH_PIPE_DELAY (0x004) +#define DSI_DYN_REFRESH_PIPE_DELAY2 (0x008) +#define DSI_DYN_REFRESH_PLL_DELAY (0x00C) +#define DSI_DYN_REFRESH_STATUS (0x010) +#define DSI_DYN_REFRESH_PLL_CTRL0 (0x014) +#define DSI_DYN_REFRESH_PLL_CTRL1 (0x018) +#define DSI_DYN_REFRESH_PLL_CTRL2 (0x01C) +#define DSI_DYN_REFRESH_PLL_CTRL3 (0x020) +#define DSI_DYN_REFRESH_PLL_CTRL4 (0x024) +#define DSI_DYN_REFRESH_PLL_CTRL5 (0x028) +#define DSI_DYN_REFRESH_PLL_CTRL6 (0x02C) +#define DSI_DYN_REFRESH_PLL_CTRL7 (0x030) +#define DSI_DYN_REFRESH_PLL_CTRL8 (0x034) +#define DSI_DYN_REFRESH_PLL_CTRL9 (0x038) +#define DSI_DYN_REFRESH_PLL_CTRL10 (0x03C) +#define DSI_DYN_REFRESH_PLL_CTRL11 (0x040) +#define DSI_DYN_REFRESH_PLL_CTRL12 (0x044) +#define DSI_DYN_REFRESH_PLL_CTRL13 (0x048) +#define DSI_DYN_REFRESH_PLL_CTRL14 (0x04C) +#define DSI_DYN_REFRESH_PLL_CTRL15 (0x050) +#define DSI_DYN_REFRESH_PLL_CTRL16 (0x054) +#define DSI_DYN_REFRESH_PLL_CTRL17 (0x058) +#define DSI_DYN_REFRESH_PLL_CTRL18 (0x05C) +#define DSI_DYN_REFRESH_PLL_CTRL19 (0x060) +#define DSI_DYN_REFRESH_PLL_CTRL20 (0x064) +#define DSI_DYN_REFRESH_PLL_CTRL21 (0x068) +#define DSI_DYN_REFRESH_PLL_CTRL22 (0x06C) +#define DSI_DYN_REFRESH_PLL_CTRL23 (0x070) +#define DSI_DYN_REFRESH_PLL_CTRL24 (0x074) +#define DSI_DYN_REFRESH_PLL_CTRL25 (0x078) +#define DSI_DYN_REFRESH_PLL_CTRL26 (0x07C) +#define DSI_DYN_REFRESH_PLL_CTRL27 (0x080) +#define DSI_DYN_REFRESH_PLL_CTRL28 (0x084) +#define DSI_DYN_REFRESH_PLL_CTRL29 (0x088) +#define DSI_DYN_REFRESH_PLL_CTRL30 (0x08C) +#define DSI_DYN_REFRESH_PLL_CTRL31 (0x090) +#define DSI_DYN_REFRESH_PLL_UPPER_ADDR (0x094) +#define DSI_DYN_REFRESH_PLL_UPPER_ADDR2 (0x098) + /** * regulator_enable() - enable regulators for DSI PHY * @phy: Pointer to DSI PHY hardware object. @@ -462,3 +503,163 @@ int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg, timing_cfg->lane_v3[i] = timing_val[i]; return 0; } + +void dsi_phy_hw_v3_0_dyn_refresh_config(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg, bool is_master) +{ + u32 reg; + + if (is_master) { + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL9, + DSIPHY_CMN_GLBL_CTRL, DSIPHY_CMN_VREG_CTRL, + 0x10, 0x59); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL10, + DSIPHY_CMN_TIMING_CTRL_0, DSIPHY_CMN_TIMING_CTRL_1, + cfg->timing.lane_v3[0], cfg->timing.lane_v3[1]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL11, + DSIPHY_CMN_TIMING_CTRL_2, DSIPHY_CMN_TIMING_CTRL_3, + cfg->timing.lane_v3[2], cfg->timing.lane_v3[3]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL12, + DSIPHY_CMN_TIMING_CTRL_4, DSIPHY_CMN_TIMING_CTRL_5, + cfg->timing.lane_v3[4], cfg->timing.lane_v3[5]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL13, + DSIPHY_CMN_TIMING_CTRL_6, DSIPHY_CMN_TIMING_CTRL_7, + cfg->timing.lane_v3[6], cfg->timing.lane_v3[7]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL14, + DSIPHY_CMN_TIMING_CTRL_8, DSIPHY_CMN_TIMING_CTRL_9, + cfg->timing.lane_v3[8], cfg->timing.lane_v3[9]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL15, + DSIPHY_CMN_TIMING_CTRL_10, DSIPHY_CMN_TIMING_CTRL_11, + cfg->timing.lane_v3[10], cfg->timing.lane_v3[11]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL16, + DSIPHY_CMN_CTRL_0, DSIPHY_CMN_LANE_CTRL0, + 0x7f, 0x1f); + } else { + reg = DSI_R32(phy, DSIPHY_CMN_CLK_CFG0); + reg &= ~BIT(5); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL0, + DSIPHY_CMN_CLK_CFG0, DSIPHY_CMN_PLL_CNTRL, + reg, 0x0); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL1, + DSIPHY_CMN_RBUF_CTRL, DSIPHY_CMN_GLBL_CTRL, + 0x0, 0x10); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL2, + DSIPHY_CMN_VREG_CTRL, DSIPHY_CMN_TIMING_CTRL_0, + 0x59, cfg->timing.lane_v3[0]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL3, + DSIPHY_CMN_TIMING_CTRL_1, DSIPHY_CMN_TIMING_CTRL_2, + cfg->timing.lane_v3[1], cfg->timing.lane_v3[2]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL4, + DSIPHY_CMN_TIMING_CTRL_3, DSIPHY_CMN_TIMING_CTRL_4, + cfg->timing.lane_v3[3], cfg->timing.lane_v3[4]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL5, + DSIPHY_CMN_TIMING_CTRL_5, DSIPHY_CMN_TIMING_CTRL_6, + cfg->timing.lane_v3[5], cfg->timing.lane_v3[6]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL6, + DSIPHY_CMN_TIMING_CTRL_7, DSIPHY_CMN_TIMING_CTRL_8, + cfg->timing.lane_v3[7], cfg->timing.lane_v3[8]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL7, + DSIPHY_CMN_TIMING_CTRL_9, DSIPHY_CMN_TIMING_CTRL_10, + cfg->timing.lane_v3[9], cfg->timing.lane_v3[10]); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL8, + DSIPHY_CMN_TIMING_CTRL_11, DSIPHY_CMN_CTRL_0, + cfg->timing.lane_v3[11], 0x7f); + DSI_DYN_REF_REG_W(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_CTRL9, + DSIPHY_CMN_LANE_CTRL0, DSIPHY_CMN_CTRL_2, + 0x1f, 0x40); + /* + * fill with dummy register writes since controller will blindly + * send these values to DSI PHY. + */ + reg = DSI_DYN_REFRESH_PLL_CTRL11; + while (reg <= DSI_DYN_REFRESH_PLL_CTRL29) { + DSI_DYN_REF_REG_W(phy->dyn_pll_base, reg, + DSIPHY_CMN_LANE_CTRL0, DSIPHY_CMN_CTRL_0, + 0x1f, 0x7f); + reg += 0x4; + } + + DSI_GEN_W32(phy->dyn_pll_base, + DSI_DYN_REFRESH_PLL_UPPER_ADDR, 0); + DSI_GEN_W32(phy->dyn_pll_base, + DSI_DYN_REFRESH_PLL_UPPER_ADDR2, 0); + } + + wmb(); /* make sure all registers are updated */ +} + +void dsi_phy_hw_v3_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, + struct dsi_dyn_clk_delay *delay) +{ + if (!delay) + return; + + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PIPE_DELAY, + delay->pipe_delay); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PIPE_DELAY2, + delay->pipe_delay2); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_PLL_DELAY, + delay->pll_delay); +} + +void dsi_phy_hw_v3_0_dyn_refresh_helper(struct dsi_phy_hw *phy, u32 offset) +{ + u32 reg; + + /* + * if no offset is mentioned then this means we want to clear + * the dynamic refresh ctrl register which is the last step + * of dynamic refresh sequence. + */ + if (!offset) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg &= ~(BIT(0) | BIT(8)); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + wmb(); /* ensure dynamic fps is cleared */ + return; + } + + if (offset & BIT(DYN_REFRESH_INTF_SEL)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(13); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SYNC_MODE)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(16); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SWI_CTRL)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(0); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + } + + if (offset & BIT(DYN_REFRESH_SW_TRIGGER)) { + reg = DSI_GEN_R32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL); + reg |= BIT(8); + DSI_GEN_W32(phy->dyn_pll_base, DSI_DYN_REFRESH_CTRL, reg); + wmb(); /* ensure dynamic fps is triggered */ + } +} + +int dsi_phy_hw_v3_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, + u32 *dst, u32 size) +{ + int i; + + if (!timings || !dst || !size) + return -EINVAL; + + if (size != DSI_PHY_TIMING_V3_SIZE) { + pr_err("size mis-match\n"); + return -EINVAL; + } + + for (i = 0; i < size; i++) + dst[i] = timings->lane_v3[i]; + + return 0; +} diff --git a/msm/dsi/dsi_phy_timing_calc.c b/msm/dsi/dsi_phy_timing_calc.c index 70e7ee3d6a..daa07f584e 100644 --- a/msm/dsi/dsi_phy_timing_calc.c +++ b/msm/dsi/dsi_phy_timing_calc.c @@ -630,11 +630,14 @@ error: * @mode: Mode information for which timing has to be calculated. * @config: DSI host configuration for this mode. * @timing: Timing parameters for each lane which will be returned. + * @use_mode_bit_clk: Boolean to indicate whether reacalculate dsi + * bit clk or use the existing bit clk(for dynamic clk case). */ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy, - struct dsi_mode_info *mode, - struct dsi_host_common_cfg *host, - struct dsi_phy_per_lane_cfgs *timing) + struct dsi_mode_info *mode, + struct dsi_host_common_cfg *host, + struct dsi_phy_per_lane_cfgs *timing, + bool use_mode_bit_clk) { /* constants */ u32 const esc_clk_mhz = 192; /* TODO: esc clock is hardcoded */ @@ -677,7 +680,10 @@ int dsi_phy_hw_calculate_timing_params(struct dsi_phy_hw *phy, num_of_lanes++; - x = mult_frac(v_total * h_total, inter_num, num_of_lanes); + if (use_mode_bit_clk) + x = mode->clk_rate_hz; + else + x = mult_frac(v_total * h_total, inter_num, num_of_lanes); y = rounddown(x, 1); clk_params.bitclk_mbps = rounddown(DIV_ROUND_UP_ULL(y, 1000000), 1); diff --git a/msm/msm_atomic.c b/msm/msm_atomic.c index 180efcccdb..0d3e90f869 100644 --- a/msm/msm_atomic.c +++ b/msm/msm_atomic.c @@ -87,7 +87,8 @@ static inline bool _msm_seamless_for_crtc(struct drm_atomic_state *state, if (msm_is_mode_seamless(&crtc_state->mode) || msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode) || - msm_is_mode_seamless_poms(&crtc_state->adjusted_mode)) + msm_is_mode_seamless_poms(&crtc_state->adjusted_mode) || + msm_is_mode_seamless_dyn_clk(&crtc_state->adjusted_mode)) return true; if (msm_is_mode_seamless_dms(&crtc_state->adjusted_mode) && !enable) @@ -132,6 +133,10 @@ static inline bool _msm_seamless_for_conn(struct drm_connector *connector, &connector->encoder->crtc->state->adjusted_mode)) return true; + if (msm_is_mode_seamless_dyn_clk( + &connector->encoder->crtc->state->adjusted_mode)) + return true; + if (msm_is_mode_seamless_dms( &connector->encoder->crtc->state->adjusted_mode)) return true; diff --git a/msm/msm_kms.h b/msm/msm_kms.h index 3fce5ecc13..f1c1169393 100644 --- a/msm/msm_kms.h +++ b/msm/msm_kms.h @@ -40,6 +40,8 @@ #define MSM_MODE_FLAG_SEAMLESS_VRR (1<<3) /* Request to switch the panel mode */ #define MSM_MODE_FLAG_SEAMLESS_POMS (1<<4) +/* Request to switch the bit clk */ +#define MSM_MODE_FLAG_SEAMLESS_DYN_CLK (1<<5) /* As there are different display controller blocks depending on the * snapdragon version, the kms support is split out and the appropriate @@ -221,6 +223,13 @@ static inline bool msm_is_mode_seamless_poms( : false; } +static inline bool msm_is_mode_seamless_dyn_clk( + const struct drm_display_mode *mode) +{ + return mode ? (mode->private_flags & MSM_MODE_FLAG_SEAMLESS_DYN_CLK) + : false; +} + static inline bool msm_needs_vblank_pre_modeset( const struct drm_display_mode *mode) { diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 80f469cc6f..f2bef71044 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -2086,6 +2086,9 @@ static int sde_connector_populate_mode_info(struct drm_connector *conn, sde_kms_info_add_keystr(info, "mode_name", mode->name); + sde_kms_info_add_keyint(info, "bit_clk_rate", + mode_info.clk_rate); + topology_idx = (int)sde_rm_get_topology_name( mode_info.topology); if (topology_idx < SDE_RM_TOPOLOGY_MAX) { diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index f46c03f150..aacf11293a 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -430,8 +430,9 @@ static bool sde_crtc_mode_fixup(struct drm_crtc *crtc, SDE_DEBUG("\n"); if ((msm_is_mode_seamless(adjusted_mode) || - msm_is_mode_seamless_vrr(adjusted_mode)) && - (!crtc->enabled)) { + (msm_is_mode_seamless_vrr(adjusted_mode) || + msm_is_mode_seamless_dyn_clk(adjusted_mode))) && + (!crtc->enabled)) { SDE_ERROR("crtc state prevents seamless transition\n"); return false; } diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 736e7e8041..ec85900706 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -3156,7 +3156,8 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc) } if (!(msm_is_mode_seamless_vrr(cur_mode) - || msm_is_mode_seamless_dms(cur_mode))) + || msm_is_mode_seamless_dms(cur_mode) + || msm_is_mode_seamless_dyn_clk(cur_mode))) kthread_init_delayed_work(&sde_enc->delayed_off_work, sde_encoder_off_work);