Adds programming for color space conversion (CSC), regamma, and formatter. Signed-off-by: Harry Wentland <harry.wentland@xxxxxxx> Reviewed-by: Alex Deucher <alexander.deucher@xxxxxxx> --- drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c | 272 +++ drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h | 143 ++ drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c | 904 +++++++ .../drm/amd/dal/dc/dce110/dce110_opp_formatter.c | 610 +++++ .../gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c | 2474 ++++++++++++++++++++ 5 files changed, 4403 insertions(+) create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_formatter.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_regamma.c diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c new file mode 100644 index 000000000000..acb405e7b2e7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.c @@ -0,0 +1,272 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_opp.h" + +#include "gamma_types.h" + +enum { + MAX_LUT_ENTRY = 256, + MAX_NUMBER_OF_ENTRIES = 256 +}; + +static void build_evenly_distributed_points( + struct gamma_pixel *points, + uint32_t numberof_points, + struct fixed31_32 max_value, + struct fixed31_32 divider1, + struct fixed31_32 divider2, + struct fixed31_32 divider3) +{ + struct gamma_pixel *p = points; + struct gamma_pixel *p_last = p + numberof_points - 1; + + uint32_t i = 0; + + do { + struct fixed31_32 value = dal_fixed31_32_div_int( + dal_fixed31_32_mul_int(max_value, i), + numberof_points - 1); + + p->r = value; + p->g = value; + p->b = value; + + ++p; + ++i; + } while (i != numberof_points); + + p->r = dal_fixed31_32_div(p_last->r, divider1); + p->g = dal_fixed31_32_div(p_last->g, divider1); + p->b = dal_fixed31_32_div(p_last->b, divider1); + + ++p; + + p->r = dal_fixed31_32_div(p_last->r, divider2); + p->g = dal_fixed31_32_div(p_last->g, divider2); + p->b = dal_fixed31_32_div(p_last->b, divider2); + + ++p; + + p->r = dal_fixed31_32_div(p_last->r, divider3); + p->g = dal_fixed31_32_div(p_last->g, divider3); + p->b = dal_fixed31_32_div(p_last->b, divider3); +} + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +struct opp_funcs funcs = { + .opp_map_legacy_and_regamma_hw_to_x_user = dce110_opp_map_legacy_and_regamma_hw_to_x_user, + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut, + .opp_program_bit_depth_reduction = dce110_opp_program_bit_depth_reduction, + .opp_program_clamping_and_pixel_encoding = dce110_opp_program_clamping_and_pixel_encoding, + .opp_set_csc_adjustment = dce110_opp_set_csc_adjustment, + .opp_set_csc_default = dce110_opp_set_csc_default, + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_set_regamma = dce110_opp_set_regamma +}; + +bool dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_opp_reg_offsets *offsets) +{ + + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; + + opp110->base.inst = inst; + + opp110->offsets = *offsets; + + opp110->regamma.hw_points_num = 128; + opp110->regamma.coordinates_x = NULL; + opp110->regamma.rgb_resulted = NULL; + opp110->regamma.rgb_regamma = NULL; + opp110->regamma.coeff128 = NULL; + opp110->regamma.coeff128_oem = NULL; + opp110->regamma.coeff128_dx = NULL; + opp110->regamma.axis_x_256 = NULL; + opp110->regamma.axis_x_1025 = NULL; + opp110->regamma.rgb_oem = NULL; + opp110->regamma.rgb_user = NULL; + opp110->regamma.extra_points = 3; + opp110->regamma.use_half_points = false; + opp110->regamma.x_max1 = dal_fixed31_32_one; + opp110->regamma.x_max2 = dal_fixed31_32_from_int(2); + opp110->regamma.x_min = dal_fixed31_32_zero; + opp110->regamma.divider1 = dal_fixed31_32_from_fraction(3, 2); + opp110->regamma.divider2 = dal_fixed31_32_from_int(2); + opp110->regamma.divider3 = dal_fixed31_32_from_fraction(5, 2); + + opp110->regamma.rgb_user = dm_alloc( + ctx, + sizeof(struct pwl_float_data) * + (DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points)); + if (!opp110->regamma.rgb_user) + goto failure_1; + + opp110->regamma.rgb_oem = dm_alloc( + ctx, + sizeof(struct pwl_float_data) * + (DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points)); + if (!opp110->regamma.rgb_oem) + goto failure_2; + + opp110->regamma.rgb_resulted = dm_alloc( + ctx, + sizeof(struct pwl_result_data) * + (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); + if (!opp110->regamma.rgb_resulted) + goto failure_3; + + opp110->regamma.rgb_regamma = dm_alloc( + ctx, + sizeof(struct pwl_float_data_ex) * + (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); + if (!opp110->regamma.rgb_regamma) + goto failure_4; + + opp110->regamma.coordinates_x = dm_alloc( + ctx, + sizeof(struct hw_x_point) * + (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); + if (!opp110->regamma.coordinates_x) + goto failure_5; + + opp110->regamma.axis_x_256 = dm_alloc( + ctx, + sizeof(struct gamma_pixel) * + (MAX_LUT_ENTRY + opp110->regamma.extra_points)); + if (!opp110->regamma.axis_x_256) + goto failure_6; + + opp110->regamma.axis_x_1025 = dm_alloc( + ctx, + sizeof(struct gamma_pixel) * + (DX_GAMMA_RAMP_MAX + opp110->regamma.extra_points)); + if (!opp110->regamma.axis_x_1025) + goto failure_7; + + opp110->regamma.coeff128 = dm_alloc( + ctx, + sizeof(struct pixel_gamma_point) * + (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); + if (!opp110->regamma.coeff128) + goto failure_8; + + opp110->regamma.coeff128_oem = dm_alloc( + ctx, + sizeof(struct pixel_gamma_point) * + (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); + if (!opp110->regamma.coeff128_oem) + goto failure_9; + + opp110->regamma.coeff128_dx = dm_alloc( + ctx, + sizeof(struct pixel_gamma_point) * + (MAX_NUMBER_OF_ENTRIES + opp110->regamma.extra_points)); + if (!opp110->regamma.coeff128_dx) + goto failure_10; + + /* init palette */ + { + uint32_t i = 0; + + do { + opp110->regamma.saved_palette[i].red = (uint8_t)i; + opp110->regamma.saved_palette[i].green = (uint8_t)i; + opp110->regamma.saved_palette[i].blue = (uint8_t)i; + + ++i; + } while (i != MAX_LUT_ENTRY); + } + + build_evenly_distributed_points( + opp110->regamma.axis_x_256, + MAX_LUT_ENTRY, + opp110->regamma.x_max1, + opp110->regamma.divider1, + opp110->regamma.divider2, + opp110->regamma.divider3); + + build_evenly_distributed_points( + opp110->regamma.axis_x_1025, + DX_GAMMA_RAMP_MAX, + opp110->regamma.x_max1, + opp110->regamma.divider1, + opp110->regamma.divider2, + opp110->regamma.divider3); + + return true; + +failure_10: + dm_free(ctx, opp110->regamma.coeff128_oem); +failure_9: + dm_free(ctx, opp110->regamma.coeff128); +failure_8: + dm_free(ctx, opp110->regamma.axis_x_1025); +failure_7: + dm_free(ctx, opp110->regamma.axis_x_256); +failure_6: + dm_free(ctx, opp110->regamma.coordinates_x); +failure_5: + dm_free(ctx, opp110->regamma.rgb_regamma); +failure_4: + dm_free(ctx, opp110->regamma.rgb_resulted); +failure_3: + dm_free(ctx, opp110->regamma.rgb_oem); +failure_2: + dm_free(ctx, opp110->regamma.rgb_user); +failure_1: + + return true; +} + +void dce110_opp_destroy(struct output_pixel_processor **opp) +{ + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coeff128_dx); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coeff128_oem); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coeff128); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.axis_x_1025); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.axis_x_256); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.coordinates_x); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_regamma); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_resulted); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_oem); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)->regamma.rgb_user); + dm_free((*opp)->ctx, FROM_DCE11_OPP(*opp)); + *opp = NULL; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h new file mode 100644 index 000000000000..e53eb7413948 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp.h @@ -0,0 +1,143 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCE110_H__ +#define __DC_OPP_DCE110_H__ + +#include "dc_types.h" +#include "inc/opp.h" +#include "gamma_types.h" + +struct gamma_parameters; + +#define FROM_DCE11_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +enum dce110_opp_reg_type { + DCE110_OPP_REG_DCP = 0, + DCE110_OPP_REG_DCFE, + DCE110_OPP_REG_FMT, + + DCE110_OPP_REG_MAX +}; + +struct dce110_regamma { + struct gamma_curve arr_curve_points[16]; + struct curve_points arr_points[3]; + uint32_t hw_points_num; + struct hw_x_point *coordinates_x; + struct pwl_result_data *rgb_resulted; + + /* re-gamma curve */ + struct pwl_float_data_ex *rgb_regamma; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 256 */ + struct pixel_gamma_point *coeff128; + struct pixel_gamma_point *coeff128_oem; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 1025 */ + struct pixel_gamma_point *coeff128_dx; + /* evenly distributed points, gamma 256 software points 0-255 */ + struct gamma_pixel *axis_x_256; + /* evenly distributed points, gamma 1025 software points 0-1025 */ + struct gamma_pixel *axis_x_1025; + /* OEM supplied gamma for regamma LUT */ + struct pwl_float_data *rgb_oem; + /* user supplied gamma */ + struct pwl_float_data *rgb_user; + struct dev_c_lut saved_palette[RGB_256X3X16]; + uint32_t extra_points; + bool use_half_points; + struct fixed31_32 x_max1; + struct fixed31_32 x_max2; + struct fixed31_32 x_min; + struct fixed31_32 divider1; + struct fixed31_32 divider2; + struct fixed31_32 divider3; +}; + +/* OPP RELATED */ +#define TO_DCE110_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +struct dce110_opp_reg_offsets { + uint32_t fmt_offset; + uint32_t dcp_offset; + uint32_t dcfe_offset; +}; + +struct dce110_opp { + struct output_pixel_processor base; + struct dce110_opp_reg_offsets offsets; + struct dce110_regamma regamma; +}; + +bool dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_opp_reg_offsets *offsets); + +void dce110_opp_destroy(struct output_pixel_processor **opp); + +/* REGAMMA RELATED */ +void dce110_opp_power_on_regamma_lut( + struct output_pixel_processor *opp, + bool power_on); + +bool dce110_opp_set_regamma( + struct output_pixel_processor *opp, + const struct gamma_ramp *ramp, + const struct gamma_parameters *params, + bool force_bypass); + +bool dce110_opp_map_legacy_and_regamma_hw_to_x_user( + struct output_pixel_processor *opp, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params); + +void dce110_opp_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct grph_csc_adjustment *adjust); + +void dce110_opp_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust); + +/* FORMATTER RELATED */ +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params); + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params); + + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c new file mode 100644 index 000000000000..8f651e9329bc --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_opp_csc.c @@ -0,0 +1,904 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dce110_opp.h" +#include "basics/conversion.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define DCP_REG(reg)\ + (reg + opp110->offsets.dcp_offset) + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + +struct out_csc_color_matrix { + enum color_space color_space; + uint16_t regval[OUTPUT_CSC_MATRIX_SIZE]; +}; + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB_FULL_RANGE, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED_RANGE, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* YOnly same as YCbCr709 but Y in Full range -To do. */ +{ COLOR_SPACE_YCBCR601_YONLY, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_YONLY, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +static void program_color_matrix( + struct dce110_opp *opp110, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + struct dc_context *ctx = opp110->base.ctx; + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C11_C12); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C11); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C12); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C13_C14); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C13); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C14); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C21_C22); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C21); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C22); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C23_C24); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C23); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C24); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C31_C32); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C31); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C32); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C33_C34); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C33); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C34); + + dm_write_reg(ctx, addr, value); + } +} + +/* + * initialize_color_float_adj_reference_values + * This initialize display color adjust input from API to HW range for later + * calculation use. This is shared by all the display color adjustment. + * @param : + * @return None + */ +static void initialize_color_float_adj_reference_values( + const struct grph_csc_adjustment *adjust, + struct fixed31_32 *grph_cont, + struct fixed31_32 *grph_sat, + struct fixed31_32 *grph_bright, + struct fixed31_32 *sin_grph_hue, + struct fixed31_32 *cos_grph_hue) +{ + /* Hue adjustment could be negative. -45 ~ +45 */ + struct fixed31_32 hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction(adjust->grph_hue, 180), + dal_fixed31_32_pi); + + *sin_grph_hue = dal_fixed31_32_sin(hue); + *cos_grph_hue = dal_fixed31_32_cos(hue); + + if (adjust->adjust_divider) { + *grph_cont = + dal_fixed31_32_from_fraction( + adjust->grph_cont, + adjust->adjust_divider); + *grph_sat = + dal_fixed31_32_from_fraction( + adjust->grph_sat, + adjust->adjust_divider); + *grph_bright = + dal_fixed31_32_from_fraction( + adjust->grph_bright, + adjust->adjust_divider); + } else { + *grph_cont = dal_fixed31_32_from_int(adjust->grph_cont); + *grph_sat = dal_fixed31_32_from_int(adjust->grph_sat); + *grph_bright = dal_fixed31_32_from_int(adjust->grph_bright); + } +} + +static inline struct fixed31_32 fixed31_32_clamp( + struct fixed31_32 value, + int32_t min_numerator, + int32_t max_numerator, + int32_t denominator) +{ + return dal_fixed31_32_clamp( + value, + dal_fixed31_32_from_fraction( + min_numerator, + denominator), + dal_fixed31_32_from_fraction( + max_numerator, + denominator)); +} + +static void setup_reg_format( + struct fixed31_32 *coefficients, + uint16_t *reg_values) +{ + enum { + LENGTH = 12, + DENOMINATOR = 10000 + }; + + static const int32_t min_numerator[] = { + -3 * DENOMINATOR, + -DENOMINATOR + }; + + static const int32_t max_numerator[] = { + DENOMINATOR, + DENOMINATOR + }; + + static const uint8_t integer_bits[] = { 2, 0 }; + + uint32_t i = 0; + + do { + const uint32_t index = (i % 4) == 3; + + reg_values[i] = fixed_point_to_int_frac( + fixed31_32_clamp(coefficients[(i + 8) % LENGTH], + min_numerator[index], + max_numerator[index], + DENOMINATOR), + integer_bits[index], 13); + + ++i; + } while (i != LENGTH); +} + +/** + ***************************************************************************** + * Function: setup_adjustments + * @note prepare to setup the values + * + * @see + * + ***************************************************************************** + */ +static void setup_adjustments(const struct grph_csc_adjustment *adjust, + struct dc_csc_adjustments *adjustments) +{ + if (adjust->adjust_divider != 0) { + adjustments->brightness = + dal_fixed31_32_from_fraction(adjust->grph_bright, + adjust->adjust_divider); + adjustments->contrast = + dal_fixed31_32_from_fraction(adjust->grph_cont, + adjust->adjust_divider); + adjustments->saturation = + dal_fixed31_32_from_fraction(adjust->grph_sat, + adjust->adjust_divider); + } else { + adjustments->brightness = + dal_fixed31_32_from_fraction(adjust->grph_bright, 1); + adjustments->contrast = + dal_fixed31_32_from_fraction(adjust->grph_cont, 1); + adjustments->saturation = + dal_fixed31_32_from_fraction(adjust->grph_sat, 1); + } + + /* convert degrees into radians */ + adjustments->hue = + dal_fixed31_32_mul( + dal_fixed31_32_from_fraction(adjust->grph_hue, 180), + dal_fixed31_32_pi); +} + +static void prepare_tv_rgb_ideal( + struct fixed31_32 *matrix) +{ + static const int32_t matrix_[] = { + 85546875, 0, 0, 6250000, + 0, 85546875, 0, 6250000, + 0, 0, 85546875, 6250000 + }; + + uint32_t i = 0; + + do { + matrix[i] = dal_fixed31_32_from_fraction( + matrix_[i], + 100000000); + ++i; + } while (i != ARRAY_SIZE(matrix_)); +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_rgb_adjustment_legacy + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB color space + * + * @see + * + ***************************************************************************** + */ +static void set_rgb_adjustment_legacy( + struct dce110_opp *opp110, + const struct grph_csc_adjustment *adjust) +{ + const struct fixed31_32 k1 = + dal_fixed31_32_from_fraction(701000, 1000000); + const struct fixed31_32 k2 = + dal_fixed31_32_from_fraction(236568, 1000000); + const struct fixed31_32 k3 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k4 = + dal_fixed31_32_from_fraction(464432, 1000000); + const struct fixed31_32 k5 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k6 = + dal_fixed31_32_from_fraction(-701000, 1000000); + const struct fixed31_32 k7 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k8 = + dal_fixed31_32_from_fraction(-292569, 1000000); + const struct fixed31_32 k9 = + dal_fixed31_32_from_fraction(413000, 1000000); + const struct fixed31_32 k10 = + dal_fixed31_32_from_fraction(-92482, 1000000); + const struct fixed31_32 k11 = + dal_fixed31_32_from_fraction(-114000, 1000000); + const struct fixed31_32 k12 = + dal_fixed31_32_from_fraction(385051, 1000000); + const struct fixed31_32 k13 = + dal_fixed31_32_from_fraction(-299000, 1000000); + const struct fixed31_32 k14 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k15 = + dal_fixed31_32_from_fraction(-587000, 1000000); + const struct fixed31_32 k16 = + dal_fixed31_32_from_fraction(-741914, 1000000); + const struct fixed31_32 k17 = + dal_fixed31_32_from_fraction(886000, 1000000); + const struct fixed31_32 k18 = + dal_fixed31_32_from_fraction(-144086, 1000000); + + const struct fixed31_32 luma_r = + dal_fixed31_32_from_fraction(299, 1000); + const struct fixed31_32 luma_g = + dal_fixed31_32_from_fraction(587, 1000); + const struct fixed31_32 luma_b = + dal_fixed31_32_from_fraction(114, 1000); + + struct out_csc_color_matrix tbl_entry; + struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; + + struct fixed31_32 grph_cont; + struct fixed31_32 grph_sat; + struct fixed31_32 grph_bright; + struct fixed31_32 sin_grph_hue; + struct fixed31_32 cos_grph_hue; + + initialize_color_float_adj_reference_values( + adjust, &grph_cont, &grph_sat, + &grph_bright, &sin_grph_hue, &cos_grph_hue); + + /* COEF_1_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + + * Sin(GrphHue) * K2)) */ + /* (Cos(GrphHue) * K1 + Sin(GrphHue) * K2) */ + matrix[0] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k1), + dal_fixed31_32_mul(sin_grph_hue, k2)); + /* GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2 */ + matrix[0] = dal_fixed31_32_mul(grph_sat, matrix[0]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * K2)) */ + matrix[0] = dal_fixed31_32_add(luma_r, matrix[0]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K1 + Sin(GrphHue) * + * K2)) */ + matrix[0] = dal_fixed31_32_mul(grph_cont, matrix[0]); + + /* COEF_1_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + + * Sin(GrphHue) * K4)) */ + /* (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ + matrix[1] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k3), + dal_fixed31_32_mul(sin_grph_hue, k4)); + /* GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4) */ + matrix[1] = dal_fixed31_32_mul(grph_sat, matrix[1]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * K4)) */ + matrix[1] = dal_fixed31_32_add(luma_g, matrix[1]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K3 + Sin(GrphHue) * + * K4)) */ + matrix[1] = dal_fixed31_32_mul(grph_cont, matrix[1]); + + /* COEF_1_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + + * Sin(GrphHue) * K6)) */ + /* (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k5), + dal_fixed31_32_mul(sin_grph_hue, k6)); + /* GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = dal_fixed31_32_mul(grph_sat, matrix[2]); + /* LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * K6) */ + matrix[2] = dal_fixed31_32_add(luma_b, matrix[2]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K5 + Sin(GrphHue) * + * K6)) */ + matrix[2] = dal_fixed31_32_mul(grph_cont, matrix[2]); + + /* COEF_1_4 = GrphBright */ + matrix[3] = grph_bright; + + /* COEF_2_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + + * Sin(GrphHue) * K8)) */ + /* (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ + matrix[4] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k7), + dal_fixed31_32_mul(sin_grph_hue, k8)); + /* GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8) */ + matrix[4] = dal_fixed31_32_mul(grph_sat, matrix[4]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * K8)) */ + matrix[4] = dal_fixed31_32_add(luma_r, matrix[4]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K7 + Sin(GrphHue) * + * K8)) */ + matrix[4] = dal_fixed31_32_mul(grph_cont, matrix[4]); + + /* COEF_2_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + + * Sin(GrphHue) * K10)) */ + /* (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k9), + dal_fixed31_32_mul(sin_grph_hue, k10)); + /* GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = dal_fixed31_32_mul(grph_sat, matrix[5]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * K10)) */ + matrix[5] = dal_fixed31_32_add(luma_g, matrix[5]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K9 + Sin(GrphHue) * + * K10)) */ + matrix[5] = dal_fixed31_32_mul(grph_cont, matrix[5]); + + /* COEF_2_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + + * Sin(GrphHue) * K12)) */ + /* (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k11), + dal_fixed31_32_mul(sin_grph_hue, k12)); + /* GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = dal_fixed31_32_mul(grph_sat, matrix[6]); + /* (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * K12)) */ + matrix[6] = dal_fixed31_32_add(luma_b, matrix[6]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K11 + Sin(GrphHue) * + * K12)) */ + matrix[6] = dal_fixed31_32_mul(grph_cont, matrix[6]); + + /* COEF_2_4 = GrphBright */ + matrix[7] = grph_bright; + + /* COEF_3_1 = GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + + * Sin(GrphHue) * K14)) */ + /* (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k13), + dal_fixed31_32_mul(sin_grph_hue, k14)); + /* GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = dal_fixed31_32_mul(grph_sat, matrix[8]); + /* (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * K14)) */ + matrix[8] = dal_fixed31_32_add(luma_r, matrix[8]); + /* GrphCont * (LumaR + GrphSat * (Cos(GrphHue) * K13 + Sin(GrphHue) * + * K14)) */ + matrix[8] = dal_fixed31_32_mul(grph_cont, matrix[8]); + + /* COEF_3_2 = GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + + * Sin(GrphHue) * K16)) */ + /* GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16) */ + matrix[9] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k15), + dal_fixed31_32_mul(sin_grph_hue, k16)); + /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ + matrix[9] = dal_fixed31_32_mul(grph_sat, matrix[9]); + /* (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * K16)) */ + matrix[9] = dal_fixed31_32_add(luma_g, matrix[9]); + /* GrphCont * (LumaG + GrphSat * (Cos(GrphHue) * K15 + Sin(GrphHue) * + * K16)) */ + matrix[9] = dal_fixed31_32_mul(grph_cont, matrix[9]); + + /* COEF_3_3 = GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + + * Sin(GrphHue) * K18)) */ + /* (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = + dal_fixed31_32_add( + dal_fixed31_32_mul(cos_grph_hue, k17), + dal_fixed31_32_mul(sin_grph_hue, k18)); + /* GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = dal_fixed31_32_mul(grph_sat, matrix[10]); + /* (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * K18)) */ + matrix[10] = dal_fixed31_32_add(luma_b, matrix[10]); + /* GrphCont * (LumaB + GrphSat * (Cos(GrphHue) * K17 + Sin(GrphHue) * + * K18)) */ + matrix[10] = dal_fixed31_32_mul(grph_cont, matrix[10]); + + /* COEF_3_4 = GrphBright */ + matrix[11] = grph_bright; + + tbl_entry.color_space = adjust->c_space; + + convert_float_matrix(tbl_entry.regval, matrix, OUTPUT_CSC_MATRIX_SIZE); + + program_color_matrix( + opp110, &tbl_entry, adjust->color_adjust_option); +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_rgb_limited_range_adjustment + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and program color adjustments for sRGB limited color space + * + * @see + * + ***************************************************************************** + */ +static void set_rgb_limited_range_adjustment( + struct dce110_opp *opp110, + const struct grph_csc_adjustment *adjust) +{ + struct out_csc_color_matrix reg_matrix; + struct fixed31_32 change_matrix[OUTPUT_CSC_MATRIX_SIZE]; + struct fixed31_32 matrix[OUTPUT_CSC_MATRIX_SIZE]; + struct dc_csc_adjustments adjustments; + struct fixed31_32 ideals[OUTPUT_CSC_MATRIX_SIZE]; + + prepare_tv_rgb_ideal(ideals); + + setup_adjustments(adjust, &adjustments); + + calculate_adjustments(ideals, &adjustments, matrix); + + dm_memmove(change_matrix, matrix, sizeof(matrix)); + + /* from 1 -> 3 */ + matrix[8] = change_matrix[0]; + matrix[9] = change_matrix[1]; + matrix[10] = change_matrix[2]; + matrix[11] = change_matrix[3]; + + /* from 2 -> 1 */ + matrix[0] = change_matrix[4]; + matrix[1] = change_matrix[5]; + matrix[2] = change_matrix[6]; + matrix[3] = change_matrix[7]; + + /* from 3 -> 2 */ + matrix[4] = change_matrix[8]; + matrix[5] = change_matrix[9]; + matrix[6] = change_matrix[10]; + matrix[7] = change_matrix[11]; + + dm_memset(®_matrix, 0, sizeof(struct out_csc_color_matrix)); + + setup_reg_format(matrix, reg_matrix.regval); + + program_color_matrix(opp110, ®_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(®_matrix, 0, sizeof(struct out_csc_color_matrix)); + + setup_reg_format(matrix, reg_matrix.regval); + + program_color_matrix(opp110, ®_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, ¶ms->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, ¶ms->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( + ¶ms->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, ¶ms->regamma.regamma_ramp); + + if (!build_oem_custom_gamma_mapping_coefficients( + opp110, CHANNEL_NAME_RED, + opp110->regamma.hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_oem_custom_gamma_mapping_coefficients( + opp110, CHANNEL_NAME_GREEN, + opp110->regamma.hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!build_oem_custom_gamma_mapping_coefficients( + opp110, CHANNEL_NAME_BLUE, + opp110->regamma.hw_points_num, + params->surface_pixel_format)) { + BREAK_TO_DEBUGGER(); + return false; + } + + i = 0; + + while (i != opp110->regamma.hw_points_num + 1) { + rgb->r = calculate_oem_mapped_value( + opp110, i, CHANNEL_NAME_RED, max_entries); + rgb->g = calculate_oem_mapped_value( + opp110, i, CHANNEL_NAME_GREEN, max_entries); + rgb->b = calculate_oem_mapped_value( + opp110, i, CHANNEL_NAME_BLUE, max_entries); + ++rgb; + ++i; + } + } + + return true; +} + +static void build_new_custom_resulted_curve( + struct dce110_opp *opp110, + const struct gamma_parameters *params) +{ + struct pwl_result_data *rgb = opp110->regamma.rgb_resulted; + struct pwl_result_data *rgb_plus_1 = rgb + 1; + + uint32_t i; + + i = 0; + + while (i != opp110->regamma.hw_points_num + 1) { + rgb->red = dal_fixed31_32_clamp( + rgb->red, opp110->regamma.x_min, + opp110->regamma.x_max1); + rgb->green = dal_fixed31_32_clamp( + rgb->green, opp110->regamma.x_min, + opp110->regamma.x_max1); + rgb->blue = dal_fixed31_32_clamp( + rgb->blue, opp110->regamma.x_min, + opp110->regamma.x_max1); + + ++rgb; + ++i; + } + + rgb = opp110->regamma.rgb_resulted; + + i = 1; + + while (i != opp110->regamma.hw_points_num + 1) { + if (dal_fixed31_32_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = rgb->red; + if (dal_fixed31_32_lt(rgb_plus_1->green, rgb->green)) + rgb_plus_1->green = rgb->green; + if (dal_fixed31_32_lt(rgb_plus_1->blue, rgb->blue)) + rgb_plus_1->blue = rgb->blue; + + rgb->delta_red = dal_fixed31_32_sub( + rgb_plus_1->red, + rgb->red); + rgb->delta_green = dal_fixed31_32_sub( + rgb_plus_1->green, + rgb->green); + rgb->delta_blue = dal_fixed31_32_sub( + rgb_plus_1->blue, + rgb->blue); + + ++rgb_plus_1; + ++rgb; + ++i; + } +} + +static bool rebuild_curve_configuration_magic( + struct dce110_opp *opp110) +{ + const struct fixed31_32 magic_number = + dal_fixed31_32_from_fraction(249, 1000); + + struct fixed31_32 y_r; + struct fixed31_32 y_g; + struct fixed31_32 y_b; + + struct fixed31_32 y1_min; + struct fixed31_32 y2_max; + struct fixed31_32 y3_max; + + y_r = opp110->regamma.rgb_resulted[0].red; + y_g = opp110->regamma.rgb_resulted[0].green; + y_b = opp110->regamma.rgb_resulted[0].blue; + + y1_min = dal_fixed31_32_min(y_r, dal_fixed31_32_min(y_g, y_b)); + + opp110->regamma.arr_points[0].x = + opp110->regamma.coordinates_x[0].adjusted_x; + opp110->regamma.arr_points[0].y = y1_min; + opp110->regamma.arr_points[0].slope = dal_fixed31_32_div( + opp110->regamma.arr_points[0].y, + opp110->regamma.arr_points[0].x); + + opp110->regamma.arr_points[1].x = dal_fixed31_32_add( + opp110->regamma.coordinates_x + [opp110->regamma.hw_points_num - 1].adjusted_x, + magic_number); + + opp110->regamma.arr_points[2].x = + opp110->regamma.arr_points[1].x; + + y_r = opp110->regamma.rgb_resulted + [opp110->regamma.hw_points_num - 1].red; + y_g = opp110->regamma.rgb_resulted + [opp110->regamma.hw_points_num - 1].green; + y_b = opp110->regamma.rgb_resulted + [opp110->regamma.hw_points_num - 1].blue; + + y2_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); + + opp110->regamma.arr_points[1].y = y2_max; + + y_r = opp110->regamma.rgb_resulted + [opp110->regamma.hw_points_num].red; + y_g = opp110->regamma.rgb_resulted + [opp110->regamma.hw_points_num].green; + y_b = opp110->regamma.rgb_resulted + [opp110->regamma.hw_points_num].blue; + + y3_max = dal_fixed31_32_max(y_r, dal_fixed31_32_max(y_g, y_b)); + + opp110->regamma.arr_points[2].y = y3_max; + + opp110->regamma.arr_points[2].slope = dal_fixed31_32_one; + + return true; +} + +static bool build_custom_float( + struct fixed31_32 value, + const struct custom_float_format *format, + bool *negative, + uint32_t *mantissa, + uint32_t *exponenta) +{ + uint32_t exp_offset = (1 << (format->exponenta_bits - 1)) - 1; + + const struct fixed31_32 mantissa_constant_plus_max_fraction = + dal_fixed31_32_from_fraction( + (1LL << (format->mantissa_bits + 1)) - 1, + 1LL << format->mantissa_bits); + + struct fixed31_32 mantiss; + + if (dal_fixed31_32_eq( + value, + dal_fixed31_32_zero)) { + *negative = false; + *mantissa = 0; + *exponenta = 0; + return true; + } + + if (dal_fixed31_32_lt( + value, + dal_fixed31_32_zero)) { + *negative = format->sign; + value = dal_fixed31_32_neg(value); + } else { + *negative = false; + } + + if (dal_fixed31_32_lt( + value, + dal_fixed31_32_one)) { + uint32_t i = 1; + + do { + value = dal_fixed31_32_shl(value, 1); + ++i; + } while (dal_fixed31_32_lt( + value, + dal_fixed31_32_one)); + + --i; + + if (exp_offset <= i) { + *mantissa = 0; + *exponenta = 0; + return true; + } + + *exponenta = exp_offset - i; + } else if (dal_fixed31_32_le( + mantissa_constant_plus_max_fraction, + value)) { + uint32_t i = 1; + + do { + value = dal_fixed31_32_shr(value, 1); + ++i; + } while (dal_fixed31_32_lt( + mantissa_constant_plus_max_fraction, + value)); + + *exponenta = exp_offset + i - 1; + } else { + *exponenta = exp_offset; + } + + mantiss = dal_fixed31_32_sub( + value, + dal_fixed31_32_one); + + if (dal_fixed31_32_lt( + mantiss, + dal_fixed31_32_zero) || + dal_fixed31_32_lt( + dal_fixed31_32_one, + mantiss)) + mantiss = dal_fixed31_32_zero; + else + mantiss = dal_fixed31_32_shl( + mantiss, + format->mantissa_bits); + + *mantissa = dal_fixed31_32_floor(mantiss); + + return true; +} + +static bool setup_custom_float( + const struct custom_float_format *format, + bool negative, + uint32_t mantissa, + uint32_t exponenta, + uint32_t *result) +{ + uint32_t i = 0; + uint32_t j = 0; + + uint32_t value = 0; + + /* verification code: + * once calculation is ok we can remove it */ + + const uint32_t mantissa_mask = + (1 << (format->mantissa_bits + 1)) - 1; + + const uint32_t exponenta_mask = + (1 << (format->exponenta_bits + 1)) - 1; + + if (mantissa & ~mantissa_mask) { + BREAK_TO_DEBUGGER(); + mantissa = mantissa_mask; + } + + if (exponenta & ~exponenta_mask) { + BREAK_TO_DEBUGGER(); + exponenta = exponenta_mask; + } + + /* end of verification code */ + + while (i < format->mantissa_bits) { + uint32_t mask = 1 << i; + + if (mantissa & mask) + value |= mask; + + ++i; + } + + while (j < format->exponenta_bits) { + uint32_t mask = 1 << j; + + if (exponenta & mask) + value |= mask << i; + + ++j; + } + + if (negative && format->sign) + value |= 1 << (i + j); + + *result = value; + + return true; +} + +static bool convert_to_custom_float_format( + struct fixed31_32 value, + const struct custom_float_format *format, + uint32_t *result) +{ + uint32_t mantissa; + uint32_t exponenta; + bool negative; + + return build_custom_float( + value, format, &negative, &mantissa, &exponenta) && + setup_custom_float( + format, negative, mantissa, exponenta, result); +} + +static bool convert_to_custom_float_format_ex( + struct fixed31_32 value, + const struct custom_float_format *format, + struct custom_float_value *result) +{ + return build_custom_float( + value, format, + &result->negative, &result->mantissa, &result->exponenta) && + setup_custom_float( + format, result->negative, result->mantissa, result->exponenta, + &result->value); +} + +static bool convert_to_custom_float( + struct dce110_opp *opp110) +{ + struct custom_float_format fmt; + + struct pwl_result_data *rgb = opp110->regamma.rgb_resulted; + + uint32_t i = 0; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = true; + + if (!convert_to_custom_float_format( + opp110->regamma.arr_points[0].x, + &fmt, + &opp110->regamma.arr_points[0].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + opp110->regamma.arr_points[0].offset, + &fmt, + &opp110->regamma.arr_points[0].custom_float_offset)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + opp110->regamma.arr_points[0].slope, + &fmt, + &opp110->regamma.arr_points[0].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 10; + fmt.sign = false; + + if (!convert_to_custom_float_format( + opp110->regamma.arr_points[1].x, + &fmt, + &opp110->regamma.arr_points[1].custom_float_x)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + opp110->regamma.arr_points[1].y, + &fmt, + &opp110->regamma.arr_points[1].custom_float_y)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + opp110->regamma.arr_points[2].slope, + &fmt, + &opp110->regamma.arr_points[2].custom_float_slope)) { + BREAK_TO_DEBUGGER(); + return false; + } + + fmt.mantissa_bits = 12; + fmt.sign = true; + + while (i != opp110->regamma.hw_points_num) { + if (!convert_to_custom_float_format( + rgb->red, + &fmt, + &rgb->red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->green, + &fmt, + &rgb->green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->blue, + &fmt, + &rgb->blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_red, + &fmt, + &rgb->delta_red_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_green, + &fmt, + &rgb->delta_green_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (!convert_to_custom_float_format( + rgb->delta_blue, + &fmt, + &rgb->delta_blue_reg)) { + BREAK_TO_DEBUGGER(); + return false; + } + + ++rgb; + ++i; + } + + return true; +} + +static bool round_custom_float_6_12( + struct hw_x_point *x) +{ + struct custom_float_format fmt; + + struct custom_float_value value; + + fmt.exponenta_bits = 6; + fmt.mantissa_bits = 12; + fmt.sign = true; + + if (!convert_to_custom_float_format_ex( + x->x, &fmt, &value)) + return false; + + x->adjusted_x = x->x; + + if (value.mantissa) { + BREAK_TO_DEBUGGER(); + + return false; + } + + return true; +} + +static bool build_hw_curve_configuration( + const struct curve_config *curve_config, + struct gamma_curve *gamma_curve, + struct curve_points *curve_points, + struct hw_x_point *points, + uint32_t *number_of_points) +{ + const int8_t max_regions_number = ARRAY_SIZE(curve_config->segments); + + int8_t i; + + uint8_t segments_calculation[8] = { 0 }; + + struct fixed31_32 region1 = dal_fixed31_32_zero; + struct fixed31_32 region2; + struct fixed31_32 increment; + + uint32_t index = 0; + uint32_t segments = 0; + uint32_t max_number; + + bool result = false; + + if (!number_of_points) { + BREAK_TO_DEBUGGER(); + return false; + } + + max_number = *number_of_points; + + i = 0; + + while (i != max_regions_number) { + gamma_curve[i].offset = 0; + gamma_curve[i].segments_num = 0; + + ++i; + } + + i = 0; + + while (i != max_regions_number) { + /* number should go in uninterruptible sequence */ + if (curve_config->segments[i] == -1) + break; + + ASSERT(curve_config->segments[i] >= 0); + + segments += (1 << curve_config->segments[i]); + + ++i; + } + + if (segments > max_number) { + BREAK_TO_DEBUGGER(); + } else { + int32_t divisor; + uint32_t offset = 0; + int8_t begin = curve_config->begin; + int32_t region_number = 0; + + i = begin; + + while ((index < max_number) && + (region_number < max_regions_number) && + (i <= 1)) { + int32_t j = 0; + + segments = curve_config->segments[region_number]; + divisor = 1 << segments; + + if (segments == -1) { + if (i > 0) { + region1 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i - 1); + region2 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i); + } else { + region1 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -(i - 1)); + region2 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -i); + } + + break; + } + + if (i > -1) { + region1 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i); + region2 = dal_fixed31_32_shl( + dal_fixed31_32_one, + i + 1); + } else { + region1 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -i); + region2 = dal_fixed31_32_shr( + dal_fixed31_32_one, + -(i + 1)); + } + + gamma_curve[region_number].offset = offset; + gamma_curve[region_number].segments_num = segments; + + offset += divisor; + + ++segments_calculation[segments]; + + increment = dal_fixed31_32_div_int( + dal_fixed31_32_sub( + region2, + region1), + divisor); + + points[index].x = region1; + + round_custom_float_6_12(points + index); + + ++index; + ++region_number; + + while ((index < max_number) && (j < divisor - 1)) { + region1 = dal_fixed31_32_add( + region1, + increment); + + points[index].x = region1; + points[index].adjusted_x = region1; + + ++index; + ++j; + } + + ++i; + } + + points[index].x = region1; + + round_custom_float_6_12(points + index); + + *number_of_points = index; + + result = true; + } + + curve_points[0].x = points[0].adjusted_x; + curve_points[0].offset = dal_fixed31_32_zero; + + curve_points[1].x = points[index - 1].adjusted_x; + curve_points[1].offset = dal_fixed31_32_zero; + + curve_points[2].x = points[index].adjusted_x; + curve_points[2].offset = dal_fixed31_32_zero; + + return result; +} + +static bool setup_distribution_points( + struct dce110_opp *opp110) +{ + uint32_t hw_points_num = MAX_PWL_ENTRY * 2; + + struct curve_config cfg; + + cfg.offset = 0; + + cfg.segments[0] = 3; + cfg.segments[1] = 4; + cfg.segments[2] = 4; + cfg.segments[3] = 4; + cfg.segments[4] = 4; + cfg.segments[5] = 4; + cfg.segments[6] = 4; + cfg.segments[7] = 4; + cfg.segments[8] = 5; + cfg.segments[9] = 5; + cfg.segments[10] = 0; + cfg.segments[11] = -1; + cfg.segments[12] = -1; + cfg.segments[13] = -1; + cfg.segments[14] = -1; + cfg.segments[15] = -1; + + cfg.begin = -10; + + if (!build_hw_curve_configuration( + &cfg, opp110->regamma.arr_curve_points, + opp110->regamma.arr_points, + opp110->regamma.coordinates_x, &hw_points_num)) { + ASSERT_CRITICAL(false); + return false; + } + + opp110->regamma.hw_points_num = hw_points_num; + + return true; +} + + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static void regamma_config_regions_and_segments( + struct dce110_opp *opp110) +{ + struct gamma_curve *curve; + uint32_t value = 0; + + { + set_reg_field_value( + value, + opp110->regamma.arr_points[0].custom_float_x, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START); + + set_reg_field_value( + value, + 0, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START_SEGMENT); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_START_CNTL), + value); + } + { + value = 0; + set_reg_field_value( + value, + opp110->regamma.arr_points[0].custom_float_slope, + REGAMMA_CNTLA_SLOPE_CNTL, + REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_SLOPE_CNTL), value); + } + { + value = 0; + set_reg_field_value( + value, + opp110->regamma.arr_points[1].custom_float_x, + REGAMMA_CNTLA_END_CNTL1, + REGAMMA_CNTLA_EXP_REGION_END); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_END_CNTL1), value); + } + { + value = 0; + set_reg_field_value( + value, + opp110->regamma.arr_points[2].custom_float_slope, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_BASE); + + set_reg_field_value( + value, + opp110->regamma.arr_points[1].custom_float_y, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_SLOPE); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_END_CNTL2), value); + } + + curve = opp110->regamma.arr_curve_points; + + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS); + + dm_write_reg( + opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_0_1), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_2_3), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_4_5), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_6_7), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_8_9), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_10_11), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_12_13), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_14_15), + value); + } +} + +static void program_pwl( + struct dce110_opp *opp110, + const struct gamma_parameters *params) +{ + uint32_t value; + + { + uint8_t max_tries = 10; + uint8_t counter = 0; + + /* Power on LUT memory */ + value = dm_read_reg(opp110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_CTRL)); + + set_reg_field_value( + value, + 1, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dm_write_reg(opp110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); + + while (counter < max_tries) { + value = + dm_read_reg( + opp110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_STATUS)); + + if (get_reg_field_value( + value, + DCFE_MEM_PWR_STATUS, + DCP_REGAMMA_MEM_PWR_STATE) == 0) + break; + + ++counter; + } + + if (counter == max_tries) { + dal_logger_write(opp110->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: regamma lut was not powered on " + "in a timely manner," + " programming still proceeds\n", + __func__); + } + } + + value = 0; + + set_reg_field_value( + value, + 7, + REGAMMA_LUT_WRITE_EN_MASK, + REGAMMA_LUT_WRITE_EN_MASK); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_LUT_WRITE_EN_MASK), value); + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_LUT_INDEX), 0); + + /* Program REGAMMA_LUT_DATA */ + { + const uint32_t addr = DCP_REG(mmREGAMMA_LUT_DATA); + + uint32_t i = 0; + + struct pwl_result_data *rgb = + opp110->regamma.rgb_resulted; + + while (i != opp110->regamma.hw_points_num) { + dm_write_reg(opp110->base.ctx, addr, rgb->red_reg); + dm_write_reg(opp110->base.ctx, addr, rgb->green_reg); + dm_write_reg(opp110->base.ctx, addr, rgb->blue_reg); + + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_red_reg); + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_green_reg); + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + value = dm_read_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL)); + + set_reg_field_value( + value, + 0, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dm_write_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); +} + +void dce110_opp_power_on_regamma_lut( + struct output_pixel_processor *opp, + bool power_on) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + uint32_t value = + dm_read_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL)); + + set_reg_field_value( + value, + power_on, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + set_reg_field_value( + value, + power_on, + DCFE_MEM_PWR_CTRL, + DCP_LUT_MEM_PWR_DIS); + + dm_write_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); +} + +static bool scale_gamma( + struct dce110_opp *opp110, + const struct gamma_ramp *gamma_ramp, + const struct gamma_parameters *params) +{ + const struct gamma_ramp_rgb256x3x16 *gamma; + bool use_palette = params->surface_pixel_format == PIXEL_FORMAT_INDEX8; + + const uint16_t max_driver = 0xFFFF; + const uint16_t max_os = 0xFF00; + + uint16_t scaler = max_os; + + uint32_t i; + + struct dev_c_lut *palette = opp110->regamma.saved_palette; + + struct pwl_float_data *rgb = opp110->regamma.rgb_user; + struct pwl_float_data *rgb_last = rgb + RGB_256X3X16 - 1; + + if (gamma_ramp->type == GAMMA_RAMP_RBG256X3X16) + gamma = &gamma_ramp->gamma_ramp_rgb256x3x16; + else + return false; /* invalid option */ + + i = 0; + + do { + if ((gamma->red[i] > max_os) || + (gamma->green[i] > max_os) || + (gamma->blue[i] > max_os)) { + scaler = max_driver; + break; + } + ++i; + } while (i != RGB_256X3X16); + + i = 0; + + if (use_palette) + do { + rgb->r = dal_fixed31_32_from_fraction( + gamma->red[palette->red], scaler); + rgb->g = dal_fixed31_32_from_fraction( + gamma->green[palette->green], scaler); + rgb->b = dal_fixed31_32_from_fraction( + gamma->blue[palette->blue], scaler); + + ++palette; + ++rgb; + ++i; + } while (i != RGB_256X3X16); + else + do { + rgb->r = dal_fixed31_32_from_fraction( + gamma->red[i], scaler); + rgb->g = dal_fixed31_32_from_fraction( + gamma->green[i], scaler); + rgb->b = dal_fixed31_32_from_fraction( + gamma->blue[i], scaler); + + ++rgb; + ++i; + } while (i != RGB_256X3X16); + + rgb->r = dal_fixed31_32_mul(rgb_last->r, + opp110->regamma.divider1); + rgb->g = dal_fixed31_32_mul(rgb_last->g, + opp110->regamma.divider1); + rgb->b = dal_fixed31_32_mul(rgb_last->b, + opp110->regamma.divider1); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, + opp110->regamma.divider2); + rgb->g = dal_fixed31_32_mul(rgb_last->g, + opp110->regamma.divider2); + rgb->b = dal_fixed31_32_mul(rgb_last->b, + opp110->regamma.divider2); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, + opp110->regamma.divider3); + rgb->g = dal_fixed31_32_mul(rgb_last->g, + opp110->regamma.divider3); + rgb->b = dal_fixed31_32_mul(rgb_last->b, + opp110->regamma.divider3); + + return true; +} + + +static void configure_regamma_mode( + struct dce110_opp *opp110, + const struct gamma_parameters *params, + bool force_bypass) +{ + const uint32_t addr = DCP_REG(mmREGAMMA_CONTROL); + + enum wide_gamut_regamma_mode mode = + WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A; + + uint32_t value = dm_read_reg(opp110->base.ctx, addr); + + if (force_bypass) { + + set_reg_field_value( + value, + 0, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + + dm_write_reg(opp110->base.ctx, addr, value); + + return; + } + + if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_BYPASS) + mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS; + else if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_HW) { + if (params->surface_pixel_format == PIXEL_FORMAT_FP16) + mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS; + else + mode = WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24; + } + + switch (mode) { + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_BYPASS: + set_reg_field_value( + value, + 0, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_SRGB24: + set_reg_field_value( + value, + 1, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_XYYCC22: + set_reg_field_value( + value, + 2, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_A: + set_reg_field_value( + value, + 3, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + case WIDE_GAMUT_REGAMMA_MODE_GRAPHICS_MATRIX_B: + set_reg_field_value( + value, + 4, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + break; + default: + break; + } + + dm_write_reg(opp110->base.ctx, addr, value); +} + +bool dce110_opp_set_regamma( + struct output_pixel_processor *opp, + const struct gamma_ramp *ramp, + const struct gamma_parameters *params, + bool force_bypass) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + if (force_bypass) { + configure_regamma_mode(opp110, params, true); + } else { + /* 1. Scale gamma to 0 - 1 to m_pRgbUser */ + if (!scale_gamma(opp110, ramp, params)) { + ASSERT_CRITICAL(false); + /* invalid option */ + return false; + } + + /* 2. Configure regamma curve without analysis (future task) */ + /* and program the PWL regions and segments */ + if (params->regamma_adjust_type == GRAPHICS_REGAMMA_ADJUST_SW || + params->surface_pixel_format == PIXEL_FORMAT_FP16) { + + /* 3. Setup x exponentially distributed points */ + if (!setup_distribution_points(opp110)) { + ASSERT_CRITICAL(false); + /* invalid option */ + return false; + } + + /* 4. Build ideal regamma curve */ + if (!build_regamma_curve(opp110, params)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 5. Map user gamma (evenly distributed x points) to + * new curve when x is y from ideal regamma , step 5 */ + if (!map_regamma_hw_to_x_user( + opp110, ramp, params)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 6.Build and verify resulted curve */ + build_new_custom_resulted_curve(opp110, params); + + /* 7. Build and translate x to hw format */ + if (!rebuild_curve_configuration_magic(opp110)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 8. convert all params to the custom float format */ + if (!convert_to_custom_float(opp110)) { + ASSERT_CRITICAL(false); + /* invalid parameters or bug */ + return false; + } + + /* 9. program regamma curve configuration */ + regamma_config_regions_and_segments(opp110); + + /* 10. Program PWL */ + program_pwl(opp110, params); + } + + /* + * 11. program regamma config + */ + configure_regamma_mode(opp110, params, false); + } + return true; +} -- 2.1.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel