drm/msm/sde: Add support for Memory Color in plane

Add support for skin, sky, and foliage memory color hardware in
planes which have color processing hardwares.

Change-Id: I9ec72f44f36939cae90215bc668f3186d140e8b8
Signed-off-by: Benet Clark <benetc@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index dbe52a2..9fa63f8 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -1,4 +1,3 @@
-
 /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -16,6 +15,18 @@
 #define _SDE_COLOR_PROCESSING_H
 #include <drm/drm_crtc.h>
 
+/*
+ * PA MEMORY COLOR types
+ * @MEMCOLOR_SKIN          Skin memory color type
+ * @MEMCOLOR_SKY           Sky memory color type
+ * @MEMCOLOR_FOLIAGE       Foliage memory color type
+ */
+enum sde_memcolor_type {
+	MEMCOLOR_SKIN = 0,
+	MEMCOLOR_SKY,
+	MEMCOLOR_FOLIAGE
+};
+
 /**
  * sde_cp_crtc_init(): Initialize color processing lists for a crtc.
  *                     Should be called during crtc initialization.
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
index 9f49625..47c9bd9 100644
--- 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
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+#include <drm/msm_drm_pp.h>
 #include "sde_hw_color_processing_v1_7.h"
 
 #define PA_HUE_OFF		0x110
@@ -21,6 +22,23 @@
 #define PA_CONT_OFF		0x11C
 #define PA_CONT_MASK		0xFF
 
+#define MEMCOL_PWL0_OFF		0x88
+#define MEMCOL_PWL0_MASK	0xFFFF07FF
+#define MEMCOL_PWL1_OFF		0x8C
+#define MEMCOL_PWL1_MASK	0xFFFFFFFF
+#define MEMCOL_HUE_REGION_OFF	0x90
+#define MEMCOL_HUE_REGION_MASK	0x7FF07FF
+#define MEMCOL_SAT_REGION_OFF	0x94
+#define MEMCOL_SAT_REGION_MASK	0xFFFFFF
+#define MEMCOL_VAL_REGION_OFF	0x98
+#define MEMCOL_VAL_REGION_MASK	0xFFFFFF
+#define MEMCOL_P0_LEN		0x14
+#define MEMCOL_P1_LEN		0x8
+#define MEMCOL_PWL2_OFF		0x218
+#define MEMCOL_PWL2_MASK	0xFFFFFFFF
+#define MEMCOL_BLEND_GAIN_OFF	0x21C
+#define MEMCOL_PWL_HOLD_OFF	0x214
+
 #define VIG_OP_PA_EN		BIT(4)
 #define VIG_OP_PA_SKIN_EN	BIT(5)
 #define VIG_OP_PA_FOL_EN	BIT(6)
@@ -119,3 +137,66 @@
 
 	SDE_REG_WRITE(&ctx->hw, base, opmode);
 }
+
+void sde_setup_pipe_pa_memcol_v1_7(struct sde_hw_pipe *ctx,
+				   enum sde_memcolor_type type,
+				   void *cfg)
+{
+	struct drm_msm_memcol *mc = cfg;
+	u32 base = ctx->cap->sblk->memcolor_blk.base;
+	u32 off, op, mc_en, hold = 0;
+	u32 mc_i = 0;
+
+	switch (type) {
+	case MEMCOLOR_SKIN:
+		mc_en = VIG_OP_PA_SKIN_EN;
+		mc_i = 0;
+		break;
+	case MEMCOLOR_SKY:
+		mc_en = VIG_OP_PA_SKY_EN;
+		mc_i = 1;
+		break;
+	case MEMCOLOR_FOLIAGE:
+		mc_en = VIG_OP_PA_FOL_EN;
+		mc_i = 2;
+		break;
+	default:
+		DRM_ERROR("Invalid memory color type %d\n", type);
+		return;
+	}
+
+	op = SDE_REG_READ(&ctx->hw, base);
+	if (!mc) {
+		op &= ~mc_en;
+		if (PA_DISABLE_REQUIRED(op))
+			op &= ~VIG_OP_PA_EN;
+		SDE_REG_WRITE(&ctx->hw, base, op);
+		return;
+	}
+
+	off = base + (mc_i * MEMCOL_P0_LEN);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_PWL0_OFF),
+		      mc->color_adjust_p0 & MEMCOL_PWL0_MASK);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_PWL1_OFF),
+		      mc->color_adjust_p1 & MEMCOL_PWL1_MASK);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_HUE_REGION_OFF),
+		      mc->hue_region & MEMCOL_HUE_REGION_MASK);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_SAT_REGION_OFF),
+		      mc->sat_region & MEMCOL_SAT_REGION_MASK);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_VAL_REGION_OFF),
+		      mc->val_region & MEMCOL_VAL_REGION_MASK);
+
+	off = base + (mc_i * MEMCOL_P1_LEN);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_PWL2_OFF),
+		      mc->color_adjust_p2 & MEMCOL_PWL2_MASK);
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_BLEND_GAIN_OFF), mc->blend_gain);
+
+	hold = SDE_REG_READ(&ctx->hw, off + MEMCOL_PWL_HOLD_OFF);
+	hold &= ~(0xF << (mc_i * 4));
+	hold |= ((mc->sat_hold & 0x3) << (mc_i * 4));
+	hold |= ((mc->val_hold & 0x3) << ((mc_i * 4) + 2));
+	SDE_REG_WRITE(&ctx->hw, (off + MEMCOL_PWL_HOLD_OFF), hold);
+
+	op |= VIG_OP_PA_EN | mc_en;
+	SDE_REG_WRITE(&ctx->hw, base, op);
+}
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
index 788bde0..174ed85 100644
--- 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
@@ -43,4 +43,14 @@
  */
 void sde_setup_pipe_pa_cont_v1_7(struct sde_hw_pipe *ctx, void *cfg);
 
+/**
+ * sde_setup_pipe_pa_memcol_v1_7 - setup SSPP memory color in v1.7 hardware
+ * @ctx: Pointer to pipe context
+ * @type: Memory color type (Skin, sky, or foliage)
+ * @cfg: Pointer to memory color config data
+ */
+void sde_setup_pipe_pa_memcol_v1_7(struct sde_hw_pipe *ctx,
+				   enum sde_memcolor_type type,
+				   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 dae36ef..929c59a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -879,6 +879,13 @@
 			c->ops.setup_pa_cont = sde_setup_pipe_pa_cont_v1_7;
 		}
 	}
+
+	if (test_bit(SDE_SSPP_MEMCOLOR, &features)) {
+		if (c->cap->sblk->memcolor_blk.version ==
+			(SDE_COLOR_PROCESS_VER(0x1, 0x7)))
+			c->ops.setup_pa_memcolor =
+				sde_setup_pipe_pa_memcol_v1_7;
+	}
 }
 
 static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 85f2ae6..743f5e7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -17,6 +17,7 @@
 #include "sde_hw_mdss.h"
 #include "sde_hw_util.h"
 #include "sde_formats.h"
+#include "sde_color_processing.h"
 
 struct sde_hw_pipe;
 
@@ -358,11 +359,11 @@
 	/**
 	 * setup_pa_memcolor - setup source color processing
 	 * @ctx: Pointer to pipe context
-	 * @memcolortype: Memcolor type
-	 * @en: PA enable
+	 * @type: Memcolor type (Skin, sky or foliage)
+	 * @cfg: Pointer to memory color config data
 	 */
 	void (*setup_pa_memcolor)(struct sde_hw_pipe *ctx,
-			u32 memcolortype, u8 en);
+			enum sde_memcolor_type type, void *cfg);
 
 	/**
 	 * setup_igc - setup inverse gamma correction
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 70b6d25..5368cbd 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -14,6 +14,7 @@
 
 #include <linux/debugfs.h>
 #include <uapi/drm/sde_drm.h>
+#include <uapi/drm/msm_drm_pp.h>
 
 #include "msm_prop.h"
 
@@ -25,6 +26,7 @@
 #include "sde_crtc.h"
 #include "sde_vbif.h"
 #include "sde_plane.h"
+#include "sde_color_processing.h"
 
 #define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\
 		(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
@@ -779,6 +781,8 @@
 	struct sde_plane *psde;
 	struct sde_plane_state *pstate;
 	uint32_t hue, saturation, value, contrast;
+	struct drm_msm_memcol *memcol = NULL;
+	size_t memcol_sz = 0;
 
 	psde = to_sde_plane(plane);
 	pstate = to_sde_plane_state(plane->state);
@@ -798,8 +802,33 @@
 		PLANE_PROP_CONTRAST_ADJUST);
 	if (psde->pipe_hw->ops.setup_pa_cont)
 		psde->pipe_hw->ops.setup_pa_cont(psde->pipe_hw, &contrast);
-}
 
+	if (psde->pipe_hw->ops.setup_pa_memcolor) {
+		/* Skin memory color setup */
+		memcol = msm_property_get_blob(&psde->property_info,
+					pstate->property_blobs,
+					&memcol_sz,
+					PLANE_PROP_SKIN_COLOR);
+		psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw,
+					MEMCOLOR_SKIN, memcol);
+
+		/* Sky memory color setup */
+		memcol = msm_property_get_blob(&psde->property_info,
+					pstate->property_blobs,
+					&memcol_sz,
+					PLANE_PROP_SKY_COLOR);
+		psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw,
+					MEMCOLOR_SKY, memcol);
+
+		/* Foliage memory color setup */
+		memcol = msm_property_get_blob(&psde->property_info,
+					pstate->property_blobs,
+					&memcol_sz,
+					PLANE_PROP_FOLIAGE_COLOR);
+		psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw,
+					MEMCOLOR_FOLIAGE, memcol);
+	}
+}
 
 static void _sde_plane_setup_scaler(struct sde_plane *psde,
 		const struct sde_format *fmt,
@@ -1602,6 +1631,24 @@
 			info->data, info->len, PLANE_PROP_INFO);
 
 	kfree(info);
+
+	if (psde->features & BIT(SDE_SSPP_MEMCOLOR)) {
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_SKIN_COLOR_V",
+			psde->pipe_sblk->memcolor_blk.version >> 16);
+		msm_property_install_blob(&psde->property_info, feature_name, 0,
+			PLANE_PROP_SKIN_COLOR);
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_SKY_COLOR_V",
+			psde->pipe_sblk->memcolor_blk.version >> 16);
+		msm_property_install_blob(&psde->property_info, feature_name, 0,
+			PLANE_PROP_SKY_COLOR);
+		snprintf(feature_name, sizeof(feature_name), "%s%d",
+			"SDE_SSPP_FOLIAGE_COLOR_V",
+			psde->pipe_sblk->memcolor_blk.version >> 16);
+		msm_property_install_blob(&psde->property_info, feature_name, 0,
+			PLANE_PROP_FOLIAGE_COLOR);
+	}
 }
 
 static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr)