From 03f9c40e7d3e567f80e9624c478796bf0810a55c Mon Sep 17 00:00:00 2001 From: Satya Rama Aditya Pinapala Date: Tue, 25 Aug 2020 17:21:48 -0700 Subject: [PATCH] disp: msm: add allowed_mode_switch blob property The change adds a new mode property allowed_mode_switch. The new property is a 32bit bitmask that indicates the modes each mode can switch to. This change is required to pass the driver mode switching capabilities, so that user mode can reject any mode switch that is not supported by the driver. Change-Id: I76d1733a07a6d57487ba9f461055270d7e60e060 Signed-off-by: Satya Rama Aditya Pinapala --- msm/dsi/dsi_defs.h | 2 + msm/dsi/dsi_drm.c | 99 +++++++++++++++++++++++++++++++++++++++++ msm/dsi/dsi_drm.h | 8 ++++ msm/msm_drv.h | 2 + msm/sde/sde_connector.c | 7 +++ msm/sde/sde_connector.h | 8 ++++ msm/sde/sde_kms.c | 3 ++ 7 files changed, 129 insertions(+) diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index f3f3096354..810ad84028 100644 --- a/msm/dsi/dsi_defs.h +++ b/msm/dsi/dsi_defs.h @@ -597,6 +597,7 @@ struct dsi_host_config { * @pclk_scale: pclk scale factor, target bpp to source bpp * @roi_caps: Panel ROI capabilities * @widebus_support 48 bit wide data bus is supported by hw + * @allowed_mode_switch: BIT mask to mark allowed mode switches */ struct dsi_display_mode_priv_info { struct dsi_panel_cmd_set cmd_sets[DSI_CMD_SET_MAX]; @@ -620,6 +621,7 @@ struct dsi_display_mode_priv_info { struct msm_ratio pclk_scale; struct msm_roi_caps roi_caps; bool widebus_support; + u32 allowed_mode_switch; }; /** diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 3db7b37c0d..6e53e422d9 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -523,6 +523,9 @@ int dsi_conn_get_mode_info(struct drm_connector *connector, sizeof(dsi_mode.priv_info->roi_caps)); } + mode_info->allowed_mode_switches = + dsi_mode.priv_info->allowed_mode_switch; + return 0; } @@ -1084,3 +1087,99 @@ void dsi_drm_bridge_cleanup(struct dsi_bridge *bridge) kfree(bridge); } + +static bool is_valid_poms_switch(struct dsi_display_mode *mode_a, + struct dsi_display_mode *mode_b) +{ + /* + * POMS cannot happen in conjunction with any other type of mode set. + * Check to ensure FPS remains same between the modes and also + * resolution. + */ + return((mode_a->timing.refresh_rate == mode_b->timing.refresh_rate) && + (mode_a->timing.v_active == mode_b->timing.v_active) && + (mode_a->timing.h_active == mode_b->timing.h_active)); +} + +void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector, + void *display) +{ + u32 mode_idx = 0, cmp_mode_idx = 0; + struct drm_display_mode *drm_mode, *cmp_drm_mode; + struct dsi_display_mode dsi_mode, *panel_dsi_mode, *cmp_panel_dsi_mode; + struct list_head *mode_list = &connector->modes; + struct dsi_display *disp = display; + struct dsi_panel *panel; + int mode_count, rc = 0; + struct dsi_display_mode_priv_info *dsi_mode_info, *cmp_dsi_mode_info; + bool allow_switch = false; + + if (!disp || !disp->panel) { + DSI_ERR("invalid parameters"); + return; + } + + panel = disp->panel; + mode_count = panel->num_display_modes; + + list_for_each_entry(drm_mode, &connector->modes, head) { + + convert_to_dsi_mode(drm_mode, &dsi_mode); + + rc = dsi_display_find_mode(display, &dsi_mode, &panel_dsi_mode); + if (rc) + return; + + dsi_mode_info = panel_dsi_mode->priv_info; + dsi_mode_info->allowed_mode_switch |= BIT(mode_idx); + if (mode_idx == mode_count - 1) + break; + + mode_list = mode_list->next; + cmp_mode_idx = 1; + list_for_each_entry(cmp_drm_mode, mode_list, head) { + convert_to_dsi_mode(cmp_drm_mode, &dsi_mode); + + rc = dsi_display_find_mode(display, &dsi_mode, + &cmp_panel_dsi_mode); + if (rc) + return; + + cmp_dsi_mode_info = cmp_panel_dsi_mode->priv_info; + allow_switch = false; + + /* + * FPS switch among video modes, is only supported + * if DFPS or dynamic clocks are specified. + * Reject any mode switches between video mode timing + * nodes if support for those features is not present. + */ + if (panel_dsi_mode->panel_mode == + cmp_panel_dsi_mode->panel_mode) { + if (panel_dsi_mode->panel_mode == + DSI_OP_CMD_MODE) + allow_switch = true; + else if (panel->dfps_caps.dfps_support || + panel->dyn_clk_caps.dyn_clk_support) + allow_switch = true; + } else { + if (is_valid_poms_switch(panel_dsi_mode, + cmp_panel_dsi_mode)) + allow_switch = true; + } + + if (allow_switch) { + dsi_mode_info->allowed_mode_switch |= + BIT(mode_idx + cmp_mode_idx); + cmp_dsi_mode_info->allowed_mode_switch |= + BIT(mode_idx); + } + + if ((mode_idx + cmp_mode_idx) >= mode_count - 1) + break; + + cmp_mode_idx++; + } + mode_idx++; + } +} diff --git a/msm/dsi/dsi_drm.h b/msm/dsi/dsi_drm.h index 5a67bbc8e9..cd198d83cc 100644 --- a/msm/dsi/dsi_drm.h +++ b/msm/dsi/dsi_drm.h @@ -147,4 +147,12 @@ u64 dsi_drm_find_bit_clk_rate(void *display, int dsi_conn_prepare_commit(void *display, struct msm_display_conn_params *params); +/** + * dsi_set_allowed_mode_switch - set allowed mode switch bitmask + * @connector: Pointer to drm connector structure + * @display: Pointer to private display structure + */ +void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector, + void *display); + #endif /* _DSI_DRM_H_ */ diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 0748aca9ea..99a380741f 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -680,6 +680,7 @@ struct msm_display_topology { * @wide_bus_en: wide-bus mode cfg for interface module * @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. */ struct msm_mode_info { uint32_t frame_rate; @@ -693,6 +694,7 @@ struct msm_mode_info { struct msm_roi_caps roi_caps; bool wide_bus_en; u32 mdp_transfer_time_us; + u32 allowed_mode_switches; }; /** diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 4413231801..aad5a8863c 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -2204,6 +2204,10 @@ static int sde_connector_fill_modes(struct drm_connector *connector, mode_count = drm_helper_probe_single_connector_modes(connector, max_width, max_height); + if (sde_conn->ops.set_allowed_mode_switch) + sde_conn->ops.set_allowed_mode_switch(connector, + sde_conn->display); + rc = sde_connector_set_blob_data(connector, connector->state, CONNECTOR_PROP_MODE_INFO); @@ -2554,6 +2558,9 @@ static int sde_connector_populate_mode_info(struct drm_connector *conn, sde_kms_info_add_keyint(info, "mdp_transfer_time_us", mode_info.mdp_transfer_time_us); + sde_kms_info_add_keyint(info, "allowed_mode_switch", + mode_info.allowed_mode_switches); + if (!mode_info.roi_caps.num_roi) continue; diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 10ef859650..80a7392ca6 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -366,6 +366,14 @@ struct sde_connector_ops { * Returns: Zero on success */ int (*install_properties)(void *display, struct drm_connector *conn); + + /** + * set_allowed_mode_switch - set allowed_mode_switch flag + * @connector: Pointer to drm connector structure + * @display: Pointer to private display structure + */ + void (*set_allowed_mode_switch)(struct drm_connector *connector, + void *display); }; /** diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 5020ac05c7..0a6e9589e3 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1657,6 +1657,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .get_default_lms = dsi_display_get_default_lms, .cmd_receive = dsi_display_cmd_receive, .install_properties = NULL, + .set_allowed_mode_switch = dsi_conn_set_allowed_mode_switch, }; static const struct sde_connector_ops wb_ops = { .post_init = sde_wb_connector_post_init, @@ -1675,6 +1676,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .get_panel_vfp = NULL, .cmd_receive = NULL, .install_properties = NULL, + .set_allowed_mode_switch = NULL, }; static const struct sde_connector_ops dp_ops = { .post_init = dp_connector_post_init, @@ -1695,6 +1697,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .update_pps = dp_connector_update_pps, .cmd_receive = NULL, .install_properties = dp_connector_install_properties, + .set_allowed_mode_switch = NULL, }; struct msm_display_info info; struct drm_encoder *encoder;