[PATCH 19/29] drm/amd/dal: Add output pixel processing HW programming

Harry Wentland harry.wentland at amd.com
Thu Feb 11 17:19:59 UTC 2016


Adds programming for color space conversion (CSC),
regamma, and formatter.

Signed-off-by: Harry Wentland <harry.wentland at amd.com>
Reviewed-by: Alex Deucher <alexander.deucher at amd.com>
---
 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c     |  272 +++
 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h     |  143 ++
 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c |  904 +++++++
 .../drm/amd/dal/dc/dce110/dce110_opp_formatter.c   |  610 +++++
 .../gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c | 2474 ++++++++++++++++++++
 5 files changed, 4403 insertions(+)
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c
 create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c

diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c
new file mode 100644
index 000000000000..acb405e7b2e7
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: AMD
+ *
+ */
+
+#include "dm_services.h"
+
+/* include DCE11 register header files */
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+#include "dce110_opp.h"
+
+#include "gamma_types.h"
+
+enum {
+	MAX_LUT_ENTRY = 256,
+	MAX_NUMBER_OF_ENTRIES = 256
+};
+
+static void build_evenly_distributed_points(
+	struct gamma_pixel *points,
+	uint32_t numberof_points,
+	struct fixed31_32 max_value,
+	struct fixed31_32 divider1,
+	struct fixed31_32 divider2,
+	struct fixed31_32 divider3)
+{
+	struct gamma_pixel *p = points;
+	struct gamma_pixel *p_last = p + numberof_points - 1;
+
+	uint32_t i = 0;
+
+	do {
+		struct fixed31_32 value = dal_fixed31_32_div_int(
+			dal_fixed31_32_mul_int(max_value, i),
+			numberof_points - 1);
+
+		p->r = value;
+		p->g = value;
+		p->b = value;
+
+		++p;
+		++i;
+	} while (i != numberof_points);
+
+	p->r = dal_fixed31_32_div(p_last->r, divider1);
+	p->g = dal_fixed31_32_div(p_last->g, divider1);
+	p->b = dal_fixed31_32_div(p_last->b, divider1);
+
+	++p;
+
+	p->r = dal_fixed31_32_div(p_last->r, divider2);
+	p->g = dal_fixed31_32_div(p_last->g, divider2);
+	p->b = dal_fixed31_32_div(p_last->b, divider2);
+
+	++p;
+
+	p->r = dal_fixed31_32_div(p_last->r, divider3);
+	p->g = dal_fixed31_32_div(p_last->g, divider3);
+	p->b = dal_fixed31_32_div(p_last->b, divider3);
+}
+
+/*****************************************/
+/* Constructor, Destructor               */
+/*****************************************/
+
+struct opp_funcs funcs = {
+		.opp_map_legacy_and_regamma_hw_to_x_user = dce110_opp_map_legacy_and_regamma_hw_to_x_user,
+		.opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut,
+		.opp_program_bit_depth_reduction = dce110_opp_program_bit_depth_reduction,
+		.opp_program_clamping_and_pixel_encoding = dce110_opp_program_clamping_and_pixel_encoding,
+		.opp_set_csc_adjustment = dce110_opp_set_csc_adjustment,
+		.opp_set_csc_default = dce110_opp_set_csc_default,
+		.opp_set_dyn_expansion = dce110_opp_set_dyn_expansion,
+		.opp_set_regamma = dce110_opp_set_regamma
+};
+
+bool dce110_opp_construct(struct dce110_opp *opp110,
+	struct dc_context *ctx,
+	uint32_t inst,
+	const struct dce110_opp_reg_offsets *offsets)
+{
+
+	opp110->base.funcs = &funcs;
+
+	opp110->base.ctx = ctx;
+
+	opp110->base.inst = inst;
+
+	opp110->offsets = *offsets;
+
+	opp110->regamma.hw_points_num = 128;
+	opp110->regamma.coordinates_x = NULL;
+	opp110->regamma.rgb_resulted = NULL;
+	opp110->regamma.rgb_regamma = NULL;
+	opp110->regamma.coeff128 = NULL;
+	opp110->regamma.coeff128_oem = NULL;
+	opp110->regamma.coeff128_dx = NULL;
+	opp110->regamma.axis_x_256 = NULL;
+	opp110->regamma.axis_x_1025 = NULL;
+	opp110->regamma.rgb_oem = NULL;
+	opp110->regamma.rgb_user = NULL;
+	opp110->regamma.extra_points = 3;
+	opp110->regamma.use_half_points = false;
+	opp110->regamma.x_max1 = dal_fixed31_32_one;
+	opp110->regamma.x_max2 = dal_fixed31_32_from_int(2);
+	opp110->regamma.x_min = dal_fixed31_32_zero;
+	opp110->regamma.divider1 = dal_fixed31_32_from_fraction(3, 2);
+	opp110->regamma.divider2 = dal_fixed31_32_from_int(2);
+	opp110->regamma.divider3 = dal_fixed31_32_from_fraction(5, 2);
+
+	opp110->regamma.rgb_user = dm_alloc(
+		ctx,
+		sizeof(struct pwl_float_data) *
+		(DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points));
+	if (!opp110->regamma.rgb_user)
+		goto failure_1;
+
+	opp110->regamma.rgb_oem = dm_alloc(
+		ctx,
+		sizeof(struct pwl_float_data) *
+		(DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points));
+	if (!opp110->regamma.rgb_oem)
+		goto failure_2;
+
+	opp110->regamma.rgb_resulted = dm_alloc(
+		ctx,
+		sizeof(struct pwl_result_data) *
+		(MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points));
+	if (!opp110->regamma.rgb_resulted)
+		goto failure_3;
+
+	opp110->regamma.rgb_regamma = dm_alloc(
+		ctx,
+		sizeof(struct pwl_float_data_ex) *
+		(MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points));
+	if (!opp110->regamma.rgb_regamma)
+		goto failure_4;
+
+	opp110->regamma.coordinates_x = dm_alloc(
+		ctx,
+		sizeof(struct hw_x_point) *
+		(MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points));
+	if (!opp110->regamma.coordinates_x)
+		goto failure_5;
+
+	opp110->regamma.axis_x_256 = dm_alloc(
+		ctx,
+		sizeof(struct gamma_pixel) *
+		(MAX_LUT_ENTRY + opp110->regamma.extra_points));
+	if (!opp110->regamma.axis_x_256)
+		goto failure_6;
+
+	opp110->regamma.axis_x_1025 = dm_alloc(
+		ctx,
+		sizeof(struct gamma_pixel) *
+		(DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points));
+	if (!opp110->regamma.axis_x_1025)
+		goto failure_7;
+
+	opp110->regamma.coeff128 = dm_alloc(
+		ctx,
+		sizeof(struct pixel_gamma_point) *
+		(MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points));
+	if (!opp110->regamma.coeff128)
+		goto failure_8;
+
+	opp110->regamma.coeff128_oem = dm_alloc(
+		ctx,
+		sizeof(struct pixel_gamma_point) *
+		(MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points));
+	if (!opp110->regamma.coeff128_oem)
+		goto failure_9;
+
+	opp110->regamma.coeff128_dx = dm_alloc(
+		ctx,
+		sizeof(struct pixel_gamma_point) *
+		(MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points));
+	if (!opp110->regamma.coeff128_dx)
+		goto failure_10;
+
+	/* init palette */
+	{
+		uint32_t i = 0;
+
+		do {
+			opp110->regamma.saved_palette[i].red = (uint8_t)i;
+			opp110->regamma.saved_palette[i].green = (uint8_t)i;
+			opp110->regamma.saved_palette[i].blue = (uint8_t)i;
+
+			++i;
+		} while (i != MAX_LUT_ENTRY);
+	}
+
+	build_evenly_distributed_points(
+		opp110->regamma.axis_x_256,
+		MAX_LUT_ENTRY,
+		opp110->regamma.x_max1,
+		opp110->regamma.divider1,
+		opp110->regamma.divider2,
+		opp110->regamma.divider3);
+
+	build_evenly_distributed_points(
+		opp110->regamma.axis_x_1025,
+		DX_GAMMA_RAMP_MAX,
+		opp110->regamma.x_max1,
+		opp110->regamma.divider1,
+		opp110->regamma.divider2,
+		opp110->regamma.divider3);
+
+	return true;
+
+failure_10:
+	dm_free(ctx, opp110->regamma.coeff128_oem);
+failure_9:
+	dm_free(ctx, opp110->regamma.coeff128);
+failure_8:
+	dm_free(ctx, opp110->regamma.axis_x_1025);
+failure_7:
+	dm_free(ctx, opp110->regamma.axis_x_256);
+failure_6:
+	dm_free(ctx, opp110->regamma.coordinates_x);
+failure_5:
+	dm_free(ctx, opp110->regamma.rgb_regamma);
+failure_4:
+	dm_free(ctx, opp110->regamma.rgb_resulted);
+failure_3:
+	dm_free(ctx, opp110->regamma.rgb_oem);
+failure_2:
+	dm_free(ctx, opp110->regamma.rgb_user);
+failure_1:
+
+	return true;
+}
+
+void dce110_opp_destroy(struct output_pixel_processor **opp)
+{
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coeff128_dx);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coeff128_oem);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coeff128);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.axis_x_1025);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.axis_x_256);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coordinates_x);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_regamma);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_resulted);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_oem);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_user);
+	dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp));
+	*opp = NULL;
+}
+
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h
new file mode 100644
index 000000000000..e53eb7413948
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h
@@ -0,0 +1,143 @@
+/* Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: AMD
+ *
+ */
+
+#ifndef __DC_OPP_DCE110_H__
+#define __DC_OPP_DCE110_H__
+
+#include "dc_types.h"
+#include "inc/opp.h"
+#include "gamma_types.h"
+
+struct gamma_parameters;
+
+#define FROM_DCE11_OPP(opp)\
+	container_of(opp, struct dce110_opp, base)
+
+enum dce110_opp_reg_type {
+	DCE110_OPP_REG_DCP = 0,
+	DCE110_OPP_REG_DCFE,
+	DCE110_OPP_REG_FMT,
+
+	DCE110_OPP_REG_MAX
+};
+
+struct dce110_regamma {
+	struct gamma_curve arr_curve_points[16];
+	struct curve_points arr_points[3];
+	uint32_t hw_points_num;
+	struct hw_x_point *coordinates_x;
+	struct pwl_result_data *rgb_resulted;
+
+	/* re-gamma curve */
+	struct pwl_float_data_ex *rgb_regamma;
+	/* coeff used to map user evenly distributed points
+	 * to our hardware points (predefined) for gamma 256 */
+	struct pixel_gamma_point *coeff128;
+	struct pixel_gamma_point *coeff128_oem;
+	/* coeff used to map user evenly distributed points
+	 * to our hardware points (predefined) for gamma 1025 */
+	struct pixel_gamma_point *coeff128_dx;
+	/* evenly distributed points, gamma 256 software points 0-255 */
+	struct gamma_pixel *axis_x_256;
+	/* evenly distributed points, gamma 1025 software points 0-1025 */
+	struct gamma_pixel *axis_x_1025;
+	/* OEM supplied gamma for regamma LUT */
+	struct pwl_float_data *rgb_oem;
+	/* user supplied gamma */
+	struct pwl_float_data *rgb_user;
+	struct dev_c_lut saved_palette[RGB_256X3X16];
+	uint32_t extra_points;
+	bool use_half_points;
+	struct fixed31_32 x_max1;
+	struct fixed31_32 x_max2;
+	struct fixed31_32 x_min;
+	struct fixed31_32 divider1;
+	struct fixed31_32 divider2;
+	struct fixed31_32 divider3;
+};
+
+/* OPP RELATED */
+#define TO_DCE110_OPP(opp)\
+	container_of(opp, struct dce110_opp, base)
+
+struct dce110_opp_reg_offsets {
+	uint32_t fmt_offset;
+	uint32_t dcp_offset;
+	uint32_t dcfe_offset;
+};
+
+struct dce110_opp {
+	struct output_pixel_processor base;
+	struct dce110_opp_reg_offsets offsets;
+	struct dce110_regamma regamma;
+};
+
+bool dce110_opp_construct(struct dce110_opp *opp110,
+	struct dc_context *ctx,
+	uint32_t inst,
+	const struct dce110_opp_reg_offsets *offsets);
+
+void dce110_opp_destroy(struct output_pixel_processor **opp);
+
+/* REGAMMA RELATED */
+void dce110_opp_power_on_regamma_lut(
+	struct output_pixel_processor *opp,
+	bool power_on);
+
+bool dce110_opp_set_regamma(
+	struct output_pixel_processor *opp,
+	const struct gamma_ramp *ramp,
+	const struct gamma_parameters *params,
+	bool force_bypass);
+
+bool dce110_opp_map_legacy_and_regamma_hw_to_x_user(
+	struct output_pixel_processor *opp,
+	const struct gamma_ramp *gamma_ramp,
+	const struct gamma_parameters *params);
+
+void dce110_opp_set_csc_adjustment(
+	struct output_pixel_processor *opp,
+	const struct grph_csc_adjustment *adjust);
+
+void dce110_opp_set_csc_default(
+	struct output_pixel_processor *opp,
+	const struct default_adjustment *default_adjust);
+
+/* FORMATTER RELATED */
+void dce110_opp_program_bit_depth_reduction(
+	struct output_pixel_processor *opp,
+	const struct bit_depth_reduction_params *params);
+
+void dce110_opp_program_clamping_and_pixel_encoding(
+	struct output_pixel_processor *opp,
+	const struct clamping_and_pixel_encoding_params *params);
+
+
+void dce110_opp_set_dyn_expansion(
+	struct output_pixel_processor *opp,
+	enum color_space color_sp,
+	enum dc_color_depth color_dpth,
+	enum signal_type signal);
+
+#endif
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c
new file mode 100644
index 000000000000..8f651e9329bc
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: AMD
+ *
+ */
+
+#include "dm_services.h"
+#include "dce110_opp.h"
+#include "basics/conversion.h"
+
+/* include DCE11 register header files */
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+#define DCP_REG(reg)\
+	(reg + opp110->offsets.dcp_offset)
+
+enum {
+	OUTPUT_CSC_MATRIX_SIZE = 12
+};
+
+struct out_csc_color_matrix {
+	enum color_space color_space;
+	uint16_t regval[OUTPUT_CSC_MATRIX_SIZE];
+};
+
+static const struct out_csc_color_matrix global_color_matrix[] = {
+{ COLOR_SPACE_SRGB_FULL_RANGE,
+	{ 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} },
+{ COLOR_SPACE_SRGB_LIMITED_RANGE,
+	{ 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} },
+{ COLOR_SPACE_YCBCR601,
+	{ 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47,
+		0xF6B9, 0xE00, 0x1000} },
+{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA,
+	0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} },
+/*  YOnly same as YCbCr709 but Y in Full range -To do. */
+{ COLOR_SPACE_YCBCR601_YONLY, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991,
+	0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} },
+{ COLOR_SPACE_YCBCR709_YONLY, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3,
+	0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }
+};
+
+enum csc_color_mode {
+	/* 00 - BITS2:0 Bypass */
+	CSC_COLOR_MODE_GRAPHICS_BYPASS,
+	/* 01 - hard coded coefficient TV RGB */
+	CSC_COLOR_MODE_GRAPHICS_PREDEFINED,
+	/* 04 - programmable OUTPUT CSC coefficient */
+	CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC,
+};
+
+static void program_color_matrix(
+	struct dce110_opp *opp110,
+	const struct out_csc_color_matrix *tbl_entry,
+	enum grph_color_adjust_option options)
+{
+	struct dc_context *ctx = opp110->base.ctx;
+	{
+		uint32_t value = 0;
+		uint32_t addr = DCP_REG(mmOUTPUT_CSC_C11_C12);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[0],
+			OUTPUT_CSC_C11_C12,
+			OUTPUT_CSC_C11);
+
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[1],
+			OUTPUT_CSC_C11_C12,
+			OUTPUT_CSC_C12);
+
+		dm_write_reg(ctx, addr, value);
+	}
+	{
+		uint32_t value = 0;
+		uint32_t addr = DCP_REG(mmOUTPUT_CSC_C13_C14);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[2],
+			OUTPUT_CSC_C13_C14,
+			OUTPUT_CSC_C13);
+		/* fixed S0.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[3],
+			OUTPUT_CSC_C13_C14,
+			OUTPUT_CSC_C14);
+
+		dm_write_reg(ctx, addr, value);
+	}
+	{
+		uint32_t value = 0;
+		uint32_t addr = DCP_REG(mmOUTPUT_CSC_C21_C22);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[4],
+			OUTPUT_CSC_C21_C22,
+			OUTPUT_CSC_C21);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[5],
+			OUTPUT_CSC_C21_C22,
+			OUTPUT_CSC_C22);
+
+		dm_write_reg(ctx, addr, value);
+	}
+	{
+		uint32_t value = 0;
+		uint32_t addr = DCP_REG(mmOUTPUT_CSC_C23_C24);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[6],
+			OUTPUT_CSC_C23_C24,
+			OUTPUT_CSC_C23);
+		/* fixed S0.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[7],
+			OUTPUT_CSC_C23_C24,
+			OUTPUT_CSC_C24);
+
+		dm_write_reg(ctx, addr, value);
+	}
+	{
+		uint32_t value = 0;
+		uint32_t addr = DCP_REG(mmOUTPUT_CSC_C31_C32);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[8],
+			OUTPUT_CSC_C31_C32,
+			OUTPUT_CSC_C31);
+		/* fixed S0.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[9],
+			OUTPUT_CSC_C31_C32,
+			OUTPUT_CSC_C32);
+
+		dm_write_reg(ctx, addr, value);
+	}
+	{
+		uint32_t value = 0;
+		uint32_t addr = DCP_REG(mmOUTPUT_CSC_C33_C34);
+		/* fixed S2.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[10],
+			OUTPUT_CSC_C33_C34,
+			OUTPUT_CSC_C33);
+		/* fixed S0.13 format */
+		set_reg_field_value(
+			value,
+			tbl_entry->regval[11],
+			OUTPUT_CSC_C33_C34,
+			OUTPUT_CSC_C34);
+
+		dm_write_reg(ctx, addr, value);
+	}
+}
+
+/*
+ * initialize_color_float_adj_reference_values
+ * This initialize display color adjust input from API to HW range for later
+ * calculation use. This is shared by all the display color adjustment.
+ * @param :
+ * @return None
+ */
+static void initialize_color_float_adj_reference_values(
+	const struct grph_csc_adjustment *adjust,
+	struct fixed31_32 *grph_cont,
+	struct fixed31_32 *grph_sat,
+	struct fixed31_32 *grph_bright,
+	struct fixed31_32 *sin_grph_hue,
+	struct fixed31_32 *cos_grph_hue)
+{
+	/* Hue adjustment could be negative. -45 ~ +45 */
+	struct fixed31_32 hue =
+		dal_fixed31_32_mul(
+			dal_fixed31_32_from_fraction(adjust->grph_hue, 180),
+			dal_fixed31_32_pi);
+
+	*sin_grph_hue = dal_fixed31_32_sin(hue);
+	*cos_grph_hue = dal_fixed31_32_cos(hue);
+
+	if (adjust->adjust_divider) {
+		*grph_cont =
+			dal_fixed31_32_from_fraction(
+				adjust->grph_cont,
+				adjust->adjust_divider);
+		*grph_sat =
+			dal_fixed31_32_from_fraction(
+				adjust->grph_sat,
+				adjust->adjust_divider);
+		*grph_bright =
+			dal_fixed31_32_from_fraction(
+				adjust->grph_bright,
+				adjust->adjust_divider);
+	} else {
+		*grph_cont = dal_fixed31_32_from_int(adjust->grph_cont);
+		*grph_sat = dal_fixed31_32_from_int(adjust->grph_sat);
+		*grph_bright = dal_fixed31_32_from_int(adjust->grph_bright);
+	}
+}
+
+static inline struct fixed31_32 fixed31_32_clamp(
+	struct fixed31_32 value,
+	int32_t min_numerator,
+	int32_t max_numerator,
+	int32_t denominator)
+{
+	return dal_fixed31_32_clamp(
+		value,
+		dal_fixed31_32_from_fraction(
+			min_numerator,
+			denominator),
+		dal_fixed31_32_from_fraction(
+			max_numerator,
+			denominator));
+}
+
+static void setup_reg_format(
+	struct fixed31_32 *coefficients,
+	uint16_t *reg_values)
+{
+	enum {
+		LENGTH = 12,
+		DENOMINATOR = 10000
+	};
+
+	static const int32_t min_numerator[] = {
+		-3 * DENOMINATOR,
+		-DENOMINATOR
+	};
+
+	static const int32_t max_numerator[] = {
+		DENOMINATOR,
+		DENOMINATOR
+	};
+
+	static const uint8_t integer_bits[] = { 2, 0 };
+
+	uint32_t i = 0;
+
+	do {
+		const uint32_t index = (i % 4) == 3;
+
+		reg_values[i] = fixed_point_to_int_frac(
+			fixed31_32_clamp(coefficients[(i + 8) % LENGTH],
+				min_numerator[index],
+				max_numerator[index],
+				DENOMINATOR),
+			integer_bits[index], 13);
+
+		++i;
+	} while (i != LENGTH);
+}
+
+/**
+ *****************************************************************************
+ *  Function: setup_adjustments
+ *  @note prepare to setup the values
+ *
+ *  @see
+ *
+ *****************************************************************************
+ */
+static void setup_adjustments(const struct grph_csc_adjustment *adjust,
+	struct dc_csc_adjustments *adjustments)
+{
+	if (adjust->adjust_divider != 0) {
+		adjustments->brightness =
+			dal_fixed31_32_from_fraction(adjust->grph_bright,
+			adjust->adjust_divider);
+		adjustments->contrast =
+			dal_fixed31_32_from_fraction(adjust->grph_cont,
+			adjust->adjust_divider);
+		adjustments->saturation =
+			dal_fixed31_32_from_fraction(adjust->grph_sat,
+			adjust->adjust_divider);
+	} else {
+		adjustments->brightness =
+			dal_fixed31_32_from_fraction(adjust->grph_bright, 1);
+		adjustments->contrast =
+			dal_fixed31_32_from_fraction(adjust->grph_cont, 1);
+		adjustments->saturation =
+			dal_fixed31_32_from_fraction(adjust->grph_sat, 1);
+	}
+
+	/* convert degrees into radians */
+	adjustments->hue =
+		dal_fixed31_32_mul(
+			dal_fixed31_32_from_fraction(adjust->grph_hue, 180),
+			dal_fixed31_32_pi);
+}
+
+static void prepare_tv_rgb_ideal(
+	struct fixed31_32 *matrix)
+{
+	static const int32_t matrix_[] = {
+		85546875, 0, 0, 6250000,
+		0, 85546875, 0, 6250000,
+		0, 0, 85546875, 6250000
+	};
+
+	uint32_t i = 0;
+
+	do {
+		matrix[i] = dal_fixed31_32_from_fraction(
+			matrix_[i],
+			100000000);
+		++i;
+	} while (i != ARRAY_SIZE(matrix_));
+}
+
+/**
+ *****************************************************************************
+ *  Function: dal_transform_wide_gamut_set_rgb_adjustment_legacy
+ *
+ *  @param [in] const struct grph_csc_adjustment *adjust
+ *
+ *  @return
+ *     void
+ *
+ *  @note calculate and program color adjustments for sRGB color space
+ *
+ *  @see
+ *
+ *****************************************************************************
+ */
+static void set_rgb_adjustment_legacy(
+	struct dce110_opp *opp110,
+	const struct grph_csc_adjustment *adjust)
+{
+	const struct fixed31_32 k1 =
+		dal_fixed31_32_from_fraction(701000, 1000000);
+	const struct fixed31_32 k2 =
+		dal_fixed31_32_from_fraction(236568, 1000000);
+	const struct fixed31_32 k3 =
+		dal_fixed31_32_from_fraction(-587000, 1000000);
+	const struct fixed31_32 k4 =
+		dal_fixed31_32_from_fraction(464432, 1000000);
+	const struct fixed31_32 k5 =
+		dal_fixed31_32_from_fraction(-114000, 1000000);
+	const struct fixed31_32 k6 =
+		dal_fixed31_32_from_fraction(-701000, 1000000);
+	const struct fixed31_32 k7 =
+		dal_fixed31_32_from_fraction(-299000, 1000000);
+	const struct fixed31_32 k8 =
+		dal_fixed31_32_from_fraction(-292569, 1000000);
+	const struct fixed31_32 k9 =
+		dal_fixed31_32_from_fraction(413000, 1000000);
+	const struct fixed31_32 k10 =
+		dal_fixed31_32_from_fraction(-92482, 1000000);
+	const struct fixed31_32 k11 =
+		dal_fixed31_32_from_fraction(-114000, 1000000);
+	const struct fixed31_32 k12 =
+		dal_fixed31_32_from_fraction(385051, 1000000);
+	const struct fixed31_32 k13 =
+		dal_fixed31_32_from_fraction(-299000, 1000000);
+	const struct fixed31_32 k14 =
+		dal_fixed31_32_from_fraction(886000, 1000000);
+	const struct fixed31_32 k15 =
+		dal_fixed31_32_from_fraction(-587000, 1000000);
+	const struct fixed31_32 k16 =
+		dal_fixed31_32_from_fraction(-741914, 1000000);
+	const struct fixed31_32 k17 =
+		dal_fixed31_32_from_fraction(886000, 1000000);
+	const struct fixed31_32 k18 =
+		dal_fixed31_32_from_fraction(-144086, 1000000);
+
+	const struct fixed31_32 luma_r =
+		dal_fixed31_32_from_fraction(299, 1000);
+	const struct fixed31_32 luma_g =
+		dal_fixed31_32_from_fraction(587, 1000);
+	const struct fixed31_32 luma_b =
+		dal_fixed31_32_from_fraction(114, 1000);
+
+	struct out_csc_color_matrix tbl_entry;
+	struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE];
+
+	struct fixed31_32 grph_cont;
+	struct fixed31_32 grph_sat;
+	struct fixed31_32 grph_bright;
+	struct fixed31_32 sin_grph_hue;
+	struct fixed31_32 cos_grph_hue;
+
+	initialize_color_float_adj_reference_values(
+		adjust, &grph_cont, &grph_sat,
+		&grph_bright, &sin_grph_hue, &cos_grph_hue);
+
+	/* COEF_1_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 +
+	 * Sin(GrphHue) * K2)) */
+	/* (Cos(GrphHue) * K1 + Sin(GrphHue) * K2) */
+	matrix[0] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k1),
+			dal_fixed31_32_mul(sin_grph_hue, k2));
+	/* GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2 */
+	matrix[0] = dal_fixed31_32_mul(grph_sat, matrix[0]);
+	/* (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2)) */
+	matrix[0] = dal_fixed31_32_add(luma_r, matrix[0]);
+	/* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) *
+	 * K2)) */
+	matrix[0] = dal_fixed31_32_mul(grph_cont, matrix[0]);
+
+	/* COEF_1_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 +
+	 * Sin(GrphHue) * K4)) */
+	/* (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */
+	matrix[1] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k3),
+			dal_fixed31_32_mul(sin_grph_hue, k4));
+	/* GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */
+	matrix[1] = dal_fixed31_32_mul(grph_sat, matrix[1]);
+	/* (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)) */
+	matrix[1] = dal_fixed31_32_add(luma_g, matrix[1]);
+	/* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) *
+	 * K4)) */
+	matrix[1] = dal_fixed31_32_mul(grph_cont, matrix[1]);
+
+	/* COEF_1_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 +
+	 * Sin(GrphHue) * K6)) */
+	/* (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */
+	matrix[2] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k5),
+			dal_fixed31_32_mul(sin_grph_hue, k6));
+	/* GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */
+	matrix[2] = dal_fixed31_32_mul(grph_sat, matrix[2]);
+	/* LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */
+	matrix[2] = dal_fixed31_32_add(luma_b, matrix[2]);
+	/* GrphCont  * (LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) *
+	 * K6)) */
+	matrix[2] = dal_fixed31_32_mul(grph_cont, matrix[2]);
+
+	/* COEF_1_4 = GrphBright */
+	matrix[3] = grph_bright;
+
+	/* COEF_2_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 +
+	 * Sin(GrphHue) * K8)) */
+	/* (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */
+	matrix[4] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k7),
+			dal_fixed31_32_mul(sin_grph_hue, k8));
+	/* GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */
+	matrix[4] = dal_fixed31_32_mul(grph_sat, matrix[4]);
+	/* (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)) */
+	matrix[4] = dal_fixed31_32_add(luma_r, matrix[4]);
+	/* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) *
+	 * K8)) */
+	matrix[4] = dal_fixed31_32_mul(grph_cont, matrix[4]);
+
+	/* COEF_2_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 +
+	 * Sin(GrphHue) * K10)) */
+	/* (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */
+	matrix[5] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k9),
+			dal_fixed31_32_mul(sin_grph_hue, k10));
+	/* GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */
+	matrix[5] = dal_fixed31_32_mul(grph_sat, matrix[5]);
+	/* (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */
+	matrix[5] = dal_fixed31_32_add(luma_g, matrix[5]);
+	/* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) *
+	 * K10)) */
+	matrix[5] = dal_fixed31_32_mul(grph_cont, matrix[5]);
+
+	/*  COEF_2_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 +
+	 * Sin(GrphHue) * K12)) */
+	/* (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */
+	matrix[6] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k11),
+			dal_fixed31_32_mul(sin_grph_hue, k12));
+	/* GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */
+	matrix[6] = dal_fixed31_32_mul(grph_sat, matrix[6]);
+	/*  (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */
+	matrix[6] = dal_fixed31_32_add(luma_b, matrix[6]);
+	/* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) *
+	 * K12)) */
+	matrix[6] = dal_fixed31_32_mul(grph_cont, matrix[6]);
+
+	/* COEF_2_4 = GrphBright */
+	matrix[7] = grph_bright;
+
+	/* COEF_3_1 = GrphCont  * (LumaR + GrphSat * (Cos(GrphHue) * K13 +
+	 * Sin(GrphHue) * K14)) */
+	/* (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */
+	matrix[8] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k13),
+			dal_fixed31_32_mul(sin_grph_hue, k14));
+	/* GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */
+	matrix[8] = dal_fixed31_32_mul(grph_sat, matrix[8]);
+	/* (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */
+	matrix[8] = dal_fixed31_32_add(luma_r, matrix[8]);
+	/* GrphCont  * (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) *
+	 * K14)) */
+	matrix[8] = dal_fixed31_32_mul(grph_cont, matrix[8]);
+
+	/* COEF_3_2    = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 +
+	 * Sin(GrphHue) * K16)) */
+	/* GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16) */
+	matrix[9] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k15),
+			dal_fixed31_32_mul(sin_grph_hue, k16));
+	/* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */
+	matrix[9] = dal_fixed31_32_mul(grph_sat, matrix[9]);
+	/* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */
+	matrix[9] = dal_fixed31_32_add(luma_g, matrix[9]);
+	 /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) *
+	  * K16)) */
+	matrix[9] = dal_fixed31_32_mul(grph_cont, matrix[9]);
+
+	/*  COEF_3_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 +
+	 * Sin(GrphHue) * K18)) */
+	/* (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */
+	matrix[10] =
+		dal_fixed31_32_add(
+			dal_fixed31_32_mul(cos_grph_hue, k17),
+			dal_fixed31_32_mul(sin_grph_hue, k18));
+	/*  GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */
+	matrix[10] = dal_fixed31_32_mul(grph_sat, matrix[10]);
+	/* (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */
+	matrix[10] = dal_fixed31_32_add(luma_b, matrix[10]);
+	 /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) *
+	  * K18)) */
+	matrix[10] = dal_fixed31_32_mul(grph_cont, matrix[10]);
+
+	/*  COEF_3_4    = GrphBright */
+	matrix[11] = grph_bright;
+
+	tbl_entry.color_space = adjust->c_space;
+
+	convert_float_matrix(tbl_entry.regval, matrix, OUTPUT_CSC_MATRIX_SIZE);
+
+	program_color_matrix(
+		opp110, &tbl_entry, adjust->color_adjust_option);
+}
+
+/**
+ *****************************************************************************
+ *  Function: dal_transform_wide_gamut_set_rgb_limited_range_adjustment
+ *
+ *  @param [in] const struct grph_csc_adjustment *adjust
+ *
+ *  @return
+ *     void
+ *
+ *  @note calculate and program color adjustments for sRGB limited color space
+ *
+ *  @see
+ *
+ *****************************************************************************
+ */
+static void set_rgb_limited_range_adjustment(
+	struct dce110_opp *opp110,
+	const struct grph_csc_adjustment *adjust)
+{
+	struct out_csc_color_matrix reg_matrix;
+	struct fixed31_32 change_matrix[OUTPUT_CSC_MATRIX_SIZE];
+	struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE];
+	struct dc_csc_adjustments adjustments;
+	struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE];
+
+	prepare_tv_rgb_ideal(ideals);
+
+	setup_adjustments(adjust, &adjustments);
+
+	calculate_adjustments(ideals, &adjustments, matrix);
+
+	dm_memmove(change_matrix, matrix, sizeof(matrix));
+
+	/* from 1 -> 3 */
+	matrix[8] = change_matrix[0];
+	matrix[9] = change_matrix[1];
+	matrix[10] = change_matrix[2];
+	matrix[11] = change_matrix[3];
+
+	/* from 2 -> 1 */
+	matrix[0] = change_matrix[4];
+	matrix[1] = change_matrix[5];
+	matrix[2] = change_matrix[6];
+	matrix[3] = change_matrix[7];
+
+	/* from 3 -> 2 */
+	matrix[4] = change_matrix[8];
+	matrix[5] = change_matrix[9];
+	matrix[6] = change_matrix[10];
+	matrix[7] = change_matrix[11];
+
+	dm_memset(&reg_matrix, 0, sizeof(struct out_csc_color_matrix));
+
+	setup_reg_format(matrix, reg_matrix.regval);
+
+	program_color_matrix(opp110, &reg_matrix, GRPH_COLOR_MATRIX_SW);
+}
+
+static void prepare_yuv_ideal(
+	bool b601,
+	struct fixed31_32 *matrix)
+{
+	static const int32_t matrix_1[] = {
+		25578516, 50216016, 9752344, 6250000,
+		-14764391, -28985609, 43750000, 50000000,
+		43750000, -36635164, -7114836, 50000000
+	};
+
+	static const int32_t matrix_2[] = {
+		18187266, 61183125, 6176484, 6250000,
+		-10025059, -33724941, 43750000, 50000000,
+		43750000, -39738379, -4011621, 50000000
+	};
+
+	const int32_t *matrix_x = b601 ? matrix_1 : matrix_2;
+
+	uint32_t i = 0;
+
+	do {
+		matrix[i] = dal_fixed31_32_from_fraction(
+			matrix_x[i],
+			100000000);
+		++i;
+	} while (i != ARRAY_SIZE(matrix_1));
+}
+
+/**
+ *****************************************************************************
+ *  Function: dal_transform_wide_gamut_set_yuv_adjustment
+ *
+ *  @param [in] const struct grph_csc_adjustment *adjust
+ *
+ *  @return
+ *     void
+ *
+ *  @note calculate and program color adjustments for YUV  color spaces
+ *
+ *  @see
+ *
+ *****************************************************************************
+ */
+static void set_yuv_adjustment(
+	struct dce110_opp *opp110,
+	const struct grph_csc_adjustment *adjust)
+{
+	bool b601 = (adjust->c_space == COLOR_SPACE_YPBPR601) ||
+		(adjust->c_space == COLOR_SPACE_YCBCR601) ||
+		(adjust->c_space == COLOR_SPACE_YCBCR601_YONLY);
+	struct out_csc_color_matrix reg_matrix;
+	struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE];
+	struct dc_csc_adjustments adjustments;
+	struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE];
+
+	prepare_yuv_ideal(b601, ideals);
+
+	setup_adjustments(adjust, &adjustments);
+
+	if ((adjust->c_space == COLOR_SPACE_YCBCR601_YONLY) ||
+		(adjust->c_space == COLOR_SPACE_YCBCR709_YONLY))
+		calculate_adjustments_y_only(
+			ideals, &adjustments, matrix);
+	else
+		calculate_adjustments(
+			ideals, &adjustments, matrix);
+
+	dm_memset(&reg_matrix, 0, sizeof(struct out_csc_color_matrix));
+
+	setup_reg_format(matrix, reg_matrix.regval);
+
+	program_color_matrix(opp110, &reg_matrix, GRPH_COLOR_MATRIX_SW);
+}
+
+static bool configure_graphics_mode(
+	struct dce110_opp *opp110,
+	enum csc_color_mode config,
+	enum graphics_csc_adjust_type csc_adjust_type,
+	enum color_space color_space)
+{
+	struct dc_context *ctx = opp110->base.ctx;
+	uint32_t addr = DCP_REG(mmOUTPUT_CSC_CONTROL);
+	uint32_t value = dm_read_reg(ctx, addr);
+
+	set_reg_field_value(
+		value,
+		0,
+		OUTPUT_CSC_CONTROL,
+		OUTPUT_CSC_GRPH_MODE);
+
+	if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) {
+		if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) {
+			set_reg_field_value(
+				value,
+				4,
+				OUTPUT_CSC_CONTROL,
+				OUTPUT_CSC_GRPH_MODE);
+		} else {
+
+			switch (color_space) {
+			case COLOR_SPACE_SRGB_FULL_RANGE:
+				/* by pass */
+				set_reg_field_value(
+					value,
+					0,
+					OUTPUT_CSC_CONTROL,
+					OUTPUT_CSC_GRPH_MODE);
+				break;
+			case COLOR_SPACE_SRGB_LIMITED_RANGE:
+				/* TV RGB */
+				set_reg_field_value(
+					value,
+					1,
+					OUTPUT_CSC_CONTROL,
+					OUTPUT_CSC_GRPH_MODE);
+				break;
+			case COLOR_SPACE_YCBCR601:
+			case COLOR_SPACE_YPBPR601:
+			case COLOR_SPACE_YCBCR601_YONLY:
+				/* YCbCr601 */
+				set_reg_field_value(
+					value,
+					2,
+					OUTPUT_CSC_CONTROL,
+					OUTPUT_CSC_GRPH_MODE);
+				break;
+			case COLOR_SPACE_YCBCR709:
+			case COLOR_SPACE_YPBPR709:
+			case COLOR_SPACE_YCBCR709_YONLY:
+				/* YCbCr709 */
+				set_reg_field_value(
+					value,
+					3,
+					OUTPUT_CSC_CONTROL,
+					OUTPUT_CSC_GRPH_MODE);
+				break;
+			default:
+				return false;
+			}
+		}
+	} else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) {
+		switch (color_space) {
+		case COLOR_SPACE_SRGB_FULL_RANGE:
+			/* by pass */
+			set_reg_field_value(
+				value,
+				0,
+				OUTPUT_CSC_CONTROL,
+				OUTPUT_CSC_GRPH_MODE);
+			break;
+		case COLOR_SPACE_SRGB_LIMITED_RANGE:
+			/* TV RGB */
+			set_reg_field_value(
+				value,
+				1,
+				OUTPUT_CSC_CONTROL,
+				OUTPUT_CSC_GRPH_MODE);
+			break;
+		case COLOR_SPACE_YCBCR601:
+		case COLOR_SPACE_YPBPR601:
+		case COLOR_SPACE_YCBCR601_YONLY:
+			/* YCbCr601 */
+			set_reg_field_value(
+				value,
+				2,
+				OUTPUT_CSC_CONTROL,
+				OUTPUT_CSC_GRPH_MODE);
+			break;
+		case COLOR_SPACE_YCBCR709:
+		case COLOR_SPACE_YPBPR709:
+		case COLOR_SPACE_YCBCR709_YONLY:
+			 /* YCbCr709 */
+			set_reg_field_value(
+				value,
+				3,
+				OUTPUT_CSC_CONTROL,
+				OUTPUT_CSC_GRPH_MODE);
+			break;
+		default:
+			return false;
+		}
+
+	} else
+		/* by pass */
+		set_reg_field_value(
+			value,
+			0,
+			OUTPUT_CSC_CONTROL,
+			OUTPUT_CSC_GRPH_MODE);
+
+	addr = DCP_REG(mmOUTPUT_CSC_CONTROL);
+	dm_write_reg(ctx, addr, value);
+
+	return true;
+}
+
+void dce110_opp_set_csc_adjustment(
+	struct output_pixel_processor *opp,
+	const struct grph_csc_adjustment *adjust)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+	enum csc_color_mode config =
+			CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC;
+
+	/* Apply color adjustments: brightness, saturation, hue, contrast and
+	 * CSC. No need for different color space routine, color space defines
+	 * the ideal values only, but keep original design to allow quick switch
+	 * to the old legacy routines */
+	switch (adjust->c_space) {
+	case COLOR_SPACE_SRGB_FULL_RANGE:
+		set_rgb_adjustment_legacy(opp110, adjust);
+		break;
+	case COLOR_SPACE_SRGB_LIMITED_RANGE:
+		set_rgb_limited_range_adjustment(
+			opp110, adjust);
+		break;
+	case COLOR_SPACE_YCBCR601:
+	case COLOR_SPACE_YCBCR709:
+	case COLOR_SPACE_YCBCR601_YONLY:
+	case COLOR_SPACE_YCBCR709_YONLY:
+	case COLOR_SPACE_YPBPR601:
+	case COLOR_SPACE_YPBPR709:
+		set_yuv_adjustment(opp110, adjust);
+		break;
+	default:
+		set_rgb_adjustment_legacy(opp110, adjust);
+		break;
+	}
+
+	/*  We did everything ,now program DxOUTPUT_CSC_CONTROL */
+	configure_graphics_mode(opp110, config, adjust->csc_adjust_type,
+		adjust->c_space);
+}
+
+void dce110_opp_set_csc_default(
+	struct output_pixel_processor *opp,
+	const struct default_adjustment *default_adjust)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+	enum csc_color_mode config =
+			CSC_COLOR_MODE_GRAPHICS_PREDEFINED;
+
+	if (default_adjust->force_hw_default == false) {
+		const struct out_csc_color_matrix *elm;
+		/* currently parameter not in use */
+		enum grph_color_adjust_option option =
+			GRPH_COLOR_MATRIX_HW_DEFAULT;
+		uint32_t i;
+		/*
+		 * HW default false we program locally defined matrix
+		 * HW default true  we use predefined hw matrix and we
+		 * do not need to program matrix
+		 * OEM wants the HW default via runtime parameter.
+		 */
+		option = GRPH_COLOR_MATRIX_SW;
+
+		for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) {
+			elm = &global_color_matrix[i];
+			if (elm->color_space != default_adjust->color_space)
+				continue;
+			/* program the matrix with default values from this
+			 * file */
+			program_color_matrix(opp110, elm, option);
+			config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC;
+			break;
+		}
+	}
+
+	/* configure the what we programmed :
+	 * 1. Default values from this file
+	 * 2. Use hardware default from ROM_A and we do not need to program
+	 * matrix */
+
+	configure_graphics_mode(opp110, config,
+		default_adjust->csc_adjust_type,
+		default_adjust->color_space);
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c
new file mode 100644
index 000000000000..235b92e3af61
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: AMD
+ *
+ */
+
+#include "dm_services.h"
+
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+#include "dce110_opp.h"
+
+#define FMT_REG(reg)\
+	(reg + opp110->offsets.fmt_offset)
+
+/**
+ *	set_truncation
+ *	1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp
+ *	2) enable truncation
+ *	3) HW remove 12bit FMT support for DCE11 power saving reason.
+ */
+static void set_truncation(
+		struct dce110_opp *opp110,
+		const struct bit_depth_reduction_params *params)
+{
+	uint32_t value = 0;
+	uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL);
+
+	/*Disable truncation*/
+	value = dm_read_reg(opp110->base.ctx, addr);
+	set_reg_field_value(value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN);
+	set_reg_field_value(value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH);
+	set_reg_field_value(value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE);
+
+	dm_write_reg(opp110->base.ctx, addr, value);
+
+	/* no 10bpc trunc on DCE11*/
+	if (params->flags.TRUNCATE_ENABLED == 0 ||
+		params->flags.TRUNCATE_DEPTH == 2)
+		return;
+
+	/*Set truncation depth and Enable truncation*/
+	set_reg_field_value(value, 1,
+		FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN);
+	set_reg_field_value(value, params->flags.TRUNCATE_MODE,
+		FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE);
+	set_reg_field_value(value, params->flags.TRUNCATE_DEPTH,
+		FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH);
+
+	dm_write_reg(opp110->base.ctx, addr, value);
+
+}
+
+/**
+ *	set_spatial_dither
+ *	1) set spatial dithering mode: pattern of seed
+ *	2) set spatical dithering depth: 0 for 18bpp or 1 for 24bpp
+ *	3) set random seed
+ *	4) set random mode
+ *		lfsr is reset every frame or not reset
+ *		RGB dithering method
+ *		0: RGB data are all dithered with x^28+x^3+1
+ *		1: R data is dithered with x^28+x^3+1
+ *		G data is dithered with x^28+X^9+1
+ *		B data is dithered with x^28+x^13+1
+ *		enable high pass filter or not
+ *	5) enable spatical dithering
+ */
+static void set_spatial_dither(
+	struct dce110_opp *opp110,
+	const struct bit_depth_reduction_params *params)
+{
+	uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL);
+	uint32_t depth_cntl_value = 0;
+	uint32_t fmt_cntl_value = 0;
+	uint32_t dither_r_value = 0;
+	uint32_t dither_g_value = 0;
+	uint32_t dither_b_value = 0;
+
+	/*Disable spatial (random) dithering*/
+	depth_cntl_value = dm_read_reg(opp110->base.ctx, addr);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE);
+	set_reg_field_value(depth_cntl_value, 0,
+		FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE);
+
+	dm_write_reg(opp110->base.ctx, addr, depth_cntl_value);
+
+	/* no 10bpc on DCE11*/
+	if (params->flags.SPATIAL_DITHER_ENABLED == 0 ||
+		params->flags.SPATIAL_DITHER_DEPTH == 2)
+		return;
+
+	addr = FMT_REG(mmFMT_CONTROL);
+	fmt_cntl_value = dm_read_reg(opp110->base.ctx, addr);
+	/* only use FRAME_COUNTER_MAX if frameRandom == 1*/
+	if (params->flags.FRAME_RANDOM == 1) {
+		if (params->flags.SPATIAL_DITHER_DEPTH == 0 ||
+		params->flags.SPATIAL_DITHER_DEPTH == 1) {
+			set_reg_field_value(fmt_cntl_value, 15,
+				FMT_CONTROL,
+				FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX);
+			set_reg_field_value(fmt_cntl_value, 2,
+				FMT_CONTROL,
+				FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP);
+		} else if (params->flags.SPATIAL_DITHER_DEPTH == 2) {
+			set_reg_field_value(fmt_cntl_value, 3,
+				FMT_CONTROL,
+				FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX);
+			set_reg_field_value(fmt_cntl_value, 1,
+				FMT_CONTROL,
+				FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP);
+		} else
+			return;
+	} else {
+		set_reg_field_value(fmt_cntl_value, 0,
+			FMT_CONTROL,
+			FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX);
+		set_reg_field_value(fmt_cntl_value, 0,
+			FMT_CONTROL,
+			FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP);
+	}
+
+	dm_write_reg(opp110->base.ctx, addr, fmt_cntl_value);
+
+	/*Set seed for random values for
+	 * spatial dithering for R,G,B channels*/
+	addr = FMT_REG(mmFMT_DITHER_RAND_R_SEED);
+	set_reg_field_value(dither_r_value, params->r_seed_value,
+		FMT_DITHER_RAND_R_SEED,
+		FMT_RAND_R_SEED);
+	dm_write_reg(opp110->base.ctx, addr, dither_r_value);
+
+	addr = FMT_REG(mmFMT_DITHER_RAND_G_SEED);
+	set_reg_field_value(dither_g_value,
+		params->g_seed_value,
+		FMT_DITHER_RAND_G_SEED,
+		FMT_RAND_G_SEED);
+	dm_write_reg(opp110->base.ctx, addr, dither_g_value);
+
+	addr = FMT_REG(mmFMT_DITHER_RAND_B_SEED);
+	set_reg_field_value(dither_b_value, params->b_seed_value,
+		FMT_DITHER_RAND_B_SEED,
+		FMT_RAND_B_SEED);
+	dm_write_reg(opp110->base.ctx, addr, dither_b_value);
+
+	/* FMT_OFFSET_R_Cr  31:16 0x0 Setting the zero
+	 * offset for the R/Cr channel, lower 4LSB
+	 * is forced to zeros. Typically set to 0
+	 * RGB and 0x80000 YCbCr.
+	 */
+	/* FMT_OFFSET_G_Y   31:16 0x0 Setting the zero
+	 * offset for the G/Y  channel, lower 4LSB is
+	 * forced to zeros. Typically set to 0 RGB
+	 * and 0x80000 YCbCr.
+	 */
+	/* FMT_OFFSET_B_Cb  31:16 0x0 Setting the zero
+	 * offset for the B/Cb channel, lower 4LSB is
+	 * forced to zeros. Typically set to 0 RGB and
+	 * 0x80000 YCbCr.
+	 */
+
+	/*Set spatial dithering bit depth*/
+	set_reg_field_value(depth_cntl_value,
+		params->flags.SPATIAL_DITHER_DEPTH,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_SPATIAL_DITHER_DEPTH);
+
+	/* Set spatial dithering mode
+	 * (default is Seed patterrn AAAA...)
+	 */
+	set_reg_field_value(depth_cntl_value,
+		params->flags.SPATIAL_DITHER_MODE,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_SPATIAL_DITHER_MODE);
+
+	/*Reset only at startup*/
+	set_reg_field_value(depth_cntl_value,
+		params->flags.FRAME_RANDOM,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_RGB_RANDOM_ENABLE);
+
+	/*Set RGB data dithered with x^28+x^3+1*/
+	set_reg_field_value(depth_cntl_value,
+		params->flags.RGB_RANDOM,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_RGB_RANDOM_ENABLE);
+
+	/*Disable High pass filter*/
+	set_reg_field_value(depth_cntl_value,
+		params->flags.HIGHPASS_RANDOM,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_HIGHPASS_RANDOM_ENABLE);
+
+	/*Enable spatial dithering*/
+	set_reg_field_value(depth_cntl_value,
+		1,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_SPATIAL_DITHER_EN);
+
+	addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL);
+	dm_write_reg(opp110->base.ctx, addr, depth_cntl_value);
+
+}
+
+/**
+ *	SetTemporalDither (Frame Modulation)
+ *	1) set temporal dither depth
+ *	2) select pattern: from hard-coded pattern or programmable pattern
+ *	3) select optimized strips for BGR or RGB LCD sub-pixel
+ *	4) set s matrix
+ *	5) set t matrix
+ *	6) set grey level for 0.25, 0.5, 0.75
+ *	7) enable temporal dithering
+ */
+static void set_temporal_dither(
+	struct dce110_opp *opp110,
+	const struct bit_depth_reduction_params *params)
+{
+	uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL);
+	uint32_t value;
+
+	/*Disable temporal (frame modulation) dithering first*/
+	value = dm_read_reg(opp110->base.ctx, addr);
+
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_EN);
+
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_RESET);
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_OFFSET);
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_DEPTH);
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_LEVEL);
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_25FRC_SEL);
+
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_50FRC_SEL);
+
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_75FRC_SEL);
+
+	dm_write_reg(opp110->base.ctx, addr, value);
+
+	/* no 10bpc dither on DCE11*/
+	if (params->flags.FRAME_MODULATION_ENABLED == 0 ||
+		params->flags.FRAME_MODULATION_DEPTH == 2)
+		return;
+
+	/* Set temporal dithering depth*/
+	set_reg_field_value(value,
+		params->flags.FRAME_MODULATION_DEPTH,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_DEPTH);
+
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_RESET);
+
+	set_reg_field_value(value,
+		0,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_OFFSET);
+
+	/*Select legacy pattern based on FRC and Temporal level*/
+	addr = FMT_REG(mmFMT_TEMPORAL_DITHER_PATTERN_CONTROL);
+	dm_write_reg(opp110->base.ctx, addr, 0);
+	/*Set s matrix*/
+	addr = FMT_REG(
+		mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX);
+	dm_write_reg(opp110->base.ctx, addr, 0);
+	/*Set t matrix*/
+	addr = FMT_REG(
+		mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX);
+	dm_write_reg(opp110->base.ctx, addr, 0);
+
+	/*Select patterns for 0.25, 0.5 and 0.75 grey level*/
+	set_reg_field_value(value,
+		params->flags.TEMPORAL_LEVEL,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_LEVEL);
+
+	set_reg_field_value(value,
+		params->flags.FRC25,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_25FRC_SEL);
+
+	set_reg_field_value(value,
+		params->flags.FRC50,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_50FRC_SEL);
+
+	set_reg_field_value(value,
+		params->flags.FRC75,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_75FRC_SEL);
+
+	/*Enable bit reduction by temporal (frame modulation) dithering*/
+	set_reg_field_value(value,
+		1,
+		FMT_BIT_DEPTH_CONTROL,
+		FMT_TEMPORAL_DITHER_EN);
+
+	addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL);
+	dm_write_reg(opp110->base.ctx, addr, value);
+
+}
+
+/**
+ *	Set Clamping
+ *	1) Set clamping format based on bpc - 0 for 6bpc (No clamping)
+ *		1 for 8 bpc
+ *		2 for 10 bpc
+ *		3 for 12 bpc
+ *		7 for programable
+ *	2) Enable clamp if Limited range requested
+ */
+static void set_clamping(
+	struct dce110_opp *opp110,
+	const struct clamping_and_pixel_encoding_params *params)
+{
+	uint32_t clamp_cntl_value = 0;
+	uint32_t red_clamp_value = 0;
+	uint32_t green_clamp_value = 0;
+	uint32_t blue_clamp_value = 0;
+	uint32_t addr = FMT_REG(mmFMT_CLAMP_CNTL);
+
+	clamp_cntl_value = dm_read_reg(opp110->base.ctx, addr);
+
+	set_reg_field_value(clamp_cntl_value,
+		0,
+		FMT_CLAMP_CNTL,
+		FMT_CLAMP_DATA_EN);
+
+	set_reg_field_value(clamp_cntl_value,
+		0,
+		FMT_CLAMP_CNTL,
+		FMT_CLAMP_COLOR_FORMAT);
+
+	switch (params->clamping_level) {
+	case CLAMPING_FULL_RANGE:
+		break;
+
+	case CLAMPING_LIMITED_RANGE_8BPC:
+		set_reg_field_value(clamp_cntl_value,
+			1,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_DATA_EN);
+
+		set_reg_field_value(clamp_cntl_value,
+			1,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_COLOR_FORMAT);
+
+		break;
+
+	case CLAMPING_LIMITED_RANGE_10BPC:
+		set_reg_field_value(clamp_cntl_value,
+			1,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_DATA_EN);
+
+		set_reg_field_value(clamp_cntl_value,
+			2,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_COLOR_FORMAT);
+
+		break;
+	case CLAMPING_LIMITED_RANGE_12BPC:
+		set_reg_field_value(clamp_cntl_value,
+			1,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_DATA_EN);
+
+		set_reg_field_value(clamp_cntl_value,
+			3,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_COLOR_FORMAT);
+
+		break;
+	case CLAMPING_LIMITED_RANGE_PROGRAMMABLE:
+		set_reg_field_value(clamp_cntl_value,
+			1,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_DATA_EN);
+
+		set_reg_field_value(clamp_cntl_value,
+			7,
+			FMT_CLAMP_CNTL,
+			FMT_CLAMP_COLOR_FORMAT);
+
+		/*set the defaults*/
+		set_reg_field_value(red_clamp_value,
+			0x10,
+			FMT_CLAMP_COMPONENT_R,
+			FMT_CLAMP_LOWER_R);
+
+		set_reg_field_value(red_clamp_value,
+			0xFEF,
+			FMT_CLAMP_COMPONENT_R,
+			FMT_CLAMP_UPPER_R);
+
+		addr = FMT_REG(mmFMT_CLAMP_COMPONENT_R);
+		dm_write_reg(opp110->base.ctx, addr, red_clamp_value);
+
+		set_reg_field_value(green_clamp_value,
+			0x10,
+			FMT_CLAMP_COMPONENT_G,
+			FMT_CLAMP_LOWER_G);
+
+		set_reg_field_value(green_clamp_value,
+			0xFEF,
+			FMT_CLAMP_COMPONENT_G,
+			FMT_CLAMP_UPPER_G);
+
+		addr = FMT_REG(mmFMT_CLAMP_COMPONENT_G);
+		dm_write_reg(opp110->base.ctx, addr, green_clamp_value);
+
+		set_reg_field_value(blue_clamp_value,
+			0x10,
+			FMT_CLAMP_COMPONENT_B,
+			FMT_CLAMP_LOWER_B);
+
+		set_reg_field_value(blue_clamp_value,
+			0xFEF,
+			FMT_CLAMP_COMPONENT_B,
+			FMT_CLAMP_UPPER_B);
+
+		addr = FMT_REG(mmFMT_CLAMP_COMPONENT_B);
+		dm_write_reg(opp110->base.ctx, addr, blue_clamp_value);
+
+		break;
+
+	default:
+		break;
+	}
+
+	addr = FMT_REG(mmFMT_CLAMP_CNTL);
+	/*Set clamp control*/
+	dm_write_reg(opp110->base.ctx, addr, clamp_cntl_value);
+
+}
+
+/**
+ *	set_pixel_encoding
+ *
+ *	Set Pixel Encoding
+ *		0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly
+ *		1: YCbCr 4:2:2
+ */
+static void set_pixel_encoding(
+	struct dce110_opp *opp110,
+	const struct clamping_and_pixel_encoding_params *params)
+{
+	uint32_t fmt_cntl_value;
+	uint32_t addr = FMT_REG(mmFMT_CONTROL);
+
+	/*RGB 4:4:4 or YCbCr 4:4:4 - 0; YCbCr 4:2:2 -1.*/
+	fmt_cntl_value = dm_read_reg(opp110->base.ctx, addr);
+
+	set_reg_field_value(fmt_cntl_value,
+		0,
+		FMT_CONTROL,
+		FMT_PIXEL_ENCODING);
+
+	if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
+		set_reg_field_value(fmt_cntl_value,
+			1,
+			FMT_CONTROL,
+			FMT_PIXEL_ENCODING);
+
+		/*00 - Pixels drop mode ,01 - Pixels average mode*/
+		set_reg_field_value(fmt_cntl_value,
+			0,
+			FMT_CONTROL,
+			FMT_SUBSAMPLING_MODE);
+
+		/*00 - Cb before Cr ,01 - Cr before Cb*/
+		set_reg_field_value(fmt_cntl_value,
+			0,
+			FMT_CONTROL,
+			FMT_SUBSAMPLING_ORDER);
+	}
+	dm_write_reg(opp110->base.ctx, addr, fmt_cntl_value);
+
+}
+
+void dce110_opp_program_bit_depth_reduction(
+	struct output_pixel_processor *opp,
+	const struct bit_depth_reduction_params *params)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+
+	set_truncation(opp110, params);
+	set_spatial_dither(opp110, params);
+	set_temporal_dither(opp110, params);
+}
+
+void dce110_opp_program_clamping_and_pixel_encoding(
+	struct output_pixel_processor *opp,
+	const struct clamping_and_pixel_encoding_params *params)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+
+	set_clamping(opp110, params);
+	set_pixel_encoding(opp110, params);
+}
+
+void dce110_opp_set_dyn_expansion(
+	struct output_pixel_processor *opp,
+	enum color_space color_sp,
+	enum dc_color_depth color_dpth,
+	enum signal_type signal)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+	uint32_t value;
+	bool enable_dyn_exp = false;
+	uint32_t addr = FMT_REG(mmFMT_DYNAMIC_EXP_CNTL);
+
+	value = dm_read_reg(opp->ctx, addr);
+
+	set_reg_field_value(value, 0,
+		FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN);
+	set_reg_field_value(value, 0,
+		FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE);
+
+	/* From HW programming guide:
+		FMT_DYNAMIC_EXP_EN = 0 for limited RGB or YCbCr output
+		FMT_DYNAMIC_EXP_EN = 1 for RGB full range only*/
+	if (color_sp == COLOR_SPACE_SRGB_FULL_RANGE)
+		enable_dyn_exp = true;
+
+	/*00 - 10-bit -> 12-bit dynamic expansion*/
+	/*01 - 8-bit  -> 12-bit dynamic expansion*/
+	if (signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+		switch (color_dpth) {
+		case COLOR_DEPTH_888:
+			set_reg_field_value(value, enable_dyn_exp ? 1:0,
+				FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN);
+			set_reg_field_value(value, 1,
+				FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE);
+			break;
+		case COLOR_DEPTH_101010:
+			set_reg_field_value(value, enable_dyn_exp ? 1:0,
+				FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN);
+			set_reg_field_value(value, 0,
+				FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE);
+			break;
+		case COLOR_DEPTH_121212:
+			break;
+		default:
+			break;
+		}
+	}
+
+	dm_write_reg(opp->ctx, addr, value);
+}
diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c
new file mode 100644
index 000000000000..32cf57dd80d2
--- /dev/null
+++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c
@@ -0,0 +1,2474 @@
+/*
+ * Copyright 2012-15 Advanced Micro Devices, Inc.
+ *
+ * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: AMD
+ *
+ */
+
+#include "dm_services.h"
+
+/* include DCE11 register header files */
+#include "dce/dce_11_0_d.h"
+#include "dce/dce_11_0_sh_mask.h"
+
+#include "dce110_opp.h"
+#include "gamma_types.h"
+
+#define DCP_REG(reg)\
+	(reg + opp110->offsets.dcp_offset)
+
+#define DCFE_REG(reg)\
+	(reg + opp110->offsets.dcfe_offset)
+
+enum {
+	MAX_PWL_ENTRY = 128,
+	MAX_REGIONS_NUMBER = 16
+
+};
+
+struct curve_config {
+	uint32_t offset;
+	int8_t segments[MAX_REGIONS_NUMBER];
+	int8_t begin;
+};
+
+/* BASE */
+static bool find_software_points(
+	struct dce110_opp *opp110,
+	struct fixed31_32 hw_point,
+	enum channel_name channel,
+	uint32_t *index_to_start,
+	uint32_t *index_left,
+	uint32_t *index_right,
+	enum hw_point_position *pos)
+{
+	const uint32_t max_number =
+			RGB_256X3X16 + opp110->regamma.extra_points;
+
+	struct fixed31_32 left, right;
+
+	uint32_t i = *index_to_start;
+
+	while (i < max_number) {
+		if (channel == CHANNEL_NAME_RED) {
+			left = opp110->
+					regamma.axis_x_256[i].r;
+
+			if (i < max_number - 1)
+				right = opp110->
+					regamma.axis_x_256[i + 1].r;
+			else
+				right = opp110->
+					regamma.axis_x_256[max_number - 1].r;
+		} else if (channel == CHANNEL_NAME_GREEN) {
+			left = opp110->regamma.axis_x_256[i].g;
+
+			if (i < max_number - 1)
+				right = opp110->
+					regamma.axis_x_256[i + 1].g;
+			else
+				right = opp110->
+					regamma.axis_x_256[max_number - 1].g;
+		} else {
+			left = opp110->regamma.axis_x_256[i].b;
+
+			if (i < max_number - 1)
+				right = opp110->
+					regamma.axis_x_256[i + 1].b;
+			else
+				right = opp110->
+					regamma.axis_x_256[max_number - 1].b;
+		}
+
+		if (dal_fixed31_32_le(left, hw_point) &&
+			dal_fixed31_32_le(hw_point, right)) {
+			*index_to_start = i;
+			*index_left = i;
+
+			if (i < max_number - 1)
+				*index_right = i + 1;
+			else
+				*index_right = max_number - 1;
+
+			*pos = HW_POINT_POSITION_MIDDLE;
+
+			return true;
+		} else if ((i == *index_to_start) &&
+			dal_fixed31_32_le(hw_point, left)) {
+			*index_to_start = i;
+			*index_left = i;
+			*index_right = i;
+
+			*pos = HW_POINT_POSITION_LEFT;
+
+			return true;
+		} else if ((i == max_number - 1) &&
+			dal_fixed31_32_le(right, hw_point)) {
+			*index_to_start = i;
+			*index_left = i;
+			*index_right = i;
+
+			*pos = HW_POINT_POSITION_RIGHT;
+
+			return true;
+		}
+
+		++i;
+	}
+
+	return false;
+}
+
+static bool find_software_points_dx(
+	struct dce110_opp *opp110,
+	struct fixed31_32 hw_point,
+	enum channel_name channel,
+	uint32_t *index_to_start,
+	uint32_t *index_left,
+	uint32_t *index_right,
+	enum hw_point_position *pos)
+{
+	const uint32_t max_number = DX_GAMMA_RAMP_MAX +
+					opp110->regamma.extra_points;
+
+	struct fixed31_32 left, right;
+
+	uint32_t i = *index_to_start;
+
+	while (i < max_number) {
+		if (channel == CHANNEL_NAME_RED) {
+			left = opp110->regamma.axis_x_1025[i].r;
+
+			if (i < DX_GAMMA_RAMP_MAX - 1)
+				right = opp110->
+					regamma.axis_x_1025[i + 1].r;
+			else
+				right = opp110->
+				regamma.axis_x_1025[DX_GAMMA_RAMP_MAX-1].r;
+		} else if (channel == CHANNEL_NAME_GREEN) {
+			left = opp110->regamma.axis_x_1025[i].g;
+
+			if (i < DX_GAMMA_RAMP_MAX - 1)
+				right = opp110->
+					regamma.axis_x_1025[i + 1].g;
+			else
+				right = opp110->
+				regamma.axis_x_1025[DX_GAMMA_RAMP_MAX-1].g;
+		} else {
+			left = opp110->regamma.axis_x_1025[i].b;
+
+			if (i < DX_GAMMA_RAMP_MAX - 1)
+				right = opp110->
+					regamma.axis_x_1025[i + 1].b;
+			else
+				right = opp110->
+				regamma.axis_x_1025[DX_GAMMA_RAMP_MAX-1].b;
+		}
+
+		if (dal_fixed31_32_le(left, hw_point) &&
+			dal_fixed31_32_le(hw_point, right)) {
+			*index_to_start = i;
+			*index_left = i;
+
+			if (i < DX_GAMMA_RAMP_MAX - 1)
+				*index_right = i + 1;
+			else
+				*index_right = DX_GAMMA_RAMP_MAX - 1;
+
+			*pos = HW_POINT_POSITION_MIDDLE;
+
+			return true;
+		} else if ((i == *index_to_start) &&
+			dal_fixed31_32_le(hw_point, left)) {
+			*index_to_start = i;
+			*index_left = i;
+			*index_right = i;
+
+			*pos = HW_POINT_POSITION_LEFT;
+
+			return true;
+		} else if ((i == max_number - 1) &&
+			dal_fixed31_32_le(right, hw_point)) {
+			*index_to_start = i;
+			*index_left = i;
+			*index_right = i;
+
+			*pos = HW_POINT_POSITION_RIGHT;
+
+			return true;
+		}
+
+		++i;
+	}
+
+	return false;
+}
+
+static bool build_custom_gamma_mapping_coefficients_worker(
+	struct dce110_opp *opp110,
+	struct pixel_gamma_point *coeff,
+	enum channel_name channel,
+	uint32_t number_of_points,
+	enum pixel_format pixel_format)
+{
+	uint32_t i = 0;
+
+	while (i <= number_of_points) {
+		struct fixed31_32 coord_x;
+
+		uint32_t index_to_start = 0;
+		uint32_t index_left = 0;
+		uint32_t index_right = 0;
+
+		enum hw_point_position hw_pos;
+
+		struct gamma_point *point;
+
+		struct fixed31_32 left_pos;
+		struct fixed31_32 right_pos;
+
+		if (pixel_format == PIXEL_FORMAT_FP16)
+			coord_x = opp110->
+				regamma.coordinates_x[i].adjusted_x;
+		else if (channel == CHANNEL_NAME_RED)
+			coord_x = opp110->
+				regamma.coordinates_x[i].regamma_y_red;
+		else if (channel == CHANNEL_NAME_GREEN)
+			coord_x = opp110->
+				regamma.coordinates_x[i].regamma_y_green;
+		else
+			coord_x = opp110->
+				regamma.coordinates_x[i].regamma_y_blue;
+
+		if (!find_software_points(
+			opp110, coord_x, channel,
+			&index_to_start, &index_left, &index_right, &hw_pos)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (index_left >= RGB_256X3X16 +
+				opp110->regamma.extra_points) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (index_right >= RGB_256X3X16 +
+				opp110->regamma.extra_points) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (channel == CHANNEL_NAME_RED) {
+			point = &coeff[i].r;
+
+			left_pos = opp110->
+					regamma.axis_x_256[index_left].r;
+			right_pos = opp110->
+					regamma.axis_x_256[index_right].r;
+		} else if (channel == CHANNEL_NAME_GREEN) {
+			point = &coeff[i].g;
+
+			left_pos = opp110->
+					regamma.axis_x_256[index_left].g;
+			right_pos = opp110->
+					regamma.axis_x_256[index_right].g;
+		} else {
+			point = &coeff[i].b;
+
+			left_pos = opp110->
+					regamma.axis_x_256[index_left].b;
+			right_pos = opp110->
+					regamma.axis_x_256[index_right].b;
+		}
+
+		if (hw_pos == HW_POINT_POSITION_MIDDLE)
+			point->coeff = dal_fixed31_32_div(
+				dal_fixed31_32_sub(
+					coord_x,
+					left_pos),
+				dal_fixed31_32_sub(
+					right_pos,
+					left_pos));
+		else if (hw_pos == HW_POINT_POSITION_LEFT)
+			point->coeff = opp110->regamma.x_min;
+		else if (hw_pos == HW_POINT_POSITION_RIGHT)
+			point->coeff = opp110->regamma.x_max2;
+		else {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		point->left_index = index_left;
+		point->right_index = index_right;
+		point->pos = hw_pos;
+
+		++i;
+	}
+
+	return true;
+}
+
+static inline bool build_custom_gamma_mapping_coefficients(
+	struct dce110_opp *opp110,
+	enum channel_name channel,
+	uint32_t number_of_points,
+	enum pixel_format pixel_format)
+{
+	return build_custom_gamma_mapping_coefficients_worker(
+		opp110, opp110->regamma.coeff128, channel,
+		number_of_points, pixel_format);
+}
+
+static inline bool build_oem_custom_gamma_mapping_coefficients(
+	struct dce110_opp *opp110,
+	enum channel_name channel,
+	uint32_t number_of_points,
+	enum pixel_format pixel_format)
+{
+	return build_custom_gamma_mapping_coefficients_worker(
+		opp110, opp110->regamma.coeff128_oem, channel,
+		number_of_points, pixel_format);
+}
+
+static bool build_custom_dx_gamma_mapping_coefficients(
+	struct dce110_opp *opp110,
+	enum channel_name channel,
+	uint32_t number_of_points,
+	enum pixel_format pixel_format)
+{
+	uint32_t i = 0;
+
+	while (i <= number_of_points) {
+		struct fixed31_32 coord_x;
+
+		uint32_t index_to_start = 0;
+		uint32_t index_left = 0;
+		uint32_t index_right = 0;
+
+		enum hw_point_position hw_pos;
+
+		struct gamma_point *point;
+
+		struct fixed31_32 left_pos;
+		struct fixed31_32 right_pos;
+
+		if (pixel_format == PIXEL_FORMAT_FP16)
+			coord_x = opp110->
+			regamma.coordinates_x[i].adjusted_x;
+		else if (channel == CHANNEL_NAME_RED)
+			coord_x = opp110->
+			regamma.coordinates_x[i].regamma_y_red;
+		else if (channel == CHANNEL_NAME_GREEN)
+			coord_x = opp110->
+			regamma.coordinates_x[i].regamma_y_green;
+		else
+			coord_x = opp110->
+			regamma.coordinates_x[i].regamma_y_blue;
+
+		if (!find_software_points_dx(
+			opp110, coord_x, channel,
+			&index_to_start, &index_left, &index_right, &hw_pos)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (index_left >= DX_GAMMA_RAMP_MAX +
+				opp110->regamma.extra_points) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (index_right >= DX_GAMMA_RAMP_MAX +
+				opp110->regamma.extra_points) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (channel == CHANNEL_NAME_RED) {
+			point = &opp110->regamma.coeff128_dx[i].r;
+
+			left_pos = opp110->
+					regamma.axis_x_1025[index_left].r;
+			right_pos = opp110->
+					regamma.axis_x_1025[index_right].r;
+		} else if (channel == CHANNEL_NAME_GREEN) {
+			point = &opp110->regamma.coeff128_dx[i].g;
+
+			left_pos = opp110->
+					regamma.axis_x_1025[index_left].g;
+			right_pos = opp110->
+					regamma.axis_x_1025[index_right].g;
+		} else {
+			point = &opp110->regamma.coeff128_dx[i].b;
+
+			left_pos = opp110->
+					regamma.axis_x_1025[index_left].b;
+			right_pos = opp110->
+					regamma.axis_x_1025[index_right].b;
+		}
+
+		if (hw_pos == HW_POINT_POSITION_MIDDLE)
+			point->coeff = dal_fixed31_32_div(
+				dal_fixed31_32_sub(
+					coord_x,
+					left_pos),
+				dal_fixed31_32_sub(
+					right_pos,
+					left_pos));
+		else if (hw_pos == HW_POINT_POSITION_LEFT)
+			point->coeff = opp110->regamma.x_min;
+		else if (hw_pos == HW_POINT_POSITION_RIGHT)
+			point->coeff = opp110->regamma.x_max2;
+		else {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		point->left_index = index_left;
+		point->right_index = index_right;
+		point->pos = hw_pos;
+
+		++i;
+	}
+
+	return true;
+}
+
+static struct fixed31_32 calculate_mapped_value(
+	struct dce110_opp *opp110,
+	struct pwl_float_data *rgb,
+	const struct pixel_gamma_point *coeff,
+	enum channel_name channel,
+	uint32_t max_index)
+{
+	const struct gamma_point *point;
+
+	struct fixed31_32 result;
+
+	if (channel == CHANNEL_NAME_RED)
+		point = &coeff->r;
+	else if (channel == CHANNEL_NAME_GREEN)
+		point = &coeff->g;
+	else
+		point = &coeff->b;
+
+	if ((point->left_index < 0) || (point->left_index > max_index)) {
+		BREAK_TO_DEBUGGER();
+		return dal_fixed31_32_zero;
+	}
+
+	if ((point->right_index < 0) || (point->right_index > max_index)) {
+		BREAK_TO_DEBUGGER();
+		return dal_fixed31_32_zero;
+	}
+
+	if (point->pos == HW_POINT_POSITION_MIDDLE)
+		if (channel == CHANNEL_NAME_RED)
+			result = dal_fixed31_32_add(
+				dal_fixed31_32_mul(
+					point->coeff,
+					dal_fixed31_32_sub(
+						rgb[point->right_index].r,
+						rgb[point->left_index].r)),
+				rgb[point->left_index].r);
+		else if (channel == CHANNEL_NAME_GREEN)
+			result = dal_fixed31_32_add(
+				dal_fixed31_32_mul(
+					point->coeff,
+					dal_fixed31_32_sub(
+						rgb[point->right_index].g,
+						rgb[point->left_index].g)),
+				rgb[point->left_index].g);
+		else
+			result = dal_fixed31_32_add(
+				dal_fixed31_32_mul(
+					point->coeff,
+					dal_fixed31_32_sub(
+						rgb[point->right_index].b,
+						rgb[point->left_index].b)),
+				rgb[point->left_index].b);
+	else if (point->pos == HW_POINT_POSITION_LEFT) {
+		BREAK_TO_DEBUGGER();
+		result = opp110->regamma.x_min;
+	} else {
+		BREAK_TO_DEBUGGER();
+		result = opp110->regamma.x_max1;
+	}
+
+	return result;
+}
+
+static inline struct fixed31_32 calculate_regamma_user_mapped_value(
+	struct dce110_opp *opp110,
+	const struct pixel_gamma_point *coeff,
+	enum channel_name channel,
+	uint32_t max_index)
+{
+	return calculate_mapped_value(
+		opp110, opp110->regamma.rgb_oem,
+		coeff, channel, max_index);
+}
+
+static inline struct fixed31_32 calculate_user_mapped_value(
+	struct dce110_opp *opp110,
+	const struct pixel_gamma_point *coeff,
+	enum channel_name channel,
+	uint32_t max_index)
+{
+	return calculate_mapped_value(
+		opp110, opp110->regamma.rgb_user,
+		coeff, channel, max_index);
+}
+
+static inline struct fixed31_32 calculate_oem_mapped_value(
+	struct dce110_opp *opp110,
+	uint32_t index,
+	enum channel_name channel,
+	uint32_t max_index)
+{
+	return calculate_regamma_user_mapped_value(
+		opp110, opp110->regamma.coeff128_oem +
+		index, channel, max_index);
+}
+
+static void scale_oem_gamma(
+	struct dce110_opp *opp110,
+	const struct regamma_ramp *regamma_ramp)
+{
+	const uint16_t max_driver = 0xFFFF;
+	const uint16_t max_os = 0xFF00;
+
+	uint16_t scale = max_os;
+
+	uint32_t i;
+
+	struct pwl_float_data *rgb = opp110->regamma.rgb_oem;
+	struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1;
+
+	/* find OEM maximum */
+
+	i = 0;
+
+	do {
+		if ((regamma_ramp->gamma[i] > max_os) ||
+			(regamma_ramp->gamma[i + RGB_256X3X16] > max_os) ||
+			(regamma_ramp->gamma[i + 2 * RGB_256X3X16] > max_os)) {
+			scale = max_driver;
+			break;
+		}
+
+		++i;
+	} while (i != RGB_256X3X16);
+
+	/* scale */
+
+	i = 0;
+
+	do {
+		rgb->r = dal_fixed31_32_div_int(
+			dal_fixed31_32_from_int(
+				regamma_ramp->gamma[i]),
+			scale);
+		rgb->g = dal_fixed31_32_div_int(
+			dal_fixed31_32_from_int(
+				regamma_ramp->gamma[i + RGB_256X3X16]),
+			scale);
+		rgb->b = dal_fixed31_32_div_int(
+			dal_fixed31_32_from_int(
+				regamma_ramp->gamma[i + 2 * RGB_256X3X16]),
+			scale);
+
+		++rgb;
+		++i;
+	} while (i != RGB_256X3X16);
+
+	/* add 3 extra points, 2 physical plus 1 virtual */
+
+	rgb->r = dal_fixed31_32_mul(rgb_last->r,
+			opp110->regamma.divider1);
+	rgb->g = dal_fixed31_32_mul(rgb_last->g,
+			opp110->regamma.divider1);
+	rgb->b = dal_fixed31_32_mul(rgb_last->b,
+			opp110->regamma.divider1);
+
+	++rgb;
+
+	rgb->r = dal_fixed31_32_mul(rgb_last->r,
+			opp110->regamma.divider2);
+	rgb->g = dal_fixed31_32_mul(rgb_last->g,
+			opp110->regamma.divider2);
+	rgb->b = dal_fixed31_32_mul(rgb_last->b,
+			opp110->regamma.divider2);
+
+	++rgb;
+
+	rgb->r = dal_fixed31_32_mul(rgb_last->r,
+			opp110->regamma.divider3);
+	rgb->g = dal_fixed31_32_mul(rgb_last->g,
+			opp110->regamma.divider3);
+	rgb->b = dal_fixed31_32_mul(rgb_last->b,
+			opp110->regamma.divider3);
+}
+
+static inline void copy_rgb_regamma_to_coordinates_x(
+	struct dce110_opp *opp110)
+{
+	struct hw_x_point *coords = opp110->regamma.coordinates_x;
+	const struct pwl_float_data_ex *rgb_regamma =
+			opp110->regamma.rgb_regamma;
+
+	uint32_t i = 0;
+
+	while (i <= opp110->regamma.hw_points_num) {
+		coords->regamma_y_red = rgb_regamma->r;
+		coords->regamma_y_green = rgb_regamma->g;
+		coords->regamma_y_blue = rgb_regamma->b;
+
+		++coords;
+		++rgb_regamma;
+		++i;
+	}
+}
+
+static bool calculate_interpolated_hardware_curve(
+	struct dce110_opp *opp110,
+	const struct gamma_ramp *gamma_ramp,
+	const struct gamma_parameters *params)
+{
+	struct pwl_result_data *rgb_resulted =
+			opp110->regamma.rgb_resulted;
+
+	const struct pixel_gamma_point *coeff;
+	uint32_t max_entries = opp110->regamma.extra_points - 1;
+
+	uint32_t i = 0;
+
+	if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16) {
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_RED,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_GREEN,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_BLUE,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		coeff = opp110->regamma.coeff128;
+		max_entries += RGB_256X3X16;
+	} else if (gamma_ramp->type == GAMMA_RAMP_DXGI_1) {
+		if (!build_custom_dx_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_RED,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_dx_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_GREEN,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_dx_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_BLUE,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		coeff = opp110->regamma.coeff128_dx;
+		max_entries += DX_GAMMA_RAMP_MAX;
+	} else {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	while (i <= opp110->regamma.hw_points_num) {
+		rgb_resulted->red = calculate_user_mapped_value(
+			opp110, coeff, CHANNEL_NAME_RED, max_entries);
+		rgb_resulted->green = calculate_user_mapped_value(
+			opp110, coeff, CHANNEL_NAME_GREEN, max_entries);
+		rgb_resulted->blue = calculate_user_mapped_value(
+			opp110, coeff, CHANNEL_NAME_BLUE, max_entries);
+
+		++coeff;
+		++rgb_resulted;
+		++i;
+	}
+
+	return true;
+}
+
+static void map_standard_regamma_hw_to_x_user(
+	struct dce110_opp *opp110,
+	enum gamma_ramp_type type,
+	const struct gamma_parameters *params)
+{
+	struct pwl_result_data *rgb_resulted =
+			opp110->regamma.rgb_resulted;
+	const struct pwl_float_data_ex *rgb_regamma =
+			opp110->regamma.rgb_regamma;
+
+	uint32_t i = 0;
+
+	while (i <= opp110->regamma.hw_points_num) {
+		rgb_resulted->red = rgb_regamma->r;
+		rgb_resulted->green = rgb_regamma->g;
+		rgb_resulted->blue = rgb_regamma->b;
+
+		++rgb_resulted;
+		++rgb_regamma;
+		++i;
+	}
+}
+
+bool dce110_opp_map_legacy_and_regamma_hw_to_x_user(
+	struct output_pixel_processor *opp,
+	const struct gamma_ramp *gamma_ramp,
+	const struct gamma_parameters *params)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+
+	if (params->regamma.features.bits.GAMMA_RAMP_ARRAY ||
+		params->regamma.features.bits.APPLY_DEGAMMA) {
+
+		const uint32_t max_entries =
+			RGB_256X3X16 + opp110->regamma.extra_points - 1;
+
+		const struct pixel_gamma_point *coeff =
+				opp110->regamma.coeff128;
+		struct pwl_result_data *rgb_resulted =
+				opp110->regamma.rgb_resulted;
+
+		uint32_t i = 0;
+
+		scale_oem_gamma(opp110, &params->regamma.regamma_ramp);
+
+		copy_rgb_regamma_to_coordinates_x(opp110);
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_RED,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_GREEN,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_BLUE,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		while (i <= opp110->regamma.hw_points_num) {
+			rgb_resulted->red =
+				calculate_regamma_user_mapped_value(opp110,
+					coeff,
+					CHANNEL_NAME_RED, max_entries);
+			rgb_resulted->green =
+				calculate_regamma_user_mapped_value(opp110,
+					coeff,
+					CHANNEL_NAME_GREEN, max_entries);
+			rgb_resulted->blue =
+				calculate_regamma_user_mapped_value(opp110,
+					coeff,
+					CHANNEL_NAME_BLUE, max_entries);
+
+			++coeff;
+			++rgb_resulted;
+			++i;
+		}
+	} else
+		map_standard_regamma_hw_to_x_user(opp110,
+				gamma_ramp->type,
+				params);
+
+	return true;
+}
+
+static bool map_regamma_hw_to_x_user(
+	struct dce110_opp *opp110,
+	const struct gamma_ramp *gamma_ramp,
+	const struct gamma_parameters *params)
+{
+	/* setup to spare calculated ideal regamma values */
+	if (params->regamma.features.bits.GAMMA_RAMP_ARRAY ||
+		params->regamma.features.bits.APPLY_DEGAMMA) {
+
+		const uint32_t max_entries =
+			RGB_256X3X16 + opp110->regamma.extra_points - 1;
+
+		const struct pixel_gamma_point *coeff =
+				opp110->regamma.coeff128;
+		struct hw_x_point *coords =
+				opp110->regamma.coordinates_x;
+
+		uint32_t i = 0;
+
+		scale_oem_gamma(opp110, &params->regamma.regamma_ramp);
+
+		copy_rgb_regamma_to_coordinates_x(opp110);
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_RED,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_GREEN,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_BLUE,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		while (i <= opp110->regamma.hw_points_num) {
+			coords->regamma_y_red =
+				calculate_regamma_user_mapped_value(opp110,
+					coeff,
+					CHANNEL_NAME_RED, max_entries);
+			coords->regamma_y_green =
+				calculate_regamma_user_mapped_value(opp110,
+					coeff,
+					CHANNEL_NAME_GREEN, max_entries);
+			coords->regamma_y_blue =
+				calculate_regamma_user_mapped_value(opp110,
+					coeff,
+					CHANNEL_NAME_BLUE, max_entries);
+
+			++coeff;
+			++coords;
+			++i;
+		}
+	} else {
+		copy_rgb_regamma_to_coordinates_x(opp110);
+	}
+
+	return calculate_interpolated_hardware_curve(opp110, gamma_ramp,
+		params);
+}
+
+static void build_regamma_coefficients(
+	const struct regamma_lut *regamma,
+	bool is_degamma_srgb,
+	struct gamma_coefficients *coefficients)
+{
+	/* sRGB should apply 2.4 */
+	static const int32_t numerator01[3] = { 31308, 31308, 31308 };
+	static const int32_t numerator02[3] = { 12920, 12920, 12920 };
+	static const int32_t numerator03[3] = { 55, 55, 55 };
+	static const int32_t numerator04[3] = { 55, 55, 55 };
+	static const int32_t numerator05[3] = { 2400, 2400, 2400 };
+
+	/* Non-sRGB should apply 2.2 */
+	static const int32_t numerator11[3] = { 180000, 180000, 180000 };
+	static const int32_t numerator12[3] = { 4500, 4500, 4500 };
+	static const int32_t numerator13[3] = { 99, 99, 99 };
+	static const int32_t numerator14[3] = { 99, 99, 99 };
+	static const int32_t numerator15[3] = { 2200, 2200, 2200 };
+
+	const int32_t *numerator1;
+	const int32_t *numerator2;
+	const int32_t *numerator3;
+	const int32_t *numerator4;
+	const int32_t *numerator5;
+
+	uint32_t i = 0;
+
+	if (!regamma->features.bits.GAMMA_RAMP_ARRAY) {
+		numerator1 = regamma->gamma_coeff.a0;
+		numerator2 = regamma->gamma_coeff.a1;
+		numerator3 = regamma->gamma_coeff.a2;
+		numerator4 = regamma->gamma_coeff.a3;
+		numerator5 = regamma->gamma_coeff.gamma;
+	} else if (is_degamma_srgb) {
+		numerator1 = numerator01;
+		numerator2 = numerator02;
+		numerator3 = numerator03;
+		numerator4 = numerator04;
+		numerator5 = numerator05;
+	} else {
+		numerator1 = numerator11;
+		numerator2 = numerator12;
+		numerator3 = numerator13;
+		numerator4 = numerator14;
+		numerator5 = numerator15;
+	}
+
+	do {
+		coefficients->a0[i] = dal_fixed31_32_from_fraction(
+			numerator1[i], 10000000);
+		coefficients->a1[i] = dal_fixed31_32_from_fraction(
+			numerator2[i], 1000);
+		coefficients->a2[i] = dal_fixed31_32_from_fraction(
+			numerator3[i], 1000);
+		coefficients->a3[i] = dal_fixed31_32_from_fraction(
+			numerator4[i], 1000);
+		coefficients->user_gamma[i] = dal_fixed31_32_from_fraction(
+			numerator5[i], 1000);
+
+		++i;
+	} while (i != ARRAY_SIZE(regamma->gamma_coeff.a0));
+}
+
+static struct fixed31_32 translate_from_linear_space(
+	struct fixed31_32 arg,
+	struct fixed31_32 a0,
+	struct fixed31_32 a1,
+	struct fixed31_32 a2,
+	struct fixed31_32 a3,
+	struct fixed31_32 gamma)
+{
+	const struct fixed31_32 one = dal_fixed31_32_from_int(1);
+
+	if (dal_fixed31_32_le(arg, dal_fixed31_32_neg(a0)))
+		return dal_fixed31_32_sub(
+			a2,
+			dal_fixed31_32_mul(
+				dal_fixed31_32_add(
+					one,
+					a3),
+				dal_fixed31_32_pow(
+					dal_fixed31_32_neg(arg),
+					dal_fixed31_32_recip(gamma))));
+	else if (dal_fixed31_32_le(a0, arg))
+		return dal_fixed31_32_sub(
+			dal_fixed31_32_mul(
+				dal_fixed31_32_add(
+					one,
+					a3),
+				dal_fixed31_32_pow(
+					arg,
+					dal_fixed31_32_recip(gamma))),
+			a2);
+	else
+		return dal_fixed31_32_mul(
+			arg,
+			a1);
+}
+
+static inline struct fixed31_32 translate_from_linear_space_ex(
+	struct fixed31_32 arg,
+	struct gamma_coefficients *coeff,
+	uint32_t color_index)
+{
+	return translate_from_linear_space(
+		arg,
+		coeff->a0[color_index],
+		coeff->a1[color_index],
+		coeff->a2[color_index],
+		coeff->a3[color_index],
+		coeff->user_gamma[color_index]);
+}
+
+static bool build_regamma_curve(
+	struct dce110_opp *opp110,
+	const struct gamma_parameters *params)
+{
+	struct pwl_float_data_ex *rgb = opp110->regamma.rgb_regamma;
+
+	uint32_t i;
+
+	struct gamma_coefficients coeff;
+
+	struct hw_x_point *coord_x =
+		opp110->regamma.coordinates_x;
+
+	build_regamma_coefficients(
+		&params->regamma,
+		params->regamma.features.bits.GRAPHICS_DEGAMMA_SRGB,
+		&coeff);
+
+	/* Use opp110->regamma.coordinates_x to retrieve
+	 * coordinates chosen base on given user curve (future task).
+	 * The x values are exponentially distributed and currently
+	 * it is hard-coded, the user curve shape is ignored.
+	 * The future task is to recalculate opp110-
+	 * regamma.coordinates_x based on input/user curve,
+	 * translation from 256/1025 to 128 pwl points.
+	 */
+
+	i = 0;
+
+	while (i != opp110->regamma.hw_points_num + 1) {
+		rgb->r = translate_from_linear_space_ex(
+			coord_x->adjusted_x, &coeff, 0);
+		rgb->g = translate_from_linear_space_ex(
+			coord_x->adjusted_x, &coeff, 1);
+		rgb->b = translate_from_linear_space_ex(
+			coord_x->adjusted_x, &coeff, 2);
+
+		++coord_x;
+		++rgb;
+		++i;
+	}
+
+	if (params->regamma.features.bits.GAMMA_RAMP_ARRAY &&
+			!params->regamma.features.bits.APPLY_DEGAMMA) {
+		const uint32_t max_entries =
+			RGB_256X3X16 + opp110->regamma.extra_points - 1;
+
+		/* interpolate between 256 input points and output 185 points */
+
+		scale_oem_gamma(opp110, &params->regamma.regamma_ramp);
+
+		if (!build_oem_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_RED,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_oem_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_GREEN,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!build_oem_custom_gamma_mapping_coefficients(
+			opp110, CHANNEL_NAME_BLUE,
+			opp110->regamma.hw_points_num,
+			params->surface_pixel_format)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		i = 0;
+
+		while (i != opp110->regamma.hw_points_num + 1) {
+			rgb->r = calculate_oem_mapped_value(
+				opp110, i, CHANNEL_NAME_RED, max_entries);
+			rgb->g = calculate_oem_mapped_value(
+				opp110, i, CHANNEL_NAME_GREEN, max_entries);
+			rgb->b = calculate_oem_mapped_value(
+				opp110, i, CHANNEL_NAME_BLUE, max_entries);
+			++rgb;
+			++i;
+		}
+	}
+
+	return true;
+}
+
+static void build_new_custom_resulted_curve(
+	struct dce110_opp *opp110,
+	const struct gamma_parameters *params)
+{
+	struct pwl_result_data *rgb = opp110->regamma.rgb_resulted;
+	struct pwl_result_data *rgb_plus_1 = rgb + 1;
+
+	uint32_t i;
+
+	i = 0;
+
+	while (i != opp110->regamma.hw_points_num + 1) {
+		rgb->red = dal_fixed31_32_clamp(
+			rgb->red, opp110->regamma.x_min,
+			opp110->regamma.x_max1);
+		rgb->green = dal_fixed31_32_clamp(
+			rgb->green, opp110->regamma.x_min,
+			opp110->regamma.x_max1);
+		rgb->blue = dal_fixed31_32_clamp(
+			rgb->blue, opp110->regamma.x_min,
+			opp110->regamma.x_max1);
+
+		++rgb;
+		++i;
+	}
+
+	rgb = opp110->regamma.rgb_resulted;
+
+	i = 1;
+
+	while (i != opp110->regamma.hw_points_num + 1) {
+		if (dal_fixed31_32_lt(rgb_plus_1->red, rgb->red))
+			rgb_plus_1->red = rgb->red;
+		if (dal_fixed31_32_lt(rgb_plus_1->green, rgb->green))
+			rgb_plus_1->green = rgb->green;
+		if (dal_fixed31_32_lt(rgb_plus_1->blue, rgb->blue))
+			rgb_plus_1->blue = rgb->blue;
+
+		rgb->delta_red = dal_fixed31_32_sub(
+			rgb_plus_1->red,
+			rgb->red);
+		rgb->delta_green = dal_fixed31_32_sub(
+			rgb_plus_1->green,
+			rgb->green);
+		rgb->delta_blue = dal_fixed31_32_sub(
+			rgb_plus_1->blue,
+			rgb->blue);
+
+		++rgb_plus_1;
+		++rgb;
+		++i;
+	}
+}
+
+static bool rebuild_curve_configuration_magic(
+	struct dce110_opp *opp110)
+{
+	const struct fixed31_32 magic_number =
+		dal_fixed31_32_from_fraction(249, 1000);
+
+	struct fixed31_32 y_r;
+	struct fixed31_32 y_g;
+	struct fixed31_32 y_b;
+
+	struct fixed31_32 y1_min;
+	struct fixed31_32 y2_max;
+	struct fixed31_32 y3_max;
+
+	y_r = opp110->regamma.rgb_resulted[0].red;
+	y_g = opp110->regamma.rgb_resulted[0].green;
+	y_b = opp110->regamma.rgb_resulted[0].blue;
+
+	y1_min = dal_fixed31_32_min(y_r, dal_fixed31_32_min(y_g, y_b));
+
+	opp110->regamma.arr_points[0].x =
+			opp110->regamma.coordinates_x[0].adjusted_x;
+	opp110->regamma.arr_points[0].y = y1_min;
+	opp110->regamma.arr_points[0].slope = dal_fixed31_32_div(
+		opp110->regamma.arr_points[0].y,
+		opp110->regamma.arr_points[0].x);
+
+	opp110->regamma.arr_points[1].x = dal_fixed31_32_add(
+		opp110->regamma.coordinates_x
+		[opp110->regamma.hw_points_num - 1].adjusted_x,
+		magic_number);
+
+	opp110->regamma.arr_points[2].x =
+			opp110->regamma.arr_points[1].x;
+
+	y_r = opp110->regamma.rgb_resulted
+			[opp110->regamma.hw_points_num - 1].red;
+	y_g = opp110->regamma.rgb_resulted
+			[opp110->regamma.hw_points_num - 1].green;
+	y_b = opp110->regamma.rgb_resulted
+			[opp110->regamma.hw_points_num - 1].blue;
+
+	y2_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b));
+
+	opp110->regamma.arr_points[1].y = y2_max;
+
+	y_r = opp110->regamma.rgb_resulted
+			[opp110->regamma.hw_points_num].red;
+	y_g = opp110->regamma.rgb_resulted
+			[opp110->regamma.hw_points_num].green;
+	y_b = opp110->regamma.rgb_resulted
+			[opp110->regamma.hw_points_num].blue;
+
+	y3_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b));
+
+	opp110->regamma.arr_points[2].y = y3_max;
+
+	opp110->regamma.arr_points[2].slope = dal_fixed31_32_one;
+
+	return true;
+}
+
+static bool build_custom_float(
+	struct fixed31_32 value,
+	const struct custom_float_format *format,
+	bool *negative,
+	uint32_t *mantissa,
+	uint32_t *exponenta)
+{
+	uint32_t exp_offset = (1 << (format->exponenta_bits - 1)) - 1;
+
+	const struct fixed31_32 mantissa_constant_plus_max_fraction =
+		dal_fixed31_32_from_fraction(
+			(1LL << (format->mantissa_bits + 1)) - 1,
+			1LL << format->mantissa_bits);
+
+	struct fixed31_32 mantiss;
+
+	if (dal_fixed31_32_eq(
+		value,
+		dal_fixed31_32_zero)) {
+		*negative = false;
+		*mantissa = 0;
+		*exponenta = 0;
+		return true;
+	}
+
+	if (dal_fixed31_32_lt(
+		value,
+		dal_fixed31_32_zero)) {
+		*negative = format->sign;
+		value = dal_fixed31_32_neg(value);
+	} else {
+		*negative = false;
+	}
+
+	if (dal_fixed31_32_lt(
+		value,
+		dal_fixed31_32_one)) {
+		uint32_t i = 1;
+
+		do {
+			value = dal_fixed31_32_shl(value, 1);
+			++i;
+		} while (dal_fixed31_32_lt(
+			value,
+			dal_fixed31_32_one));
+
+		--i;
+
+		if (exp_offset <= i) {
+			*mantissa = 0;
+			*exponenta = 0;
+			return true;
+		}
+
+		*exponenta = exp_offset - i;
+	} else if (dal_fixed31_32_le(
+		mantissa_constant_plus_max_fraction,
+		value)) {
+		uint32_t i = 1;
+
+		do {
+			value = dal_fixed31_32_shr(value, 1);
+			++i;
+		} while (dal_fixed31_32_lt(
+			mantissa_constant_plus_max_fraction,
+			value));
+
+		*exponenta = exp_offset + i - 1;
+	} else {
+		*exponenta = exp_offset;
+	}
+
+	mantiss = dal_fixed31_32_sub(
+		value,
+		dal_fixed31_32_one);
+
+	if (dal_fixed31_32_lt(
+			mantiss,
+			dal_fixed31_32_zero) ||
+		dal_fixed31_32_lt(
+			dal_fixed31_32_one,
+			mantiss))
+		mantiss = dal_fixed31_32_zero;
+	else
+		mantiss = dal_fixed31_32_shl(
+			mantiss,
+			format->mantissa_bits);
+
+	*mantissa = dal_fixed31_32_floor(mantiss);
+
+	return true;
+}
+
+static bool setup_custom_float(
+	const struct custom_float_format *format,
+	bool negative,
+	uint32_t mantissa,
+	uint32_t exponenta,
+	uint32_t *result)
+{
+	uint32_t i = 0;
+	uint32_t j = 0;
+
+	uint32_t value = 0;
+
+	/* verification code:
+	 * once calculation is ok we can remove it */
+
+	const uint32_t mantissa_mask =
+		(1 << (format->mantissa_bits + 1)) - 1;
+
+	const uint32_t exponenta_mask =
+		(1 << (format->exponenta_bits + 1)) - 1;
+
+	if (mantissa & ~mantissa_mask) {
+		BREAK_TO_DEBUGGER();
+		mantissa = mantissa_mask;
+	}
+
+	if (exponenta & ~exponenta_mask) {
+		BREAK_TO_DEBUGGER();
+		exponenta = exponenta_mask;
+	}
+
+	/* end of verification code */
+
+	while (i < format->mantissa_bits) {
+		uint32_t mask = 1 << i;
+
+		if (mantissa & mask)
+			value |= mask;
+
+		++i;
+	}
+
+	while (j < format->exponenta_bits) {
+		uint32_t mask = 1 << j;
+
+		if (exponenta & mask)
+			value |= mask << i;
+
+		++j;
+	}
+
+	if (negative && format->sign)
+		value |= 1 << (i + j);
+
+	*result = value;
+
+	return true;
+}
+
+static bool convert_to_custom_float_format(
+	struct fixed31_32 value,
+	const struct custom_float_format *format,
+	uint32_t *result)
+{
+	uint32_t mantissa;
+	uint32_t exponenta;
+	bool negative;
+
+	return build_custom_float(
+		value, format, &negative, &mantissa, &exponenta) &&
+	setup_custom_float(
+		format, negative, mantissa, exponenta, result);
+}
+
+static bool convert_to_custom_float_format_ex(
+	struct fixed31_32 value,
+	const struct custom_float_format *format,
+	struct custom_float_value *result)
+{
+	return build_custom_float(
+		value, format,
+		&result->negative, &result->mantissa, &result->exponenta) &&
+	setup_custom_float(
+		format, result->negative, result->mantissa, result->exponenta,
+		&result->value);
+}
+
+static bool convert_to_custom_float(
+	struct dce110_opp *opp110)
+{
+	struct custom_float_format fmt;
+
+	struct pwl_result_data *rgb = opp110->regamma.rgb_resulted;
+
+	uint32_t i = 0;
+
+	fmt.exponenta_bits = 6;
+	fmt.mantissa_bits = 12;
+	fmt.sign = true;
+
+	if (!convert_to_custom_float_format(
+		opp110->regamma.arr_points[0].x,
+		&fmt,
+		&opp110->regamma.arr_points[0].custom_float_x)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	if (!convert_to_custom_float_format(
+		opp110->regamma.arr_points[0].offset,
+		&fmt,
+		&opp110->regamma.arr_points[0].custom_float_offset)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	if (!convert_to_custom_float_format(
+		opp110->regamma.arr_points[0].slope,
+		&fmt,
+		&opp110->regamma.arr_points[0].custom_float_slope)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	fmt.mantissa_bits = 10;
+	fmt.sign = false;
+
+	if (!convert_to_custom_float_format(
+		opp110->regamma.arr_points[1].x,
+		&fmt,
+		&opp110->regamma.arr_points[1].custom_float_x)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	if (!convert_to_custom_float_format(
+		opp110->regamma.arr_points[1].y,
+		&fmt,
+		&opp110->regamma.arr_points[1].custom_float_y)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	if (!convert_to_custom_float_format(
+		opp110->regamma.arr_points[2].slope,
+		&fmt,
+		&opp110->regamma.arr_points[2].custom_float_slope)) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	fmt.mantissa_bits = 12;
+	fmt.sign = true;
+
+	while (i != opp110->regamma.hw_points_num) {
+		if (!convert_to_custom_float_format(
+			rgb->red,
+			&fmt,
+			&rgb->red_reg)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!convert_to_custom_float_format(
+			rgb->green,
+			&fmt,
+			&rgb->green_reg)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!convert_to_custom_float_format(
+			rgb->blue,
+			&fmt,
+			&rgb->blue_reg)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!convert_to_custom_float_format(
+			rgb->delta_red,
+			&fmt,
+			&rgb->delta_red_reg)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!convert_to_custom_float_format(
+			rgb->delta_green,
+			&fmt,
+			&rgb->delta_green_reg)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		if (!convert_to_custom_float_format(
+			rgb->delta_blue,
+			&fmt,
+			&rgb->delta_blue_reg)) {
+			BREAK_TO_DEBUGGER();
+			return false;
+		}
+
+		++rgb;
+		++i;
+	}
+
+	return true;
+}
+
+static bool round_custom_float_6_12(
+	struct hw_x_point *x)
+{
+	struct custom_float_format fmt;
+
+	struct custom_float_value value;
+
+	fmt.exponenta_bits = 6;
+	fmt.mantissa_bits = 12;
+	fmt.sign = true;
+
+	if (!convert_to_custom_float_format_ex(
+		x->x, &fmt, &value))
+		return false;
+
+	x->adjusted_x = x->x;
+
+	if (value.mantissa) {
+		BREAK_TO_DEBUGGER();
+
+		return false;
+	}
+
+	return true;
+}
+
+static bool build_hw_curve_configuration(
+	const struct curve_config *curve_config,
+	struct gamma_curve *gamma_curve,
+	struct curve_points *curve_points,
+	struct hw_x_point *points,
+	uint32_t *number_of_points)
+{
+	const int8_t max_regions_number = ARRAY_SIZE(curve_config->segments);
+
+	int8_t i;
+
+	uint8_t segments_calculation[8] = { 0 };
+
+	struct fixed31_32 region1 = dal_fixed31_32_zero;
+	struct fixed31_32 region2;
+	struct fixed31_32 increment;
+
+	uint32_t index = 0;
+	uint32_t segments = 0;
+	uint32_t max_number;
+
+	bool result = false;
+
+	if (!number_of_points) {
+		BREAK_TO_DEBUGGER();
+		return false;
+	}
+
+	max_number = *number_of_points;
+
+	i = 0;
+
+	while (i != max_regions_number) {
+		gamma_curve[i].offset = 0;
+		gamma_curve[i].segments_num = 0;
+
+		++i;
+	}
+
+	i = 0;
+
+	while (i != max_regions_number) {
+		/* number should go in uninterruptible sequence */
+		if (curve_config->segments[i] == -1)
+			break;
+
+		ASSERT(curve_config->segments[i] >= 0);
+
+		segments += (1 << curve_config->segments[i]);
+
+		++i;
+	}
+
+	if (segments > max_number) {
+		BREAK_TO_DEBUGGER();
+	} else {
+		int32_t divisor;
+		uint32_t offset = 0;
+		int8_t begin = curve_config->begin;
+		int32_t region_number = 0;
+
+		i = begin;
+
+		while ((index < max_number) &&
+			(region_number < max_regions_number) &&
+			(i <= 1)) {
+			int32_t j = 0;
+
+			segments = curve_config->segments[region_number];
+			divisor = 1 << segments;
+
+			if (segments == -1) {
+				if (i > 0) {
+					region1 = dal_fixed31_32_shl(
+						dal_fixed31_32_one,
+						i - 1);
+					region2 = dal_fixed31_32_shl(
+						dal_fixed31_32_one,
+						i);
+				} else {
+					region1 = dal_fixed31_32_shr(
+						dal_fixed31_32_one,
+						-(i - 1));
+					region2 = dal_fixed31_32_shr(
+						dal_fixed31_32_one,
+						-i);
+				}
+
+				break;
+			}
+
+			if (i > -1) {
+				region1 = dal_fixed31_32_shl(
+					dal_fixed31_32_one,
+					i);
+				region2 = dal_fixed31_32_shl(
+					dal_fixed31_32_one,
+					i + 1);
+			} else {
+				region1 = dal_fixed31_32_shr(
+					dal_fixed31_32_one,
+					-i);
+				region2 = dal_fixed31_32_shr(
+					dal_fixed31_32_one,
+					-(i + 1));
+			}
+
+			gamma_curve[region_number].offset = offset;
+			gamma_curve[region_number].segments_num = segments;
+
+			offset += divisor;
+
+			++segments_calculation[segments];
+
+			increment = dal_fixed31_32_div_int(
+				dal_fixed31_32_sub(
+					region2,
+					region1),
+				divisor);
+
+			points[index].x = region1;
+
+			round_custom_float_6_12(points + index);
+
+			++index;
+			++region_number;
+
+			while ((index < max_number) && (j < divisor - 1)) {
+				region1 = dal_fixed31_32_add(
+					region1,
+					increment);
+
+				points[index].x = region1;
+				points[index].adjusted_x = region1;
+
+				++index;
+				++j;
+			}
+
+			++i;
+		}
+
+		points[index].x = region1;
+
+		round_custom_float_6_12(points + index);
+
+		*number_of_points = index;
+
+		result = true;
+	}
+
+	curve_points[0].x = points[0].adjusted_x;
+	curve_points[0].offset = dal_fixed31_32_zero;
+
+	curve_points[1].x = points[index - 1].adjusted_x;
+	curve_points[1].offset = dal_fixed31_32_zero;
+
+	curve_points[2].x = points[index].adjusted_x;
+	curve_points[2].offset = dal_fixed31_32_zero;
+
+	return result;
+}
+
+static bool setup_distribution_points(
+	struct dce110_opp *opp110)
+{
+	uint32_t hw_points_num = MAX_PWL_ENTRY * 2;
+
+	struct curve_config cfg;
+
+	cfg.offset = 0;
+
+		cfg.segments[0] = 3;
+		cfg.segments[1] = 4;
+		cfg.segments[2] = 4;
+		cfg.segments[3] = 4;
+		cfg.segments[4] = 4;
+		cfg.segments[5] = 4;
+		cfg.segments[6] = 4;
+		cfg.segments[7] = 4;
+		cfg.segments[8] = 5;
+		cfg.segments[9] = 5;
+		cfg.segments[10] = 0;
+		cfg.segments[11] = -1;
+		cfg.segments[12] = -1;
+		cfg.segments[13] = -1;
+		cfg.segments[14] = -1;
+		cfg.segments[15] = -1;
+
+	cfg.begin = -10;
+
+	if (!build_hw_curve_configuration(
+		&cfg, opp110->regamma.arr_curve_points,
+		opp110->regamma.arr_points,
+		opp110->regamma.coordinates_x, &hw_points_num)) {
+		ASSERT_CRITICAL(false);
+		return false;
+	}
+
+	opp110->regamma.hw_points_num = hw_points_num;
+
+	return true;
+}
+
+
+/*
+ *****************************************************************************
+ *  Function: regamma_config_regions_and_segments
+ *
+ *     build regamma curve by using predefined hw points
+ *     uses interface parameters ,like EDID coeff.
+ *
+ * @param   : parameters   interface parameters
+ *  @return void
+ *
+ *  @note
+ *
+ *  @see
+ *
+ *****************************************************************************
+ */
+static void regamma_config_regions_and_segments(
+	struct dce110_opp *opp110)
+{
+	struct gamma_curve *curve;
+	uint32_t value = 0;
+
+	{
+		set_reg_field_value(
+			value,
+			opp110->regamma.arr_points[0].custom_float_x,
+			REGAMMA_CNTLA_START_CNTL,
+			REGAMMA_CNTLA_EXP_REGION_START);
+
+		set_reg_field_value(
+			value,
+			0,
+			REGAMMA_CNTLA_START_CNTL,
+			REGAMMA_CNTLA_EXP_REGION_START_SEGMENT);
+
+		dm_write_reg(opp110->base.ctx,
+				DCP_REG(mmREGAMMA_CNTLA_START_CNTL),
+				value);
+	}
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			opp110->regamma.arr_points[0].custom_float_slope,
+			REGAMMA_CNTLA_SLOPE_CNTL,
+			REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_SLOPE_CNTL), value);
+	}
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			opp110->regamma.arr_points[1].custom_float_x,
+			REGAMMA_CNTLA_END_CNTL1,
+			REGAMMA_CNTLA_EXP_REGION_END);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_END_CNTL1), value);
+	}
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			opp110->regamma.arr_points[2].custom_float_slope,
+			REGAMMA_CNTLA_END_CNTL2,
+			REGAMMA_CNTLA_EXP_REGION_END_BASE);
+
+		set_reg_field_value(
+			value,
+			opp110->regamma.arr_points[1].custom_float_y,
+			REGAMMA_CNTLA_END_CNTL2,
+			REGAMMA_CNTLA_EXP_REGION_END_SLOPE);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_END_CNTL2), value);
+	}
+
+	curve = opp110->regamma.arr_curve_points;
+
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_0_1,
+			REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_0_1,
+			REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_0_1,
+			REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_0_1,
+			REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS);
+
+		dm_write_reg(
+			opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_0_1),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_2_3,
+			REGAMMA_CNTLA_EXP_REGION2_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_2_3,
+			REGAMMA_CNTLA_EXP_REGION2_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_2_3,
+			REGAMMA_CNTLA_EXP_REGION3_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_2_3,
+			REGAMMA_CNTLA_EXP_REGION3_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_2_3),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_4_5,
+			REGAMMA_CNTLA_EXP_REGION4_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_4_5,
+			REGAMMA_CNTLA_EXP_REGION4_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_4_5,
+			REGAMMA_CNTLA_EXP_REGION5_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_4_5,
+			REGAMMA_CNTLA_EXP_REGION5_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_4_5),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_6_7,
+			REGAMMA_CNTLA_EXP_REGION6_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_6_7,
+			REGAMMA_CNTLA_EXP_REGION6_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_6_7,
+			REGAMMA_CNTLA_EXP_REGION7_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_6_7,
+			REGAMMA_CNTLA_EXP_REGION7_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_6_7),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_8_9,
+			REGAMMA_CNTLA_EXP_REGION8_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_8_9,
+			REGAMMA_CNTLA_EXP_REGION8_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_8_9,
+			REGAMMA_CNTLA_EXP_REGION9_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_8_9,
+			REGAMMA_CNTLA_EXP_REGION9_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_8_9),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_10_11,
+			REGAMMA_CNTLA_EXP_REGION10_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_10_11,
+			REGAMMA_CNTLA_EXP_REGION10_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_10_11,
+			REGAMMA_CNTLA_EXP_REGION11_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_10_11,
+			REGAMMA_CNTLA_EXP_REGION11_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_10_11),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_12_13,
+			REGAMMA_CNTLA_EXP_REGION12_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_12_13,
+			REGAMMA_CNTLA_EXP_REGION12_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_12_13,
+			REGAMMA_CNTLA_EXP_REGION13_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_12_13,
+			REGAMMA_CNTLA_EXP_REGION13_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_12_13),
+			value);
+	}
+
+	curve += 2;
+	{
+		value = 0;
+		set_reg_field_value(
+			value,
+			curve[0].offset,
+			REGAMMA_CNTLA_REGION_14_15,
+			REGAMMA_CNTLA_EXP_REGION14_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[0].segments_num,
+			REGAMMA_CNTLA_REGION_14_15,
+			REGAMMA_CNTLA_EXP_REGION14_NUM_SEGMENTS);
+
+		set_reg_field_value(
+			value,
+			curve[1].offset,
+			REGAMMA_CNTLA_REGION_14_15,
+			REGAMMA_CNTLA_EXP_REGION15_LUT_OFFSET);
+
+		set_reg_field_value(
+			value,
+			curve[1].segments_num,
+			REGAMMA_CNTLA_REGION_14_15,
+			REGAMMA_CNTLA_EXP_REGION15_NUM_SEGMENTS);
+
+		dm_write_reg(opp110->base.ctx,
+			DCP_REG(mmREGAMMA_CNTLA_REGION_14_15),
+			value);
+	}
+}
+
+static void program_pwl(
+	struct dce110_opp *opp110,
+	const struct gamma_parameters *params)
+{
+	uint32_t value;
+
+	{
+		uint8_t max_tries = 10;
+		uint8_t counter = 0;
+
+		/* Power on LUT memory */
+		value = dm_read_reg(opp110->base.ctx,
+				DCFE_REG(mmDCFE_MEM_PWR_CTRL));
+
+		set_reg_field_value(
+			value,
+			1,
+			DCFE_MEM_PWR_CTRL,
+			DCP_REGAMMA_MEM_PWR_DIS);
+
+		dm_write_reg(opp110->base.ctx,
+				DCFE_REG(mmDCFE_MEM_PWR_CTRL), value);
+
+		while (counter < max_tries) {
+			value =
+				dm_read_reg(
+					opp110->base.ctx,
+					DCFE_REG(mmDCFE_MEM_PWR_STATUS));
+
+			if (get_reg_field_value(
+				value,
+				DCFE_MEM_PWR_STATUS,
+				DCP_REGAMMA_MEM_PWR_STATE) == 0)
+				break;
+
+			++counter;
+		}
+
+		if (counter == max_tries) {
+			dal_logger_write(opp110->base.ctx->logger,
+				LOG_MAJOR_WARNING,
+				LOG_MINOR_COMPONENT_CONTROLLER,
+				"%s: regamma lut was not powered on "
+				"in a timely manner,"
+				" programming still proceeds\n",
+				__func__);
+		}
+	}
+
+	value = 0;
+
+	set_reg_field_value(
+		value,
+		7,
+		REGAMMA_LUT_WRITE_EN_MASK,
+		REGAMMA_LUT_WRITE_EN_MASK);
+
+	dm_write_reg(opp110->base.ctx,
+		DCP_REG(mmREGAMMA_LUT_WRITE_EN_MASK), value);
+	dm_write_reg(opp110->base.ctx,
+		DCP_REG(mmREGAMMA_LUT_INDEX), 0);
+
+	/* Program REGAMMA_LUT_DATA */
+	{
+		const uint32_t addr = DCP_REG(mmREGAMMA_LUT_DATA);
+
+		uint32_t i = 0;
+
+		struct pwl_result_data *rgb =
+				opp110->regamma.rgb_resulted;
+
+		while (i != opp110->regamma.hw_points_num) {
+			dm_write_reg(opp110->base.ctx, addr, rgb->red_reg);
+			dm_write_reg(opp110->base.ctx, addr, rgb->green_reg);
+			dm_write_reg(opp110->base.ctx, addr, rgb->blue_reg);
+
+			dm_write_reg(opp110->base.ctx, addr,
+				rgb->delta_red_reg);
+			dm_write_reg(opp110->base.ctx, addr,
+				rgb->delta_green_reg);
+			dm_write_reg(opp110->base.ctx, addr,
+				rgb->delta_blue_reg);
+
+			++rgb;
+			++i;
+		}
+	}
+
+	/*  we are done with DCP LUT memory; re-enable low power mode */
+	value = dm_read_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL));
+
+	set_reg_field_value(
+		value,
+		0,
+		DCFE_MEM_PWR_CTRL,
+		DCP_REGAMMA_MEM_PWR_DIS);
+
+	dm_write_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value);
+}
+
+void dce110_opp_power_on_regamma_lut(
+	struct output_pixel_processor *opp,
+	bool power_on)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+
+	uint32_t value =
+		dm_read_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL));
+
+	set_reg_field_value(
+		value,
+		power_on,
+		DCFE_MEM_PWR_CTRL,
+		DCP_REGAMMA_MEM_PWR_DIS);
+
+	set_reg_field_value(
+		value,
+		power_on,
+		DCFE_MEM_PWR_CTRL,
+		DCP_LUT_MEM_PWR_DIS);
+
+	dm_write_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value);
+}
+
+static bool scale_gamma(
+	struct dce110_opp *opp110,
+	const struct gamma_ramp *gamma_ramp,
+	const struct gamma_parameters *params)
+{
+	const struct gamma_ramp_rgb256x3x16 *gamma;
+	bool use_palette = params->surface_pixel_format == PIXEL_FORMAT_INDEX8;
+
+	const uint16_t max_driver = 0xFFFF;
+	const uint16_t max_os = 0xFF00;
+
+	uint16_t scaler = max_os;
+
+	uint32_t i;
+
+	struct dev_c_lut *palette = opp110->regamma.saved_palette;
+
+	struct pwl_float_data *rgb = opp110->regamma.rgb_user;
+	struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1;
+
+	if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16)
+		gamma = &gamma_ramp->gamma_ramp_rgb256x3x16;
+	else
+		return false; /* invalid option */
+
+	i = 0;
+
+	do {
+		if ((gamma->red[i] > max_os) ||
+			(gamma->green[i] > max_os) ||
+			(gamma->blue[i] > max_os)) {
+			scaler = max_driver;
+			break;
+		}
+		++i;
+	} while (i != RGB_256X3X16);
+
+	i = 0;
+
+	if (use_palette)
+		do {
+			rgb->r = dal_fixed31_32_from_fraction(
+				gamma->red[palette->red], scaler);
+			rgb->g = dal_fixed31_32_from_fraction(
+				gamma->green[palette->green], scaler);
+			rgb->b = dal_fixed31_32_from_fraction(
+				gamma->blue[palette->blue], scaler);
+
+			++palette;
+			++rgb;
+			++i;
+		} while (i != RGB_256X3X16);
+	else
+		do {
+			rgb->r = dal_fixed31_32_from_fraction(
+				gamma->red[i], scaler);
+			rgb->g = dal_fixed31_32_from_fraction(
+				gamma->green[i], scaler);
+			rgb->b = dal_fixed31_32_from_fraction(
+				gamma->blue[i], scaler);
+
+			++rgb;
+			++i;
+		} while (i != RGB_256X3X16);
+
+	rgb->r = dal_fixed31_32_mul(rgb_last->r,
+			opp110->regamma.divider1);
+	rgb->g = dal_fixed31_32_mul(rgb_last->g,
+			opp110->regamma.divider1);
+	rgb->b = dal_fixed31_32_mul(rgb_last->b,
+			opp110->regamma.divider1);
+
+	++rgb;
+
+	rgb->r = dal_fixed31_32_mul(rgb_last->r,
+			opp110->regamma.divider2);
+	rgb->g = dal_fixed31_32_mul(rgb_last->g,
+			opp110->regamma.divider2);
+	rgb->b = dal_fixed31_32_mul(rgb_last->b,
+			opp110->regamma.divider2);
+
+	++rgb;
+
+	rgb->r = dal_fixed31_32_mul(rgb_last->r,
+			opp110->regamma.divider3);
+	rgb->g = dal_fixed31_32_mul(rgb_last->g,
+			opp110->regamma.divider3);
+	rgb->b = dal_fixed31_32_mul(rgb_last->b,
+			opp110->regamma.divider3);
+
+	return true;
+}
+
+
+static void configure_regamma_mode(
+	struct dce110_opp *opp110,
+	const struct gamma_parameters *params,
+	bool force_bypass)
+{
+	const uint32_t addr = DCP_REG(mmREGAMMA_CONTROL);
+
+	enum wide_gamut_regamma_mode mode =
+		WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A;
+
+	uint32_t value = dm_read_reg(opp110->base.ctx, addr);
+
+	if (force_bypass) {
+
+		set_reg_field_value(
+			value,
+			0,
+			REGAMMA_CONTROL,
+			GRPH_REGAMMA_MODE);
+
+		dm_write_reg(opp110->base.ctx, addr, value);
+
+		return;
+	}
+
+	if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_BYPASS)
+		mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS;
+	else if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_HW) {
+		if (params->surface_pixel_format == PIXEL_FORMAT_FP16)
+			mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS;
+		else
+			mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24;
+	}
+
+	switch (mode) {
+	case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS:
+		set_reg_field_value(
+			value,
+			0,
+			REGAMMA_CONTROL,
+			GRPH_REGAMMA_MODE);
+		break;
+	case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24:
+		set_reg_field_value(
+			value,
+			1,
+			REGAMMA_CONTROL,
+			GRPH_REGAMMA_MODE);
+		break;
+	case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22:
+		set_reg_field_value(
+			value,
+			2,
+			REGAMMA_CONTROL,
+			GRPH_REGAMMA_MODE);
+		break;
+	case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A:
+		set_reg_field_value(
+			value,
+			3,
+			REGAMMA_CONTROL,
+			GRPH_REGAMMA_MODE);
+		break;
+	case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B:
+		set_reg_field_value(
+			value,
+			4,
+			REGAMMA_CONTROL,
+			GRPH_REGAMMA_MODE);
+		break;
+	default:
+		break;
+	}
+
+	dm_write_reg(opp110->base.ctx, addr, value);
+}
+
+bool dce110_opp_set_regamma(
+	struct output_pixel_processor *opp,
+	const struct gamma_ramp *ramp,
+	const struct gamma_parameters *params,
+	bool force_bypass)
+{
+	struct dce110_opp *opp110 = TO_DCE110_OPP(opp);
+
+	if (force_bypass) {
+		configure_regamma_mode(opp110, params, true);
+	} else {
+		/* 1. Scale gamma to 0 - 1 to m_pRgbUser */
+		if (!scale_gamma(opp110, ramp, params)) {
+			ASSERT_CRITICAL(false);
+			/* invalid option */
+			return false;
+		}
+
+		/* 2. Configure regamma curve without analysis (future task) */
+		/*    and program the PWL regions and segments */
+		if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_SW ||
+			params->surface_pixel_format == PIXEL_FORMAT_FP16) {
+
+			/* 3. Setup x exponentially distributed points */
+			if (!setup_distribution_points(opp110)) {
+				ASSERT_CRITICAL(false);
+				/* invalid option */
+				return false;
+			}
+
+			/* 4. Build ideal regamma curve */
+			if (!build_regamma_curve(opp110, params)) {
+				ASSERT_CRITICAL(false);
+				/* invalid parameters or bug */
+				return false;
+			}
+
+			/* 5. Map user gamma (evenly distributed x points) to
+			 * new curve when x is y from ideal regamma , step 5 */
+			if (!map_regamma_hw_to_x_user(
+				opp110, ramp, params)) {
+				ASSERT_CRITICAL(false);
+				/* invalid parameters or bug */
+				return false;
+			}
+
+			/* 6.Build and verify resulted curve */
+			build_new_custom_resulted_curve(opp110, params);
+
+			/* 7. Build and translate x to hw format */
+			if (!rebuild_curve_configuration_magic(opp110)) {
+				ASSERT_CRITICAL(false);
+				/* invalid parameters or bug */
+				return false;
+			}
+
+			/* 8. convert all params to the custom float format */
+			if (!convert_to_custom_float(opp110)) {
+				ASSERT_CRITICAL(false);
+				/* invalid parameters or bug */
+				return false;
+			}
+
+			/* 9. program regamma curve configuration */
+			regamma_config_regions_and_segments(opp110);
+
+			/* 10. Program PWL */
+			program_pwl(opp110, params);
+		}
+
+		/*
+		 * 11. program regamma config
+		 */
+		configure_regamma_mode(opp110, params, false);
+	}
+	return true;
+}
-- 
2.1.4



More information about the dri-devel mailing list