drm/msm/sde: Add PA global adjustment properties to plane

Add support for hue, saturation, intensity, and contrast adjustment
in planes that have color processing support.

Change-Id: I4c794deb7a5a1c0cc30cc0d64fbffd967eb1d399
Signed-off-by: Benet Clark <benetc@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 8ef30cc..c988279 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -119,7 +119,8 @@
 	sde/sde_hw_interrupts.o \
 	sde/sde_hw_vbif.o \
 	sde/sde_formats.o \
-	sde_power_handle.o
+	sde_power_handle.o \
+	sde/sde_hw_color_processing_v1_7.o
 
 msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
 	sde/sde_encoder_phys_wb.o
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 91660c9..d08e65d 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -95,6 +95,10 @@
 	PLANE_PROP_H_DECIMATE,
 	PLANE_PROP_V_DECIMATE,
 	PLANE_PROP_INPUT_FENCE,
+	PLANE_PROP_HUE_ADJUST,
+	PLANE_PROP_SATURATION_ADJUST,
+	PLANE_PROP_VALUE_ADJUST,
+	PLANE_PROP_CONTRAST_ADJUST,
 
 	/* enum/bitmask properties */
 	PLANE_PROP_ROTATION,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
new file mode 100644
index 0000000..a30e1a5
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2016, The Linux Foundation. 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.
+ */
+
+#ifndef _SDE_HW_COLOR_PROCESSING_H
+#define _SDE_HW_COLOR_PROCESSING_H
+
+#include "sde_hw_color_processing_v1_7.h"
+
+#endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
new file mode 100644
index 0000000..9f49625
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 2016, The Linux Foundation. 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 "sde_hw_color_processing_v1_7.h"
+
+#define PA_HUE_OFF		0x110
+#define PA_HUE_MASK		0xFFF
+#define PA_SAT_OFF		0x114
+#define PA_SAT_MASK		0xFFFF
+#define PA_VAL_OFF		0x118
+#define PA_VAL_MASK		0xFF
+#define PA_CONT_OFF		0x11C
+#define PA_CONT_MASK		0xFF
+
+#define VIG_OP_PA_EN		BIT(4)
+#define VIG_OP_PA_SKIN_EN	BIT(5)
+#define VIG_OP_PA_FOL_EN	BIT(6)
+#define VIG_OP_PA_SKY_EN	BIT(7)
+#define VIG_OP_PA_HUE_EN	BIT(25)
+#define VIG_OP_PA_SAT_EN	BIT(26)
+#define VIG_OP_PA_VAL_EN	BIT(27)
+#define VIG_OP_PA_CONT_EN	BIT(28)
+
+#define PA_DISABLE_REQUIRED(x)	!((x) & \
+				(VIG_OP_PA_SKIN_EN | VIG_OP_PA_SKY_EN | \
+				VIG_OP_PA_FOL_EN | VIG_OP_PA_HUE_EN | \
+				VIG_OP_PA_SAT_EN | VIG_OP_PA_VAL_EN | \
+				VIG_OP_PA_CONT_EN))
+
+void sde_setup_pipe_pa_hue_v1_7(struct sde_hw_pipe *ctx, void *cfg)
+{
+	uint32_t hue = *((uint32_t *)cfg);
+	u32 base = ctx->cap->sblk->hsic_blk.base;
+	u32 opmode = 0;
+
+	SDE_REG_WRITE(&ctx->hw, base + PA_HUE_OFF, hue & PA_HUE_MASK);
+
+	opmode = SDE_REG_READ(&ctx->hw, base);
+
+	if (!hue) {
+		opmode &= ~VIG_OP_PA_HUE_EN;
+		if (PA_DISABLE_REQUIRED(opmode))
+			opmode &= ~VIG_OP_PA_EN;
+	} else {
+		opmode |= VIG_OP_PA_HUE_EN | VIG_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, base, opmode);
+}
+
+void sde_setup_pipe_pa_sat_v1_7(struct sde_hw_pipe *ctx, void *cfg)
+{
+	uint32_t sat = *((uint32_t *)cfg);
+	u32 base = ctx->cap->sblk->hsic_blk.base;
+	u32 opmode = 0;
+
+	SDE_REG_WRITE(&ctx->hw, base + PA_SAT_OFF, sat & PA_SAT_MASK);
+
+	opmode = SDE_REG_READ(&ctx->hw, base);
+
+	if (!sat) {
+		opmode &= ~VIG_OP_PA_SAT_EN;
+		if (PA_DISABLE_REQUIRED(opmode))
+			opmode &= ~VIG_OP_PA_EN;
+	} else {
+		opmode |= VIG_OP_PA_SAT_EN | VIG_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, base, opmode);
+}
+
+void sde_setup_pipe_pa_val_v1_7(struct sde_hw_pipe *ctx, void *cfg)
+{
+	uint32_t value = *((uint32_t *)cfg);
+	u32 base = ctx->cap->sblk->hsic_blk.base;
+	u32 opmode = 0;
+
+	SDE_REG_WRITE(&ctx->hw, base + PA_VAL_OFF, value & PA_VAL_MASK);
+
+	opmode = SDE_REG_READ(&ctx->hw, base);
+
+	if (!value) {
+		opmode &= ~VIG_OP_PA_VAL_EN;
+		if (PA_DISABLE_REQUIRED(opmode))
+			opmode &= ~VIG_OP_PA_EN;
+	} else {
+		opmode |= VIG_OP_PA_VAL_EN | VIG_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, base, opmode);
+}
+
+void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg)
+{
+	uint32_t contrast = *((uint32_t *)cfg);
+	u32 base = ctx->cap->sblk->hsic_blk.base;
+	u32 opmode = 0;
+
+	SDE_REG_WRITE(&ctx->hw, base + PA_CONT_OFF, contrast & PA_CONT_MASK);
+
+	opmode = SDE_REG_READ(&ctx->hw, base);
+
+	if (!contrast) {
+		opmode &= ~VIG_OP_PA_CONT_EN;
+		if (PA_DISABLE_REQUIRED(opmode))
+			opmode &= ~VIG_OP_PA_EN;
+	} else {
+		opmode |= VIG_OP_PA_CONT_EN | VIG_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, base, opmode);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
new file mode 100644
index 0000000..788bde0
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2016, The Linux Foundation. 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.
+ */
+
+#ifndef _SDE_HW_COLOR_PROCESSING_V1_7_H
+#define _SDE_HW_COLOR_PROCESSING_V1_7_H
+
+#include "sde_hw_sspp.h"
+
+/**
+ * sde_setup_pipe_pa_hue_v1_7 - setup SSPP hue feature in v1.7 hardware
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to hue data
+ */
+void sde_setup_pipe_pa_hue_v1_7(struct sde_hw_pipe *ctx, void *cfg);
+
+/**
+ * sde_setup_pipe_pa_sat_v1_7 - setup SSPP saturation feature in v1.7 hardware
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to saturation data
+ */
+void sde_setup_pipe_pa_sat_v1_7(struct sde_hw_pipe *ctx, void *cfg);
+
+/**
+ * sde_setup_pipe_pa_val_v1_7 - setup SSPP value feature in v1.7 hardware
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to value data
+ */
+void sde_setup_pipe_pa_val_v1_7(struct sde_hw_pipe *ctx, void *cfg);
+
+/**
+ * sde_setup_pipe_pa_cont_v1_7 - setup SSPP contrast feature in v1.7 hardware
+ * @ctx: Pointer to pipe context
+ * @cfg: Pointer to contrast data
+ */
+void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg);
+
+#endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index f590f91..dae36ef 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -14,6 +14,7 @@
 #include "sde_hw_catalog.h"
 #include "sde_hw_lm.h"
 #include "sde_hw_sspp.h"
+#include "sde_hw_color_processing.h"
 
 #define SDE_FETCH_CONFIG_RESET_VALUE   0x00000087
 
@@ -840,31 +841,44 @@
 	SDE_REG_WRITE(&ctx->hw, SSPP_QOS_CTRL + idx, qos_ctrl);
 }
 
-static void _setup_layer_ops(struct sde_hw_sspp_ops *ops,
+static void _setup_layer_ops(struct sde_hw_pipe *c,
 		unsigned long features)
 {
 	if (test_bit(SDE_SSPP_SRC, &features)) {
-		ops->setup_format = sde_hw_sspp_setup_format;
-		ops->setup_rects = sde_hw_sspp_setup_rects;
-		ops->setup_sourceaddress = sde_hw_sspp_setup_sourceaddress;
-		ops->setup_solidfill = sde_hw_sspp_setup_solidfill;
+		c->ops.setup_format = sde_hw_sspp_setup_format;
+		c->ops.setup_rects = sde_hw_sspp_setup_rects;
+		c->ops.setup_sourceaddress = sde_hw_sspp_setup_sourceaddress;
+		c->ops.setup_solidfill = sde_hw_sspp_setup_solidfill;
 	}
 	if (test_bit(SDE_SSPP_QOS, &features)) {
-		ops->setup_danger_safe_lut = sde_hw_sspp_setup_danger_safe_lut;
-		ops->setup_creq_lut = sde_hw_sspp_setup_creq_lut;
-		ops->setup_qos_ctrl = sde_hw_sspp_setup_qos_ctrl;
+		c->ops.setup_danger_safe_lut =
+			sde_hw_sspp_setup_danger_safe_lut;
+		c->ops.setup_creq_lut = sde_hw_sspp_setup_creq_lut;
+		c->ops.setup_qos_ctrl = sde_hw_sspp_setup_qos_ctrl;
 	}
+
 	if (test_bit(SDE_SSPP_CSC, &features) ||
 		test_bit(SDE_SSPP_CSC_10BIT, &features))
-		ops->setup_csc = sde_hw_sspp_setup_csc;
+		c->ops.setup_csc = sde_hw_sspp_setup_csc;
 
 	if (test_bit(SDE_SSPP_SCALER_QSEED2, &features))
-		ops->setup_sharpening = sde_hw_sspp_setup_sharpening;
+		c->ops.setup_sharpening = sde_hw_sspp_setup_sharpening;
 
 	if (test_bit(SDE_SSPP_SCALER_QSEED3, &features))
-		ops->setup_scaler = _sde_hw_sspp_setup_scaler3;
+		c->ops.setup_scaler = _sde_hw_sspp_setup_scaler3;
 	else
-		ops->setup_scaler = _sde_hw_sspp_setup_scaler;
+		c->ops.setup_scaler = _sde_hw_sspp_setup_scaler;
+
+	if (test_bit(SDE_SSPP_HSIC, &features)) {
+		/* TODO: add version based assignment here as inline or macro */
+		if (c->cap->sblk->hsic_blk.version ==
+			(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
+			c->ops.setup_pa_hue = sde_setup_pipe_pa_hue_v1_7;
+			c->ops.setup_pa_sat = sde_setup_pipe_pa_sat_v1_7;
+			c->ops.setup_pa_val = sde_setup_pipe_pa_val_v1_7;
+			c->ops.setup_pa_cont = sde_setup_pipe_pa_cont_v1_7;
+		}
+	}
 }
 
 static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp,
@@ -909,7 +923,7 @@
 	/* Assign ops */
 	ctx->idx = idx;
 	ctx->cap = cfg;
-	_setup_layer_ops(&ctx->ops, ctx->cap->features);
+	_setup_layer_ops(ctx, ctx->cap->features);
 	ctx->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
 
 	return ctx;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 488936e..85f2ae6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -326,6 +326,35 @@
 	void (*setup_sharpening)(struct sde_hw_pipe *ctx,
 			struct sde_hw_sharp_cfg *cfg);
 
+
+	/**
+	 * setup_pa_hue(): Setup source hue adjustment
+	 * @ctx: Pointer to pipe context
+	 * @cfg: Pointer to hue data
+	 */
+	void (*setup_pa_hue)(struct sde_hw_pipe *ctx, void *cfg);
+
+	/**
+	 * setup_pa_sat(): Setup source saturation adjustment
+	 * @ctx: Pointer to pipe context
+	 * @cfg: Pointer to saturation data
+	 */
+	void (*setup_pa_sat)(struct sde_hw_pipe *ctx, void *cfg);
+
+	/**
+	 * setup_pa_val(): Setup source value adjustment
+	 * @ctx: Pointer to pipe context
+	 * @cfg: Pointer to value data
+	 */
+	void (*setup_pa_val)(struct sde_hw_pipe *ctx, void *cfg);
+
+	/**
+	 * setup_pa_cont(): Setup source contrast adjustment
+	 * @ctx: Pointer to pipe context
+	 * @cfg: Pointer contrast data
+	 */
+	void (*setup_pa_cont)(struct sde_hw_pipe *ctx, void *cfg);
+
 	/**
 	 * setup_pa_memcolor - setup source color processing
 	 * @ctx: Pointer to pipe context
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index cd4dbb7..70b6d25 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -774,6 +774,33 @@
 			psde->csc_ptr->csc_mv[2]);
 }
 
+static void sde_color_process_plane_setup(struct drm_plane *plane)
+{
+	struct sde_plane *psde;
+	struct sde_plane_state *pstate;
+	uint32_t hue, saturation, value, contrast;
+
+	psde = to_sde_plane(plane);
+	pstate = to_sde_plane_state(plane->state);
+
+	hue = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_HUE_ADJUST);
+	if (psde->pipe_hw->ops.setup_pa_hue)
+		psde->pipe_hw->ops.setup_pa_hue(psde->pipe_hw, &hue);
+	saturation = (uint32_t) sde_plane_get_property(pstate,
+		PLANE_PROP_SATURATION_ADJUST);
+	if (psde->pipe_hw->ops.setup_pa_sat)
+		psde->pipe_hw->ops.setup_pa_sat(psde->pipe_hw, &saturation);
+	value = (uint32_t) sde_plane_get_property(pstate,
+		PLANE_PROP_VALUE_ADJUST);
+	if (psde->pipe_hw->ops.setup_pa_val)
+		psde->pipe_hw->ops.setup_pa_val(psde->pipe_hw, &value);
+	contrast = (uint32_t) sde_plane_get_property(pstate,
+		PLANE_PROP_CONTRAST_ADJUST);
+	if (psde->pipe_hw->ops.setup_pa_cont)
+		psde->pipe_hw->ops.setup_pa_cont(psde->pipe_hw, &contrast);
+}
+
+
 static void _sde_plane_setup_scaler(struct sde_plane *psde,
 		const struct sde_format *fmt,
 		struct sde_plane_state *pstate)
@@ -1081,6 +1108,8 @@
 			psde->csc_ptr = 0;
 	}
 
+	sde_color_process_plane_setup(plane);
+
 	/* update sharpening */
 	if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) &&
 		psde->pipe_hw->ops.setup_sharpening) {
@@ -1429,6 +1458,7 @@
 	struct sde_plane *psde = to_sde_plane(plane);
 	int zpos_max = 255;
 	int zpos_def = 0;
+	char feature_name[256];
 
 	if (!plane || !psde) {
 		SDE_ERROR("invalid plane\n");
@@ -1495,6 +1525,33 @@
 			"csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1);
 	}
 
+	if (psde->features & BIT(SDE_SSPP_HSIC)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_HUE_V",
+			psde->pipe_sblk->hsic_blk.version >> 16);
+		msm_property_install_range(&psde->property_info,
+			feature_name, 0, 0, 0xFFFFFFFF, 0,
+			PLANE_PROP_HUE_ADJUST);
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_SATURATION_V",
+			psde->pipe_sblk->hsic_blk.version >> 16);
+		msm_property_install_range(&psde->property_info,
+			feature_name, 0, 0, 0xFFFFFFFF, 0,
+			PLANE_PROP_SATURATION_ADJUST);
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_VALUE_V",
+			psde->pipe_sblk->hsic_blk.version >> 16);
+		msm_property_install_range(&psde->property_info,
+			feature_name, 0, 0, 0xFFFFFFFF, 0,
+			PLANE_PROP_VALUE_ADJUST);
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_CONTRAST_V",
+			psde->pipe_sblk->hsic_blk.version >> 16);
+		msm_property_install_range(&psde->property_info,
+			feature_name, 0, 0, 0xFFFFFFFF, 0,
+			PLANE_PROP_CONTRAST_ADJUST);
+	}
+
 	/* standard properties */
 	msm_property_install_rotation(&psde->property_info,
 		BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y), PLANE_PROP_ROTATION);