msm: mdss: Add support for command mode autorefresh

This change adds support to autorefresh the command mode panels
without having to manually issue a kickoff. We need to enable
sys/class/graphics/fb0/msm_cmd_autorefresh_en to configure after
how many idle ticks(read ptr/vsync) should we trigger a frame.

e.g. If we want to send an update at 60fps
we need to echo 1 > sys/class/graphics/fb0/msm_cmd_autorefresh_en

to disable we need to echo 0 >
	sys/class/graphics/fb0/msm_cmd_autorefresh_en

Change-Id: Ib0cda1142f8fadfa6cad5e61e0c7fb36fe43aca1
Signed-off-by: Siddhartha Agrawal <agrawals@codeaurora.org>
[imaund@codeaurora.org: Updated INIT_COMPLETION call to
  reinit_completion]
Signed-off-by: Ian Maund <imaund@codeaurora.org>
This commit is contained in:
Siddhartha Agrawal 2014-11-26 16:27:39 -08:00 committed by David Keitel
parent f40a73c904
commit c4c4dac0e1
4 changed files with 268 additions and 3 deletions

View file

@ -250,6 +250,9 @@ struct mdss_mdp_ctl {
u8 roi_changed; u8 roi_changed;
u8 valid_roi; u8 valid_roi;
int cmd_autorefresh_en;
int autorefresh_frame_cnt;
int (*start_fnc) (struct mdss_mdp_ctl *ctl); int (*start_fnc) (struct mdss_mdp_ctl *ctl);
int (*stop_fnc) (struct mdss_mdp_ctl *ctl, int panel_power_state); int (*stop_fnc) (struct mdss_mdp_ctl *ctl, int panel_power_state);
int (*prepare_fnc) (struct mdss_mdp_ctl *ctl, void *arg); int (*prepare_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
@ -1038,5 +1041,9 @@ int mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl);
int mdss_mdp_wait_for_xin_halt(u32 xin_id, bool is_vbif_nrt); 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, void mdss_mdp_set_ot_limit(struct mdss_mdp_set_ot_params *params,
bool is_rot, bool is_wb, bool is_yuv); bool is_rot, bool is_wb, bool is_yuv);
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);
#endif /* MDSS_MDP_H */ #endif /* MDSS_MDP_H */

View file

@ -1786,6 +1786,20 @@ int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer)
return 0; return 0;
} }
int mdss_mdp_ctl_cmd_autorefresh_enable(struct mdss_mdp_ctl *ctl,
int frame_cnt)
{
int ret = 0;
if (ctl->panel_data->panel_info.type == MIPI_CMD_PANEL) {
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_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff) int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff)
{ {
switch (ctl->panel_data->panel_info.type) { switch (ctl->panel_data->panel_info.type) {

View file

@ -37,6 +37,7 @@ struct mdss_mdp_cmd_ctx {
u32 pp_num; u32 pp_num;
u8 ref_cnt; u8 ref_cnt;
struct completion stop_comp; struct completion stop_comp;
struct completion readptr_done;
wait_queue_head_t pp_waitq; wait_queue_head_t pp_waitq;
struct list_head vsync_handlers; struct list_head vsync_handlers;
int panel_power_state; int panel_power_state;
@ -50,7 +51,10 @@ struct mdss_mdp_cmd_ctx {
spinlock_t koff_lock; spinlock_t koff_lock;
struct work_struct clk_work; struct work_struct clk_work;
struct work_struct pp_done_work; struct work_struct pp_done_work;
struct mutex autorefresh_mtx;
atomic_t pp_done_cnt; atomic_t pp_done_cnt;
int autorefresh_pending;
u8 autorefresh_init;
struct mdss_intf_recovery intf_recovery; struct mdss_intf_recovery intf_recovery;
struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */ struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
u32 pp_timeout_report_cnt; u32 pp_timeout_report_cnt;
@ -259,6 +263,11 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx)
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); struct mdss_data_type *mdata = mdss_mdp_get_mdata();
int set_clk_off = 0; int set_clk_off = 0;
if (ctx->autorefresh_init) {
/* Do not turn off clocks if aurtorefresh is on. */
return;
}
mutex_lock(&ctx->clk_mtx); mutex_lock(&ctx->clk_mtx);
MDSS_XLOG(ctx->pp_num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, MDSS_XLOG(ctx->pp_num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled,
ctx->rdptr_enabled); ctx->rdptr_enabled);
@ -267,6 +276,8 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx)
set_clk_off = 1; set_clk_off = 1;
spin_unlock_irqrestore(&ctx->clk_lock, flags); spin_unlock_irqrestore(&ctx->clk_lock, flags);
pr_debug("clk_enabled =%d set_clk_off=%d\n", ctx->clk_enabled,
set_clk_off);
if (ctx->clk_enabled && set_clk_off) { if (ctx->clk_enabled && set_clk_off) {
ctx->clk_enabled = 0; ctx->clk_enabled = 0;
mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_SUSPEND); mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_SUSPEND);
@ -291,6 +302,11 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
return; return;
} }
if (ctx->autorefresh_init) {
pr_debug("Completing read pointer done\n");
complete_all(&ctx->readptr_done);
}
vsync_time = ktime_get(); vsync_time = ktime_get();
ctl->vsync_cnt++; ctl->vsync_cnt++;
MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled,
@ -303,7 +319,7 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
} }
if (!ctx->vsync_enabled) { if (!ctx->vsync_enabled) {
if (ctx->rdptr_enabled) if (ctx->rdptr_enabled && !ctx->autorefresh_init)
ctx->rdptr_enabled--; ctx->rdptr_enabled--;
/* keep clk on during kickoff */ /* keep clk on during kickoff */
@ -396,7 +412,7 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)
schedule_work(&ctx->pp_done_work); schedule_work(&ctx->pp_done_work);
} }
wake_up_all(&ctx->pp_waitq); wake_up_all(&ctx->pp_waitq);
} else { } else if (!ctl->cmd_autorefresh_en) {
pr_err("%s: should not have pingpong interrupt!\n", __func__); pr_err("%s: should not have pingpong interrupt!\n", __func__);
} }
@ -745,6 +761,97 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl,
return rc; return rc;
} }
static int __mdss_mdp_cmd_configure_autorefresh(struct mdss_mdp_ctl *ctl, int
frame_cnt, bool delayed)
{
struct mdss_mdp_cmd_ctx *ctx;
int enable = frame_cnt ? 1 : 0;
if (!ctl || !ctl->mixer_left) {
pr_err("invalid ctl structure\n");
return -ENODEV;
}
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data;
if (!ctx) {
pr_err("invalid ctx\n");
return -ENODEV;
}
if (frame_cnt == ctl->autorefresh_frame_cnt) {
pr_err("No change to the refresh count\n");
return 0;
}
pr_debug("%s enable = %d frame_cnt = %d clk_cnt=%d\n", __func__,
enable, frame_cnt, ctx->autorefresh_init);
mutex_lock(&ctx->autorefresh_mtx);
if (enable) {
if (delayed) {
ctx->autorefresh_pending = frame_cnt;
} else {
if (!ctx->autorefresh_init) {
ctx->autorefresh_init = 1;
mdss_mdp_cmd_clk_on(ctx);
}
mdss_mdp_pingpong_write(ctl->mixer_left->pingpong_base,
MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG,
(enable << 31) |
(frame_cnt));
/*
* This manual kickoff is needed to actually start the
* autorefresh feature. The h/w relies on one commit
* before it starts counting the read ptrs to trigger
* the frames.
*/
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
ctl->autorefresh_frame_cnt = frame_cnt;
ctl->cmd_autorefresh_en = 1;
}
} else {
if (ctx->autorefresh_init) {
/*
* Safe to turn off the feature. The clocks will be on
* at this time since the feature was enabled.
*/
mdss_mdp_pingpong_write(ctl->mixer_left->pingpong_base,
MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG, 0);
}
ctx->autorefresh_init = 0;
ctl->autorefresh_frame_cnt = 0;
ctl->cmd_autorefresh_en = 0;
ctx->autorefresh_pending = 0;
}
mutex_unlock(&ctx->autorefresh_mtx);
return 0;
}
/*
* 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)
{
return __mdss_mdp_cmd_configure_autorefresh(ctl, frame_cnt, true);
}
/*
* 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.
*/
int mdss_mdp_cmd_enable_cmd_autorefresh(struct mdss_mdp_ctl *ctl,
int frame_cnt)
{
return __mdss_mdp_cmd_configure_autorefresh(ctl, frame_cnt, false);
}
/* /*
* There are 3 partial update possibilities * There are 3 partial update possibilities
* left only ==> enable left pingpong_done * left only ==> enable left pingpong_done
@ -773,6 +880,7 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
return -EPERM; return -EPERM;
} }
reinit_completion(&ctx->readptr_done);
/* sctl will be null for right only in the case of Partial update */ /* sctl will be null for right only in the case of Partial update */
sctl = mdss_mdp_get_split_ctl(ctl); sctl = mdss_mdp_get_split_ctl(ctl);
@ -820,6 +928,16 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
mdss_mdp_cmd_set_sync_ctx(ctl, sctl); mdss_mdp_cmd_set_sync_ctx(ctl, sctl);
if (ctx->autorefresh_init) {
/*
* 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.
*/
pr_debug("Wait for read pointer done before enabling PP irq\n");
wait_for_completion(&ctx->readptr_done);
}
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num); mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
if (sctx) if (sctx)
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, sctx->pp_num); mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, sctx->pp_num);
@ -828,7 +946,14 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
ctl->mdata->mdp_rev == MDSS_MDP_HW_REV_109) ctl->mdata->mdp_rev == MDSS_MDP_HW_REV_109)
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_INTF_RESTORE, NULL); mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_INTF_RESTORE, NULL);
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1); /* Kickoff */ if (!ctx->autorefresh_pending && !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);
}
mdss_mdp_ctl_perf_set_transaction_status(ctl, mdss_mdp_ctl_perf_set_transaction_status(ctl,
PERF_SW_COMMIT_STATE, PERF_STATUS_DONE); PERF_SW_COMMIT_STATE, PERF_STATUS_DONE);
@ -1002,6 +1127,12 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
pr_debug("%s: transition from %d --> %d\n", __func__, pr_debug("%s: transition from %d --> %d\n", __func__,
ctx->panel_power_state, panel_power_state); ctx->panel_power_state, panel_power_state);
if (ctl->cmd_autorefresh_en) {
int pre_suspend = ctx->autorefresh_pending;
mdss_mdp_cmd_enable_cmd_autorefresh(ctl, 0);
ctx->autorefresh_pending = pre_suspend;
}
if (__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) { if (__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) {
if (mdss_panel_is_power_on_lp(panel_power_state)) { if (mdss_panel_is_power_on_lp(panel_power_state)) {
/* /*
@ -1152,12 +1283,15 @@ static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl,
ctx->pp_timeout_report_cnt = 0; ctx->pp_timeout_report_cnt = 0;
init_waitqueue_head(&ctx->pp_waitq); init_waitqueue_head(&ctx->pp_waitq);
init_completion(&ctx->stop_comp); init_completion(&ctx->stop_comp);
init_completion(&ctx->readptr_done);
spin_lock_init(&ctx->clk_lock); spin_lock_init(&ctx->clk_lock);
spin_lock_init(&ctx->koff_lock); spin_lock_init(&ctx->koff_lock);
mutex_init(&ctx->clk_mtx); mutex_init(&ctx->clk_mtx);
mutex_init(&ctx->autorefresh_mtx);
INIT_WORK(&ctx->clk_work, clk_ctrl_work); INIT_WORK(&ctx->clk_work, clk_ctrl_work);
INIT_WORK(&ctx->pp_done_work, pingpong_done_work); INIT_WORK(&ctx->pp_done_work, pingpong_done_work);
atomic_set(&ctx->pp_done_cnt, 0); atomic_set(&ctx->pp_done_cnt, 0);
ctx->autorefresh_init = 0;
INIT_LIST_HEAD(&ctx->vsync_handlers); INIT_LIST_HEAD(&ctx->vsync_handlers);
ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery; ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery;

View file

@ -2366,7 +2366,116 @@ static ssize_t mdss_mdp_dyn_pu_store(struct device *dev,
return count; return count;
} }
static ssize_t mdss_cmd_autorefresh_enabled(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_mdp_ctl *ctl;
if (!mfd) {
pr_err("Invalid mfd structure\n");
return -EINVAL;
}
ctl = mfd_to_ctl(mfd);
if (!ctl) {
pr_err("Invalid ctl structure\n");
return -EINVAL;
}
if (mfd->panel_info->type != MIPI_CMD_PANEL) {
pr_err("Panel doesnt support autorefresh\n");
ret = -EINVAL;
} else {
ret = snprintf(buf, PAGE_SIZE, "%d\n",
ctl->autorefresh_frame_cnt);
}
return ret;
}
static inline int mdss_validate_autorefresh_param(int frame_cnt,
struct mdss_mdp_ctl *ctl)
{
int rc = 0;
if (frame_cnt == ctl->autorefresh_frame_cnt) {
/* No parameters were changed */
rc = -EINVAL;
pr_debug("No change to autorefresh parameters\n");
goto no_change;
}
/* Check for correctness. Frame cnt length is 16bits */
if (frame_cnt < 0 || frame_cnt >= BIT(16)) {
rc = -EINVAL;
pr_err("Invalid frame_cnt=%d passed to autorefresh\n",
frame_cnt);
goto no_change;
}
pr_debug("Setting autorefresh_enable=%d frame_cnt=%d\n",
ctl->cmd_autorefresh_en,
frame_cnt);
no_change:
return rc;
}
static ssize_t mdss_set_cmd_autorefresh(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
u32 data = 0;
struct fb_info *fbi = dev_get_drvdata(dev);
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
struct mdss_mdp_ctl *ctl;
if (!mfd) {
pr_err("Invalid mfd structure\n");
len = 0;
goto mode_fail;
}
ctl = mfd_to_ctl(mfd);
if (!ctl) {
pr_err("Invalid ctl structure\n");
len = 0;
goto mode_fail;
}
if (mfd->panel_info->type != MIPI_CMD_PANEL) {
pr_err("Panel doesnt support autorefresh\n");
len = 0;
goto mode_fail;
}
if (1 != sscanf(buf, "%d", &data)) {
pr_err("Not able to read video autorefresh value\n");
} else if (!mdss_validate_autorefresh_param(data, ctl)) {
if (ctl->autorefresh_frame_cnt) {
mfd->mdp_sync_pt_data.threshold = 2;
mfd->mdp_sync_pt_data.retire_threshold = 0;
} else {
mfd->mdp_sync_pt_data.threshold = 1;
mfd->mdp_sync_pt_data.retire_threshold = 1;
}
mdss_mdp_ctl_cmd_autorefresh_enable(ctl,
data);
}
pr_debug("Setting video autorefresh=%d cnt=%d\n",
ctl->cmd_autorefresh_en,
data);
mode_fail:
return len;
}
static DEVICE_ATTR(msm_cmd_autorefresh_en, S_IRUGO | S_IWUSR,
mdss_cmd_autorefresh_enabled,
mdss_set_cmd_autorefresh);
static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL); static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL);
static DEVICE_ATTR(ad, S_IRUGO | S_IWUSR | S_IWGRP, mdss_mdp_ad_show, static DEVICE_ATTR(ad, S_IRUGO | S_IWUSR | S_IWGRP, mdss_mdp_ad_show,
mdss_mdp_ad_store); mdss_mdp_ad_store);
@ -2377,6 +2486,7 @@ static struct attribute *mdp_overlay_sysfs_attrs[] = {
&dev_attr_vsync_event.attr, &dev_attr_vsync_event.attr,
&dev_attr_ad.attr, &dev_attr_ad.attr,
&dev_attr_dyn_pu.attr, &dev_attr_dyn_pu.attr,
&dev_attr_msm_cmd_autorefresh_en.attr,
NULL, NULL,
}; };