Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
new file mode 100644
index 0000000..cc3bd1b
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -0,0 +1,2290 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/fb.h>
+#include <linux/msm_mdp.h>
+#include <linux/file.h>
+#include <linux/android_pmem.h>
+#include <linux/major.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/msm_kgsl.h>
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+
+#define VERSION_KEY_MASK 0xFFFFFF00
+
+struct mdp4_overlay_ctrl {
+ struct mdp4_pipe_desc ov_pipe[OVERLAY_PIPE_MAX];/* 4 */
+ struct mdp4_overlay_pipe plist[MDP4_MAX_PIPE]; /* 4 + 2 */
+ struct mdp4_overlay_pipe *stage[MDP4_MAX_MIXER][MDP4_MAX_STAGE + 2];
+ uint32 panel_3d;
+ uint32 panel_mode;
+ uint32 mixer0_played;
+ uint32 mixer1_played;
+} mdp4_overlay_db = {
+ .ov_pipe = {
+ {
+ .share = 0, /* RGB 1 */
+ },
+ {
+ .share = 0, /* RGB 2 */
+ },
+ {
+ .share = 1, /* VG 1 */
+ },
+ {
+ .share = 1, /* VG 2 */
+ },
+ },
+ .plist = {
+ {
+ .pipe_type = OVERLAY_TYPE_RGB,
+ .pipe_num = OVERLAY_PIPE_RGB1,
+ .pipe_ndx = 1,
+ },
+ {
+ .pipe_type = OVERLAY_TYPE_RGB,
+ .pipe_num = OVERLAY_PIPE_RGB2,
+ .pipe_ndx = 2,
+ },
+ {
+ .pipe_type = OVERLAY_TYPE_RGB, /* shared */
+ .pipe_num = OVERLAY_PIPE_VG1,
+ .pipe_ndx = 3,
+ },
+ {
+ .pipe_type = OVERLAY_TYPE_RGB, /* shared */
+ .pipe_num = OVERLAY_PIPE_VG2,
+ .pipe_ndx = 4,
+ },
+ {
+ .pipe_type = OVERLAY_TYPE_VIDEO, /* shared */
+ .pipe_num = OVERLAY_PIPE_VG1,
+ .pipe_ndx = 5,
+ },
+ {
+ .pipe_type = OVERLAY_TYPE_VIDEO, /* shared */
+ .pipe_num = OVERLAY_PIPE_VG2,
+ .pipe_ndx = 6,
+ },
+ },
+};
+
+static struct mdp4_overlay_ctrl *ctrl = &mdp4_overlay_db;
+static uint32 perf_level;
+static uint32 mdp4_del_res_rel;
+/* static array with index 0 for unset status and 1 for set status */
+static bool overlay_status[MDP4_OVERLAY_TYPE_MAX];
+
+void mdp4_overlay_status_write(enum mdp4_overlay_status type, bool val)
+{
+ overlay_status[type] = val;
+}
+
+bool mdp4_overlay_status_read(enum mdp4_overlay_status type)
+{
+ return overlay_status[type];
+}
+
+int mdp4_overlay_mixer_play(int mixer_num)
+{
+ if (mixer_num == MDP4_MIXER1)
+ return ctrl->mixer1_played;
+ else
+ return ctrl->mixer0_played;
+}
+
+void mdp4_overlay_panel_3d(int mixer_num, uint32 panel_3d)
+{
+ ctrl->panel_3d = panel_3d;
+}
+
+void mdp4_overlay_panel_mode(int mixer_num, uint32 mode)
+{
+ ctrl->panel_mode |= mode;
+}
+
+uint32 mdp4_overlay_panel_list(void)
+{
+ return ctrl->panel_mode;
+}
+
+void mdp4_overlay_dmae_cfg(struct msm_fb_data_type *mfd, int atv)
+{
+ uint32 dmae_cfg_reg;
+
+ if (atv)
+ dmae_cfg_reg = DMA_DEFLKR_EN;
+ else
+ dmae_cfg_reg = 0;
+
+ if (mfd->fb_imgType == MDP_BGR_565)
+ dmae_cfg_reg |= DMA_PACK_PATTERN_BGR;
+ else
+ dmae_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+
+ if (mfd->panel_info.bpp == 18) {
+ dmae_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */
+ DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+ } else if (mfd->panel_info.bpp == 16) {
+ dmae_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */
+ DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+ } else {
+ dmae_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */
+ DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+ }
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ /* dma2 config register */
+ MDP_OUTP(MDP_BASE + 0xb0000, dmae_cfg_reg);
+ if (atv) {
+ MDP_OUTP(MDP_BASE + 0xb0070, 0xeb0010);
+ MDP_OUTP(MDP_BASE + 0xb0074, 0xf00010);
+ MDP_OUTP(MDP_BASE + 0xb0078, 0xf00010);
+ MDP_OUTP(MDP_BASE + 0xb3000, 0x80);
+ MDP_OUTP(MDP_BASE + 0xb3010, 0x1800040);
+ MDP_OUTP(MDP_BASE + 0xb3014, 0x1000080);
+ MDP_OUTP(MDP_BASE + 0xb4004, 0x67686970);
+ } else {
+ MDP_OUTP(MDP_BASE + 0xb0070, 0xff0000);
+ MDP_OUTP(MDP_BASE + 0xb0074, 0xff0000);
+ MDP_OUTP(MDP_BASE + 0xb0078, 0xff0000);
+ }
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void unfill_black_screen(void)
+{
+ uint32 temp_src_format;
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ /*
+ * VG2 Constant Color
+ */
+ temp_src_format = inpdw(MDP_BASE + 0x30050);
+ MDP_OUTP(MDP_BASE + 0x30050, temp_src_format&(~BIT(22)));
+ /*
+ * MDP_OVERLAY_REG_FLUSH
+ */
+ MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void fill_black_screen(void)
+{
+ /*Black color*/
+ uint32 color = 0x00000000;
+ uint32 temp_src_format;
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ /*
+ * VG2 Constant Color
+ */
+ MDP_OUTP(MDP_BASE + 0x31008, color);
+ /*
+ * MDP_VG2_SRC_FORMAT
+ */
+ temp_src_format = inpdw(MDP_BASE + 0x30050);
+ MDP_OUTP(MDP_BASE + 0x30050, temp_src_format | BIT(22));
+ /*
+ * MDP_OVERLAY_REG_FLUSH
+ */
+ MDP_OUTP(MDP_BASE + 0x18000, BIT(3));
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_overlay_dmae_xy(struct mdp4_overlay_pipe *pipe)
+{
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ /* dma_p source */
+ MDP_OUTP(MDP_BASE + 0xb0004,
+ (pipe->src_height << 16 | pipe->src_width));
+ MDP_OUTP(MDP_BASE + 0xb0008, pipe->srcp0_addr);
+ MDP_OUTP(MDP_BASE + 0xb000c, pipe->srcp0_ystride);
+
+ /* dma_p dest */
+ MDP_OUTP(MDP_BASE + 0xb0010, (pipe->dst_y << 16 | pipe->dst_x));
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc)
+{
+ uint32 dma2_cfg_reg;
+
+ dma2_cfg_reg = DMA_DITHER_EN;
+#ifdef BLT_RGB565
+ /* RGB888 is 0 */
+ dma2_cfg_reg |= DMA_BUF_FORMAT_RGB565; /* blt only */
+#endif
+
+ if (mfd->fb_imgType == MDP_BGR_565)
+ dma2_cfg_reg |= DMA_PACK_PATTERN_BGR;
+ else
+ dma2_cfg_reg |= DMA_PACK_PATTERN_RGB;
+
+
+ if (mfd->panel_info.bpp == 18) {
+ dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 666 18BPP */
+ DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
+ } else if (mfd->panel_info.bpp == 16) {
+ dma2_cfg_reg |= DMA_DSTC0G_6BITS | /* 565 16BPP */
+ DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
+ } else {
+ dma2_cfg_reg |= DMA_DSTC0G_8BITS | /* 888 16BPP */
+ DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
+ }
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+#ifndef CONFIG_FB_MSM_LCDC_CHIMEI_WXGA_PANEL
+ if (lcdc)
+ dma2_cfg_reg |= DMA_PACK_ALIGN_MSB;
+#endif
+
+ /* dma2 config register */
+ MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+/*
+ * mdp4_overlay_dmap_xy: called form baselayer only
+ */
+void mdp4_overlay_dmap_xy(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 off, bpp;
+
+ if (mdp_is_in_isr == FALSE)
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ /* dma_p source */
+ MDP_OUTP(MDP_BASE + 0x90004,
+ (pipe->src_height << 16 | pipe->src_width));
+ if (pipe->blt_addr) {
+#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;
+ MDP_OUTP(MDP_BASE + 0x90008, pipe->blt_addr + off);
+ /* RGB888, output of overlay blending */
+ MDP_OUTP(MDP_BASE + 0x9000c, pipe->src_width * bpp);
+ } else {
+ MDP_OUTP(MDP_BASE + 0x90008, pipe->srcp0_addr);
+ MDP_OUTP(MDP_BASE + 0x9000c, pipe->srcp0_ystride);
+ }
+
+ /* dma_p dest */
+ MDP_OUTP(MDP_BASE + 0x90010, (pipe->dst_y << 16 | pipe->dst_x));
+
+ if (mdp_is_in_isr == FALSE)
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+#define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000
+#define MDP4_VG_PHASE_STEP_SHIFT 29
+
+static int mdp4_leading_0(uint32 num)
+{
+ uint32 bit = 0x80000000;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (bit & num)
+ return i;
+ bit >>= 1;
+ }
+
+ return i;
+}
+
+static uint32 mdp4_scale_phase_step(int f_num, uint32 src, uint32 dst)
+{
+ uint32 val;
+ int n;
+
+ n = mdp4_leading_0(src);
+ if (n > f_num)
+ n = f_num;
+ val = src << n; /* maximum to reduce lose of resolution */
+ val /= dst;
+ if (n < f_num) {
+ n = f_num - n;
+ val <<= n;
+ }
+
+ return val;
+}
+
+static void mdp4_scale_setup(struct mdp4_overlay_pipe *pipe)
+{
+ int ptype;
+
+ pipe->phasex_step = MDP4_VG_PHASE_STEP_DEFAULT;
+ pipe->phasey_step = MDP4_VG_PHASE_STEP_DEFAULT;
+ ptype = mdp4_overlay_format2type(pipe->src_format);
+
+ if (pipe->dst_h && pipe->src_h != pipe->dst_h) {
+ if (pipe->dst_h > pipe->src_h * 8) /* too much */
+ return;
+ pipe->op_mode |= MDP4_OP_SCALEY_EN;
+
+ if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
+ if (pipe->dst_h <= (pipe->src_h / 4))
+ pipe->op_mode |= MDP4_OP_SCALEY_MN_PHASE;
+ else
+ pipe->op_mode |= MDP4_OP_SCALEY_FIR;
+ }
+
+ pipe->phasey_step = mdp4_scale_phase_step(29,
+ pipe->src_h, pipe->dst_h);
+ }
+
+ if (pipe->dst_w && pipe->src_w != pipe->dst_w) {
+ if (pipe->dst_w > pipe->src_w * 8) /* too much */
+ return;
+ pipe->op_mode |= MDP4_OP_SCALEX_EN;
+
+ if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
+ if (pipe->dst_w <= (pipe->src_w / 4))
+ pipe->op_mode |= MDP4_OP_SCALEX_MN_PHASE;
+ else
+ pipe->op_mode |= MDP4_OP_SCALEX_FIR;
+ }
+
+ pipe->phasex_step = mdp4_scale_phase_step(29,
+ pipe->src_w, pipe->dst_w);
+ }
+}
+
+void mdp4_overlay_rgb_setup(struct mdp4_overlay_pipe *pipe)
+{
+ char *rgb_base;
+ uint32 src_size, src_xy, dst_size, dst_xy;
+ uint32 format, pattern;
+
+ rgb_base = MDP_BASE + MDP4_RGB_BASE;
+ rgb_base += (MDP4_RGB_OFF * pipe->pipe_num);
+
+ src_size = ((pipe->src_h << 16) | pipe->src_w);
+ src_xy = ((pipe->src_y << 16) | pipe->src_x);
+ dst_size = ((pipe->dst_h << 16) | pipe->dst_w);
+ dst_xy = ((pipe->dst_y << 16) | pipe->dst_x);
+
+ format = mdp4_overlay_format(pipe);
+ pattern = mdp4_overlay_unpack_pattern(pipe);
+
+#ifdef MDP4_IGC_LUT_ENABLE
+ pipe->op_mode |= MDP4_OP_IGC_LUT_EN;
+#endif
+
+ mdp4_scale_setup(pipe);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ outpdw(rgb_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */
+ outpdw(rgb_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */
+ outpdw(rgb_base + 0x0008, dst_size); /* MDP_RGB_DST_SIZE */
+ outpdw(rgb_base + 0x000c, dst_xy); /* MDP_RGB_DST_XY */
+
+ outpdw(rgb_base + 0x0010, pipe->srcp0_addr);
+ outpdw(rgb_base + 0x0040, pipe->srcp0_ystride);
+
+ outpdw(rgb_base + 0x0050, format);/* MDP_RGB_SRC_FORMAT */
+ outpdw(rgb_base + 0x0054, pattern);/* MDP_RGB_SRC_UNPACK_PATTERN */
+ outpdw(rgb_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */
+ outpdw(rgb_base + 0x005c, pipe->phasex_step);
+ outpdw(rgb_base + 0x0060, pipe->phasey_step);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+ mdp4_stat.pipe[pipe->pipe_num]++;
+}
+
+void mdp4_overlay_vg_setup(struct mdp4_overlay_pipe *pipe)
+{
+ char *vg_base;
+ uint32 frame_size, src_size, src_xy, dst_size, dst_xy;
+ uint32 format, pattern;
+ int pnum;
+
+ pnum = pipe->pipe_num - OVERLAY_PIPE_VG1; /* start from 0 */
+ vg_base = MDP_BASE + MDP4_VIDEO_BASE;
+ vg_base += (MDP4_VIDEO_OFF * pnum);
+
+ frame_size = ((pipe->src_height << 16) | pipe->src_width);
+ src_size = ((pipe->src_h << 16) | pipe->src_w);
+ src_xy = ((pipe->src_y << 16) | pipe->src_x);
+ dst_size = ((pipe->dst_h << 16) | pipe->dst_w);
+ dst_xy = ((pipe->dst_y << 16) | pipe->dst_x);
+
+ format = mdp4_overlay_format(pipe);
+ pattern = mdp4_overlay_unpack_pattern(pipe);
+
+ /* not RGB use VG pipe, pure VG pipe */
+ if (pipe->pipe_type != OVERLAY_TYPE_RGB)
+#ifdef MDP4_IGC_LUT_ENABLE
+ pipe->op_mode |= (MDP4_OP_CSC_EN | MDP4_OP_SRC_DATA_YCBCR |
+ MDP4_OP_IGC_LUT_EN);
+#else
+ pipe->op_mode |= (MDP4_OP_CSC_EN | MDP4_OP_SRC_DATA_YCBCR);
+#endif
+
+ mdp4_scale_setup(pipe);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ outpdw(vg_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */
+ outpdw(vg_base + 0x0004, src_xy); /* MDP_RGB_SRC_XY */
+ outpdw(vg_base + 0x0008, dst_size); /* MDP_RGB_DST_SIZE */
+ outpdw(vg_base + 0x000c, dst_xy); /* MDP_RGB_DST_XY */
+ outpdw(vg_base + 0x0048, frame_size); /* TILE frame size */
+
+ /* luma component plane */
+ outpdw(vg_base + 0x0010, pipe->srcp0_addr);
+
+ /* chroma component plane or planar color 1 */
+ outpdw(vg_base + 0x0014, pipe->srcp1_addr);
+
+ /* planar color 2 */
+ outpdw(vg_base + 0x0018, pipe->srcp2_addr);
+
+ outpdw(vg_base + 0x0040,
+ pipe->srcp1_ystride << 16 | pipe->srcp0_ystride);
+
+ outpdw(vg_base + 0x0044,
+ pipe->srcp3_ystride << 16 | pipe->srcp2_ystride);
+
+ outpdw(vg_base + 0x0050, format); /* MDP_RGB_SRC_FORMAT */
+ outpdw(vg_base + 0x0054, pattern); /* MDP_RGB_SRC_UNPACK_PATTERN */
+ outpdw(vg_base + 0x0058, pipe->op_mode);/* MDP_RGB_OP_MODE */
+ outpdw(vg_base + 0x005c, pipe->phasex_step);
+ outpdw(vg_base + 0x0060, pipe->phasey_step);
+
+ if (pipe->op_mode & MDP4_OP_DITHER_EN) {
+ outpdw(vg_base + 0x0068,
+ pipe->r_bit << 4 | pipe->b_bit << 2 | pipe->g_bit);
+ }
+
+ if (pipe->flags & MDP_SHARPENING) {
+ outpdw(vg_base + 0x8200,
+ mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength,
+ 0));
+ outpdw(vg_base + 0x8204,
+ mdp4_ss_table_value(pipe->req_data.dpp.sharp_strength,
+ 1));
+ }
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+ mdp4_stat.pipe[pipe->pipe_num]++;
+}
+
+int mdp4_overlay_format2type(uint32 format)
+{
+ switch (format) {
+ case MDP_RGB_565:
+ case MDP_RGB_888:
+ case MDP_BGR_565:
+ case MDP_XRGB_8888:
+ case MDP_ARGB_8888:
+ case MDP_RGBA_8888:
+ case MDP_BGRA_8888:
+ case MDP_RGBX_8888:
+ return OVERLAY_TYPE_RGB;
+ case MDP_YCRYCB_H2V1:
+ case MDP_Y_CRCB_H2V1:
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V2:
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CBCR_H2V2_TILE:
+ case MDP_Y_CRCB_H2V2_TILE:
+ case MDP_Y_CR_CB_H2V2:
+ case MDP_Y_CB_CR_H2V2:
+ case MDP_Y_CRCB_H1V1:
+ case MDP_Y_CBCR_H1V1:
+ return OVERLAY_TYPE_VIDEO;
+ default:
+ mdp4_stat.err_format++;
+ return -ERANGE;
+ }
+
+}
+
+#define C3_ALPHA 3 /* alpha */
+#define C2_R_Cr 2 /* R/Cr */
+#define C1_B_Cb 1 /* B/Cb */
+#define C0_G_Y 0 /* G/luma */
+#define YUV_444_MAX_WIDTH 1280 /* Max width for YUV 444*/
+
+int mdp4_overlay_format2pipe(struct mdp4_overlay_pipe *pipe)
+{
+ switch (pipe->src_format) {
+ case MDP_RGB_565:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 0;
+ pipe->r_bit = 1; /* R, 5 bits */
+ pipe->b_bit = 1; /* B, 5 bits */
+ pipe->g_bit = 2; /* G, 6 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 2;
+ pipe->element2 = C2_R_Cr; /* R */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->bpp = 2; /* 2 bpp */
+ break;
+ case MDP_RGB_888:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 0;
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 2;
+ pipe->element2 = C2_R_Cr; /* R */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->bpp = 3; /* 3 bpp */
+ break;
+ case MDP_BGR_565:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 0;
+ pipe->r_bit = 1; /* R, 5 bits */
+ pipe->b_bit = 1; /* B, 5 bits */
+ pipe->g_bit = 2; /* G, 6 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 2;
+ pipe->element2 = C1_B_Cb; /* B */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->bpp = 2; /* 2 bpp */
+ break;
+ case MDP_XRGB_8888:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 3; /* alpha, 4 bits */
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 3;
+ pipe->element3 = C3_ALPHA; /* alpha */
+ pipe->element2 = C2_R_Cr; /* R */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->bpp = 4; /* 4 bpp */
+ break;
+ case MDP_ARGB_8888:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 3; /* alpha, 4 bits */
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 1;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 3;
+ pipe->element3 = C3_ALPHA; /* alpha */
+ pipe->element2 = C2_R_Cr; /* R */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->bpp = 4; /* 4 bpp */
+ break;
+ case MDP_RGBA_8888:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 3; /* alpha, 4 bits */
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 1;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 3;
+ pipe->element3 = C3_ALPHA; /* alpha */
+ pipe->element2 = C1_B_Cb; /* B */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->bpp = 4; /* 4 bpp */
+ break;
+ case MDP_RGBX_8888:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 3;
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 3;
+ pipe->element3 = C3_ALPHA; /* alpha */
+ pipe->element2 = C1_B_Cb; /* B */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->bpp = 4; /* 4 bpp */
+ break;
+ case MDP_BGRA_8888:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 3; /* alpha, 4 bits */
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 1;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 3;
+ pipe->element3 = C3_ALPHA; /* alpha */
+ pipe->element2 = C2_R_Cr; /* R */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->bpp = 4; /* 4 bpp */
+ break;
+ case MDP_YCRYCB_H2V1:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_INTERLEAVED;
+ pipe->a_bit = 0; /* alpha, 4 bits */
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 3;
+ pipe->element3 = C0_G_Y; /* G */
+ pipe->element2 = C2_R_Cr; /* R */
+ pipe->element1 = C0_G_Y; /* G */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->bpp = 2; /* 2 bpp */
+ pipe->chroma_sample = MDP4_CHROMA_H2V1;
+ break;
+ case MDP_Y_CRCB_H2V1:
+ case MDP_Y_CBCR_H2V1:
+ case MDP_Y_CRCB_H2V2:
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CRCB_H1V1:
+ case MDP_Y_CBCR_H1V1:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR;
+ pipe->a_bit = 0;
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 1; /* 2 */
+ pipe->element3 = C0_G_Y; /* not used */
+ pipe->element2 = C0_G_Y; /* not used */
+ if (pipe->src_format == MDP_Y_CRCB_H2V1) {
+ pipe->element1 = C2_R_Cr; /* R */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->chroma_sample = MDP4_CHROMA_H2V1;
+ } else if (pipe->src_format == MDP_Y_CRCB_H1V1) {
+ pipe->element1 = C2_R_Cr; /* R */
+ pipe->element0 = C1_B_Cb; /* B */
+ if (pipe->src_width > YUV_444_MAX_WIDTH)
+ pipe->chroma_sample = MDP4_CHROMA_H1V2;
+ else
+ pipe->chroma_sample = MDP4_CHROMA_RGB;
+ } else if (pipe->src_format == MDP_Y_CBCR_H2V1) {
+ pipe->element1 = C1_B_Cb; /* B */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->chroma_sample = MDP4_CHROMA_H2V1;
+ } else if (pipe->src_format == MDP_Y_CBCR_H1V1) {
+ pipe->element1 = C1_B_Cb; /* B */
+ pipe->element0 = C2_R_Cr; /* R */
+ if (pipe->src_width > YUV_444_MAX_WIDTH)
+ pipe->chroma_sample = MDP4_CHROMA_H1V2;
+ else
+ pipe->chroma_sample = MDP4_CHROMA_RGB;
+ } else if (pipe->src_format == MDP_Y_CRCB_H2V2) {
+ pipe->element1 = C2_R_Cr; /* R */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->chroma_sample = MDP4_CHROMA_420;
+ } else if (pipe->src_format == MDP_Y_CBCR_H2V2) {
+ pipe->element1 = C1_B_Cb; /* B */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->chroma_sample = MDP4_CHROMA_420;
+ }
+ pipe->bpp = 2; /* 2 bpp */
+ break;
+ case MDP_Y_CBCR_H2V2_TILE:
+ case MDP_Y_CRCB_H2V2_TILE:
+ pipe->frame_format = MDP4_FRAME_FORMAT_VIDEO_SUPERTILE;
+ pipe->fetch_plane = OVERLAY_PLANE_PSEUDO_PLANAR;
+ pipe->a_bit = 0;
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 1; /* 2 */
+ pipe->element3 = C0_G_Y; /* not used */
+ pipe->element2 = C0_G_Y; /* not used */
+ if (pipe->src_format == MDP_Y_CRCB_H2V2_TILE) {
+ pipe->element1 = C2_R_Cr; /* R */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->chroma_sample = MDP4_CHROMA_420;
+ } else if (pipe->src_format == MDP_Y_CBCR_H2V2_TILE) {
+ pipe->element1 = C1_B_Cb; /* B */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->chroma_sample = MDP4_CHROMA_420;
+ }
+ pipe->bpp = 2; /* 2 bpp */
+ break;
+ case MDP_Y_CR_CB_H2V2:
+ case MDP_Y_CB_CR_H2V2:
+ pipe->frame_format = MDP4_FRAME_FORMAT_LINEAR;
+ pipe->fetch_plane = OVERLAY_PLANE_PLANAR;
+ pipe->a_bit = 0;
+ pipe->r_bit = 3; /* R, 8 bits */
+ pipe->b_bit = 3; /* B, 8 bits */
+ pipe->g_bit = 3; /* G, 8 bits */
+ pipe->alpha_enable = 0;
+ pipe->unpack_tight = 1;
+ pipe->unpack_align_msb = 0;
+ pipe->unpack_count = 1; /* 2 */
+ pipe->element3 = C0_G_Y; /* not used */
+ pipe->element2 = C0_G_Y; /* not used */
+ if (pipe->src_format == MDP_Y_CR_CB_H2V2) {
+ pipe->element1 = C2_R_Cr; /* R */
+ pipe->element0 = C1_B_Cb; /* B */
+ pipe->chroma_sample = MDP4_CHROMA_420;
+ } else if (pipe->src_format == MDP_Y_CB_CR_H2V2) {
+ pipe->element1 = C1_B_Cb; /* B */
+ pipe->element0 = C2_R_Cr; /* R */
+ pipe->chroma_sample = MDP4_CHROMA_420;
+ }
+ pipe->bpp = 2; /* 2 bpp */
+ break;
+ default:
+ /* not likely */
+ mdp4_stat.err_format++;
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+/*
+ * color_key_convert: output with 12 bits color key
+ */
+static uint32 color_key_convert(int start, int num, uint32 color)
+{
+ uint32 data;
+
+ data = (color >> start) & ((1 << num) - 1);
+
+ /* convert to 8 bits */
+ if (num == 5)
+ data = ((data << 3) | (data >> 2));
+ else if (num == 6)
+ data = ((data << 2) | (data >> 4));
+
+ /* convert 8 bits to 12 bits */
+ data = (data << 4) | (data >> 4);
+
+ return data;
+}
+
+void transp_color_key(int format, uint32 transp,
+ uint32 *c0, uint32 *c1, uint32 *c2)
+{
+ int b_start, g_start, r_start;
+ int b_num, g_num, r_num;
+
+ switch (format) {
+ case MDP_RGB_565:
+ b_start = 0;
+ g_start = 5;
+ r_start = 11;
+ r_num = 5;
+ g_num = 6;
+ b_num = 5;
+ break;
+ case MDP_RGB_888:
+ case MDP_XRGB_8888:
+ case MDP_ARGB_8888:
+ case MDP_BGRA_8888:
+ b_start = 0;
+ g_start = 8;
+ r_start = 16;
+ r_num = 8;
+ g_num = 8;
+ b_num = 8;
+ break;
+ case MDP_RGBA_8888:
+ case MDP_RGBX_8888:
+ b_start = 16;
+ g_start = 8;
+ r_start = 0;
+ r_num = 8;
+ g_num = 8;
+ b_num = 8;
+ break;
+ case MDP_BGR_565:
+ b_start = 11;
+ g_start = 5;
+ r_start = 0;
+ r_num = 5;
+ g_num = 6;
+ b_num = 5;
+ break;
+ case MDP_Y_CB_CR_H2V2:
+ case MDP_Y_CBCR_H2V2:
+ case MDP_Y_CBCR_H2V1:
+ b_start = 8;
+ g_start = 16;
+ r_start = 0;
+ r_num = 8;
+ g_num = 8;
+ b_num = 8;
+ break;
+ case MDP_Y_CR_CB_H2V2:
+ case MDP_Y_CRCB_H2V2:
+ case MDP_Y_CRCB_H2V1:
+ case MDP_Y_CRCB_H1V1:
+ case MDP_Y_CBCR_H1V1:
+ b_start = 0;
+ g_start = 16;
+ r_start = 8;
+ r_num = 8;
+ g_num = 8;
+ b_num = 8;
+ break;
+ default:
+ b_start = 0;
+ g_start = 8;
+ r_start = 16;
+ r_num = 8;
+ g_num = 8;
+ b_num = 8;
+ break;
+ }
+
+ *c0 = color_key_convert(g_start, g_num, transp);
+ *c1 = color_key_convert(b_start, b_num, transp);
+ *c2 = color_key_convert(r_start, r_num, transp);
+}
+
+uint32 mdp4_overlay_format(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 format;
+
+ format = 0;
+
+ if (pipe->solid_fill)
+ format |= MDP4_FORMAT_SOLID_FILL;
+
+ if (pipe->unpack_align_msb)
+ format |= MDP4_FORMAT_UNPACK_ALIGN_MSB;
+
+ if (pipe->unpack_tight)
+ format |= MDP4_FORMAT_UNPACK_TIGHT;
+
+ if (pipe->alpha_enable)
+ format |= MDP4_FORMAT_ALPHA_ENABLE;
+
+ if (pipe->flags & MDP_SOURCE_ROTATED_90)
+ format |= MDP4_FORMAT_90_ROTATED;
+ format |= (pipe->unpack_count << 13);
+ format |= ((pipe->bpp - 1) << 9);
+ format |= (pipe->a_bit << 6);
+ format |= (pipe->r_bit << 4);
+ format |= (pipe->b_bit << 2);
+ format |= pipe->g_bit;
+
+ format |= (pipe->frame_format << 29);
+
+ if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR ||
+ pipe->fetch_plane == OVERLAY_PLANE_PLANAR) {
+ /* video/graphic */
+ format |= (pipe->fetch_plane << 19);
+ format |= (pipe->chroma_site << 28);
+ format |= (pipe->chroma_sample << 26);
+ }
+
+ return format;
+}
+
+uint32 mdp4_overlay_unpack_pattern(struct mdp4_overlay_pipe *pipe)
+{
+ return (pipe->element3 << 24) | (pipe->element2 << 16) |
+ (pipe->element1 << 8) | pipe->element0;
+}
+
+/*
+ * mdp4_overlayproc_cfg: only be called from base layer
+ */
+void mdp4_overlayproc_cfg(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 data, intf;
+ char *overlay_base;
+
+ intf = 0;
+ if (pipe->mixer_num == MDP4_MIXER1) {
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+ intf = inpdw(MDP_BASE + 0x0038); /* MDP_DISP_INTF_SEL */
+ intf >>= 4;
+ intf &= 0x03;
+ } else
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+
+ if (mdp_is_in_isr == FALSE)
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ /*
+ * BLT only siupport at primary display
+ */
+ if (pipe->mixer_num == MDP4_MIXER0 && pipe->blt_addr) {
+ int off, bpp;
+#ifdef BLT_RGB565
+ bpp = 2; /* overlay ouput is RGB565 */
+#else
+ bpp = 3; /* overlay ouput is RGB888 */
+#endif
+ data = pipe->src_height;
+ 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 = pipe->src_height * pipe->src_width * bpp;
+ outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
+ /* LCDC - FRAME BUFFER + vsync rate */
+ outpdw(overlay_base + 0x0004, 0x02);
+ } 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);
+ }
+#ifdef BLT_RGB565
+ outpdw(overlay_base + 0x0014, 0x1); /* RGB565 */
+#else
+ outpdw(overlay_base + 0x0014, 0x0); /* RGB888 */
+#endif
+ } else {
+ data = pipe->src_height;
+ data <<= 16;
+ data |= pipe->src_width;
+ outpdw(overlay_base + 0x0008, data); /* ROI, height + width */
+ outpdw(overlay_base + 0x000c, pipe->srcp0_addr);
+ outpdw(overlay_base + 0x0010, pipe->srcp0_ystride);
+ outpdw(overlay_base + 0x0004, 0x01); /* directout */
+ }
+
+ if (pipe->mixer_num == MDP4_MIXER1) {
+ if (intf == TV_INTF) {
+ outpdw(overlay_base + 0x0014, 0x02); /* yuv422 */
+ /* overlay1 CSC config */
+ outpdw(overlay_base + 0x0200, 0x05); /* rgb->yuv */
+ }
+ }
+
+#ifdef MDP4_IGC_LUT_ENABLE
+ outpdw(overlay_base + 0x0014, 0x4); /* GC_LUT_EN, 888 */
+#endif
+
+ if (mdp_is_in_isr == FALSE)
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+int mdp4_overlay_pipe_staged(int mixer)
+{
+ uint32 data, mask, i;
+ int p1, p2;
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ data = inpdw(MDP_BASE + 0x10100);
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ p1 = 0;
+ p2 = 0;
+ for (i = 0; i < 8; i++) {
+ mask = data & 0x0f;
+ if (mask) {
+ if (mask <= 4)
+ p1++;
+ else
+ p2++;
+ }
+ data >>= 4;
+ }
+
+ if (mixer)
+ return p2;
+ else
+ return p1;
+}
+
+void mdp4_mixer_stage_up(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 data, mask, snum, stage, mixer, pnum;
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ stage = pipe->mixer_stage;
+ mixer = pipe->mixer_num;
+ pnum = pipe->pipe_num;
+
+ /* MDP_LAYERMIXER_IN_CFG, shard by both mixer 0 and 1 */
+ data = inpdw(MDP_BASE + 0x10100);
+
+ if (mixer == MDP4_MIXER1)
+ stage += 8;
+
+ if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {/* VG1 and VG2 */
+ pnum -= OVERLAY_PIPE_VG1; /* start from 0 */
+ snum = 0;
+ snum += (4 * pnum);
+ } else {
+ snum = 8;
+ snum += (4 * pnum); /* RGB1 and RGB2 */
+ }
+
+ mask = 0x0f;
+ mask <<= snum;
+ stage <<= snum;
+ data &= ~mask; /* clear old bits */
+
+ data |= stage;
+
+ outpdw(MDP_BASE + 0x10100, data); /* MDP_LAYERMIXER_IN_CFG */
+
+ data = inpdw(MDP_BASE + 0x10100);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+ ctrl->stage[pipe->mixer_num][pipe->mixer_stage] = pipe; /* keep it */
+}
+
+void mdp4_mixer_stage_down(struct mdp4_overlay_pipe *pipe)
+{
+ uint32 data, mask, snum, stage, mixer, pnum;
+
+ stage = pipe->mixer_stage;
+ mixer = pipe->mixer_num;
+ pnum = pipe->pipe_num;
+
+ if (pipe != ctrl->stage[mixer][stage]) /* not runing */
+ return;
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ /* MDP_LAYERMIXER_IN_CFG, shard by both mixer 0 and 1 */
+ data = inpdw(MDP_BASE + 0x10100);
+
+ if (mixer == MDP4_MIXER1)
+ stage += 8;
+
+ if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {/* VG1 and VG2 */
+ pnum -= OVERLAY_PIPE_VG1; /* start from 0 */
+ snum = 0;
+ snum += (4 * pnum);
+ } else {
+ snum = 8;
+ snum += (4 * pnum); /* RGB1 and RGB2 */
+ }
+
+ mask = 0x0f;
+ mask <<= snum;
+ data &= ~mask; /* clear old bits */
+
+ outpdw(MDP_BASE + 0x10100, data); /* MDP_LAYERMIXER_IN_CFG */
+
+ data = inpdw(MDP_BASE + 0x10100);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+ ctrl->stage[pipe->mixer_num][pipe->mixer_stage] = NULL; /* clear it */
+}
+
+void mdp4_mixer_blend_setup(struct mdp4_overlay_pipe *pipe)
+{
+ struct mdp4_overlay_pipe *bg_pipe;
+ unsigned char *overlay_base, *rgb_base;
+ uint32 c0, c1, c2, blend_op, constant_color = 0, rgb_src_format;
+ int off;
+
+ if (pipe->mixer_num) /* mixer number, /dev/fb0, /dev/fb1 */
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC1_BASE;/* 0x18000 */
+ else
+ overlay_base = MDP_BASE + MDP4_OVERLAYPROC0_BASE;/* 0x10000 */
+
+ /* stage 0 to stage 2 */
+ off = 0x20 * (pipe->mixer_stage - MDP4_MIXER_STAGE0);
+
+ bg_pipe = mdp4_overlay_stage_pipe(pipe->mixer_num,
+ MDP4_MIXER_STAGE_BASE);
+ if (bg_pipe == NULL) {
+ pr_err("%s: Error: no bg_pipe\n", __func__);
+ return;
+ }
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+ blend_op = 0;
+
+ if (pipe->is_fg) {
+ blend_op |= (MDP4_BLEND_FG_ALPHA_FG_CONST |
+ MDP4_BLEND_BG_ALPHA_BG_CONST);
+ outpdw(overlay_base + off + 0x108, pipe->alpha);
+ outpdw(overlay_base + off + 0x10c, 0xff - pipe->alpha);
+ if (pipe->alpha == 0xff) {
+ rgb_base = MDP_BASE + MDP4_RGB_BASE;
+ rgb_base += MDP4_RGB_OFF * bg_pipe->pipe_num;
+ rgb_src_format = inpdw(rgb_base + 0x50);
+ rgb_src_format |= MDP4_FORMAT_SOLID_FILL;
+ outpdw(rgb_base + 0x50, rgb_src_format);
+ outpdw(rgb_base + 0x1008, constant_color);
+ }
+ } else {
+ if (bg_pipe->alpha_enable && pipe->alpha_enable) {
+ /* both pipe have alpha */
+ blend_op |= (MDP4_BLEND_FG_ALPHA_BG_PIXEL |
+ MDP4_BLEND_FG_INV_ALPHA |
+ MDP4_BLEND_BG_ALPHA_BG_PIXEL);
+ } else if (bg_pipe->alpha_enable && pipe->alpha_enable == 0) {
+ /* no alpha on both pipe */
+ blend_op = (MDP4_BLEND_BG_ALPHA_BG_PIXEL |
+ MDP4_BLEND_FG_ALPHA_BG_PIXEL |
+ MDP4_BLEND_FG_INV_ALPHA);
+ }
+ }
+
+
+ if (pipe->transp != MDP_TRANSP_NOP) {
+ if (pipe->is_fg) {
+ transp_color_key(pipe->src_format, pipe->transp,
+ &c0, &c1, &c2);
+ /* Fg blocked */
+ blend_op |= MDP4_BLEND_FG_TRANSP_EN;
+ /* lower limit */
+ outpdw(overlay_base + off + 0x110,
+ (c1 << 16 | c0));/* low */
+ outpdw(overlay_base + off + 0x114, c2);/* low */
+ /* upper limit */
+ outpdw(overlay_base + off + 0x118,
+ (c1 << 16 | c0));
+ outpdw(overlay_base + off + 0x11c, c2);
+ } else {
+ transp_color_key(bg_pipe->src_format,
+ pipe->transp, &c0, &c1, &c2);
+ /* bg blocked */
+ blend_op |= MDP4_BLEND_BG_TRANSP_EN;
+ /* lower limit */
+ outpdw(overlay_base + 0x180,
+ (c1 << 16 | c0));/* low */
+ outpdw(overlay_base + 0x184, c2);/* low */
+ /* upper limit */
+ outpdw(overlay_base + 0x188,
+ (c1 << 16 | c0));/* high */
+ outpdw(overlay_base + 0x18c, c2);/* high */
+ }
+ }
+
+ outpdw(overlay_base + off + 0x104, blend_op);
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+void mdp4_overlay_reg_flush(struct mdp4_overlay_pipe *pipe, int all)
+{
+ struct mdp4_overlay_pipe *bg_pipe;
+ uint32 bits = 0;
+
+ if (pipe->mixer_num == MDP4_MIXER1)
+ bits |= 0x02;
+ else
+ bits |= 0x01;
+
+ if (all) {
+ if (pipe->pipe_num <= OVERLAY_PIPE_RGB2) {
+ if (pipe->pipe_num == OVERLAY_PIPE_RGB2)
+ bits |= 0x20;
+ else
+ bits |= 0x10;
+ } else {
+ if (pipe->is_fg && pipe->alpha == 0xFF) {
+ bg_pipe = mdp4_overlay_stage_pipe(
+ pipe->mixer_num,
+ MDP4_MIXER_STAGE_BASE);
+ if (bg_pipe->pipe_num <= OVERLAY_PIPE_RGB2) {
+ if (bg_pipe->pipe_num ==
+ OVERLAY_PIPE_RGB2)
+ bits |= 0x20;
+ else
+ bits |= 0x10;
+ }
+ }
+ if (pipe->pipe_num == OVERLAY_PIPE_VG2)
+ bits |= 0x08;
+ else
+ bits |= 0x04;
+ }
+ }
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ outpdw(MDP_BASE + 0x18000, bits); /* MDP_OVERLAY_REG_FLUSH */
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+}
+
+struct mdp4_overlay_pipe *mdp4_overlay_stage_pipe(int mixer, int stage)
+{
+ return ctrl->stage[mixer][stage];
+}
+
+struct mdp4_overlay_pipe *mdp4_overlay_ndx2pipe(int ndx)
+{
+ struct mdp4_overlay_pipe *pipe;
+
+ if (ndx <= 0 || ndx > MDP4_MAX_PIPE)
+ return NULL;
+
+ pipe = &ctrl->plist[ndx - 1]; /* ndx start from 1 */
+
+ if (pipe->pipe_used == 0)
+ return NULL;
+
+ return pipe;
+}
+
+struct mdp4_overlay_pipe *mdp4_overlay_pipe_alloc(
+ int ptype, int mixer, int req_share)
+{
+ int i, j, ndx, found;
+ struct mdp4_overlay_pipe *pipe, *opipe;
+ struct mdp4_pipe_desc *pd;
+
+ found = 0;
+ pipe = &ctrl->plist[0];
+
+ for (i = 0; i < MDP4_MAX_PIPE; i++) {
+ if (pipe->pipe_type == ptype && pipe->pipe_used == 0) {
+ pd = &ctrl->ov_pipe[pipe->pipe_num];
+ if (pd->share) { /* pipe can be shared */
+ if (pd->ref_cnt == 0) {
+ /* not yet been used */
+ found++;
+ break;
+ }
+ /* pipe occupied already */
+ if (req_share && pd->ref_cnt < MDP4_MAX_SHARE) {
+ for (j = 0; j < MDP4_MAX_SHARE; j++) {
+ ndx = pd->ndx_list[j];
+ if (ndx != 0)
+ break;
+ }
+ /* ndx satrt from 1 */
+ opipe = &ctrl->plist[ndx - 1];
+ /*
+ * occupied pipe willing to share and
+ * same mixer
+ */
+ if (opipe->pipe_share &&
+ opipe->mixer_num == mixer) {
+ found++;
+ break;
+ }
+ }
+ } else { /* not a shared pipe */
+ if (req_share == 0 && pd->ref_cnt == 0) {
+ found++;
+ break;
+ }
+ }
+ }
+ pipe++;
+ }
+
+ if (found) {
+ init_completion(&pipe->comp);
+ init_completion(&pipe->dmas_comp);
+ pr_info("%s: pipe=%x ndx=%d num=%d share=%d cnt=%d\n",
+ __func__, (int)pipe, pipe->pipe_ndx, pipe->pipe_num,
+ pd->share, pd->ref_cnt);
+ return pipe;
+ }
+
+ pr_debug("%s: ptype=%d mixer=%d req_share=%d FAILED\n",
+ __func__, ptype, mixer, req_share);
+
+ return NULL;
+}
+
+
+void mdp4_overlay_pipe_free(struct mdp4_overlay_pipe *pipe)
+{
+ int i;
+ uint32 ptype, num, ndx;
+ struct mdp4_pipe_desc *pd;
+
+ pr_debug("%s: pipe=%x ndx=%d\n", __func__,
+ (int)pipe, pipe->pipe_ndx);
+ pd = &ctrl->ov_pipe[pipe->pipe_num];
+ if (pd->ref_cnt) {
+ pd->ref_cnt--;
+ for (i = 0; i < MDP4_MAX_SHARE; i++) {
+ if (pd->ndx_list[i] == pipe->pipe_ndx) {
+ pd->ndx_list[i] = 0;
+ break;
+ }
+ }
+ }
+
+ pd->player = NULL;
+
+ ptype = pipe->pipe_type;
+ num = pipe->pipe_num;
+ ndx = pipe->pipe_ndx;
+
+ memset(pipe, 0, sizeof(*pipe));
+
+ pipe->pipe_type = ptype;
+ pipe->pipe_num = num;
+ pipe->pipe_ndx = ndx;
+}
+
+int mdp4_overlay_req_check(uint32 id, uint32 z_order, uint32 mixer)
+{
+ struct mdp4_overlay_pipe *pipe;
+
+ pipe = ctrl->stage[mixer][z_order];
+
+ if (pipe == NULL)
+ return 0;
+
+ if (pipe->pipe_ndx == id) /* same req, recycle */
+ return 0;
+
+ if (id == MSMFB_NEW_REQUEST) { /* new request */
+ if (pipe->pipe_num >= OVERLAY_PIPE_VG1) /* share pipe */
+ return 0;
+ }
+
+ return -EPERM;
+}
+
+static int mdp4_overlay_validate_downscale(struct mdp_overlay *req,
+ struct msm_fb_data_type *mfd, uint32 perf_level, uint32 pclk_rate)
+{
+ __u32 panel_clk_khz, mdp_clk_khz;
+ __u32 num_hsync_pix_clks, mdp_clks_per_hsync, src_wh;
+ __u32 hsync_period_ps, mdp_period_ps, total_hsync_period_ps;
+ unsigned long fill_rate_y_dir, fill_rate_x_dir;
+ unsigned long fillratex100, mdp_pixels_produced;
+ unsigned long mdp_clk_hz;
+
+ pr_debug("%s: LCDC Mode Downscale validation with MDP Core"
+ " Clk rate\n", __func__);
+ pr_debug("src_w %u, src_h %u, dst_w %u, dst_h %u\n",
+ req->src_rect.w, req->src_rect.h, req->dst_rect.w,
+ req->dst_rect.h);
+
+
+ panel_clk_khz = pclk_rate/1000;
+ mdp_clk_hz = mdp_perf_level2clk_rate(perf_level);
+
+ if (!mdp_clk_hz) {
+ pr_debug("mdp_perf_level2clk_rate returned 0,"
+ "Downscale Validation incomplete\n");
+ return 0;
+ }
+
+ mdp_clk_khz = mdp_clk_hz/1000;
+
+ num_hsync_pix_clks = mfd->panel_info.lcdc.h_back_porch +
+ mfd->panel_info.lcdc.h_front_porch +
+ mfd->panel_info.lcdc.h_pulse_width +
+ mfd->panel_info.xres;
+
+ hsync_period_ps = 1000000000/panel_clk_khz;
+ mdp_period_ps = 1000000000/mdp_clk_khz;
+
+ total_hsync_period_ps = num_hsync_pix_clks * hsync_period_ps;
+ mdp_clks_per_hsync = total_hsync_period_ps/mdp_period_ps;
+
+ pr_debug("hsync_period_ps %u, mdp_period_ps %u,"
+ "total_hsync_period_ps %u\n", hsync_period_ps,
+ mdp_period_ps, total_hsync_period_ps);
+
+ src_wh = req->src_rect.w * req->src_rect.h;
+ if (src_wh % req->dst_rect.h)
+ fill_rate_y_dir = (src_wh / req->dst_rect.h) + 1;
+ else
+ fill_rate_y_dir = (src_wh / req->dst_rect.h);
+
+ fill_rate_x_dir = (mfd->panel_info.xres - req->dst_rect.w)
+ + req->src_rect.w;
+
+ if (fill_rate_y_dir >= fill_rate_x_dir)
+ fillratex100 = 100 * fill_rate_y_dir / mfd->panel_info.xres;
+ else
+ fillratex100 = 100 * fill_rate_x_dir / mfd->panel_info.xres;
+
+ pr_debug("mdp_clks_per_hsync %u, fill_rate_y_dir %lu,"
+ "fill_rate_x_dir %lu\n", mdp_clks_per_hsync,
+ fill_rate_y_dir, fill_rate_x_dir);
+
+ mdp_pixels_produced = 100 * mdp_clks_per_hsync/fillratex100;
+ pr_debug("fillratex100 %lu, mdp_pixels_produced %lu\n",
+ fillratex100, mdp_pixels_produced);
+ if (mdp_pixels_produced <= mfd->panel_info.xres) {
+ pr_err("%s(): LCDC underflow detected during downscale\n",
+ __func__);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static int mdp4_overlay_req2pipe(struct mdp_overlay *req, int mixer,
+ struct mdp4_overlay_pipe **ppipe,
+ struct msm_fb_data_type *mfd)
+{
+ struct mdp4_overlay_pipe *pipe;
+ struct mdp4_pipe_desc *pd;
+ int ret, ptype, req_share;
+ int j;
+
+ if (mfd == NULL) {
+ pr_err("%s: mfd == NULL, -ENODEV\n", __func__);
+ return -ENODEV;
+ }
+
+ if (mixer >= MDP4_MAX_MIXER) {
+ pr_err("%s: mixer out of range!\n", __func__);
+ mdp4_stat.err_mixer++;
+ return -ERANGE;
+ }
+
+ if (req->z_order < 0 || req->z_order > 2) {
+ pr_err("%s: z_order=%d out of range!\n", __func__,
+ req->z_order);
+ mdp4_stat.err_zorder++;
+ return -ERANGE;
+ }
+
+ if (req->src_rect.h == 0 || req->src_rect.w == 0) {
+ pr_err("%s: src img of zero size!\n", __func__);
+ mdp4_stat.err_size++;
+ return -EINVAL;
+ }
+
+
+ if (req->dst_rect.h > (req->src_rect.h * 8)) { /* too much */
+ mdp4_stat.err_scale++;
+ pr_err("%s: scale up, too much (h)!\n", __func__);
+ return -ERANGE;
+ }
+
+ if (req->src_rect.h > (req->dst_rect.h * 8)) { /* too little */
+ mdp4_stat.err_scale++;
+ pr_err("%s: scale down, too little (h)!\n", __func__);
+ return -ERANGE;
+ }
+
+ if (req->dst_rect.w > (req->src_rect.w * 8)) { /* too much */
+ mdp4_stat.err_scale++;
+ pr_err("%s: scale up, too much (w)!\n", __func__);
+ return -ERANGE;
+ }
+
+ if (req->src_rect.w > (req->dst_rect.w * 8)) { /* too little */
+ mdp4_stat.err_scale++;
+ pr_err("%s: scale down, too little (w)!\n", __func__);
+ return -ERANGE;
+ }
+
+ if (mdp_hw_revision == MDP4_REVISION_V1) {
+ /* non integer down saceling ratio smaller than 1/4
+ * is not supportted
+ */
+ if (req->src_rect.h > (req->dst_rect.h * 4)) {
+ if (req->src_rect.h % req->dst_rect.h) {
+ mdp4_stat.err_scale++;
+ pr_err("%s: need integer (h)!\n", __func__);
+ return -ERANGE;
+ }
+ }
+
+ if (req->src_rect.w > (req->dst_rect.w * 4)) {
+ if (req->src_rect.w % req->dst_rect.w) {
+ mdp4_stat.err_scale++;
+ pr_err("%s: need integer (w)!\n", __func__);
+ return -ERANGE;
+ }
+ }
+ }
+
+ if (((req->src_rect.x + req->src_rect.w) > req->src.width) ||
+ ((req->src_rect.y + req->src_rect.h) > req->src.height)) {
+ mdp4_stat.err_size++;
+ pr_err("%s invalid src rectangle\n", __func__);
+ return -ERANGE;
+ }
+
+ if (ctrl->panel_3d != MDP4_3D_SIDE_BY_SIDE) {
+ int xres;
+ int yres;
+
+ xres = mfd->panel_info.xres;
+ yres = mfd->panel_info.yres;
+
+ if (((req->dst_rect.x + req->dst_rect.w) > xres) ||
+ ((req->dst_rect.y + req->dst_rect.h) > yres)) {
+ mdp4_stat.err_size++;
+ pr_err("%s invalid dst rectangle\n", __func__);
+ return -ERANGE;
+ }
+ }
+
+ ptype = mdp4_overlay_format2type(req->src.format);
+ if (ptype < 0) {
+ pr_err("%s: mdp4_overlay_format2type!\n", __func__);
+ return ptype;
+ }
+
+ req_share = (req->flags & MDP_OV_PIPE_SHARE);
+
+ if (req->id == MSMFB_NEW_REQUEST) /* new request */
+ pipe = mdp4_overlay_pipe_alloc(ptype, mixer, req_share);
+ else
+ pipe = mdp4_overlay_ndx2pipe(req->id);
+
+ if (pipe == NULL) {
+ pr_err("%s: pipe == NULL!\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* no down scale at rgb pipe */
+ if (pipe->pipe_num <= OVERLAY_PIPE_RGB2) {
+ if ((req->src_rect.h > req->dst_rect.h) ||
+ (req->src_rect.w > req->dst_rect.w)) {
+ pr_err("%s: h>h || w>w!\n", __func__);
+ return -ERANGE;
+ }
+ }
+
+ pipe->src_format = req->src.format;
+ ret = mdp4_overlay_format2pipe(pipe);
+ if (ret < 0) {
+ pr_err("%s: mdp4_overlay_format2pipe!\n", __func__);
+ return ret;
+ }
+
+ /*
+ * base layer == 1, reserved for frame buffer
+ * zorder 0 == stage 0 == 2
+ * zorder 1 == stage 1 == 3
+ * zorder 2 == stage 2 == 4
+ */
+ if (req->id == MSMFB_NEW_REQUEST) { /* new request */
+ pd = &ctrl->ov_pipe[pipe->pipe_num];
+ for (j = 0; j < MDP4_MAX_SHARE; j++) {
+ if (pd->ndx_list[j] == 0) {
+ pd->ndx_list[j] = pipe->pipe_ndx;
+ break;
+ }
+ }
+ pipe->pipe_share = req_share;
+ pd->ref_cnt++;
+ pipe->pipe_used++;
+ pipe->mixer_num = mixer;
+ pipe->mixer_stage = req->z_order + MDP4_MIXER_STAGE0;
+ pr_debug("%s: zorder=%d pipe ndx=%d num=%d\n", __func__,
+ req->z_order, pipe->pipe_ndx, pipe->pipe_num);
+
+ }
+
+ pipe->src_width = req->src.width & 0x07ff; /* source img width */
+ pipe->src_height = req->src.height & 0x07ff; /* source img height */
+ pipe->src_h = req->src_rect.h & 0x07ff;
+ pipe->src_w = req->src_rect.w & 0x07ff;
+ pipe->src_y = req->src_rect.y & 0x07ff;
+ pipe->src_x = req->src_rect.x & 0x07ff;
+ pipe->dst_h = req->dst_rect.h & 0x07ff;
+ pipe->dst_w = req->dst_rect.w & 0x07ff;
+ pipe->dst_y = req->dst_rect.y & 0x07ff;
+ pipe->dst_x = req->dst_rect.x & 0x07ff;
+
+ pipe->op_mode = 0;
+
+ if (req->flags & MDP_FLIP_LR)
+ pipe->op_mode |= MDP4_OP_FLIP_LR;
+
+ if (req->flags & MDP_FLIP_UD)
+ pipe->op_mode |= MDP4_OP_FLIP_UD;
+
+ if (req->flags & MDP_DITHER)
+ pipe->op_mode |= MDP4_OP_DITHER_EN;
+
+ if (req->flags & MDP_DEINTERLACE)
+ pipe->op_mode |= MDP4_OP_DEINT_EN;
+
+ if (req->flags & MDP_DEINTERLACE_ODD)
+ pipe->op_mode |= MDP4_OP_DEINT_ODD_REF;
+
+ pipe->is_fg = req->is_fg;/* control alpha and color key */
+
+ pipe->alpha = req->alpha & 0x0ff;
+
+ pipe->transp = req->transp_mask;
+
+ *ppipe = pipe;
+
+ return 0;
+}
+
+static int get_img(struct msmfb_data *img, struct fb_info *info,
+ unsigned long *start, unsigned long *len, struct file **pp_file)
+{
+ int put_needed, ret = 0, fb_num;
+ struct file *file;
+#ifdef CONFIG_ANDROID_PMEM
+ unsigned long vstart;
+#endif
+
+ if (img->flags & MDP_BLIT_SRC_GEM) {
+ *pp_file = NULL;
+ return kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
+ start, len);
+ }
+
+#ifdef CONFIG_ANDROID_PMEM
+ if (!get_pmem_file(img->memory_id, start, &vstart, len, pp_file))
+ return 0;
+#endif
+ file = fget_light(img->memory_id, &put_needed);
+ if (file == NULL)
+ return -1;
+
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
+ fb_num = MINOR(file->f_dentry->d_inode->i_rdev);
+ if (get_fb_phys_info(start, len, fb_num))
+ ret = -1;
+ else
+ *pp_file = file;
+ } else
+ ret = -1;
+ if (ret)
+ fput_light(file, put_needed);
+ return ret;
+}
+
+int mdp4_overlay_3d(struct fb_info *info, struct msmfb_overlay_3d *req)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ int ret = -EPERM;
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+ return -EINTR;
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ /* Only dsi_cmd panel support 3D */
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+ mdp4_dsi_cmd_3d(mfd, req);
+ ret = 0;
+ }
+#endif
+ mutex_unlock(&mfd->dma->ov_mutex);
+
+ return ret;
+}
+
+#ifdef CONFIG_FB_MSM_OVERLAY_WRITEBACK
+int mdp4_overlay_blt(struct fb_info *info, struct msmfb_overlay_blt *req)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+ if (mfd == NULL)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+ return -EINTR;
+
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
+ mdp4_dsi_overlay_blt(mfd, req);
+ else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO)
+ mdp4_dsi_video_overlay_blt(mfd, req);
+ else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
+ mdp4_lcdc_overlay_blt(mfd, req);
+
+ mutex_unlock(&mfd->dma->ov_mutex);
+
+ return 0;
+}
+
+int mdp4_overlay_blt_offset(struct fb_info *info, struct msmfb_overlay_blt *req)
+{
+ int ret = 0;
+
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+ return -EINTR;
+
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
+ ret = mdp4_dsi_overlay_blt_offset(mfd, req);
+ else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO)
+ ret = mdp4_dsi_video_overlay_blt_offset(mfd, req);
+ else if (ctrl->panel_mode & MDP4_PANEL_LCDC)
+ ret = mdp4_lcdc_overlay_blt_offset(mfd, req);
+
+ mutex_unlock(&mfd->dma->ov_mutex);
+
+ return ret;
+}
+#endif
+
+int mdp4_overlay_get(struct fb_info *info, struct mdp_overlay *req)
+{
+ struct mdp4_overlay_pipe *pipe;
+
+ pipe = mdp4_overlay_ndx2pipe(req->id);
+ if (pipe == NULL)
+ return -ENODEV;
+
+ *req = pipe->req_data;
+
+ return 0;
+}
+
+#define OVERLAY_VGA_SIZE 0x04B000
+#define OVERLAY_720P_TILE_SIZE 0x0E6000
+#define OVERLAY_WSVGA_SIZE 0x98000 /* 1024x608, align 600 to 32bit */
+#define OVERLAY_PERF_LEVEL1 1
+#define OVERLAY_PERF_LEVEL2 2
+#define OVERLAY_PERF_LEVEL3 3
+#define OVERLAY_PERF_LEVEL4 4
+
+#ifdef CONFIG_MSM_BUS_SCALING
+#define OVERLAY_BUS_SCALE_TABLE_BASE 6
+#endif
+
+static int mdp4_overlay_is_rgb_type(int format)
+{
+ switch (format) {
+ case MDP_RGB_565:
+ case MDP_RGB_888:
+ case MDP_BGR_565:
+ case MDP_XRGB_8888:
+ case MDP_ARGB_8888:
+ case MDP_RGBA_8888:
+ case MDP_BGRA_8888:
+ case MDP_RGBX_8888:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static uint32 mdp4_overlay_get_perf_level(struct mdp_overlay *req)
+{
+ int is_fg;
+
+ if (req->is_fg && ((req->alpha & 0x0ff) == 0xff))
+ is_fg = 1;
+
+ if (req->flags & MDP_DEINTERLACE)
+ return OVERLAY_PERF_LEVEL1;
+
+ if (mdp4_overlay_is_rgb_type(req->src.format) && is_fg &&
+ ((req->src.width * req->src.height) <= OVERLAY_WSVGA_SIZE))
+ return OVERLAY_PERF_LEVEL4;
+ else if (mdp4_overlay_is_rgb_type(req->src.format))
+ return OVERLAY_PERF_LEVEL1;
+
+ if (ctrl->ov_pipe[OVERLAY_PIPE_VG1].ref_cnt &&
+ ctrl->ov_pipe[OVERLAY_PIPE_VG2].ref_cnt)
+ return OVERLAY_PERF_LEVEL1;
+
+ if (req->src.width*req->src.height <= OVERLAY_VGA_SIZE)
+ return OVERLAY_PERF_LEVEL3;
+ else if (req->src.width*req->src.height <= OVERLAY_720P_TILE_SIZE)
+ return OVERLAY_PERF_LEVEL2;
+ else
+ return OVERLAY_PERF_LEVEL1;
+}
+
+int mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ int ret, mixer;
+ struct mdp4_overlay_pipe *pipe;
+
+ if (mfd == NULL) {
+ pr_err("%s: mfd == NULL, -ENODEV\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!mfd->panel_power_on) /* suspended */
+ return -EPERM;
+
+ if (req->src.format == MDP_FB_FORMAT)
+ req->src.format = mfd->fb_imgType;
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex)) {
+ pr_err("%s: mutex_lock_interruptible, -EINTR\n", __func__);
+ return -EINTR;
+ }
+
+ perf_level = mdp4_overlay_get_perf_level(req);
+
+ if ((mfd->panel_info.type == LCDC_PANEL) &&
+ (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;
+ }
+ }
+ if ((mfd->panel_info.type == MIPI_VIDEO_PANEL) &&
+ (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.mipi)->dsi_pclk_rate)) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return -ERANGE;
+ }
+ }
+ mixer = mfd->panel_info.pdest; /* DISPLAY_1 or DISPLAY_2 */
+
+ ret = mdp4_overlay_req2pipe(req, mixer, &pipe, mfd);
+ if (ret < 0) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ pr_err("%s: mdp4_overlay_req2pipe, ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ /*
+ * writeback (blt) mode to provide work around for
+ * dsi cmd mode interface hardware bug.
+ */
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+ if (mixer == MDP4_MIXER0 && req->dst_rect.x != 0) {
+ mdp4_dsi_blt_dmap_busy_wait(mfd);
+ mdp4_dsi_overlay_blt_start(mfd);
+ }
+ }
+#endif
+
+ /* return id back to user */
+ req->id = pipe->pipe_ndx; /* pipe_ndx start from 1 */
+ pipe->req_data = *req; /* keep original req */
+
+ pipe->flags = req->flags;
+
+ if (pipe->flags & MDP_SHARPENING) {
+ bool test = ((pipe->req_data.dpp.sharp_strength > 0) &&
+ ((req->src_rect.w > req->dst_rect.w) &&
+ (req->src_rect.h > req->dst_rect.h)));
+ if (test) {
+ pr_warn("%s: No sharpening while downscaling.\n",
+ __func__);
+ pipe->flags &= ~MDP_SHARPENING;
+ }
+ }
+
+ mdp4_stat.overlay_set[pipe->mixer_num]++;
+
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ if (mdp_hw_revision == MDP4_REVISION_V2_1 &&
+ pipe->mixer_num == MDP4_MIXER0)
+ mdp4_overlay_status_write(MDP4_OVERLAY_TYPE_SET, true);
+ }
+
+ mdp4_del_res_rel = 0;
+ mutex_unlock(&mfd->dma->ov_mutex);
+ mdp_set_core_clk(perf_level);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+ if (pipe->mixer_num == MDP4_MIXER0) {
+ mdp_bus_scale_update_request(OVERLAY_BUS_SCALE_TABLE_BASE
+ - perf_level);
+ }
+#endif
+
+ return 0;
+}
+
+void mdp4_overlay_resource_release(void)
+{
+ if (mdp4_del_res_rel) {
+ mdp_set_core_clk(OVERLAY_PERF_LEVEL4);
+ mdp4_del_res_rel = 0;
+ }
+}
+
+int mdp4_overlay_unset(struct fb_info *info, int ndx)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct mdp4_overlay_pipe *pipe;
+ uint32 flags;
+
+ if (mfd == NULL)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+ return -EINTR;
+
+ pipe = mdp4_overlay_ndx2pipe(ndx);
+
+ if (pipe == NULL) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return -ENODEV;
+ }
+
+ if (pipe->mixer_num == MDP4_MIXER1)
+ ctrl->mixer1_played = 0;
+ else {
+ /* mixer 0 */
+ ctrl->mixer0_played = 0;
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+ if (mfd->panel_power_on) {
+ mdp4_dsi_blt_dmap_busy_wait(mfd);
+ }
+ }
+#else
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ if (mfd->panel_power_on)
+ mdp4_mddi_dma_busy_wait(mfd);
+ }
+#endif
+ }
+
+ mdp4_mixer_stage_down(pipe);
+
+ if (pipe->mixer_num == MDP4_MIXER0) {
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+ if (mfd->panel_power_on)
+ if (mdp4_dsi_overlay_blt_stop(mfd) == 0)
+ mdp4_dsi_cmd_overlay_restore();
+ } else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
+ flags = pipe->flags;
+ pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+ mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
+ pipe->flags = flags;
+ }
+#else
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ if (mdp_hw_revision == MDP4_REVISION_V2_1)
+ mdp4_overlay_status_write(
+ MDP4_OVERLAY_TYPE_UNSET, true);
+ if (mfd->panel_power_on)
+ mdp4_mddi_overlay_restore();
+ }
+#endif
+ else if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
+ flags = pipe->flags;
+ pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+ mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+ pipe->flags = flags;
+ }
+ }
+#ifdef CONFIG_FB_MSM_DTV
+ else { /* mixer1, DTV, ATV */
+ flags = pipe->flags;
+ pipe->flags &= ~MDP_OV_PLAY_NOWAIT;
+ mdp4_overlay_dtv_vsync_push(mfd, pipe);
+ pipe->flags = flags;
+ }
+#endif
+
+ mdp4_stat.overlay_unset[pipe->mixer_num]++;
+
+ mdp4_overlay_pipe_free(pipe);
+
+ if (!(ctrl->ov_pipe[OVERLAY_PIPE_VG1].ref_cnt +
+ ctrl->ov_pipe[OVERLAY_PIPE_VG2].ref_cnt))
+ mdp4_del_res_rel = 1;
+
+ mutex_unlock(&mfd->dma->ov_mutex);
+
+#ifdef CONFIG_MSM_BUS_SCALING
+ if (pipe->mixer_num == MDP4_MIXER0)
+ if (mfd->panel_power_on)
+ mdp_bus_scale_update_request(2);
+#endif
+ return 0;
+}
+
+struct tile_desc {
+ uint32 width; /* tile's width */
+ uint32 height; /* tile's height */
+ uint32 row_tile_w; /* tiles per row's width */
+ uint32 row_tile_h; /* tiles per row's height */
+};
+
+void tile_samsung(struct tile_desc *tp)
+{
+ /*
+ * each row of samsung tile consists of two tiles in height
+ * and two tiles in width which means width should align to
+ * 64 x 2 bytes and height should align to 32 x 2 bytes.
+ * video decoder generate two tiles in width and one tile
+ * in height which ends up height align to 32 X 1 bytes.
+ */
+ tp->width = 64; /* 64 bytes */
+ tp->row_tile_w = 2; /* 2 tiles per row's width */
+ tp->height = 32; /* 32 bytes */
+ tp->row_tile_h = 1; /* 1 tiles per row's height */
+}
+
+uint32 tile_mem_size(struct mdp4_overlay_pipe *pipe, struct tile_desc *tp)
+{
+ uint32 tile_w, tile_h;
+ uint32 row_num_w, row_num_h;
+
+
+ tile_w = tp->width * tp->row_tile_w;
+ tile_h = tp->height * tp->row_tile_h;
+
+ row_num_w = (pipe->src_width + tile_w - 1) / tile_w;
+ row_num_h = (pipe->src_height + tile_h - 1) / tile_h;
+ return ((row_num_w * row_num_h * tile_w * tile_h) + 8191) & ~8191;
+}
+
+int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req,
+ struct file **pp_src_file, struct file **pp_src_plane1_file,
+ struct file **pp_src_plane2_file)
+{
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct msmfb_data *img;
+ struct mdp4_overlay_pipe *pipe;
+ struct mdp4_pipe_desc *pd;
+ ulong start, addr;
+ ulong len = 0;
+ struct file *p_src_file = 0;
+ struct file *p_src_plane1_file = 0, *p_src_plane2_file = 0;
+ uint32_t overlay_version = 0;
+
+ if (mfd == NULL)
+ return -ENODEV;
+
+ if (!mfd->panel_power_on) /* suspended */
+ return -EPERM;
+
+ pipe = mdp4_overlay_ndx2pipe(req->id);
+ if (pipe == NULL) {
+ pr_err("%s: req_id=%d Error\n", __func__, req->id);
+ return -ENODEV;
+ }
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+ return -EINTR;
+
+ pd = &ctrl->ov_pipe[pipe->pipe_num];
+ if (pd->player && pipe != pd->player) {
+ if (pipe->pipe_type == OVERLAY_TYPE_RGB) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return 0; /* ignore it, kicked out already */
+ }
+ }
+
+ pd->player = pipe; /* keep */
+
+ img = &req->data;
+ get_img(img, info, &start, &len, &p_src_file);
+ if (len == 0) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ pr_err("%s: pmem Error\n", __func__);
+ return -1;
+ }
+ *pp_src_file = p_src_file;
+
+ addr = start + img->offset;
+ pipe->srcp0_addr = addr;
+ pipe->srcp0_ystride = pipe->src_width * pipe->bpp;
+
+ if ((req->version_key & VERSION_KEY_MASK) == 0xF9E8D700)
+ overlay_version = (req->version_key & ~VERSION_KEY_MASK);
+
+ if (pipe->fetch_plane == OVERLAY_PLANE_PSEUDO_PLANAR) {
+ if (overlay_version > 0) {
+ img = &req->plane1_data;
+ get_img(img, info, &start, &len, &p_src_plane1_file);
+ if (len == 0) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ pr_err("%s: Error to get plane1\n", __func__);
+ return -EINVAL;
+ }
+ pipe->srcp1_addr = start + img->offset;
+ *pp_src_plane1_file = p_src_plane1_file;
+ } else if (pipe->frame_format ==
+ MDP4_FRAME_FORMAT_VIDEO_SUPERTILE) {
+ struct tile_desc tile;
+
+ tile_samsung(&tile);
+ pipe->srcp1_addr = addr + tile_mem_size(pipe, &tile);
+ } else {
+ pipe->srcp1_addr = addr + (pipe->src_width *
+ pipe->src_height);
+ }
+ pipe->srcp0_ystride = pipe->src_width;
+ if ((pipe->src_format == MDP_Y_CRCB_H1V1) ||
+ (pipe->src_format == MDP_Y_CBCR_H1V1)) {
+ if (pipe->src_width > YUV_444_MAX_WIDTH)
+ pipe->srcp1_ystride = pipe->src_width << 2;
+ else
+ pipe->srcp1_ystride = pipe->src_width << 1;
+ } else
+ pipe->srcp1_ystride = pipe->src_width;
+
+ } else if (pipe->fetch_plane == OVERLAY_PLANE_PLANAR) {
+ if (overlay_version > 0) {
+ img = &req->plane1_data;
+ get_img(img, info, &start, &len, &p_src_plane1_file);
+ if (len == 0) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ pr_err("%s: Error to get plane1\n", __func__);
+ return -EINVAL;
+ }
+ pipe->srcp1_addr = start + img->offset;
+ *pp_src_plane1_file = p_src_plane1_file;
+
+ img = &req->plane2_data;
+ get_img(img, info, &start, &len, &p_src_plane2_file);
+ if (len == 0) {
+ mutex_unlock(&mfd->dma->ov_mutex);
+ pr_err("%s: Error to get plane2\n", __func__);
+ return -EINVAL;
+ }
+ pipe->srcp2_addr = start + img->offset;
+ *pp_src_plane2_file = p_src_plane2_file;
+ } else {
+ addr += (pipe->src_width * pipe->src_height);
+ pipe->srcp1_addr = addr;
+ addr += ((pipe->src_width / 2) *
+ (pipe->src_height / 2));
+ pipe->srcp2_addr = addr;
+ }
+ pipe->srcp0_ystride = pipe->src_width;
+ pipe->srcp1_ystride = pipe->src_width / 2;
+ pipe->srcp2_ystride = pipe->src_width / 2;
+ }
+
+ if (pipe->pipe_num >= OVERLAY_PIPE_VG1)
+ mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */
+ else {
+ if (pipe->flags & MDP_SHARPENING) {
+ pr_warn(
+ "%s: Sharpening/Smoothing not supported on RGB pipe\n",
+ __func__);
+ pipe->flags &= ~MDP_SHARPENING;
+ }
+ mdp4_overlay_rgb_setup(pipe); /* rgb pipe */
+ }
+
+ mdp4_mixer_blend_setup(pipe);
+ mdp4_mixer_stage_up(pipe);
+
+ if (pipe->mixer_num == MDP4_MIXER1) {
+ ctrl->mixer1_played++;
+ /* enternal interface */
+ if (ctrl->panel_mode & MDP4_PANEL_DTV)
+#ifdef CONFIG_FB_MSM_DTV
+ mdp4_overlay_dtv_ov_done_push(mfd, pipe);
+#else
+ mdp4_overlay_reg_flush(pipe, 1);
+#endif
+ else if (ctrl->panel_mode & MDP4_PANEL_ATV)
+ mdp4_overlay_reg_flush(pipe, 1);
+ } else {
+ /* primary interface */
+ ctrl->mixer0_played++;
+ if (ctrl->panel_mode & MDP4_PANEL_LCDC)
+ mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO)
+ mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
+#endif
+ else {
+ /* mddi & mipi dsi cmd mode */
+ if (pipe->flags & MDP_OV_PLAY_NOWAIT) {
+ mdp4_stat.overlay_play[pipe->mixer_num]++;
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return 0;
+ }
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+ mdp4_dsi_cmd_dma_busy_wait(mfd);
+ mdp4_dsi_cmd_kickoff_video(mfd, pipe);
+ }
+#else
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ mdp4_mddi_dma_busy_wait(mfd);
+ mdp4_mddi_kickoff_video(mfd, pipe);
+ }
+#endif
+ }
+ }
+
+ mdp4_stat.overlay_play[pipe->mixer_num]++;
+
+ mutex_unlock(&mfd->dma->ov_mutex);
+
+ return 0;
+}