mdss: mdp: add event timer for auto refresh

Display hardware keep generating the vsync isr
during auto refresh mode without any kickoff. These
vsync events may arrive late due to cpu latency and
cause corruption on screen. This change enables the
cpu event timer at vsync timeline to absorbs the
software latency in auto refresh usecase.

Conflicts:
	drivers/video/fbdev/msm/mdss_mdp.h
	drivers/video/fbdev/msm/mdss_mdp_ctl.c
	drivers/video/fbdev/msm/mdss_mdp.h

Change-Id: I24c5180f685be251d1a5f3b393be8ec9b46e21eb
Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
[hsahu@codeaurora.org: Resolved conflicts]
Signed-off-by: Harsh Sahu <hsahu@codeaurora.org>
This commit is contained in:
Dhaval Patel 2016-04-12 15:43:21 -07:00 committed by Harsh Sahu
parent a42b1f1045
commit 66c484153d
3 changed files with 118 additions and 79 deletions

View file

@ -465,6 +465,9 @@ struct mdss_mdp_ctl {
u32 vsync_cnt;
u32 underrun_cnt;
struct work_struct cpu_pm_work;
int autorefresh_frame_cnt;
u16 width;
u16 height;
u16 border_x_off;
@ -1910,6 +1913,7 @@ 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_enable_panel_disable_mode(struct msm_fb_data_type *mfd,
bool disable_panel);
void mdss_mdp_ctl_event_timer(void *data);
int mdss_mdp_pp_get_version(struct mdp_pp_feature_version *version);
int mdss_mdp_layer_pre_commit_cwb(struct msm_fb_data_type *mfd,
struct mdp_layer_commit_v1 *commit);

View file

@ -22,6 +22,7 @@
#include <linux/clk.h>
#include <linux/bitmap.h>
#include <soc/qcom/event_timer.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_trace.h"
@ -2681,12 +2682,122 @@ int mdss_mdp_block_mixer_destroy(struct mdss_mdp_mixer *mixer)
return 0;
}
int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
ktime_t *wakeup_time)
{
struct mdss_panel_info *pinfo;
u64 clk_rate;
u32 clk_period;
u32 current_line, total_line;
u32 time_of_line, time_to_vsync, adjust_line_ns;
ktime_t current_time = ktime_get();
if (!ctl->ops.read_line_cnt_fnc)
return -EINVAL;
pinfo = &ctl->panel_data->panel_info;
if (!pinfo)
return -ENODEV;
clk_rate = mdss_mdp_get_pclk_rate(ctl);
clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate)
return -EINVAL;
/*
* calculate clk_period as pico second to maintain good
* accuracy with high pclk rate and this number is in 17 bit
* range.
*/
clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period)
return -EINVAL;
time_of_line = (pinfo->lcdc.h_back_porch +
pinfo->lcdc.h_front_porch +
pinfo->lcdc.h_pulse_width +
pinfo->xres) * clk_period;
time_of_line /= 1000; /* in nano second */
if (!time_of_line)
return -EINVAL;
current_line = ctl->ops.read_line_cnt_fnc(ctl);
total_line = pinfo->lcdc.v_back_porch +
pinfo->lcdc.v_front_porch +
pinfo->lcdc.v_pulse_width +
pinfo->yres;
if (current_line >= total_line)
time_to_vsync = time_of_line * total_line;
else
time_to_vsync = time_of_line * (total_line - current_line);
if (pinfo->adjust_timer_delay_ms) {
adjust_line_ns = pinfo->adjust_timer_delay_ms
* 1000000; /* convert to ns */
/* Ignore large values of adjust_line_ns\ */
if (time_to_vsync > adjust_line_ns)
time_to_vsync -= adjust_line_ns;
}
if (!time_to_vsync)
return -EINVAL;
*wakeup_time = ktime_add_ns(current_time, time_to_vsync);
pr_debug("clk_rate=%lldkHz clk_period=%d cur_line=%d tot_line=%d\n",
clk_rate, clk_period, current_line, total_line);
pr_debug("time_to_vsync=%d current_time=%d wakeup_time=%d\n",
time_to_vsync, (int)ktime_to_ms(current_time),
(int)ktime_to_ms(*wakeup_time));
return 0;
}
static void __cpu_pm_work_handler(struct work_struct *work)
{
struct mdss_mdp_ctl *ctl =
container_of(work, typeof(*ctl), cpu_pm_work);
ktime_t wakeup_time;
struct mdss_overlay_private *mdp5_data;
if (!ctl)
return;
if (mdss_mdp_display_wakeup_time(ctl, &wakeup_time))
return;
mdp5_data = mfd_to_mdp5_data(ctl->mfd);
activate_event_timer(mdp5_data->cpu_pm_hdl, wakeup_time);
}
void mdss_mdp_ctl_event_timer(void *data)
{
struct mdss_overlay_private *mdp5_data =
(struct mdss_overlay_private *)data;
struct mdss_mdp_ctl *ctl = mdp5_data->ctl;
if (mdp5_data->cpu_pm_hdl && ctl && ctl->autorefresh_frame_cnt)
schedule_work(&ctl->cpu_pm_work);
}
int mdss_mdp_ctl_cmd_set_autorefresh(struct mdss_mdp_ctl *ctl, int frame_cnt)
{
int ret = 0;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(ctl->mfd);
if (ctl->panel_data->panel_info.type == MIPI_CMD_PANEL) {
ret = mdss_mdp_cmd_set_autorefresh_mode(ctl, frame_cnt);
if (!ret) {
ctl->autorefresh_frame_cnt = frame_cnt;
if (frame_cnt)
mdss_mdp_ctl_event_timer(mdp5_data);
}
} else {
pr_err("Mode not supported for this panel\n");
ret = -EINVAL;
@ -3824,6 +3935,7 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata,
ctl->intf_type = MDSS_INTF_DSI;
ctl->opmode = MDSS_MDP_CTL_OP_CMD_MODE;
ctl->ops.start_fnc = mdss_mdp_cmd_start;
INIT_WORK(&ctl->cpu_pm_work, __cpu_pm_work_handler);
break;
case DTV_PANEL:
ctl->is_video_mode = true;
@ -5408,83 +5520,6 @@ exit:
return ret;
}
int mdss_mdp_display_wakeup_time(struct mdss_mdp_ctl *ctl,
ktime_t *wakeup_time)
{
struct mdss_panel_info *pinfo;
u64 clk_rate;
u32 clk_period;
u32 current_line, total_line;
u32 time_of_line, time_to_vsync, adjust_line_ns;
ktime_t current_time = ktime_get();
if (!ctl->ops.read_line_cnt_fnc)
return -ENOSYS;
pinfo = &ctl->panel_data->panel_info;
if (!pinfo)
return -ENODEV;
clk_rate = mdss_mdp_get_pclk_rate(ctl);
clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate)
return -EINVAL;
/*
* calculate clk_period as pico second to maintain good
* accuracy with high pclk rate and this number is in 17 bit
* range.
*/
clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period)
return -EINVAL;
time_of_line = (pinfo->lcdc.h_back_porch +
pinfo->lcdc.h_front_porch +
pinfo->lcdc.h_pulse_width +
pinfo->xres) * clk_period;
time_of_line /= 1000; /* in nano second */
if (!time_of_line)
return -EINVAL;
current_line = ctl->ops.read_line_cnt_fnc(ctl);
total_line = pinfo->lcdc.v_back_porch +
pinfo->lcdc.v_front_porch +
pinfo->lcdc.v_pulse_width +
pinfo->yres;
if (current_line > total_line)
return -EINVAL;
time_to_vsync = time_of_line * (total_line - current_line);
if (pinfo->adjust_timer_delay_ms) {
adjust_line_ns = pinfo->adjust_timer_delay_ms
* 1000000; /* convert to ns */
/* Ignore large values of adjust_line_ns\ */
if (time_to_vsync > adjust_line_ns)
time_to_vsync -= adjust_line_ns;
}
if (!time_to_vsync)
return -EINVAL;
*wakeup_time = ktime_add_ns(current_time, time_to_vsync);
pr_debug("clk_rate=%lldkHz clk_period=%d cur_line=%d tot_line=%d\n",
clk_rate, clk_period, current_line, total_line);
pr_debug("time_to_vsync=%d current_time=%d wakeup_time=%d\n",
time_to_vsync, (int)ktime_to_ms(current_time),
(int)ktime_to_ms(*wakeup_time));
return 0;
}
int mdss_mdp_display_wait4comp(struct mdss_mdp_ctl *ctl)
{
int ret;

View file

@ -6082,8 +6082,8 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
/* Adding event timer only for primary panel */
if ((mfd->index == 0) && (mfd->panel_info->type != WRITEBACK_PANEL)) {
mdp5_data->cpu_pm_hdl = add_event_timer(mdss_irq->irq, NULL,
(void *)mdp5_data);
mdp5_data->cpu_pm_hdl = add_event_timer(mdss_irq->irq,
mdss_mdp_ctl_event_timer, (void *)mdp5_data);
if (!mdp5_data->cpu_pm_hdl)
pr_warn("%s: unable to add event timer\n", __func__);
}