video:msm: Add IGC LUT functionality to PP IOCTL
This patch adds the functionality to write to the IGC Look Up
Tables (LUTs) in the MDP.
Change-Id: I4789820a56748b1a5e33ee4ca979e418170695a3
Signed-off-by: Carl Vanderlip <carlv@codeaurora.org>
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 9a40066..f7f48e4 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -722,4 +722,5 @@
void mdp4_init_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
+int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg);
#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 1662e0c..3ca8c9c 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -277,6 +277,7 @@
void mdp4_overlay_dmap_cfg(struct msm_fb_data_type *mfd, int lcdc)
{
uint32 dma2_cfg_reg;
+ uint32 mask, curr;
dma2_cfg_reg = DMA_DITHER_EN;
#ifdef BLT_RGB565
@@ -309,6 +310,9 @@
#endif
/* dma2 config register */
+ curr = inpdw(MDP_BASE + 0x90000);
+ mask = 0xBFFFFFFF;
+ dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask);
MDP_OUTP(MDP_BASE + 0x90000, dma2_cfg_reg);
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
@@ -442,6 +446,7 @@
char *rgb_base;
uint32 src_size, src_xy, dst_size, dst_xy;
uint32 format, pattern;
+ uint32 curr, mask;
uint32 offset = 0;
int pnum;
@@ -473,6 +478,12 @@
mdp4_scale_setup(pipe);
+ /* Ensure proper covert matrix loaded when color space swaps */
+ curr = inpdw(rgb_base + 0x0058);
+ /* Don't touch bits you don't want to configure*/
+ mask = 0xFFFEFFFF;
+ pipe->op_mode = (pipe->op_mode & mask) | (curr & ~mask);
+
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
outpdw(rgb_base + 0x0000, src_size); /* MDP_RGB_SRC_SIZE */
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index d8a55ed..b69a2fc 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -2989,3 +2989,144 @@
return ret;
}
+static uint32_t mdp4_pp_block2igc(uint32_t block)
+{
+ uint32_t valid = 0;
+ switch (block) {
+ case MDP_BLOCK_VG_1:
+ valid = 0x1;
+ break;
+ case MDP_BLOCK_VG_2:
+ valid = 0x1;
+ break;
+ case MDP_BLOCK_RGB_1:
+ valid = 0x1;
+ break;
+ case MDP_BLOCK_RGB_2:
+ valid = 0x1;
+ break;
+ case MDP_BLOCK_DMA_P:
+ valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+ break;
+ case MDP_BLOCK_DMA_S:
+ valid = (mdp_rev >= MDP_REV_40) ? 1 : 0;
+ break;
+ default:
+ break;
+ }
+ return valid;
+}
+
+static int mdp4_igc_lut_write(struct mdp_igc_lut_data *cfg, uint32_t en_off,
+ uint32_t lut_off)
+{
+ int i;
+ uint32_t base, *off_low, *off_high;
+ uint32_t low[cfg->len];
+ uint32_t high[cfg->len];
+
+ base = mdp_block2base(cfg->block);
+
+ if (cfg->len != 256)
+ return -EINVAL;
+
+ off_low = (uint32_t *)(MDP_BASE + base + lut_off);
+ off_high = (uint32_t *)(MDP_BASE + base + lut_off + 0x800);
+ if (copy_from_user(&low, cfg->c0_c1_data, cfg->len * sizeof(uint32_t)))
+ return -EFAULT;
+ if (copy_from_user(&high, cfg->c2_data, cfg->len * sizeof(uint32_t)))
+ return -EFAULT;
+
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ for (i = 0; i < cfg->len; i++) {
+ MDP_OUTP(off_low++, low[i]);
+ /*low address write should occur before high address write*/
+ wmb();
+ MDP_OUTP(off_high++, high[i]);
+ }
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+ return 0;
+}
+
+static int mdp4_igc_lut_ctrl(struct mdp_igc_lut_data *cfg)
+{
+ uint32_t mask, out;
+ uint32_t base = mdp_block2base(cfg->block);
+ int8_t shift = 0;
+
+ switch (cfg->block) {
+ case MDP_BLOCK_DMA_P:
+ case MDP_BLOCK_DMA_S:
+ base = base;
+ shift = 30;
+ break;
+ case MDP_BLOCK_VG_1:
+ case MDP_BLOCK_VG_2:
+ case MDP_BLOCK_RGB_1:
+ case MDP_BLOCK_RGB_2:
+ base += 0x58;
+ shift = 16;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+ out = 1<<shift;
+ mask = ~out;
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ out = inpdw(MDP_BASE + base) & mask;
+ MDP_OUTP(MDP_BASE + base, out | ((cfg->ops & 0x1)<<shift));
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+ return 0;
+}
+
+static int mdp4_igc_lut_write_cfg(struct mdp_igc_lut_data *cfg)
+{
+ int ret = 0;
+
+ switch (cfg->block) {
+ case MDP_BLOCK_DMA_P:
+ case MDP_BLOCK_DMA_S:
+ ret = mdp4_igc_lut_write(cfg, 0x00, 0x9000);
+ break;
+ case MDP_BLOCK_VG_1:
+ case MDP_BLOCK_VG_2:
+ case MDP_BLOCK_RGB_1:
+ case MDP_BLOCK_RGB_2:
+ ret = mdp4_igc_lut_write(cfg, 0x58, 0x5000);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg)
+{
+ int ret = 0;
+
+ if (!mdp4_pp_block2igc(cfg->block)) {
+ ret = -ENOTTY;
+ goto error;
+ }
+
+ switch ((cfg->ops & 0x6) >> 1) {
+ case 0x1:
+ pr_info("%s: IGC LUT read not supported\n", __func__);
+ break;
+ case 0x2:
+ ret = mdp4_igc_lut_write_cfg(cfg);
+ if (ret)
+ goto error;
+ break;
+ default:
+ break;
+ }
+
+ ret = mdp4_igc_lut_ctrl(cfg);
+
+error:
+ return ret;
+}
diff --git a/drivers/video/msm/mdp_dma_dsi_video.c b/drivers/video/msm/mdp_dma_dsi_video.c
index 0e76a07..3c89d8b 100644
--- a/drivers/video/msm/mdp_dma_dsi_video.c
+++ b/drivers/video/msm/mdp_dma_dsi_video.c
@@ -75,6 +75,7 @@
struct msm_fb_data_type *mfd;
struct msm_panel_info *panel_info;
int ret;
+ uint32 mask, curr;
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
panel_info = &mfd->panel_info;
@@ -163,6 +164,9 @@
MDP_OUTP(MDP_BASE + DMA_P_BASE + 0x10, 0);
/* dma config */
+ curr = inpdw(MDP_BASE + 0x90000);
+ mask = 0xBFFFFFFF;
+ dma2_cfg_reg = (dma2_cfg_reg & mask) | (curr & ~mask);
MDP_OUTP(MDP_BASE + DMA_P_BASE, dma2_cfg_reg);
/*
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index e0657cd..593f10b 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -2952,6 +2952,9 @@
case mdp_op_lut_cfg:
switch (pp_ptr->data.lut_cfg_data.lut_type) {
case mdp_lut_igc:
+ ret = mdp4_igc_lut_config(
+ (struct mdp_igc_lut_data *)
+ &pp_ptr->data.lut_cfg_data.data);
break;
case mdp_lut_pgc: