diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index 9fc942cc627d..90abf0305319 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -152,7 +152,8 @@ Optional properties: "dfps_immediate_porch_mode_vfp" = FPS change request is implemented immediately by changing panel vertical front porch values. -- qcom,min-refresh-rate: Minimum refresh rate supported by the panel. +- qcom,min-refresh-rate: Minimum refresh rate supported by the panel. Used in + adaptive variable refresh(AVR) to compute new avr vtotal - qcom,max-refresh-rate: Maximum refresh rate supported by the panel. If max refresh rate is not specified, then the frame rate of the panel in qcom,mdss-dsi-panel-framerate is used. diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 88950e9cb2aa..b601cafba6fd 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -176,6 +176,7 @@ enum mdss_hw_capabilities { MDSS_CAPS_10_BIT_SUPPORTED, MDSS_CAPS_CWB_SUPPORTED, MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED, + MDSS_CAPS_AVR_SUPPORTED, MDSS_CAPS_MAX, }; diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 9548ea471385..2e54f335e948 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -1967,7 +1967,6 @@ static void mdss_dsi_parse_dfps_config(struct device_node *pan_node, pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; pr_debug("default dfps mode: suspend/resume\n"); } - mdss_dsi_set_refresh_rate_range(pan_node, pinfo); } else { pinfo->dynamic_fps = false; pr_debug("dfps update mode not configured: disable\n"); @@ -2528,6 +2527,8 @@ static int mdss_panel_parse_dt(struct device_node *np, mdss_dsi_parse_dfps_config(np, ctrl_pdata); + mdss_dsi_set_refresh_rate_range(np, pinfo); + pinfo->is_dba_panel = of_property_read_bool(np, "qcom,dba-panel"); diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index cc8ce49c7387..99a924c38d65 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -552,13 +552,19 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev, struct msm_fb_data_type *mfd = fbi->par; struct mdss_panel_info *pinfo = mfd->panel_info; int ret; + bool dfps_porch_mode = false; + + if (pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP || + pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP) + dfps_porch_mode = true; ret = scnprintf(buf, PAGE_SIZE, "pu_en=%d\nxstart=%d\nwalign=%d\nystart=%d\nhalign=%d\n" "min_w=%d\nmin_h=%d\nroi_merge=%d\ndyn_fps_en=%d\n" "min_fps=%d\nmax_fps=%d\npanel_name=%s\n" "primary_panel=%d\nis_pluggable=%d\ndisplay_id=%s\n" - "is_cec_supported=%d\nis_pingpong_split=%d\n", + "is_cec_supported=%d\nis_pingpong_split=%d\n" + "dfps_porch_mode=%d\n", pinfo->partial_update_enabled, pinfo->roi_alignment.xstart_pix_align, pinfo->roi_alignment.width_pix_align, @@ -570,7 +576,8 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev, pinfo->dynamic_fps, pinfo->min_fps, pinfo->max_fps, pinfo->panel_name, pinfo->is_prim_panel, pinfo->is_pluggable, pinfo->display_id, - pinfo->is_cec_supported, is_pingpong_split(mfd)); + pinfo->is_cec_supported, is_pingpong_split(mfd), + dfps_porch_mode); return ret; } diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 5a355f226179..518b84fbad51 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1950,6 +1950,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_set_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS); mdata->has_wb_ubwc = true; set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map); + set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map); break; default: mdata->max_target_zorder = 4; /* excluding base layer */ @@ -2489,6 +2490,8 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev, SPRINT(" separate_rotator"); if (test_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map)) SPRINT(" concurrent_writeback"); + if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) + SPRINT(" avr"); SPRINT("\n"); #undef SPRINT diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index da5e7bb8a343..da60570c7085 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -303,6 +303,11 @@ enum mdp_wb_blk_caps { MDSS_MDP_WB_UBWC = BIT(3), }; +enum mdss_mdp_avr_mode { + MDSS_MDP_AVR_CONTINUOUS = 0, + MDSS_MDP_AVR_ONE_SHOT, +}; + /** * enum perf_calc_vote_mode - enum to decide if mdss_mdp_get_bw_vote_mode * function needs an extra efficiency factor. @@ -391,6 +396,7 @@ struct mdss_mdp_ctl_intfs_ops { /* to update lineptr, [1..yres] - enable, 0 - disable */ int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable); + int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *); }; struct mdss_mdp_cwb { @@ -406,6 +412,11 @@ struct mdss_mdp_cwb { struct work_struct cwb_work; }; +struct mdss_mdp_avr_info { + bool avr_enabled; + int avr_mode; +}; + struct mdss_mdp_ctl { u32 num; char __iomem *base; @@ -513,6 +524,7 @@ struct mdss_mdp_ctl { /* dynamic resolution switch during cont-splash handoff */ bool switch_with_handoff; + struct mdss_mdp_avr_info avr_info; }; struct mdss_mdp_mixer { diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 3fc6d94393d5..1a0ba8f0e2a7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -5528,6 +5528,26 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, sctl = mdss_mdp_get_split_ctl(ctl); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + if (ctl->ops.avr_ctrl_fnc) { + ret = ctl->ops.avr_ctrl_fnc(ctl); + if (ret) { + pr_err("error configuring avr ctrl registers ctl=%d err=%d\n", + ctl->num, ret); + mutex_unlock(&ctl->lock); + return ret; + } + } + + if (sctl && sctl->ops.avr_ctrl_fnc) { + ret = sctl->ops.avr_ctrl_fnc(sctl); + if (ret) { + pr_err("error configuring avr ctrl registers sctl=%d err=%d\n", + sctl->num, ret); + mutex_unlock(&ctl->lock); + return ret; + } + } + mutex_lock(&ctl->flush_lock); /* diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index 74ab902f6e8e..de868bcd8f6f 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -688,6 +688,11 @@ enum mdss_mpd_intf_index { #define MDSS_MDP_REG_INTF_PROG_LINE_INTR_CONF 0x250 #define MDSS_MDP_REG_INTF_VBLANK_END_CONF 0x264 +#define MDSS_MDP_REG_INTF_AVR_CONTROL 0x270 +#define MDSS_MDP_REG_INTF_AVR_MODE 0x274 +#define MDSS_MDP_REG_INTF_AVR_TRIGGER 0x278 +#define MDSS_MDP_REG_INTF_AVR_VTOTAL 0x27C + #define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8 #define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC #define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0 diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index d1ced303b059..ee14fd0d0660 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -403,6 +403,76 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) } } +static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl, + struct intf_timing_params *p, + struct mdss_mdp_video_ctx *ctx) +{ + struct mdss_data_type *mdata = ctl->mdata; + + if (test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) { + struct mdss_panel_data *pdata = ctl->panel_data; + u32 hsync_period = p->hsync_pulse_width + p->h_back_porch + + p->width + p->h_front_porch; + u32 vsync_period = p->vsync_pulse_width + p->v_back_porch + + p->height + p->v_front_porch; + u32 min_fps = pdata->panel_info.min_fps; + u32 diff_fps = abs(pdata->panel_info.default_fps - min_fps); + u32 vtotal = mdss_panel_get_vtotal(&pdata->panel_info); + + int add_porches = mult_frac(vtotal, diff_fps, min_fps); + + u32 vsync_period_slow = vsync_period + add_porches; + u32 avr_vtotal = vsync_period_slow * hsync_period; + + mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_VTOTAL, avr_vtotal); + + MDSS_XLOG(min_fps, vsync_period, vsync_period_slow, avr_vtotal); + } +} + +static int mdss_mdp_video_avr_trigger_setup(struct mdss_mdp_ctl *ctl) +{ + struct mdss_mdp_video_ctx *ctx = NULL; + struct mdss_data_type *mdata = ctl->mdata; + + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; + if (!ctx || !ctx->ref_cnt) { + pr_err("invalid master ctx\n"); + return -EINVAL; + } + + if (!ctl->is_master) + return 0; + + if (ctl->avr_info.avr_enabled && + test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map)) + mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_TRIGGER, 1); + + return 0; +} + +static void mdss_mdp_video_avr_ctrl_setup(struct mdss_mdp_video_ctx *ctx, + struct mdss_mdp_avr_info *avr_info, bool is_master) +{ + u32 avr_ctrl = 0; + u32 avr_mode = 0; + + avr_ctrl = avr_info->avr_enabled; + avr_mode = avr_info->avr_mode; + + /* Enable avr_vsync_clear_en bit to clear avr in next vsync */ + if (avr_mode == MDSS_MDP_AVR_ONE_SHOT) + avr_mode |= (1 << 8); + + if (is_master) + mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_CONTROL, avr_ctrl); + + mdp_video_write(ctx, MDSS_MDP_REG_INTF_AVR_MODE, avr_mode); + + pr_debug("intf:%d avr_mode:%x avr_ctrl:%x\n", + ctx->intf_num, avr_mode, avr_ctrl); +} + static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, struct intf_timing_params *p, struct mdss_mdp_video_ctx *ctx) @@ -1530,6 +1600,12 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) CTL_INTF_EVENT_FLAG_DEFAULT); } + rc = mdss_mdp_video_avr_trigger_setup(ctl); + if (rc) { + pr_err("avr trigger setup failed\n"); + return rc; + } + if (mdss_mdp_is_lineptr_supported(ctl)) mdss_mdp_video_lineptr_ctrl(ctl, true); @@ -1886,6 +1962,8 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, mdss_mdp_handoff_programmable_fetch(ctl, ctx); } + mdss_mdp_video_avr_vtotal_setup(ctl, itp, ctx); + mdss_mdp_disable_prefill(ctl); mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format); @@ -2124,6 +2202,29 @@ static int mdss_mdp_video_early_wake_up(struct mdss_mdp_ctl *ctl) return 0; } +static int mdss_mdp_video_avr_ctrl(struct mdss_mdp_ctl *ctl) +{ + struct mdss_mdp_video_ctx *ctx = NULL, *sctx = NULL; + + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; + if (!ctx || !ctx->ref_cnt) { + pr_err("invalid master ctx\n"); + return -EINVAL; + } + mdss_mdp_video_avr_ctrl_setup(ctx, &ctl->avr_info, ctl->is_master); + + if (is_pingpong_split(ctl->mfd)) { + sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX]; + if (!sctx || !sctx->ref_cnt) { + pr_err("invalid slave ctx\n"); + return -EINVAL; + } + mdss_mdp_video_avr_ctrl_setup(sctx, &ctl->avr_info, false); + } + + return 0; +} + int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) { int intfs_num, ret = 0; @@ -2144,6 +2245,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) ctl->ops.config_fps_fnc = mdss_mdp_video_config_fps; ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up; ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl; + ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl; return 0; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 0ae420724d61..35bd0932f321 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -288,6 +288,57 @@ static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd, return ret; } +static int mdss_mdp_avr_validate(struct msm_fb_data_type *mfd, + struct mdp_layer_commit_v1 *commit) +{ + struct mdss_data_type *mdata = mfd_to_mdata(mfd); + struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); + int req = 0; + struct mdss_panel_info *pinfo = NULL; + + if (!ctl || !mdata || !commit) { + pr_err("Invalid input parameters\n"); + return -EINVAL; + } + + if (!(commit->flags & MDP_COMMIT_AVR_EN)) + return 0; + + pinfo = &ctl->panel_data->panel_info; + + if (!test_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map) || + (pinfo->max_fps == pinfo->min_fps)) { + pr_err("AVR not supported\n"); + return -ENODEV; + } + + if (pinfo->dynamic_fps && + !(pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP || + pinfo->dfps_update == DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP)) { + pr_err("Dynamic fps and AVR cannot coexists\n"); + return -EINVAL; + } + + if (!ctl->is_video_mode) { + pr_err("AVR not supported in command mode\n"); + return -EINVAL; + } + + return req; +} + +static void __update_avr_info(struct mdss_mdp_ctl *ctl, + struct mdp_layer_commit_v1 *commit) +{ + if (commit->flags & MDP_COMMIT_AVR_EN) + ctl->avr_info.avr_enabled = true; + + ctl->avr_info.avr_mode = MDSS_MDP_AVR_CONTINUOUS; + + if (commit->flags & MDP_COMMIT_AVR_ONE_SHOT_MODE) + ctl->avr_info.avr_mode = MDSS_MDP_AVR_ONE_SHOT; +} + /* * __layer_needs_src_split() - check needs source split configuration * @layer: input layer @@ -2247,13 +2298,13 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd, struct mdss_overlay_private *mdp5_data; struct mdss_mdp_data *src_data[MDSS_MDP_MAX_SSPP]; struct mdss_mdp_validate_info_t *validate_info_list; + struct mdss_mdp_ctl *sctl = NULL; mdp5_data = mfd_to_mdp5_data(mfd); if (!mdp5_data || !mdp5_data->ctl) return -EINVAL; - if (commit->output_layer) { ret = __is_cwb_requested(commit->output_layer->flags); if (IS_ERR_VALUE(ret)) { @@ -2267,6 +2318,18 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd, } } + ret = mdss_mdp_avr_validate(mfd, commit); + if (IS_ERR_VALUE(ret)) { + pr_err("AVR validate failed\n"); + return -EINVAL; + } + + __update_avr_info(mdp5_data->ctl, commit); + + sctl = mdss_mdp_get_split_ctl(mdp5_data->ctl); + if (sctl) + __update_avr_info(sctl, commit); + layer_list = commit->input_layers; /* handle null commit */ @@ -2428,6 +2491,12 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd, } } + rc = mdss_mdp_avr_validate(mfd, commit); + if (IS_ERR_VALUE(rc)) { + pr_err("AVR validate failed\n"); + return -EINVAL; + } + return __validate_layers(mfd, file, commit); } diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c index 97025b3a9c23..61911810b2c0 100644 --- a/drivers/video/fbdev/msm/mdss_panel.c +++ b/drivers/video/fbdev/msm/mdss_panel.c @@ -644,6 +644,7 @@ void mdss_panel_info_from_timing(struct mdss_panel_timing *pt, pinfo->dsc_enc_total = pt->dsc_enc_total; pinfo->fbc = pt->fbc; pinfo->compression_mode = pt->compression_mode; + pinfo->default_fps = pinfo->mipi.frame_rate; pinfo->roi_alignment = pt->roi_alignment; pinfo->te = pt->te; diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h index 8472224f33a6..811d8b4e1994 100644 --- a/include/uapi/linux/msm_mdp_ext.h +++ b/include/uapi/linux/msm_mdp_ext.h @@ -138,6 +138,15 @@ VALIDATE/COMMIT FLAG CONFIGURATION */ #define MDP_COMMIT_SYNC_FENCE_WAIT 0x04 +/* Flag to enable AVR(Adaptive variable refresh) feature. */ +#define MDP_COMMIT_AVR_EN 0x08 + +/* + * Flag to select one shot mode when AVR feature is enabled. + * Default mode is continuous mode. + */ +#define MDP_COMMIT_AVR_ONE_SHOT_MODE 0x10 + /* Flag to enable concurrent writeback for the frame */ #define MDP_COMMIT_CWB_EN 0x800