From: Ramkumar Radhakrishnan Date: Wed, 13 Jul 2016 21:56:31 +0000 (-0700) Subject: msm: mdss: add support for adaptive variable refresh feature X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=3d764869b9039dcf51a0dc1496cd91583880f512;p=sagit-ice-cold%2Fkernel_xiaomi_msm8998.git msm: mdss: add support for adaptive variable refresh feature Adaptive variable refresh(AVR) allows timing modification of a current frame by extending the vertical front porch values. This helps shifting the vsync to slowest vsync interval supported by panel when GPU rendering ready event is delayed. Define an interface for AVR to enable client to control AVR settings. Validate client input for AVR and configure the corresponding AVR registers. CRs-Fixed: 1043819 Change-Id: I28f786bf0bdea4ecaa17270974ba6d7ffe469828 Signed-off-by: Ramkumar Radhakrishnan --- 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