msm_fb: display: use blt mode to implement MDP writeback
MDP frame buffer mode was used to implement writeback for
pulling interface (lcdc and dsi video). However frame buffer
mode is not recommanded by hardware engineer due to lacking
of verification. This patch will replace frame buffer mode
with BLT mode to implement MDP writeback.
Signed-off-by: Kuogee Hsieh <khsieh@codeaurora.org>
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 70f3d5e..0b905f9 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -424,7 +424,7 @@
void mdp4_dsi_video_overlay(struct msm_fb_data_type *mfd);
int mdp4_dsi_video_on(struct platform_device *pdev);
int mdp4_dsi_video_off(struct platform_device *pdev);
-void mdp4_overlay0_done_dsi_video(void);
+void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma);
void mdp4_overlay0_done_dsi_cmd(struct mdp_dma_data *dma);
void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd);
void mdp4_overlay_dsi_state_set(int state);
@@ -454,7 +454,7 @@
void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe);
int mdp4_overlay_pipe_staged(int mixer);
void mdp4_lcdc_primary_vsyn(void);
-void mdp4_overlay0_done_lcdc(void);
+void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma);
void mdp4_overlay0_done_mddi(struct mdp_dma_data *dma);
void mdp4_dma_s_done_mddi(void);
void mdp4_dma_p_done_mddi(void);
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 35de4d7..0f4d330 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -1057,26 +1057,16 @@
data <<= 16;
data |= pipe->src_width;
outpdw(overlay_base + 0x0008, data); /* ROI, height + width */
- if (ctrl->panel_mode & MDP4_PANEL_LCDC ||
- ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
- outpdw(overlay_base + 0x000c, pipe->blt_addr);
- outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
+ off = 0;
+ if (pipe->ov_cnt & 0x01)
off = pipe->src_height * pipe->src_width * bpp;
- outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
- /* LCDC - FRAME BUFFER + vsync rate */
- outpdw(overlay_base + 0x0004, 0x04);/* 30 refresh */
- } else { /* MDDI */
- off = 0;
- if (pipe->ov_cnt & 0x01)
- off = pipe->src_height * pipe->src_width * bpp;
- outpdw(overlay_base + 0x000c, pipe->blt_addr + off);
- /* overlay ouput is RGB888 */
- outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
- outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
- /* MDDI - BLT + on demand */
- outpdw(overlay_base + 0x0004, 0x08);
- }
+ outpdw(overlay_base + 0x000c, pipe->blt_addr + off);
+ /* overlay ouput is RGB888 */
+ outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
+ outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
+ /* MDDI - BLT + on demand */
+ outpdw(overlay_base + 0x0004, 0x08);
#ifdef BLT_RGB565
outpdw(overlay_base + 0x0014, 0x1); /* RGB565 */
#else
@@ -1464,7 +1454,7 @@
uint32 ptype, num, ndx;
struct mdp4_pipe_desc *pd;
- pr_debug("%s: pipe=%x ndx=%d\n", __func__,
+ pr_info("%s: pipe=%x ndx=%d\n", __func__,
(int)pipe, pipe->pipe_ndx);
pd = &ctrl->ov_pipe[pipe->pipe_num];
if (pd->ref_cnt) {
@@ -1999,10 +1989,8 @@
(req->src_rect.h >
req->dst_rect.h || req->src_rect.w > req->dst_rect.w)) {
if (mdp4_overlay_validate_downscale(req, mfd,
- perf_level, mfd->panel_info.clk_rate)) {
- mutex_unlock(&mfd->dma->ov_mutex);
- return -ERANGE;
- }
+ perf_level, mfd->panel_info.clk_rate))
+ mdp4_lcdc_overlay_blt_start(mfd);
}
if ((mfd->panel_info.type == MIPI_VIDEO_PANEL) &&
@@ -2010,7 +1998,7 @@
req->dst_rect.h || req->src_rect.w > req->dst_rect.w)) {
if (mdp4_overlay_validate_downscale(req, mfd,
perf_level, (&mfd->panel_info.mipi)->dsi_pclk_rate))
- mdp4_dsi_video_blt_start(mfd);
+ mdp4_dsi_video_blt_start(mfd);
}
mixer = mfd->panel_info.pdest; /* DISPLAY_1 or DISPLAY_2 */
@@ -2167,6 +2155,7 @@
mdp4_overlay_reg_flush(pipe, 1);
mdp4_overlay_lcdc_vsync_push(mfd, pipe);
pipe->flags = flags;
+ mdp4_lcdc_overlay_blt_stop(mfd);
}
}
#ifdef CONFIG_FB_MSM_DTV
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 852a1b5..7f799b3 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -366,16 +366,71 @@
mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
}
+static void mdp4_dsi_video_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+ char *overlay_base;
+
+
+ if (pipe->blt_addr == 0)
+ return;
+
+
+#ifdef BLT_RGB565
+ bpp = 2; /* overlay ouput is RGB565 */
+#else
+ bpp = 3; /* overlay ouput is RGB888 */
+#endif
+ off = 0;
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->blt_addr + off;
+
+ /* overlay 0 */
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+ outpdw(overlay_base + 0x000c, addr);
+ outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_dsi_video_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+
+ if (pipe->blt_addr == 0)
+ return;
+
+
+#ifdef BLT_RGB565
+ bpp = 2; /* overlay ouput is RGB565 */
+#else
+ bpp = 3; /* overlay ouput is RGB888 */
+#endif
+ off = 0;
+ if (pipe->dmap_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->blt_addr + off;
+
+ /* dmap */
+ MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+/*
+ * mdp4_overlay_dsi_video_wait4event:
+ * INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only
+ * no INTR_OVERLAY0_DONE event allowed.
+ */
static void mdp4_overlay_dsi_video_wait4event(struct msm_fb_data_type *mfd,
- int intr)
+ int intr_done)
{
unsigned long flag;
spin_lock_irqsave(&mdp_spin_lock, flag);
INIT_COMPLETION(dsi_video_comp);
mfd->dma->waiting = TRUE;
- outp32(MDP_INTR_CLEAR, intr);
- mdp_intr_mask |= intr;
+ outp32(MDP_INTR_CLEAR, intr_done);
+ mdp_intr_mask |= intr_done;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */
spin_unlock_irqrestore(&mdp_spin_lock, flag);
@@ -383,17 +438,59 @@
mdp_disable_irq(MDP_DMA2_TERM);
}
+static void mdp4_overlay_dsi_video_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+ unsigned long flag;
+ int need_wait = 0;
+
+ pr_debug("%s: start pid=%d\n", __func__, current->pid);
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ if (mfd->dma->busy == TRUE) {
+ INIT_COMPLETION(mfd->dma->comp);
+ need_wait++;
+ }
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+ if (need_wait) {
+ /* wait until DMA finishes the current job */
+ pr_debug("%s: pending pid=%d\n", __func__, current->pid);
+ wait_for_completion(&mfd->dma->comp);
+ }
+ pr_debug("%s: done pid=%d\n", __func__, current->pid);
+}
+
void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe)
{
+ unsigned long flag;
if (pipe->flags & MDP_OV_PLAY_NOWAIT)
return;
- mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+ if (dsi_pipe->blt_addr) {
+ mdp4_overlay_dsi_video_dma_busy_wait(mfd);
- /* change mdp clk while mdp is idle */
- mdp4_set_perf_level();
+ mdp4_dsi_video_blt_ov_update(dsi_pipe);
+ dsi_pipe->ov_cnt++;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+ mdp_intr_mask |= INTR_OVERLAY0_DONE;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(MDP_OVERLAY0_TERM);
+ mfd->dma->busy = TRUE;
+ mb(); /* make sure all registers updated */
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+ outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */
+ mb();
+ mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+ } else {
+
+ mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+ /* change mdp clk while mdp is idle */
+ mdp4_set_perf_level();
+ }
}
/*
@@ -412,13 +509,18 @@
complete_all(&dsi_video_comp);
}
-
/*
* mdp4_overlay1_done_dsi: called from isr
*/
-void mdp4_overlay0_done_dsi_video()
+void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma)
{
- complete(&dsi_video_comp);
+ spin_lock(&mdp_spin_lock);
+ dma->busy = FALSE;
+ mdp4_dsi_video_blt_dmap_update(dsi_pipe);
+ dsi_pipe->dmap_cnt++;
+ mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+ spin_unlock(&mdp_spin_lock);
+ complete(&dma->comp);
}
#ifdef CONFIG_FB_MSM_OVERLAY_WRITEBACK
@@ -435,6 +537,9 @@
spin_lock_irqsave(&mdp_spin_lock, flag);
if (enable && dsi_pipe->blt_addr == 0) {
dsi_pipe->blt_addr = dsi_pipe->blt_base;
+ dsi_pipe->blt_cnt = 0;
+ dsi_pipe->ov_cnt = 0;
+ dsi_pipe->dmap_cnt = 0;
change++;
} else if (enable == 0 && dsi_pipe->blt_addr) {
dsi_pipe->blt_addr = 0;
@@ -442,7 +547,6 @@
}
pr_info("%s: enable=%d blt_addr=%x\n", __func__,
enable, (int)dsi_pipe->blt_addr);
- dsi_pipe->blt_cnt = 0;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
if (!change)
@@ -454,7 +558,7 @@
*/
data = inpdw(MDP_BASE + DSI_VIDEO_BASE);
if (data) { /* timing generatore enabled */
- mdp4_overlay_dsi_video_wait4event(mfd, 1); /* dmap_done */
+ mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
msleep(20); /* make sure last frame is finished */
mipi_dsi_controller_cfg(0);
@@ -464,8 +568,8 @@
if (data) { /* timing generatore enabled */
MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 1);
- mdp4_overlay_dsi_video_wait4event(mfd, 1);
- mdp4_overlay_dsi_video_wait4event(mfd, 1);
+ mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
+ mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
MDP_OUTP(MDP_BASE + DSI_VIDEO_BASE, 0);
mipi_dsi_sw_reset();
mipi_dsi_controller_cfg(1);
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index 7a3a94e..e303f56 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -279,16 +279,71 @@
return ret;
}
+static void mdp4_lcdc_blt_ov_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+ char *overlay_base;
+
+
+ if (pipe->blt_addr == 0)
+ return;
+
+
+#ifdef BLT_RGB565
+ bpp = 2; /* overlay ouput is RGB565 */
+#else
+ bpp = 3; /* overlay ouput is RGB888 */
+#endif
+ off = 0;
+ if (pipe->ov_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->blt_addr + off;
+
+ /* overlay 0 */
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+ outpdw(overlay_base + 0x000c, addr);
+ outpdw(overlay_base + 0x001c, addr);
+}
+
+static void mdp4_lcdc_blt_dmap_update(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, addr;
+ int bpp;
+
+ if (pipe->blt_addr == 0)
+ return;
+
+
+#ifdef BLT_RGB565
+ bpp = 2; /* overlay ouput is RGB565 */
+#else
+ bpp = 3; /* overlay ouput is RGB888 */
+#endif
+ off = 0;
+ if (pipe->dmap_cnt & 0x01)
+ off = pipe->src_height * pipe->src_width * bpp;
+ addr = pipe->blt_addr + off;
+
+ /* dmap */
+ MDP_OUTP(MDP_BASE + 0x90008, addr);
+}
+
+/*
+ * mdp4_overlay_dsi_video_wait4event:
+ * INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only
+ * no INTR_OVERLAY0_DONE event allowed.
+ */
static void mdp4_overlay_lcdc_wait4event(struct msm_fb_data_type *mfd,
- int intr)
+ int intr_done)
{
unsigned long flag;
spin_lock_irqsave(&mdp_spin_lock, flag);
INIT_COMPLETION(lcdc_comp);
mfd->dma->waiting = TRUE;
- outp32(MDP_INTR_CLEAR, intr);
- mdp_intr_mask |= intr;
+ outp32(MDP_INTR_CLEAR, intr_done);
+ mdp_intr_mask |= intr_done;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
mdp_enable_irq(MDP_DMA2_TERM); /* enable intr */
spin_unlock_irqrestore(&mdp_spin_lock, flag);
@@ -296,16 +351,59 @@
mdp_disable_irq(MDP_DMA2_TERM);
}
+static void mdp4_overlay_lcdc_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+ unsigned long flag;
+ int need_wait = 0;
+
+ pr_debug("%s: start pid=%d\n", __func__, current->pid);
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ if (mfd->dma->busy == TRUE) {
+ INIT_COMPLETION(mfd->dma->comp);
+ need_wait++;
+ }
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+ if (need_wait) {
+ /* wait until DMA finishes the current job */
+ pr_debug("%s: pending pid=%d\n", __func__, current->pid);
+ wait_for_completion(&mfd->dma->comp);
+ }
+ pr_debug("%s: done pid=%d\n", __func__, current->pid);
+}
+
void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
struct mdp4_overlay_pipe *pipe)
{
+ unsigned long flag;
+
if (pipe->flags & MDP_OV_PLAY_NOWAIT)
return;
- mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE);
+ if (lcdc_pipe->blt_addr) {
+ mdp4_overlay_lcdc_dma_busy_wait(mfd);
- /* change mdp clk while mdp is idle` */
- mdp4_set_perf_level();
+ mdp4_lcdc_blt_ov_update(lcdc_pipe);
+ lcdc_pipe->ov_cnt++;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+ mdp_intr_mask |= INTR_OVERLAY0_DONE;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(MDP_OVERLAY0_TERM);
+ mfd->dma->busy = TRUE;
+ mb(); /* make sure all registers updated */
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+ outpdw(MDP_BASE + 0x0004, 0); /* kickoff overlay engine */
+ mb();
+ mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE);
+ } else {
+ mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE);
+
+ /* change mdp clk while mdp is idle` */
+ mdp4_set_perf_level();
+ }
}
/*
@@ -327,9 +425,15 @@
/*
* mdp4_overlay0_done_lcdc: called from isr
*/
-void mdp4_overlay0_done_lcdc(void)
+void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma)
{
- complete_all(&lcdc_comp);
+ spin_lock(&mdp_spin_lock);
+ dma->busy = FALSE;
+ mdp4_lcdc_blt_dmap_update(lcdc_pipe);
+ lcdc_pipe->dmap_cnt++;
+ mdp_disable_irq_nosync(MDP_OVERLAY0_TERM);
+ spin_unlock(&mdp_spin_lock);
+ complete(&dma->comp);
}
#ifdef CONFIG_FB_MSM_OVERLAY_WRITEBACK
@@ -346,12 +450,14 @@
if (enable && lcdc_pipe->blt_addr == 0) {
lcdc_pipe->blt_addr = lcdc_pipe->blt_base;
change++;
+ lcdc_pipe->blt_cnt = 0;
+ lcdc_pipe->ov_cnt = 0;
+ lcdc_pipe->dmap_cnt = 0;
} else if (enable == 0 && lcdc_pipe->blt_addr) {
lcdc_pipe->blt_addr = 0;
change++;
}
pr_info("%s: blt_addr=%x\n", __func__, (int)lcdc_pipe->blt_addr);
- lcdc_pipe->blt_cnt = 0;
spin_unlock_irqrestore(&mdp_spin_lock, flag);
if (!change)
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 2226715..77b0008 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -509,10 +509,10 @@
dma->waiting = FALSE;
spin_unlock(&mdp_spin_lock);
if (panel & MDP4_PANEL_LCDC)
- mdp4_overlay0_done_lcdc();
+ mdp4_overlay0_done_lcdc(dma);
#ifdef CONFIG_FB_MSM_MIPI_DSI
else if (panel & MDP4_PANEL_DSI_VIDEO)
- mdp4_overlay0_done_dsi_video();
+ mdp4_overlay0_done_dsi_video(dma);
#endif
} else { /* MDDI, DSI_CMD */
#ifdef CONFIG_FB_MSM_MIPI_DSI