drm/msm/sde: add support for histogram properties

PA histogram feature is needed for ABA features in display
postprocessing. Change adds histogram control property to
enable and disable histogram feature. Add histogram interrupt
property to enable histogram interrupt notification. Add
histogram operation support to control histogram feature and
read histogram data for version 1.7.

Change-Id: Ifee38b42e4322f703406fe2468d960e6b174fffc
Signed-off-by: Xu Yang <yangxu@codeaurora.org>
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 6051106..31d5bd6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -200,6 +200,7 @@
 			qcom,sde-dspp-gamut = <0x1000 0x00040000>;
 			qcom,sde-dspp-pcc = <0x1700 0x00040000>;
 			qcom,sde-dspp-gc = <0x17c0 0x00010008>;
+			qcom,sde-dspp-hist = <0x800 0x00010007>;
 		};
 
 		qcom,platform-supply-entries {
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index f4f48c8..80b5b52 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -62,6 +62,8 @@
 
 static void dspp_igc_install_property(struct drm_crtc *crtc);
 
+static void dspp_hist_install_property(struct drm_crtc *crtc);
+
 typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc);
 
 static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX];
@@ -86,6 +88,7 @@
 	func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \
 	func[SDE_DSPP_GC] = dspp_gc_install_property; \
 	func[SDE_DSPP_IGC] = dspp_igc_install_property; \
+	func[SDE_DSPP_HIST] = dspp_hist_install_property; \
 } while (0)
 
 typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -111,7 +114,8 @@
 	SDE_CP_CRTC_DSPP_SIXZONE,
 	SDE_CP_CRTC_DSPP_GAMUT,
 	SDE_CP_CRTC_DSPP_DITHER,
-	SDE_CP_CRTC_DSPP_HIST,
+	SDE_CP_CRTC_DSPP_HIST_CTRL,
+	SDE_CP_CRTC_DSPP_HIST_IRQ,
 	SDE_CP_CRTC_DSPP_AD,
 	SDE_CP_CRTC_DSPP_VLUT,
 	SDE_CP_CRTC_DSPP_AD_MODE,
@@ -532,6 +536,42 @@
 	sde_cp_crtc_prop_attach(&prop_attach);
 }
 
+static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc)
+{
+	struct drm_crtc *crtc_drm = &sde_crtc->base;
+	struct sde_kms *kms = NULL;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_dspp *hw_dspp = NULL;
+	int i, irq_idx;
+
+	if (!crtc_drm) {
+		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
+		return;
+	}
+
+	kms = get_kms(crtc_drm);
+
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		hw_lm = sde_crtc->mixers[i].hw_lm;
+		hw_dspp = sde_crtc->mixers[i].hw_dspp;
+		if (!hw_lm->cfg.right_mixer)
+			break;
+	}
+
+	if (!hw_dspp) {
+		DRM_ERROR("invalid dspp\n");
+		return;
+	}
+
+	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
+					hw_dspp->idx);
+	if (irq_idx < 0) {
+		DRM_ERROR("failed to get irq idx\n");
+		return;
+	}
+	sde_core_irq_enable(kms, &irq_idx, 1);
+}
+
 static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
 				   struct sde_crtc *sde_crtc)
 {
@@ -640,6 +680,21 @@
 			}
 			hw_lm->ops.setup_gc(hw_lm, &hw_cfg);
 			break;
+		case SDE_CP_CRTC_DSPP_HIST_CTRL:
+			if (!hw_dspp || !hw_dspp->ops.setup_histogram) {
+				ret = -EINVAL;
+				continue;
+			}
+			hw_dspp->ops.setup_histogram(hw_dspp, &feature_enabled);
+			break;
+		case SDE_CP_CRTC_DSPP_HIST_IRQ:
+			if (!hw_dspp || !hw_lm) {
+				ret = -EINVAL;
+				continue;
+			}
+			if (!hw_lm->cfg.right_mixer)
+				_sde_cp_crtc_enable_hist_irq(sde_crtc);
+			break;
 		case SDE_CP_CRTC_DSPP_AD_MODE:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
 				ret = -EINVAL;
@@ -1295,6 +1350,30 @@
 	}
 }
 
+static void dspp_hist_install_property(struct drm_crtc *crtc)
+{
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+
+	version = catalog->dspp[0].sblk->hist.version >> 16;
+	switch (version) {
+	case 1:
+		sde_cp_crtc_install_enum_property(crtc,
+			SDE_CP_CRTC_DSPP_HIST_CTRL, sde_hist_modes,
+			ARRAY_SIZE(sde_hist_modes), "SDE_DSPP_HIST_CTRL_V1");
+		sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_HIST_IRQ_V1",
+			SDE_CP_CRTC_DSPP_HIST_IRQ, 0, U16_MAX, 0);
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
 static void sde_cp_update_list(struct sde_cp_node *prop_node,
 		struct sde_crtc *crtc, bool dirty_list)
 {
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index 08e345d..53f0844 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -29,6 +29,25 @@
 	MEMCOLOR_FOLIAGE
 };
 
+/*
+ * PA HISTOGRAM modes
+ * @HIST_DISABLED          Histogram disabled
+ * @HIST_ENABLED           Histogram enabled
+ */
+enum sde_hist_modes {
+	HIST_DISABLED,
+	HIST_ENABLED
+};
+
+/**
+ * struct drm_prop_enum_list - drm structure for creating enum property and
+ *                             enumerating values
+ */
+static const struct drm_prop_enum_list sde_hist_modes[] = {
+	{HIST_DISABLED, "hist_off"},
+	{HIST_ENABLED, "hist_on"},
+};
+
 /**
  * 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 4191367..900a489 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
@@ -23,6 +23,9 @@
 #define PA_VAL_DSPP_OFF		0x240
 #define PA_CONT_DSPP_OFF	0x244
 
+#define PA_HIST_CTRL_DSPP_OFF	0x4
+#define PA_HIST_DATA_DSPP_OFF	0x400
+
 #define PA_LUTV_DSPP_OFF	0x1400
 #define PA_LUT_SWAP_OFF		0x234
 
@@ -66,6 +69,7 @@
 #define DSPP_OP_PA_CONT_EN	BIT(28)
 #define DSPP_OP_PA_EN		BIT(20)
 #define DSPP_OP_PA_LUTV_EN	BIT(19)
+#define DSPP_OP_PA_HIST_EN	BIT(16)
 #define DSPP_OP_PA_SKIN_EN	BIT(5)
 #define DSPP_OP_PA_FOL_EN	BIT(6)
 #define DSPP_OP_PA_SKY_EN	BIT(7)
@@ -503,3 +507,69 @@
 	i = BIT(0) | ((payload->flags & PGC_8B_ROUND) ? BIT(1) : 0);
 	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, i);
 }
+
+void sde_setup_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	u32 base, offset;
+	u32 op_mode;
+	bool feature_enabled;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid parameters ctx %pK cfg %pK", ctx, cfg);
+		return;
+	}
+
+	feature_enabled = *(bool *)cfg;
+	base = ctx->cap->sblk->hist.base;
+	offset = base + PA_HIST_CTRL_DSPP_OFF;
+
+	op_mode = SDE_REG_READ(&ctx->hw, base);
+	if (!feature_enabled) {
+		op_mode &= ~DSPP_OP_PA_HIST_EN;
+		if (PA_DSPP_DISABLE_REQUIRED(op_mode))
+			op_mode &= ~DSPP_OP_PA_EN;
+	} else {
+		op_mode |= DSPP_OP_PA_HIST_EN | DSPP_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, offset, 0);
+	SDE_REG_WRITE(&ctx->hw, base, op_mode);
+}
+
+void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_hist *hist_data;
+	u32 offset, offset_ctl;
+	u32 i;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid parameters ctx %pK cfg %pK", ctx, cfg);
+		return;
+	}
+
+	hist_data = (struct drm_msm_hist *)cfg;
+	offset = ctx->cap->sblk->hist.base + PA_HIST_DATA_DSPP_OFF;
+	offset_ctl = ctx->cap->sblk->hist.base + PA_HIST_CTRL_DSPP_OFF;
+
+	for (i = 0; i < HIST_V_SIZE; i++)
+		hist_data->data[i] = SDE_REG_READ(&ctx->hw, offset + i * 4) &
+					REG_MASK(24);
+
+	/* unlock hist buffer */
+	SDE_REG_WRITE(&ctx->hw, offset_ctl, 0);
+}
+
+void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	u32 offset_ctl;
+
+	if (!ctx) {
+		DRM_ERROR("invalid parameters ctx %pK", ctx);
+		return;
+	}
+
+	offset_ctl = ctx->cap->sblk->hist.base + PA_HIST_CTRL_DSPP_OFF;
+
+	/* lock hist buffer */
+	SDE_REG_WRITE(&ctx->hw, offset_ctl, 1);
+}
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 25e446b7..5e54b66 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
@@ -82,4 +82,23 @@
  */
 void sde_setup_dspp_gc_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 
+/**
+ * sde_setup_dspp_hist_v1_7 - setup DSPP histogram feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to histogram control data
+ */
+void sde_setup_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_read_dspp_hist_v1_7 - read DSPP histogram data in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to histogram data
+ */
+void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_lock_dspp_hist_v1_7 - lock DSPP histogram buffer in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ */
+void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 #endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 5b3f51e..636282e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -87,6 +87,17 @@
 					reg_dmav1_setup_dspp_vlutv18;
 			}
 			break;
+		case SDE_DSPP_HIST:
+			if (c->cap->sblk->hist.version ==
+				(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
+				c->ops.setup_histogram =
+				    sde_setup_dspp_hist_v1_7;
+				c->ops.read_histogram =
+				    sde_read_dspp_hist_v1_7;
+				c->ops.lock_histogram =
+				    sde_lock_dspp_hist_v1_7;
+			}
+			break;
 		case SDE_DSPP_GAMUT:
 			if (c->cap->sblk->gamut.version ==
 					SDE_COLOR_PROCESS_VER(0x4, 0)) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 44b3831..4878fc6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -38,6 +38,13 @@
 	void (*read_histogram)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
+	 * lock_histogram - lock dspp histogram buffer
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*lock_histogram)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
 	 * setup_igc - update dspp igc
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index 5f70a57..0765527 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -174,6 +174,17 @@
 	__u32 strength;
 };
 
+#define HIST_V_SIZE 256
+/**
+ * struct drm_msm_hist - histogram feature structure
+ * @flags: for customizing operations
+ * @data: histogram data
+ */
+struct drm_msm_hist {
+	__u64 flags;
+	__u32 data[HIST_V_SIZE];
+};
+
 #define AD4_LUT_GRP0_SIZE 33
 #define AD4_LUT_GRP1_SIZE 32
 /*