Loading msm/dsi/dsi_defs.h +16 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,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 Loading msm/dsi/dsi_display.c +106 −11 Original line number Diff line number Diff line Loading @@ -4231,6 +4231,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"); Loading @@ -4239,8 +4240,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; } Loading Loading @@ -4517,7 +4519,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) Loading Loading @@ -5902,6 +5929,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_DSC(&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_DSC(&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) { Loading Loading @@ -5941,6 +6013,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; Loading @@ -5960,6 +6035,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; Loading Loading @@ -6254,13 +6333,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. Loading @@ -6284,14 +6378,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, Loading @@ -6303,10 +6397,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; Loading msm/dsi/dsi_panel.c +19 −0 Original line number Diff line number Diff line Loading @@ -1316,6 +1316,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"); Loading Loading @@ -1348,6 +1349,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; } Loading msm/dsi/dsi_panel.h +2 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,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 { Loading Loading
msm/dsi/dsi_defs.h +16 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,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 Loading
msm/dsi/dsi_display.c +106 −11 Original line number Diff line number Diff line Loading @@ -4231,6 +4231,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"); Loading @@ -4239,8 +4240,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; } Loading Loading @@ -4517,7 +4519,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) Loading Loading @@ -5902,6 +5929,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_DSC(&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_DSC(&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) { Loading Loading @@ -5941,6 +6013,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; Loading @@ -5960,6 +6035,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; Loading Loading @@ -6254,13 +6333,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. Loading @@ -6284,14 +6378,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, Loading @@ -6303,10 +6397,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; Loading
msm/dsi/dsi_panel.c +19 −0 Original line number Diff line number Diff line Loading @@ -1316,6 +1316,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"); Loading Loading @@ -1348,6 +1349,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; } Loading
msm/dsi/dsi_panel.h +2 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,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 { Loading