summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarry Wentland <harry.wentland@amd.com>2016-02-04 12:06:22 -0500
committerAlex Deucher <alexander.deucher@amd.com>2016-03-09 14:40:23 -0500
commit222c17a2e2ccf992e31d46dbf58eaa20ba172691 (patch)
tree68d3cf85d526e6ffb90c18c5a6ed9a099c1a97d1
parente7a3d74025d2f247f0f8ee552056c0faa7a919bf (diff)
drm/amd/dal: Add output pixel processing HW programming
Adds programming for color space conversion (CSC), regamma, and formatter. Signed-off-by: Harry Wentland <harry.wentland@amd.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c272
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h143
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c904
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c610
-rw-r--r--drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c2474
5 files changed, 4403 insertions, 0 deletions
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;
+}