msm: mdss: add mdp guard window property

For some panels, the mdp kickoff transaction must
be controlled and only triggered when the scanline
is within certain region. This change adds the
support to control the scanline where the kickoff
can be triggered through panel properties and
if the scanline is not within this region, then
driver will wait for an extra delay that is also
configurable through a panel property.

Change-Id: I06bc6b03f77109adfed428b876915f59d3b5bbfd
Signed-off-by: Ingrid Gallardo <ingridg@codeaurora.org>
This commit is contained in:
Ingrid Gallardo 2016-09-22 21:20:08 -07:00
parent 2590d5faf4
commit 1675d1884a
4 changed files with 150 additions and 13 deletions

View file

@ -249,6 +249,35 @@ Optional properties:
60 = 60 frames per second (default)
- qcom,mdss-dsi-panel-clockrate: A 64 bit value specifies the panel clock speed in Hz.
0 = default value.
- qcom,mdss-mdp-kickoff-threshold: This property can be used to define a region
(in terms of scanlines) where the
hardware is allowed
to trigger a data transfer from MDP to DSI.
If this property is used, the region must be defined setting
two values, the low and the high thresholds:
<low_threshold high_threshold>
Where following condition must be met:
low_threshold < high_threshold
These values will be used by the driver in such way that if
the Driver receives a request to kickoff a transfer (MDP to DSI),
the transfer will be triggered only if the following condition
is satisfied:
low_threshold < scanline < high_threshold
If the condition is not met, then the driver will delay the
transfer by the time defined in the following property:
"qcom,mdss-mdp-kickoff-delay".
So in order to use this property, the delay property must
be defined as well and greater than 0.
- qcom,mdss-mdp-kickoff-delay: This property defines the delay in microseconds that
the driver will delay before triggering an MDP transfer if the
thresholds defined by the following property are not met:
"qcom,mdss-mdp-kickoff-threshold".
So in order to use this property, the threshold property must
be defined as well. Note that this delay cannot be zero
and also should not be greater than
the fps window.
i.e. For 60fps value should not exceed
16666 uS.
- qcom,mdss-mdp-transfer-time-us: Specifies the dsi transfer time for command mode
panels in microseconds. Driver uses this number to adjust
the clock rate according to the expected transfer time.
@ -568,6 +597,8 @@ Example:
qcom,mdss-dsi-dma-trigger = <0>;
qcom,mdss-dsi-panel-framerate = <60>;
qcom,mdss-dsi-panel-clockrate = <424000000>;
qcom,mdss-mdp-kickoff-threshold = <11 2430>;
qcom,mdss-mdp-kickoff-delay = <1000>;
qcom,mdss-mdp-transfer-time-us = <12500>;
qcom,mdss-dsi-panel-timings = [7d 25 1d 00 37 33
22 27 1e 03 04 00];

View file

@ -851,6 +851,48 @@ static int mdss_dsi_panel_low_power_config(struct mdss_panel_data *pdata,
return 0;
}
static void mdss_dsi_parse_mdp_kickoff_threshold(struct device_node *np,
struct mdss_panel_info *pinfo)
{
int len, rc;
const u32 *src;
u32 tmp;
u32 max_delay_us;
pinfo->mdp_koff_thshold = false;
src = of_get_property(np, "qcom,mdss-mdp-kickoff-threshold", &len);
if (!src || (len == 0))
return;
rc = of_property_read_u32(np, "qcom,mdss-mdp-kickoff-delay", &tmp);
if (!rc)
pinfo->mdp_koff_delay = tmp;
else
return;
if (pinfo->mipi.frame_rate == 0) {
pr_err("cannot enable guard window, unexpected panel fps\n");
return;
}
pinfo->mdp_koff_thshold_low = be32_to_cpu(src[0]);
pinfo->mdp_koff_thshold_high = be32_to_cpu(src[1]);
max_delay_us = 1000000 / pinfo->mipi.frame_rate;
/* enable the feature if threshold is valid */
if ((pinfo->mdp_koff_thshold_low < pinfo->mdp_koff_thshold_high) &&
((pinfo->mdp_koff_delay > 0) ||
(pinfo->mdp_koff_delay < max_delay_us)))
pinfo->mdp_koff_thshold = true;
pr_debug("panel kickoff thshold:[%d, %d] delay:%d (max:%d) enable:%d\n",
pinfo->mdp_koff_thshold_low,
pinfo->mdp_koff_thshold_high,
pinfo->mdp_koff_delay,
max_delay_us,
pinfo->mdp_koff_thshold);
}
static void mdss_dsi_parse_trigger(struct device_node *np, char *trigger,
char *trigger_key)
{
@ -2492,6 +2534,8 @@ static int mdss_panel_parse_dt(struct device_node *np,
rc = of_property_read_u32(np, "qcom,mdss-mdp-transfer-time-us", &tmp);
pinfo->mdp_transfer_time_us = (!rc ? tmp : DEFAULT_MDP_TRANSFER_TIME);
mdss_dsi_parse_mdp_kickoff_threshold(np, pinfo);
pinfo->mipi.lp11_init = of_property_read_bool(np,
"qcom,mdss-dsi-lp11-init");
rc = of_property_read_u32(np, "qcom,mdss-dsi-init-delay-us", &tmp);

View file

@ -73,6 +73,7 @@ struct mdss_mdp_cmd_ctx {
struct mutex clk_mtx;
spinlock_t clk_lock;
spinlock_t koff_lock;
spinlock_t ctlstart_lock;
struct work_struct gate_clk_work;
struct delayed_work delayed_off_clk_work;
struct work_struct pp_done_work;
@ -144,15 +145,11 @@ static inline u32 mdss_mdp_cmd_line_count(struct mdss_mdp_ctl *ctl)
u32 init;
u32 height;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
if (!mixer) {
mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT);
if (!mixer) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
if (!mixer)
goto exit;
}
}
init = mdss_mdp_pingpong_read(mixer->pingpong_base,
@ -160,10 +157,8 @@ static inline u32 mdss_mdp_cmd_line_count(struct mdss_mdp_ctl *ctl)
height = mdss_mdp_pingpong_read(mixer->pingpong_base,
MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT) & 0xffff;
if (height < init) {
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
if (height < init)
goto exit;
}
cnt = mdss_mdp_pingpong_read(mixer->pingpong_base,
MDSS_MDP_REG_PP_INT_COUNT_VAL) & 0xffff;
@ -173,13 +168,21 @@ static inline u32 mdss_mdp_cmd_line_count(struct mdss_mdp_ctl *ctl)
else
cnt -= init;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
pr_debug("cnt=%d init=%d height=%d\n", cnt, init, height);
exit:
return cnt;
}
static inline u32 mdss_mdp_cmd_line_count_wrapper(struct mdss_mdp_ctl *ctl)
{
u32 ret;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
ret = mdss_mdp_cmd_line_count(ctl);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
return ret;
}
static int mdss_mdp_tearcheck_enable(struct mdss_mdp_ctl *ctl, bool enable)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
@ -2595,12 +2598,42 @@ static int mdss_mdp_disable_autorefresh(struct mdss_mdp_ctl *ctl,
return 0;
}
static bool wait_for_read_ptr_if_late(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *sctl, struct mdss_panel_info *pinfo)
{
u32 line_count;
u32 sline_count = 0;
bool ret = true;
u32 low_threshold = pinfo->mdp_koff_thshold_low;
u32 high_threshold = pinfo->mdp_koff_thshold_high;
/* read the line count */
line_count = mdss_mdp_cmd_line_count(ctl);
if (sctl)
sline_count = mdss_mdp_cmd_line_count(sctl);
/* if line count is between the range, return to trigger transfer */
if (((line_count > low_threshold) && (line_count < high_threshold)) &&
(!sctl || ((sline_count > low_threshold) &&
(sline_count < high_threshold))))
ret = false;
pr_debug("threshold:[%d, %d]\n", low_threshold, high_threshold);
pr_debug("line:%d sline:%d ret:%d\n", line_count, sline_count, ret);
MDSS_XLOG(line_count, sline_count, ret);
return ret;
}
static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_cmd_ctx *ctx)
struct mdss_mdp_ctl *sctl, struct mdss_mdp_cmd_ctx *ctx)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
bool is_pp_split = is_pingpong_split(ctl->mfd);
struct mdss_panel_info *pinfo = NULL;
if (ctl->panel_data)
pinfo = &ctl->panel_data->panel_info;
MDSS_XLOG(ctx->autorefresh_state);
@ -2625,9 +2658,33 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl,
ctx->autorefresh_state = MDP_AUTOREFRESH_ON;
} else {
/*
* Some panels can require that mdp is within some range
* of the scanlines in order to trigger the tansfer.
* If that is the case, make sure the panel scanline
* is within the limit to start.
* Acquire an spinlock for this operation to raise the
* priority of this thread and make sure the context
* is maintained, so we can have the less time possible
* between the check of the scanline and the kickoff.
*/
if (pinfo && pinfo->mdp_koff_thshold) {
spin_lock(&ctx->ctlstart_lock);
if (wait_for_read_ptr_if_late(ctl, sctl, pinfo)) {
spin_unlock(&ctx->ctlstart_lock);
usleep_range(pinfo->mdp_koff_delay,
pinfo->mdp_koff_delay + 10);
spin_lock(&ctx->ctlstart_lock);
}
}
/* SW Kickoff */
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1);
MDSS_XLOG(0x11, ctx->autorefresh_state);
if (pinfo && pinfo->mdp_koff_thshold)
spin_unlock(&ctx->ctlstart_lock);
}
}
@ -2759,7 +2816,7 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
}
/* Kickoff */
__mdss_mdp_kickoff(ctl, ctx);
__mdss_mdp_kickoff(ctl, sctl, ctx);
mdss_mdp_cmd_post_programming(ctl);
@ -3185,6 +3242,7 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
init_completion(&ctx->autorefresh_done);
spin_lock_init(&ctx->clk_lock);
spin_lock_init(&ctx->koff_lock);
spin_lock_init(&ctx->ctlstart_lock);
mutex_init(&ctx->clk_mtx);
mutex_init(&ctx->mdp_rdptr_lock);
mutex_init(&ctx->mdp_wrptr_lock);
@ -3475,7 +3533,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
ctl->ops.wait_pingpong = mdss_mdp_cmd_wait4pingpong;
ctl->ops.add_vsync_handler = mdss_mdp_cmd_add_vsync_handler;
ctl->ops.remove_vsync_handler = mdss_mdp_cmd_remove_vsync_handler;
ctl->ops.read_line_cnt_fnc = mdss_mdp_cmd_line_count;
ctl->ops.read_line_cnt_fnc = mdss_mdp_cmd_line_count_wrapper;
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;

View file

@ -633,6 +633,10 @@ struct mdss_panel_info {
u32 saved_fporch;
/* current fps, once is programmed in hw */
int current_fps;
u32 mdp_koff_thshold_low;
u32 mdp_koff_thshold_high;
bool mdp_koff_thshold;
u32 mdp_koff_delay;
int panel_max_fps;
int panel_max_vtotal;