diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index fc09f65af7..c0eb0521cc 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -6726,6 +6726,29 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display, } } +int dsi_display_restore_bit_clk(struct dsi_display *display, struct dsi_display_mode *mode) +{ + u32 clk_rate_hz = 0; + + if (!display || !mode || !mode->priv_info) { + DSI_ERR("invalid arguments\n"); + return -EINVAL; + } + + /* for dynamic DSI use specified clock rate otherwise restore clock rate */ + if (display->dyn_bit_clk > 0) + clk_rate_hz = display->dyn_bit_clk; + else if (display->cached_clk_rate > 0) + clk_rate_hz = display->cached_clk_rate; + + mode->timing.clk_rate_hz = clk_rate_hz; + mode->priv_info->clk_rate_hz = clk_rate_hz; + DSI_DEBUG("dyn_bit_clk:%u, cached_clk_rate:%u, clk_rate_hz:%u\n", + display->dyn_bit_clk, display->cached_clk_rate, clk_rate_hz); + + return 0; +} + void dsi_display_put_mode(struct dsi_display *display, struct dsi_display_mode *mode) { @@ -7237,9 +7260,11 @@ int dsi_display_set_mode(struct dsi_display *display, } } - /*For dynamic DSI setting, use specified clock rate */ - if (display->cached_clk_rate > 0) - adj_mode.priv_info->clk_rate_hz = display->cached_clk_rate; + rc = dsi_display_restore_bit_clk(display, &adj_mode); + if (rc) { + DSI_ERR("[%s] bit clk rate cannot be restored\n", display->name); + goto error; + } rc = dsi_display_validate_mode_set(display, &adj_mode, flags); if (rc) { @@ -7253,11 +7278,13 @@ int dsi_display_set_mode(struct dsi_display *display, goto error; } - DSI_INFO("mdp_transfer_time=%d, hactive=%d, vactive=%d, fps=%d\n", + DSI_INFO("mdp_transfer_time=%d, hactive=%d, vactive=%d, fps=%d, clk_rate=%llu\n", adj_mode.priv_info->mdp_transfer_time_us, - timing.h_active, timing.v_active, timing.refresh_rate); + timing.h_active, timing.v_active, timing.refresh_rate, + adj_mode.priv_info->clk_rate_hz); SDE_EVT32(adj_mode.priv_info->mdp_transfer_time_us, - timing.h_active, timing.v_active, timing.refresh_rate); + timing.h_active, timing.v_active, timing.refresh_rate, + adj_mode.priv_info->clk_rate_hz); memcpy(display->panel->cur_mode, &adj_mode, sizeof(adj_mode)); error: @@ -8366,6 +8393,62 @@ int dsi_display_update_pps(char *pps_cmd, void *disp) return 0; } +int dsi_display_update_dyn_bit_clk(struct dsi_display *display, + struct dsi_display_mode *mode) +{ + struct dsi_dyn_clk_caps *dyn_clk_caps; + struct dsi_host_common_cfg *host_cfg; + int bpp, lanes = 0; + + if (!display || !mode) { + DSI_ERR("invalid arguments\n"); + return -EINVAL; + } + + dyn_clk_caps = &(display->panel->dyn_clk_caps); + if (!dyn_clk_caps->dyn_clk_support) { + DSI_DEBUG("dynamic bit clock support not enabled\n"); + return 0; + } else if (!display->dyn_bit_clk_pending) { + DSI_DEBUG("dynamic bit clock rate not updated\n"); + return 0; + } else if (display->dyn_bit_clk < mode->priv_info->min_dsi_clk_hz) { + DSI_ERR("dynamic bit clock rate %llu smaller than minimum value:%llu\n", + display->dyn_bit_clk, mode->priv_info->min_dsi_clk_hz); + return -EINVAL; + } + + /* update mode clk rate with user value */ + mode->timing.clk_rate_hz = display->dyn_bit_clk; + mode->priv_info->clk_rate_hz = display->dyn_bit_clk; + + host_cfg = &(display->panel->host_config); + bpp = dsi_pixel_format_to_bpp(host_cfg->dst_format); + + if (host_cfg->data_lanes & DSI_DATA_LANE_0) + lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_1) + lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_2) + lanes++; + if (host_cfg->data_lanes & DSI_DATA_LANE_3) + lanes++; + + dsi_display_adjust_mode_timing(display, mode, lanes, bpp); + + /* adjust pixel clock based on dynamic bit clock */ + mode->pixel_clk_khz = div_u64(mode->timing.clk_rate_hz * lanes, bpp); + do_div(mode->pixel_clk_khz, 1000); + mode->pixel_clk_khz *= display->ctrl_count; + DSI_DEBUG("dynamic bit clk:%u, min dsi clk:%llu, lanes:%d, bpp:%d, pck:%d Khz\n", + display->dyn_bit_clk, mode->priv_info->min_dsi_clk_hz, lanes, bpp, + mode->pixel_clk_khz); + + display->dyn_bit_clk_pending = false; + + return 0; +} + int dsi_display_dump_clks_state(struct dsi_display *display) { int rc = 0; diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index cce1500ed4..9f22b2bab7 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -154,6 +154,8 @@ struct dsi_display_ext_bridge { * index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array. * @cmd_master_idx: The master controller for sending DSI commands to panel. * @video_master_idx: The master controller for enabling video engine. + * @dyn_bit_clk: The DSI bit clock rate dynamically set by user mode client. + * @dyn_bit_clk_pending: Flag indicating the pending DSI dynamic bit clock rate change. * @cached_clk_rate: The cached DSI clock rate set dynamically by sysfs. * @clkrate_change_pending: Flag indicating the pending DSI clock re-enabling. * @clock_info: Clock sourcing for DSI display. @@ -225,7 +227,9 @@ struct dsi_display { u32 video_master_idx; /* dynamic DSI clock info*/ - u32 cached_clk_rate; + u32 dyn_bit_clk; + bool dyn_bit_clk_pending; + u32 cached_clk_rate; atomic_t clkrate_change_pending; struct dsi_display_clk_info clock_info; @@ -776,4 +780,21 @@ int dsi_display_get_panel_vfp(void *display, */ int dsi_display_dump_clks_state(struct dsi_display *display); +/** + * dsi_display_update_dyn_bit_clk() - update mode timing to compensate for dynamic bit clock + * @display: Handle to display + * @mode: Mode to be updated + * Return: Zero on Success + */ +int dsi_display_update_dyn_bit_clk(struct dsi_display *display, struct dsi_display_mode *mode); + +/** + * dsi_display_restore_bit_clk() - restore mode bit clock rate value from dynamic bit clock + * @display: Handle to display + * @mode: Mode to be updated + * Return: Zero on Success + */ +int dsi_display_restore_bit_clk(struct dsi_display *display, struct dsi_display_mode *mode); + + #endif /* _DSI_DISPLAY_H_ */ diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 0d459f244d..4f971c3ccd 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -342,7 +342,9 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - struct dsi_bridge *c_bridge = to_dsi_bridge(bridge); + int rc = 0; + struct dsi_bridge *c_bridge = NULL; + struct dsi_display *display; struct drm_connector *conn; struct sde_connector_state *conn_state; @@ -351,6 +353,18 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, return; } + c_bridge = to_dsi_bridge(bridge); + if (!c_bridge) { + DSI_ERR("invalid dsi bridge\n"); + return; + } + + display = c_bridge->display; + if (!display || !display->drm_conn || !display->drm_conn->state) { + DSI_ERR("invalid display\n"); + return; + } + memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode)); convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode)); conn = sde_encoder_get_connector(bridge->dev, bridge->encoder); @@ -366,9 +380,11 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge, msm_parse_mode_priv_info(&conn_state->msm_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); + rc = dsi_display_restore_bit_clk(display, &c_bridge->dsi_mode); + if (rc) { + DSI_ERR("[%s] bit clk rate cannot be restored\n", display->name); + return; + } DSI_DEBUG("clk_rate: %llu\n", c_bridge->dsi_mode.timing.clk_rate_hz); } @@ -436,6 +452,18 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge, dsi_mode.timing.dsc_enabled = dsi_mode.priv_info->dsc_enabled; dsi_mode.timing.dsc = &dsi_mode.priv_info->dsc; + rc = dsi_display_restore_bit_clk(display, &dsi_mode); + if (rc) { + DSI_ERR("[%s] bit clk rate cannot be restored\n", display->name); + return false; + } + + rc = dsi_display_update_dyn_bit_clk(display, &dsi_mode); + if (rc) { + DSI_ERR("[%s] failed to update bit clock\n", display->name); + return false; + } + rc = dsi_display_validate_mode(c_bridge->display, &dsi_mode, DSI_VALIDATE_FLAG_ALLOW_ADJUST); if (rc) { @@ -517,33 +545,6 @@ u32 dsi_drm_get_dfps_maxfps(void *display) return dfps_maxfps; } -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(drm_mode))) { - 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, @@ -696,6 +697,11 @@ int dsi_conn_set_info_blob(struct drm_connector *connector, sde_kms_info_add_keystr(info, "dyn bitclk support", panel->dyn_clk_caps.dyn_clk_support ? "true" : "false"); + if (panel->dyn_clk_caps.dyn_clk_support) + sde_kms_info_add_list(info, "dyn_bitclk_list", + panel->dyn_clk_caps.bit_clk_list, + panel->dyn_clk_caps.bit_clk_list_len); + switch (panel->phy_props.rotation) { case DSI_PANEL_ROTATE_NONE: sde_kms_info_add_keystr(info, "panel orientation", "none"); @@ -1288,3 +1294,42 @@ void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector, mode_idx++; } } + +int dsi_conn_set_dyn_bit_clk(struct drm_connector *connector, uint64_t value) +{ + int i; + bool is_valid = false; + struct sde_connector *c_conn = NULL; + struct sde_connector_state *c_state; + struct dsi_display *display; + struct dsi_dyn_clk_caps *dyn_clk_caps; + + if (!connector) { + DSI_ERR("invalid connector\n"); + return -EINVAL; + } + + c_conn = to_sde_connector(connector); + c_state = to_sde_connector_state(connector->state); + + display = (struct dsi_display *) c_conn->display; + dyn_clk_caps = &display->panel->dyn_clk_caps; + + for (i = 0; i < dyn_clk_caps->bit_clk_list_len; i++) { + if (dyn_clk_caps->bit_clk_list[i] == value) { + is_valid = true; + break; + } + } + + if (!is_valid) { + DSI_ERR("invalid dynamic bit clock rate selection %llu\n", value); + return -EINVAL; + } + + display->dyn_bit_clk = value; + display->dyn_bit_clk_pending = true; + DSI_DEBUG("update dynamic bit clock rate to %llu\n", display->dyn_bit_clk); + + return 0; +} diff --git a/msm/dsi/dsi_drm.h b/msm/dsi/dsi_drm.h index 188281f8c1..e198412d9b 100644 --- a/msm/dsi/dsi_drm.h +++ b/msm/dsi/dsi_drm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #ifndef _DSI_DRM_H_ @@ -134,9 +134,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); - /** * dsi_conn_prepare_commit - program pre commit time features * @display: Pointer to private display structure @@ -154,4 +151,13 @@ int dsi_conn_prepare_commit(void *display, void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector, void *display); +/** + * dsi_conn_set_dyn_bit_clk - set target dynamic clock rate + * @connector: Pointer to drm connector structure + * @value: Target dynamic clock rate + * Returns: Zero on success + */ +int dsi_conn_set_dyn_bit_clk(struct drm_connector *connector, + uint64_t value); + #endif /* _DSI_DRM_H_ */ diff --git a/msm/msm_drv.h b/msm/msm_drv.h index a4a8b0ee08..4f35471309 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -211,6 +211,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_BL_SCALE, CONNECTOR_PROP_SV_BL_SCALE, CONNECTOR_PROP_SUPPORTED_COLORSPACES, + CONNECTOR_PROP_DYN_BIT_CLK, /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, diff --git a/msm/msm_kms.h b/msm/msm_kms.h index 00e04a649b..3e081bab30 100644 --- a/msm/msm_kms.h +++ b/msm/msm_kms.h @@ -292,6 +292,9 @@ static inline bool msm_is_private_mode_changed( if (msm_is_mode_seamless_poms(msm_mode)) return true; + if (msm_is_mode_seamless_dyn_clk(msm_mode)) + return true; + return false; } diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index e9ee658733..b87d6c37ff 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -1595,6 +1595,15 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, c_conn->expected_panel_mode = MSM_DISPLAY_CMD_MODE; break; + case CONNECTOR_PROP_DYN_BIT_CLK: + if (!c_conn->ops.set_dyn_bit_clk) + break; + + rc = c_conn->ops.set_dyn_bit_clk(connector, val); + if (rc) + SDE_ERROR_CONN(c_conn, "dynamic bit clock set failed, rc: %d", rc); + + break; default: break; } @@ -2752,9 +2761,11 @@ static int _sde_connector_install_properties(struct drm_device *dev, struct msm_display_info *display_info) { struct dsi_display *dsi_display; - int rc; + int rc, i; struct drm_connector *connector; u64 panel_id = ~0x0; + struct dsi_dyn_clk_caps *dyn_clk_caps; + u32 max_bit_clk; msm_property_install_blob(&c_conn->property_info, "capabilities", DRM_MODE_PROP_IMMUTABLE, CONNECTOR_PROP_SDE_INFO); @@ -2788,6 +2799,19 @@ static int _sde_connector_install_properties(struct drm_device *dev, CONNECTOR_PROP_HDR_INFO); } + if (dsi_display && dsi_display->panel && + dsi_display->panel->dyn_clk_caps.dyn_clk_support) { + + dyn_clk_caps = &dsi_display->panel->dyn_clk_caps; + max_bit_clk = dyn_clk_caps->bit_clk_list[0]; + for (i = 1; i < dyn_clk_caps->bit_clk_list_len; i++) + max_bit_clk = max(dyn_clk_caps->bit_clk_list[i], max_bit_clk); + + msm_property_install_range(&c_conn->property_info, "dyn_bit_clk", + 0x0, 0, max_bit_clk, max_bit_clk, + CONNECTOR_PROP_DYN_BIT_CLK); + } + mutex_lock(&c_conn->base.dev->mode_config.mutex); sde_connector_fill_modes(&c_conn->base, dev->mode_config.max_width, diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 0d266a5b43..4a5f5456e5 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -374,6 +374,14 @@ struct sde_connector_ops { void (*set_allowed_mode_switch)(struct drm_connector *connector, void *display); + /** + * set_dyn_bit_clk - set target dynamic clock rate + * @connector: Pointer to drm connector structure + * @value: Target dynamic clock rate + * Returns: Zero on success + */ + int (*set_dyn_bit_clk)(struct drm_connector *connector, uint64_t value); + /** * get_qsync_min_fps - Get qsync min fps from qsync-min-fps-list * @display: Pointer to private display structure @@ -961,6 +969,14 @@ int sde_connector_set_blob_data(struct drm_connector *conn, */ int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state); +/** + * sde_connector_set_dyn_bit_clk - set dynamic bit clock + * @conn: Pointer to drm_connector struct + * @value: Property value + * Returns: Zero on success + */ +int sde_connector_set_dyn_bit_clk(struct drm_connector *conn, uint64_t value); + /** * sde_connector_schedule_status_work - manage ESD thread * conn: Pointer to drm_connector struct diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index f8a43caa9d..6d21878ef9 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1736,6 +1736,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .cmd_receive = dsi_display_cmd_receive, .install_properties = NULL, .set_allowed_mode_switch = dsi_conn_set_allowed_mode_switch, + .set_dyn_bit_clk = dsi_conn_set_dyn_bit_clk, .get_qsync_min_fps = dsi_display_get_qsync_min_fps, .prepare_commit = dsi_conn_prepare_commit, }; @@ -1756,6 +1757,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .get_panel_vfp = NULL, .cmd_receive = NULL, .install_properties = NULL, + .set_dyn_bit_clk = NULL, .set_allowed_mode_switch = NULL, }; static const struct sde_connector_ops dp_ops = { @@ -1778,6 +1780,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .cmd_receive = NULL, .install_properties = dp_connector_install_properties, .set_allowed_mode_switch = NULL, + .set_dyn_bit_clk = NULL, }; struct msm_display_info info; struct drm_encoder *encoder; diff --git a/msm/sde/sde_kms.h b/msm/sde/sde_kms.h index cf34ff3744..1ad19fece4 100644 --- a/msm/sde/sde_kms.h +++ b/msm/sde/sde_kms.h @@ -611,6 +611,16 @@ void sde_kms_info_append_format(struct sde_kms_info *info, */ void sde_kms_info_stop(struct sde_kms_info *info); +/** + * sde_kms_info_add_list - add a space separated list to 'sde_kms_info' + * @info: Pointer to sde_kms_info structure + * @key: Pointer to key string + * @item: Pointer to array of integer values + * @size: Number of integers to parse + */ +void sde_kms_info_add_list(struct sde_kms_info *info, + const char *key, uint32_t *item, size_t size); + /** * sde_kms_rect_intersect - intersect two rectangles * @r1: first rectangle diff --git a/msm/sde/sde_kms_utils.c b/msm/sde/sde_kms_utils.c index 7bb2445e2b..e2b156a961 100644 --- a/msm/sde/sde_kms_utils.c +++ b/msm/sde/sde_kms_utils.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "sde-kms_utils:[%s] " fmt, __func__ @@ -91,6 +91,28 @@ void sde_kms_info_append(struct sde_kms_info *info, } } +void sde_kms_info_add_list(struct sde_kms_info *info, const char *key, uint32_t *value, size_t size) +{ + uint32_t i, len; + + if (!info || !key || !value || !size) + return; + + sde_kms_info_start(info, key); + for (i = 0; i < size; i++) { + len = scnprintf(info->data + info->staged_len, + SDE_KMS_INFO_MAX_SIZE - info->len, "%d ", + value[i]); + + /* check if snprintf truncated the string */ + if ((info->staged_len + len) < SDE_KMS_INFO_MAX_SIZE) { + info->staged_len += len; + info->start = false; + } + } + sde_kms_info_stop(info); +} + void sde_kms_info_append_format(struct sde_kms_info *info, uint32_t pixel_format, uint64_t modifier)