video: msm: Add QSEED API to MDP_PP IOCTL

Add the ability to program the QSEED Table 1 coefficients to the features
supported by the MSMFB_MDP_PP IOCTL

Doesn't support table2 writes currently, however, the code to write to them
is already present.

QSEED support is currently only configurable via a tuneable parameter that
maps to a small subset of possible  QSEED settings. By moving to the
MSMFB_MDP_PP IOCTL, QSEEDs tables will be able to programmed to any value
requested by userspace. This also allows for future expasion of the QSEED
feature by allowing dynamic loading of the table2 coefficients (rather than
only configuring them at init).

Change-Id: I7394e211169efb0e677391e2c150a6a6b8d0477c
Signed-off-by: Carl Vanderlip <carlv@codeaurora.org>
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 9f8b5c6..b156d52 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -3205,3 +3205,90 @@
 error:
 	return ret;
 }
+
+#define QSEED_TABLE_1_COUNT	2
+#define QSEED_TABLE_2_COUNT	1024
+
+static uint32_t mdp4_pp_block2qseed(uint32_t block)
+{
+	uint32_t valid = 0;
+	switch (block) {
+	case MDP_BLOCK_VG_1:
+	case MDP_BLOCK_VG_2:
+		valid = 0x1;
+		break;
+	default:
+		break;
+	}
+	return valid;
+}
+
+static int mdp4_qseed_write_cfg(struct mdp_qseed_cfg_data *cfg)
+{
+	int i, ret = 0;
+	uint32_t base = (uint32_t) (MDP_BASE + mdp_block2base(cfg->block));
+	uint32_t *values;
+
+	if ((cfg->table_num != 1) && (cfg->table_num != 2)) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	if (((cfg->table_num == 1) && (cfg->len != QSEED_TABLE_1_COUNT)) ||
+		((cfg->table_num == 2) && (cfg->len != QSEED_TABLE_2_COUNT))) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	values = kmalloc(cfg->len * sizeof(uint32_t), GFP_KERNEL);
+	if (!values) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	ret = copy_from_user(values, cfg->data, sizeof(uint32_t) * cfg->len);
+
+	base += (cfg->table_num == 1) ? MDP4_QSEED_TABLE1_OFF :
+						MDP4_QSEED_TABLE2_OFF;
+	for (i = 0; i < cfg->len; i++) {
+		MDP_OUTP(base , values[i]);
+		base += sizeof(uint32_t);
+	}
+
+	kfree(values);
+error:
+	return ret;
+}
+
+int mdp4_qseed_cfg(struct mdp_qseed_cfg_data *cfg)
+{
+	int ret = 0;
+
+	if (!mdp4_pp_block2qseed(cfg->block)) {
+		ret = -ENOTTY;
+		goto error;
+	}
+
+	if (cfg->table_num != 1) {
+		ret = -ENOTTY;
+		pr_info("%s: Only QSEED table1 supported.\n", __func__);
+		goto error;
+	}
+
+	switch ((cfg->ops & 0x6) >> 1) {
+	case 0x1:
+		pr_info("%s: QSEED read not supported\n", __func__);
+		ret = -ENOTTY;
+		break;
+	case 0x2:
+		ret = mdp4_qseed_write_cfg(cfg);
+		if (ret)
+			goto error;
+		break;
+	default:
+		break;
+	}
+
+error:
+	return ret;
+}