"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.
MDSS_CAPS_10_BIT_SUPPORTED,
MDSS_CAPS_CWB_SUPPORTED,
MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
+ MDSS_CAPS_AVR_SUPPORTED,
MDSS_CAPS_MAX,
};
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");
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");
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,
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;
}
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 */
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
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.
/* 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 {
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;
/* dynamic resolution switch during cont-splash handoff */
bool switch_with_handoff;
+ struct mdss_mdp_avr_info avr_info;
};
struct mdss_mdp_mixer {
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);
/*
#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
}
}
+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)
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);
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);
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;
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;
}
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
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)) {
}
}
+ 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 */
}
}
+ 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);
}
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;
*/
#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