Merge "drm/msm/sde: Add reg dma programming support for vlut" into msm-4.8
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 2713f67..e84f4ca 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -47,6 +47,7 @@
 	sde/sde_vbif.o \
 	sde_dbg_evtlog.o \
 	sde_io_util.o \
+	sde/sde_hw_reg_dma_v1_color_proc.o \
 
 # use drm gpu driver only if qcom_kgsl driver not available
 ifneq ($(CONFIG_QCOM_KGSL),y)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
index a30e1a5..8ab2209 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
@@ -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
@@ -14,5 +14,6 @@
 #define _SDE_HW_COLOR_PROCESSING_H
 
 #include "sde_hw_color_processing_v1_7.h"
+#include "sde_hw_reg_dma_v1_color_proc.h"
 
 #endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 66c03f0..f11c065 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -36,33 +36,9 @@
 	return ERR_PTR(-EINVAL);
 }
 
-void sde_dspp_setup_histogram(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_read_histogram(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_update_igc(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_setup_sharpening(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_setup_danger_safe(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_setup_dither(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
 static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
 {
-	int i = 0;
+	int i = 0, ret;
 
 	for (i = 0; i < SDE_DSPP_MAX; i++) {
 		if (!test_bit(i, &features))
@@ -81,8 +57,16 @@
 		case SDE_DSPP_VLUT:
 			if (c->cap->sblk->vlut.version ==
 				(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
-				c->ops.setup_vlut = sde_setup_dspp_pa_vlut_v1_7;
+				c->ops.setup_vlut =
+				    sde_setup_dspp_pa_vlut_v1_7;
+			} else if (c->cap->sblk->vlut.version ==
+					(SDE_COLOR_PROCESS_VER(0x1, 0x8))) {
+				ret = reg_dmav1_init_dspp_op_v4(i, c->idx);
+				if (ret)
+					c->ops.setup_vlut =
+					reg_dmav1_setup_dspp_vlutv18;
 			}
+			break;
 		default:
 			break;
 		}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
new file mode 100644
index 0000000..18986b2
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
@@ -0,0 +1,302 @@
+/* Copyright (c) 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.
+ */
+#include <drm/msm_drm_pp.h>
+#include "sde_reg_dma.h"
+#include "sde_hw_reg_dma_v1_color_proc.h"
+
+/* Reserve space of 128 words for LUT dma payload set-up */
+#define REG_DMA_HEADERS_BUFFER_SZ (sizeof(u32) * 128)
+
+#define VLUT_MEM_SIZE ((128 * sizeof(u32)) + REG_DMA_HEADERS_BUFFER_SZ)
+#define VLUT_LEN (128 * sizeof(u32))
+#define PA_OP_MODE_OFF 0x800
+#define PA_LUTV_OPMODE_OFF 0x84c
+
+#define GAMUT_LUT_MEM_SIZE ((sizeof(struct drm_msm_3d_gamut)) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+
+#define REG_MASK(n) ((BIT(n)) - 1)
+
+static struct sde_reg_dma_buffer *dspp_buf[REG_DMA_FEATURES_MAX][DSPP_MAX];
+
+static u32 feature_map[SDE_DSPP_MAX] = {
+	[SDE_DSPP_VLUT] = VLUT,
+	[SDE_DSPP_GAMUT] = GAMUT,
+	[SDE_DSPP_IGC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_PCC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_GC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_HSIC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_MEMCOLOR] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_SIXZONE] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
+};
+
+static u32 feature_reg_dma_sz[SDE_DSPP_MAX] = {
+	[SDE_DSPP_VLUT] = VLUT_MEM_SIZE,
+	[SDE_DSPP_GAMUT] = GAMUT_LUT_MEM_SIZE,
+};
+
+static u32 dspp_mapping[DSPP_MAX] = {
+	[DSPP_0] = DSPP0,
+	[DSPP_1] = DSPP1,
+	[DSPP_2] = DSPP2,
+	[DSPP_3] = DSPP3,
+};
+
+#define REG_DMA_OP_SETUP(cfg, block, reg_dma_feature, op, buf) \
+	do { \
+		(cfg).blk = block; \
+		(cfg).feature = reg_dma_feature; \
+		(cfg).ops = op; \
+		(cfg).dma_buf = buf; \
+	} while (0)
+
+static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 sz);
+static int reg_dma_dspp_check(struct sde_hw_dspp *ctx, void *cfg,
+		enum sde_reg_dma_features feature);
+static int reg_dma_blk_select(enum sde_reg_dma_features feature,
+		enum sde_reg_dma_blk blk, struct sde_reg_dma_buffer *dma_buf);
+static int reg_dma_write(enum sde_reg_dma_setup_ops ops, u32 off, u32 data_sz,
+			u32 *data, struct sde_reg_dma_buffer *dma_buf,
+			enum sde_reg_dma_features feature,
+			enum sde_reg_dma_blk blk);
+static int reg_dma_kick_off(enum sde_reg_dma_op op, enum sde_reg_dma_queue q,
+		enum sde_reg_dma_trigger_mode mode,
+		struct sde_reg_dma_buffer *dma_buf, struct sde_hw_ctl *ctl);
+
+
+static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 size)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+
+	dma_ops = sde_reg_dma_get_ops();
+	if (IS_ERR_OR_NULL(dma_ops))
+		return -ENOTSUPP;
+
+	if (!buf) {
+		DRM_ERROR("invalid buf\n");
+		return -EINVAL;
+	}
+
+	/* buffer already initialized */
+	if (*buf)
+		return 0;
+
+	*buf = dma_ops->alloc_reg_dma_buf(size);
+	if (IS_ERR_OR_NULL(*buf))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int reg_dma_dspp_check(struct sde_hw_dspp *ctx, void *cfg,
+		enum sde_reg_dma_features feature)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+
+	if (!cfg || !ctx) {
+		DRM_ERROR("invalid cfg %pK ctx %pK\n", cfg, ctx);
+		return -EINVAL;
+	}
+
+	dma_ops = sde_reg_dma_get_ops();
+	if (IS_ERR_OR_NULL(dma_ops))
+		return -EINVAL;
+
+	if (!hw_cfg->ctl || ctx->idx >= DSPP_MAX ||
+		feature >= REG_DMA_FEATURES_MAX) {
+		DRM_ERROR("invalid ctl %pK dspp idx %d feature %d\n",
+			hw_cfg->ctl, ctx->idx, feature);
+		return -EINVAL;
+	}
+
+	if (!dspp_buf[feature][ctx->idx]) {
+		DRM_ERROR("invalid dma_buf\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int reg_dma_blk_select(enum sde_reg_dma_features feature,
+		enum sde_reg_dma_blk blk, struct sde_reg_dma_buffer *dma_buf)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	int rc = 0;
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dma_buf);
+	memset(&dma_write_cfg, 0, sizeof(dma_write_cfg));
+	dma_write_cfg.blk = blk;
+	dma_write_cfg.feature = feature;
+	dma_write_cfg.ops = HW_BLK_SELECT;
+	dma_write_cfg.dma_buf = dma_buf;
+
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc)
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+
+	return rc;
+}
+
+static int reg_dma_write(enum sde_reg_dma_setup_ops ops, u32 off, u32 data_sz,
+			u32 *data, struct sde_reg_dma_buffer *dma_buf,
+			enum sde_reg_dma_features feature,
+			enum sde_reg_dma_blk blk)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	int rc;
+
+	dma_ops = sde_reg_dma_get_ops();
+	memset(&dma_write_cfg, 0, sizeof(dma_write_cfg));
+
+	dma_write_cfg.ops = ops;
+	dma_write_cfg.blk_offset = off;
+	dma_write_cfg.data_size = data_sz;
+	dma_write_cfg.data = data;
+	dma_write_cfg.dma_buf = dma_buf;
+	dma_write_cfg.feature = feature;
+	dma_write_cfg.blk = blk;
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc)
+		DRM_ERROR("write single reg failed ret %d\n", rc);
+
+	return rc;
+}
+
+static int reg_dma_kick_off(enum sde_reg_dma_op op, enum sde_reg_dma_queue q,
+		enum sde_reg_dma_trigger_mode mode,
+		struct sde_reg_dma_buffer *dma_buf, struct sde_hw_ctl *ctl)
+{
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	int rc;
+
+	dma_ops = sde_reg_dma_get_ops();
+	memset(&kick_off, 0, sizeof(kick_off));
+	kick_off.ctl = ctl;
+	kick_off.dma_buf = dma_buf;
+	kick_off.op = op;
+	kick_off.queue_select = q;
+	kick_off.trigger_mode = mode;
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc)
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+
+	return rc;
+}
+
+int reg_dmav1_init_dspp_op_v4(int feature, enum sde_dspp idx)
+{
+	int rc = -ENOTSUPP;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	bool is_supported = false;
+
+	if (feature >= SDE_DSPP_MAX || idx >= DSPP_MAX) {
+		DRM_ERROR("invalid feature %x max %x dspp idx %x max %xd\n",
+			feature, SDE_DSPP_MAX, idx, DSPP_MAX);
+		return rc;
+	}
+
+	if (feature_map[feature] >= REG_DMA_FEATURES_MAX) {
+		DRM_ERROR("invalid feature map %d for feature %d\n",
+			feature_map[feature], feature);
+		return -ENOTSUPP;
+	}
+
+	dma_ops = sde_reg_dma_get_ops();
+	if (IS_ERR_OR_NULL(dma_ops))
+		return -ENOTSUPP;
+
+	rc = dma_ops->check_support(feature_map[feature], dspp_mapping[idx],
+			&is_supported);
+	if (!rc)
+		rc = (is_supported) ? 0 : -ENOTSUPP;
+
+	if (!rc)
+		rc = reg_dma_buf_init(&dspp_buf[feature_map[feature]][idx],
+				feature_reg_dma_sz[feature]);
+
+	return rc;
+}
+
+int reg_dmav1_init_sspp_op_v4(int feature, enum sde_sspp idx)
+{
+	return -ENOTSUPP;
+}
+
+void reg_dmav1_setup_dspp_vlutv18(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_pa_vlut *payload = NULL;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	u32 op_mode;
+	u32 *data = NULL;
+	int i, j, rc = 0;
+
+	rc = reg_dma_dspp_check(ctx, cfg, VLUT);
+	if (rc)
+		return;
+
+	op_mode = SDE_REG_READ(&ctx->hw, PA_OP_MODE_OFF);
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("Disable vlut feature\n");
+		SDE_REG_WRITE(&ctx->hw, PA_LUTV_OPMODE_OFF, 0);
+		if (op_mode & (~(BIT(20))))
+			op_mode = 0;
+		SDE_REG_WRITE(&ctx->hw, PA_OP_MODE_OFF, op_mode);
+		return;
+	}
+
+	rc = reg_dma_blk_select(VLUT, dspp_mapping[ctx->idx],
+			dspp_buf[VLUT][ctx->idx]);
+	if (rc) {
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+		return;
+	}
+
+	data = kzalloc(VLUT_LEN, GFP_KERNEL);
+	if (!data)
+		return;
+
+	payload = hw_cfg->payload;
+	DRM_DEBUG_DRIVER("Enable vlut feature flags %llx\n", payload->flags);
+	for (i = 0, j = 0; i < ARRAY_SIZE(payload->val); i += 2, j++)
+		data[j] = (payload->val[i] & REG_MASK(10)) |
+			((payload->val[i + 1] & REG_MASK(10)) << 16);
+
+	rc = reg_dma_write(REG_BLK_WRITE_SINGLE, ctx->cap->sblk->vlut.base,
+			VLUT_LEN, data,
+			dspp_buf[VLUT][ctx->idx], VLUT,
+			dspp_mapping[ctx->idx]);
+	if (rc) {
+		DRM_ERROR("write single reg failed ret %d\n", rc);
+		goto exit;
+	}
+
+	rc = reg_dma_kick_off(REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE,
+			dspp_buf[VLUT][ctx->idx], hw_cfg->ctl);
+	if (rc) {
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+		goto exit;
+	}
+	SDE_REG_WRITE(&ctx->hw, PA_LUTV_OPMODE_OFF, BIT(0));
+	SDE_REG_WRITE(&ctx->hw, PA_OP_MODE_OFF, op_mode | BIT(20));
+
+exit:
+	kfree(data);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
new file mode 100644
index 0000000..b82267c
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 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_HW_REG_DMA_V1_COLOR_PROC_H
+#define _SDE_HW_REG_DMA_V1_COLOR_PROC_H
+
+#include "sde_hw_util.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_dspp.h"
+
+/**
+ * reg_dmav1_init_dspp_op_v4() - initialize the dspp feature op for sde v4
+ *                               using reg dma v1.
+ * @feature: dspp feature
+ * idx: dspp idx
+ */
+int reg_dmav1_init_dspp_op_v4(int feature, enum sde_dspp idx);
+
+/**
+ * reg_dma_init_sspp_op_v4() - initialize the sspp feature op for sde v4
+ * @feature: sspp feature
+ * @idx: sspp idx
+ */
+int reg_dmav1_init_sspp_op_v4(int feature, enum sde_sspp idx);
+
+/**
+ * reg_dmav1_setup_dspp_vlutv18() - vlut v18 implementation using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_vlutv18(struct sde_hw_dspp *ctx, void *cfg);
+
+#endif /* _SDE_HW_REG_DMA_V1_COLOR_PROC_H */