diff --git a/msm/dsi/dsi_defs.h b/msm/dsi/dsi_defs.h index 327af9e3b6..9f424781b7 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 3694fd54f3..91e7e5dfe9 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -681,6 +681,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; @@ -694,6 +695,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 3c612fd3c2..a88ccef07f 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1653,6 +1653,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, @@ -1671,6 +1672,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, @@ -1691,6 +1693,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;