diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 35ef717ffd..f957ae6618 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -223,6 +223,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_QSYNC_MODE, CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE, CONNECTOR_PROP_SET_PANEL_MODE, + CONNECTOR_PROP_AVR_STEP, /* total # of properties */ CONNECTOR_PROP_COUNT diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index e58a48e768..6e3dc0a049 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -697,7 +697,7 @@ void sde_connector_set_qsync_params(struct drm_connector *connector) { struct sde_connector *c_conn; struct sde_connector_state *c_state; - u32 qsync_propval = 0; + u32 qsync_propval = 0, step_val = 0; bool prop_dirty; if (!connector) @@ -720,6 +720,17 @@ void sde_connector_set_qsync_params(struct drm_connector *connector) c_conn->qsync_mode = qsync_propval; } } + + prop_dirty = msm_property_is_dirty(&c_conn->property_info, &c_state->property_state, + CONNECTOR_PROP_AVR_STEP); + if (prop_dirty) { + step_val = sde_connector_get_property(c_conn->base.state, CONNECTOR_PROP_AVR_STEP); + if (step_val != c_conn->avr_step) { + SDE_DEBUG("updated avr step %d -> %d\n", c_conn->avr_step, step_val); + c_conn->qsync_updated = true; + c_conn->avr_step = step_val; + } + } } void sde_connector_complete_qsync_commit(struct drm_connector *conn, @@ -2870,11 +2881,16 @@ static int _sde_connector_install_properties(struct drm_device *dev, CONNECTOR_PROP_AUTOREFRESH); if (connector_type == DRM_MODE_CONNECTOR_DSI) { - if (sde_kms->catalog->has_qsync && display_info->qsync_min_fps) + if (sde_kms->catalog->has_qsync && display_info->qsync_min_fps) { msm_property_install_enum(&c_conn->property_info, "qsync_mode", 0, 0, e_qsync_mode, ARRAY_SIZE(e_qsync_mode), 0, CONNECTOR_PROP_QSYNC_MODE); + if (sde_kms->catalog->has_avr_step) + msm_property_install_range(&c_conn->property_info, + "avr_step", 0x0, 0, U32_MAX, 0, + CONNECTOR_PROP_AVR_STEP); + } if (display_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) msm_property_install_enum(&c_conn->property_info, diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 4a5f5456e5..b4c29da041 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -484,6 +484,7 @@ struct sde_connector_dyn_hdr_metadata { * @allow_bl_update: Flag to indicate if BL update is allowed currently or not * @qsync_mode: Cached Qsync mode, 0=disabled, 1=continuous mode * @qsync_updated: Qsync settings were updated + * @avr_step: fps rate for fixed steps in AVR mode; 0 means step is disabled * @colorspace_updated: Colorspace property was updated * @last_cmd_tx_sts: status of the last command transfer * @hdr_capable: external hdr support present @@ -553,6 +554,7 @@ struct sde_connector { u8 hdr_plus_app_ver; u32 qsync_mode; bool qsync_updated; + u32 avr_step; bool colorspace_updated; @@ -604,6 +606,13 @@ struct sde_connector { #define sde_connector_get_qsync_mode(C) \ ((C) ? to_sde_connector((C))->qsync_mode : 0) +/** + * sde_connector_get_avr_step - get sde connector's avr_step + * @C: Pointer to drm connector structure + * Returns: Current cached avr_step value for given connector + */ +#define sde_connector_get_avr_step(C) ((C) ? to_sde_connector((C))->avr_step : 0) + /** * sde_connector_get_propinfo - get sde connector's property info pointer * @C: Pointer to drm connector structure diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index f0982c8e2a..0064ba2229 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -995,6 +995,101 @@ static int _sde_encoder_atomic_check_reserve(struct drm_encoder *drm_enc, return ret; } +static void _sde_encoder_get_qsync_fps_callback( + struct drm_encoder *drm_enc, 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; + + *qsync_fps = 0; + if (!drm_enc) { + SDE_ERROR("invalid drm encoder\n"); + return; + } + + sde_enc = to_sde_encoder_virt(drm_enc); + disp_info = &sde_enc->disp_info; + *qsync_fps = disp_info->qsync_min_fps; + + if (!disp_info->has_qsync_min_fps_list) { + return; + } else if (!sde_enc->cur_master || !(disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE)) { + SDE_ERROR("invalid qsync settings %d\n", !sde_enc->cur_master); + return; + } + + /* + * If "dsi-supported-qsync-min-fps-list" is defined, get + * the qsync min fps corresponding to the fps in dfps list + */ + 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; +} + +static int _sde_encoder_avr_step_check(struct sde_connector *sde_conn, + struct sde_connector_state *sde_conn_state, u32 step) +{ + u32 nom_fps = drm_mode_vrefresh(sde_conn_state->msm_mode.base); + u32 min_fps; + u32 vtotal = sde_conn_state->msm_mode.base->vtotal; + + if (!step) + return 0; + + _sde_encoder_get_qsync_fps_callback(sde_conn_state->base.best_encoder, &min_fps, nom_fps); + if (!min_fps || !nom_fps || step % nom_fps || step % min_fps || step < nom_fps || + (vtotal * nom_fps) % step) { + SDE_ERROR("invalid avr_step rate! nom:%u min:%u step:%u vtotal:%u\n", nom_fps, + min_fps, step, vtotal); + return -EINVAL; + } + + return 0; +} + +static int _sde_encoder_atomic_check_qsync(struct sde_connector *sde_conn, + struct sde_connector_state *sde_conn_state) +{ + int rc = 0; + u32 avr_step; + bool qsync_dirty, has_modeset; + struct drm_connector_state *conn_state = &sde_conn_state->base; + u32 qsync_mode = sde_connector_get_property(&sde_conn_state->base, + CONNECTOR_PROP_QSYNC_MODE); + + has_modeset = sde_crtc_atomic_check_has_modeset(conn_state->state, conn_state->crtc); + qsync_dirty = msm_property_is_dirty(&sde_conn->property_info, + &sde_conn_state->property_state, CONNECTOR_PROP_QSYNC_MODE); + + if (has_modeset && qsync_dirty && + (msm_is_mode_seamless_poms(&sde_conn_state->msm_mode) || + msm_is_mode_seamless_dms(&sde_conn_state->msm_mode) || + msm_is_mode_seamless_dyn_clk(&sde_conn_state->msm_mode))) { + SDE_ERROR("invalid qsync update during modeset priv flag:%x\n", + sde_conn_state->msm_mode.private_flags); + return -EINVAL; + } + + avr_step = sde_connector_get_property(conn_state, CONNECTOR_PROP_AVR_STEP); + if (qsync_dirty || (avr_step != sde_conn->avr_step) || (qsync_mode && has_modeset)) + rc = _sde_encoder_avr_step_check(sde_conn, sde_conn_state, avr_step); + + return rc; +} + static int sde_encoder_virt_atomic_check( struct drm_encoder *drm_enc, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) @@ -1010,7 +1105,6 @@ static int sde_encoder_virt_atomic_check( enum sde_rm_topology_name top_name; struct msm_display_info *disp_info; int ret = 0; - bool qsync_dirty = false, has_modeset = false; if (!drm_enc || !crtc_state || !conn_state) { SDE_ERROR("invalid arg(s), drm_enc %d, crtc/conn state %d/%d\n", @@ -1083,25 +1177,12 @@ static int sde_encoder_virt_atomic_check( drm_mode_set_crtcinfo(adj_mode, 0); - has_modeset = sde_crtc_atomic_check_has_modeset(conn_state->state, - conn_state->crtc); - qsync_dirty = msm_property_is_dirty(&sde_conn->property_info, - &sde_conn_state->property_state, - CONNECTOR_PROP_QSYNC_MODE); - - if (has_modeset && qsync_dirty && - (msm_is_mode_seamless_poms(&sde_conn_state->msm_mode) || - msm_is_mode_seamless_dms(&sde_conn_state->msm_mode) || - msm_is_mode_seamless_dyn_clk(&sde_conn_state->msm_mode))) { - SDE_ERROR("invalid qsync update during modeset priv flag:%x\n", - sde_conn_state->msm_mode.private_flags); - return -EINVAL; - } + ret = _sde_encoder_atomic_check_qsync(sde_conn, sde_conn_state); SDE_EVT32(DRMID(drm_enc), adj_mode->flags, sde_conn_state->msm_mode.private_flags, old_top, drm_mode_vrefresh(adj_mode), adj_mode->hdisplay, - adj_mode->vdisplay, adj_mode->htotal, adj_mode->vtotal); + adj_mode->vdisplay, adj_mode->htotal, adj_mode->vtotal, ret); return ret; } @@ -3342,54 +3423,6 @@ static void sde_encoder_frame_done_callback( } } -static void sde_encoder_get_qsync_fps_callback( - struct drm_encoder *drm_enc, - 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; - - *qsync_fps = 0; - if (!drm_enc) { - SDE_ERROR("invalid drm encoder\n"); - return; - } - - 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 %d\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) { struct sde_encoder_virt *sde_enc; @@ -4905,7 +4938,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc, sde_encoder_vblank_callback, sde_encoder_underrun_callback, sde_encoder_frame_done_callback, - sde_encoder_get_qsync_fps_callback, + _sde_encoder_get_qsync_fps_callback, }; struct sde_enc_phys_init_params phys_params; diff --git a/msm/sde/sde_encoder_phys_vid.c b/msm/sde/sde_encoder_phys_vid.c index 913086cc6a..4963459e43 100644 --- a/msm/sde/sde_encoder_phys_vid.c +++ b/msm/sde/sde_encoder_phys_vid.c @@ -367,21 +367,20 @@ static void _sde_encoder_phys_vid_setup_avr( static void _sde_encoder_phys_vid_avr_ctrl(struct sde_encoder_phys *phys_enc) { struct intf_avr_params avr_params; - struct sde_encoder_phys_vid *vid_enc = - to_sde_encoder_phys_vid(phys_enc); + struct sde_encoder_phys_vid *vid_enc = to_sde_encoder_phys_vid(phys_enc); + u32 avr_step_fps = sde_connector_get_avr_step(phys_enc->connector); - avr_params.avr_mode = sde_connector_get_qsync_mode( - phys_enc->connector); + memset(&avr_params, 0, sizeof(avr_params)); + avr_params.avr_mode = sde_connector_get_qsync_mode(phys_enc->connector); + if (avr_step_fps) + avr_params.avr_step_lines = mult_frac(phys_enc->cached_mode.vtotal, + vid_enc->timing_params.vrefresh, avr_step_fps); - if (vid_enc->base.hw_intf->ops.avr_ctrl) { - vid_enc->base.hw_intf->ops.avr_ctrl( - vid_enc->base.hw_intf, - &avr_params); - } + if (vid_enc->base.hw_intf->ops.avr_ctrl) + vid_enc->base.hw_intf->ops.avr_ctrl(vid_enc->base.hw_intf, &avr_params); - SDE_EVT32(DRMID(phys_enc->parent), - phys_enc->hw_intf->idx - INTF_0, - avr_params.avr_mode); + SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_intf->idx - INTF_0, + avr_params.avr_mode, avr_params.avr_step_lines, avr_step_fps); } static void sde_encoder_phys_vid_setup_timing_engine( diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index fee160ccba..c5baf936a2 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -5070,6 +5070,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) sde_cfg->has_fp16 = true; set_bit(SDE_MDP_PERIPH_TOP_0_REMOVED, &sde_cfg->mdp[0].features); sde_cfg->has_precise_vsync_ts = true; + sde_cfg->has_avr_step = true; sde_cfg->has_trusted_vm_support = true; } else if (IS_YUPIK_TARGET(hw_rev)) { sde_cfg->has_cwb_support = true; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 38710bf6b0..759aab3601 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -1517,6 +1517,7 @@ struct sde_perf_cfg { * @has_3d_merge_reset Supports 3D merge reset * @has_decimation Supports decimation * @has_trusted_vm_support Supported HW sharing with trusted VM + * @has_avr_step Supports AVR with vsync alignment to a set step rate * @rc_lm_flush_override Support Rounded Corner using layer mixer flush * @has_mixer_combined_alpha Mixer has single register for FG & BG alpha * @vbif_disable_inner_outer_shareable VBIF requires disabling shareables @@ -1604,6 +1605,7 @@ struct sde_mdss_cfg { bool has_base_layer; bool has_demura; bool has_trusted_vm_support; + bool has_avr_step; bool rc_lm_flush_override; u32 demura_supported[SSPP_MAX][2]; u32 qseed_sw_lib_rev; diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index e0652c7d68..577f12905f 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -175,9 +175,10 @@ static void sde_hw_intf_avr_ctrl(struct sde_hw_intf *ctx, c = &ctx->hw; if (avr_params->avr_mode) { avr_ctrl = BIT(0); - avr_mode = - (avr_params->avr_mode == SDE_RM_QSYNC_ONE_SHOT_MODE) ? - (BIT(0) | BIT(8)) : 0x0; + avr_mode = (avr_params->avr_mode == SDE_RM_QSYNC_ONE_SHOT_MODE) ? + (BIT(0) | BIT(8)) : 0x0; + if (avr_params->avr_step_lines) + avr_mode |= avr_params->avr_step_lines << 16; } SDE_REG_WRITE(c, INTF_AVR_CONTROL, avr_ctrl); diff --git a/msm/sde/sde_hw_intf.h b/msm/sde/sde_hw_intf.h index 1867d79a31..1619d1ffac 100644 --- a/msm/sde/sde_hw_intf.h +++ b/msm/sde/sde_hw_intf.h @@ -63,7 +63,8 @@ struct intf_tear_status { struct intf_avr_params { u32 default_fps; u32 min_fps; - u32 avr_mode; /* 0 - disable, 1 - continuous, 2 - one-shot */ + u32 avr_mode; /* one of enum @sde_rm_qsync_modes */ + u32 avr_step_lines; /* 0 or 1 means disabled */ }; /**