[Intel-gfx] [PATCH 1/6] drm/i915: Add Color manager framework

Shashank Sharma shashank.sharma at intel.com
Thu Feb 20 13:37:22 CET 2014


Intel color manager is a new framework to provide control over
few of the color properties (supported by intel Gen 7 and+
hardware) via sysfs interface. Currently supported properties
are:
1. CSC correction (wide gamute)
2. Gamma correction
3. Hue and Saturation correction
4. Brightness and contrast correction

This patch contains basic implementation of color manager
framework consisting:
1. A register function, which gets called from a dispaly
   while initializing its DRM connector(for example eDP
   and Mipi).
2. Read and write functions for /sysfs interface
3. A command and a data parser.
4. Dummy prototypes for color correction functions.

The sysfs entry will be created at:
/sys/class/drm/<connector-name>/color-manager
Signed-off-by: Shashank Sharma <shashank.sharma at intel.com>
Signed-off-by: Uma Shankar <uma.shankar at intel.com>
---
 drivers/gpu/drm/i915/Makefile       |    1 +
 drivers/gpu/drm/i915/i915_drv.h     |   29 ++
 drivers/gpu/drm/i915/intel_clrmgr.c |  643 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_clrmgr.h |  238 +++++++++++++
 drivers/gpu/drm/i915/intel_dp.c     |    4 +
 drivers/gpu/drm/i915/intel_dsi.c    |    4 +
 6 files changed, 919 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/intel_clrmgr.c
 create mode 100644 drivers/gpu/drm/i915/intel_clrmgr.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 4850494..0d8d877 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -40,6 +40,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
 	  intel_sprite.o \
 	  intel_opregion.o \
 	  intel_sideband.o \
+	  intel_clrmgr.o \
 	  intel_uncore.o \
 	  dvo_ch7xxx.o \
 	  dvo_ch7017.o \
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b4587ac..6c8cbc3 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -79,6 +79,13 @@ enum plane {
 };
 #define plane_name(p) ((p) + 'A')
 
+enum sprite_plane {
+	SPRITE_PLANE_A = 0,
+	SPRITE_PLANE_B = 1,
+	SPRITE_PLANE_C = 0,
+	SPRITE_PLANE_D = 1,
+};
+
 #define sprite_name(p, s) ((p) * INTEL_INFO(dev)->num_sprites + (s) + 'A')
 
 enum port {
@@ -1391,6 +1398,25 @@ struct intel_pipe_crc {
 	wait_queue_head_t wq;
 };
 
+/*
+  * Intel color manager structures.
+  * Used to represent the current status of
+  * color manager parameters
+  */
+struct clrmgr_pipe_status {
+	bool csc_enabled;
+	bool gamma_enabled;
+	bool hs_enabled;
+	bool cb_enabled;
+	bool gamma_s_enabled;
+	int planeid;
+};
+
+struct clrmgr_map {
+	struct drm_connector *connector;
+	struct clrmgr_pipe_status *pstatus;
+};
+
 typedef struct drm_i915_private {
 	struct drm_device *dev;
 	struct kmem_cache *slab;
@@ -1594,6 +1620,8 @@ typedef struct drm_i915_private {
 	struct i915_dri1_state dri1;
 	/* Old ums support infrastructure, same warning applies. */
 	struct i915_ums_state ums;
+	/* Color manager current status */
+	struct clrmgr_map clrmgr_status;
 } drm_i915_private_t;
 
 static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
@@ -2008,6 +2036,7 @@ extern void intel_uncore_early_sanitize(struct drm_device *dev);
 extern void intel_uncore_init(struct drm_device *dev);
 extern void intel_uncore_check_errors(struct drm_device *dev);
 extern void intel_uncore_fini(struct drm_device *dev);
+extern bool intel_clrmgr_register(struct drm_connector *connector);
 
 void
 i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe,
diff --git a/drivers/gpu/drm/i915/intel_clrmgr.c b/drivers/gpu/drm/i915/intel_clrmgr.c
new file mode 100644
index 0000000..2c826f3
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_clrmgr.c
@@ -0,0 +1,643 @@
+/*
+ * Copyright © 2008 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Shashank Sharma <shashank.sharma at intel.com>
+ * Uma Shankar <uma.shankar at intel.com>
+ * Shobhit Kumar <shobhit.kumar at intel.com>
+ */
+
+#include <linux/device.h>
+#include "drmP.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_clrmgr.h"
+
+/* Sprite register default gamma values */
+u32 default_sprite_gamma_vals[] = {
+	0, 0, 0, 0, 0, 0
+};
+
+/* Gamma lookup table for Sprite planes */
+u32 gamma_sprite_softlut[GAMMA_SP_MAX_COUNT] = {
+	0, 0, 0, 0, 0, 1023
+};
+
+/* Gamma soft lookup table for default gamma =1.0 */
+u32 gamma_softlut[GAMMA_CORRECT_MAX_COUNT] =  {
+	0x000000, 0x0, 0x020202, 0x0, 0x040404, 0x0, 0x060606, 0x0,
+	0x080808, 0x0, 0x0A0A0A, 0x0, 0x0C0C0C, 0x0, 0x0E0E0E, 0x0,
+	0x101010, 0x0, 0x121212, 0x0, 0x141414, 0x0, 0x161616, 0x0,
+	0x181818, 0x0, 0x1A1A1A, 0x0, 0x1C1C1C, 0x0, 0x1E1E1E, 0x0,
+	0x202020, 0x0, 0x222222, 0x0, 0x242424, 0x0, 0x262626, 0x0,
+	0x282828, 0x0, 0x2A2A2A, 0x0, 0x2C2C2C, 0x0, 0x2E2E2E, 0x0,
+	0x303030, 0x0, 0x323232, 0x0, 0x343434, 0x0, 0x363636, 0x0,
+	0x383838, 0x0, 0x3A3A3A, 0x0, 0x3C3C3C, 0x0, 0x3E3E3E, 0x0,
+	0x404040, 0x0, 0x424242, 0x0, 0x444444, 0x0, 0x464646, 0x0,
+	0x484848, 0x0, 0x4A4A4A, 0x0, 0x4C4C4C, 0x0, 0x4E4E4E, 0x0,
+	0x505050, 0x0, 0x525252, 0x0, 0x545454, 0x0, 0x565656, 0x0,
+	0x585858, 0x0, 0x5A5A5A, 0x0, 0x5C5C5C, 0x0, 0x5E5E5E, 0x0,
+	0x606060, 0x0, 0x626262, 0x0, 0x646464, 0x0, 0x666666, 0x0,
+	0x686868, 0x0, 0x6A6A6A, 0x0, 0x6C6C6C, 0x0, 0x6E6E6E, 0x0,
+	0x707070, 0x0, 0x727272, 0x0, 0x747474, 0x0, 0x767676, 0x0,
+	0x787878, 0x0, 0x7A7A7A, 0x0, 0x7C7C7C, 0x0, 0x7E7E7E, 0x0,
+	0x808080, 0x0, 0x828282, 0x0, 0x848484, 0x0, 0x868686, 0x0,
+	0x888888, 0x0, 0x8A8A8A, 0x0, 0x8C8C8C, 0x0, 0x8E8E8E, 0x0,
+	0x909090, 0x0, 0x929292, 0x0, 0x949494, 0x0, 0x969696, 0x0,
+	0x989898, 0x0, 0x9A9A9A, 0x0, 0x9C9C9C, 0x0, 0x9E9E9E, 0x0,
+	0xA0A0A0, 0x0, 0xA2A2A2, 0x0, 0xA4A4A4, 0x0, 0xA6A6A6, 0x0,
+	0xA8A8A8, 0x0, 0xAAAAAA, 0x0, 0xACACAC, 0x0, 0xAEAEAE, 0x0,
+	0xB0B0B0, 0x0, 0xB2B2B2, 0x0, 0xB4B4B4, 0x0, 0xB6B6B6, 0x0,
+	0xB8B8B8, 0x0, 0xBABABA, 0x0, 0xBCBCBC, 0x0, 0xBEBEBE, 0x0,
+	0xC0C0C0, 0x0, 0xC2C2C2, 0x0, 0xC4C4C4, 0x0, 0xC6C6C6, 0x0,
+	0xC8C8C8, 0x0, 0xCACACA, 0x0, 0xCCCCCC, 0x0, 0xCECECE, 0x0,
+	0xD0D0D0, 0x0, 0xD2D2D2, 0x0, 0xD4D4D4, 0x0, 0xD6D6D6, 0x0,
+	0xD8D8D8, 0x0, 0xDADADA, 0x0, 0xDCDCDC, 0x0, 0xDEDEDE, 0x0,
+	0xE0E0E0, 0x0, 0xE2E2E2, 0x0, 0xE4E4E4, 0x0, 0xE6E6E6, 0x0,
+	0xE8E8E8, 0x0, 0xEAEAEA, 0x0, 0xECECEC, 0x0, 0xEEEEEE, 0x0,
+	0xF0F0F0, 0x0, 0xF2F2F2, 0x0, 0xF4F4F4, 0x0, 0xF6F6F6, 0x0,
+	0xF8F8F8, 0x0, 0xFAFAFA, 0x0, 0xFCFCFC, 0x0, 0xFEFEFE, 0x0
+};
+
+/* Color space conversion coff's */
+u32 csc_softlut[CSC_MAX_COEFF_COUNT] = {
+	1024,	 0, 67108864, 0, 0, 1024
+};
+
+u32 cb_softlut[CB_MAX_COEFF_COUNT] = {
+	0x80
+};
+
+u32 hs_softlut[HS_MAX_COEFF_COUNT] = {
+	0x1000000
+};
+
+u32 *clrmgr_luts[] = {
+	csc_softlut,
+	gamma_softlut,
+	cb_softlut,
+	hs_softlut,
+	gamma_sprite_softlut
+};
+
+/* Hue Saturation defaults */
+struct hue_saturationlut saved_hsvals[VLV_NO_SPRITE_REG] = {
+	{sprite_a, 0x1000000},
+	{sprite_b, 0x1000000},
+	{sprite_c, 0x1000000},
+	{sprite_d, 0x1000000}
+};
+
+/* Contrast brightness defaults */
+struct cont_brightlut saved_cbvals[VLV_NO_SPRITE_REG] = {
+	{sprite_a, 0x80},
+	{sprite_b, 0x80},
+	{sprite_c, 0x80},
+	{sprite_d, 0x80}
+};
+
+/* Get no of pipes in SOC */
+static int get_no_of_pipes(struct drm_device *dev)
+{
+	if (!dev) {
+		DRM_ERROR("NULL input to get no of pipes");
+		return 0;
+	}
+
+	if (IS_VALLEYVIEW(dev))
+		return VLV_NO_OF_PIPES;
+	if (IS_HASWELL(dev))
+		return HSW_NO_OF_PIPES;
+
+	return 0;
+}
+
+static bool intel_clrmgr_disable_hs(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+
+static bool intel_clrmgr_disable_cb(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+
+static bool intel_clrmgr_disable_gamma(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+
+static void intel_clrmgr_disable_csc(struct drm_device *dev, int identifier)
+{
+}
+
+static bool intel_clrmgr_enable_hs(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+static bool intel_clrmgr_enable_cb(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+
+static bool intel_clrmgr_enable_gamma(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+
+static bool intel_clrmgr_enable_csc(struct drm_device *dev, int identifier)
+{
+	return true;
+}
+
+/*
+* Enable a color manager property
+* This function assumes all the validation is done
+* by the caller
+*/
+static bool intel_clrmgr_enable_property(struct drm_device *dev,
+		int property, int identifier)
+{
+	switch (property) {
+	case clrmgr_csc:
+		intel_clrmgr_enable_csc(dev, identifier);
+		break;
+	case clrmgr_gamma:
+	case clrmgr_gammaspr:
+		intel_clrmgr_enable_gamma(dev, identifier);
+		break;
+	case clrmgr_cb:
+		intel_clrmgr_enable_cb(dev, identifier);
+		break;
+	case clrmgr_hs:
+		intel_clrmgr_enable_hs(dev, identifier);
+		break;
+	default:
+		DRM_ERROR("Clrmgr: Enable, invalid property %d", property);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+* Disable a color manager property
+* This function assumes all the validation is done
+* by the caller
+*/
+static bool intel_clrmgr_disable_property(struct drm_device *dev,
+		int property, int identifier)
+{
+	switch (property) {
+	case clrmgr_csc:
+		intel_clrmgr_disable_csc(dev, identifier);
+		break;
+	case clrmgr_gamma:
+		intel_clrmgr_disable_gamma(dev, identifier);
+		break;
+	case clrmgr_cb:
+		intel_clrmgr_disable_cb(dev, identifier);
+		break;
+	case clrmgr_hs:
+		intel_clrmgr_disable_hs(dev, identifier);
+		break;
+	}
+	DRM_DEBUG_DRIVER("Clrmgr: %s disabled",
+		clrmgr_properties[property]);
+	return true;
+}
+
+/*
+* _parse_clrmgr_data: Extract the data from color manager buffer
+* The data parser follows a strict grammar.
+===============================
+* -The values must start with 0x and
+* -The values must be seperated by a comma
+* -There mustn't be a space after/before comma
+* -No non alphanumeric at start
+* -Sample: 0x5678,0xABCD
+*/
+int _parse_clrmgr_data(uint *dest, char *src, int max)
+{
+	int size = 0;
+	int bytes = 0;
+	char *populate = NULL;
+
+	/* Check grammar */
+	if (!dest || !src || *src != '0') {
+		DRM_ERROR("Clrmgr: Invalid input to parser");
+		return -EINVAL;
+	}
+
+	/* Extract values from buffer */
+	while ((size < max) && (*src != '\n')) {
+		populate = strsep(&src, ",");
+		if (!populate) {
+			if (size < max)
+				DRM_ERROR("Clrmgr: Parser: %d values missing",
+					max-size);
+			break;
+		}
+
+		/* Consider ',' also for length */
+		bytes += (strlen(populate)+1);
+		if (kstrtouint((const char *)populate, CLRMGR_PARSE_BASE,
+			&dest[size++])) {
+			DRM_ERROR("ClrMgr Parse: Invalid limit\n");
+			return -EINVAL;
+		}
+
+		if (CLRMGR_DEBUG_ENABLE)
+			DRM_DEBUG_DRIVER("Parse data: dest[%d] = 0x%x",
+			size-1, dest[size-1]);
+
+		/* End of data */
+		if (src == NULL || *src == '\0') {
+			if (size < max)
+				DRM_ERROR("Clrmgr: Parser: %d values missing",
+				max-size);
+			break;
+		}
+	}
+
+	DRM_DEBUG_DRIVER("Clrmgr: Parser: Loaded %d bytes from source", bytes);
+	return bytes;
+}
+
+/*
+* _extract_cmd
+* Actual extraction and interpratation of a
+* color manager command
+*/
+int _extract_cmd(const char *buf)
+{
+	u8 count = 2;
+	u8 update = 0;
+	int cmd = 0;
+
+	if (!buf) {
+		DRM_ERROR("Clrmgr: Apply: invalid input to extract_cmd\n");
+		return -1;
+	}
+
+	/* Check for alphanumeric chars */
+	while (count--) {
+		if (*buf >= 'A' && *buf <= 'F')
+			update = (*buf - 'A' + CLRMGR_HEX_ADDITION);
+		else if (*buf >= 'a' && *buf <= 'f')
+			update = (*buf - 'a' + CLRMGR_HEX_ADDITION);
+		else if (*buf >= '0' && *buf <= '9')
+			update = (*buf - '0');
+		else {
+			DRM_ERROR("Clrmgr: extract_cmd: Stupid input");
+			return -1;
+		}
+		cmd = cmd * CLRMGR_PARSE_BASE + update;
+		buf++;
+	}
+	DRM_DEBUG_DRIVER("Clrmgr: Extarcted: %d", cmd);
+	return cmd;
+}
+
+
+/*
+* _parse_clrmgr_cmd:
+* Extract command from color EDID
+*Color EDID (4 bytes) :
+*================
+*Byte 0	: Property to modify
+*Byte 1	: Enable /Disable property
+*Byte 2	: Identifier (Plane/Pipe)
+*Byte 3	: How many data bytes are following this byte
+*
+*	<1Byte>	  <1Byte>	               <1Byte>		<1Byte>
+*<<=property=>,<=enable/disable=>,<=identifier=>,<=No of data blocks=>,
+*<0xdata>,<0xdata>,<0xdata> ..... <0xdata>
+* Sample command+ data : 0x01010001,0x1234
+* This is to enable Gamma on Pipe A, with val=0x1234
+*/
+bool _parse_clrmgr_cmd(const char *ubuf, struct clrmgr_cmd *cmd)
+{
+	int ret = 0;
+
+	/* Validate input, command must start with 0x */
+	if (!ubuf || !cmd || *ubuf != '0') {
+		DRM_ERROR("Clrmgr: Apply: invalid input to parse_command\n");
+		return false;
+	}
+
+	/* Extract property to be changed */
+	ret = _extract_cmd(ubuf + CLR_EDID_PROPERTY);
+	if (ret < 0) {
+		DRM_ERROR("Clrmgr: Parse: extract property failed\n");
+		return false;
+	}
+	cmd->property = ret;
+
+	/* Extract enabled/disable choice */
+	ret = _extract_cmd(ubuf + CLR_EDID_ENABLE);
+	if (ret  < 0) {
+		DRM_ERROR("Clrmgr: Parse: extract enable failed\n");
+		return false;
+	}
+	cmd->enable = (ret ? true : false);
+
+	/* Extract Identifier of pipe/plane */
+	ret = _extract_cmd(ubuf + CLR_EDID_IDENTIFIER);
+	if (ret < 0) {
+		DRM_ERROR("Clrmgr: Parse: extract identifier failed\n");
+		return false;
+	}
+	cmd->identifier = ret;
+
+	/* Extract no of data bytes following */
+	ret = _extract_cmd(ubuf + CLR_EDID_SIZE);
+	if (ret < 0) {
+		DRM_ERROR("Clrmgr: Parse: extract size failed\n");
+		return false;
+	}
+	cmd->size = ret;
+	return true;
+}
+
+/*
+* intel_clrmgr_apply:
+* Parse, decode and Apply a change.
+*/
+bool intel_clrmgr_apply(struct drm_device *dev,
+	const char *ubuf, size_t count)
+{
+	bool ret = false;
+	struct clrmgr_cmd cmd = {0, 0, 0, 0};
+	char *raw_data = NULL;
+
+	if (!ubuf || !count) {
+		DRM_ERROR("Clrmgr: Apply: insufficient data\n");
+		return -EINVAL;
+	}
+
+	/* Parse command */
+	if (!_parse_clrmgr_cmd(ubuf, &cmd)) {
+		DRM_ERROR("Clrmgr: Command parsing failed");
+		return false;
+	}
+
+	/* Validate property */
+	if (cmd.property < clrmgr_csc || cmd.property > clrmgr_gammaspr) {
+		DRM_ERROR("Clrmgr: Invalid input, propery Max=%d, Min=%d",
+			clrmgr_csc, clrmgr_gammaspr);
+		return false;
+	}
+
+	/* Validate Identifier */
+	if (cmd.identifier < pipe_a || cmd.identifier > sprite_d) {
+		DRM_ERROR("Clrmgr: Invalid input, identifier Max=%d, Min=%d",
+			pipe_a, sprite_d);
+		return false;
+	}
+
+	if (cmd.enable) {
+		/* Validite size, min 1 block of data is required */
+		if (cmd.size > clrmgr_maxsize[cmd.property]  ||
+			cmd.size < CLR_MGR_PARSE_MIN) {
+			DRM_ERROR("Clrmgr: Invalid size=%d, range %d to %d",
+				(int)count, CLR_MGR_PARSE_MIN,
+				clrmgr_maxsize[cmd.property]);
+			return false;
+		}
+
+		raw_data = kzalloc(count, GFP_KERNEL);
+		if (!raw_data) {
+			DRM_ERROR("Clrmgr: Out of memory");
+			return false;
+		}
+
+		/* Get the data */
+		memcpy((void *)raw_data,
+			(const void *)&ubuf[CLR_EDID_DATA], count);
+
+		/* Parse data and load corresponsing soft LUT */
+		if (_parse_clrmgr_data(clrmgr_luts[cmd.property], raw_data,
+				cmd.size) < 0) {
+			DRM_ERROR("Clrmgr: Parse failed");
+			ret = false;
+			goto FREE_AND_RETURN;
+		}
+
+		/* Data loaded, now do changes in property */
+		if (!intel_clrmgr_enable_property(dev, cmd.property,
+			cmd.identifier)) {
+			DRM_ERROR("Clrmgr: Enable property %s failed",
+				clrmgr_properties[cmd.property]);
+			ret = false;
+			goto FREE_AND_RETURN;
+		}
+	} else {
+		/* Validite size, for disable just command is enough */
+		if (cmd.size) {
+			DRM_ERROR("Clrmgr: Invalid input, No data required");
+			return false;
+		}
+
+		/* Disable specified property */
+		if (!intel_clrmgr_disable_property(dev, cmd.property,
+			cmd.identifier)) {
+			DRM_ERROR("Clrmgr: Disable property %s failed",
+				clrmgr_properties[cmd.property]);
+			return false;
+		}
+	}
+
+	ret = true;
+	DRM_DEBUG_DRIVER("Clrmgr: apply success");
+
+FREE_AND_RETURN:
+	kfree(raw_data);
+	return ret;
+}
+
+/*
+  * Color manager write function.
+  * Current interface is /sys/class/drm/<connector-name>/color-manager
+  * Shows the current status of color manager features
+*/
+ssize_t intel_clrmgr_write(struct file *filp, struct kobject *kobj,
+		struct bin_attribute *ba, char *ubuf,
+		loff_t offset, size_t count)
+{
+	struct device *connector_dev = container_of(kobj, struct device, kobj);
+	struct drm_connector *connector = dev_get_drvdata(connector_dev);
+	struct drm_device *dev = connector->dev;
+
+	DRM_DEBUG_DRIVER("Clrmgr write");
+
+	/* Validate input */
+	if (!count || !ubuf) {
+		DRM_ERROR("Clrmgr: insufficient data\n");
+		return -EINVAL;
+	}
+
+	/* Parse the color EDID, apply the change */
+	if (!intel_clrmgr_apply(dev, ubuf, count)) {
+		DRM_ERROR("Clrmgr: Parse and apply failed\n");
+		return -1;
+	}
+
+	DRM_DEBUG_DRIVER("Clrmgr: Write success\n");
+	return count;
+}
+
+/*
+  * Color manager read function.
+  * Current interface is /sys/class/drm/<connector-name>/color-manager
+  * Shows the current status of color manager features
+*/
+ssize_t intel_clrmgr_read(struct file *filp, struct kobject *kobj,
+		struct bin_attribute *ba, char *buf, loff_t off, size_t count)
+{
+	u16 size = 0;
+	u8 pipe_count = 0;
+	const char *p = NULL;
+	struct device *connector_dev = container_of(kobj, struct device, kobj);
+	struct drm_connector *connector = dev_get_drvdata(connector_dev);
+	struct drm_device *dev = connector->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct clrmgr_pipe_status *pstatus = dev_priv->clrmgr_status.pstatus;
+
+	if (!pstatus) {
+		DRM_DEBUG_DRIVER("Clrmgr not initialized yet");
+		return 0;
+	}
+
+	/* One page read is enough */
+	if (off)
+		return 0;
+
+	/* Get no of pipes in this arch */
+	pipe_count = get_no_of_pipes(dev);
+	if (!pipe_count) {
+		DRM_ERROR("This Gen device is not supported, cant get pipes\n");
+		return false;
+	}
+
+	/* Load per pipe color status */
+	do {
+		size += sprintf(buf + size, "PIPE %c\n", ('A' + pipe_count-1));
+		size += sprintf(buf + size, "=====\n");
+		p = clrmgr_properties[clrmgr_csc];
+		size += sprintf(buf + size, "1.%s %s\n", p,
+		pstatus[pipe_count-1].csc_enabled ? "Enabled" : "Disabled");
+		p = clrmgr_properties[clrmgr_gamma];
+		size += sprintf(buf + size, "2.%s %s\n", p,
+		pstatus[pipe_count-1].gamma_enabled ? "Enabled" : "Disabled");
+		p = clrmgr_properties[clrmgr_cb];
+		size += sprintf(buf + size, "3.%s %s\n", p,
+		pstatus[pipe_count-1].cb_enabled ? "Enabled" : "Disabled");
+		p = clrmgr_properties[clrmgr_hs];
+		size += sprintf(buf + size, "4.%s %s\n", p,
+		pstatus[pipe_count-1].hs_enabled ? "Enabled" : "Disabled");
+		p = clrmgr_properties[clrmgr_gammaspr];
+		size += sprintf(buf + size, "5.%s %s\n", p,
+		pstatus[pipe_count-1].gamma_s_enabled ? "Enabled" : "Disabled");
+	} while (--pipe_count);
+
+	DRM_DEBUG_DRIVER("Clrmgr Read done, %d bytes", size);
+	return size;
+}
+
+static struct bin_attribute clrmgr_attr = {
+	.attr.name = "color-manager",
+	.attr.mode = 0644,
+	.size = 0,
+	.read = intel_clrmgr_read,
+	.write = intel_clrmgr_write
+};
+
+/* Register color manager with a connector
+  * The connecter init function should call this function
+  * The current implementation is for Primary/Fix panels Like Mipi/EDP
+*/
+bool intel_clrmgr_register(struct drm_connector *connector)
+{
+	int pipe_count = 0;
+	struct clrmgr_pipe_status *pstatus = NULL;
+	struct drm_device *dev = connector->dev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+
+	/* Clr mgr is available for Gen 7 and + for now */
+	if (INTEL_INFO(dev)->gen < CLRMGR_GEN_THRESHOLD) {
+		DRM_ERROR("Clrmgr: This Gen device is not supported\n");
+		return false;
+	}
+
+	/* Todo: Current implementation supports only VLV.
+	Extend this for HSW and other gen 7+ devices */
+	if (!IS_VALLEYVIEW(dev)) {
+		DRM_ERROR("Clrmgr: Current implementation supports only VLV\n");
+		return false;
+	}
+
+	/* Create sysfs entry for color manager */
+	if (sysfs_create_bin_file(&connector->kdev->kobj, &clrmgr_attr)) {
+		DRM_ERROR("Clrmgr:  %s Register Color interface failed\n",
+			drm_get_connector_name(connector));
+		return false;
+	}
+
+	/* Get no of pipes of the arch */
+	pipe_count = get_no_of_pipes(dev);
+	if (!pipe_count) {
+		DRM_ERROR("Clrmgr: Cant get pipe info\n");
+		return false;
+	}
+
+	/* Load color status of the pipes */
+	pstatus = kzalloc(pipe_count * sizeof(struct clrmgr_pipe_status),
+				GFP_KERNEL);
+	if (!pstatus) {
+		DRM_ERROR("Clrmgr: %s Out of memory\n",
+			drm_get_connector_name(connector));
+		return false;
+	}
+
+	/* Initialize color status of the pipe,
+	  * planeid will be filled with specific enable call
+	  */
+	do {
+		pstatus[pipe_count-1].planeid = -1;
+		pstatus[pipe_count-1].csc_enabled = false;
+		pstatus[pipe_count-1].gamma_enabled = false;
+		pstatus[pipe_count-1].hs_enabled = false;
+		pstatus[pipe_count-1].cb_enabled = false;
+		pstatus[pipe_count-1].gamma_s_enabled = false;
+	} while (--pipe_count);
+
+	/* Load color manager status */
+	dev_priv->clrmgr_status.pstatus = pstatus;
+	dev_priv->clrmgr_status.connector = connector;
+
+	DRM_DEBUG_DRIVER("Clrmgr: Successfully registered for %s",
+			drm_get_connector_name(connector));
+	return true;
+}
+EXPORT_SYMBOL(intel_clrmgr_register);
diff --git a/drivers/gpu/drm/i915/intel_clrmgr.h b/drivers/gpu/drm/i915/intel_clrmgr.h
new file mode 100644
index 0000000..25d3491
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_clrmgr.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright © 2008 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Shashank Sharma <shashank.sharma at intel.com>
+ * Uma Shankar <uma.shankar at intel.com>
+ */
+
+#ifndef _I915_CLR_MNGR_H_
+#define _I915_CLR_MNGR_H_
+
+
+struct cont_brightlut {
+	short sprite_no;
+	u32 val;
+};
+
+struct hue_saturationlut {
+	short sprite_no;
+	u32 val;
+};
+
+/* Debugging support */
+#define CLRMGR_DEBUG_ENABLE		0
+
+/* General defines */
+#define CLR_MGR_PARSE_MAX		256
+#define CLR_MGR_PARSE_MIN		1
+#define VLV_NO_SPRITE_REG				4
+#define SIZE_STATUS				10
+#define CLR_EDID_PROPERTY			2
+#define CLR_EDID_ENABLE			(CLR_EDID_PROPERTY + 2)
+#define CLR_EDID_IDENTIFIER		(CLR_EDID_ENABLE + 2)
+#define CLR_EDID_SIZE				(CLR_EDID_IDENTIFIER + 2)
+#define CLR_EDID_DATA				(CLR_EDID_SIZE + 3)
+#define CLRMGR_GEN_THRESHOLD	7
+#define VLV_NO_OF_PIPES			2
+#define HSW_NO_OF_PIPES			3
+#define CLRMGR_PARSE_BASE		16
+#define CLRMGR_HEX_ADDITION		10
+
+/* Pipe level gamma correction defines */
+#define PIPECONF_GAMMA			(1<<24)
+#define GAMMA_SP_MAX_COUNT		6
+#define GAMMA_MAX_VAL			1024
+#define SHIFTBY6(val)				(val<<6)
+#define GAMMA_CORRECT_MAX_COUNT 256
+
+#define PIPEA_MAX_RED		(dev_priv->info.display_mmio_offset + 0x70010)
+#define PIPEA_MAX_GREEN	(dev_priv->info.display_mmio_offset + 0x70014)
+#define PIPEA_MAX_BLUE	(dev_priv->info.display_mmio_offset + 0x70018)
+
+/* Sprite gamma correction regs */
+#define GAMMA_SPA_GAMC0		(dev_priv->info.display_mmio_offset + 0x721F4)
+#define GAMMA_SPB_GAMC0		(dev_priv->info.display_mmio_offset + 0x722F4)
+#define GAMMA_SPC_GAMC0		(dev_priv->info.display_mmio_offset + 0x723F4)
+#define GAMMA_SPD_GAMC0		(dev_priv->info.display_mmio_offset + 0x724F4)
+#define GAMMA_SP_REG_OFFSET	0x100
+
+/* Sprite control regs */
+#define GAMMA_SPA_CNTRL		(dev_priv->info.display_mmio_offset + 0x72180)
+#define GAMMA_SPB_CNTRL		(dev_priv->info.display_mmio_offset + 0x72280)
+#define GAMMA_SPC_CNTRL		(dev_priv->info.display_mmio_offset + 0x72380)
+#define GAMMA_SPD_CNTRL		(dev_priv->info.display_mmio_offset + 0x72480)
+#define GAMMA_SP_CTL_OFFSET	0x100
+#define GAMMA_ENABLE_SPR		(1<<30)
+#define GET_SPRITE_CTL(plid)	(GAMMA_SPA_CNTRL +		\
+				((plid - sprite_a) * GAMMA_SP_CTL_OFFSET))
+#define GET_SPRITE_REG(plid)	(GAMMA_SPA_GAMC0 +		\
+				((plid - sprite_a) * GAMMA_SP_REG_OFFSET))
+
+/* CSC defines and Control Register */
+#define	_PIPEACSC		(dev_priv->info.display_mmio_offset + 0x600b0)
+#define	_PIPEBCSC		(dev_priv->info.display_mmio_offset + 0x610b0)
+#define	PIPECSC(p)		(p ? _PIPEACSC : _PIPEBCSC)
+#define	PIPECONF_CSC_ENABLE		(1<<15)
+#define	CSC_MAX_COEFF_COUNT	6
+
+
+/* Sprite Contrast and Brightness Registers */
+#define CB_MAX_COEFF_COUNT	1
+#define SPRITEA_CB_REG		(dev_priv->info.display_mmio_offset + 0x721d0)
+#define SPRITEB_CB_REG		(dev_priv->info.display_mmio_offset + 0x722d0)
+#define SPRITEC_CB_REG		(dev_priv->info.display_mmio_offset + 0x723d0)
+#define SPRITED_CB_REG		(dev_priv->info.display_mmio_offset + 0x724d0)
+#define SPRITE_CB_OFFSET		0x100
+#define GET_SPRITE_CB(plid)		(SPRITEA_CB_REG +		\
+					(plid * SPRITE_CB_OFFSET))
+#define CB_DEFAULT_VAL		0x80
+
+/* Sprite Hue and Saturation Registers */
+#define HS_MAX_COEFF_COUNT	1
+#define SPRITEA_HS_REG		0x721d4
+#define SPRITEB_HS_REG		0x722d4
+#define SPRITEC_HS_REG		0x723d4
+#define SPRITED_HS_REG		0x724d4
+#define HS_DEFAULT_VAL		0x1000000
+#define SPRITE_HS_OFFSET		0x100
+#define GET_SPRITE_HS(plid)		(SPRITEA_HS_REG +		\
+						(plid * SPRITE_HS_OFFSET))
+
+
+/* Color manager properties */
+const char	*clrmgr_properties[] = {
+	"CSC_CORRECTION",
+	"GAMMA CORRECTION",
+	"BRIGHTNESS/CONTRAST",
+	"HUE/SATURATION",
+	"GAMMA CORRECTTION SPRITE"
+};
+
+/* Color manager max values */
+int clrmgr_maxsize[] = {
+	CSC_MAX_COEFF_COUNT,
+	GAMMA_CORRECT_MAX_COUNT,
+	CB_MAX_COEFF_COUNT,
+	HS_MAX_COEFF_COUNT,
+	GAMMA_SP_MAX_COUNT
+};
+
+/* Color manager features */
+enum clrmgr_features {
+	clrmgr_csc = 0,
+	clrmgr_gamma,
+	clrmgr_cb,
+	clrmgr_hs,
+	clrmgr_gammaspr
+};
+
+struct clrmgr_cmd {
+	bool enable;
+	int size;
+	int identifier;
+	enum clrmgr_features property;
+};
+
+/* Color manager features */
+enum clrmgr_identifiers {
+	pipe_a = 0,
+	pipe_b,
+	pipe_c,
+	plane_a,
+	plane_b,
+	plane_c,
+	sprite_a,
+	sprite_b,
+	sprite_c,
+	sprite_d
+};
+
+/* Required for sysfs calls */
+extern u32 csc_softlut[CSC_MAX_COEFF_COUNT];
+extern u32 gamma_softlut[GAMMA_CORRECT_MAX_COUNT];
+extern u32 gamma_sprite_softlut[GAMMA_SP_MAX_COUNT];
+extern u32 cb_softlut[CB_MAX_COEFF_COUNT];
+extern u32 hs_softlut[HS_MAX_COEFF_COUNT];
+extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+
+/* Data dump */
+#define CLR_LIMIT_INTERNAL		1
+
+#if CLR_LIMIT_INTERNAL
+static inline int _validate_pipe(int identifier)
+{
+	if (identifier != pipe_a) {
+		DRM_ERROR("This functionality is only for PIPE A\n");
+		return -ENOSYS;
+	}
+	return 0;
+}
+static inline int _validate_plane(int identifier)
+{
+	if (identifier != plane_a) {
+		DRM_ERROR("This functionality is only for Plane A\n");
+		return -ENOSYS;
+	}
+	return 0;
+}
+static inline int _validate_sprite(int identifier)
+{
+	if (identifier != sprite_a && identifier != sprite_b) {
+		DRM_ERROR("This functionality is only for Sprite A/B\n");
+		return -ENOSYS;
+	}
+	return 0;
+}
+#else
+static inline int _validate_pipe(int identifier)
+{
+	return 0;
+}
+static inline int _validate_plane(int identifier)
+{
+	return 0;
+}
+static inline int _validate_sprite(int identifier)
+{
+	return 0;
+}
+#endif
+#define clrmgr_get_drvdata(d) dev_get_drvdata(d)
+#define _get_pipe_from_plane(pid) (pid - plane_a)
+
+
+/* Prototypes */
+int parse_clrmgr_input(uint *dest, char *src, int max, int read);
+int do_intel_enable_csc(struct drm_device *dev, void *data,
+				struct drm_crtc *crtc);
+bool intel_pipe_has_type(const struct drm_crtc *crtc, int type);
+void do_intel_disable_csc(struct drm_device *dev, struct drm_crtc *crtc);
+int intel_crtc_enable_gamma(struct drm_device *dev, u32 identifier);
+int intel_crtc_disable_gamma(struct drm_device *dev, u32 identifier);
+int intel_sprite_cb_adjust(struct drm_device *dev,
+		struct cont_brightlut *cb_ptr);
+int intel_sprite_hs_adjust(struct drm_device *dev,
+		struct hue_saturationlut *hs_ptr);
+void intel_save_clr_mgr_status(struct drm_device *dev);
+bool intel_restore_clr_mgr_status(struct drm_device *dev);
+#endif
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 1ac4b11..10ef04e 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3898,6 +3898,10 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
 	}
 
+	/* Register color manager interface */
+	if (!intel_clrmgr_register(connector))
+		DRM_ERROR("DP: Failed to register color manager features");
+
 	return true;
 }
 
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 3ee1db1..ff9bbd8 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -627,6 +627,10 @@ bool intel_dsi_init(struct drm_device *dev)
 	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
 	intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
 
+	/* Register to color manager interface */
+	if (!intel_clrmgr_register(connector))
+		DRM_ERROR("Mipi: Failed to register color manager features");
+
 	return true;
 
 err:
-- 
1.7.10.4




More information about the Intel-gfx mailing list