mdss: display: Add support for variable refresh rate

Add support for variable refresh rate by changing the
VFP immediately and programming it to MDP TG and
DSI registers.

Change-Id: I186e80cf38d42f4224afedadda96cc613ae68470
Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
This commit is contained in:
Padmanabhan Komanduru 2014-01-17 16:49:22 +05:30
parent d82905e562
commit 6f1e602d38
6 changed files with 160 additions and 31 deletions

View file

@ -126,6 +126,9 @@ Optional properties:
implemented during suspend/resume.
"dfps_immediate_clk_mode" = FPS change request is
implemented immediately using DSI clocks.
"dfps_immediate_porch_mode" = FPS change request is
implemented immediately by changing panel porch
values.
- qcom,mdss-dsi-bl-pmic-control-type: A string that specifies the implementation of backlight
control for this panel.
"bl_ctrl_pwm" = Backlight controlled by PWM gpio.

View file

@ -659,33 +659,58 @@ static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps)
if (new_fps !=
ctrl_pdata->panel_data.panel_info.mipi.frame_rate) {
rc = mdss_dsi_clk_div_config
(&ctrl_pdata->panel_data.panel_info, new_fps);
if (rc) {
pr_err("%s: unable to initialize the clk dividers\n",
__func__);
return rc;
}
ctrl_pdata->pclk_rate =
ctrl_pdata->panel_data.panel_info.mipi.dsi_pclk_rate;
ctrl_pdata->byte_clk_rate =
ctrl_pdata->panel_data.panel_info.clk_rate / 8;
if (pdata->panel_info.dfps_update
== DFPS_IMMEDIATE_CLK_UPDATE_MODE) {
dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) +
0x0004);
ctrl_pdata->panel_data.panel_info.mipi.frame_rate =
new_fps;
dsi_ctrl &= ~0x2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
mdss_dsi_controller_cfg(true, pdata);
mdss_dsi_clk_ctrl(ctrl_pdata, 0);
mdss_dsi_clk_ctrl(ctrl_pdata, 1);
dsi_ctrl |= 0x2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
== DFPS_IMMEDIATE_PORCH_UPDATE_MODE) {
u32 hsync_period, vsync_period;
u32 new_dsi_v_total, current_dsi_v_total;
vsync_period =
mdss_panel_get_vtotal(&pdata->panel_info);
hsync_period =
mdss_panel_get_htotal(&pdata->panel_info);
current_dsi_v_total =
MIPI_INP((ctrl_pdata->ctrl_base) + 0x2C);
new_dsi_v_total =
((vsync_period - 1) << 16) | (hsync_period - 1);
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
(current_dsi_v_total | 0x8000000));
if (new_dsi_v_total & 0x8000000) {
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
new_dsi_v_total);
} else {
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
(new_dsi_v_total | 0x8000000));
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C,
(new_dsi_v_total & 0x7ffffff));
}
pdata->panel_info.mipi.frame_rate = new_fps;
} else {
rc = mdss_dsi_clk_div_config
(&ctrl_pdata->panel_data.panel_info, new_fps);
if (rc) {
pr_err("%s: unable to initialize the clk dividers\n",
__func__);
return rc;
}
ctrl_pdata->pclk_rate =
pdata->panel_info.mipi.dsi_pclk_rate;
ctrl_pdata->byte_clk_rate =
pdata->panel_info.clk_rate / 8;
if (pdata->panel_info.dfps_update
== DFPS_IMMEDIATE_CLK_UPDATE_MODE) {
dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) +
0x0004);
pdata->panel_info.mipi.frame_rate = new_fps;
dsi_ctrl &= ~0x2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
mdss_dsi_controller_cfg(true, pdata);
mdss_dsi_clk_ctrl(ctrl_pdata, 0);
mdss_dsi_clk_ctrl(ctrl_pdata, 1);
dsi_ctrl |= 0x2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004,
dsi_ctrl);
}
}
} else {
pr_debug("%s: Panel is already at this FPS\n", __func__);
@ -1220,6 +1245,12 @@ int dsi_panel_device_register(struct device_node *pan_node,
DFPS_IMMEDIATE_CLK_UPDATE_MODE;
pr_debug("%s: dfps mode: Immediate clk\n",
__func__);
} else if (!strcmp(data,
"dfps_immediate_porch_mode")) {
pinfo->dfps_update =
DFPS_IMMEDIATE_PORCH_UPDATE_MODE;
pr_debug("%s: dfps mode: Immediate porch\n",
__func__);
} else {
pr_debug("%s: dfps to default mode\n",
__func__);

View file

@ -186,7 +186,8 @@ struct mdss_mdp_ctl {
struct mdss_mdp_vsync_handler *);
int (*remove_vsync_handler) (struct mdss_mdp_ctl *,
struct mdss_mdp_vsync_handler *);
int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl, int new_fps);
int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *sctl, int new_fps);
struct blocking_notifier_head notifier_head;

View file

@ -2240,9 +2240,12 @@ static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer)
int mdss_mdp_ctl_update_fps(struct mdss_mdp_ctl *ctl, int fps)
{
int ret = 0;
struct mdss_mdp_ctl *sctl = NULL;
sctl = mdss_mdp_get_split_ctl(ctl);
if (ctl->config_fps_fnc)
ret = ctl->config_fps_fnc(ctl, fps);
ret = ctl->config_fps_fnc(ctl, sctl, fps);
return ret;
}

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2014, 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
@ -466,7 +466,63 @@ static void mdss_mdp_video_underrun_intr_done(void *arg)
ctl->underrun_cnt);
}
static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
static int mdss_mdp_video_vfp_fps_update(struct mdss_mdp_ctl *ctl, int new_fps)
{
int curr_fps;
u32 add_v_lines = 0;
u32 current_vsync_period_f0, new_vsync_period_f0;
struct mdss_panel_data *pdata;
struct mdss_mdp_video_ctx *ctx;
u32 vsync_period, hsync_period;
ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
if (!ctx) {
pr_err("invalid ctx\n");
return -ENODEV;
}
pdata = ctl->panel_data;
if (pdata == NULL) {
pr_err("%s: Invalid panel data\n", __func__);
return -EINVAL;
}
vsync_period = mdss_panel_get_vtotal(&pdata->panel_info);
hsync_period = mdss_panel_get_htotal(&pdata->panel_info);
curr_fps = mdss_panel_get_framerate(&pdata->panel_info);
if (curr_fps > new_fps) {
add_v_lines = mult_frac(vsync_period,
(curr_fps - new_fps), new_fps);
pdata->panel_info.lcdc.v_front_porch += add_v_lines;
} else {
add_v_lines = mult_frac(vsync_period,
(new_fps - curr_fps), new_fps);
pdata->panel_info.lcdc.v_front_porch -= add_v_lines;
}
vsync_period = mdss_panel_get_vtotal(&pdata->panel_info);
current_vsync_period_f0 = mdp_video_read(ctx,
MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0);
new_vsync_period_f0 = (vsync_period * hsync_period);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
current_vsync_period_f0 | 0x800000);
if (new_vsync_period_f0 & 0x800000) {
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
new_vsync_period_f0);
} else {
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
new_vsync_period_f0 | 0x800000);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
new_vsync_period_f0 & 0x7fffff);
}
return 0;
}
static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *sctl, int new_fps)
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_panel_data *pdata;
@ -529,6 +585,40 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
ctl->force_screen_state = MDSS_SCREEN_DEFAULT;
mdss_mdp_display_commit(ctl, NULL);
mdss_mdp_display_wait4comp(ctl);
} else if (pdata->panel_info.dfps_update
== DFPS_IMMEDIATE_PORCH_UPDATE_MODE){
if (!ctx->timegen_en) {
pr_err("TG is OFF. DFPS mode invalid\n");
return -EINVAL;
}
video_vsync_irq_enable(ctl, true);
INIT_COMPLETION(ctx->vsync_comp);
rc = wait_for_completion_timeout(&ctx->vsync_comp,
usecs_to_jiffies(VSYNC_TIMEOUT_US));
WARN(rc <= 0, "timeout (%d) vsync interrupt on ctl=%d\n",
rc, ctl->num);
rc = 0;
video_vsync_irq_disable(ctl);
rc = mdss_mdp_video_vfp_fps_update(ctl, new_fps);
if (rc < 0) {
pr_err("%s: Error during DFPS\n", __func__);
return rc;
}
if (sctl) {
rc = mdss_mdp_video_vfp_fps_update(sctl,
new_fps);
if (rc < 0) {
pr_err("%s: DFPS error\n", __func__);
return rc;
}
}
rc = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_PANEL_UPDATE_FPS,
(void *)new_fps);
WARN(rc, "intf %d panel fps update error (%d)\n",
ctl->intf_num, rc);
} else {
pr_err("intf %d panel, unknown FPS mode\n",
ctl->intf_num);

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2008-2014, 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
@ -234,6 +234,7 @@ struct edp_panel_info {
enum dynamic_fps_update {
DFPS_SUSPEND_RESUME_MODE,
DFPS_IMMEDIATE_CLK_UPDATE_MODE,
DFPS_IMMEDIATE_PORCH_UPDATE_MODE,
};
enum lvds_mode {