diff options
| author | Ingrid Gallardo <ingridg@codeaurora.org> | 2015-12-16 16:43:03 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 21:14:48 -0700 |
| commit | 92198b45df854e503d3b2e955928fb8bf2d751a5 (patch) | |
| tree | f89a7988bb1aac6e6dcb89abeaf47bafd43914f2 /drivers/video | |
| parent | 23249cac8fb15b6ebcef7c3057d08aea5e491deb (diff) | |
msm: mdss: cleanup auto-refresh implementation
Auto-refresh is used on smart displays to refresh display without
SW intervention, similar to dumb displays. So when it is on, display
controller will automatically send new frame every time panel TE signal
is received. Now as per the HW implementation, disabling auto-refresh
requires that display controller is idle and there is no frame transfer
on-going. To achieve this, we need to follow certain disable sequence.
To implement this disable sequence, clean up current implementation and
introduce auto-refresh state machine.
Change-Id: Iac5d76b6431f7ef2fcd68114e19c5cb5fb02577d
Signed-off-by: Ingrid Gallardo <ingridg@codeaurora.org>
Signed-off-by: Ujwal Patel <ujwalp@codeaurora.org>
Diffstat (limited to 'drivers/video')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss.h | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 13 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 13 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_debug.c | 8 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 684 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_overlay.c | 54 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_util.c | 21 |
7 files changed, 617 insertions, 177 deletions
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 02b1910e3762..66505ea2c0a0 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -353,6 +353,7 @@ struct mdss_data_type { u32 enable_bw_release; u32 enable_rotator_bw_release; u32 serialize_wait4pp; + u32 wait4autorefresh; u32 lines_before_active; struct mdss_hw_settings *hw_settings; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 03fe9cc6b43b..0e25c0ab2a6a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -265,6 +265,8 @@ struct mdss_mdp_ctl_intfs_ops { */ int (*reconfigure)(struct mdss_mdp_ctl *ctl, enum dynamic_switch_modes mode, bool pre); + /* called before do any register programming from commit thread */ + void (*pre_programming)(struct mdss_mdp_ctl *ctl); }; struct mdss_mdp_ctl { @@ -355,9 +357,6 @@ struct mdss_mdp_ctl { struct mdss_rect roi; struct mdss_rect roi_bkup; - bool cmd_autorefresh_en; - int autorefresh_frame_cnt; - struct blocking_notifier_head notifier_head; void *priv_data; @@ -1473,10 +1472,10 @@ void mdss_mdp_ctl_restore(bool locked); int mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl, bool is_recovery); int mdss_mdp_wait_for_xin_halt(u32 xin_id, bool is_vbif_nrt); void mdss_mdp_set_ot_limit(struct mdss_mdp_set_ot_params *params); -int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *ctl, - int frame_cnt); -int mdss_mdp_ctl_cmd_autorefresh_enable(struct mdss_mdp_ctl *ctl, - int frame_cnt); +int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *ctl, int frame_cnt); +int mdss_mdp_cmd_get_autorefresh_mode(struct mdss_mdp_ctl *ctl); +int mdss_mdp_ctl_cmd_set_autorefresh(struct mdss_mdp_ctl *ctl, int frame_cnt); +int mdss_mdp_ctl_cmd_get_autorefresh(struct mdss_mdp_ctl *ctl); int mdss_mdp_pp_get_version(struct mdp_pp_feature_version *version); struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata, diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 7a1c5c5be74a..029f3c7a7131 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -2390,19 +2390,28 @@ int mdss_mdp_block_mixer_destroy(struct mdss_mdp_mixer *mixer) return 0; } -int mdss_mdp_ctl_cmd_autorefresh_enable(struct mdss_mdp_ctl *ctl, - int frame_cnt) +int mdss_mdp_ctl_cmd_set_autorefresh(struct mdss_mdp_ctl *ctl, int frame_cnt) { int ret = 0; + if (ctl->panel_data->panel_info.type == MIPI_CMD_PANEL) { ret = mdss_mdp_cmd_set_autorefresh_mode(ctl, frame_cnt); } else { pr_err("Mode not supported for this panel\n"); ret = -EINVAL; } + return ret; } +int mdss_mdp_ctl_cmd_get_autorefresh(struct mdss_mdp_ctl *ctl) +{ + if (ctl->panel_data->panel_info.type == MIPI_CMD_PANEL) + return mdss_mdp_cmd_get_autorefresh_mode(ctl); + else + return 0; +} + int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff) { switch (ctl->panel_data->panel_info.type) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c index 390d307d7414..13ebfa1182fd 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_debug.c +++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -933,6 +933,8 @@ static struct debug_bus dbg_bus_8996[] = { { 0x348, 20, 0}, { 0x348, 20, 1}, { 0x348, 20, 3}, + + { 0x418, 60, 0}, }; static struct vbif_debug_bus vbif_dbg_bus_8996[] = { @@ -1420,7 +1422,9 @@ int mdss_mdp_debugfs_init(struct mdss_data_type *mdata) &mdss_debugfs_safe_stats_fops); debugfs_create_u32("serialize_wait4pp", 0644, mdd->root, (u32 *)&mdata->serialize_wait4pp); - debugfs_create_u32("enable_gate", 0644, mdd->root, + debugfs_create_bool("wait4autorefresh", 0644, mdd->root, + (u32 *)&mdata->wait4autorefresh); + debugfs_create_bool("enable_gate", 0644, mdd->root, (u32 *)&mdata->enable_gate); debugfs_create_u32("color0", 0644, mdd->bordercolor, diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 15df392998e8..1c3fcba9d0f8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,9 +29,17 @@ #define POWER_COLLAPSE_TIME msecs_to_jiffies(100) #define CMD_MODE_IDLE_TIMEOUT msecs_to_jiffies(16 * 4) #define INPUT_EVENT_HANDLER_DELAY_USECS (16000 * 4) +#define AUTOREFRESH_MAX_FRAME_CNT 6 static DEFINE_MUTEX(cmd_clk_mtx); +enum mdss_mdp_cmd_autorefresh_state { + MDP_AUTOREFRESH_OFF, + MDP_AUTOREFRESH_ON_REQUESTED, + MDP_AUTOREFRESH_ON, + MDP_AUTOREFRESH_OFF_REQUESTED +}; + struct mdss_mdp_cmd_ctx { struct mdss_mdp_ctl *ctl; @@ -49,7 +57,6 @@ struct mdss_mdp_cmd_ctx { u8 ref_cnt; struct completion stop_comp; - struct completion readptr_done; struct completion pp_done; wait_queue_head_t pp_waitq; struct list_head vsync_handlers; @@ -64,12 +71,20 @@ struct mdss_mdp_cmd_ctx { struct delayed_work delayed_off_clk_work; struct work_struct pp_done_work; struct work_struct early_wakeup_clk_work; - struct mutex autorefresh_mtx; atomic_t pp_done_cnt; + struct completion rdptr_done; + + /* + * While autorefresh is on, partial update is not supported. So + * autorefresh state machine is always maintained through master ctx. + */ + struct mutex autorefresh_lock; + struct completion autorefresh_ppdone; + enum mdss_mdp_cmd_autorefresh_state autorefresh_state; + int autorefresh_frame_cnt; + bool ignore_external_te; + struct completion autorefresh_done; - int autorefresh_pending_frame_cnt; - bool autorefresh_off_pending; - bool autorefresh_init; int vsync_irq_cnt; struct mdss_intf_recovery intf_recovery; @@ -84,6 +99,8 @@ static int mdss_mdp_cmd_do_notifier(struct mdss_mdp_cmd_ctx *ctx); static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx); static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx); static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg); +static int mdss_mdp_disable_autorefresh(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_ctl *sctl); static bool __mdss_mdp_cmd_is_aux_pp_needed(struct mdss_data_type *mdata, struct mdss_mdp_ctl *mctl) @@ -676,7 +693,7 @@ int mdss_mdp_resource_control(struct mdss_mdp_ctl *ctl, u32 sw_event) */ if ((PERF_STATUS_DONE == status) && !ctx->intf_stopped && - !ctx->autorefresh_init && + (ctx->autorefresh_state == MDP_AUTOREFRESH_OFF) && !ctl->mfd->validate_pending) { pr_debug("schedule release after:%d ms\n", jiffies_to_msecs @@ -707,10 +724,16 @@ int mdss_mdp_resource_control(struct mdss_mdp_ctl *ctl, u32 sw_event) /* If pp_done is on-going, wait for it to finish */ mdss_mdp_cmd_wait4pingpong(ctl, NULL); - if (sctl) mdss_mdp_cmd_wait4pingpong(sctl, NULL); + mutex_lock(&ctx->autorefresh_lock); + if (ctx->autorefresh_state != MDP_AUTOREFRESH_OFF) { + pr_debug("move autorefresh to disable state\n"); + mdss_mdp_disable_autorefresh(ctl, sctl); + } + mutex_unlock(&ctx->autorefresh_lock); + /* * If a pp_done happened just before the stop, * we can still have some work items running; @@ -885,11 +908,6 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct dsi_panel_clk_ctrl clk_ctrl; - if (ctx->autorefresh_init) { - /* Do not turn off clocks if aurtorefresh is on. */ - return; - } - pr_debug("%pS-->%s: task:%s ctx%d\n", __builtin_return_address(0), __func__, current->group_leader->comm, ctx->current_pp_num); @@ -932,22 +950,14 @@ static void mdss_mdp_cmd_readptr_done(void *arg) return; } - if (ctx->autorefresh_init || ctx->autorefresh_off_pending) { - pr_debug("Completing read pointer done\n"); - complete_all(&ctx->readptr_done); - } - - if (ctx->autorefresh_off_pending) - ctx->autorefresh_off_pending = false; - vsync_time = ktime_get(); ctl->vsync_cnt++; MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt)); + complete_all(&ctx->rdptr_done); spin_lock(&ctx->clk_lock); list_for_each_entry(tmp, &ctx->vsync_handlers, list) { - if (tmp->enabled && !tmp->cmd_post_flush && - !ctx->autorefresh_pending_frame_cnt) + if (tmp->enabled && !tmp->cmd_post_flush) tmp->vsync_handler(ctl, vsync_time); } spin_unlock(&ctx->clk_lock); @@ -1043,7 +1053,7 @@ static void mdss_mdp_cmd_pingpong_done(void *arg) MDP_RSRC_CTL_EVENT_PP_DONE); } wake_up_all(&ctx->pp_waitq); - } else if (!ctl->cmd_autorefresh_en) { + } else { pr_err("%s: should not have pingpong interrupt!\n", __func__); } @@ -1057,6 +1067,36 @@ static void mdss_mdp_cmd_pingpong_done(void *arg) spin_unlock(&ctx->koff_lock); } +/** + * mdss_mdp_cmd_autorefresh_pp_done() - pp done irq callback for autorefresh + * @arg: void pointer to the controller context. + * + * This function is the pp_done interrupt callback while disabling + * autorefresh. This function does not modify the kickoff count (koff_cnt). + */ +static void mdss_mdp_cmd_autorefresh_pp_done(void *arg) +{ + struct mdss_mdp_ctl *ctl = arg; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; + + if (!ctx) { + pr_err("%s: invalid ctx\n", __func__); + return; + } + + mdss_mdp_irq_disable_nosync(MDSS_MDP_IRQ_PING_PONG_COMP, + ctx->current_pp_num); + mdss_mdp_set_intr_callback_nosync(MDSS_MDP_IRQ_PING_PONG_COMP, + ctx->current_pp_num, NULL, NULL); + + MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->current_pp_num); + complete_all(&ctx->autorefresh_ppdone); + + pr_debug("%s: ctl_num=%d intf_num=%d ctx=%d cnt=%d\n", __func__, + ctl->num, ctl->intf_num, ctx->current_pp_num, + atomic_read(&ctx->koff_cnt)); +} + static void pingpong_done_work(struct work_struct *work) { u32 status; @@ -1139,7 +1179,7 @@ static void clk_ctrl_delayed_off_work(struct work_struct *work) } } - if (ctx->autorefresh_init) { + if (ctx->autorefresh_state != MDP_AUTOREFRESH_OFF) { /* * Driver shouldn't have scheduled this work item if * autorefresh was enabled, but if any race @@ -1147,7 +1187,7 @@ static void clk_ctrl_delayed_off_work(struct work_struct *work) * the enable of the feature, make sure we do not * process this request and mark this error. */ - pr_err("cannot disable with autorefresh\n"); + pr_err("cannot disable clks while autorefresh is not off\n"); goto exit; } @@ -1246,7 +1286,7 @@ static void clk_ctrl_gate_work(struct work_struct *work) } } - if (ctx->autorefresh_init) { + if (ctx->autorefresh_state != MDP_AUTOREFRESH_OFF) { /* * Driver shouldn't have scheduled this work item if * autorefresh was enabled, but if any race @@ -1709,98 +1749,488 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, return rc; } -static int __mdss_mdp_cmd_configure_autorefresh(struct mdss_mdp_ctl *ctl, int - frame_cnt, bool delayed) +/* + * This function will be called from the sysfs node to enable and disable the + * feature with master ctl only. + */ +int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *mctl, int frame_cnt) { + int rc = 0; struct mdss_mdp_cmd_ctx *ctx; - bool enable = frame_cnt ? true : false; + struct mdss_panel_info *pinfo; - if (!ctl || !ctl->mixer_left) { - pr_err("invalid ctl structure\n"); + if (!mctl || !mctl->is_master || !mctl->panel_data) { + pr_err("invalid ctl mctl:%p pdata:%p\n", + mctl, mctl ? mctl->panel_data : 0); return -ENODEV; } - ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; + + ctx = mctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; } - if (frame_cnt == ctl->autorefresh_frame_cnt) { + pinfo = &mctl->panel_data->panel_info; + if (!pinfo->mipi.hw_vsync_mode) { + pr_err("hw vsync disabled, cannot handle autorefresh\n"); + return -ENODEV; + } + + if (frame_cnt < 0 || frame_cnt >= AUTOREFRESH_MAX_FRAME_CNT) { + pr_err("frame cnt %d is out of range (16 bits).\n", frame_cnt); + return -EINVAL; + } + + if (ctx->intf_stopped) { + pr_debug("autorefresh cannot be changed when display is off\n"); + return -EPERM; + } + + mutex_lock(&ctx->autorefresh_lock); + + if (frame_cnt == ctx->autorefresh_frame_cnt) { pr_debug("No change to the refresh count\n"); - return 0; + goto exit; } - pr_debug("%s enable = %d frame_cnt = %d init=%d\n", __func__, - enable, frame_cnt, ctx->autorefresh_init); - mutex_lock(&ctx->autorefresh_mtx); + MDSS_XLOG(ctx->autorefresh_state, + ctx->autorefresh_frame_cnt, frame_cnt); - if (enable) { - if (delayed) { - ctx->autorefresh_pending_frame_cnt = frame_cnt; - } else { - if (!ctx->autorefresh_init) { - /* - * clocks and resources were powered on - * during kickoff and following flag - * will prevent to schedule work - * items to release resources. - */ - ctx->autorefresh_init = true; - } + pr_debug("curent autorfresh state=%d, frmae_cnt: old=%d new=%d\n", + ctx->autorefresh_state, + ctx->autorefresh_frame_cnt, frame_cnt); - /* - * When autorefresh is enabled, there is no need to - * trigger manual kickoff. - */ - mdss_mdp_pingpong_write(ctl->mixer_left->pingpong_base, - MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG, - BIT(31) | frame_cnt); + switch (ctx->autorefresh_state) { + case MDP_AUTOREFRESH_OFF: + if (frame_cnt == 0) { + pr_debug("oops autorefresh is already disabled. We shouldn't get here\n"); + rc = -EINVAL; + goto exit; + } + + /* + * actual enable will happen in commit context when + * next update is kicked off. + */ + ctx->autorefresh_state = MDP_AUTOREFRESH_ON_REQUESTED; + ctx->autorefresh_frame_cnt = frame_cnt; + + /* Cancel GATE Work Item */ + if (cancel_work_sync(&ctx->gate_clk_work)) + pr_debug("%s: gate work canceled\n", __func__); - ctl->autorefresh_frame_cnt = frame_cnt; - ctl->cmd_autorefresh_en = true; + /* Cancel OFF Work Item */ + if (cancel_delayed_work_sync(&ctx->delayed_off_clk_work)) + pr_debug("%s: off work canceled\n", __func__); + break; + case MDP_AUTOREFRESH_ON_REQUESTED: + if (frame_cnt == 0) { + ctx->autorefresh_state = MDP_AUTOREFRESH_OFF; + ctx->autorefresh_frame_cnt = 0; + } else { + ctx->autorefresh_frame_cnt = frame_cnt; } - } else { - if (ctx->autorefresh_init) { + break; + case MDP_AUTOREFRESH_ON: + if (frame_cnt == 0) { /* - * Safe to turn off the feature. The clocks will be on - * at this time since the feature was enabled. + * actual disable will happen in commit context when + * next update is kicked off. */ + ctx->autorefresh_state = MDP_AUTOREFRESH_OFF_REQUESTED; + } else { + ctx->autorefresh_frame_cnt = frame_cnt; + } + break; + case MDP_AUTOREFRESH_OFF_REQUESTED: + if (frame_cnt == 0) { + pr_debug("autorefresh off is already requested\n"); + } else { + pr_debug("cancelling autorefresh off request\n"); + ctx->autorefresh_state = MDP_AUTOREFRESH_ON; + ctx->autorefresh_frame_cnt = frame_cnt; + } + break; + default: + pr_err("invalid autorefresh state\n"); + } + + MDSS_XLOG(ctx->autorefresh_state, + ctx->autorefresh_frame_cnt); + +exit: + mutex_unlock(&ctx->autorefresh_lock); + return rc; +} + +int mdss_mdp_cmd_get_autorefresh_mode(struct mdss_mdp_ctl *mctl) +{ + struct mdss_mdp_cmd_ctx *ctx = mctl->intf_ctx[MASTER_CTX]; + int autorefresh_frame_cnt; + + /* check the ctl to make sure the lock was initialized */ + if (!ctx || !ctx->ctl) + return 0; + + mutex_lock(&ctx->autorefresh_lock); + autorefresh_frame_cnt = ctx->autorefresh_frame_cnt; + mutex_unlock(&ctx->autorefresh_lock); + + return ctx->autorefresh_frame_cnt; +} + +static void mdss_mdp_cmd_pre_programming(struct mdss_mdp_ctl *mctl) +{ + struct mdss_mdp_cmd_ctx *ctx = mctl->intf_ctx[MASTER_CTX]; + char __iomem *pp_base; + u32 autorefresh_state; + u32 cfg; + + if (!mctl->is_master) + return; + + mutex_lock(&ctx->autorefresh_lock); + + autorefresh_state = ctx->autorefresh_state; + MDSS_XLOG(autorefresh_state); + pr_debug("pre_programming state: %d\n", autorefresh_state); + + if ((autorefresh_state == MDP_AUTOREFRESH_ON) || + (autorefresh_state == MDP_AUTOREFRESH_OFF_REQUESTED)) { + + pp_base = mctl->mixer_left->pingpong_base; + + /* + * instruct MDP to ignore the panel TE so the next auto-refresh + * is delayed until flush bits are set. + */ + cfg = mdss_mdp_pingpong_read(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC); + cfg &= ~BIT(20); + mdss_mdp_pingpong_write(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); + ctx->ignore_external_te = true; + + } + mutex_unlock(&ctx->autorefresh_lock); +} + +/* this function assumes that autorefresh_lock is held by the caller */ +static void mdss_mdp_cmd_post_programming(struct mdss_mdp_ctl *mctl) +{ + struct mdss_mdp_cmd_ctx *ctx = mctl->intf_ctx[MASTER_CTX]; + char __iomem *pp_base; + u32 cfg; + + if (!mctl->is_master) + return; + + /* + * If listening to the external panel TE was disabled + * (this happens when we get a kickoff with + * autorefresh enabled), enable the panel TE back. + */ + if (ctx->ignore_external_te) { + + MDSS_XLOG(ctx->ignore_external_te); + pr_debug("post_programming TE status: %d\n", + ctx->ignore_external_te); + + pp_base = mctl->mixer_left->pingpong_base; + + /* enable MDP to listen to the TE */ + cfg = mdss_mdp_pingpong_read(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC); + cfg |= BIT(20); + mdss_mdp_pingpong_write(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); + ctx->ignore_external_te = false; + } +} + +static void mdss_mdp_cmd_wait4_autorefresh_pp(struct mdss_mdp_ctl *ctl) +{ + int rc; + u32 val, line_out, intr_type = MDSS_MDP_IRQ_PING_PONG_COMP; + char __iomem *pp_base = ctl->mixer_left->pingpong_base; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; - mdss_mdp_pingpong_write(ctl->mixer_left->pingpong_base, - MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG, 0); + line_out = mdss_mdp_pingpong_read(pp_base, MDSS_MDP_REG_PP_LINE_COUNT); + + MDSS_XLOG(ctl->num, line_out, ctl->mixer_left->roi.h); + + if (line_out < ctl->mixer_left->roi.h) { + reinit_completion(&ctx->autorefresh_ppdone); + + /* enable ping pong done */ + mdss_mdp_set_intr_callback(intr_type, ctx->current_pp_num, + mdss_mdp_cmd_autorefresh_pp_done, ctl); + mdss_mdp_irq_enable(intr_type, ctx->current_pp_num); + + /* wait for ping pong done */ + rc = wait_for_completion_timeout(&ctx->autorefresh_ppdone, + KOFF_TIMEOUT); + if (rc <= 0) { + val = mdss_mdp_pingpong_read(pp_base, + MDSS_MDP_REG_PP_LINE_COUNT); + if (val == ctl->mixer_left->roi.h) { + mdss_mdp_irq_clear(ctl->mdata, + MDSS_MDP_IRQ_PING_PONG_COMP, + ctx->current_pp_num); + mdss_mdp_irq_disable_nosync(intr_type, + ctx->current_pp_num); + mdss_mdp_set_intr_callback(intr_type, + ctx->current_pp_num, NULL, NULL); + } else { + pr_err("timedout waiting for ctl%d autorefresh pp done\n", + ctl->num); + MDSS_XLOG(0xbad3); + MDSS_XLOG_TOUT_HANDLER("mdp", + "vbif", "dbg_bus", "vbif_dbg_bus", + "panic"); + } } + } +} - ctx->autorefresh_init = false; - ctx->autorefresh_pending_frame_cnt = 0; - ctx->autorefresh_off_pending = true; +static void mdss_mdp_cmd_autorefresh_done(void *arg) +{ + struct mdss_mdp_ctl *ctl = arg; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; - ctl->autorefresh_frame_cnt = 0; - ctl->cmd_autorefresh_en = false; + if (!ctx) { + pr_err("%s: invalid ctx\n", __func__); + return; } - mutex_unlock(&ctx->autorefresh_mtx); + mdss_mdp_irq_disable_nosync(MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num); + mdss_mdp_set_intr_callback_nosync(MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num, NULL, NULL); - return 0; + MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->current_pp_num); + complete_all(&ctx->autorefresh_done); } -/* - * This function will be called from the sysfs node to enable and disable the - * feature. - */ -int mdss_mdp_cmd_set_autorefresh_mode(struct mdss_mdp_ctl *ctl, int frame_cnt) +static u32 get_autorefresh_timeout(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_cmd_ctx *ctx, u32 frame_cnt) { - return __mdss_mdp_cmd_configure_autorefresh(ctl, frame_cnt, true); + struct mdss_mdp_mixer *mixer = + mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); + struct mdss_panel_info *pinfo; + u32 line_count; + u32 fps, v_total; + unsigned long autorefresh_timeout; + + pinfo = &ctl->panel_data->panel_info; + + if (!ctx->ignore_external_te) + line_count = ctl->mixer_left->roi.h; + else + line_count = mdss_mdp_pingpong_read(mixer->pingpong_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT) & 0xffff; + + fps = mdss_panel_get_framerate(pinfo); + v_total = mdss_panel_get_vtotal(pinfo); + + /* + * calculate the expected delay for the autorefresh to happen, + * this should be: + * autorefresh_done = line_count * frame_cnt * line_time + */ + frame_cnt *= 1000; /* to use mS */ + autorefresh_timeout = mult_frac(line_count, frame_cnt, + (fps * v_total)); + + /* multiply by two to consider worst case scenario */ + autorefresh_timeout *= 2; + autorefresh_timeout = msecs_to_jiffies(autorefresh_timeout); + + pr_debug("lines:%d fps:%d v_total:%d frames:%d timeout=%lu\n", + line_count, fps, v_total, frame_cnt, autorefresh_timeout); + + autorefresh_timeout = (autorefresh_timeout > CMD_MODE_IDLE_TIMEOUT) ? + autorefresh_timeout : CMD_MODE_IDLE_TIMEOUT; + + return autorefresh_timeout; } -/* - * This function is called from the commit thread. This function will check if - * there was are any pending requests from the sys fs node for the feature and - * if so then it will enable in the h/w. - */ -static int mdss_mdp_cmd_enable_cmd_autorefresh(struct mdss_mdp_ctl *ctl, - int frame_cnt) +static void mdss_mdp_cmd_wait4_autorefresh_done(struct mdss_mdp_ctl *ctl) { - return __mdss_mdp_cmd_configure_autorefresh(ctl, frame_cnt, false); + int rc; + u32 val, line_out; + char __iomem *pp_base = ctl->mixer_left->pingpong_base; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; + unsigned long flags; + unsigned long autorefresh_timeout; + + line_out = mdss_mdp_pingpong_read(pp_base, MDSS_MDP_REG_PP_LINE_COUNT); + + MDSS_XLOG(ctl->num, line_out, ctl->mixer_left->roi.h); + + reinit_completion(&ctx->autorefresh_done); + + /* enable autorefresh done */ + mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num, mdss_mdp_cmd_autorefresh_done, ctl); + mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num); + + /* + * Wait for autorefresh done, note that this interrupt would happen + * once the RD_PTR is reset to init value for the number of frames + * programmed with "autorefresh_frame_cnt", so this wait would take + * one RD_PTR reset, if autorefresh_frame_cnt = 1 + * or the number of RD_PTR resets set by "autorefresh_frame_cnt". + */ + autorefresh_timeout = get_autorefresh_timeout(ctl, + ctx, ctx->autorefresh_frame_cnt); + rc = wait_for_completion_timeout(&ctx->autorefresh_done, + autorefresh_timeout); + + if (rc <= 0) { + u32 status, mask; + + mask = BIT(MDSS_MDP_IRQ_PING_PONG_AUTO_REF + + ctx->current_pp_num); + status = mask & readl_relaxed(ctl->mdata->mdp_base + + MDSS_MDP_REG_INTR_STATUS); + + if (status) { + pr_warn("autorefresh done but irq not triggered\n"); + mdss_mdp_irq_clear(ctl->mdata, + MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num); + local_irq_save(flags); + mdss_mdp_irq_disable_nosync( + MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num); + mdss_mdp_set_intr_callback_nosync( + MDSS_MDP_IRQ_PING_PONG_AUTO_REF, + ctx->current_pp_num, NULL, NULL); + local_irq_restore(flags); + rc = 1; + } + } + + if (rc <= 0) { + val = mdss_mdp_pingpong_read(pp_base, + MDSS_MDP_REG_PP_LINE_COUNT); + + pr_err("timedout waiting for ctl%d autorefresh done line_cnt:%d frames:%d\n", + ctl->num, val, ctx->autorefresh_frame_cnt); + MDSS_XLOG(0xbad4, val); + MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl", "dsi0_phy", + "dsi1_ctrl", "dsi1_phy", "vbif", "vbif_nrt", + "dbg_bus", "vbif_dbg_bus", "panic"); + } +} + +/* caller needs to hold autorefresh_lock before calling this function */ +static int mdss_mdp_disable_autorefresh(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_ctl *sctl) +{ + u32 cfg; + struct mdss_mdp_cmd_ctx *ctx; + char __iomem *pp_base = ctl->mixer_left->pingpong_base; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; + if (!ctx) { + pr_err("invalid ctx\n"); + return -ENODEV; + } + + MDSS_XLOG(ctx->autorefresh_state, ctx->autorefresh_frame_cnt); + + /* + * This can happen if driver gets sysfs request to enable autorefresh, + * and a CMD_STOP is received before autorefresh is turned on by + * the atomic commit. + */ + if (ctx->autorefresh_state == MDP_AUTOREFRESH_ON_REQUESTED) { + ctx->autorefresh_state = MDP_AUTOREFRESH_OFF; + ctx->autorefresh_frame_cnt = 0; + return 0; + } + + pr_debug("%pS->%s: disabling autorefresh\n", + __builtin_return_address(0), __func__); + + /* + * Wait for autorefresh done before disabling it. + * This is intended for debug only; if enabled it would cause a large + * delay during disable due RD_PTR is program to wait for + * wrapping around, which can take hundreds of ms + */ + if (mdata->wait4autorefresh) + mdss_mdp_cmd_wait4_autorefresh_done(ctl); + + /* + * To disable auto-refresh we need to make sure that no transfer + * is on-going when we write the bit to disable it. + * But since when autorefresh is enabled the HW automatically + * will trigger a transfer whenever external TE is received and + * the hw frame_cnt matches the programmed autorefresh frame_cnt, + * in order to have enough time to disable the feature we will instruct + * MDP to ignore the panel TE first; when doing this, the hw frame_cnt + * will be increased only when the internal counter wraps-around + * (instead of each time that the external panel TE is genarated), + * this gives us enough margin to disable autorefresh. + */ + cfg = mdss_mdp_pingpong_read(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC); + cfg &= ~BIT(20); + mdss_mdp_pingpong_write(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); + MDSS_XLOG(cfg); + + /* wait for previous transfer to finish */ + mdss_mdp_cmd_wait4_autorefresh_pp(ctl); + if (sctl) + mdss_mdp_cmd_wait4_autorefresh_pp(sctl); + + /* disable autorefresh */ + mdss_mdp_pingpong_write(pp_base, MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG, 0); + ctx->autorefresh_state = MDP_AUTOREFRESH_OFF; + ctx->autorefresh_frame_cnt = 0; + + /* enable MDP to listen to the TE */ + cfg = mdss_mdp_pingpong_read(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC); + cfg |= BIT(20); + mdss_mdp_pingpong_write(pp_base, + MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); + + return 0; +} + + +static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_cmd_ctx *ctx) +{ + MDSS_XLOG(ctx->autorefresh_state); + + if ((ctx->autorefresh_state == MDP_AUTOREFRESH_ON_REQUESTED) || + (ctx->autorefresh_state == MDP_AUTOREFRESH_ON)) { + + pr_debug("enabling autorefresh for every %d frames state %d\n", + ctx->autorefresh_frame_cnt, ctx->autorefresh_state); + + /* Program HW to take care of Kickoff */ + mdss_mdp_pingpong_write(ctl->mixer_left->pingpong_base, + MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG, + BIT(31) | ctx->autorefresh_frame_cnt); + MDSS_XLOG(0x11, ctx->autorefresh_frame_cnt, + ctx->autorefresh_state); + ctx->autorefresh_state = MDP_AUTOREFRESH_ON; + + } else { + /* SW Kickoff */ + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1); + MDSS_XLOG(0x11, ctx->autorefresh_state); + } } /* @@ -1842,7 +2272,6 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) } } - reinit_completion(&ctx->readptr_done); mdss_mdp_ctl_perf_set_transaction_status(ctl, PERF_HW_MDP_STATE, PERF_STATUS_BUSY); @@ -1900,17 +2329,10 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) mdss_mdp_cmd_set_sync_ctx(ctl, sctl); - if (ctx->autorefresh_init || ctx->autorefresh_off_pending) { - /* - * If autorefresh is enabled then do not queue the frame till - * the next read ptr is done otherwise we might get a pp done - * immediately for the past autorefresh frame instead. - * Clocks and resources were already powered on during kickoff, - * and "autorefresh_init" flag will prevent from release - * resources during pp done. - */ - pr_debug("Wait for read pointer done before enabling PP irq\n"); - wait_for_completion(&ctx->readptr_done); + mutex_lock(&ctx->autorefresh_lock); + if (ctx->autorefresh_state == MDP_AUTOREFRESH_OFF_REQUESTED) { + pr_debug("%s: disable autorefresh ctl%d\n", __func__, ctl->num); + mdss_mdp_disable_autorefresh(ctl, sctl); } mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, @@ -1930,16 +2352,22 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) PERF_SW_COMMIT_STATE, PERF_STATUS_DONE); } - if (!ctx->autorefresh_pending_frame_cnt && !ctl->cmd_autorefresh_en) { - /* Kickoff */ - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1); - } else { - pr_debug("Enabling autorefresh in hardware.\n"); - mdss_mdp_cmd_enable_cmd_autorefresh(ctl, - ctx->autorefresh_pending_frame_cnt); - } + /* Kickoff */ + __mdss_mdp_kickoff(ctl, ctx); + + mdss_mdp_cmd_post_programming(ctl); + + /* + * If auto-refresh is enabled, wait for an autorefresh done, + * to make sure configuration has taken effect. + * Do this after post-programming, so TE is enabled. + */ + if (ctx->autorefresh_state == MDP_AUTOREFRESH_ON) + mdss_mdp_cmd_wait4_autorefresh_done(ctl); mb(); + mutex_unlock(&ctx->autorefresh_lock); + MDSS_XLOG(ctl->num, ctx->current_pp_num, sctx ? sctx->current_pp_num : -1, atomic_read(&ctx->koff_cnt)); return 0; @@ -2032,7 +2460,7 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl, return 0; } -int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, +static int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, int panel_power_state) { struct mdss_mdp_cmd_ctx *ctx; @@ -2119,27 +2547,7 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) if (sctl) sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; - if (ctl->cmd_autorefresh_en) { - int pre_suspend = ctx->autorefresh_pending_frame_cnt; - int hz, rc; - - mdss_mdp_cmd_enable_cmd_autorefresh(ctl, 0); - ctx->autorefresh_pending_frame_cnt = pre_suspend; - - reinit_completion(&ctx->readptr_done); - - /* enable read pointer to wait for one frame to finish */ - mdss_mdp_setup_vsync(ctx, true); - - /* wait one read pointer to make sure current frame is done */ - hz = mdss_panel_get_framerate(&ctl->panel_data->panel_info); - rc = wait_for_completion_timeout(&ctx->readptr_done, - STOP_TIMEOUT(hz)); - if (rc == 0) - pr_err("Timed out waiting for read ptr!\n"); - - mdss_mdp_setup_vsync(ctx, false); - } + MDSS_XLOG(ctx->panel_power_state, panel_power_state); mutex_lock(&ctl->offlock); if (mdss_panel_is_power_off(panel_power_state)) { @@ -2319,6 +2727,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl, { int ret = 0; + /* + * Initialize the mutex before the ctl is assigned, + * so we can prevent any race condition with the + * initialization of the the mutex and the autorefresh + * sysfs. + */ + mutex_init(&ctx->autorefresh_lock); + ctx->ctl = ctl; ctx->default_pp_num = default_pp_num; ctx->aux_pp_num = aux_pp_num; @@ -2326,21 +2742,22 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl, ctx->pp_timeout_report_cnt = 0; init_waitqueue_head(&ctx->pp_waitq); init_completion(&ctx->stop_comp); - init_completion(&ctx->readptr_done); + init_completion(&ctx->autorefresh_ppdone); + init_completion(&ctx->rdptr_done); init_completion(&ctx->pp_done); + init_completion(&ctx->autorefresh_done); spin_lock_init(&ctx->clk_lock); spin_lock_init(&ctx->koff_lock); mutex_init(&ctx->clk_mtx); mutex_init(&ctx->mdp_rdptr_lock); - mutex_init(&ctx->autorefresh_mtx); INIT_WORK(&ctx->gate_clk_work, clk_ctrl_gate_work); INIT_DELAYED_WORK(&ctx->delayed_off_clk_work, clk_ctrl_delayed_off_work); INIT_WORK(&ctx->pp_done_work, pingpong_done_work); INIT_WORK(&ctx->early_wakeup_clk_work, early_wakeup_work); atomic_set(&ctx->pp_done_cnt, 0); - ctx->autorefresh_off_pending = false; - ctx->autorefresh_init = false; + ctx->autorefresh_state = MDP_AUTOREFRESH_OFF; + ctx->autorefresh_frame_cnt = 0; INIT_LIST_HEAD(&ctx->vsync_handlers); ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery; @@ -2622,6 +3039,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) ctl->ops.restore_fnc = mdss_mdp_cmd_restore; ctl->ops.early_wake_up_fnc = mdss_mdp_cmd_early_wake_up; ctl->ops.reconfigure = mdss_mdp_cmd_reconfigure; + ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming; pr_debug("%s:-\n", __func__); return 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 8e3074de1496..2957a78b8d05 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -1967,6 +1967,10 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, list_move(&pipe->list, &mdp5_data->pipes_destroy); } + /* call this function before any registers programming */ + if (ctl->ops.pre_programming) + ctl->ops.pre_programming(ctl); + ATRACE_BEGIN("sspp_programming"); ret = __overlay_queue_pipes(mfd); ATRACE_END("sspp_programming"); @@ -3027,24 +3031,11 @@ static ssize_t mdss_mdp_cmd_autorefresh_show(struct device *dev, ret = -EINVAL; } else { ret = snprintf(buf, PAGE_SIZE, "%d\n", - ctl->autorefresh_frame_cnt); + mdss_mdp_ctl_cmd_get_autorefresh(ctl)); } return ret; } -static inline int mdss_validate_autorefresh_param(struct mdss_mdp_ctl *ctl, - int frame_cnt) -{ - int rc = 0; - - if (frame_cnt < 0 || frame_cnt >= BIT(16)) { - rc = -EINVAL; - pr_err("frame cnt %d is out of range (16 bits).\n", frame_cnt); - } - - return rc; -} - static ssize_t mdss_mdp_cmd_autorefresh_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -3076,27 +3067,26 @@ static ssize_t mdss_mdp_cmd_autorefresh_store(struct device *dev, if (rc) { pr_err("kstrtoint failed. rc=%d\n", rc); return rc; - } else if (frame_cnt != ctl->autorefresh_frame_cnt) { - rc = mdss_validate_autorefresh_param(ctl, frame_cnt); - if (rc) - return rc; + } - if (frame_cnt) { - /* enable autorefresh */ - mfd->mdp_sync_pt_data.threshold = 2; - mfd->mdp_sync_pt_data.retire_threshold = 0; - } else { - /* disable autorefresh */ - mfd->mdp_sync_pt_data.threshold = 1; - mfd->mdp_sync_pt_data.retire_threshold = 1; - } + rc = mdss_mdp_ctl_cmd_set_autorefresh(ctl, frame_cnt); + if (rc) { + pr_err("cmd_set_autorefresh failed, rc=%d, frame_cnt=%d\n", + rc, frame_cnt); + return rc; + } - rc = mdss_mdp_ctl_cmd_autorefresh_enable(ctl, frame_cnt); - if (rc) - return rc; + if (frame_cnt) { + /* enable/reconfig autorefresh */ + mfd->mdp_sync_pt_data.threshold = 2; + mfd->mdp_sync_pt_data.retire_threshold = 0; + } else { + /* disable autorefresh */ + mfd->mdp_sync_pt_data.threshold = 1; + mfd->mdp_sync_pt_data.retire_threshold = 1; } - pr_debug("Setting video autorefresh=%d cnt=%d\n", - ctl->cmd_autorefresh_en, frame_cnt); + + pr_debug("setting cmd autorefresh to cnt=%d\n", frame_cnt); return len; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c index ab18644e0580..d6bcf157a4c8 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_util.c +++ b/drivers/video/fbdev/msm/mdss_mdp_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -52,6 +52,10 @@ enum { MDP_INTR_WB_0, MDP_INTR_WB_1, MDP_INTR_WB_2, + MDP_INTR_PING_PONG_0_AUTO_REF, + MDP_INTR_PING_PONG_1_AUTO_REF, + MDP_INTR_PING_PONG_2_AUTO_REF, + MDP_INTR_PING_PONG_3_AUTO_REF, MDP_INTR_MAX, }; @@ -85,6 +89,9 @@ static int mdss_mdp_intr2index(u32 intr_type, u32 intf_num) case MDSS_MDP_IRQ_WB_WFD: index = MDP_INTR_WB_2 + intf_num; break; + case MDSS_MDP_IRQ_PING_PONG_AUTO_REF: + index = MDP_INTR_PING_PONG_0_AUTO_REF + intf_num; + break; } return index; @@ -241,6 +248,18 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr) mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP); } + if (isr & MDSS_MDP_INTR_PING_PONG_0_AUTOREFRESH_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_0_AUTO_REF); + + if (isr & MDSS_MDP_INTR_PING_PONG_1_AUTOREFRESH_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_1_AUTO_REF); + + if (isr & MDSS_MDP_INTR_PING_PONG_2_AUTOREFRESH_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_2_AUTO_REF); + + if (isr & MDSS_MDP_INTR_PING_PONG_3_AUTOREFRESH_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_3_AUTO_REF); + mdp_isr_done: hist_isr = readl_relaxed(mdata->mdp_base + MDSS_MDP_REG_HIST_INTR_STATUS); |
