diff --git a/msm/dsi/dsi_display.c b/msm/dsi/dsi_display.c index 99fbe2bcbc..128756537b 100644 --- a/msm/dsi/dsi_display.c +++ b/msm/dsi/dsi_display.c @@ -6445,7 +6445,10 @@ int dsi_display_get_info(struct drm_connector *connector, info->max_width = 1920; info->max_height = 1080; info->qsync_min_fps = - display->panel->qsync_min_fps; + display->panel->qsync_caps.qsync_min_fps; + info->has_qsync_min_fps_list = + (display->panel->qsync_caps.qsync_min_fps_list_len > 0) ? + true : false; info->poms_align_vsync = display->panel->poms_align_vsync; switch (display->panel->panel_mode) { @@ -6907,6 +6910,25 @@ int dsi_display_get_default_lms(void *dsi_display, u32 *num_lm) return rc; } +int dsi_display_get_qsync_min_fps(void *display_dsi, u32 mode_fps) +{ + struct dsi_display *display = (struct dsi_display *)display_dsi; + struct dsi_panel *panel; + u32 i; + + if (display == NULL || display->panel == NULL) + return -EINVAL; + + panel = display->panel; + for (i = 0; i < panel->dfps_caps.dfps_list_len; i++) { + if (panel->dfps_caps.dfps_list[i] == mode_fps) + return panel->qsync_caps.qsync_min_fps_list[i]; + } + SDE_EVT32(mode_fps); + DSI_DEBUG("Invalid mode_fps %d\n", mode_fps); + return -EINVAL; +} + int dsi_display_find_mode(struct dsi_display *display, const struct dsi_display_mode *cmp, struct dsi_display_mode **out_mode) @@ -7754,7 +7776,7 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable) int i; int rc = 0; - if (!display->panel->qsync_min_fps) { + if (!display->panel->qsync_caps.qsync_min_fps) { DSI_ERR("%s:ERROR: qsync set, but no fps\n", __func__); return 0; } @@ -7782,7 +7804,7 @@ static int dsi_display_qsync(struct dsi_display *display, bool enable) } exit: - SDE_EVT32(enable, display->panel->qsync_min_fps, rc); + SDE_EVT32(enable, display->panel->qsync_caps.qsync_min_fps, rc); mutex_unlock(&display->display_lock); return rc; } diff --git a/msm/dsi/dsi_display.h b/msm/dsi/dsi_display.h index 7406d78295..b732c231f7 100644 --- a/msm/dsi/dsi_display.h +++ b/msm/dsi/dsi_display.h @@ -407,6 +407,16 @@ void dsi_display_put_mode(struct dsi_display *display, */ int dsi_display_get_default_lms(void *dsi_display, u32 *num_lm); +/** + * dsi_display_get_qsync_min_fps() - get qsync min fps for given fps + * @display: Handle to display. + * @mode_fps: Fps value of current mode + * + * Return: error code. + */ +int dsi_display_get_qsync_min_fps(void *dsi_display, u32 mode_fps); + + /** * dsi_display_find_mode() - retrieve cached DSI mode given relevant params * @display: Handle to display. diff --git a/msm/dsi/dsi_drm.c b/msm/dsi/dsi_drm.c index 316c70c120..bc383bc446 100644 --- a/msm/dsi/dsi_drm.c +++ b/msm/dsi/dsi_drm.c @@ -628,14 +628,16 @@ int dsi_conn_set_info_blob(struct drm_connector *connector, case DSI_OP_VIDEO_MODE: sde_kms_info_add_keystr(info, "panel mode", "video"); sde_kms_info_add_keystr(info, "qsync support", - panel->qsync_min_fps ? "true" : "false"); + panel->qsync_caps.qsync_min_fps ? + "true" : "false"); break; case DSI_OP_CMD_MODE: sde_kms_info_add_keystr(info, "panel mode", "command"); sde_kms_info_add_keyint(info, "mdp_transfer_time_us", mode_info->mdp_transfer_time_us); sde_kms_info_add_keystr(info, "qsync support", - panel->qsync_min_fps ? "true" : "false"); + panel->qsync_caps.qsync_min_fps ? + "true" : "false"); break; default: DSI_DEBUG("invalid panel type:%d\n", panel->panel_mode); diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c index c132e6fb83..ea364f78ac 100644 --- a/msm/dsi/dsi_panel.c +++ b/msm/dsi/dsi_panel.c @@ -1219,8 +1219,15 @@ static int dsi_panel_parse_qsync_caps(struct dsi_panel *panel, struct device_node *of_node) { int rc = 0; - u32 val = 0; + u32 val = 0, i; + struct dsi_qsync_capabilities *qsync_caps = &panel->qsync_caps; + struct dsi_parser_utils *utils = &panel->utils; + const char *name = panel->name; + /** + * "mdss-dsi-qsync-min-refresh-rate" is defined in cmd mode and + * video mode when there is only one qsync min fps present. + */ rc = of_property_read_u32(of_node, "qcom,mdss-dsi-qsync-min-refresh-rate", &val); @@ -1228,8 +1235,75 @@ static int dsi_panel_parse_qsync_caps(struct dsi_panel *panel, DSI_DEBUG("[%s] qsync min fps not defined rc:%d\n", panel->name, rc); - panel->qsync_min_fps = val; + qsync_caps->qsync_min_fps = val; + /** + * "dsi-supported-qsync-min-fps-list" may be defined in video + * mode, only in dfps case when "qcom,dsi-supported-dfps-list" + * is defined. + */ + qsync_caps->qsync_min_fps_list_len = utils->count_u32_elems(utils->data, + "qcom,dsi-supported-qsync-min-fps-list"); + if (qsync_caps->qsync_min_fps_list_len < 1) + goto qsync_support; + + /** + * qcom,dsi-supported-qsync-min-fps-list cannot be defined + * along with qcom,mdss-dsi-qsync-min-refresh-rate. + */ + if (qsync_caps->qsync_min_fps_list_len >= 1 && + qsync_caps->qsync_min_fps) { + DSI_ERR("[%s] Both qsync nodes are defined\n", + name); + rc = -EINVAL; + goto error; + } + + if (panel->dfps_caps.dfps_list_len != + qsync_caps->qsync_min_fps_list_len) { + DSI_ERR("[%s] Qsync min fps list mismatch with dfps\n", name); + rc = -EINVAL; + goto error; + } + + qsync_caps->qsync_min_fps_list = + kcalloc(qsync_caps->qsync_min_fps_list_len, sizeof(u32), + GFP_KERNEL); + if (!qsync_caps->qsync_min_fps_list) { + rc = -ENOMEM; + goto error; + } + + rc = utils->read_u32_array(utils->data, + "qcom,dsi-supported-qsync-min-fps-list", + qsync_caps->qsync_min_fps_list, + qsync_caps->qsync_min_fps_list_len); + if (rc) { + DSI_ERR("[%s] Qsync min fps list parse failed\n", name); + rc = -EINVAL; + goto error; + } + + qsync_caps->qsync_min_fps = qsync_caps->qsync_min_fps_list[0]; + + for (i = 1; i < qsync_caps->qsync_min_fps_list_len; i++) { + if (qsync_caps->qsync_min_fps_list[i] < + qsync_caps->qsync_min_fps) + qsync_caps->qsync_min_fps = + qsync_caps->qsync_min_fps_list[i]; + } + +qsync_support: + /* allow qsync support only if DFPS is with VFP approach */ + if ((panel->dfps_caps.dfps_support) && + !(panel->dfps_caps.type == DSI_DFPS_IMMEDIATE_VFP)) + panel->qsync_caps.qsync_min_fps = 0; + +error: + if (rc < 0) { + qsync_caps->qsync_min_fps = 0; + qsync_caps->qsync_min_fps_list_len = 0; + } return rc; } @@ -3396,11 +3470,6 @@ struct dsi_panel *dsi_panel_get(struct device *parent, if (rc) DSI_DEBUG("failed to parse qsync features, rc=%d\n", rc); - /* allow qsync support only if DFPS is with VFP approach */ - if ((panel->dfps_caps.dfps_support) && - !(panel->dfps_caps.type == DSI_DFPS_IMMEDIATE_VFP)) - panel->qsync_min_fps = 0; - rc = dsi_panel_parse_dyn_clk_caps(panel); if (rc) DSI_ERR("failed to parse dynamic clk config, rc=%d\n", rc); diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h index 959f63519c..439144ae93 100644 --- a/msm/dsi/dsi_panel.h +++ b/msm/dsi/dsi_panel.h @@ -86,6 +86,13 @@ struct dsi_dfps_capabilities { bool dfps_support; }; +struct dsi_qsync_capabilities { + /* qsync disabled if qsync_min_fps = 0 */ + u32 qsync_min_fps; + u32 *qsync_min_fps_list; + int qsync_min_fps_list_len; +}; + struct dsi_dyn_clk_caps { bool dyn_clk_support; u32 *bit_clk_list; @@ -231,7 +238,7 @@ struct dsi_panel { bool panel_initialized; bool te_using_watchdog_timer; - u32 qsync_min_fps; + struct dsi_qsync_capabilities qsync_caps; char dce_pps_cmd[DSI_CMD_PPS_SIZE]; enum dsi_dms_mode dms_mode; diff --git a/msm/msm_drv.h b/msm/msm_drv.h index abb02fcc6d..58fc2d84c8 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -739,6 +739,7 @@ struct msm_resource_caps_info { * @poms_align_vsync: poms with vsync aligned * @roi_caps: Region of interest capability info * @qsync_min_fps Minimum fps supported by Qsync feature + * @has_qsync_min_fps_list True if dsi-supported-qsync-min-fps-list exits * @te_source vsync source pin information * @dsc_count: max dsc hw blocks used by display (only available * for dsi display) @@ -767,6 +768,8 @@ struct msm_display_info { struct msm_roi_caps roi_caps; uint32_t qsync_min_fps; + bool has_qsync_min_fps_list; + uint32_t te_source; uint32_t dsc_count; diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 80a7392ca6..1099065998 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); + + /** + * get_qsync_min_fps - Get qsync min fps from qsync-min-fps-list + * @display: Pointer to private display structure + * @mode_fps: Fps value in dfps list + * Returns: Qsync min fps value on success + */ + int (*get_qsync_min_fps)(void *display, u32 mode_fps); }; /** diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 7633a3af8b..b8a8d9c82f 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -3204,10 +3204,12 @@ static void sde_encoder_frame_done_callback( static void sde_encoder_get_qsync_fps_callback( struct drm_encoder *drm_enc, - u32 *qsync_fps) + u32 *qsync_fps, u32 vrr_fps) { struct msm_display_info *disp_info; struct sde_encoder_virt *sde_enc; + int rc = 0; + struct sde_connector *sde_conn; if (!qsync_fps) return; @@ -3221,6 +3223,31 @@ static void sde_encoder_get_qsync_fps_callback( sde_enc = to_sde_encoder_virt(drm_enc); disp_info = &sde_enc->disp_info; *qsync_fps = disp_info->qsync_min_fps; + + /** + * If "dsi-supported-qsync-min-fps-list" is defined, get + * the qsync min fps corresponding to the fps in dfps list + */ + if (disp_info->has_qsync_min_fps_list) { + + if (!sde_enc->cur_master || + !(sde_enc->disp_info.capabilities & + MSM_DISPLAY_CAP_VID_MODE)) { + SDE_ERROR("invalid qsync settings %b\n", + !sde_enc->cur_master); + return; + } + sde_conn = to_sde_connector(sde_enc->cur_master->connector); + + if (sde_conn->ops.get_qsync_min_fps) + rc = sde_conn->ops.get_qsync_min_fps(sde_conn->display, + vrr_fps); + if (rc <= 0) { + SDE_ERROR("invalid qsync min fps %d\n", rc); + return; + } + *qsync_fps = rc; + } } int sde_encoder_idle_request(struct drm_encoder *drm_enc) diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index 283b75d8e4..019130364b 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -82,7 +82,7 @@ struct sde_encoder_virt_ops { void (*handle_frame_done)(struct drm_encoder *parent, struct sde_encoder_phys *phys, u32 event); void (*get_qsync_fps)(struct drm_encoder *parent, - u32 *qsync_fps); + u32 *qsync_fps, u32 vrr_fps); }; /** diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index eac76f9580..5b2ec6d87b 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -957,7 +957,7 @@ static int _get_tearcheck_threshold(struct sde_encoder_phys *phys_enc) if (phys_enc->parent_ops.get_qsync_fps) phys_enc->parent_ops.get_qsync_fps( - phys_enc->parent, &qsync_min_fps); + phys_enc->parent, &qsync_min_fps, 0); if (!qsync_min_fps || !default_fps || !yres) { SDE_ERROR_CMDENC(cmd_enc, diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 03dae31704..3f57cc81c6 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -470,7 +470,7 @@ static void sde_encoder_phys_vid_setup_timing_engine( exit: if (phys_enc->parent_ops.get_qsync_fps) phys_enc->parent_ops.get_qsync_fps( - phys_enc->parent, &qsync_min_fps); + phys_enc->parent, &qsync_min_fps, mode.vrefresh); /* only panels which support qsync will have a non-zero min fps */ if (qsync_min_fps) { diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index 239e5dfb59..e11ed1b6ce 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -1752,6 +1752,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, + .get_qsync_min_fps = dsi_display_get_qsync_min_fps, }; static const struct sde_connector_ops wb_ops = { .post_init = sde_wb_connector_post_init,