From d4def5bd8ca1befe253b1cf567c7ebebfef2b584 Mon Sep 17 00:00:00 2001 From: Amine Najahi Date: Tue, 16 Feb 2021 16:53:04 -0500 Subject: [PATCH 1/2] disp: msm: sde: add new interface for RFI feature Add a new connector range property and a new entry to the panel capability blob to publish the list of supported RFI frequencies. In addition, add the required functions to set, validate and update DSI bit clock rate value to trigger an internal seamless mode switch and reconfigure DSI clock and PLL. Change-Id: I7d19cc369f8c5528709f2f20a51ef02180ebdea4 Signed-off-by: Amine Najahi --- msm/dsi/dsi_display.c | 95 ++++++++++++++++++++++++++++++++--- msm/dsi/dsi_display.h | 23 ++++++++- msm/dsi/dsi_drm.c | 107 ++++++++++++++++++++++++++++------------ msm/dsi/dsi_drm.h | 14 ++++-- msm/msm_drv.h | 1 + msm/msm_kms.h | 3 ++ msm/sde/sde_connector.c | 26 +++++++++- msm/sde/sde_connector.h | 16 ++++++ msm/sde/sde_kms.c | 3 ++ msm/sde/sde_kms.h | 10 ++++ msm/sde/sde_kms_utils.c | 24 ++++++++- 11 files changed, 278 insertions(+), 44 deletions(-) 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) From c5f2bd7401c6eb1e365f4e1fe24d2d3f5289a432 Mon Sep 17 00:00:00 2001 From: Amine Najahi Date: Tue, 16 Feb 2021 16:53:04 -0500 Subject: [PATCH 2/2] disp: msm: sde: add multi-mode RFI support Currently, RFI feature only supports panel that contains a single timing node. This limits the feature availability for panel with multiple modes or with DFPS support. This change adds support for RFI on panels that contains multiple timing nodes. Change-Id: I3a7aadf7b6da3518350b2eb815602b13b5c259f5 Signed-off-by: Amine Najahi --- msm/dsi/dsi_defs.h | 12 ++++++++ msm/dsi/dsi_display.c | 38 +++++++++++++++--------- msm/dsi/dsi_drm.c | 33 ++++++--------------- msm/dsi/dsi_panel.c | 65 ++++++++++++++++++++++++++--------------- msm/dsi/dsi_panel.h | 2 -- msm/msm_drv.h | 4 +++ msm/sde/sde_connector.c | 23 +++++++-------- 7 files changed, 102 insertions(+), 75 deletions(-) diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index fec47e87ee..9248e8cf4f 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -587,6 +587,16 @@ struct dsi_host_config { struct dsi_lane_map lane_map; }; +/** + * struct dyn_clk_list - list of dynamic clock rates. + * @rates: list of supported clock rates + * @count: number of supported clock rates + */ +struct dyn_clk_list { + u32 *rates; + u32 count; +}; + /** * struct dsi_display_mode_priv_info - private mode info that will be attached * with each drm mode @@ -600,6 +610,7 @@ struct dsi_host_config { * @dsi_transfer_time_us: Specifies the dsi transfer time for cmd panels. * @clk_rate_hz: DSI bit clock per lane in hz. * @min_dsi_clk_hz: Min dsi clk per lane to transfer frame in vsync time. + * @bit_clk_list: List of dynamic bit clock rates supported. * @topology: Topology selected for the panel * @dsc: DSC compression info * @vdc: VDC compression info @@ -623,6 +634,7 @@ struct dsi_display_mode_priv_info { u32 dsi_transfer_time_us; u64 clk_rate_hz; u64 min_dsi_clk_hz; + struct dyn_clk_list bit_clk_list; struct msm_display_topology topology; struct msm_display_dsc_info dsc; diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index c0eb0521cc..9f15d3253a 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -6678,12 +6678,11 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display, 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]; + + if (!src->priv_info->bit_clk_list.count) + continue; + + src->timing.clk_rate_hz = src->priv_info->bit_clk_list.rates[0]; dsi_display_adjust_mode_timing(display, src, lanes, bpp); @@ -6728,6 +6727,7 @@ 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) { + int i; u32 clk_rate_hz = 0; if (!display || !mode || !mode->priv_info) { @@ -6735,17 +6735,24 @@ int dsi_display_restore_bit_clk(struct dsi_display *display, struct dsi_display_ 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; + clk_rate_hz = display->cached_clk_rate; + + if (mode->priv_info->bit_clk_list.count) { + /* use first entry as the default bit clk rate */ + clk_rate_hz = mode->priv_info->bit_clk_list.rates[0]; + + for (i = 0; i < mode->priv_info->bit_clk_list.count; i++) { + if (display->dyn_bit_clk == mode->priv_info->bit_clk_list.rates[i]) + clk_rate_hz = display->dyn_bit_clk; + } + } 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); + SDE_EVT32(clk_rate_hz, display->cached_clk_rate, display->dyn_bit_clk); + DSI_DEBUG("clk_rate_hz:%u, cached_clk_rate:%u, dyn_bit_clk:%u\n", + clk_rate_hz, display->cached_clk_rate, display->dyn_bit_clk); return 0; } @@ -8412,6 +8419,9 @@ int dsi_display_update_dyn_bit_clk(struct dsi_display *display, } else if (!display->dyn_bit_clk_pending) { DSI_DEBUG("dynamic bit clock rate not updated\n"); return 0; + } else if (!display->dyn_bit_clk) { + DSI_DEBUG("dynamic bit clock rate cleared\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); @@ -8440,6 +8450,8 @@ int dsi_display_update_dyn_bit_clk(struct dsi_display *display, 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; + + SDE_EVT32(display->dyn_bit_clk, mode->priv_info->min_dsi_clk_hz, mode->pixel_clk_khz); 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); diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 4f971c3ccd..dea8474830 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -579,6 +579,13 @@ int dsi_conn_get_mode_info(struct drm_connector *connector, memcpy(&mode_info->topology, &dsi_mode->priv_info->topology, sizeof(struct msm_display_topology)); + if (dsi_mode->priv_info->bit_clk_list.count) { + mode_info->bit_clk_rates = + dsi_mode->priv_info->bit_clk_list.rates; + mode_info->bit_clk_count = + dsi_mode->priv_info->bit_clk_list.count; + } + if (dsi_mode->priv_info->dsc_enabled) { mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_DSC; mode_info->topology.comp_type = MSM_DISPLAY_COMPRESSION_DSC; @@ -697,11 +704,6 @@ 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"); @@ -1297,12 +1299,8 @@ void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector, 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"); @@ -1310,25 +1308,12 @@ int dsi_conn_set_dyn_bit_clk(struct drm_connector *connector, uint64_t value) } 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; + + SDE_EVT32(display->dyn_bit_clk); DSI_DEBUG("update dynamic bit clock rate to %llu\n", display->dyn_bit_clk); return 0; diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index 85d02d8b4e..fa194876a7 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1326,13 +1326,48 @@ error: return rc; } +static int dsi_panel_parse_dyn_clk_list(struct dsi_display_mode *mode, + struct dsi_parser_utils *utils) +{ + int i, rc = 0; + struct dyn_clk_list *bit_clk_list; + + if (!mode || !mode->priv_info) { + DSI_ERR("invalid arguments\n"); + return -EINVAL; + } + + bit_clk_list = &mode->priv_info->bit_clk_list; + + bit_clk_list->count = utils->count_u32_elems(utils->data, "qcom,dsi-dyn-clk-list"); + if (bit_clk_list->count < 1) + return 0; + + bit_clk_list->rates = kcalloc(bit_clk_list->count, sizeof(u32), GFP_KERNEL); + if (!bit_clk_list->rates) { + DSI_ERR("failed to allocate space for bit clock list\n"); + return -ENOMEM; + } + + rc = utils->read_u32_array(utils->data, "qcom,dsi-dyn-clk-list", + bit_clk_list->rates, bit_clk_list->count); + if (rc) { + DSI_ERR("failed to parse supported bit clk list, rc=%d\n", rc); + return -EINVAL; + } + + for (i = 0; i < bit_clk_list->count; i++) + DSI_DEBUG("bit clk rate[%d]:%d\n", i, bit_clk_list->rates[i]); + + return 0; +} + 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; const char *type; supported = utils->read_bool(utils->data, "qcom,dsi-dyn-clk-enable"); @@ -1342,28 +1377,6 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel) 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) { - DSI_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) { - DSI_ERR("[%s] failed to parse supported bit clk list\n", name); - return -EINVAL; - } - dyn_clk_caps->dyn_clk_support = true; type = utils->get_property(utils->data, @@ -4032,6 +4045,12 @@ int dsi_panel_get_mode(struct dsi_panel *panel, goto parse_fail; } + if (panel->dyn_clk_caps.dyn_clk_support) { + rc = dsi_panel_parse_dyn_clk_list(mode, utils); + if (rc) + DSI_ERR("failed to parse dynamic clk rates, rc=%d\n", rc); + } + rc = dsi_panel_parse_dsc_params(mode, utils); if (rc) { DSI_ERR("failed to parse dsc params, rc=%d\n", rc); diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index b9707c3f2c..cf3becf268 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -97,8 +97,6 @@ struct dsi_qsync_capabilities { 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; }; diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 4f35471309..09accfe2db 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -703,6 +703,8 @@ struct msm_display_topology { * @mdp_transfer_time_us Specifies the mdp transfer time for command mode * panels in microseconds. * @allowed_mode_switches: bit mask to indicate supported mode switch. + * @bit_clk_rates: list of supported bit clock rates + * @bit_clk_count: number of supported bit clock rates */ struct msm_mode_info { uint32_t frame_rate; @@ -719,6 +721,8 @@ struct msm_mode_info { u32 panel_mode_caps; u32 mdp_transfer_time_us; u32 allowed_mode_switches; + u32 *bit_clk_rates; + u32 bit_clk_count; }; /** diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index b87d6c37ff..e2b728c7a1 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -2623,6 +2623,12 @@ static int sde_connector_populate_mode_info(struct drm_connector *conn, sde_kms_info_add_keyint(info, "bit_clk_rate", mode_info.clk_rate); + if (mode_info.bit_clk_count > 0) + sde_kms_info_add_list(info, "dyn_bitclk_list", + mode_info.bit_clk_rates, + mode_info.bit_clk_count); + + topology_idx = (int)sde_rm_get_topology_name(&sde_kms->rm, mode_info.topology); if (topology_idx < SDE_RM_TOPOLOGY_MAX) { @@ -2761,11 +2767,9 @@ static int _sde_connector_install_properties(struct drm_device *dev, struct msm_display_info *display_info) { struct dsi_display *dsi_display; - int rc, i; + int rc; 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); @@ -2800,17 +2804,10 @@ static int _sde_connector_install_properties(struct drm_device *dev, } 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); - + dsi_display->panel->dyn_clk_caps.dyn_clk_support) msm_property_install_range(&c_conn->property_info, "dyn_bit_clk", - 0x0, 0, max_bit_clk, max_bit_clk, - CONNECTOR_PROP_DYN_BIT_CLK); - } + 0x0, 0, ~0, 0, CONNECTOR_PROP_DYN_BIT_CLK); + mutex_lock(&c_conn->base.dev->mode_config.mutex); sde_connector_fill_modes(&c_conn->base,