From ec7e6bb814911aab1995f963260184c248c44614 Mon Sep 17 00:00:00 2001 From: "Leo (Sunpeng) Li" Date: Mon, 5 Feb 2018 14:29:57 -0500 Subject: [PATCH] drm/amd/display: Add color module's gamma helpers to Linux build Also guard includes that we don't need. Signed-off-by: Leo (Sunpeng) Li Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/Makefile | 3 +- drivers/gpu/drm/amd/display/modules/color/Makefile | 31 + .../drm/amd/display/modules/color/color_gamma.c | 1070 ++++++++++++++++++++ .../drm/amd/display/modules/color/color_gamma.h | 45 + 4 files changed, 1148 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/amd/display/modules/color/Makefile create mode 100644 drivers/gpu/drm/amd/display/modules/color/color_gamma.c create mode 100644 drivers/gpu/drm/amd/display/modules/color/color_gamma.h diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile index c27c81c..3d14478 100644 --- a/drivers/gpu/drm/amd/display/Makefile +++ b/drivers/gpu/drm/amd/display/Makefile @@ -32,11 +32,12 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/inc/ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/inc/hw subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/inc subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/freesync +subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/color #TODO: remove when Timing Sync feature is complete subdir-ccflags-y += -DBUILD_FEATURE_TIMING_SYNC=0 -DAL_LIBS = amdgpu_dm dc modules/freesync +DAL_LIBS = amdgpu_dm dc modules/freesync modules/color AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/,$(DAL_LIBS))) diff --git a/drivers/gpu/drm/amd/display/modules/color/Makefile b/drivers/gpu/drm/amd/display/modules/color/Makefile new file mode 100644 index 0000000..65c33a7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/color/Makefile @@ -0,0 +1,31 @@ +# +# Copyright 2018 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. +# +# +# Makefile for the color sub-module of DAL. +# + +MOD_COLOR = color_gamma.o + +AMD_DAL_MOD_COLOR = $(addprefix $(AMDDALPATH)/modules/color/,$(MOD_COLOR)) +#$(info ************ DAL COLOR MODULE MAKEFILE ************) + +AMD_DISPLAY_FILES += $(AMD_DAL_MOD_COLOR) diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c new file mode 100644 index 0000000..fde3ae8b --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -0,0 +1,1070 @@ +/* + * Copyright 2016 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 "dc.h" +#include "opp.h" +#include "color_gamma.h" + +/* MAX_HW_POINTS = NUM_REGIONS * NUM_PTS_IN_REGION */ +#define NUM_PTS_IN_REGION 16 +#define NUM_REGIONS 32 +#define MAX_HW_POINTS 512 + +static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2]; +static struct fixed31_32 pq_table[MAX_HW_POINTS + 2]; +static bool pq_initialized; /* = false; */ + +/* one-time setup of X points */ +void setup_x_points_distribution(void) +{ + struct fixed31_32 region_size = dal_fixed31_32_from_int(128); + int32_t segment; + uint32_t seg_offset; + uint32_t index; + struct fixed31_32 increment; + + coordinates_x[NUM_REGIONS * NUM_PTS_IN_REGION].x = region_size; + coordinates_x[NUM_REGIONS * NUM_PTS_IN_REGION + 1].x = region_size; + + for (segment = 6; segment > (6 - NUM_REGIONS); segment--) { + region_size = dal_fixed31_32_div_int(region_size, 2); + increment = dal_fixed31_32_div_int(region_size, + NUM_PTS_IN_REGION); + seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION; + coordinates_x[seg_offset].x = region_size; + + for (index = seg_offset + 1; + index < seg_offset + NUM_PTS_IN_REGION; + index++) { + coordinates_x[index].x = dal_fixed31_32_add + (coordinates_x[index-1].x, increment); + } + } +} + +static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) +{ + /* consts for PQ gamma formula. */ + const struct fixed31_32 m1 = + dal_fixed31_32_from_fraction(159301758, 1000000000); + const struct fixed31_32 m2 = + dal_fixed31_32_from_fraction(7884375, 100000); + const struct fixed31_32 c1 = + dal_fixed31_32_from_fraction(8359375, 10000000); + const struct fixed31_32 c2 = + dal_fixed31_32_from_fraction(188515625, 10000000); + const struct fixed31_32 c3 = + dal_fixed31_32_from_fraction(186875, 10000); + + struct fixed31_32 l_pow_m1; + struct fixed31_32 base; + + if (dal_fixed31_32_lt(in_x, dal_fixed31_32_zero)) + in_x = dal_fixed31_32_zero; + + l_pow_m1 = dal_fixed31_32_pow(in_x, m1); + base = dal_fixed31_32_div( + dal_fixed31_32_add(c1, + (dal_fixed31_32_mul(c2, l_pow_m1))), + dal_fixed31_32_add(dal_fixed31_32_one, + (dal_fixed31_32_mul(c3, l_pow_m1)))); + *out_y = dal_fixed31_32_pow(base, m2); +} + +/* one-time pre-compute PQ values - only for sdr_white_level 80 */ +void precompute_pq(void) +{ + int i; + struct fixed31_32 x; + const struct hw_x_point *coord_x = coordinates_x + 32; + struct fixed31_32 scaling_factor = + dal_fixed31_32_from_fraction(80, 10000); + + /* pow function has problems with arguments too small */ + for (i = 0; i < 32; i++) + pq_table[i] = dal_fixed31_32_zero; + + for (i = 32; i <= MAX_HW_POINTS; i++) { + x = dal_fixed31_32_mul(coord_x->x, scaling_factor); + compute_pq(x, &pq_table[i]); + ++coord_x; + } +} + +struct dividers { + struct fixed31_32 divider1; + struct fixed31_32 divider2; + struct fixed31_32 divider3; +}; + +static void build_regamma_coefficients(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 }; + + const int32_t *numerator1; + const int32_t *numerator2; + const int32_t *numerator3; + const int32_t *numerator4; + const int32_t *numerator5; + + uint32_t i = 0; + + numerator1 = numerator01; + numerator2 = numerator02; + numerator3 = numerator03; + numerator4 = numerator04; + numerator5 = numerator05; + + 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(coefficients->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_lt(one, arg)) + return one; + + 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 find_software_points( + const struct dc_gamma *ramp, + const struct gamma_pixel *axis_x, + 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 = ramp->num_entries + 3; + + struct fixed31_32 left, right; + + uint32_t i = *index_to_start; + + while (i < max_number) { + if (channel == CHANNEL_NAME_RED) { + left = axis_x[i].r; + + if (i < max_number - 1) + right = axis_x[i + 1].r; + else + right = axis_x[max_number - 1].r; + } else if (channel == CHANNEL_NAME_GREEN) { + left = axis_x[i].g; + + if (i < max_number - 1) + right = axis_x[i + 1].g; + else + right = axis_x[max_number - 1].g; + } else { + left = axis_x[i].b; + + if (i < max_number - 1) + right = axis_x[i + 1].b; + else + right = axis_x[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 build_custom_gamma_mapping_coefficients_worker( + const struct dc_gamma *ramp, + struct pixel_gamma_point *coeff, + const struct hw_x_point *coordinates_x, + const struct gamma_pixel *axis_x, + enum channel_name channel, + uint32_t number_of_points) +{ + 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; + + /* + * TODO: confirm enum in surface_pixel_format + * if (pixel_format == PIXEL_FORMAT_FP16) + *coord_x = coordinates_x[i].adjusted_x; + *else + */ + if (channel == CHANNEL_NAME_RED) + coord_x = coordinates_x[i].regamma_y_red; + else if (channel == CHANNEL_NAME_GREEN) + coord_x = coordinates_x[i].regamma_y_green; + else + coord_x = coordinates_x[i].regamma_y_blue; + + if (!find_software_points( + ramp, axis_x, coord_x, channel, + &index_to_start, &index_left, &index_right, &hw_pos)) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (index_left >= ramp->num_entries + 3) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (index_right >= ramp->num_entries + 3) { + BREAK_TO_DEBUGGER(); + return false; + } + + if (channel == CHANNEL_NAME_RED) { + point = &coeff[i].r; + + left_pos = axis_x[index_left].r; + right_pos = axis_x[index_right].r; + } else if (channel == CHANNEL_NAME_GREEN) { + point = &coeff[i].g; + + left_pos = axis_x[index_left].g; + right_pos = axis_x[index_right].g; + } else { + point = &coeff[i].b; + + left_pos = axis_x[index_left].b; + right_pos = axis_x[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 = dal_fixed31_32_zero; + else if (hw_pos == HW_POINT_POSITION_RIGHT) + point->coeff = dal_fixed31_32_from_int(2); + 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 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 = dal_fixed31_32_zero; + } else { + BREAK_TO_DEBUGGER(); + result = dal_fixed31_32_one; + } + + return result; +} + +static void build_regamma_curve_pq(struct pwl_float_data_ex *rgb_regamma, + uint32_t hw_points_num, + const struct hw_x_point *coordinate_x, + uint32_t sdr_white_level) +{ + uint32_t i, start_index; + + struct pwl_float_data_ex *rgb = rgb_regamma; + const struct hw_x_point *coord_x = coordinate_x; + struct fixed31_32 x; + struct fixed31_32 output; + struct fixed31_32 scaling_factor = + dal_fixed31_32_from_fraction(sdr_white_level, 10000); + + if (!pq_initialized && sdr_white_level == 80) { + precompute_pq(); + pq_initialized = true; + } + + /* TODO: start index is from segment 2^-24, skipping first segment + * due to x values too small for power calculations + */ + start_index = 32; + rgb += start_index; + coord_x += start_index; + + /* use coord_x to retrieve coordinates chosen base on given user curve + * the x values are exponentially distributed and currently it is hard + * coded, the user curve shape is ignored. Need to recalculate coord_x + * based on input curve, translation from 256/1025 to 128 PWL points. + */ + for (i = start_index; i <= hw_points_num; i++) { + /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125. + * FP 1.0 = 80nits + */ + if (sdr_white_level == 80) { + output = pq_table[i]; + } else { + x = dal_fixed31_32_mul(coord_x->x, scaling_factor); + compute_pq(x, &output); + } + + /* should really not happen? */ + if (dal_fixed31_32_lt(output, dal_fixed31_32_zero)) + output = dal_fixed31_32_zero; + else if (dal_fixed31_32_lt(dal_fixed31_32_one, output)) + output = dal_fixed31_32_one; + + rgb->r = output; + rgb->g = output; + rgb->b = output; + + ++coord_x; + ++rgb; + } +} + +static void build_regamma_curve(struct pwl_float_data_ex *rgb_regamma, + uint32_t hw_points_num, + const struct hw_x_point *coordinate_x) +{ + uint32_t i; + + struct gamma_coefficients coeff; + struct pwl_float_data_ex *rgb = rgb_regamma; + const struct hw_x_point *coord_x = coordinate_x; + + build_regamma_coefficients(&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 != hw_points_num + 1) { + rgb->r = translate_from_linear_space_ex( + coord_x->x, &coeff, 0); + rgb->g = translate_from_linear_space_ex( + coord_x->x, &coeff, 1); + rgb->b = translate_from_linear_space_ex( + coord_x->x, &coeff, 2); + + ++coord_x; + ++rgb; + ++i; + } +} + +static bool scale_gamma(struct pwl_float_data *pwl_rgb, + const struct dc_gamma *ramp, + struct dividers dividers) +{ + const struct fixed31_32 max_driver = dal_fixed31_32_from_int(0xFFFF); + const struct fixed31_32 max_os = dal_fixed31_32_from_int(0xFF00); + struct fixed31_32 scaler = max_os; + uint32_t i; + struct pwl_float_data *rgb = pwl_rgb; + struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1; + + i = 0; + + do { + if (dal_fixed31_32_lt(max_os, ramp->entries.red[i]) || + dal_fixed31_32_lt(max_os, ramp->entries.green[i]) || + dal_fixed31_32_lt(max_os, ramp->entries.blue[i])) { + scaler = max_driver; + break; + } + ++i; + } while (i != ramp->num_entries); + + i = 0; + + do { + rgb->r = dal_fixed31_32_div( + ramp->entries.red[i], scaler); + rgb->g = dal_fixed31_32_div( + ramp->entries.green[i], scaler); + rgb->b = dal_fixed31_32_div( + ramp->entries.blue[i], scaler); + + ++rgb; + ++i; + } while (i != ramp->num_entries); + + rgb->r = dal_fixed31_32_mul(rgb_last->r, + dividers.divider1); + rgb->g = dal_fixed31_32_mul(rgb_last->g, + dividers.divider1); + rgb->b = dal_fixed31_32_mul(rgb_last->b, + dividers.divider1); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, + dividers.divider2); + rgb->g = dal_fixed31_32_mul(rgb_last->g, + dividers.divider2); + rgb->b = dal_fixed31_32_mul(rgb_last->b, + dividers.divider2); + + ++rgb; + + rgb->r = dal_fixed31_32_mul(rgb_last->r, + dividers.divider3); + rgb->g = dal_fixed31_32_mul(rgb_last->g, + dividers.divider3); + rgb->b = dal_fixed31_32_mul(rgb_last->b, + dividers.divider3); + + return true; +} + +static bool scale_gamma_dx(struct pwl_float_data *pwl_rgb, + const struct dc_gamma *ramp, + struct dividers dividers) +{ + uint32_t i; + struct fixed31_32 min = dal_fixed31_32_zero; + struct fixed31_32 max = dal_fixed31_32_one; + + struct fixed31_32 delta = dal_fixed31_32_zero; + struct fixed31_32 offset = dal_fixed31_32_zero; + + for (i = 0 ; i < ramp->num_entries; i++) { + if (dal_fixed31_32_lt(ramp->entries.red[i], min)) + min = ramp->entries.red[i]; + + if (dal_fixed31_32_lt(ramp->entries.green[i], min)) + min = ramp->entries.green[i]; + + if (dal_fixed31_32_lt(ramp->entries.blue[i], min)) + min = ramp->entries.blue[i]; + + if (dal_fixed31_32_lt(max, ramp->entries.red[i])) + max = ramp->entries.red[i]; + + if (dal_fixed31_32_lt(max, ramp->entries.green[i])) + max = ramp->entries.green[i]; + + if (dal_fixed31_32_lt(max, ramp->entries.blue[i])) + max = ramp->entries.blue[i]; + } + + if (dal_fixed31_32_lt(min, dal_fixed31_32_zero)) + delta = dal_fixed31_32_neg(min); + + offset = dal_fixed31_32_add(min, max); + + for (i = 0 ; i < ramp->num_entries; i++) { + pwl_rgb[i].r = dal_fixed31_32_div( + dal_fixed31_32_add( + ramp->entries.red[i], delta), offset); + pwl_rgb[i].g = dal_fixed31_32_div( + dal_fixed31_32_add( + ramp->entries.green[i], delta), offset); + pwl_rgb[i].b = dal_fixed31_32_div( + dal_fixed31_32_add( + ramp->entries.blue[i], delta), offset); + + } + + pwl_rgb[i].r = dal_fixed31_32_sub(dal_fixed31_32_mul_int( + pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); + pwl_rgb[i].g = dal_fixed31_32_sub(dal_fixed31_32_mul_int( + pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); + pwl_rgb[i].b = dal_fixed31_32_sub(dal_fixed31_32_mul_int( + pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); + ++i; + pwl_rgb[i].r = dal_fixed31_32_sub(dal_fixed31_32_mul_int( + pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r); + pwl_rgb[i].g = dal_fixed31_32_sub(dal_fixed31_32_mul_int( + pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g); + pwl_rgb[i].b = dal_fixed31_32_sub(dal_fixed31_32_mul_int( + pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b); + + return true; +} + +/* + * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here + * Input is evenly distributed in the output color space as specified in + * SetTimings + * + * Interpolation details: + * 1D LUT has 4096 values which give curve correction in 0-1 float range + * for evenly spaced points in 0-1 range. lut1D[index] gives correction + * for index/4095. + * First we find index for which: + * index/4095 < regamma_y < (index+1)/4095 => + * index < 4095*regamma_y < index + 1 + * norm_y = 4095*regamma_y, and index is just truncating to nearest integer + * lut1 = lut1D[index], lut2 = lut1D[index+1] + * + *adjustedY is then linearly interpolating regamma Y between lut1 and lut2 + */ +static void apply_lut_1d( + const struct dc_gamma *ramp, + uint32_t num_hw_points, + struct dc_transfer_func_distributed_points *tf_pts) +{ + int i = 0; + int color = 0; + struct fixed31_32 *regamma_y; + struct fixed31_32 norm_y; + struct fixed31_32 lut1; + struct fixed31_32 lut2; + const int max_lut_index = 4095; + const struct fixed31_32 max_lut_index_f = + dal_fixed31_32_from_int_nonconst(max_lut_index); + int32_t index = 0, index_next = 0; + struct fixed31_32 index_f; + struct fixed31_32 delta_lut; + struct fixed31_32 delta_index; + + if (ramp->type != GAMMA_CS_TFM_1D) + return; // this is not expected + + for (i = 0; i < num_hw_points; i++) { + for (color = 0; color < 3; color++) { + if (color == 0) + regamma_y = &tf_pts->red[i]; + else if (color == 1) + regamma_y = &tf_pts->green[i]; + else + regamma_y = &tf_pts->blue[i]; + + norm_y = dal_fixed31_32_mul(max_lut_index_f, + *regamma_y); + index = dal_fixed31_32_floor(norm_y); + index_f = dal_fixed31_32_from_int_nonconst(index); + + if (index < 0 || index > max_lut_index) + continue; + + index_next = (index == max_lut_index) ? index : index+1; + + if (color == 0) { + lut1 = ramp->entries.red[index]; + lut2 = ramp->entries.red[index_next]; + } else if (color == 1) { + lut1 = ramp->entries.green[index]; + lut2 = ramp->entries.green[index_next]; + } else { + lut1 = ramp->entries.blue[index]; + lut2 = ramp->entries.blue[index_next]; + } + + // we have everything now, so interpolate + delta_lut = dal_fixed31_32_sub(lut2, lut1); + delta_index = dal_fixed31_32_sub(norm_y, index_f); + + *regamma_y = dal_fixed31_32_add(lut1, + dal_fixed31_32_mul(delta_index, delta_lut)); + } + } +} + +static void build_evenly_distributed_points( + struct gamma_pixel *points, + uint32_t numberof_points, + struct dividers dividers) +{ + 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_from_fraction(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, dividers.divider1); + p->g = dal_fixed31_32_div(p_last->g, dividers.divider1); + p->b = dal_fixed31_32_div(p_last->b, dividers.divider1); + + ++p; + + p->r = dal_fixed31_32_div(p_last->r, dividers.divider2); + p->g = dal_fixed31_32_div(p_last->g, dividers.divider2); + p->b = dal_fixed31_32_div(p_last->b, dividers.divider2); + + ++p; + + p->r = dal_fixed31_32_div(p_last->r, dividers.divider3); + p->g = dal_fixed31_32_div(p_last->g, dividers.divider3); + p->b = dal_fixed31_32_div(p_last->b, dividers.divider3); +} + +static inline void copy_rgb_regamma_to_coordinates_x( + struct hw_x_point *coordinates_x, + uint32_t hw_points_num, + const struct pwl_float_data_ex *rgb_ex) +{ + struct hw_x_point *coords = coordinates_x; + uint32_t i = 0; + const struct pwl_float_data_ex *rgb_regamma = rgb_ex; + + while (i <= 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( + const struct dc_gamma *ramp, + struct pixel_gamma_point *coeff128, + struct pwl_float_data *rgb_user, + const struct hw_x_point *coordinates_x, + const struct gamma_pixel *axis_x, + uint32_t number_of_points, + struct dc_transfer_func_distributed_points *tf_pts) +{ + + const struct pixel_gamma_point *coeff = coeff128; + uint32_t max_entries = 3 - 1; + + uint32_t i = 0; + + for (i = 0; i < 3; i++) { + if (!build_custom_gamma_mapping_coefficients_worker( + ramp, coeff128, coordinates_x, axis_x, i, + number_of_points)) + return false; + } + + i = 0; + max_entries += ramp->num_entries; + + /* TODO: float point case */ + + while (i <= number_of_points) { + tf_pts->red[i] = calculate_mapped_value( + rgb_user, coeff, CHANNEL_NAME_RED, max_entries); + tf_pts->green[i] = calculate_mapped_value( + rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries); + tf_pts->blue[i] = calculate_mapped_value( + rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries); + + ++coeff; + ++i; + } + + return true; +} + +static void build_new_custom_resulted_curve( + uint32_t hw_points_num, + struct dc_transfer_func_distributed_points *tf_pts) +{ + uint32_t i; + + i = 0; + + while (i != hw_points_num + 1) { + tf_pts->red[i] = dal_fixed31_32_clamp( + tf_pts->red[i], dal_fixed31_32_zero, + dal_fixed31_32_one); + tf_pts->green[i] = dal_fixed31_32_clamp( + tf_pts->green[i], dal_fixed31_32_zero, + dal_fixed31_32_one); + tf_pts->blue[i] = dal_fixed31_32_clamp( + tf_pts->blue[i], dal_fixed31_32_zero, + dal_fixed31_32_one); + + ++i; + } +} + +static bool map_regamma_hw_to_x_user( + const struct dc_gamma *ramp, + struct pixel_gamma_point *coeff128, + struct pwl_float_data *rgb_user, + struct hw_x_point *coords_x, + const struct gamma_pixel *axis_x, + const struct pwl_float_data_ex *rgb_regamma, + uint32_t hw_points_num, + struct dc_transfer_func_distributed_points *tf_pts, + bool mapUserRamp) +{ + /* setup to spare calculated ideal regamma values */ + + int i = 0; + struct hw_x_point *coords = coords_x; + const struct pwl_float_data_ex *regamma = rgb_regamma; + + if (mapUserRamp) { + copy_rgb_regamma_to_coordinates_x(coords, + hw_points_num, + rgb_regamma); + + calculate_interpolated_hardware_curve( + ramp, coeff128, rgb_user, coords, axis_x, + hw_points_num, tf_pts); + } else { + /* just copy current rgb_regamma into tf_pts */ + while (i <= hw_points_num) { + tf_pts->red[i] = regamma->r; + tf_pts->green[i] = regamma->g; + tf_pts->blue[i] = regamma->b; + + ++regamma; + ++i; + } + } + + build_new_custom_resulted_curve(hw_points_num, tf_pts); + + return true; +} + +bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, + const struct dc_gamma *ramp, bool mapUserRamp) +{ + struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; + struct dividers dividers; + + struct pwl_float_data *rgb_user = NULL; + struct pwl_float_data_ex *rgb_regamma = NULL; + struct gamma_pixel *axix_x = NULL; + struct pixel_gamma_point *coeff128 = NULL; + enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; + bool ret = false; + + if (output_tf->type == TF_TYPE_BYPASS) + return false; + + /* we can use hardcoded curve for plain SRGB TF */ + if (output_tf->type == TF_TYPE_PREDEFINED && + output_tf->tf == TRANSFER_FUNCTION_SRGB && + (!mapUserRamp && ramp->type == GAMMA_RGB_256)) + return true; + + output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; + + rgb_user = kzalloc(sizeof(*rgb_user) * (ramp->num_entries + 3), + GFP_KERNEL); + if (!rgb_user) + goto rgb_user_alloc_fail; + rgb_regamma = kzalloc(sizeof(*rgb_regamma) * (MAX_HW_POINTS + 3), + GFP_KERNEL); + if (!rgb_regamma) + goto rgb_regamma_alloc_fail; + axix_x = kzalloc(sizeof(*axix_x) * (ramp->num_entries + 3), + GFP_KERNEL); + if (!axix_x) + goto axix_x_alloc_fail; + coeff128 = kzalloc(sizeof(*coeff128) * (MAX_HW_POINTS + 3), GFP_KERNEL); + if (!coeff128) + goto coeff128_alloc_fail; + + dividers.divider1 = dal_fixed31_32_from_fraction(3, 2); + dividers.divider2 = dal_fixed31_32_from_int(2); + dividers.divider3 = dal_fixed31_32_from_fraction(5, 2); + + tf = output_tf->tf; + + build_evenly_distributed_points( + axix_x, + ramp->num_entries, + dividers); + + if (ramp->type == GAMMA_RGB_256 && mapUserRamp) + scale_gamma(rgb_user, ramp, dividers); + else if (ramp->type == GAMMA_RGB_FLOAT_1024) + scale_gamma_dx(rgb_user, ramp, dividers); + + if (tf == TRANSFER_FUNCTION_PQ) { + tf_pts->end_exponent = 7; + tf_pts->x_point_at_y1_red = 125; + tf_pts->x_point_at_y1_green = 125; + tf_pts->x_point_at_y1_blue = 125; + + build_regamma_curve_pq(rgb_regamma, + MAX_HW_POINTS, + coordinates_x, + output_tf->sdr_ref_white_level); + } else { + tf_pts->end_exponent = 0; + tf_pts->x_point_at_y1_red = 1; + tf_pts->x_point_at_y1_green = 1; + tf_pts->x_point_at_y1_blue = 1; + + build_regamma_curve(rgb_regamma, + MAX_HW_POINTS, + coordinates_x); + } + + map_regamma_hw_to_x_user(ramp, coeff128, rgb_user, + coordinates_x, axix_x, rgb_regamma, + MAX_HW_POINTS, tf_pts, + (mapUserRamp || ramp->type != GAMMA_RGB_256) && + ramp->type != GAMMA_CS_TFM_1D); + + if (ramp->type == GAMMA_CS_TFM_1D) + apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); + + ret = true; + + kfree(coeff128); +coeff128_alloc_fail: + kfree(axix_x); +axix_x_alloc_fail: + kfree(rgb_regamma); +rgb_regamma_alloc_fail: + kfree(rgb_user); +rgb_user_alloc_fail: + return ret; +} + + +/*TODO fix me should be 2*/ +#define _EXTRA_POINTS 3 + +bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, + struct dc_transfer_func_distributed_points *points) +{ + uint32_t i; + bool ret = false; + struct pwl_float_data_ex *rgb_regamma = NULL; + + if (trans == TRANSFER_FUNCTION_UNITY) { + //setup_x_points_distribution(coordinates_x); + for (i = 0; i < MAX_HW_POINTS ; i++) { + points->red[i] = coordinates_x[i].x; + points->green[i] = coordinates_x[i].x; + points->blue[i] = coordinates_x[i].x; + } + ret = true; + } else if (trans == TRANSFER_FUNCTION_PQ) { + rgb_regamma = kzalloc(sizeof(*rgb_regamma) * (MAX_HW_POINTS + + _EXTRA_POINTS), GFP_KERNEL); + if (!rgb_regamma) + goto rgb_regamma_alloc_fail; + //setup_x_points_distribution(coordinates_x); + points->end_exponent = 0; + points->x_point_at_y1_red = 1; + points->x_point_at_y1_green = 1; + points->x_point_at_y1_blue = 1; + + build_regamma_curve_pq(rgb_regamma, + MAX_HW_POINTS, + coordinates_x, + 80); + for (i = 0; i < MAX_HW_POINTS ; i++) { + points->red[i] = rgb_regamma[i].r; + points->green[i] = rgb_regamma[i].g; + points->blue[i] = rgb_regamma[i].b; + } + ret = true; + + kfree(rgb_regamma); + } +rgb_regamma_alloc_fail: + return ret; +} + + diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h new file mode 100644 index 0000000..774c6da --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h @@ -0,0 +1,45 @@ +/* + * Copyright 2016 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 COLOR_MOD_COLOR_GAMMA_H_ +#define COLOR_MOD_COLOR_GAMMA_H_ + +struct dc_transfer_func; +struct dc_gamma; +struct dc_transfer_func_distributed_points; +struct dc_rgb_fixed; +enum dc_transfer_func_predefined; + +void setup_x_points_distribution(void); +void precompute_pq(void); + +bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, + const struct dc_gamma *ramp, bool mapUserRamp); + +bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, + struct dc_transfer_func_distributed_points *points); + + +#endif /* COLOR_MOD_COLOR_GAMMA_H_ */ -- 2.7.4