summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Salido-Moreno <adrianm@codeaurora.org>2016-02-23 19:09:34 -0800
committerKyle Yan <kyan@codeaurora.org>2016-04-28 16:43:45 -0700
commit49765e580abe02bb5786170322e1f135e03c24f9 (patch)
tree2781e0de22f1c1b8487c62f98cfe31b8a67c4b37
parent2f7c91ce4b38af84f01c0b0e971308d91195aa7c (diff)
msm: mdss: add support for multi-rect feature
Add support for DMA multirect pipes available in new MDP revision. These pipes are able to fetch two different buffers and rectangles to allow more layers to be fetched. Add support and proper validation checks for limitations. CRs-Fixed: 987777 Change-Id: I588ecd7829a45908241f6d9fa1e97147e79798f6 Signed-off-by: Ujwal Patel <ujwalp@codeaurora.org> Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
-rw-r--r--drivers/video/fbdev/msm/mdss.h11
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c21
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h58
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c59
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_debug.c69
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h12
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c613
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c77
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c483
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c41
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c3
-rw-r--r--include/uapi/linux/msm_mdp_ext.h12
12 files changed, 1151 insertions, 308 deletions
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 2c26a80bbc85..b5fd49de457b 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -162,6 +162,7 @@ enum mdss_hw_quirk {
MDSS_QUIRK_MIN_BUS_VOTE,
MDSS_QUIRK_FMT_PACK_PATTERN,
MDSS_QUIRK_NEED_SECURE_MAP,
+ MDSS_QUIRK_SRC_SPLIT_ALWAYS,
MDSS_QUIRK_MAX,
};
@@ -187,6 +188,15 @@ enum mdss_qos_settings {
MDSS_QOS_MAX,
};
+enum mdss_mdp_pipe_type {
+ MDSS_MDP_PIPE_TYPE_INVALID = -1,
+ MDSS_MDP_PIPE_TYPE_VIG = 0,
+ MDSS_MDP_PIPE_TYPE_RGB,
+ MDSS_MDP_PIPE_TYPE_DMA,
+ MDSS_MDP_PIPE_TYPE_CURSOR,
+ MDSS_MDP_PIPE_TYPE_MAX,
+};
+
struct reg_bus_client {
char name[MAX_CLIENT_NAME_LEN];
short usecase_ndx;
@@ -389,6 +399,7 @@ struct mdss_data_type {
struct mdss_hw_settings *hw_settings;
+ int rects_per_sspp[MDSS_MDP_PIPE_TYPE_MAX];
struct mdss_mdp_pipe *vig_pipes;
struct mdss_mdp_pipe *rgb_pipes;
struct mdss_mdp_pipe *dma_pipes;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index e472be9bad7b..266fa8cc8192 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1517,6 +1517,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->min_prefill_lines = 21;
mdata->has_ubwc = true;
mdata->pixel_ram_size = 50 * 1024;
+ mdata->rects_per_sspp[MDSS_MDP_PIPE_TYPE_DMA] = 2;
+
set_bit(MDSS_QOS_PER_PIPE_IB, mdata->mdss_qos_map);
set_bit(MDSS_QOS_OVERHEAD_FACTOR, mdata->mdss_qos_map);
set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map);
@@ -1533,6 +1535,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_mdp_init_default_prefill_factors(mdata);
mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
+ mdss_set_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS);
mdata->has_wb_ubwc = true;
set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map);
break;
@@ -1896,14 +1899,14 @@ static void __update_sspp_info(struct mdss_mdp_pipe *pipe,
(*cnt += scnprintf(buf + *cnt, len - *cnt, fmt, ##__VA_ARGS__))
for (i = 0; i < pipe_cnt; i++) {
- SPRINT("pipe_num:%d pipe_type:%s pipe_ndx:%d pipe_is_handoff:%d display_id:%d ",
- pipe->num, type, pipe->ndx, pipe->is_handed_off,
- mdss_mdp_get_display_id(pipe));
+ SPRINT("pipe_num:%d pipe_type:%s pipe_ndx:%d rects:%d pipe_is_handoff:%d display_id:%d ",
+ pipe->num, type, pipe->ndx, pipe->multirect.max_rects,
+ pipe->is_handed_off, mdss_mdp_get_display_id(pipe));
SPRINT("fmts_supported:");
for (j = 0; j < num_bytes && pipe; j++)
SPRINT("%d,", pipe->supported_formats[j]);
SPRINT("\n");
- pipe++;
+ pipe += pipe->multirect.max_rects;
}
#undef SPRINT
}
@@ -2647,6 +2650,7 @@ static int mdss_mdp_parse_dt_pipe_helper(struct platform_device *pdev,
struct mdss_mdp_pipe *pipe_list;
char prop_name[64];
int i, cnt, rc;
+ u32 rects_per_sspp;
if (!out_plist)
return -EINVAL;
@@ -2667,8 +2671,12 @@ static int mdss_mdp_parse_dt_pipe_helper(struct platform_device *pdev,
return 0;
}
+ /* by default works in single rect mode unless otherwise noted */
+ rects_per_sspp = mdata->rects_per_sspp[ptype] ? : 1;
+
pipe_list = devm_kzalloc(&pdev->dev,
- (sizeof(struct mdss_mdp_pipe) * cnt), GFP_KERNEL);
+ (sizeof(struct mdss_mdp_pipe) * cnt * rects_per_sspp),
+ GFP_KERNEL);
if (!pipe_list)
return -ENOMEM;
@@ -2697,7 +2705,8 @@ static int mdss_mdp_parse_dt_pipe_helper(struct platform_device *pdev,
goto parse_fail;
rc = mdss_mdp_pipe_addr_setup(mdata, pipe_list, offsets, ftch_id,
- xin_id, ptype, pnums, cnt, priority_base);
+ xin_id, ptype, pnums, cnt, rects_per_sspp,
+ priority_base);
if (rc)
goto parse_fail;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 748e350e122d..6abe08111334 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -114,14 +114,6 @@ enum mdss_mdp_mixer_mux {
MDSS_MDP_MIXER_MUX_RIGHT,
};
-enum mdss_mdp_pipe_type {
- MDSS_MDP_PIPE_TYPE_INVALID,
- MDSS_MDP_PIPE_TYPE_VIG,
- MDSS_MDP_PIPE_TYPE_RGB,
- MDSS_MDP_PIPE_TYPE_DMA,
- MDSS_MDP_PIPE_TYPE_CURSOR,
-};
-
static inline enum mdss_mdp_sspp_index get_pipe_num_from_ndx(u32 ndx)
{
u32 id;
@@ -647,6 +639,42 @@ struct mdss_mdp_shared_reg_ctrl {
u32 bit_off;
};
+enum mdss_mdp_pipe_rect {
+ MDSS_MDP_PIPE_RECT0, /* default */
+ MDSS_MDP_PIPE_RECT1,
+ MDSS_MDP_PIPE_MAX_RECTS,
+};
+
+/**
+ * enum mdss_mdp_pipe_multirect_mode - pipe multirect mode
+ * @MDSS_MDP_PIPE_MULTIRECT_NONE: pipe is not working in multirect mode
+ * @MDSS_MDP_PIPE_MULTIRECT_PARALLEL: rectangles are being fetched at the
+ * same time in time multiplexed fashion
+ * @MDSS_MDP_PIPE_MULTIRECT_SERIAL: rectangles are fetched serially, where
+ * one is only fetched after the other one
+ * is complete
+ */
+enum mdss_mdp_pipe_multirect_mode {
+ MDSS_MDP_PIPE_MULTIRECT_NONE,
+ MDSS_MDP_PIPE_MULTIRECT_PARALLEL,
+ MDSS_MDP_PIPE_MULTIRECT_SERIAL,
+};
+
+/**
+ * struct mdss_mdp_pipe_multirect_params - multirect info for layer or pipe
+ * @num: rectangle being operated, default is RECT0 if pipe doesn't
+ * support multirect
+ * @mode: mode of multirect operation, default is NONE
+ * @next: pointer to sibling pipe/layer which is also operating in
+ * multirect mode
+ */
+struct mdss_mdp_pipe_multirect_params {
+ enum mdss_mdp_pipe_rect num; /* RECT0 or RECT1 */
+ int max_rects;
+ enum mdss_mdp_pipe_multirect_mode mode;
+ void *next; /* pointer to next pipe or layer */
+};
+
struct mdss_mdp_pipe {
u32 num;
u32 type;
@@ -719,6 +747,8 @@ struct mdss_mdp_pipe {
u32 frame_rate;
u8 csc_coeff_set;
u8 supported_formats[BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)];
+
+ struct mdss_mdp_pipe_multirect_params multirect;
};
struct mdss_mdp_writeback_arg {
@@ -1402,7 +1432,8 @@ int mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe,
u32 flags);
int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe);
struct mdss_mdp_pipe *mdss_mdp_pipe_assign(struct mdss_data_type *mdata,
- struct mdss_mdp_mixer *mixer, u32 ndx);
+ struct mdss_mdp_mixer *mixer, u32 ndx,
+ enum mdss_mdp_pipe_rect rect_num);
struct mdss_mdp_pipe *mdss_mdp_overlay_pipe_reuse(
struct msm_fb_data_type *mfd, int pipe_ndx);
void mdss_mdp_pipe_position_update(struct mdss_mdp_pipe *pipe,
@@ -1557,12 +1588,12 @@ int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe);
int mdss_mdp_smp_handoff(struct mdss_data_type *mdata);
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
u32 type, struct mdss_mdp_pipe *left_blend_pipe);
-struct mdss_mdp_pipe *mdss_mdp_pipe_get(struct mdss_data_type *mdata, u32 ndx);
+struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx,
+ enum mdss_mdp_pipe_rect rect_num);
struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata,
- u32 ndx);
+ u32 ndx, enum mdss_mdp_pipe_rect rect_num);
int mdss_mdp_pipe_map(struct mdss_mdp_pipe *pipe);
void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe);
-struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_dma(struct mdss_mdp_mixer *mixer);
u32 mdss_mdp_smp_calc_num_blocks(struct mdss_mdp_pipe *pipe);
u32 mdss_mdp_smp_get_size(struct mdss_mdp_pipe *pipe);
@@ -1572,7 +1603,8 @@ void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_id, u32 *xin_id,
- u32 type, const int *pnums, u32 len, u8 priority_base);
+ u32 type, const int *pnums, u32 len, u32 rects_per_sspp,
+ u8 priority_base);
int mdss_mdp_mixer_addr_setup(struct mdss_data_type *mdata, u32 *mixer_offsets,
u32 *dspp_offsets, u32 *pingpong_offsets, u32 type, u32 len);
int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 7b4c945c1e7e..46d8b4ccd3f4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -57,6 +57,15 @@ static struct {
[MDSS_MDP_SSPP_CURSOR1] = { 23, .ext = { 26, 4, 0 } },
};
+static struct {
+ struct mdss_mdp_hwio_cfg ext2;
+} mdp_pipe_rec1_hwio[MDSS_MDP_MAX_SSPP] = {
+ [MDSS_MDP_SSPP_DMA0] = { .ext2 = { 8, 4, 0 } },
+ [MDSS_MDP_SSPP_DMA1] = { .ext2 = { 12, 4, 0 } },
+ [MDSS_MDP_SSPP_DMA2] = { .ext2 = { 16, 4, 0 } },
+ [MDSS_MDP_SSPP_DMA3] = { .ext2 = { 20, 4, 0 } },
+};
+
static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
struct mdss_mdp_mixer_cfg *cfg);
@@ -475,7 +484,7 @@ u32 mdss_mdp_perf_calc_smp_size(struct mdss_mdp_pipe *pipe,
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 smp_bytes;
- if (pipe->type == PIPE_TYPE_CURSOR)
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_CURSOR)
return 0;
/* Get allocated or fixed smp bytes */
@@ -1086,7 +1095,8 @@ static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
if (mdss_mdp_perf_is_overlap(y0, y1, pipe->dst.y,
(pipe->dst.y + pipe->dst.h)))
bw_max_region += bw_overlap[j];
- pr_debug("v[%d](%d,%d)pipe[%d](%d,%d)bw(%llu %llu)\n",
+ pr_debug("pipe%d rect%d: v[%d](%d,%d)pipe[%d](%d,%d)bw(%llu %llu)\n",
+ pipe->num, pipe->multirect.num,
i, y0, y1, j, pipe->dst.y,
pipe->dst.y + pipe->dst.h, bw_overlap[j],
bw_max_region);
@@ -3845,7 +3855,13 @@ static void mdss_mdp_pipe_reset(struct mdss_mdp_mixer *mixer, bool is_recovery)
for_each_set_bit_from(bit, &pipe_map, MAX_PIPES_PER_LM) {
struct mdss_mdp_pipe *pipe;
- pipe = mdss_mdp_pipe_search(mdata, 1 << bit);
+ /*
+ * this assumes that within lm there can be either rect0+rect1
+ * or rect0 only. Thus to find the hardware pipe to halt only
+ * check for rect 0 is sufficient.
+ */
+ pipe = mdss_mdp_pipe_search(mdata, 1 << bit,
+ MDSS_MDP_PIPE_RECT0);
if (pipe) {
mdss_mdp_pipe_fetch_halt(pipe, is_recovery);
if (sw_rst_avail)
@@ -4047,8 +4063,9 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl,
}
}
-static void __mdss_mdp_mixer_update_cfg_masks(u32 pnum, u32 stage,
- struct mdss_mdp_mixer_cfg *cfg)
+static void __mdss_mdp_mixer_update_cfg_masks(u32 pnum,
+ enum mdss_mdp_pipe_rect rect_num,
+ u32 stage, struct mdss_mdp_mixer_cfg *cfg)
{
u32 masks[NUM_MIXERCFG_REGS] = { 0 };
int i;
@@ -4056,9 +4073,14 @@ static void __mdss_mdp_mixer_update_cfg_masks(u32 pnum, u32 stage,
if (pnum >= MDSS_MDP_MAX_SSPP)
return;
- masks[0] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].base, stage);
- masks[1] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].ext, stage);
- masks[2] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].ext2, stage);
+ if (rect_num == MDSS_MDP_PIPE_RECT0) {
+ masks[0] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].base, stage);
+ masks[1] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].ext, stage);
+ masks[2] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].ext2, stage);
+ } else { /* RECT1 */
+ masks[2] = mdss_mdp_hwio_mask(&mdp_pipe_rec1_hwio[pnum].ext2,
+ stage);
+ }
for (i = 0; i < NUM_MIXERCFG_REGS; i++)
cfg->config_masks[i] |= masks[i];
@@ -4141,9 +4163,9 @@ static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
__mdss_mdp_mixer_write_layer(mixer->ctl, mixer_num,
vals, ARRAY_SIZE(vals));
- pr_debug("mixer=%d cfg=0%08x cfg_extn=0x%08x\n",
- mixer->num, vals[0], vals[1]);
- MDSS_XLOG(mixer->num, vals[0], vals[1]);
+ pr_debug("mixer=%d cfg=0%08x cfg_extn=0x%08x cfg_extn2=0x%08x\n",
+ mixer->num, vals[0], vals[1], vals[2]);
+ MDSS_XLOG(mixer->num, vals[0], vals[1], vals[2]);
}
void mdss_mdp_reset_mixercfg(struct mdss_mdp_ctl *ctl)
@@ -4179,7 +4201,8 @@ bool mdss_mdp_mixer_reg_has_pipe(struct mdss_mdp_mixer *mixer,
for (i = 0; i < NUM_MIXERCFG_REGS; i++)
cfgs[i] = mdss_mdp_ctl_read(mixer->ctl, offs[i]);
- __mdss_mdp_mixer_update_cfg_masks(pipe->num, -1, &mixercfg);
+ __mdss_mdp_mixer_update_cfg_masks(pipe->num, pipe->multirect.num, -1,
+ &mixercfg);
for (i = 0; i < NUM_MIXERCFG_REGS; i++) {
if (cfgs[i] & mixercfg.config_masks[i]) {
MDSS_XLOG(mixer->num, cfgs[0], cfgs[1]);
@@ -4276,7 +4299,8 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
mixercfg.border_enabled = true;
} else {
__mdss_mdp_mixer_update_cfg_masks(pipe->num,
- MDSS_MDP_STAGE_BASE, &mixercfg);
+ pipe->multirect.num, MDSS_MDP_STAGE_BASE,
+ &mixercfg);
if (pipe->src_fmt->alpha_enable)
bg_alpha_enable = 1;
@@ -4290,9 +4314,9 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
stage = i / MAX_PIPES_PER_STAGE;
if (stage != pipe->mixer_stage) {
- pr_err("pipe%d mixer:%d mixer:%d stage mismatch. pipe->mixer_stage=%d, mixer->stage_pipe=%d. skip staging it\n",
- pipe->num, mixer->num, mixer->num,
- pipe->mixer_stage, stage);
+ pr_warn("pipe%d rec%d mixer:%d stage mismatch. pipe->mixer_stage=%d, mixer->stage_pipe=%d multirect_mode=%d. skip staging it\n",
+ pipe->num, pipe->multirect.num, mixer->num,
+ pipe->mixer_stage, stage, pipe->multirect.mode);
mixer->stage_pipe[i] = NULL;
continue;
}
@@ -4377,7 +4401,8 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
if (!pipe->src_fmt->alpha_enable && bg_alpha_enable)
mixer_op_mode = 0;
- __mdss_mdp_mixer_update_cfg_masks(pipe->num, stage, &mixercfg);
+ __mdss_mdp_mixer_update_cfg_masks(pipe->num,
+ pipe->multirect.num, stage, &mixercfg);
trace_mdp_sspp_change(pipe);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 943a454a906e..f19161ad25c4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -1074,6 +1074,23 @@ static void __dump_pipe(struct seq_file *s, struct mdss_mdp_pipe *pipe)
seq_printf(s, "\tstage=%d alpha=0x%x transp=0x%x blend_op=%d\n",
pipe->mixer_stage, pipe->alpha,
pipe->transp, pipe->blend_op);
+ if (pipe->multirect.max_rects > 1) {
+ const char const *fmodes[] = {
+ [MDSS_MDP_PIPE_MULTIRECT_PARALLEL] = "parallel",
+ [MDSS_MDP_PIPE_MULTIRECT_SERIAL] = "serial",
+ [MDSS_MDP_PIPE_MULTIRECT_NONE] = "single",
+ };
+ const char *mode = NULL;
+
+ if (pipe->multirect.mode < ARRAY_SIZE(fmodes))
+ mode = fmodes[pipe->multirect.mode];
+ if (!mode)
+ mode = "invalid";
+
+ seq_printf(s, "\trect=%d/%d fetch_mode=%s\n",
+ pipe->multirect.num, pipe->multirect.max_rects,
+ mode);
+ }
format = pipe->src_fmt->format;
seq_printf(s, "\tsrc w=%d h=%d format=%d (%s)\n",
@@ -1403,10 +1420,39 @@ static void __stats_ctl_dump(struct mdss_mdp_ctl *ctl, struct seq_file *s)
}
}
+static void __dump_stat(struct seq_file *s, char *ptypestr,
+ struct mdss_mdp_pipe *pipe_list, int count)
+{
+ struct mdss_mdp_pipe *pipe;
+ int i = 0, ndx = 0;
+ u32 rects_per_pipe = 1;
+
+ while (i < count) {
+ pipe = pipe_list + ndx;
+ rects_per_pipe = pipe->multirect.max_rects;
+
+ if (rects_per_pipe == 1)
+ seq_printf(s, "%s%d", ptypestr, i);
+ else
+ seq_printf(s, "%s%d.%d", ptypestr, i,
+ ndx % rects_per_pipe);
+
+ seq_printf(s, " : %08u\t", pipe->play_cnt);
+
+ if ((++ndx % rects_per_pipe) == 0)
+ i++;
+
+ if ((ndx % 4) == 0)
+ seq_puts(s, "\n");
+ }
+
+ if ((ndx % 4) != 0)
+ seq_puts(s, "\n");
+}
+
static int mdss_debugfs_stats_show(struct seq_file *s, void *v)
{
struct mdss_data_type *mdata = (struct mdss_data_type *)s->private;
- struct mdss_mdp_pipe *pipe;
int i;
seq_puts(s, "\nmdp:\n");
@@ -1415,23 +1461,10 @@ static int mdss_debugfs_stats_show(struct seq_file *s, void *v)
__stats_ctl_dump(mdata->ctl_off + i, s);
seq_puts(s, "\n");
- for (i = 0; i < mdata->nvig_pipes; i++) {
- pipe = mdata->vig_pipes + i;
- seq_printf(s, "VIG%d : %08u\t", i, pipe->play_cnt);
- }
- seq_puts(s, "\n");
-
- for (i = 0; i < mdata->nrgb_pipes; i++) {
- pipe = mdata->rgb_pipes + i;
- seq_printf(s, "RGB%d : %08u\t", i, pipe->play_cnt);
- }
- seq_puts(s, "\n");
-
- for (i = 0; i < mdata->ndma_pipes; i++) {
- pipe = mdata->dma_pipes + i;
- seq_printf(s, "DMA%d : %08u\t", i, pipe->play_cnt);
- }
- seq_puts(s, "\n");
+ __dump_stat(s, "VIG", mdata->vig_pipes, mdata->nvig_pipes);
+ __dump_stat(s, "RGB", mdata->rgb_pipes, mdata->nrgb_pipes);
+ __dump_stat(s, "DMA", mdata->dma_pipes, mdata->ndma_pipes);
+ __dump_stat(s, "CURSOR", mdata->cursor_pipes, mdata->ncursor_pipes);
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 9a0c1c9afa53..7669a0be21c5 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -239,6 +239,7 @@ enum mdss_mdp_sspp_chroma_samp_type {
#define MDSS_MDP_REG_SSPP_STILE_FRAME_SIZE 0x02C
#define MDSS_MDP_REG_SSPP_SRC_FORMAT 0x030
#define MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN 0x034
+#define MDSS_MDP_REG_SSPP_SRC_OP_MODE 0x038
#define MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR 0x03C
#define MDSS_MDP_REG_SSPP_REQPRIO_FIFO_WM_0 0x050
#define MDSS_MDP_REG_SSPP_REQPRIO_FIFO_WM_1 0x054
@@ -250,7 +251,16 @@ enum mdss_mdp_sspp_chroma_samp_type {
#define MDSS_MDP_REG_SSPP_CDP_CTRL 0x134
#define MDSS_MDP_REG_SSPP_UBWC_ERROR_STATUS 0x138
-#define MDSS_MDP_REG_SSPP_SRC_OP_MODE 0x038
+#define MDSS_MDP_REG_SSPP_MULTI_REC_OP_MODE 0x170
+#define MDSS_MDP_REG_SSPP_OUT_SIZE_REC1 0x160
+#define MDSS_MDP_REG_SSPP_OUT_XY_REC1 0x164
+#define MDSS_MDP_REG_SSPP_SRC_XY_REC1 0x168
+#define MDSS_MDP_REG_SSPP_SRC_SIZE_REC1 0x16C
+#define MDSS_MDP_REG_SSPP_SRC_FORMAT_REC1 0x174
+#define MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN_REC1 0x178
+#define MDSS_MDP_REG_SSPP_SRC_OP_MODE_REC1 0x17C
+#define MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR_REC1 0x180
+
#define MDSS_MDP_OP_DEINTERLACE BIT(22)
#define MDSS_MDP_OP_DEINTERLACE_ODD BIT(23)
#define MDSS_MDP_OP_IGC_ROM_1 BIT(18)
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index f437109c8382..bf7e65beb68c 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -56,6 +56,11 @@ enum layer_zorder_used {
LAYER_ZORDER_BOTH = 3,
};
+struct mdss_mdp_validate_info_t {
+ struct mdp_input_layer *layer;
+ struct mdss_mdp_pipe_multirect_params multirect;
+};
+
/*
* __layer_needs_src_split() - check needs source split configuration
* @layer: input layer
@@ -64,7 +69,10 @@ enum layer_zorder_used {
*/
static bool __layer_needs_src_split(struct mdp_input_layer *layer)
{
- return layer->flags & MDP_LAYER_ASYNC;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ return (layer->flags & MDP_LAYER_ASYNC) ||
+ mdss_has_quirk(mdata, MDSS_QUIRK_SRC_SPLIT_ALWAYS);
}
static int __async_update_position_check(struct msm_fb_data_type *mfd,
@@ -157,7 +165,8 @@ static int __layer_xres_check(struct msm_fb_data_type *mfd,
}
static int __layer_param_check(struct msm_fb_data_type *mfd,
- struct mdp_input_layer *layer, struct mdss_mdp_format_params *fmt)
+ struct mdp_input_layer *layer, struct mdss_mdp_format_params *fmt,
+ enum mdss_mdp_pipe_rect rect_num)
{
u32 yres;
u32 min_src_size, min_dst_size = 1;
@@ -191,8 +200,9 @@ static int __layer_param_check(struct msm_fb_data_type *mfd,
return -EINVAL;
}
- if (!mdss_mdp_pipe_search(mdata, layer->pipe_ndx)) {
- pr_err("layer pipe is invalid :%d\n", layer->pipe_ndx);
+ if (!mdss_mdp_pipe_search(mdata, layer->pipe_ndx, rect_num)) {
+ pr_err("layer pipe is invalid: 0x%x rect:%d\n",
+ layer->pipe_ndx, rect_num);
return -EINVAL;
}
@@ -345,7 +355,7 @@ static int __validate_layer_reconfig(struct mdp_input_layer *layer,
}
static int __validate_single_layer(struct msm_fb_data_type *mfd,
- struct mdp_input_layer *layer, u32 mixer_mux)
+ struct mdss_mdp_validate_info_t *layer_info, u32 mixer_mux)
{
u32 bwc_enabled;
int ret;
@@ -354,6 +364,7 @@ static int __validate_single_layer(struct msm_fb_data_type *mfd,
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdp_input_layer *layer = layer_info->layer;
int ptype = get_pipe_type_from_ndx(layer->pipe_ndx);
if (ptype == MDSS_MDP_PIPE_TYPE_INVALID) {
@@ -411,7 +422,7 @@ static int __validate_single_layer(struct msm_fb_data_type *mfd,
if (ret)
goto exit_fail;
- ret = __layer_param_check(mfd, layer, fmt);
+ ret = __layer_param_check(mfd, layer, fmt, layer_info->multirect.num);
if (ret)
goto exit_fail;
@@ -449,7 +460,7 @@ exit_fail:
}
static int __configure_pipe_params(struct msm_fb_data_type *mfd,
- struct mdp_input_layer *layer, struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_validate_info_t *vinfo, struct mdss_mdp_pipe *pipe,
struct mdss_mdp_pipe *left_blend_pipe, bool is_single_layer,
u32 mixer_mux)
{
@@ -460,6 +471,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdp_input_layer *layer = vinfo->layer;
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, mixer_mux);
pipe->src_fmt = mdss_mdp_get_format_params(layer->buffer.format);
@@ -603,6 +615,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
}
}
+ pipe->multirect.mode = vinfo->multirect.mode;
pipe->mixer_stage = layer->z_order;
if (mfd->panel_orientation & MDP_FLIP_LR)
@@ -845,7 +858,8 @@ sync_fence_err:
* buffer mapping right now. This is case for all formats including UBWC.
*/
static struct mdss_mdp_data *__map_layer_buffer(struct msm_fb_data_type *mfd,
- struct mdss_mdp_pipe *pipe, struct mdp_input_layer *layer_list,
+ struct mdss_mdp_pipe *pipe,
+ struct mdss_mdp_validate_info_t *validate_info_list,
u32 layer_count)
{
struct mdss_mdp_data *src_data;
@@ -854,10 +868,13 @@ static struct mdss_mdp_data *__map_layer_buffer(struct msm_fb_data_type *mfd,
struct msmfb_data image;
int i, ret;
u32 flags;
+ struct mdss_mdp_validate_info_t *vitem;
for (i = 0; i < layer_count; i++) {
- layer = &layer_list[i];
- if (layer->pipe_ndx == pipe->ndx)
+ vitem = &validate_info_list[i];
+ layer = vitem->layer;
+ if ((layer->pipe_ndx == pipe->ndx) &&
+ (vitem->multirect.num == pipe->multirect.num))
break;
}
@@ -948,15 +965,17 @@ static inline bool __compare_layer_config(struct mdp_input_layer *validate,
* compares all input layer params except buffer handle, offset, fences.
*/
static struct mdss_mdp_pipe *__find_layer_in_validate_q(
- struct mdp_input_layer *layer,
+ struct mdss_mdp_validate_info_t *vinfo,
struct mdss_overlay_private *mdp5_data)
{
bool found = false;
- struct mdss_mdp_pipe *pipe, *tmp;
+ struct mdss_mdp_pipe *pipe;
+ struct mdp_input_layer *layer = vinfo->layer;
mutex_lock(&mdp5_data->list_lock);
- list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
- if (pipe->ndx == layer->pipe_ndx) {
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
+ if ((pipe->ndx == layer->pipe_ndx) &&
+ (pipe->multirect.num == vinfo->multirect.num)) {
if (__compare_layer_config(layer, pipe))
found = true;
break;
@@ -968,12 +987,14 @@ static struct mdss_mdp_pipe *__find_layer_in_validate_q(
}
static bool __find_pipe_in_list(struct list_head *head,
- int pipe_ndx, struct mdss_mdp_pipe **out_pipe)
+ int pipe_ndx, struct mdss_mdp_pipe **out_pipe,
+ enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe;
list_for_each_entry(pipe, head, list) {
- if (pipe_ndx == pipe->ndx) {
+ if ((pipe_ndx == pipe->ndx) &&
+ (rect_num == pipe->multirect.num)) {
*out_pipe = pipe;
return true;
}
@@ -987,18 +1008,20 @@ static bool __find_pipe_in_list(struct list_head *head,
* It is caller responsibility to hold the list lock before calling this API.
*/
static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe(
- struct mdss_overlay_private *mdp5_data, u32 pipe_ndx)
+ struct mdss_overlay_private *mdp5_data, u32 pipe_ndx,
+ enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
- if (__find_pipe_in_list(&mdp5_data->pipes_destroy, pipe_ndx, &pipe)) {
- pr_debug("reuse destroy pipe id:%d ndx:%d\n", pipe->num,
- pipe_ndx);
+ if (__find_pipe_in_list(&mdp5_data->pipes_destroy,
+ pipe_ndx, &pipe, rect_num)) {
+ pr_debug("reuse destroy pipe id:%d ndx:%d rect:%d\n",
+ pipe->num, pipe_ndx, rect_num);
list_move(&pipe->list, &mdp5_data->pipes_used);
- } else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup, pipe_ndx,
- &pipe)) {
- pr_debug("reuse cleanup pipe id:%d ndx:%d\n", pipe->num,
- pipe_ndx);
+ } else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup,
+ pipe_ndx, &pipe, rect_num)) {
+ pr_debug("reuse cleanup pipe id:%d ndx:%d rect:%d\n",
+ pipe->num, pipe_ndx, rect_num);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;
@@ -1020,16 +1043,18 @@ static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe(
static struct mdss_mdp_pipe *__assign_pipe_for_layer(
struct msm_fb_data_type *mfd,
struct mdss_mdp_mixer *mixer, u32 pipe_ndx,
- enum layer_pipe_q *pipe_q_type)
+ enum layer_pipe_q *pipe_q_type,
+ enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
mutex_lock(&mdp5_data->list_lock);
- __find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe);
+ __find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe, rect_num);
if (IS_ERR_OR_NULL(pipe)) {
- pipe = __find_and_move_cleanup_pipe(mdp5_data, pipe_ndx);
+ pipe = __find_and_move_cleanup_pipe(mdp5_data,
+ pipe_ndx, rect_num);
if (IS_ERR_OR_NULL(pipe))
*pipe_q_type = LAYER_USES_NEW_PIPE_Q;
else
@@ -1059,10 +1084,10 @@ static struct mdss_mdp_pipe *__assign_pipe_for_layer(
goto end;
}
- pipe = mdss_mdp_pipe_assign(mdata, mixer, pipe_ndx);
+ pipe = mdss_mdp_pipe_assign(mdata, mixer, pipe_ndx, rect_num);
if (IS_ERR_OR_NULL(pipe)) {
- pr_err("error reserving pipe. pipe_ndx=0x%x mfd ndx=%d\n",
- pipe_ndx, mfd->index);
+ pr_err("error reserving pipe. pipe_ndx=0x%x rect_num=%d mfd ndx=%d\n",
+ pipe_ndx, rect_num, mfd->index);
goto end;
}
@@ -1124,18 +1149,21 @@ static int __validate_secure_display(struct mdss_overlay_private *mdp5_data)
* Moving pipes before can affects staged pipe for previous cycle.
*/
static void __handle_free_list(struct mdss_overlay_private *mdp5_data,
- struct mdp_input_layer *layer_list, u32 layer_count)
+ struct mdss_mdp_validate_info_t *validate_info_list, u32 layer_count)
{
int i;
-
struct mdp_input_layer *layer;
+ struct mdss_mdp_validate_info_t *vinfo;
struct mdss_mdp_pipe *pipe, *tmp;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
for (i = 0; i < layer_count; i++) {
- layer = &layer_list[i];
- if (pipe->ndx == layer->pipe_ndx)
+ vinfo = &validate_info_list[i];
+ layer = vinfo->layer;
+
+ if ((pipe->ndx == layer->pipe_ndx) &&
+ (pipe->multirect.num == vinfo->multirect.num))
break;
}
@@ -1150,6 +1178,404 @@ static void __handle_free_list(struct mdss_overlay_private *mdp5_data,
mutex_unlock(&mdp5_data->list_lock);
}
+static bool __multirect_validate_flip(struct mdp_input_layer **layers,
+ size_t count)
+{
+ /* not supporting more than 2 layers */
+ if (count != 2)
+ return false;
+
+ /* flip related validation */
+ if ((layers[0]->flags & MDP_LAYER_FLIP_LR) ||
+ (layers[1]->flags & MDP_LAYER_FLIP_LR)) {
+ pr_err("multirect and HFLIP is not allowed. input layer flags=0x%x paired layer flags=0x%x\n",
+ layers[0]->flags, layers[1]->flags);
+ return false;
+ }
+ if ((layers[0]->flags & MDP_LAYER_FLIP_UD) !=
+ (layers[1]->flags & MDP_LAYER_FLIP_UD)) {
+ pr_err("multirect VLFIP mismatch is not allowed\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool __multirect_validate_format(struct mdp_input_layer **layers,
+ size_t count)
+{
+ struct mdss_mdp_format_params *rec0_fmt, *rec1_fmt;
+ bool is_ubwc;
+
+ /* not supporting more than 2 layers */
+ if (count != 2)
+ return false;
+
+ /* format related validation */
+ rec0_fmt = mdss_mdp_get_format_params(layers[0]->buffer.format);
+ if (!rec0_fmt) {
+ pr_err("invalid input layer format %d\n",
+ layers[0]->buffer.format);
+ return false;
+ }
+ rec1_fmt = mdss_mdp_get_format_params(layers[1]->buffer.format);
+ if (!rec1_fmt) {
+ pr_err("invalid paired layer format %d\n",
+ layers[1]->buffer.format);
+ return false;
+ }
+ if (rec0_fmt->is_yuv || rec1_fmt->is_yuv) {
+ pr_err("multirect on YUV format is not supported. input=%d paired=%d\n",
+ rec0_fmt->is_yuv, rec1_fmt->is_yuv);
+ return false;
+ }
+ if (rec0_fmt->fetch_mode != rec1_fmt->fetch_mode) {
+ pr_err("multirect fetch_mode mismatch is not allowed. input=%d paired=%d\n",
+ rec0_fmt->fetch_mode, rec1_fmt->fetch_mode);
+ return false;
+ }
+ is_ubwc = mdss_mdp_is_ubwc_format(rec0_fmt);
+ if (is_ubwc && (rec0_fmt != rec1_fmt)) {
+ pr_err("multirect UBWC format mismatch is not allowed\n");
+ return false;
+ } else if (rec0_fmt->bpp != rec1_fmt->bpp) {
+ pr_err("multirect linear format bpp mismatch is not allowed. input=%d paired=%d\n",
+ rec0_fmt->bpp, rec1_fmt->bpp);
+ return false;
+ } else if (rec0_fmt->unpack_dx_format != rec1_fmt->unpack_dx_format) {
+ pr_err("multirect linear format 10bit vs 8bit mismatch is not allowed. input=%d paired=%d\n",
+ rec0_fmt->unpack_dx_format, rec1_fmt->unpack_dx_format);
+ return false;
+ }
+
+ if ((layers[0]->flags & MDP_LAYER_SOLID_FILL) !=
+ (layers[1]->flags & MDP_LAYER_SOLID_FILL)) {
+ pr_err("solid fill mismatch between multirect layers\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool __multirect_validate_rects(struct mdp_input_layer **layers,
+ size_t count)
+{
+ struct mdss_rect dst[MDSS_MDP_PIPE_MAX_RECTS];
+ int i;
+
+ /* not supporting more than 2 layers */
+ if (count != 2)
+ return false;
+
+ for (i = 0; i < count; i++) {
+ if ((layers[i]->src_rect.w != layers[i]->dst_rect.w) ||
+ (layers[i]->src_rect.h != layers[i]->dst_rect.h)) {
+ pr_err("multirect layers cannot have scaling: src: %dx%d dst: %dx%d\n",
+ layers[i]->src_rect.w, layers[i]->src_rect.h,
+ layers[i]->dst_rect.w, layers[i]->dst_rect.h);
+ return false;
+ }
+
+ dst[i] = (struct mdss_rect) {layers[i]->dst_rect.x,
+ layers[i]->dst_rect.y,
+ layers[i]->dst_rect.w,
+ layers[i]->dst_rect.h};
+ }
+
+ /* resolution related validation */
+ if (mdss_rect_overlap_check(&dst[0], &dst[1])) {
+ pr_err("multirect dst overlap is not allowed. input: %d,%d,%d,%d paired %d,%d,%d,%d\n",
+ dst[0].x, dst[0].y, dst[0].w, dst[0].y,
+ dst[1].x, dst[1].y, dst[1].w, dst[1].y);
+ return false;
+ }
+
+ return true;
+}
+
+static bool __multirect_validate_properties(struct mdp_input_layer **layers,
+ size_t count)
+{
+ /* not supporting more than 2 layers */
+ if (count != 2)
+ return false;
+
+ if ((layers[0]->flags & MDP_LAYER_ASYNC) ||
+ (layers[1]->flags & MDP_LAYER_ASYNC)) {
+ pr_err("ASYNC update is not allowed with multirect\n");
+ return false;
+ }
+
+ if (layers[0]->z_order == layers[1]->z_order) {
+ pr_err("multirect layers cannot have same z_order=%d\n",
+ layers[0]->z_order);
+ return false;
+ }
+
+ return true;
+}
+
+static bool (*__multirect_validators[])(struct mdp_input_layer **layers,
+ size_t count) = {
+ __multirect_validate_flip,
+ __multirect_validate_format,
+ __multirect_validate_rects,
+ __multirect_validate_properties,
+};
+
+static inline int __multirect_layer_flags_to_mode(u32 flags)
+{
+ int mode;
+
+ if (flags & MDP_LAYER_MULTIRECT_ENABLE) {
+ if (flags & MDP_LAYER_MULTIRECT_PARALLEL_MODE)
+ mode = MDSS_MDP_PIPE_MULTIRECT_PARALLEL;
+ else
+ mode = MDSS_MDP_PIPE_MULTIRECT_SERIAL;
+ } else {
+ if (flags & MDP_LAYER_MULTIRECT_PARALLEL_MODE) {
+ pr_err("Invalid parallel mode flag set without multirect enabled\n");
+ return -EINVAL;
+ }
+
+ mode = MDSS_MDP_PIPE_MULTIRECT_NONE;
+ }
+ return mode;
+}
+
+static int __multirect_validate_mode(struct msm_fb_data_type *mfd,
+ struct mdp_input_layer **layers,
+ size_t count)
+{
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ struct mdss_mdp_format_params *rec0_fmt;
+ bool is_ubwc;
+ int i, mode;
+ struct mdp_rect *dst[MDSS_MDP_PIPE_MAX_RECTS];
+
+ /* not supporting more than 2 layers */
+ if (count != 2)
+ return false;
+
+ for (i = 0; i < count; i++)
+ dst[i] = &layers[i]->dst_rect;
+
+ mode = __multirect_layer_flags_to_mode(layers[0]->flags);
+
+ /* format related validation */
+ rec0_fmt = mdss_mdp_get_format_params(layers[0]->buffer.format);
+ if (!rec0_fmt) {
+ pr_err("invalid input layer format %d\n",
+ layers[0]->buffer.format);
+ return false;
+ }
+
+ is_ubwc = mdss_mdp_is_ubwc_format(rec0_fmt);
+
+ if (mode == MDSS_MDP_PIPE_MULTIRECT_SERIAL) {
+ int threshold, yoffset;
+
+ if (dst[0]->y < dst[1]->y)
+ yoffset = dst[1]->y - (dst[0]->y + dst[0]->h);
+ else if (dst[1]->y < dst[0]->y)
+ yoffset = dst[0]->y - (dst[1]->y + dst[1]->h);
+ else
+ yoffset = 0;
+
+ /*
+ * time multiplexed is possible only if the y position of layers
+ * is not overlapping and there is sufficient time to buffer
+ * 2 lines/tiles. Otherwise use parallel fetch mode
+ */
+ threshold = 2;
+ if (is_ubwc) {
+ struct mdss_mdp_format_params_ubwc *uf;
+
+ /* in ubwc all layers would need to be same format */
+ uf = (struct mdss_mdp_format_params_ubwc *)rec0_fmt;
+ threshold *= uf->micro.tile_height;
+ }
+
+ if (yoffset < threshold) {
+ pr_err("Unable to operate in serial fetch mode with yoffset=%d dst[0]=%d,%d dst[1]=%d,%d\n",
+ yoffset, dst[0]->y, dst[0]->h,
+ dst[1]->y, dst[1]->h);
+ return -EINVAL;
+ }
+ } else if (mode == MDSS_MDP_PIPE_MULTIRECT_PARALLEL) {
+ u32 left_lm_w, rec0_mixer, rec1_mixer;
+
+ /*
+ * For UBWC, 5 lines worth of buffering is needed in to meet
+ * the performance which requires 2560w*4bpp*5lines = 50KB,
+ * where 2560 is max width. Now let's say pixel ram is fixed to
+ * 50KB then in UBWC parellel fetch, maximum width of each
+ * rectangle would be 2560/2 = 1280.
+ *
+ * For Linear, this restriction is avoided because maximum
+ * buffering of 2 lines is enough which yields to
+ * 2560w*4bpp*2lines=20KB. Based on this, we can have 2 max
+ * width rectangles in parrellel fetch mode.
+ */
+ if (is_ubwc &&
+ ((dst[0]->w > (mdata->max_mixer_width / 2)) ||
+ (dst[1]->w > (mdata->max_mixer_width / 2)))) {
+ pr_err("in UBWC multirect parallel mode, max dst_w cannot be greater than %d. rec0_w=%d rec1_w=%d\n",
+ mdata->max_mixer_width / 2,
+ dst[0]->w, dst[1]->w);
+ return -EINVAL;
+ }
+
+ left_lm_w = left_lm_w_from_mfd(mfd);
+ if (dst[0]->x < left_lm_w) {
+ if (dst[0]->w > (left_lm_w - dst[0]->x)) {
+ pr_err("multirect parallel mode, rec0 dst (%d,%d) cannot cross lm boundary (%d)\n",
+ dst[0]->x, dst[0]->w, left_lm_w);
+ return -EINVAL;
+ }
+ rec0_mixer = MDSS_MDP_MIXER_MUX_LEFT;
+ } else {
+ rec0_mixer = MDSS_MDP_MIXER_MUX_RIGHT;
+ }
+
+ if (dst[1]->x < left_lm_w) {
+ if (dst[0]->w > (left_lm_w - dst[0]->x)) {
+ pr_err("multirect parallel mode, rec1 dst (%d,%d) cannot cross lm boundary (%d)\n",
+ dst[1]->x, dst[1]->w, left_lm_w);
+ return -EINVAL;
+ }
+ rec1_mixer = MDSS_MDP_MIXER_MUX_LEFT;
+ } else {
+ rec1_mixer = MDSS_MDP_MIXER_MUX_RIGHT;
+ }
+
+ if (rec0_mixer != rec1_mixer) {
+ pr_err("multirect parallel mode mixer mismatch. rec0_mix=%d rec1_mix=%d\n",
+ rec0_mixer, rec1_mixer);
+ return -EINVAL;
+ }
+ } else {
+ pr_err("Invalid multirect mode %d\n", mode);
+ }
+
+ pr_debug("layer->pndx:%d mode=%d\n", layers[0]->pipe_ndx, mode);
+
+ return 0;
+}
+
+static int __update_multirect_info(struct msm_fb_data_type *mfd,
+ struct mdss_mdp_validate_info_t *validate_info_list,
+ struct mdp_input_layer *layer_list, int ndx, int layer_cnt)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ struct mdss_mdp_validate_info_t *vinfo[MDSS_MDP_PIPE_MAX_RECTS];
+ int i, ptype, max_rects, mode;
+ int cnt = 1;
+
+ mode = __multirect_layer_flags_to_mode(layer_list[ndx].flags);
+ if (IS_ERR_VALUE(mode))
+ return mode;
+
+ pr_debug("layer #%d pipe_ndx=%d multirect mode=%d\n",
+ ndx, layer_list[ndx].pipe_ndx, mode);
+
+ vinfo[0] = &validate_info_list[ndx];
+ vinfo[0]->layer = &layer_list[ndx];
+ vinfo[0]->multirect.mode = mode;
+ vinfo[0]->multirect.num = MDSS_MDP_PIPE_RECT0;
+ vinfo[0]->multirect.next = NULL;
+
+ /* nothing to be done if multirect is disabled */
+ if (mode == MDSS_MDP_PIPE_MULTIRECT_NONE)
+ return cnt;
+
+ ptype = get_pipe_type_from_ndx(layer_list[ndx].pipe_ndx);
+ if (ptype == MDSS_MDP_PIPE_TYPE_INVALID) {
+ pr_err("invalid pipe ndx %d\n", layer_list[ndx].pipe_ndx);
+ return -EINVAL;
+ }
+
+ max_rects = mdata->rects_per_sspp[ptype] ? : 1;
+
+ for (i = ndx + 1; i < layer_cnt; i++) {
+ if (layer_list[ndx].pipe_ndx == layer_list[i].pipe_ndx) {
+ if (cnt >= max_rects) {
+ pr_err("more than %d layers of type %d with same pipe_ndx=%d indexes=%d %d\n",
+ max_rects, ptype,
+ layer_list[ndx].pipe_ndx, ndx, i);
+ return -EINVAL;
+ }
+
+ mode = __multirect_layer_flags_to_mode(
+ layer_list[i].flags);
+ if (IS_ERR_VALUE(mode))
+ return mode;
+
+ if (mode != vinfo[0]->multirect.mode) {
+ pr_err("unable to set different multirect modes for pipe_ndx=%d (%d %d)\n",
+ layer_list[ndx].pipe_ndx, ndx, i);
+ return -EINVAL;
+ }
+
+ pr_debug("found matching pair for pipe_ndx=%d (%d %d)\n",
+ layer_list[i].pipe_ndx, ndx, i);
+
+ vinfo[cnt] = &validate_info_list[i];
+ vinfo[cnt]->multirect.num = cnt;
+ vinfo[cnt]->multirect.next = vinfo[0]->layer;
+ vinfo[cnt]->multirect.mode = mode;
+ vinfo[cnt]->layer = &layer_list[i];
+
+ vinfo[cnt - 1]->multirect.next = vinfo[cnt]->layer;
+ cnt++;
+ }
+ }
+
+ if (cnt == 1) {
+ pr_err("multirect mode enabled but unable to find extra rects for pipe_ndx=%x\n",
+ layer_list[ndx].pipe_ndx);
+ return -EINVAL;
+ }
+
+ return cnt;
+}
+
+static int __validate_multirect(struct msm_fb_data_type *mfd,
+ struct mdss_mdp_validate_info_t *validate_info_list,
+ struct mdp_input_layer *layer_list, int ndx, int layer_cnt)
+{
+ struct mdp_input_layer *layers[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
+ int i, cnt, rc;
+
+ cnt = __update_multirect_info(mfd, validate_info_list,
+ layer_list, ndx, layer_cnt);
+ if (IS_ERR_VALUE(cnt))
+ return cnt;
+
+ if (cnt <= 1) {
+ /* nothing to validate in single rect mode */
+ return 0;
+ } else if (cnt > 2) {
+ pr_err("unsupported multirect configuration, multirect cnt=%d\n",
+ cnt);
+ return -EINVAL;
+ }
+
+ layers[0] = validate_info_list[ndx].layer;
+ layers[1] = validate_info_list[ndx].multirect.next;
+
+ for (i = 0; i < ARRAY_SIZE(__multirect_validators); i++) {
+ if (!__multirect_validators[i](layers, cnt))
+ return -EINVAL;
+ }
+
+ rc = __multirect_validate_mode(mfd, layers, cnt);
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return 0;
+}
+
/*
* __validate_layers() - validate input layers
* @mfd: Framebuffer data structure for display
@@ -1162,7 +1588,10 @@ static void __handle_free_list(struct mdss_overlay_private *mdp5_data,
static int __validate_layers(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
- int ret, i, release_ndx = 0, inputndx = 0, destroy_ndx = 0;
+ int ret, i;
+ int rec_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
+ int rec_release_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
+ int rec_destroy_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
u32 left_lm_layers = 0, right_lm_layers = 0;
u32 left_cnt = 0, right_cnt = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
@@ -1176,16 +1605,28 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
struct mdss_mdp_mixer *mixer = NULL;
struct mdp_input_layer *layer, *prev_layer, *layer_list;
+ struct mdss_mdp_validate_info_t *validate_info_list = NULL;
bool is_single_layer = false;
enum layer_pipe_q pipe_q_type;
enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
+ enum mdss_mdp_pipe_rect rect_num;
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
return ret;
+ if (!layer_count)
+ goto validate_skip;
+
layer_list = commit->input_layers;
+ validate_info_list = kcalloc(layer_count, sizeof(*validate_info_list),
+ GFP_KERNEL);
+ if (!validate_info_list) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
for (i = 0; i < layer_count; i++) {
if (layer_list[i].dst_rect.x >= left_lm_w)
right_lm_layers++;
@@ -1193,13 +1634,35 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
left_lm_layers++;
if (right_lm_layers >= MAX_PIPES_PER_LM ||
- left_lm_layers >= MAX_PIPES_PER_LM) {
+ left_lm_layers >= MAX_PIPES_PER_LM) {
pr_err("too many pipes stagged mixer left: %d mixer right:%d\n",
left_lm_layers, right_lm_layers);
ret = -EINVAL;
goto end;
}
- inputndx |= layer_list[i].pipe_ndx;
+
+ if (!validate_info_list[i].layer) {
+ ret = __validate_multirect(mfd, validate_info_list,
+ layer_list, i, layer_count);
+ if (ret) {
+ pr_err("error validating multirect config. ret=%d i=%d\n",
+ ret, i);
+ goto end;
+ }
+ }
+
+ rect_num = validate_info_list[i].multirect.num;
+ BUG_ON(rect_num >= MDSS_MDP_PIPE_MAX_RECTS);
+
+ if (rec_ndx[rect_num] & layer_list[i].pipe_ndx) {
+ pr_err("duplicate layer found pipe_ndx=%d rect=%d (0x%x)\n",
+ layer_list[i].pipe_ndx, rect_num,
+ rec_ndx[rect_num]);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ rec_ndx[rect_num] |= layer_list[i].pipe_ndx;
}
for (i = 0; i < layer_count; i++) {
@@ -1253,7 +1716,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
left_blend_pipe = NULL;
}
- if (__layer_needs_src_split(layer))
+ if (!is_split_lm(mfd) || __layer_needs_src_split(layer))
z = LAYER_ZORDER_BOTH;
else if (dst_x >= left_lm_w)
z = LAYER_ZORDER_RIGHT;
@@ -1287,7 +1750,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
* are same. validation can be skipped if only buffer handle
* is changed.
*/
- pipe = __find_layer_in_validate_q(layer, mdp5_data);
+ pipe = __find_layer_in_validate_q(&validate_info_list[i],
+ mdp5_data);
if (pipe) {
if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT)
right_plist[right_cnt++] = pipe;
@@ -1318,15 +1782,18 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
}
layer->z_order += MDSS_MDP_STAGE_0;
- ret = __validate_single_layer(mfd, layer, mixer_mux);
+ ret = __validate_single_layer(mfd, &validate_info_list[i],
+ mixer_mux);
if (ret) {
pr_err("layer:%d validation failed ret=%d\n", i, ret);
layer->error_code = ret;
goto validate_exit;
}
+ rect_num = validate_info_list[i].multirect.num;
+
pipe = __assign_pipe_for_layer(mfd, mixer, layer->pipe_ndx,
- &pipe_q_type);
+ &pipe_q_type, rect_num);
if (IS_ERR_OR_NULL(pipe)) {
pr_err("error assigning pipe id=0x%x rc:%ld\n",
layer->pipe_ndx, PTR_ERR(pipe));
@@ -1336,9 +1803,9 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
}
if (pipe_q_type == LAYER_USES_NEW_PIPE_Q)
- release_ndx |= pipe->ndx;
+ rec_release_ndx[rect_num] |= pipe->ndx;
if (pipe_q_type == LAYER_USES_DESTROY_PIPE_Q)
- destroy_ndx |= pipe->ndx;
+ rec_destroy_ndx[rect_num] |= pipe->ndx;
ret = mdss_mdp_pipe_map(pipe);
if (IS_ERR_VALUE(ret)) {
@@ -1363,7 +1830,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
}
}
- ret = __configure_pipe_params(mfd, layer, pipe,
+ ret = __configure_pipe_params(mfd, &validate_info_list[i], pipe,
left_blend_pipe, is_single_layer, mixer_mux);
if (ret) {
pr_err("configure pipe param failed: pipe index= %d\n",
@@ -1395,27 +1862,33 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
goto validate_exit;
}
- __handle_free_list(mdp5_data, layer_list, layer_count);
+validate_skip:
+ __handle_free_list(mdp5_data, validate_info_list, layer_count);
ret = __validate_secure_display(mdp5_data);
validate_exit:
- pr_debug("err=%d total_layer:%d left:%d right:%d release_ndx=0x%x destroy_ndx=0x%x processed=%d\n",
+ pr_debug("err=%d total_layer:%d left:%d right:%d rec0_rel_ndx=0x%x rec1_rel_ndx=0x%x rec0_destroy_ndx=0x%x rec1_destroy_ndx=0x%x processed=%d\n",
ret, layer_count, left_lm_layers, right_lm_layers,
- release_ndx, destroy_ndx, i);
- MDSS_XLOG(inputndx, layer_count, left_lm_layers, right_lm_layers,
- release_ndx, destroy_ndx, ret);
+ rec_release_ndx[0], rec_release_ndx[1],
+ rec_destroy_ndx[0], rec_destroy_ndx[1], i);
+ MDSS_XLOG(rec_ndx[0], rec_ndx[1], layer_count,
+ left_lm_layers, right_lm_layers,
+ rec_release_ndx[0], rec_release_ndx[1],
+ rec_destroy_ndx[0], rec_destroy_ndx[1], ret);
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
if (IS_ERR_VALUE(ret)) {
- if (pipe->ndx & release_ndx) {
+ if ((pipe->ndx & rec_release_ndx[0]) ||
+ (pipe->ndx & rec_release_ndx[1])) {
mdss_mdp_smp_unreserve(pipe);
pipe->params_changed = 0;
pipe->dirty = true;
if (!list_empty(&pipe->list))
list_del_init(&pipe->list);
mdss_mdp_pipe_destroy(pipe);
- } else if (pipe->ndx & destroy_ndx) {
+ } else if ((pipe->ndx & rec_destroy_ndx[0]) ||
+ (pipe->ndx & rec_destroy_ndx[1])) {
/*
* cleanup/destroy list pipes should move back
* to destroy list. Next/current kickoff cycle
@@ -1433,6 +1906,7 @@ validate_exit:
}
mutex_unlock(&mdp5_data->list_lock);
end:
+ kfree(validate_info_list);
mutex_unlock(&mdp5_data->ov_lock);
pr_debug("fb%d validated layers =%d\n", mfd->index, i);
@@ -1460,9 +1934,10 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
bool validate_failed = false;
struct mdss_mdp_pipe *pipe, *tmp;
- struct mdp_input_layer *layer_list, *layer;
+ struct mdp_input_layer *layer_list;
struct mdss_overlay_private *mdp5_data;
struct mdss_mdp_data *src_data[MDSS_MDP_MAX_SSPP];
+ struct mdss_mdp_validate_info_t *validate_info_list;
mdp5_data = mfd_to_mdp5_data(mfd);
@@ -1477,9 +1952,26 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
return 0;
}
+ validate_info_list = kcalloc(layer_count, sizeof(*validate_info_list),
+ GFP_KERNEL);
+ if (!validate_info_list)
+ return -ENOMEM;
+
+ for (i = 0; i < layer_count; i++) {
+ if (!validate_info_list[i].layer) {
+ ret = __update_multirect_info(mfd, validate_info_list,
+ layer_list, i, layer_count);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("error updating multirect config. ret=%d i=%d\n",
+ ret, i);
+ goto end;
+ }
+ }
+ }
+
for (i = 0; i < layer_count; i++) {
- layer = &layer_list[i];
- pipe = __find_layer_in_validate_q(layer, mdp5_data);
+ pipe = __find_layer_in_validate_q(&validate_info_list[i],
+ mdp5_data);
if (!pipe) {
validate_failed = true;
break;
@@ -1488,14 +1980,16 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
if (validate_failed) {
ret = __validate_layers(mfd, file, commit);
- if (ret)
+ if (ret) {
+ pr_err("__validate_layers failed. rc=%d\n", ret);
goto end;
+ }
} else {
/*
* move unassigned pipes to cleanup list since commit
* supports validate+commit operation.
*/
- __handle_free_list(mdp5_data, layer_list, layer_count);
+ __handle_free_list(mdp5_data, validate_info_list, layer_count);
}
i = 0;
@@ -1506,7 +2000,7 @@ int mdss_mdp_layer_pre_commit(struct msm_fb_data_type *mfd,
src_data[i] = NULL;
continue;
}
- src_data[i] = __map_layer_buffer(mfd, pipe, layer_list,
+ src_data[i] = __map_layer_buffer(mfd, pipe, validate_info_list,
layer_count);
if (IS_ERR_OR_NULL(src_data[i++])) {
i--;
@@ -1534,6 +2028,7 @@ map_err:
mutex_unlock(&mdp5_data->list_lock);
}
end:
+ kfree(validate_info_list);
return ret;
}
@@ -1620,7 +2115,7 @@ int mdss_mdp_layer_pre_commit_wfd(struct msm_fb_data_type *mfd,
rc = mdss_mdp_layer_pre_commit(mfd, file, commit);
if (rc) {
- pr_err("fail to import input layer buffers\n");
+ pr_err("fail to import input layer buffers. rc=%d\n", rc);
goto input_layer_err;
}
@@ -1707,7 +2202,7 @@ int mdss_mdp_async_position_update(struct msm_fb_data_type *mfd,
layer = &update_pos->input_layers[i];
mutex_lock(&mdp5_data->list_lock);
__find_pipe_in_list(&mdp5_data->pipes_used, layer->pipe_ndx,
- &pipe);
+ &pipe, MDSS_MDP_PIPE_RECT0);
mutex_unlock(&mdp5_data->list_lock);
if (!pipe) {
pr_err("invalid pipe ndx=0x%x for async update\n",
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 69dc5fe06898..dafd9f203ab0 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -1211,12 +1211,37 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd,
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
bool recovery_mode = false;
+ bool skip_fetch_halt, pair_found;
struct mdss_mdp_data *buf, *tmpbuf;
mutex_lock(&mdp5_data->list_lock);
list_for_each_entry(pipe, destroy_pipes, list) {
+ pair_found = false;
+ skip_fetch_halt = false;
+ tmp = pipe;
+
+ /*
+ * Find if second rect is in the destroy list from the current
+ * position. So if both rects are part of the destroy list then
+ * fetch halt will be skipped for the 1st rect.
+ */
+ list_for_each_entry_from(tmp, destroy_pipes, list) {
+ if (tmp->num == pipe->num) {
+ pair_found = true;
+ break;
+ }
+ }
+
+ /* skip fetch halt if pipe's other rect is still in use */
+ if (!pair_found) {
+ tmp = (struct mdss_mdp_pipe *)pipe->multirect.next;
+ if (tmp)
+ skip_fetch_halt =
+ atomic_read(&tmp->kref.refcount);
+ }
+
/* make sure pipe fetch has been halted before freeing buffer */
- if (mdss_mdp_pipe_fetch_halt(pipe, false)) {
+ if (!skip_fetch_halt && mdss_mdp_pipe_fetch_halt(pipe, false)) {
/*
* if pipe is not able to halt. Enter recovery mode,
* by un-staging any pipes that are attached to mixer
@@ -1255,9 +1280,16 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd,
pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;
}
__overlay_pipe_cleanup(mfd, pipe);
- ctl->mixer_left->next_pipe_map &= ~pipe->ndx;
- if (ctl->mixer_right)
- ctl->mixer_right->next_pipe_map &= ~pipe->ndx;
+
+ if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
+ /*
+ * track only RECT0, since at any given point there
+ * can only be RECT0 only or RECT0 + RECT1
+ */
+ ctl->mixer_left->next_pipe_map &= ~pipe->ndx;
+ if (ctl->mixer_right)
+ ctl->mixer_right->next_pipe_map &= ~pipe->ndx;
+ }
}
mutex_unlock(&mdp5_data->list_lock);
}
@@ -1266,22 +1298,21 @@ void mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
u32 type)
{
u32 i, npipes;
- struct mdss_mdp_pipe *pipes;
struct mdss_mdp_pipe *pipe;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
switch (type) {
case MDSS_MDP_PIPE_TYPE_VIG:
- pipes = mdata->vig_pipes;
+ pipe = mdata->vig_pipes;
npipes = mdata->nvig_pipes;
break;
case MDSS_MDP_PIPE_TYPE_RGB:
- pipes = mdata->rgb_pipes;
+ pipe = mdata->rgb_pipes;
npipes = mdata->nrgb_pipes;
break;
case MDSS_MDP_PIPE_TYPE_DMA:
- pipes = mdata->dma_pipes;
+ pipe = mdata->dma_pipes;
npipes = mdata->ndma_pipes;
break;
default:
@@ -1289,13 +1320,14 @@ void mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd,
}
for (i = 0; i < npipes; i++) {
- pipe = &pipes[i];
+ /* only check for first rect and ignore additional */
if (pipe->is_handed_off) {
pr_debug("Unmapping handed off pipe %d\n", pipe->num);
list_move(&pipe->list, &mdp5_data->pipes_cleanup);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
pipe->is_handed_off = false;
}
+ pipe += pipe->multirect.max_rects;
}
}
@@ -2156,7 +2188,7 @@ done:
static int __mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd,
struct file *file)
{
- struct mdss_mdp_pipe *pipe;
+ struct mdss_mdp_pipe *pipe, *tmp;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
u32 unset_ndx = 0;
int cnt = 0;
@@ -2166,28 +2198,25 @@ static int __mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd,
mutex_lock(&mdp5_data->ov_lock);
mutex_lock(&mdp5_data->list_lock);
- list_for_each_entry(pipe, &mdp5_data->pipes_used, list) {
- if (!file || pipe->file == file) {
- unset_ndx |= pipe->ndx;
- cnt++;
- }
- }
-
if (!mfd->ref_cnt && !list_empty(&mdp5_data->pipes_cleanup)) {
pr_debug("fb%d:: free pipes present in cleanup list",
mfd->index);
cnt++;
}
+ list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) {
+ if (!file || pipe->file == file) {
+ unset_ndx |= pipe->ndx;
+ pipe->file = NULL;
+ list_move(&pipe->list, &mdp5_data->pipes_cleanup);
+ cnt++;
+ }
+ }
+
pr_debug("mfd->ref_cnt=%d unset_ndx=0x%x cnt=%d\n",
mfd->ref_cnt, unset_ndx, cnt);
mutex_unlock(&mdp5_data->list_lock);
-
- if (unset_ndx) {
- pr_debug("%d pipes need cleanup (%x)\n", cnt, unset_ndx);
- mdss_mdp_overlay_release(mfd, unset_ndx);
- }
mutex_unlock(&mdp5_data->ov_lock);
return cnt;
@@ -5045,7 +5074,9 @@ static int __mdss_mdp_ctl_handoff(struct msm_fb_data_type *mfd,
}
if (mixercfg & (0x7 << cfg)) {
pr_debug("Pipe %d staged\n", j);
- pipe = mdss_mdp_pipe_search(mdata, BIT(j));
+ /* bootloader display always uses RECT0 */
+ pipe = mdss_mdp_pipe_search(mdata, BIT(j),
+ MDSS_MDP_PIPE_RECT0);
if (!pipe) {
pr_warn("Invalid pipe %d staged\n", j);
continue;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index 08db5424a178..39d5666db4bf 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -52,7 +52,8 @@ static void mdss_mdp_pipe_free(struct kref *kref);
static int mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp);
static void mdss_mdp_smp_mmb_free(unsigned long *smp, bool write);
static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id(
- struct mdss_data_type *mdata, int client_id);
+ struct mdss_data_type *mdata, int client_id,
+ enum mdss_mdp_pipe_rect rect_num);
static int mdss_mdp_calc_stride(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_plane_sizes *ps);
static u32 mdss_mdp_calc_per_plane_num_blks(u32 ystride,
@@ -65,6 +66,20 @@ static inline void mdss_mdp_pipe_write(struct mdss_mdp_pipe *pipe,
writel_relaxed(val, pipe->base + reg);
}
+/**
+ * This function is used to decide if certain register programming can be
+ * delayed or not. This is useful when multirect is used where two pipe
+ * structures access same set of registers.
+ */
+static inline bool is_pipe_programming_delay_needed(
+ struct mdss_mdp_pipe *pipe)
+{
+ struct mdss_mdp_pipe *next_pipe = pipe->multirect.next;
+
+ return (pipe->multirect.mode != MDSS_MDP_PIPE_MULTIRECT_NONE) &&
+ next_pipe->params_changed;
+}
+
static inline u32 mdss_mdp_pipe_read(struct mdss_mdp_pipe *pipe, u32 reg)
{
return readl_relaxed(pipe->base + reg);
@@ -139,7 +154,7 @@ static inline u32 get_qos_lut_macrotile(u32 total_fl)
return qos_lut;
}
-int mdss_mdp_pipe_qos_lut(struct mdss_mdp_pipe *pipe)
+static void mdss_mdp_pipe_qos_lut(struct mdss_mdp_pipe *pipe)
{
struct mdss_mdp_ctl *ctl = pipe->mixer_left->ctl;
u32 qos_lut;
@@ -170,8 +185,6 @@ int mdss_mdp_pipe_qos_lut(struct mdss_mdp_pipe *pipe)
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_CREQ_LUT,
qos_lut);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
-
- return 0;
}
bool is_rt_pipe(struct mdss_mdp_pipe *pipe)
@@ -217,7 +230,7 @@ static void mdss_mdp_config_pipe_panic_lut(struct mdss_mdp_pipe *pipe)
* This function assumes that clocks are enabled, so it is callers
* responsibility to enable clocks before calling this function.
*/
-int mdss_mdp_pipe_panic_vblank_signal_ctrl(struct mdss_mdp_pipe *pipe,
+static int mdss_mdp_pipe_panic_vblank_signal_ctrl(struct mdss_mdp_pipe *pipe,
bool enable)
{
uint32_t panic_ctrl;
@@ -901,7 +914,7 @@ int mdss_mdp_smp_handoff(struct mdss_data_type *mdata)
if (client_id) {
if (client_id != prev_id) {
pipe = mdss_mdp_pipe_search_by_client_id(mdata,
- client_id);
+ client_id, MDSS_MDP_PIPE_RECT0);
prev_id = client_id;
}
@@ -1028,6 +1041,19 @@ static void mdss_mdp_init_pipe_params(struct mdss_mdp_pipe *pipe)
kref_init(&pipe->kref);
init_waitqueue_head(&pipe->free_waitq);
INIT_LIST_HEAD(&pipe->buf_queue);
+
+ pipe->flags = 0;
+ pipe->is_right_blend = false;
+ pipe->src_split_req = false;
+ pipe->bwc_mode = 0;
+
+ pipe->mfd = NULL;
+ pipe->mixer_left = pipe->mixer_right = NULL;
+ pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;
+ memset(&pipe->scaler, 0, sizeof(struct mdp_scale_data));
+ memset(&pipe->layer, 0, sizeof(struct mdp_input_layer));
+
+ pipe->multirect.mode = MDSS_MDP_PIPE_MULTIRECT_NONE;
}
static int mdss_mdp_pipe_init_config(struct mdss_mdp_pipe *pipe,
@@ -1048,9 +1074,9 @@ static int mdss_mdp_pipe_init_config(struct mdss_mdp_pipe *pipe,
mdata = mixer->ctl->mdata;
if (pipe) {
- pr_debug("type=%x pnum=%d\n", pipe->type, pipe->num);
+ pr_debug("type=%x pnum=%d rect=%d\n",
+ pipe->type, pipe->num, pipe->multirect.num);
mdss_mdp_init_pipe_params(pipe);
-
} else if (pipe_share) {
/*
* when there is no dedicated wfd blk, DMA pipe can be
@@ -1153,30 +1179,6 @@ cursor_done:
return pipe;
}
-struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_dma(struct mdss_mdp_mixer *mixer)
-{
- struct mdss_mdp_pipe *pipe = NULL;
- struct mdss_data_type *mdata;
-
- mutex_lock(&mdss_mdp_sspp_lock);
- mdata = mixer->ctl->mdata;
- pipe = mdss_mdp_pipe_init(mixer, MDSS_MDP_PIPE_TYPE_DMA, mixer->num,
- NULL);
- if (IS_ERR_OR_NULL(pipe)) {
- pr_err("DMA pipes not available for mixer=%d\n", mixer->num);
- pipe = NULL;
- } else if (pipe != &mdata->dma_pipes[mixer->num]) {
- pr_err("Requested DMA pnum=%d not available\n",
- mdata->dma_pipes[mixer->num].num);
- kref_put(&pipe->kref, mdss_mdp_pipe_free);
- pipe = NULL;
- } else {
- pipe->mixer_left = mixer;
- }
- mutex_unlock(&mdss_mdp_sspp_lock);
- return pipe;
-}
-
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
u32 type, struct mdss_mdp_pipe *left_blend_pipe)
{
@@ -1187,16 +1189,18 @@ struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
return pipe;
}
-struct mdss_mdp_pipe *mdss_mdp_pipe_get(struct mdss_data_type *mdata, u32 ndx)
+struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx,
+ enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
if (!ndx)
return ERR_PTR(-EINVAL);
mutex_lock(&mdss_mdp_sspp_lock);
- pipe = mdss_mdp_pipe_search(mdata, ndx);
+ pipe = mdss_mdp_pipe_search(mdata, ndx, rect_num);
if (!pipe) {
pipe = ERR_PTR(-EINVAL);
goto error;
@@ -1211,7 +1215,7 @@ error:
}
struct mdss_mdp_pipe *mdss_mdp_pipe_assign(struct mdss_data_type *mdata,
- struct mdss_mdp_mixer *mixer, u32 ndx)
+ struct mdss_mdp_mixer *mixer, u32 ndx, enum mdss_mdp_pipe_rect rect_num)
{
struct mdss_mdp_pipe *pipe = NULL;
int rc;
@@ -1221,7 +1225,7 @@ struct mdss_mdp_pipe *mdss_mdp_pipe_assign(struct mdss_data_type *mdata,
return ERR_PTR(-EINVAL);
mutex_lock(&mdss_mdp_sspp_lock);
- pipe = mdss_mdp_pipe_search(mdata, ndx);
+ pipe = mdss_mdp_pipe_search(mdata, ndx, rect_num);
if (!pipe) {
pr_err("pipe search failed\n");
pipe = ERR_PTR(-EINVAL);
@@ -1263,58 +1267,78 @@ end:
return pipe;
}
-static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id(
- struct mdss_data_type *mdata, int client_id)
+static struct mdss_mdp_pipe *__pipe_lookup(struct mdss_mdp_pipe *pipe_list,
+ int count, enum mdss_mdp_pipe_rect rect_num,
+ bool (*cmp)(struct mdss_mdp_pipe *, void *), void *data)
{
- u32 i;
+ struct mdss_mdp_pipe *pipe;
+ int i, j;
- for (i = 0; i < mdata->nrgb_pipes; i++) {
- if (mdata->rgb_pipes[i].ftch_id == client_id)
- return &mdata->rgb_pipes[i];
+ for (i = 0, pipe = pipe_list; i < count; i++) {
+ for (j = 0; j < pipe->multirect.max_rects; j++, pipe++)
+ if ((rect_num == pipe->multirect.num) &&
+ cmp(pipe, data))
+ return pipe;
}
- for (i = 0; i < mdata->nvig_pipes; i++) {
- if (mdata->vig_pipes[i].ftch_id == client_id)
- return &mdata->vig_pipes[i];
- }
+ return NULL;
+}
- for (i = 0; i < mdata->ndma_pipes; i++) {
- if (mdata->dma_pipes[i].ftch_id == client_id)
- return &mdata->dma_pipes[i];
- }
+static struct mdss_mdp_pipe *mdss_mdp_pipe_lookup(
+ struct mdss_data_type *mdata, enum mdss_mdp_pipe_rect rect_num,
+ bool (*cmp)(struct mdss_mdp_pipe *, void *), void *data)
+{
+ struct mdss_mdp_pipe *pipe;
+
+ pipe = __pipe_lookup(mdata->vig_pipes, mdata->nvig_pipes,
+ rect_num, cmp, data);
+ if (pipe)
+ return pipe;
+
+ pipe = __pipe_lookup(mdata->rgb_pipes, mdata->nrgb_pipes,
+ rect_num, cmp, data);
+ if (pipe)
+ return pipe;
+
+ pipe = __pipe_lookup(mdata->dma_pipes, mdata->ndma_pipes,
+ rect_num, cmp, data);
+ if (pipe)
+ return pipe;
+
+ pipe = __pipe_lookup(mdata->cursor_pipes, mdata->ncursor_pipes,
+ rect_num, cmp, data);
+ if (pipe)
+ return pipe;
- for (i = 0; i < mdata->ncursor_pipes; i++) {
- if (mdata->cursor_pipes[i].ftch_id == client_id)
- return &mdata->cursor_pipes[i];
- }
return NULL;
}
-struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata,
- u32 ndx)
+static bool __pipe_cmp_fetch_id(struct mdss_mdp_pipe *pipe, void *data)
{
- u32 i;
- for (i = 0; i < mdata->nvig_pipes; i++) {
- if (mdata->vig_pipes[i].ndx == ndx)
- return &mdata->vig_pipes[i];
- }
+ u32 *fetch_id = data;
- for (i = 0; i < mdata->nrgb_pipes; i++) {
- if (mdata->rgb_pipes[i].ndx == ndx)
- return &mdata->rgb_pipes[i];
- }
+ return pipe->ftch_id == *fetch_id;
+}
- for (i = 0; i < mdata->ndma_pipes; i++) {
- if (mdata->dma_pipes[i].ndx == ndx)
- return &mdata->dma_pipes[i];
- }
+static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id(
+ struct mdss_data_type *mdata, int client_id,
+ enum mdss_mdp_pipe_rect rect_num)
+{
+ return mdss_mdp_pipe_lookup(mdata, rect_num,
+ __pipe_cmp_fetch_id, &client_id);
+}
- for (i = 0; i < mdata->ncursor_pipes; i++) {
- if (mdata->cursor_pipes[i].ndx == ndx)
- return &mdata->cursor_pipes[i];
- }
+static bool __pipe_cmp_ndx(struct mdss_mdp_pipe *pipe, void *data)
+{
+ u32 *ndx = data;
- return NULL;
+ return pipe->ndx == *ndx;
+}
+
+struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata,
+ u32 ndx, enum mdss_mdp_pipe_rect rect_num)
+{
+ return mdss_mdp_pipe_lookup(mdata, rect_num, __pipe_cmp_ndx, &ndx);
}
/*
@@ -1328,14 +1352,13 @@ static void mdss_mdp_pipe_check_stage(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_mixer *mixer)
{
int index;
- u8 right_blend_index;
if (pipe->mixer_stage == MDSS_MDP_STAGE_UNUSED || !mixer)
return;
- right_blend_index = pipe->is_right_blend &&
- !(pipe->src_split_req && mixer->is_right_mixer);
- index = (pipe->mixer_stage * MAX_PIPES_PER_STAGE) + right_blend_index;
+ index = (pipe->mixer_stage * MAX_PIPES_PER_STAGE);
+ if (pipe->is_right_blend)
+ index++;
if (index < MAX_PIPES_PER_LM && pipe == mixer->stage_pipe[index]) {
pr_err("pipe%d mixer:%d pipe->mixer_stage=%d src_split:%d right blend:%d\n",
pipe->num, mixer->num, pipe->mixer_stage,
@@ -1344,15 +1367,10 @@ static void mdss_mdp_pipe_check_stage(struct mdss_mdp_pipe *pipe,
}
}
-static void mdss_mdp_pipe_free(struct kref *kref)
+static void mdss_mdp_pipe_hw_cleanup(struct mdss_mdp_pipe *pipe)
{
- struct mdss_mdp_pipe *pipe;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- pipe = container_of(kref, struct mdss_mdp_pipe, kref);
-
- pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num);
-
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
mdss_mdp_pipe_panic_vblank_signal_ctrl(pipe, false);
@@ -1365,30 +1383,41 @@ static void mdss_mdp_pipe_free(struct kref *kref)
} else {
mdss_mdp_smp_unreserve(pipe);
}
+
if (mdss_has_quirk(mdata, MDSS_QUIRK_BWCPANIC) && pipe->bwc_mode) {
unsigned long pnum_bitmap = BIT(pipe->num);
bitmap_andnot(mdata->bwc_enable_map, mdata->bwc_enable_map,
&pnum_bitmap, MAX_DRV_SUP_PIPES);
- pipe->bwc_mode = 0;
if (bitmap_empty(mdata->bwc_enable_map, MAX_DRV_SUP_PIPES))
mdss_mdp_bwcpanic_ctrl(mdata, false);
}
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_MULTI_REC_OP_MODE, 0);
+
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
+}
- pipe->flags = 0;
- pipe->is_right_blend = false;
- pipe->src_split_req = false;
+static void mdss_mdp_pipe_free(struct kref *kref)
+{
+ struct mdss_mdp_pipe *pipe, *next_pipe;
+
+ pipe = container_of(kref, struct mdss_mdp_pipe, kref);
+
+ pr_debug("ndx=%x pnum=%d rect=%d\n",
+ pipe->ndx, pipe->num, pipe->multirect.num);
+
+ next_pipe = (struct mdss_mdp_pipe *) pipe->multirect.next;
+ if (!next_pipe || (atomic_read(&next_pipe->kref.refcount) == 0)) {
+ mdss_mdp_pipe_hw_cleanup(pipe);
+ } else {
+ pr_debug("skip hw cleanup on pnum=%d rect=%d, rect%d still in use\n",
+ pipe->num, pipe->multirect.num,
+ next_pipe->multirect.num);
+ }
mdss_mdp_pipe_check_stage(pipe, pipe->mixer_left);
mdss_mdp_pipe_check_stage(pipe, pipe->mixer_right);
-
- pipe->mfd = NULL;
- pipe->mixer_left = pipe->mixer_right = NULL;
- pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED;
- memset(&pipe->scaler, 0, sizeof(struct mdp_scale_data));
- memset(&pipe->layer, 0, sizeof(struct mdp_input_layer));
}
static bool mdss_mdp_check_pipe_in_use(struct mdss_mdp_pipe *pipe)
@@ -1740,19 +1769,87 @@ void mdss_mdp_pipe_position_update(struct mdss_mdp_pipe *pipe,
}
}
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE, src_size);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY, src_xy);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_SIZE, dst_size);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_XY, dst_xy);
+ if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE, src_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY, src_xy);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_SIZE, dst_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_XY, dst_xy);
+ } else {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE_REC1,
+ src_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY_REC1,
+ src_xy);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_SIZE_REC1,
+ dst_size);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_XY_REC1,
+ dst_xy);
+ }
- MDSS_XLOG(pipe->num, src_size, src_xy, dst_size, dst_xy,
- pipe->bwc_mode);
+ MDSS_XLOG(pipe->num, pipe->multirect.num, src_size, src_xy,
+ dst_size, dst_xy, pipe->multirect.mode);
+}
+
+static void mdss_mdp_pipe_stride_update(struct mdss_mdp_pipe *pipe)
+{
+ u32 reg0, reg1;
+ u32 ystride[MAX_PLANES] = {0};
+ struct mdss_mdp_pipe *rec0_pipe, *rec1_pipe;
+ u32 secure = 0;
+
+ /*
+ * since stride registers are shared between both rectangles in
+ * multirect mode, delayed programming allows programming of both
+ * together
+ */
+ if (is_pipe_programming_delay_needed(pipe)) {
+ pr_debug("skip stride programming for pipe%d rec%d\n",
+ pipe->num, pipe->multirect.num);
+ return;
+ }
+
+ if (pipe->multirect.mode == MDSS_MDP_PIPE_MULTIRECT_NONE) {
+ memcpy(&ystride, &pipe->src_planes.ystride,
+ sizeof(u32) * MAX_PLANES);
+ if (pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ secure = 0xF;
+ } else {
+ if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
+ rec0_pipe = pipe;
+ rec1_pipe = pipe->multirect.next;
+ } else {
+ rec1_pipe = pipe;
+ rec0_pipe = pipe->multirect.next;
+ }
+
+ ystride[0] = rec0_pipe->src_planes.ystride[0];
+ ystride[2] = rec0_pipe->src_planes.ystride[2];
+ if (rec0_pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ secure |= 0x5;
+
+ ystride[1] = rec1_pipe->src_planes.ystride[0];
+ ystride[3] = rec1_pipe->src_planes.ystride[2];
+ if (rec1_pipe->flags & MDP_SECURE_OVERLAY_SESSION)
+ secure |= 0xA;
+ }
+
+ reg0 = (ystride[0]) | (ystride[1] << 16);
+ reg1 = (ystride[2]) | (ystride[3] << 16);
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE0, reg0);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE1, reg1);
+
+ pr_debug("pipe%d multirect:num%d mode=%d, ystride0=0x%x ystride1=0x%x\n",
+ pipe->num, pipe->multirect.num, pipe->multirect.mode,
+ reg0, reg1);
+ MDSS_XLOG(pipe->num, pipe->multirect.num,
+ pipe->multirect.mode, reg0, reg1);
}
static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *data)
{
- u32 img_size, ystride0, ystride1;
+ u32 img_size;
u32 width, height, decimation;
int ret = 0;
struct mdss_rect dst, src;
@@ -1855,11 +1952,6 @@ static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe,
}
}
- ystride0 = (pipe->src_planes.ystride[0]) |
- (pipe->src_planes.ystride[1] << 16);
- ystride1 = (pipe->src_planes.ystride[2]) |
- (pipe->src_planes.ystride[3] << 16);
-
/*
* Software overfetch is used when scalar pixel extension is
* not enabled
@@ -1891,10 +1983,9 @@ static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe,
}
mdss_mdp_pipe_position_update(pipe, &src, &dst);
+ mdss_mdp_pipe_stride_update(pipe);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_IMG_SIZE, img_size);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE0, ystride0);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE1, ystride1);
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_DECIMATION_CONFIG,
decimation);
@@ -1931,15 +2022,11 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe)
{
struct mdss_mdp_format_params *fmt;
u32 chroma_samp, unpack, src_format;
- u32 secure = 0;
u32 opmode;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
fmt = pipe->src_fmt;
- if (pipe->flags & MDP_SECURE_OVERLAY_SESSION)
- secure = 0xF;
-
opmode = pipe->bwc_mode;
if (pipe->flags & MDP_FLIP_LR)
opmode |= MDSS_MDP_OP_FLIP_LR;
@@ -2005,10 +2092,22 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe)
}
if (pipe->scaler.enable)
opmode |= (1 << 31);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, src_format);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
+
+ if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_FORMAT, src_format);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
+ } else {
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_FORMAT_REC1, src_format);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN_REC1, unpack);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_OP_MODE_REC1, opmode);
+ }
/* clear UBWC error */
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_UBWC_ERROR_STATUS, BIT(31));
@@ -2022,9 +2121,10 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe)
int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_id, u32 *xin_id,
- u32 type, const int *pnums, u32 len, u8 priority_base)
+ u32 type, const int *pnums, u32 len, u32 rects_per_sspp,
+ u8 priority_base)
{
- u32 i;
+ u32 i, j;
if (!head || !mdata) {
pr_err("unable to setup pipe type=%d: invalid input\n", type);
@@ -2032,16 +2132,38 @@ int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
}
for (i = 0; i < len; i++) {
- head[i].type = type;
- head[i].ftch_id = ftch_id[i];
- head[i].xin_id = xin_id[i];
- head[i].num = pnums[i];
- head[i].ndx = BIT(pnums[i]);
- head[i].priority = i + priority_base;
- head[i].base = mdata->mdss_io.base + offsets[i];
- pr_info("type:%d ftchid:%d xinid:%d num:%d ndx:0x%x prio:%d\n",
- head[i].type, head[i].ftch_id, head[i].xin_id,
- head[i].num, head[i].ndx, head[i].priority);
+ struct mdss_mdp_pipe *pipe = head + (i * rects_per_sspp);
+
+ pipe->type = type;
+ pipe->ftch_id = ftch_id[i];
+ pipe->xin_id = xin_id[i];
+ pipe->num = pnums[i];
+ pipe->ndx = BIT(pnums[i]);
+ pipe->priority = i + priority_base;
+ pipe->base = mdata->mdss_io.base + offsets[i];
+ pipe->multirect.num = MDSS_MDP_PIPE_RECT0;
+ pipe->multirect.mode = MDSS_MDP_PIPE_MULTIRECT_NONE;
+ pipe->multirect.max_rects = rects_per_sspp;
+ pipe->multirect.next = NULL;
+
+ pr_info("type:%d ftchid:%d xinid:%d num:%d rect:%d ndx:0x%x prio:%d\n",
+ pipe->type, pipe->ftch_id, pipe->xin_id, pipe->num,
+ pipe->multirect.num, pipe->ndx, pipe->priority);
+
+ for (j = 1; j < rects_per_sspp; j++) {
+ struct mdss_mdp_pipe *next = pipe + j;
+
+ pipe[j-1].multirect.next = next;
+ *next = pipe[j-1];
+ next->multirect.num++;
+ next->multirect.next = pipe;
+
+ pr_info("type:%d ftchid:%d xinid:%d num:%d rect:%d ndx:0x%x prio:%d\n",
+ next->type, next->ftch_id, next->xin_id,
+ next->num, next->multirect.num, next->ndx,
+ next->priority);
+ }
+
}
return 0;
@@ -2051,12 +2173,12 @@ static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_data *src_data)
{
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
- struct mdss_mdp_data data = *src_data;
- int ret = 0;
+ int i, ret = 0;
+ u32 addr[MAX_PLANES] = { 0 };
pr_debug("pnum=%d\n", pipe->num);
- ret = mdss_mdp_data_check(&data, &pipe->src_planes, pipe->src_fmt);
+ ret = mdss_mdp_data_check(src_data, &pipe->src_planes, pipe->src_fmt);
if (ret)
return ret;
@@ -2068,20 +2190,31 @@ static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe,
if (pipe->overfetch_disable & OVERFETCH_DISABLE_TOP)
y = pipe->src.y;
- mdss_mdp_data_calc_offset(&data, x, y,
+ mdss_mdp_data_calc_offset(src_data, x, y,
&pipe->src_planes, pipe->src_fmt);
}
+ for (i = 0; i < MAX_PLANES; i++)
+ addr[i] = src_data->p[i].addr;
+
/* planar format expects YCbCr, swap chroma planes if YCrCb */
if (mdata->mdp_rev < MDSS_MDP_HW_REV_102 &&
(pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_PLANAR)
&& (pipe->src_fmt->element[0] == C1_B_Cb))
- swap(data.p[1].addr, data.p[2].addr);
+ swap(addr[1], addr[2]);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC0_ADDR, data.p[0].addr);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC1_ADDR, data.p[1].addr);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC2_ADDR, data.p[2].addr);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, data.p[3].addr);
+ if (pipe->multirect.mode == MDSS_MDP_PIPE_MULTIRECT_NONE) {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC0_ADDR, addr[0]);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC1_ADDR, addr[1]);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC2_ADDR, addr[2]);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, addr[3]);
+ } else if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC0_ADDR, addr[0]);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC2_ADDR, addr[2]);
+ } else { /* RECT1 */
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC1_ADDR, addr[0]);
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, addr[2]);
+ }
return 0;
}
@@ -2105,14 +2238,35 @@ static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe)
/* support ARGB color format only */
unpack = (C3_ALPHA << 24) | (C2_R_Cr << 16) |
(C1_B_Cb << 8) | (C0_G_Y << 0);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, format);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR,
- pipe->bg_color);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
if (pipe->scaler.enable)
opmode |= (1 << 31);
- mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
+
+ if (pipe->multirect.num == MDSS_MDP_PIPE_RECT0) {
+ /*
+ * rect0 will drive whether to secure the pipeline, even though
+ * no secure content is being fetched
+ */
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_FORMAT, format);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR, pipe->bg_color);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode);
+ } else {
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_FORMAT_REC1, format);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR_REC1,
+ pipe->bg_color);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN_REC1, unpack);
+ mdss_mdp_pipe_write(pipe,
+ MDSS_MDP_REG_SSPP_SRC_OP_MODE_REC1, opmode);
+ }
if (pipe->type != MDSS_MDP_PIPE_TYPE_DMA) {
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_CONFIG, 0);
@@ -2157,9 +2311,10 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
int ret = 0;
struct mdss_mdp_ctl *ctl;
u32 params_changed;
- u32 opmode = 0;
+ u32 opmode = 0, multirect_opmode = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
bool roi_changed = false;
+ bool delayed_programming;
if (!pipe) {
pr_err("pipe not setup properly for queue\n");
@@ -2192,6 +2347,8 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
if (pipe->mixer_right)
roi_changed |= pipe->mixer_right->roi_changed;
+ delayed_programming = is_pipe_programming_delay_needed(pipe);
+
/*
* Reprogram the pipe when there is no dedicated wfd blk and
* virtual mixer is allocated for the DMA pipe during concurrent
@@ -2202,7 +2359,8 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
(pipe->mixer_left->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) &&
(ctl->mdata->mixer_switched)) || roi_changed;
- if (params_changed) {
+ /* apply changes that are common in case of multi rects only once */
+ if (params_changed && !delayed_programming) {
bool is_realtime = !((ctl->intf_num == MDSS_MDP_NO_INTF)
|| pipe->mixer_left->rotator_mode);
@@ -2261,20 +2419,36 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE,
opmode);
- if (test_bit(MDSS_QOS_PER_PIPE_LUT, mdata->mdss_qos_map))
- mdss_mdp_pipe_qos_lut(pipe);
+ if (!delayed_programming) {
+ if (test_bit(MDSS_QOS_PER_PIPE_LUT,
+ mdata->mdss_qos_map))
+ mdss_mdp_pipe_qos_lut(pipe);
- if (mdss_mdp_panic_signal_support_mode(mdata) ==
- MDSS_MDP_PANIC_PER_PIPE_CFG)
- mdss_mdp_config_pipe_panic_lut(pipe);
+ if (mdss_mdp_panic_signal_support_mode(mdata) ==
+ MDSS_MDP_PANIC_PER_PIPE_CFG)
+ mdss_mdp_config_pipe_panic_lut(pipe);
- if (pipe->type != MDSS_MDP_PIPE_TYPE_CURSOR) {
- mdss_mdp_pipe_panic_vblank_signal_ctrl(pipe, true);
- mdss_mdp_pipe_panic_signal_ctrl(pipe, true);
- mdss_mdp_set_ot_limit_pipe(pipe);
+ if (pipe->type != MDSS_MDP_PIPE_TYPE_CURSOR) {
+ mdss_mdp_pipe_panic_vblank_signal_ctrl(pipe, 1);
+ mdss_mdp_pipe_panic_signal_ctrl(pipe, true);
+ mdss_mdp_set_ot_limit_pipe(pipe);
+ }
}
}
+ /*
+ * enable multirect only when both RECT0 and RECT1 are enabled,
+ * othwerise expect to work in non-multirect only in RECT0
+ */
+ if (pipe->multirect.mode != MDSS_MDP_PIPE_MULTIRECT_NONE) {
+ multirect_opmode = BIT(0) | BIT(1);
+
+ if (pipe->multirect.mode == MDSS_MDP_PIPE_MULTIRECT_SERIAL)
+ multirect_opmode |= BIT(2);
+ }
+
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_MULTI_REC_OP_MODE,
+ multirect_opmode);
if (src_data == NULL) {
pr_debug("src_data=%p pipe num=%dx\n",
src_data, pipe->num);
@@ -2283,6 +2457,7 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
if (pipe->type != MDSS_MDP_PIPE_TYPE_CURSOR)
mdss_mdp_smp_alloc(pipe);
+
ret = mdss_mdp_src_addr_setup(pipe, src_data);
if (ret) {
pr_err("addr setup error for pnum=%d\n", pipe->num);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index 4ec66b2c85de..a87e5d11b4a6 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -541,7 +541,6 @@ inline int linear_map(int in, int *out, int in_max, int out_max)
*/
static inline struct mdss_mdp_pipe *__get_hist_pipe(int pnum)
{
- struct mdss_data_type *mdata = mdss_mdp_get_mdata();
enum mdss_mdp_pipe_type ptype;
ptype = get_pipe_type_from_num(pnum);
@@ -550,7 +549,7 @@ static inline struct mdss_mdp_pipe *__get_hist_pipe(int pnum)
if (ptype != MDSS_MDP_PIPE_TYPE_VIG)
return NULL;
- return mdss_mdp_pipe_get(mdata, BIT(pnum));
+ return mdss_mdp_pipe_get(BIT(pnum), MDSS_MDP_PIPE_RECT0);
}
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, struct mdp_csc_cfg *data)
@@ -572,7 +571,12 @@ int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, struct mdp_csc_cfg *data)
switch (block) {
case MDSS_MDP_BLOCK_SSPP:
lv_shift = CSC_8BIT_LV_SHIFT;
- pipe = mdss_mdp_pipe_search(mdata, BIT(blk_idx));
+ /*
+ * CSC is used on VIG pipes and currently VIG pipes do not
+ * support multirect so always use RECT0.
+ */
+ pipe = mdss_mdp_pipe_search(mdata, BIT(blk_idx),
+ MDSS_MDP_PIPE_RECT0);
if (!pipe) {
pr_err("invalid blk index=%d\n", blk_idx);
ret = -EINVAL;
@@ -613,7 +617,10 @@ int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, struct mdp_csc_cfg *data)
break;
case MDSS_MDP_BLOCK_SSPP_10:
lv_shift = CSC_10BIT_LV_SHIFT;
- pipe = mdss_mdp_pipe_search(mdata, BIT(blk_idx));
+
+ /* CSC can be applied only on VIG which RECT0 only */
+ pipe = mdss_mdp_pipe_search(mdata, BIT(blk_idx),
+ MDSS_MDP_PIPE_RECT0);
if (!pipe) {
pr_err("invalid blk index=%d\n", blk_idx);
ret = -EINVAL;
@@ -1198,15 +1205,8 @@ static int mdss_mdp_qseed2_setup(struct mdss_mdp_pipe *pipe)
pipe->scaler.enable);
mdata = mdss_mdp_get_mdata();
- mdss_mdp_pp_get_dcm_state(pipe, &dcm_state);
-
- if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102 && pipe->src_fmt->is_yuv)
- filter_mode = MDSS_MDP_SCALE_FILTER_CA;
- else
- filter_mode = MDSS_MDP_SCALE_FILTER_BIL;
-
if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA ||
- pipe->type == MDSS_MDP_PIPE_TYPE_CURSOR) {
+ pipe->type == MDSS_MDP_PIPE_TYPE_CURSOR) {
if (pipe->dst.h != pipe->src.h || pipe->dst.w != pipe->src.w) {
pr_err("no scaling supported on dma/cursor pipe, num:%d\n",
pipe->num);
@@ -1216,6 +1216,13 @@ static int mdss_mdp_qseed2_setup(struct mdss_mdp_pipe *pipe)
}
}
+ mdss_mdp_pp_get_dcm_state(pipe, &dcm_state);
+
+ if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102 && pipe->src_fmt->is_yuv)
+ filter_mode = MDSS_MDP_SCALE_FILTER_CA;
+ else
+ filter_mode = MDSS_MDP_SCALE_FILTER_BIL;
+
src_w = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci);
src_h = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci);
@@ -1815,7 +1822,7 @@ void mdss_mdp_pipe_pp_clear(struct mdss_mdp_pipe *pipe)
int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
{
- int ret = 0;
+ int i, ret = 0;
unsigned long flags = 0;
char __iomem *pipe_base;
u32 pipe_num, pipe_cnt;
@@ -1869,9 +1876,10 @@ int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
return -EINVAL;
}
- for (pipe_num = 0; pipe_num < pipe_cnt; pipe_num++) {
- if (pipe == (pipe_list + pipe_num))
+ for (i = 0, pipe_num = 0; pipe_num < pipe_cnt; pipe_num++) {
+ if (pipe->num == pipe_list[i].num)
break;
+ i += pipe->multirect.max_rects;
}
if (pipe_num == pipe_cnt) {
@@ -5331,7 +5339,8 @@ static inline struct pp_hist_col_info *get_hist_info_from_isr(u32 *isr)
*isr &= ~(MDSS_MDP_HIST_INTR_VIG_3_DONE |
MDSS_MDP_HIST_INTR_VIG_3_RESET_DONE);
}
- pipe = mdss_mdp_pipe_search(mdata, BIT(blk_idx));
+ pipe = mdss_mdp_pipe_search(mdata, BIT(blk_idx),
+ MDSS_MDP_PIPE_RECT0);
if (IS_ERR_OR_NULL(pipe)) {
pr_debug("pipe DNE, %d\n", blk_idx);
return NULL;
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 6aba03ea2570..8612d60deeca 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -746,7 +746,8 @@ static struct mdss_rot_hw_resource *mdss_rotator_hw_alloc(
goto error;
pipe_ndx = mdata->dma_pipes[pipe_id].ndx;
- hw->pipe = mdss_mdp_pipe_assign(mdata, hw->mixer, pipe_ndx);
+ hw->pipe = mdss_mdp_pipe_assign(mdata, hw->mixer,
+ pipe_ndx, MDSS_MDP_PIPE_RECT0);
if (IS_ERR_OR_NULL(hw->pipe)) {
pr_err("dma pipe allocation failed\n");
ret = -ENODEV;
diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h
index 7bb87155bfcc..c7c8c634022a 100644
--- a/include/uapi/linux/msm_mdp_ext.h
+++ b/include/uapi/linux/msm_mdp_ext.h
@@ -84,6 +84,18 @@ LAYER FLAG CONFIGURATION
/* Flag enabled qseed3 scaling for the current layer */
#define MDP_LAYER_ENABLE_QSEED3_SCALE 0x800
+/*
+ * layer will work in multirect mode, where single hardware should
+ * fetch multiple rectangles with a single hardware
+ */
+#define MDP_LAYER_MULTIRECT_ENABLE 0x1000
+
+/*
+ * if flag present and multirect is enabled, multirect will work in parallel
+ * fetch mode, otherwise it will default to serial fetch mode.
+ */
+#define MDP_LAYER_MULTIRECT_PARALLEL_MODE 0x2000
+
/**********************************************************************
DESTINATION SCALER FLAG CONFIGURATION
**********************************************************************/