mdss: display: postprocessing: picture adjustment

Support picture adjustment(hue, sat) for 8974
Use logical block id as input. Apply configuration
at next display commit.

Change-Id: I0e35e50aecdd346ec91373d0c304e21185f28502
Signed-off-by: Ken Zhang <kenz@codeaurora.org>
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 5ea52e7..8fd6d74 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -1107,6 +1107,31 @@
 	return 0;
 }
 
+static int mdss_fb_handle_pp_ioctl(void __user *argp)
+{
+	int ret;
+	struct msmfb_mdp_pp mdp_pp;
+	u32 copyback = 0;
+
+	ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
+	if (ret)
+		return ret;
+
+	switch (mdp_pp.op) {
+	case mdp_op_pa_cfg:
+		ret = mdss_mdp_pa_config(&mdp_pp.data.pa_cfg_data,
+				&copyback);
+		break;
+	default:
+		pr_err("Unsupported request to MDP_PP IOCTL.\n");
+		ret = -EINVAL;
+		break;
+	}
+	if ((ret == 0) && copyback)
+		ret = copy_to_user(argp, &mdp_pp, sizeof(struct msmfb_mdp_pp));
+	return ret;
+}
+
 static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd,
 			 unsigned long arg)
 {
@@ -1133,6 +1158,10 @@
 			return ret;
 		break;
 
+	case MSMFB_MDP_PP:
+		ret = mdss_fb_handle_pp_ioctl(argp);
+		break;
+
 	default:
 		if (mfd->ioctl_handler)
 			ret = mfd->ioctl_handler(mfd, cmd, argp);
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 4604d4a..37e9652 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -725,10 +725,17 @@
 		pr_err("unable to initialize mdss mdp resources\n");
 		goto probe_done;
 	}
+	rc = mdss_mdp_pp_init(&pdev->dev);
+	if (rc) {
+		pr_err("unable to initialize mdss pp resources\n");
+		goto probe_done;
+	}
 	rc = mdss_mdp_bus_scale_register(mdata);
 probe_done:
-	if (IS_ERR_VALUE(rc))
+	if (IS_ERR_VALUE(rc)) {
 		mdss_res = NULL;
+		mdss_mdp_pp_term(&pdev->dev);
+	}
 
 	return rc;
 }
@@ -819,6 +826,7 @@
 	if (!mdata)
 		return -ENODEV;
 	pm_runtime_disable(&pdev->dev);
+	mdss_mdp_pp_term(&pdev->dev);
 	mdss_mdp_bus_scale_unregister(mdata);
 	return 0;
 }
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index a77c18e..610c132 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -295,7 +295,7 @@
 int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg);
 
 int mdss_mdp_csc_setup(u32 block, u32 blk_idx, u32 tbl_idx, u32 csc_type);
-int mdss_mdp_dspp_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_mixer *mixer);
+int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl);
 
 struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum);
 struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type);
@@ -318,4 +318,9 @@
 int mdss_mdp_wb_kickoff(struct mdss_mdp_ctl *ctl);
 int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
 
+int mdss_mdp_pp_init(struct device *dev);
+void mdss_mdp_pp_term(struct device *dev);
+int mdss_mdp_pa_config(struct mdp_pa_cfg_data *config, u32 *copyback);
+
+int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id);
 #endif /* MDSS_MDP_H */
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 1486779..586aec8 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -808,9 +808,6 @@
 {
 	mixer->params_changed = 0;
 
-	if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF)
-		mdss_mdp_dspp_setup(mixer->ctl, mixer);
-
 	/* skip mixer setup for rotator */
 	if (!mixer->rotator_mode)
 		mdss_mdp_mixer_setup(mixer->ctl, mixer);
@@ -864,6 +861,8 @@
 		ctl->flush_bits |= BIT(17);	/* CTL */
 	}
 
+	/* postprocessing setup, including dspp */
+	mdss_mdp_pp_setup(ctl);
 	mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl->flush_bits);
 	wmb();
 	ctl->flush_bits = 0;
@@ -885,3 +884,30 @@
 
 	return ret;
 }
+
+int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id)
+{
+	int i;
+	struct mdss_mdp_ctl *ctl;
+	u32 mixer_cnt = 0;
+	mutex_lock(&mdss_mdp_ctl_lock);
+	for (i = 0; i < MDSS_MDP_MAX_CTL; i++) {
+		ctl = &mdss_mdp_ctl_list[i];
+		if ((ctl->power_on) &&
+			(ctl->mfd->index == fb_num)) {
+			if (ctl->mixer_left) {
+				mixer_id[mixer_cnt] = ctl->mixer_left->num;
+				mixer_cnt++;
+			}
+			if (mixer_cnt && ctl->mixer_right) {
+				mixer_id[mixer_cnt] = ctl->mixer_right->num;
+				mixer_cnt++;
+			}
+			if (mixer_cnt)
+				break;
+		}
+	}
+	mutex_unlock(&mdss_mdp_ctl_lock);
+	return mixer_cnt;
+}
+
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 8825cc6..0139afd 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -321,6 +321,10 @@
 	MDSS_MDP_MAX_DSPP
 };
 
+#define MDSS_MDP_REG_DSPP_OFFSET(pipe)	(0x4600 + ((pipe) * 0x400))
+#define MDSS_MDP_REG_DSPP_OP_MODE			0x000
+#define MDSS_MDP_REG_DSPP_PA_BASE			0x238
+
 enum mdss_mpd_intf_index {
 	MDSS_MDP_NO_INTF,
 	MDSS_MDP_INTF0,
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index db840a8..7ab3b01 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt)	"%s: " fmt, __func__
 
+#include "mdss_fb.h"
 #include "mdss_mdp.h"
 
 struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = {
@@ -71,6 +72,27 @@
 #define CSC_LV_OFF	0x14
 #define CSC_POST_OFF	0xC
 
+#define MDSS_BLOCK_DISP_NUM	(MDP_BLOCK_MAX - MDP_LOGICAL_BLOCK_DISP_0)
+
+struct pp_sts_type {
+	u32 pa_sts;
+};
+
+#define PP_FLAGS_DIRTY_PA	0x1
+
+#define PP_STS_ENABLE	0x1
+
+struct mdss_pp_res_type {
+	/* logical info */
+	u32 pp_disp_flags[MDSS_BLOCK_DISP_NUM];
+	struct mdp_pa_cfg_data pa_disp_cfg[MDSS_BLOCK_DISP_NUM];
+	/* physical info */
+	struct pp_sts_type pp_dspp_sts[MDSS_MDP_MAX_DSPP];
+};
+
+static DEFINE_MUTEX(mdss_pp_mutex);
+static struct mdss_pp_res_type *mdss_pp_res;
+
 static int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx,
 				   struct mdp_csc_cfg *data)
 {
@@ -160,16 +182,147 @@
 	return mdss_mdp_csc_setup_data(block, blk_idx, tbl_idx, data);
 }
 
-int mdss_mdp_dspp_setup(struct mdss_mdp_ctl *ctl, struct mdss_mdp_mixer *mixer)
+static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_ctl *ctl,
+		struct mdss_mdp_mixer *mixer)
 {
-	int dspp_num;
-
-	if (!ctl || !mixer)
-		return -EINVAL;
+	u32 flags, base, offset, dspp_num, opmode = 0;
+	struct mdp_pa_cfg_data *pa_config;
+	struct pp_sts_type *pp_sts;
 
 	dspp_num = mixer->num;
+	/* no corresponding dspp */
+	if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) ||
+		(dspp_num >= MDSS_MDP_MAX_DSPP))
+		return 0;
 
-	ctl->flush_bits |= BIT(13 + dspp_num);	/* DSPP */
+	if (disp_num < MDSS_BLOCK_DISP_NUM)
+		flags = mdss_pp_res->pp_disp_flags[disp_num];
+	else
+		flags = 0;
+
+	/* nothing to update */
+	if (!flags)
+		return 0;
+	pp_sts = &mdss_pp_res->pp_dspp_sts[dspp_num];
+	base = MDSS_MDP_REG_DSPP_OFFSET(dspp_num);
+	if (flags & PP_FLAGS_DIRTY_PA) {
+		pa_config = &mdss_pp_res->pa_disp_cfg[disp_num];
+		if (pa_config->flags & MDP_PP_OPS_WRITE) {
+			offset = base + MDSS_MDP_REG_DSPP_PA_BASE;
+			MDSS_MDP_REG_WRITE(offset, pa_config->hue_adj);
+			offset += 4;
+			MDSS_MDP_REG_WRITE(offset, pa_config->sat_adj);
+			offset += 4;
+			MDSS_MDP_REG_WRITE(offset, pa_config->val_adj);
+			offset += 4;
+			MDSS_MDP_REG_WRITE(offset, pa_config->cont_adj);
+		}
+		if (pa_config->flags & MDP_PP_OPS_DISABLE)
+			pp_sts->pa_sts &= ~PP_STS_ENABLE;
+		else if (pa_config->flags & MDP_PP_OPS_ENABLE)
+			pp_sts->pa_sts |= PP_STS_ENABLE;
+	}
+	if (pp_sts->pa_sts & PP_STS_ENABLE)
+		opmode |= (1 << 20); /* PA_EN */
+	MDSS_MDP_REG_WRITE(base + MDSS_MDP_REG_DSPP_OP_MODE, opmode);
+	ctl->flush_bits |= BIT(13 + dspp_num); /* DSPP */
+	return 0;
+
+}
+int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
+{
+	u32 disp_num;
+	if ((!ctl->mfd) || (!mdss_pp_res))
+		return -EINVAL;
+
+	/* treat fb_num the same as block logical id*/
+	disp_num = ctl->mfd->index;
+
+	mutex_lock(&mdss_pp_mutex);
+	if (ctl->mixer_left)
+		pp_dspp_setup(disp_num, ctl, ctl->mixer_left);
+	if (ctl->mixer_right)
+		pp_dspp_setup(disp_num, ctl, ctl->mixer_right);
+	/* clear dirty flag */
+	if (disp_num < MDSS_BLOCK_DISP_NUM)
+		mdss_pp_res->pp_disp_flags[disp_num] = 0;
+	mutex_unlock(&mdss_pp_mutex);
 
 	return 0;
 }
+
+int mdss_mdp_pp_init(struct device *dev)
+{
+	int ret = 0;
+	mutex_lock(&mdss_pp_mutex);
+	if (!mdss_pp_res) {
+		mdss_pp_res = devm_kzalloc(dev, sizeof(*mdss_pp_res),
+				GFP_KERNEL);
+		if (mdss_pp_res == NULL) {
+			pr_err("%s mdss_pp_res allocation failed!", __func__);
+			ret = -ENOMEM;
+		}
+	}
+	mutex_unlock(&mdss_pp_mutex);
+	return ret;
+}
+void mdss_mdp_pp_term(struct device *dev)
+{
+	if (!mdss_pp_res) {
+		mutex_lock(&mdss_pp_mutex);
+		devm_kfree(dev, mdss_pp_res);
+		mdss_pp_res = NULL;
+		mutex_unlock(&mdss_pp_mutex);
+	}
+}
+
+int mdss_mdp_pa_config(struct mdp_pa_cfg_data *config, u32 *copyback)
+{
+	int i, ret = 0;
+	u32 pa_offset, disp_num, mixer_cnt;
+	u32 mixer_id[MDSS_MDP_MAX_LAYERMIXER];
+
+	if ((config->block < MDP_LOGICAL_BLOCK_DISP_0) ||
+		(config->block >= MDP_BLOCK_MAX))
+		return -EINVAL;
+
+	mutex_lock(&mdss_pp_mutex);
+	disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
+
+	if (config->flags & MDP_PP_OPS_READ) {
+		mixer_cnt = mdss_mdp_get_ctl_mixers(disp_num, mixer_id);
+		if (!mixer_cnt) {
+			ret = -EPERM;
+			goto pa_config_exit;
+		}
+		/* only read the first mixer */
+		for (i = 0; i < mixer_cnt; i++) {
+			if (mixer_id[i] < MDSS_MDP_MAX_DSPP)
+				break;
+		}
+		if (i >= mixer_cnt) {
+			ret = -EPERM;
+			goto pa_config_exit;
+		}
+		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+		pa_offset = MDSS_MDP_REG_DSPP_OFFSET(mixer_id[i]) +
+			  MDSS_MDP_REG_DSPP_PA_BASE;
+
+		config->hue_adj = MDSS_MDP_REG_READ(pa_offset);
+		pa_offset += 4;
+		config->sat_adj = MDSS_MDP_REG_READ(pa_offset);
+		pa_offset += 4;
+		config->val_adj = MDSS_MDP_REG_READ(pa_offset);
+		pa_offset += 4;
+		config->cont_adj = MDSS_MDP_REG_READ(pa_offset);
+		*copyback = 1;
+		mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+	} else {
+		mdss_pp_res->pa_disp_cfg[disp_num] = *config;
+		mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_PA;
+	}
+
+pa_config_exit:
+	mutex_unlock(&mdss_pp_mutex);
+	return ret;
+}
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index f6229b5..8b0826b 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -271,8 +271,10 @@
 	struct msmfb_img img;
 };
 
+#define MDP_PP_OPS_ENABLE 0x1
 #define MDP_PP_OPS_READ 0x2
 #define MDP_PP_OPS_WRITE 0x4
+#define MDP_PP_OPS_DISABLE 0x8
 
 struct mdp_qseed_cfg {
 	uint32_t table_num;
@@ -472,12 +474,22 @@
 	uint32_t scale;
 };
 
+struct mdp_pa_cfg_data {
+	uint32_t block;
+	uint32_t flags;
+	uint32_t hue_adj;
+	uint32_t sat_adj;
+	uint32_t val_adj;
+	uint32_t cont_adj;
+};
+
 enum {
 	mdp_op_pcc_cfg,
 	mdp_op_csc_cfg,
 	mdp_op_lut_cfg,
 	mdp_op_qseed_cfg,
 	mdp_bl_scale_cfg,
+	mdp_op_pa_cfg,
 	mdp_op_max,
 };
 
@@ -489,6 +501,7 @@
 		struct mdp_lut_cfg_data lut_cfg_data;
 		struct mdp_qseed_cfg_data qseed_cfg_data;
 		struct mdp_bl_scale_data bl_scale_data;
+		struct mdp_pa_cfg_data pa_cfg_data;
 	} data;
 };