diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 28378d9d60..86ca54e8a1 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -217,6 +217,22 @@ enum dsi_dfps_type { DSI_DFPS_MAX }; +/** + * enum dsi_dyn_clk_feature_type - Dynamic clock feature support type + * @DSI_DYN_CLK_TYPE_LEGACY: Constant FPS is not supported + * @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP: Constant FPS supported with + * change in hfp + * @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP: Constant FPS supported with + * change in vfp + * @DSI_DYN_CLK_TYPE_MAX: + */ +enum dsi_dyn_clk_feature_type { + DSI_DYN_CLK_TYPE_LEGACY = 0, + DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP, + DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP, + DSI_DYN_CLK_TYPE_MAX +}; + /** * enum dsi_cmd_set_type - DSI command set type * @DSI_CMD_SET_PRE_ON: Panel pre on diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index e6a46c0254..e1a9e8ba2c 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -4222,6 +4222,7 @@ static int dsi_display_dfps_update(struct dsi_display *display, struct dsi_dfps_capabilities dfps_caps; int rc = 0; int i = 0; + struct dsi_dyn_clk_caps *dyn_clk_caps; if (!display || !dsi_mode || !display->panel) { DSI_ERR("Invalid params\n"); @@ -4230,8 +4231,9 @@ static int dsi_display_dfps_update(struct dsi_display *display, timing = &dsi_mode->timing; dsi_panel_get_dfps_caps(display->panel, &dfps_caps); - if (!dfps_caps.dfps_support) { - DSI_ERR("dfps not supported\n"); + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dfps_caps.dfps_support && !dyn_clk_caps->maintain_const_fps) { + DSI_ERR("dfps or constant fps not supported\n"); return -ENOTSUPP; } @@ -4508,7 +4510,32 @@ 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) { + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + rc = dsi_ctrl_update_host_config(ctrl->ctrl, + &display->config, mode, mode->dsi_mode_flags, + display->dsi_clk_handle); + if (rc) { + DSI_ERR("failed to update ctrl config\n"); + goto error; + } + } + if (priv_info->phy_timing_len) { + display_for_each_ctrl(i, display) { + ctrl = &display->ctrl[i]; + rc = dsi_phy_set_timing_params(ctrl->phy, + priv_info->phy_timing_val, + priv_info->phy_timing_len, + commit_phy_timing); + if (rc) + DSI_ERR("Fail to add timing params\n"); + } + } + if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK)) + return rc; + } + + if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) { if (display->panel->panel_mode == DSI_OP_VIDEO_MODE) { rc = dsi_display_dynamic_clk_switch_vid(display, mode); if (rc) @@ -5899,6 +5926,51 @@ int dsi_display_get_mode_count(struct dsi_display *display, return 0; } +void dsi_display_adjust_mode_timing( + struct dsi_dyn_clk_caps *dyn_clk_caps, + struct dsi_display_mode *dsi_mode, + int lanes, int bpp) +{ + u32 new_htotal, new_vtotal, htotal, vtotal, old_htotal; + + if (!dyn_clk_caps->maintain_const_fps) + return; + /* + * When there is a dynamic clock switch, there is small change + * in FPS. To compensate for this difference in FPS, hfp or vfp + * is adjusted. It has been assumed that the refined porch values + * are supported by the panel. This logic can be enhanced further + * in future by taking min/max porches supported by the panel. + */ + switch (dyn_clk_caps->type) { + case DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP: + vtotal = DSI_V_TOTAL(&dsi_mode->timing); + old_htotal = dsi_h_total_dce(&dsi_mode->timing); + new_htotal = (dsi_mode->timing.clk_rate_hz * lanes); + new_htotal /= (bpp * vtotal * dsi_mode->timing.refresh_rate); + if (old_htotal > new_htotal) + dsi_mode->timing.h_front_porch -= + (old_htotal - new_htotal); + else + dsi_mode->timing.h_front_porch += + (new_htotal - old_htotal); + break; + + case DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP: + htotal = dsi_h_total_dce(&dsi_mode->timing); + new_vtotal = (dsi_mode->timing.clk_rate_hz * lanes); + new_vtotal /= (bpp * htotal * dsi_mode->timing.refresh_rate); + dsi_mode->timing.v_front_porch = new_vtotal - + dsi_mode->timing.v_back_porch - + dsi_mode->timing.v_sync_width - + dsi_mode->timing.v_active; + break; + + default: + break; + } +} + static void _dsi_display_populate_bit_clks(struct dsi_display *display, int start, int end, u32 *mode_idx) { @@ -5938,6 +6010,9 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display, * be based on user or device tree preferrence. */ src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0]; + + dsi_display_adjust_mode_timing(dyn_clk_caps, src, lanes, bpp); + src->pixel_clk_khz = div_u64(src->timing.clk_rate_hz * lanes, bpp); src->pixel_clk_khz /= 1000; @@ -5957,6 +6032,10 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display, } memcpy(dst, src, sizeof(struct dsi_display_mode)); dst->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[i]; + + dsi_display_adjust_mode_timing(dyn_clk_caps, dst, lanes, + bpp); + dst->pixel_clk_khz = div_u64(dst->timing.clk_rate_hz * lanes, bpp); dst->pixel_clk_khz /= 1000; @@ -6251,13 +6330,28 @@ int dsi_display_find_mode(struct dsi_display *display, return rc; } +static inline bool dsi_display_mode_switch_dfps(struct dsi_display_mode *cur, + struct dsi_display_mode *adj) +{ + /* + * If there is a change in the hfp or vfp of the current and adjoining + * mode,then either it is a dfps mode switch or dynamic clk change with + * constant fps. + */ + if ((cur->timing.h_front_porch != adj->timing.h_front_porch) || + (cur->timing.v_front_porch != adj->timing.v_front_porch)) + return true; + else + return false; +} + /** * dsi_display_validate_mode_change() - Validate mode change case. * @display: DSI display handle. * @cur_mode: Current mode. * @adj_mode: Mode to be set. * MSM_MODE_FLAG_SEAMLESS_VRR flag is set if there - * is change in fps but vactive and hactive are same. + * is change in hfp or vfp but vactive and hactive are same. * DSI_MODE_FLAG_DYN_CLK flag is set if there * is change in clk but vactive and hactive are same. * Return: error code. @@ -6281,14 +6375,14 @@ int dsi_display_validate_mode_change(struct dsi_display *display, } mutex_lock(&display->display_lock); - + dyn_clk_caps = &(display->panel->dyn_clk_caps); 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) { + /* dfps and dynamic clock with const fps use case */ + if (dsi_display_mode_switch_dfps(cur_mode, adj_mode)) { dsi_panel_get_dfps_caps(display->panel, &dfps_caps); - if (dfps_caps.dfps_support) { + if (dfps_caps.dfps_support || + dyn_clk_caps->maintain_const_fps) { DSI_DEBUG("Mode switch is seamless variable refresh\n"); adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR; SDE_EVT32(cur_mode->timing.refresh_rate, @@ -6300,10 +6394,11 @@ int dsi_display_validate_mode_change(struct dsi_display *display, /* 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) { DSI_DEBUG("dynamic clk change detected\n"); - if (adj_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) { + if ((adj_mode->dsi_mode_flags & + DSI_MODE_FLAG_VRR) && + (!dyn_clk_caps->maintain_const_fps)) { DSI_ERR("dfps and dyn clk not supported in same commit\n"); rc = -ENOTSUPP; goto error; diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 3e872e04f1..407aa4d958 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1175,6 +1175,7 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) struct dsi_dyn_clk_caps *dyn_clk_caps = &panel->dyn_clk_caps; struct dsi_parser_utils *utils = &panel->utils; const char *name = panel->name; + const char *type; supported = utils->read_bool(utils->data, "qcom,dsi-dyn-clk-enable"); @@ -1207,6 +1208,24 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) dyn_clk_caps->dyn_clk_support = true; + type = utils->get_property(utils->data, + "qcom,dsi-dyn-clk-type", NULL); + if (!type) { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_LEGACY; + dyn_clk_caps->maintain_const_fps = false; + return 0; + } + if (!strcmp(type, "constant-fps-adjust-hfp")) { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP; + dyn_clk_caps->maintain_const_fps = true; + } else if (!strcmp(type, "constant-fps-adjust-vfp")) { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP; + dyn_clk_caps->maintain_const_fps = true; + } else { + dyn_clk_caps->type = DSI_DYN_CLK_TYPE_LEGACY; + dyn_clk_caps->maintain_const_fps = false; + } + DSI_DEBUG("Dynamic clock type is [%s]\n", type); return 0; } diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index 946bbf0bcb..08ff9a82c3 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -82,6 +82,8 @@ struct dsi_dyn_clk_caps { bool dyn_clk_support; u32 *bit_clk_list; u32 bit_clk_list_len; + enum dsi_dyn_clk_feature_type type; + bool maintain_const_fps; }; struct dsi_pinctrl_info {