OSDN Git Service

msm: mdss: add support for adaptive variable refresh feature
authorRamkumar Radhakrishnan <ramkumar@codeaurora.org>
Wed, 13 Jul 2016 21:56:31 +0000 (14:56 -0700)
committerGerrit - the friendly Code Review server <code-review@localhost>
Fri, 29 Jul 2016 18:14:55 +0000 (11:14 -0700)
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 <ramkumar@codeaurora.org>
12 files changed:
Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
drivers/video/fbdev/msm/mdss.h
drivers/video/fbdev/msm/mdss_dsi_panel.c
drivers/video/fbdev/msm/mdss_fb.c
drivers/video/fbdev/msm/mdss_mdp.c
drivers/video/fbdev/msm/mdss_mdp.h
drivers/video/fbdev/msm/mdss_mdp_ctl.c
drivers/video/fbdev/msm/mdss_mdp_hwio.h
drivers/video/fbdev/msm/mdss_mdp_intf_video.c
drivers/video/fbdev/msm/mdss_mdp_layer.c
drivers/video/fbdev/msm/mdss_panel.c
include/uapi/linux/msm_mdp_ext.h

index 9fc942c..90abf03 100644 (file)
@@ -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.
index 88950e9..b601caf 100644 (file)
@@ -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,
 };
 
index 9548ea4..2e54f33 100644 (file)
@@ -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");
 
index cc8ce49..99a924c 100644 (file)
@@ -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;
 }
index 5a355f2..518b84f 100644 (file)
@@ -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
 
index da5e7bb..da60570 100644 (file)
@@ -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 {
index 3fc6d94..1a0ba8f 100644 (file)
@@ -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);
 
        /*
index 74ab902..de868bc 100644 (file)
@@ -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
index d1ced30..ee14fd0 100644 (file)
@@ -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;
 }
index 0ae4207..35bd093 100644 (file)
@@ -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);
 }
 
index 97025b3..6191181 100644 (file)
@@ -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;
index 8472224..811d8b4 100644 (file)
@@ -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