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:
parent
2590d5faf4
commit
1675d1884a
4 changed files with 150 additions and 13 deletions
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue