drm/msm: Add support for PCC in Crtc

CRTC contains the dspp block which contains color processing modules.
Change adds the pcc support to the CRTC.

Change-Id: Ib4091d43d819ab4f20f16892277ccd1d21285c61
Signed-off-by: Gopikrishnaiah Anandan <agopik@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 5c3dbd0..bb09c12 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -43,6 +43,7 @@
 	sde/sde_kms.o \
 	sde/sde_plane.o \
 	sde/sde_connector.o \
+	sde/sde_color_processing.o \
 	msm_atomic.o \
 	msm_debugfs.o \
 	msm_drv.o \
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 59efc45..453434d 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -281,6 +281,9 @@
 	struct drm_property *crtc_property[CRTC_PROP_COUNT];
 	struct drm_property *conn_property[CONNECTOR_PROP_COUNT];
 
+	/* Color processing properties for the crtc */
+	struct drm_property **cp_property;
+
 	/* VRAM carveout, used when no IOMMU: */
 	struct {
 		unsigned long size;
diff --git a/drivers/gpu/drm/msm/msm_prop.c b/drivers/gpu/drm/msm/msm_prop.c
index e5b7ad5..cce0cdf 100644
--- a/drivers/gpu/drm/msm/msm_prop.c
+++ b/drivers/gpu/drm/msm/msm_prop.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 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
@@ -350,7 +350,7 @@
 
 	property_idx = msm_property_index(info, property);
 	if (!info || (property_idx == -EINVAL) || !property_values) {
-		DRM_ERROR("invalid argument(s)\n");
+		DRM_DEBUG("Invalid argument(s)\n");
 	} else {
 		/* extra handling for incoming properties */
 		mutex_lock(&info->property_lock);
@@ -399,7 +399,7 @@
 
 	property_idx = msm_property_index(info, property);
 	if (!info || (property_idx == -EINVAL) || !property_values || !val) {
-		DRM_ERROR("invalid argument(s)\n");
+		DRM_DEBUG("Invalid argument(s)\n");
 	} else {
 		mutex_lock(&info->property_lock);
 		*val = property_values[property_idx];
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
new file mode 100644
index 0000000..a4dd81f
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -0,0 +1,640 @@
+/* Copyright (c) 2016-2017, 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 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <drm/msm_drm_pp.h>
+#include "sde_color_processing.h"
+#include "sde_kms.h"
+#include "sde_crtc.h"
+#include "sde_hw_dspp.h"
+#include "sde_hw_lm.h"
+
+struct sde_color_process_node {
+	u32 property_id;
+	u32 prop_flags;
+	u32 feature;
+	void *blob_ptr;
+	uint64_t prop_val;
+	const struct sde_pp_blk *pp_blk;
+	struct list_head feature_list;
+	struct list_head active_list;
+	struct list_head dirty_list;
+	void (*dspp_feature_op)(struct sde_hw_dspp *ctx, void *cfg);
+	void (*lm_feature_op)(struct sde_hw_mixer *mixer, void *cfg);
+};
+
+struct sde_cp_prop_attach {
+	struct drm_crtc *crtc;
+	struct drm_property *prop;
+	struct sde_color_process_node *prop_node;
+	const struct sde_pp_blk *pp_blk;
+	u32 feature;
+	void *ops;
+	uint64_t val;
+};
+
+enum {
+	/* Append new DSPP features before SDE_CP_CRTC_DSPP_MAX */
+	/* DSPP Features start */
+	SDE_CP_CRTC_DSPP_IGC,
+	SDE_CP_CRTC_DSPP_PCC,
+	SDE_CP_CRTC_DSPP_GC,
+	SDE_CP_CRTC_DSPP_HUE,
+	SDE_CP_CRTC_DSPP_SAT,
+	SDE_CP_CRTC_DSPP_VAL,
+	SDE_CP_CRTC_DSPP_CONT,
+	SDE_CP_CRTC_DSPP_MEMCOLOR,
+	SDE_CP_CRTC_DSPP_SIXZONE,
+	SDE_CP_CRTC_DSPP_GAMUT,
+	SDE_CP_CRTC_DSPP_DITHER,
+	SDE_CP_CRTC_DSPP_HIST,
+	SDE_CP_CRTC_DSPP_MAX,
+	/* DSPP features end */
+
+	/* Append new LM features before SDE_CP_CRTC_MAX_FEATURES */
+	/* LM feature start*/
+	SDE_CP_CRTC_LM_GC,
+	/* LM feature end*/
+
+	SDE_CP_CRTC_MAX_FEATURES,
+};
+
+#define INIT_PROP_ATTACH(p, crtc, prop, node, blk, feature, ops, val) \
+	do { \
+		(p)->crtc = crtc; \
+		(p)->prop = prop; \
+		(p)->prop_node = node; \
+		(p)->pp_blk = blk; \
+		(p)->feature = feature; \
+		(p)->ops = ops; \
+		(p)->val = val; \
+	} while (0)
+
+static int sde_cp_disable_crtc_blob_property(
+				struct sde_color_process_node *prop_node)
+{
+	struct drm_property_blob *blob = prop_node->blob_ptr;
+
+	if (!blob)
+		return -EINVAL;
+	drm_property_unreference_blob(blob);
+	prop_node->blob_ptr = NULL;
+	return 0;
+}
+
+static int sde_cp_disable_crtc_property(struct drm_crtc *crtc,
+				 struct drm_property *property,
+				 struct sde_color_process_node *prop_node)
+{
+	int ret = 0;
+
+	if (property->flags & DRM_MODE_PROP_BLOB) {
+		ret = sde_cp_disable_crtc_blob_property(prop_node);
+	} else if (property->flags & DRM_MODE_PROP_RANGE) {
+		prop_node->prop_val = 0;
+		ret = 0;
+	}
+	return ret;
+}
+
+static int sde_cp_enable_crtc_blob_property(struct drm_crtc *crtc,
+				       struct sde_color_process_node *prop_node,
+				       uint64_t val)
+{
+	struct drm_property_blob *blob = NULL;
+
+	/**
+	 * For non-blob based properties add support to create a blob
+	 * using the val and store the blob_ptr in prop_node.
+	 */
+	blob = drm_property_lookup_blob(crtc->dev, val);
+	if (!blob) {
+		DRM_ERROR("invalid blob id %lld\n", val);
+		return -EINVAL;
+	}
+	/* Release refernce to existing payload of the property */
+	if (prop_node->blob_ptr)
+		drm_property_unreference_blob(prop_node->blob_ptr);
+
+	prop_node->blob_ptr = blob;
+	return 0;
+}
+
+static int sde_cp_enable_crtc_property(struct drm_crtc *crtc,
+				       struct drm_property *property,
+				       struct sde_color_process_node *prop_node,
+				       uint64_t val)
+{
+	int ret = -EINVAL;
+
+	if (property->flags & DRM_MODE_PROP_BLOB)
+		ret = sde_cp_enable_crtc_blob_property(crtc, prop_node, val);
+	else if (property->flags & DRM_MODE_PROP_RANGE) {
+		ret = 0;
+		prop_node->prop_val = val;
+	}
+	return ret;
+}
+
+static int sde_cp_crtc_get_mixer_idx(struct sde_crtc *sde_crtc)
+{
+	if (sde_crtc->num_mixers)
+		return sde_crtc->mixers[0].hw_lm->idx;
+	else
+		return -EINVAL;
+}
+
+static struct sde_kms *get_kms(struct drm_crtc *crtc)
+{
+	struct msm_drm_private *priv = crtc->dev->dev_private;
+
+	return to_sde_kms(priv->kms);
+}
+
+static void sde_cp_crtc_prop_attach(struct sde_cp_prop_attach *prop_attach)
+{
+
+	struct sde_crtc *sde_crtc = to_sde_crtc(prop_attach->crtc);
+
+	drm_object_attach_property(&prop_attach->crtc->base,
+				   prop_attach->prop, prop_attach->val);
+
+	INIT_LIST_HEAD(&prop_attach->prop_node->active_list);
+	INIT_LIST_HEAD(&prop_attach->prop_node->dirty_list);
+
+	prop_attach->prop_node->property_id = prop_attach->prop->base.id;
+	prop_attach->prop_node->prop_flags = prop_attach->prop->flags;
+	prop_attach->prop_node->feature = prop_attach->feature;
+	prop_attach->prop_node->pp_blk = prop_attach->pp_blk;
+
+	if (prop_attach->feature < SDE_CP_CRTC_DSPP_MAX)
+		prop_attach->prop_node->dspp_feature_op = prop_attach->ops;
+	else
+		prop_attach->prop_node->lm_feature_op = prop_attach->ops;
+
+	list_add(&prop_attach->prop_node->feature_list,
+		 &sde_crtc->feature_list);
+}
+
+void sde_cp_crtc_init(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc = NULL;
+
+	if (!crtc) {
+		DRM_ERROR("invalid crtc %pK\n", crtc);
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	if (!sde_crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+		return;
+	}
+
+	INIT_LIST_HEAD(&sde_crtc->active_list);
+	INIT_LIST_HEAD(&sde_crtc->dirty_list);
+	INIT_LIST_HEAD(&sde_crtc->feature_list);
+}
+
+static void sde_cp_crtc_install_range_property(struct drm_crtc *crtc,
+					     char *name,
+					     const struct sde_pp_blk *pp_blk,
+					     u32 feature, void *ops,
+					     uint64_t min, uint64_t max,
+					     uint64_t val)
+{
+	struct drm_property *prop;
+	struct sde_color_process_node *prop_node = NULL;
+	struct msm_drm_private *priv;
+	struct sde_cp_prop_attach prop_attach;
+
+	if (feature >=  SDE_CP_CRTC_MAX_FEATURES) {
+		DRM_ERROR("invalid feature %d max %d\n", feature,
+			  SDE_CP_CRTC_MAX_FEATURES);
+		return;
+	}
+
+	prop_node = kzalloc(sizeof(*prop_node), GFP_KERNEL);
+	if (!prop_node)
+		return;
+
+	priv = crtc->dev->dev_private;
+	prop = priv->cp_property[feature];
+
+	if (!prop) {
+		prop = drm_property_create_range(crtc->dev, 0, name, min, max);
+		if (!prop) {
+			DRM_ERROR("property create failed: %s\n", name);
+			kfree(prop_node);
+			return;
+		}
+		priv->cp_property[feature] = prop;
+	}
+
+	INIT_PROP_ATTACH(&prop_attach, crtc, prop, prop_node, pp_blk,
+				feature, ops, val);
+
+	sde_cp_crtc_prop_attach(&prop_attach);
+}
+
+static void sde_cp_crtc_create_blob_property(struct drm_crtc *crtc, char *name,
+					     const struct sde_pp_blk *pp_blk,
+					     u32 feature, void *ops)
+{
+	struct drm_property *prop;
+	struct sde_color_process_node *prop_node = NULL;
+	struct msm_drm_private *priv;
+	uint64_t val = 0;
+	struct sde_cp_prop_attach prop_attach;
+
+	if (feature >=  SDE_CP_CRTC_MAX_FEATURES) {
+		DRM_ERROR("invalid feature %d max %d\n", feature,
+		       SDE_CP_CRTC_MAX_FEATURES);
+		return;
+	}
+
+	prop_node = kzalloc(sizeof(*prop_node), GFP_KERNEL);
+	if (!prop_node)
+		return;
+
+	priv = crtc->dev->dev_private;
+	prop = priv->cp_property[feature];
+
+	if (!prop) {
+		prop = drm_property_create(crtc->dev,
+					   DRM_MODE_PROP_BLOB, name, 0);
+		if (!prop) {
+			DRM_ERROR("property create failed: %s\n", name);
+			kfree(prop_node);
+			return;
+		}
+		priv->cp_property[feature] = prop;
+	}
+
+	INIT_PROP_ATTACH(&prop_attach, crtc, prop, prop_node, pp_blk,
+				feature, ops, val);
+
+	sde_cp_crtc_prop_attach(&prop_attach);
+}
+
+
+static void sde_cp_crtc_setfeature(struct sde_color_process_node *prop_node,
+				   struct sde_crtc *sde_crtc)
+{
+	u32 num_mixers = sde_crtc->num_mixers;
+	uint64_t val = 0;
+	struct drm_property_blob *blob = NULL;
+	struct sde_hw_cp_cfg hw_cfg;
+	int i = 0;
+	bool is_dspp = true;
+
+	if (!prop_node->dspp_feature_op && !prop_node->lm_feature_op) {
+		DRM_ERROR("ops not set for dspp/lm\n");
+		return;
+	}
+
+	is_dspp = !prop_node->lm_feature_op;
+	memset(&hw_cfg, 0, sizeof(hw_cfg));
+	if (prop_node->prop_flags & DRM_MODE_PROP_BLOB) {
+		blob = prop_node->blob_ptr;
+		if (blob) {
+			hw_cfg.len = blob->length;
+			hw_cfg.payload = blob->data;
+		}
+	} else if (prop_node->prop_flags & DRM_MODE_PROP_RANGE) {
+		val = prop_node->prop_val;
+		hw_cfg.len = sizeof(prop_node->prop_val);
+		hw_cfg.payload = &prop_node->prop_val;
+	} else {
+		DRM_ERROR("property type is not supported\n");
+		return;
+	}
+
+	for (i = 0; i < num_mixers; i++) {
+		if (is_dspp) {
+			if (!sde_crtc->mixers[i].hw_dspp)
+				continue;
+			prop_node->dspp_feature_op(sde_crtc->mixers[i].hw_dspp,
+						   &hw_cfg);
+		} else {
+			if (!sde_crtc->mixers[i].hw_lm)
+				continue;
+			prop_node->lm_feature_op(sde_crtc->mixers[i].hw_lm,
+						 &hw_cfg);
+		}
+	}
+
+	if (blob || val) {
+		DRM_DEBUG_DRIVER("Add feature to active list %d\n",
+				 prop_node->property_id);
+		list_add_tail(&prop_node->active_list, &sde_crtc->active_list);
+	} else {
+		DRM_DEBUG_DRIVER("remove feature from active list %d\n",
+			 prop_node->property_id);
+		list_del_init(&prop_node->active_list);
+	}
+	/* Programming of feature done remove from dirty list */
+	list_del_init(&prop_node->dirty_list);
+}
+
+void sde_cp_crtc_apply_properties(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc = NULL;
+	bool set_dspp_flush = false, set_lm_flush = false;
+	struct sde_color_process_node *prop_node = NULL, *n = NULL;
+	struct sde_hw_ctl *ctl;
+	uint32_t flush_mask = 0;
+	u32 num_mixers = 0, i = 0;
+
+	if (!crtc || !crtc->dev) {
+		DRM_ERROR("invalid crtc %pK dev %pK\n", crtc,
+			  (crtc ? crtc->dev : NULL));
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	if (!sde_crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+		return;
+	}
+
+	num_mixers = sde_crtc->num_mixers;
+	if (!num_mixers) {
+		DRM_DEBUG_DRIVER("no mixers for this crtc\n");
+		return;
+	}
+
+	/* Check if dirty list is empty for early return */
+	if (list_empty(&sde_crtc->dirty_list)) {
+		DRM_DEBUG_DRIVER("Dirty list is empty\n");
+		return;
+	}
+
+	list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list,
+							dirty_list) {
+		sde_cp_crtc_setfeature(prop_node, sde_crtc);
+		/* Set the flush flag to true */
+		if (prop_node->dspp_feature_op)
+			set_dspp_flush = true;
+		else
+			set_lm_flush = true;
+	}
+
+	for (i = 0; i < num_mixers; i++) {
+		ctl = sde_crtc->mixers[i].hw_ctl;
+		if (!ctl)
+			continue;
+		if (set_dspp_flush && ctl->ops.get_bitmask_dspp
+				&& sde_crtc->mixers[i].hw_dspp)
+			ctl->ops.get_bitmask_dspp(ctl,
+					&flush_mask,
+					sde_crtc->mixers[i].hw_dspp->idx);
+			ctl->ops.update_pending_flush(ctl, flush_mask);
+		if (set_lm_flush && ctl->ops.get_bitmask_mixer
+				&& sde_crtc->mixers[i].hw_lm)
+			flush_mask = ctl->ops.get_bitmask_mixer(ctl,
+					sde_crtc->mixers[i].hw_lm->idx);
+			ctl->ops.update_pending_flush(ctl, flush_mask);
+	}
+}
+
+void sde_cp_crtc_install_properties(struct drm_crtc *crtc)
+{
+	struct sde_kms *kms = NULL;
+	struct sde_crtc *sde_crtc = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	unsigned long features = 0;
+	int idx = 0, i = 0;
+	char feature_name[256];
+	struct msm_drm_private *priv;
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_hw_mixer *hw_mixer = NULL;
+
+	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
+		DRM_ERROR("invalid crtc %pK dev %pK\n",
+		       crtc, ((crtc) ? crtc->dev : NULL));
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	kms = get_kms(crtc);
+	if (!kms || !kms->catalog || !sde_crtc) {
+		DRM_ERROR("invalid sde kms %pK catalog %pK sde_crtc %pK\n",
+		 kms, ((kms) ? kms->catalog : NULL), sde_crtc);
+		return;
+	}
+
+	/**
+	 * Function can be called during the atomic_check with test_only flag
+	 * and actual commit. Allocate properties only if feature list is
+	 * empty during the atomic_check with test_only flag.
+	 */
+	if (!list_empty(&sde_crtc->feature_list))
+		return;
+
+	catalog = kms->catalog;
+	idx = sde_cp_crtc_get_mixer_idx(sde_crtc);
+	if (idx < 0 || idx >= catalog->mixer_count) {
+		DRM_ERROR("invalid idx %d\n", idx);
+		return;
+	}
+
+	priv = crtc->dev->dev_private;
+	/**
+	 * DSPP/LM properties are global to all the CRTCS.
+	 * Properties are created for first CRTC and re-used for later
+	 * crtcs.
+	 */
+	if (!priv->cp_property)
+		priv->cp_property = kzalloc((sizeof(priv->cp_property) *
+				SDE_CP_CRTC_MAX_FEATURES), GFP_KERNEL);
+	if (!priv->cp_property)
+		return;
+	memset(feature_name, 0, sizeof(feature_name));
+
+	if (idx >= catalog->dspp_count)
+		goto lm_property;
+
+	/* Check for all the DSPP properties and attach it to CRTC */
+	hw_dspp = sde_crtc->mixers[0].hw_dspp;
+	features = (hw_dspp) ? hw_dspp->cap->features : 0;
+
+	if (!hw_dspp || !hw_dspp->cap->sblk)
+		goto lm_property;
+
+	for (i = 0; i < SDE_DSPP_MAX; i++) {
+		if (!test_bit(i, &features))
+			continue;
+		switch (i) {
+		case SDE_DSPP_PCC:
+			snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+				"SDE_DSPP_PCC_V",
+				(hw_dspp->cap->sblk->pcc.version >> 16));
+			sde_cp_crtc_create_blob_property(crtc, feature_name,
+				&hw_dspp->cap->sblk->pcc,
+				SDE_CP_CRTC_DSPP_PCC,
+				hw_dspp->ops.setup_pcc);
+			break;
+		case SDE_DSPP_HSIC:
+			snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+				"SDE_DSPP_HUE_V",
+				(hw_dspp->cap->sblk->hsic.version >> 16));
+			sde_cp_crtc_install_range_property(crtc, feature_name,
+				&hw_dspp->cap->sblk->hsic,
+				SDE_CP_CRTC_DSPP_HUE, hw_dspp->ops.setup_hue,
+				0, U32_MAX, 0);
+			break;
+		default:
+			break;
+		}
+	}
+
+lm_property:
+	/* Check for all the LM properties and attach it to CRTC */
+	hw_mixer = sde_crtc->mixers[0].hw_lm;
+	features = (hw_mixer) ? hw_mixer->cap->features : 0;
+
+	if (!hw_mixer || !hw_mixer->cap->sblk)
+		return;
+
+	for (i = 0; i < SDE_MIXER_MAX; i++) {
+		if (!test_bit(i, &features))
+			continue;
+		switch (i) {
+		case SDE_MIXER_GC:
+			snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+				 "SDE_LM_GC_V",
+				 (hw_mixer->cap->sblk->gc.version >> 16));
+			sde_cp_crtc_create_blob_property(crtc, feature_name,
+				&hw_mixer->cap->sblk->gc,
+				SDE_CP_CRTC_LM_GC,
+				hw_mixer->ops.setup_gc);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+int sde_cp_crtc_set_property(struct drm_crtc *crtc,
+				struct drm_property *property,
+				uint64_t val)
+{
+	struct sde_color_process_node *prop_node = NULL;
+	struct sde_crtc *sde_crtc = NULL;
+	int ret = 0;
+	u8 found = 0;
+
+	if (!crtc || !property) {
+		DRM_ERROR("invalid crtc %pK property %pK\n", crtc, property);
+		return -EINVAL;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	if (!sde_crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
+		if (property->base.id == prop_node->property_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return 0;
+
+	/* remove the property from dirty list */
+	list_del_init(&prop_node->dirty_list);
+
+	if (!val)
+		ret = sde_cp_disable_crtc_property(crtc, property, prop_node);
+	else
+		ret = sde_cp_enable_crtc_property(crtc, property,
+						  prop_node, val);
+
+	if (!ret) {
+		/* remove the property from active list */
+		list_del_init(&prop_node->active_list);
+		/* Mark the feature as dirty */
+		list_add_tail(&prop_node->dirty_list, &sde_crtc->dirty_list);
+	}
+	return ret;
+}
+
+int sde_cp_crtc_get_property(struct drm_crtc *crtc,
+			     struct drm_property *property, uint64_t *val)
+{
+	struct sde_color_process_node *prop_node = NULL;
+	struct sde_crtc *sde_crtc = NULL;
+	int ret = -EINVAL;
+
+	if (!crtc || !property || !val) {
+		DRM_ERROR("invalid crtc %pK property %pK val %pK\n",
+			  crtc, property, val);
+		return -EINVAL;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	if (!sde_crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
+		if (property->base.id == prop_node->property_id) {
+			*val = prop_node->prop_val;
+			ret = 0;
+			break;
+		}
+	}
+	return ret;
+}
+
+void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc = NULL;
+	struct sde_color_process_node *prop_node = NULL, *n = NULL;
+
+	if (!crtc) {
+		DRM_ERROR("invalid crtc %pK\n", crtc);
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	if (!sde_crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc);
+		return;
+	}
+
+	list_for_each_entry_safe(prop_node, n, &sde_crtc->feature_list,
+				 feature_list) {
+		if (prop_node->prop_flags & DRM_MODE_PROP_BLOB
+		    && prop_node->blob_ptr)
+			drm_property_unreference_blob(prop_node->blob_ptr);
+
+		list_del_init(&prop_node->active_list);
+		list_del_init(&prop_node->dirty_list);
+		list_del_init(&prop_node->feature_list);
+		kfree(prop_node);
+	}
+
+	INIT_LIST_HEAD(&sde_crtc->active_list);
+	INIT_LIST_HEAD(&sde_crtc->dirty_list);
+	INIT_LIST_HEAD(&sde_crtc->feature_list);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
new file mode 100644
index 0000000..2d62eae
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -0,0 +1,72 @@
+
+/* Copyright (c) 2016-2017, 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_COLOR_PROCESSING_H
+#define _SDE_COLOR_PROCESSING_H
+#include <drm/drm_crtc.h>
+
+/**
+ * sde_cp_crtc_init(): Initialize color processing lists for a crtc.
+ *                     Should be called during crtc initialization.
+ * @crtc:  Pointer to sde_crtc.
+ */
+void sde_cp_crtc_init(struct drm_crtc *crtc);
+
+/**
+ * sde_cp_crtc_install_properties(): Installs the color processing
+ *                                properties for a crtc.
+ *                                Should be called during crtc initialization.
+ * @crtc:  Pointer to crtc.
+ */
+void sde_cp_crtc_install_properties(struct drm_crtc *crtc);
+
+/**
+ * sde_cp_crtc_destroy_properties: Destroys color processing
+ *                                            properties for a crtc.
+ * should be called during crtc de-initialization.
+ * @crtc:  Pointer to crtc.
+ */
+void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc);
+
+/**
+ * sde_cp_crtc_set_property: Set a color processing property
+ *                                      for a crtc.
+ *                                      Should be during atomic set property.
+ * @crtc: Pointer to crtc.
+ * @property: Property that needs to enabled/disabled.
+ * @val: Value of property.
+ */
+int sde_cp_crtc_set_property(struct drm_crtc *crtc,
+				struct drm_property *property, uint64_t val);
+
+/**
+ * sde_cp_crtc_apply_properties: Enable/disable properties
+ *                               for a crtc.
+ *                               Should be called during atomic commit call.
+ * @crtc: Pointer to crtc.
+ */
+void sde_cp_crtc_apply_properties(struct drm_crtc *crtc);
+
+/**
+ * sde_cp_crtc_get_property: Get value of color processing property
+ *                                      for a crtc.
+ *                                      Should be during atomic get property.
+ * @crtc: Pointer to crtc.
+ * @property: Property that needs to enabled/disabled.
+ * @val: Value of property.
+ *
+ */
+int sde_cp_crtc_get_property(struct drm_crtc *crtc,
+				struct drm_property *property, uint64_t *val);
+#endif /*_SDE_COLOR_PROCESSING_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 5615885..84aeb46 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -24,6 +24,7 @@
 #include "sde_hw_lm.h"
 #include "sde_hw_ctl.h"
 #include "sde_crtc.h"
+#include "sde_color_processing.h"
 
 #define CTL(i)       (CTL_0 + (i))
 #define LM(i)        (LM_0  + (i))
@@ -64,6 +65,7 @@
 		return;
 
 	msm_property_destroy(&sde_crtc->property_info);
+	sde_cp_crtc_destroy_properties(crtc);
 	debugfs_remove_recursive(sde_crtc->debugfs_root);
 	sde_fence_deinit(&sde_crtc->output_fence);
 
@@ -495,10 +497,11 @@
 	struct sde_crtc_mixer *mixer;
 	struct sde_hw_ctl *last_valid_ctl = NULL;
 	int i;
-	struct sde_rm_hw_iter lm_iter, ctl_iter;
+	struct sde_rm_hw_iter lm_iter, ctl_iter, dspp_iter;
 
 	sde_rm_init_hw_iter(&lm_iter, enc->base.id, SDE_HW_BLK_LM);
 	sde_rm_init_hw_iter(&ctl_iter, enc->base.id, SDE_HW_BLK_CTL);
+	sde_rm_init_hw_iter(&dspp_iter, enc->base.id, SDE_HW_BLK_DSPP);
 
 	/* Set up all the mixers and ctls reserved by this encoder */
 	for (i = sde_crtc->num_mixers; i < ARRAY_SIZE(sde_crtc->mixers); i++) {
@@ -525,6 +528,10 @@
 			return;
 		}
 
+		/* Dspp may be null */
+		(void) sde_rm_get_hw(rm, &dspp_iter);
+		mixer->hw_dspp = (struct sde_hw_dspp *)dspp_iter.hw;
+
 		mixer->encoder = enc;
 
 		sde_crtc->num_mixers++;
@@ -598,6 +605,7 @@
 		return;
 
 	_sde_crtc_blend_setup(crtc);
+	sde_cp_crtc_apply_properties(crtc);
 
 	/*
 	 * PP_DONE irq is only used by command mode for now.
@@ -1026,7 +1034,12 @@
 					property);
 			if (idx == CRTC_PROP_INPUT_FENCE_TIMEOUT)
 				_sde_crtc_set_input_fence_timeout(cstate);
+		} else {
+			ret = sde_cp_crtc_set_property(crtc,
+					property, val);
 		}
+		if (ret)
+			DRM_ERROR("failed to set the property\n");
 	}
 
 	return ret;
@@ -1082,9 +1095,13 @@
 			ret = msm_property_atomic_get(&sde_crtc->property_info,
 					cstate->property_values,
 					cstate->property_blobs, property, val);
+			if (ret)
+				ret = sde_cp_crtc_get_property(crtc,
+					property, val);
 		}
+		if (ret)
+			DRM_ERROR("get property failed\n");
 	}
-
 	return ret;
 }
 
@@ -1194,6 +1211,7 @@
 		return ERR_PTR(-ENOMEM);
 
 	crtc = &sde_crtc->base;
+	crtc->dev = dev;
 
 	sde_crtc->drm_crtc_id = drm_crtc_id;
 	atomic_set(&sde_crtc->drm_requested_vblank, 0);
@@ -1219,6 +1237,7 @@
 			sizeof(struct sde_crtc_state));
 
 	sde_crtc_install_properties(crtc);
+	sde_cp_crtc_init(crtc);
 
 	SDE_DEBUG("%s: successfully initialized crtc\n", sde_crtc->name);
 	return crtc;
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index cee9572..31da094a 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017 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
@@ -28,6 +28,7 @@
  * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC
  * @hw_lm:	LM HW Driver context
  * @hw_ctl:	CTL Path HW driver context
+ * @hw_dspp:	DSPP HW driver context
  * @encoder:	Encoder attached to this lm & ctl
  * @mixer_op_mode: mixer blending operation mode
  * @flush_mask:	mixer flush mask for ctl, mixer and pipe
@@ -35,6 +36,7 @@
 struct sde_crtc_mixer {
 	struct sde_hw_mixer *hw_lm;
 	struct sde_hw_ctl *hw_ctl;
+	struct sde_hw_dspp  *hw_dspp;
 	struct drm_encoder *encoder;
 	u32 mixer_op_mode;
 	u32 flush_mask;
@@ -58,6 +60,9 @@
  * @property_defaults : Array of default values for generic property support
  * @stage_cfg     : H/w mixer stage configuration
  * @debugfs_root  : Parent of debugfs node
+ * @feature_list  : list of color processing features supported on a crtc
+ * @active_list   : list of color processing features are active
+ * @dirty_list    : list of color processing features are dirty
  */
 struct sde_crtc {
 	struct drm_crtc base;
@@ -84,6 +89,10 @@
 
 	struct sde_hw_stage_cfg stage_cfg;
 	struct dentry *debugfs_root;
+
+	struct list_head feature_list;
+	struct list_head active_list;
+	struct list_head dirty_list;
 };
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 69fbe3a..7289240 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -23,6 +23,7 @@
 #include "sde_formats.h"
 #include "sde_encoder_phys.h"
 #include "display_manager.h"
+#include "sde_color_processing.h"
 
 #define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\
 		(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
@@ -309,8 +310,11 @@
 		ret = sde_rm_reserve(&sde_kms->rm, drm_enc, crtc_state,
 				conn_state, true);
 
-	/* Call to populate mode->crtc* information required by framework */
-	drm_mode_set_crtcinfo(adj_mode, 0);
+	if (!ret) {
+		sde_cp_crtc_install_properties(drm_enc->crtc);
+		/* populate mode->crtc* information required by framework */
+		drm_mode_set_crtcinfo(adj_mode, 0);
+	}
 
 	MSM_EVT(drm_enc->dev, adj_mode->flags, adj_mode->private_flags);
 
@@ -437,6 +441,7 @@
 	SDE_DEBUG_ENC(sde_enc, "cleared master\n");
 
 	bs_set(sde_enc, 0);
+	sde_cp_crtc_destroy_properties(drm_enc->crtc);
 
 	sde_rm_release(&sde_kms->rm, drm_enc);
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 62f2ebb..213f49e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -9,12 +9,25 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
+#include "drm/msm_drm_pp.h"
 #include "sde_hw_mdss.h"
 #include "sde_hwio.h"
 #include "sde_hw_catalog.h"
 #include "sde_hw_dspp.h"
 
+#define PCC_ENABLE BIT(0)
+#define PCC_OP_MODE_OFF 0
+#define PCC_CONST_COEFF_OFF 4
+#define PCC_R_COEFF_OFF 0x10
+#define PCC_G_COEFF_OFF 0x1C
+#define PCC_B_COEFF_OFF 0x28
+#define PCC_RG_COEFF_OFF 0x34
+#define PCC_RB_COEFF_OFF 0x40
+#define PCC_GB_COEFF_OFF 0x4C
+#define PCC_RGB_COEFF_OFF 0x58
+#define PCC_CONST_COEFF_MASK 0xFFFF
+#define PCC_COEFF_MASK 0x3FFFF
+
 static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
 		struct sde_mdss_cfg *m,
 		void __iomem *addr,
@@ -51,8 +64,96 @@
 {
 }
 
+void sde_dspp_setup_hue(struct sde_hw_dspp *dspp, void *cfg)
+{
+}
+
 void sde_dspp_setup_pcc(struct sde_hw_dspp *ctx, void *cfg)
 {
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_pcc *pcc;
+	void  __iomem *base;
+
+	if (!hw_cfg  || (hw_cfg->len != sizeof(*pcc)  && hw_cfg->payload)) {
+		DRM_ERROR(
+			"hw_cfg %pK payload %pK payload size %d exp size %zd\n",
+			hw_cfg, (hw_cfg ? hw_cfg->payload : NULL),
+			((hw_cfg) ? hw_cfg->len : 0), sizeof(*pcc));
+		return;
+	}
+	base = ctx->hw.base_off + ctx->cap->base;
+
+	/* Turn off feature */
+	if (!hw_cfg->payload) {
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base,
+			      PCC_OP_MODE_OFF);
+		return;
+	}
+	DRM_DEBUG_DRIVER("Enable PCC feature\n");
+	pcc = hw_cfg->payload;
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_CONST_COEFF_OFF,
+				  pcc->r.c & PCC_CONST_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw,
+		      ctx->cap->sblk->pcc.base + PCC_CONST_COEFF_OFF + 4,
+		      pcc->g.c & PCC_CONST_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw,
+		      ctx->cap->sblk->pcc.base + PCC_CONST_COEFF_OFF + 8,
+		      pcc->b.c & PCC_CONST_COEFF_MASK);
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_R_COEFF_OFF,
+				  pcc->r.r & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_R_COEFF_OFF + 4,
+				  pcc->g.r & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_R_COEFF_OFF + 8,
+				  pcc->b.r & PCC_COEFF_MASK);
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_G_COEFF_OFF,
+				  pcc->r.g & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_G_COEFF_OFF + 4,
+				  pcc->g.g & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_G_COEFF_OFF + 8,
+				  pcc->b.g & PCC_COEFF_MASK);
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_B_COEFF_OFF,
+				  pcc->r.b & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_B_COEFF_OFF + 4,
+				  pcc->g.b & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_B_COEFF_OFF + 8,
+				  pcc->b.b & PCC_COEFF_MASK);
+
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RG_COEFF_OFF,
+				  pcc->r.rg & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RG_COEFF_OFF + 4,
+				  pcc->g.rg & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RG_COEFF_OFF + 8,
+				  pcc->b.rg & PCC_COEFF_MASK);
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RB_COEFF_OFF,
+				  pcc->r.rb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RB_COEFF_OFF + 4,
+				  pcc->g.rb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RB_COEFF_OFF + 8,
+				  pcc->b.rb & PCC_COEFF_MASK);
+
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_GB_COEFF_OFF,
+				  pcc->r.gb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_GB_COEFF_OFF + 4,
+				  pcc->g.gb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_GB_COEFF_OFF + 8,
+				  pcc->b.gb & PCC_COEFF_MASK);
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base + PCC_RGB_COEFF_OFF,
+				  pcc->r.rgb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw,
+		      ctx->cap->sblk->pcc.base + PCC_RGB_COEFF_OFF + 4,
+		      pcc->g.rgb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw,
+		      ctx->cap->sblk->pcc.base + PCC_RGB_COEFF_OFF + 8,
+		      pcc->b.rgb & PCC_COEFF_MASK);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base, PCC_ENABLE);
 }
 
 void sde_dspp_setup_sharpening(struct sde_hw_dspp *ctx, void *cfg)
@@ -75,10 +176,31 @@
 {
 }
 
-static void _setup_dspp_ops(struct sde_hw_dspp_ops *ops,
-		unsigned long features)
+static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
 {
+	int i = 0;
+
+	for (i = 0; i < SDE_DSPP_MAX; i++) {
+		if (!test_bit(i, &features))
+			continue;
+		switch (i) {
+		case SDE_DSPP_PCC:
+			if (c->cap->sblk->pcc.version ==
+				(SDE_COLOR_PROCESS_VER(0x1, 0x0)))
+				c->ops.setup_pcc = sde_dspp_setup_pcc;
+			break;
+		case SDE_DSPP_HSIC:
+			if (c->cap->sblk->hsic.version ==
+				(SDE_COLOR_PROCESS_VER(0x1, 0x0)))
+				c->ops.setup_hue = sde_dspp_setup_hue;
+			break;
+		default:
+			break;
+		}
+
+	}
 }
+
 struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx,
 			void __iomem *addr,
 			struct sde_mdss_cfg *m)
@@ -99,7 +221,7 @@
 	/* Assign ops */
 	c->idx = idx;
 	c->cap = cfg;
-	_setup_dspp_ops(&c->ops, c->cap->features);
+	_setup_dspp_ops(c, c->cap->features);
 
 	return c;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 6ba161a..2c6e638 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -83,12 +83,41 @@
 	 * @cfg: Pointer to configuration
 	 */
 	void (*setup_danger_safe)(struct sde_hw_dspp *ctx, void *cfg);
+
 	/**
 	 * setup_dither - setup dspp dither
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
 	 */
 	void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_hue - setup dspp PA hue
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_hue)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_sat - setup dspp PA saturation
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_sat)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_val - setup dspp PA value
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_val)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_cont - setup dspp PA contrast
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_cont)(struct sde_hw_dspp *ctx, void *cfg);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index b5d31b9..168975c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -151,7 +151,7 @@
 	SDE_REG_WRITE(c, LM_OP_MODE, op_mode);
 }
 
-static void sde_hw_lm_gammacorrection(struct sde_hw_mixer *mixer,
+static void sde_hw_lm_gc(struct sde_hw_mixer *mixer,
 			void *cfg)
 {
 }
@@ -167,7 +167,7 @@
 		ops->setup_blend_config = sde_hw_lm_setup_blend_config;
 	ops->setup_alpha_out = sde_hw_lm_setup_color3;
 	ops->setup_border_color = sde_hw_lm_setup_border_color;
-	ops->setup_gammcorrection = sde_hw_lm_gammacorrection;
+	ops->setup_gc = sde_hw_lm_gc;
 };
 
 struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.h b/drivers/gpu/drm/msm/sde/sde_hw_lm.h
index 1bde86e..ca671f8 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -60,8 +60,10 @@
 	void (*setup_border_color)(struct sde_hw_mixer *ctx,
 		struct sde_mdss_color *color,
 		u8 border_en);
-
-	void (*setup_gammcorrection)(struct sde_hw_mixer *mixer,
+	/**
+	 * setup_gc : enable/disable gamma correction feature
+	 */
+	void (*setup_gc)(struct sde_hw_mixer *mixer,
 			void *cfg);
 
 };
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index aaad568..3bc521a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -424,4 +424,14 @@
 #define SDE_DBG_MASK_TOP      (1 << 9)
 #define SDE_DBG_MASK_VBIF     (1 << 10)
 
+/**
+ * struct sde_hw_cp_cfg: hardware dspp/lm feature payload.
+ * @payload: Feature specific payload.
+ * @len: Length of the payload.
+ */
+struct sde_hw_cp_cfg {
+	void *payload;
+	u32 len;
+};
+
 #endif  /* _SDE_HW_MDSS_H */