linux/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
<<
>>
Prefs
   1/*
   2 * Copyright 2016 Advanced Micro Devices, Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: AMD
  23 *
  24 */
  25
  26#include <linux/mm.h>
  27#include <linux/slab.h>
  28
  29#include "dc.h"
  30#include "opp.h"
  31#include "color_gamma.h"
  32
  33#define NUM_PTS_IN_REGION 16
  34#define NUM_REGIONS 32
  35#define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
  36
  37static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2];
  38
  39static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
  40static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
  41
  42// these are helpers for calculations to reduce stack usage
  43// do not depend on these being preserved across calls
  44static struct fixed31_32 scratch_1;
  45static struct fixed31_32 scratch_2;
  46static struct translate_from_linear_space_args scratch_gamma_args;
  47
  48/* Helper to optimize gamma calculation, only use in translate_from_linear, in
  49 * particular the dc_fixpt_pow function which is very expensive
  50 * The idea is that our regions for X points are exponential and currently they all use
  51 * the same number of points (NUM_PTS_IN_REGION) and in each region every point
  52 * is exactly 2x the one at the same index in the previous region. In other words
  53 * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16
  54 * The other fact is that (2x)^gamma = 2^gamma * x^gamma
  55 * So we compute and save x^gamma for the first 16 regions, and for every next region
  56 * just multiply with 2^gamma which can be computed once, and save the result so we
  57 * recursively compute all the values.
  58 */
  59static struct fixed31_32 pow_buffer[NUM_PTS_IN_REGION];
  60static struct fixed31_32 gamma_of_2; // 2^gamma
  61int pow_buffer_ptr = -1;
  62                                                                                /*sRGB   709 2.2 2.4 P3*/
  63static const int32_t gamma_numerator01[] = { 31308,     180000, 0,      0,      0};
  64static const int32_t gamma_numerator02[] = { 12920,     4500,   0,      0,      0};
  65static const int32_t gamma_numerator03[] = { 55,        99,             0,      0,      0};
  66static const int32_t gamma_numerator04[] = { 55,        99,             0,      0,      0};
  67static const int32_t gamma_numerator05[] = { 2400,      2200,   2200, 2400, 2600};
  68
  69static bool pq_initialized; /* = false; */
  70static bool de_pq_initialized; /* = false; */
  71
  72/* one-time setup of X points */
  73void setup_x_points_distribution(void)
  74{
  75        struct fixed31_32 region_size = dc_fixpt_from_int(128);
  76        int32_t segment;
  77        uint32_t seg_offset;
  78        uint32_t index;
  79        struct fixed31_32 increment;
  80
  81        coordinates_x[MAX_HW_POINTS].x = region_size;
  82        coordinates_x[MAX_HW_POINTS + 1].x = region_size;
  83
  84        for (segment = 6; segment > (6 - NUM_REGIONS); segment--) {
  85                region_size = dc_fixpt_div_int(region_size, 2);
  86                increment = dc_fixpt_div_int(region_size,
  87                                                NUM_PTS_IN_REGION);
  88                seg_offset = (segment + (NUM_REGIONS - 7)) * NUM_PTS_IN_REGION;
  89                coordinates_x[seg_offset].x = region_size;
  90
  91                for (index = seg_offset + 1;
  92                                index < seg_offset + NUM_PTS_IN_REGION;
  93                                index++) {
  94                        coordinates_x[index].x = dc_fixpt_add
  95                                        (coordinates_x[index-1].x, increment);
  96                }
  97        }
  98}
  99
 100void log_x_points_distribution(struct dal_logger *logger)
 101{
 102        int i = 0;
 103
 104        if (logger != NULL) {
 105                LOG_GAMMA_WRITE("Log X Distribution\n");
 106
 107                for (i = 0; i < MAX_HW_POINTS; i++)
 108                        LOG_GAMMA_WRITE("%llu\n", coordinates_x[i].x.value);
 109        }
 110}
 111
 112static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
 113{
 114        /* consts for PQ gamma formula. */
 115        const struct fixed31_32 m1 =
 116                dc_fixpt_from_fraction(159301758, 1000000000);
 117        const struct fixed31_32 m2 =
 118                dc_fixpt_from_fraction(7884375, 100000);
 119        const struct fixed31_32 c1 =
 120                dc_fixpt_from_fraction(8359375, 10000000);
 121        const struct fixed31_32 c2 =
 122                dc_fixpt_from_fraction(188515625, 10000000);
 123        const struct fixed31_32 c3 =
 124                dc_fixpt_from_fraction(186875, 10000);
 125
 126        struct fixed31_32 l_pow_m1;
 127        struct fixed31_32 base;
 128
 129        if (dc_fixpt_lt(in_x, dc_fixpt_zero))
 130                in_x = dc_fixpt_zero;
 131
 132        l_pow_m1 = dc_fixpt_pow(in_x, m1);
 133        base = dc_fixpt_div(
 134                        dc_fixpt_add(c1,
 135                                        (dc_fixpt_mul(c2, l_pow_m1))),
 136                        dc_fixpt_add(dc_fixpt_one,
 137                                        (dc_fixpt_mul(c3, l_pow_m1))));
 138        *out_y = dc_fixpt_pow(base, m2);
 139}
 140
 141static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y)
 142{
 143        /* consts for dePQ gamma formula. */
 144        const struct fixed31_32 m1 =
 145                dc_fixpt_from_fraction(159301758, 1000000000);
 146        const struct fixed31_32 m2 =
 147                dc_fixpt_from_fraction(7884375, 100000);
 148        const struct fixed31_32 c1 =
 149                dc_fixpt_from_fraction(8359375, 10000000);
 150        const struct fixed31_32 c2 =
 151                dc_fixpt_from_fraction(188515625, 10000000);
 152        const struct fixed31_32 c3 =
 153                dc_fixpt_from_fraction(186875, 10000);
 154
 155        struct fixed31_32 l_pow_m1;
 156        struct fixed31_32 base, div;
 157
 158
 159        if (dc_fixpt_lt(in_x, dc_fixpt_zero))
 160                in_x = dc_fixpt_zero;
 161
 162        l_pow_m1 = dc_fixpt_pow(in_x,
 163                        dc_fixpt_div(dc_fixpt_one, m2));
 164        base = dc_fixpt_sub(l_pow_m1, c1);
 165
 166        if (dc_fixpt_lt(base, dc_fixpt_zero))
 167                base = dc_fixpt_zero;
 168
 169        div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1));
 170
 171        *out_y = dc_fixpt_pow(dc_fixpt_div(base, div),
 172                        dc_fixpt_div(dc_fixpt_one, m1));
 173
 174}
 175
 176
 177/*de gamma, none linear to linear*/
 178static void compute_hlg_eotf(struct fixed31_32 in_x,
 179                struct fixed31_32 *out_y,
 180                uint32_t sdr_white_level, uint32_t max_luminance_nits)
 181{
 182        struct fixed31_32 a;
 183        struct fixed31_32 b;
 184        struct fixed31_32 c;
 185        struct fixed31_32 threshold;
 186        struct fixed31_32 x;
 187
 188        struct fixed31_32 scaling_factor =
 189                        dc_fixpt_from_fraction(max_luminance_nits, sdr_white_level);
 190        a = dc_fixpt_from_fraction(17883277, 100000000);
 191        b = dc_fixpt_from_fraction(28466892, 100000000);
 192        c = dc_fixpt_from_fraction(55991073, 100000000);
 193        threshold = dc_fixpt_from_fraction(1, 2);
 194
 195        if (dc_fixpt_lt(in_x, threshold)) {
 196                x = dc_fixpt_mul(in_x, in_x);
 197                x = dc_fixpt_div_int(x, 3);
 198        } else {
 199                x = dc_fixpt_sub(in_x, c);
 200                x = dc_fixpt_div(x, a);
 201                x = dc_fixpt_exp(x);
 202                x = dc_fixpt_add(x, b);
 203                x = dc_fixpt_div_int(x, 12);
 204        }
 205        *out_y = dc_fixpt_mul(x, scaling_factor);
 206
 207}
 208
 209/*re gamma, linear to none linear*/
 210static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y,
 211                uint32_t sdr_white_level, uint32_t max_luminance_nits)
 212{
 213        struct fixed31_32 a;
 214        struct fixed31_32 b;
 215        struct fixed31_32 c;
 216        struct fixed31_32 threshold;
 217        struct fixed31_32 x;
 218
 219        struct fixed31_32 scaling_factor =
 220                        dc_fixpt_from_fraction(sdr_white_level, max_luminance_nits);
 221        a = dc_fixpt_from_fraction(17883277, 100000000);
 222        b = dc_fixpt_from_fraction(28466892, 100000000);
 223        c = dc_fixpt_from_fraction(55991073, 100000000);
 224        threshold = dc_fixpt_from_fraction(1, 12);
 225        x = dc_fixpt_mul(in_x, scaling_factor);
 226
 227
 228        if (dc_fixpt_lt(x, threshold)) {
 229                x = dc_fixpt_mul(x, dc_fixpt_from_fraction(3, 1));
 230                *out_y = dc_fixpt_pow(x, dc_fixpt_half);
 231        } else {
 232                x = dc_fixpt_mul(x, dc_fixpt_from_fraction(12, 1));
 233                x = dc_fixpt_sub(x, b);
 234                x = dc_fixpt_log(x);
 235                x = dc_fixpt_mul(a, x);
 236                *out_y = dc_fixpt_add(x, c);
 237        }
 238}
 239
 240
 241/* one-time pre-compute PQ values - only for sdr_white_level 80 */
 242void precompute_pq(void)
 243{
 244        int i;
 245        struct fixed31_32 x;
 246        const struct hw_x_point *coord_x = coordinates_x + 32;
 247        struct fixed31_32 scaling_factor =
 248                        dc_fixpt_from_fraction(80, 10000);
 249
 250        /* pow function has problems with arguments too small */
 251        for (i = 0; i < 32; i++)
 252                pq_table[i] = dc_fixpt_zero;
 253
 254        for (i = 32; i <= MAX_HW_POINTS; i++) {
 255                x = dc_fixpt_mul(coord_x->x, scaling_factor);
 256                compute_pq(x, &pq_table[i]);
 257                ++coord_x;
 258        }
 259}
 260
 261/* one-time pre-compute dePQ values - only for max pixel value 125 FP16 */
 262void precompute_de_pq(void)
 263{
 264        int i;
 265        struct fixed31_32  y;
 266        uint32_t begin_index, end_index;
 267
 268        struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
 269
 270        /* X points is 2^-25 to 2^7
 271         * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
 272         */
 273        begin_index = 13 * NUM_PTS_IN_REGION;
 274        end_index = begin_index + 12 * NUM_PTS_IN_REGION;
 275
 276        for (i = 0; i <= begin_index; i++)
 277                de_pq_table[i] = dc_fixpt_zero;
 278
 279        for (; i <= end_index; i++) {
 280                compute_de_pq(coordinates_x[i].x, &y);
 281                de_pq_table[i] = dc_fixpt_mul(y, scaling_factor);
 282        }
 283
 284        for (; i <= MAX_HW_POINTS; i++)
 285                de_pq_table[i] = de_pq_table[i-1];
 286}
 287struct dividers {
 288        struct fixed31_32 divider1;
 289        struct fixed31_32 divider2;
 290        struct fixed31_32 divider3;
 291};
 292
 293
 294static bool build_coefficients(struct gamma_coefficients *coefficients, enum dc_transfer_func_predefined type)
 295{
 296
 297        uint32_t i = 0;
 298        uint32_t index = 0;
 299        bool ret = true;
 300
 301        if (type == TRANSFER_FUNCTION_SRGB)
 302                index = 0;
 303        else if (type == TRANSFER_FUNCTION_BT709)
 304                index = 1;
 305        else if (type == TRANSFER_FUNCTION_GAMMA22)
 306                index = 2;
 307        else if (type == TRANSFER_FUNCTION_GAMMA24)
 308                index = 3;
 309        else if (type == TRANSFER_FUNCTION_GAMMA26)
 310                index = 4;
 311        else {
 312                ret = false;
 313                goto release;
 314        }
 315
 316        do {
 317                coefficients->a0[i] = dc_fixpt_from_fraction(
 318                        gamma_numerator01[index], 10000000);
 319                coefficients->a1[i] = dc_fixpt_from_fraction(
 320                        gamma_numerator02[index], 1000);
 321                coefficients->a2[i] = dc_fixpt_from_fraction(
 322                        gamma_numerator03[index], 1000);
 323                coefficients->a3[i] = dc_fixpt_from_fraction(
 324                        gamma_numerator04[index], 1000);
 325                coefficients->user_gamma[i] = dc_fixpt_from_fraction(
 326                        gamma_numerator05[index], 1000);
 327
 328                ++i;
 329        } while (i != ARRAY_SIZE(coefficients->a0));
 330release:
 331        return ret;
 332}
 333
 334static struct fixed31_32 translate_from_linear_space(
 335                struct translate_from_linear_space_args *args)
 336{
 337        const struct fixed31_32 one = dc_fixpt_from_int(1);
 338
 339        if (dc_fixpt_le(one, args->arg))
 340                return one;
 341
 342        if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) {
 343                scratch_1 = dc_fixpt_add(one, args->a3);
 344                scratch_2 = dc_fixpt_pow(
 345                                dc_fixpt_neg(args->arg),
 346                                dc_fixpt_recip(args->gamma));
 347                scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
 348                scratch_1 = dc_fixpt_sub(args->a2, scratch_1);
 349
 350                return scratch_1;
 351        } else if (dc_fixpt_le(args->a0, args->arg)) {
 352                if (pow_buffer_ptr == 0) {
 353                        gamma_of_2 = dc_fixpt_pow(dc_fixpt_from_int(2),
 354                                        dc_fixpt_recip(args->gamma));
 355                }
 356                scratch_1 = dc_fixpt_add(one, args->a3);
 357                if (pow_buffer_ptr < 16)
 358                        scratch_2 = dc_fixpt_pow(args->arg,
 359                                        dc_fixpt_recip(args->gamma));
 360                else
 361                        scratch_2 = dc_fixpt_mul(gamma_of_2,
 362                                        pow_buffer[pow_buffer_ptr%16]);
 363
 364                pow_buffer[pow_buffer_ptr%16] = scratch_2;
 365                pow_buffer_ptr++;
 366
 367                scratch_1 = dc_fixpt_mul(scratch_1, scratch_2);
 368                scratch_1 = dc_fixpt_sub(scratch_1, args->a2);
 369
 370                return scratch_1;
 371        }
 372        else
 373                return dc_fixpt_mul(args->arg, args->a1);
 374}
 375
 376static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg)
 377{
 378        struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10);
 379
 380        scratch_gamma_args.arg = arg;
 381        scratch_gamma_args.a0 = dc_fixpt_zero;
 382        scratch_gamma_args.a1 = dc_fixpt_zero;
 383        scratch_gamma_args.a2 = dc_fixpt_zero;
 384        scratch_gamma_args.a3 = dc_fixpt_zero;
 385        scratch_gamma_args.gamma = gamma;
 386
 387        return translate_from_linear_space(&scratch_gamma_args);
 388}
 389
 390static struct fixed31_32 translate_to_linear_space(
 391        struct fixed31_32 arg,
 392        struct fixed31_32 a0,
 393        struct fixed31_32 a1,
 394        struct fixed31_32 a2,
 395        struct fixed31_32 a3,
 396        struct fixed31_32 gamma)
 397{
 398        struct fixed31_32 linear;
 399
 400        a0 = dc_fixpt_mul(a0, a1);
 401        if (dc_fixpt_le(arg, dc_fixpt_neg(a0)))
 402
 403                linear = dc_fixpt_neg(
 404                                 dc_fixpt_pow(
 405                                 dc_fixpt_div(
 406                                 dc_fixpt_sub(a2, arg),
 407                                 dc_fixpt_add(
 408                                 dc_fixpt_one, a3)), gamma));
 409
 410        else if (dc_fixpt_le(dc_fixpt_neg(a0), arg) &&
 411                         dc_fixpt_le(arg, a0))
 412                linear = dc_fixpt_div(arg, a1);
 413        else
 414                linear =  dc_fixpt_pow(
 415                                        dc_fixpt_div(
 416                                        dc_fixpt_add(a2, arg),
 417                                        dc_fixpt_add(
 418                                        dc_fixpt_one, a3)), gamma);
 419
 420        return linear;
 421}
 422
 423static struct fixed31_32 translate_from_linear_space_ex(
 424        struct fixed31_32 arg,
 425        struct gamma_coefficients *coeff,
 426        uint32_t color_index)
 427{
 428        scratch_gamma_args.arg = arg;
 429        scratch_gamma_args.a0 = coeff->a0[color_index];
 430        scratch_gamma_args.a1 = coeff->a1[color_index];
 431        scratch_gamma_args.a2 = coeff->a2[color_index];
 432        scratch_gamma_args.a3 = coeff->a3[color_index];
 433        scratch_gamma_args.gamma = coeff->user_gamma[color_index];
 434
 435        return translate_from_linear_space(&scratch_gamma_args);
 436}
 437
 438
 439static inline struct fixed31_32 translate_to_linear_space_ex(
 440        struct fixed31_32 arg,
 441        struct gamma_coefficients *coeff,
 442        uint32_t color_index)
 443{
 444        return translate_to_linear_space(
 445                arg,
 446                coeff->a0[color_index],
 447                coeff->a1[color_index],
 448                coeff->a2[color_index],
 449                coeff->a3[color_index],
 450                coeff->user_gamma[color_index]);
 451}
 452
 453
 454static bool find_software_points(
 455        const struct dc_gamma *ramp,
 456        const struct gamma_pixel *axis_x,
 457        struct fixed31_32 hw_point,
 458        enum channel_name channel,
 459        uint32_t *index_to_start,
 460        uint32_t *index_left,
 461        uint32_t *index_right,
 462        enum hw_point_position *pos)
 463{
 464        const uint32_t max_number = ramp->num_entries + 3;
 465
 466        struct fixed31_32 left, right;
 467
 468        uint32_t i = *index_to_start;
 469
 470        while (i < max_number) {
 471                if (channel == CHANNEL_NAME_RED) {
 472                        left = axis_x[i].r;
 473
 474                        if (i < max_number - 1)
 475                                right = axis_x[i + 1].r;
 476                        else
 477                                right = axis_x[max_number - 1].r;
 478                } else if (channel == CHANNEL_NAME_GREEN) {
 479                        left = axis_x[i].g;
 480
 481                        if (i < max_number - 1)
 482                                right = axis_x[i + 1].g;
 483                        else
 484                                right = axis_x[max_number - 1].g;
 485                } else {
 486                        left = axis_x[i].b;
 487
 488                        if (i < max_number - 1)
 489                                right = axis_x[i + 1].b;
 490                        else
 491                                right = axis_x[max_number - 1].b;
 492                }
 493
 494                if (dc_fixpt_le(left, hw_point) &&
 495                        dc_fixpt_le(hw_point, right)) {
 496                        *index_to_start = i;
 497                        *index_left = i;
 498
 499                        if (i < max_number - 1)
 500                                *index_right = i + 1;
 501                        else
 502                                *index_right = max_number - 1;
 503
 504                        *pos = HW_POINT_POSITION_MIDDLE;
 505
 506                        return true;
 507                } else if ((i == *index_to_start) &&
 508                        dc_fixpt_le(hw_point, left)) {
 509                        *index_to_start = i;
 510                        *index_left = i;
 511                        *index_right = i;
 512
 513                        *pos = HW_POINT_POSITION_LEFT;
 514
 515                        return true;
 516                } else if ((i == max_number - 1) &&
 517                        dc_fixpt_le(right, hw_point)) {
 518                        *index_to_start = i;
 519                        *index_left = i;
 520                        *index_right = i;
 521
 522                        *pos = HW_POINT_POSITION_RIGHT;
 523
 524                        return true;
 525                }
 526
 527                ++i;
 528        }
 529
 530        return false;
 531}
 532
 533static bool build_custom_gamma_mapping_coefficients_worker(
 534        const struct dc_gamma *ramp,
 535        struct pixel_gamma_point *coeff,
 536        const struct hw_x_point *coordinates_x,
 537        const struct gamma_pixel *axis_x,
 538        enum channel_name channel,
 539        uint32_t number_of_points)
 540{
 541        uint32_t i = 0;
 542
 543        while (i <= number_of_points) {
 544                struct fixed31_32 coord_x;
 545
 546                uint32_t index_to_start = 0;
 547                uint32_t index_left = 0;
 548                uint32_t index_right = 0;
 549
 550                enum hw_point_position hw_pos;
 551
 552                struct gamma_point *point;
 553
 554                struct fixed31_32 left_pos;
 555                struct fixed31_32 right_pos;
 556
 557                if (channel == CHANNEL_NAME_RED)
 558                        coord_x = coordinates_x[i].regamma_y_red;
 559                else if (channel == CHANNEL_NAME_GREEN)
 560                        coord_x = coordinates_x[i].regamma_y_green;
 561                else
 562                        coord_x = coordinates_x[i].regamma_y_blue;
 563
 564                if (!find_software_points(
 565                        ramp, axis_x, coord_x, channel,
 566                        &index_to_start, &index_left, &index_right, &hw_pos)) {
 567                        BREAK_TO_DEBUGGER();
 568                        return false;
 569                }
 570
 571                if (index_left >= ramp->num_entries + 3) {
 572                        BREAK_TO_DEBUGGER();
 573                        return false;
 574                }
 575
 576                if (index_right >= ramp->num_entries + 3) {
 577                        BREAK_TO_DEBUGGER();
 578                        return false;
 579                }
 580
 581                if (channel == CHANNEL_NAME_RED) {
 582                        point = &coeff[i].r;
 583
 584                        left_pos = axis_x[index_left].r;
 585                        right_pos = axis_x[index_right].r;
 586                } else if (channel == CHANNEL_NAME_GREEN) {
 587                        point = &coeff[i].g;
 588
 589                        left_pos = axis_x[index_left].g;
 590                        right_pos = axis_x[index_right].g;
 591                } else {
 592                        point = &coeff[i].b;
 593
 594                        left_pos = axis_x[index_left].b;
 595                        right_pos = axis_x[index_right].b;
 596                }
 597
 598                if (hw_pos == HW_POINT_POSITION_MIDDLE)
 599                        point->coeff = dc_fixpt_div(
 600                                dc_fixpt_sub(
 601                                        coord_x,
 602                                        left_pos),
 603                                dc_fixpt_sub(
 604                                        right_pos,
 605                                        left_pos));
 606                else if (hw_pos == HW_POINT_POSITION_LEFT)
 607                        point->coeff = dc_fixpt_zero;
 608                else if (hw_pos == HW_POINT_POSITION_RIGHT)
 609                        point->coeff = dc_fixpt_from_int(2);
 610                else {
 611                        BREAK_TO_DEBUGGER();
 612                        return false;
 613                }
 614
 615                point->left_index = index_left;
 616                point->right_index = index_right;
 617                point->pos = hw_pos;
 618
 619                ++i;
 620        }
 621
 622        return true;
 623}
 624
 625static struct fixed31_32 calculate_mapped_value(
 626        struct pwl_float_data *rgb,
 627        const struct pixel_gamma_point *coeff,
 628        enum channel_name channel,
 629        uint32_t max_index)
 630{
 631        const struct gamma_point *point;
 632
 633        struct fixed31_32 result;
 634
 635        if (channel == CHANNEL_NAME_RED)
 636                point = &coeff->r;
 637        else if (channel == CHANNEL_NAME_GREEN)
 638                point = &coeff->g;
 639        else
 640                point = &coeff->b;
 641
 642        if ((point->left_index < 0) || (point->left_index > max_index)) {
 643                BREAK_TO_DEBUGGER();
 644                return dc_fixpt_zero;
 645        }
 646
 647        if ((point->right_index < 0) || (point->right_index > max_index)) {
 648                BREAK_TO_DEBUGGER();
 649                return dc_fixpt_zero;
 650        }
 651
 652        if (point->pos == HW_POINT_POSITION_MIDDLE)
 653                if (channel == CHANNEL_NAME_RED)
 654                        result = dc_fixpt_add(
 655                                dc_fixpt_mul(
 656                                        point->coeff,
 657                                        dc_fixpt_sub(
 658                                                rgb[point->right_index].r,
 659                                                rgb[point->left_index].r)),
 660                                rgb[point->left_index].r);
 661                else if (channel == CHANNEL_NAME_GREEN)
 662                        result = dc_fixpt_add(
 663                                dc_fixpt_mul(
 664                                        point->coeff,
 665                                        dc_fixpt_sub(
 666                                                rgb[point->right_index].g,
 667                                                rgb[point->left_index].g)),
 668                                rgb[point->left_index].g);
 669                else
 670                        result = dc_fixpt_add(
 671                                dc_fixpt_mul(
 672                                        point->coeff,
 673                                        dc_fixpt_sub(
 674                                                rgb[point->right_index].b,
 675                                                rgb[point->left_index].b)),
 676                                rgb[point->left_index].b);
 677        else if (point->pos == HW_POINT_POSITION_LEFT) {
 678                BREAK_TO_DEBUGGER();
 679                result = dc_fixpt_zero;
 680        } else {
 681                BREAK_TO_DEBUGGER();
 682                result = dc_fixpt_one;
 683        }
 684
 685        return result;
 686}
 687
 688static void build_pq(struct pwl_float_data_ex *rgb_regamma,
 689                uint32_t hw_points_num,
 690                const struct hw_x_point *coordinate_x,
 691                uint32_t sdr_white_level)
 692{
 693        uint32_t i, start_index;
 694
 695        struct pwl_float_data_ex *rgb = rgb_regamma;
 696        const struct hw_x_point *coord_x = coordinate_x;
 697        struct fixed31_32 x;
 698        struct fixed31_32 output;
 699        struct fixed31_32 scaling_factor =
 700                        dc_fixpt_from_fraction(sdr_white_level, 10000);
 701
 702        if (!pq_initialized && sdr_white_level == 80) {
 703                precompute_pq();
 704                pq_initialized = true;
 705        }
 706
 707        /* TODO: start index is from segment 2^-24, skipping first segment
 708         * due to x values too small for power calculations
 709         */
 710        start_index = 32;
 711        rgb += start_index;
 712        coord_x += start_index;
 713
 714        for (i = start_index; i <= hw_points_num; i++) {
 715                /* Multiply 0.008 as regamma is 0-1 and FP16 input is 0-125.
 716                 * FP 1.0 = 80nits
 717                 */
 718                if (sdr_white_level == 80) {
 719                        output = pq_table[i];
 720                } else {
 721                        x = dc_fixpt_mul(coord_x->x, scaling_factor);
 722                        compute_pq(x, &output);
 723                }
 724
 725                /* should really not happen? */
 726                if (dc_fixpt_lt(output, dc_fixpt_zero))
 727                        output = dc_fixpt_zero;
 728                else if (dc_fixpt_lt(dc_fixpt_one, output))
 729                        output = dc_fixpt_one;
 730
 731                rgb->r = output;
 732                rgb->g = output;
 733                rgb->b = output;
 734
 735                ++coord_x;
 736                ++rgb;
 737        }
 738}
 739
 740static void build_de_pq(struct pwl_float_data_ex *de_pq,
 741                uint32_t hw_points_num,
 742                const struct hw_x_point *coordinate_x)
 743{
 744        uint32_t i;
 745        struct fixed31_32 output;
 746
 747        struct fixed31_32 scaling_factor = dc_fixpt_from_int(125);
 748
 749        if (!de_pq_initialized) {
 750                precompute_de_pq();
 751                de_pq_initialized = true;
 752        }
 753
 754
 755        for (i = 0; i <= hw_points_num; i++) {
 756                output = de_pq_table[i];
 757                /* should really not happen? */
 758                if (dc_fixpt_lt(output, dc_fixpt_zero))
 759                        output = dc_fixpt_zero;
 760                else if (dc_fixpt_lt(scaling_factor, output))
 761                        output = scaling_factor;
 762                de_pq[i].r = output;
 763                de_pq[i].g = output;
 764                de_pq[i].b = output;
 765        }
 766}
 767
 768static bool build_regamma(struct pwl_float_data_ex *rgb_regamma,
 769                uint32_t hw_points_num,
 770                const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
 771{
 772        uint32_t i;
 773        bool ret = false;
 774
 775        struct gamma_coefficients *coeff;
 776        struct pwl_float_data_ex *rgb = rgb_regamma;
 777        const struct hw_x_point *coord_x = coordinate_x;
 778
 779        coeff = kvzalloc(sizeof(*coeff), GFP_KERNEL);
 780        if (!coeff)
 781                goto release;
 782
 783        if (!build_coefficients(coeff, type))
 784                goto release;
 785
 786        memset(pow_buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32));
 787        pow_buffer_ptr = 0; // see variable definition for more info
 788        i = 0;
 789        while (i <= hw_points_num) {
 790                /*TODO use y vs r,g,b*/
 791                rgb->r = translate_from_linear_space_ex(
 792                        coord_x->x, coeff, 0);
 793                rgb->g = rgb->r;
 794                rgb->b = rgb->r;
 795                ++coord_x;
 796                ++rgb;
 797                ++i;
 798        }
 799        pow_buffer_ptr = -1; // reset back to no optimize
 800        ret = true;
 801release:
 802        kfree(coeff);
 803        return ret;
 804}
 805
 806static void hermite_spline_eetf(struct fixed31_32 input_x,
 807                                struct fixed31_32 max_display,
 808                                struct fixed31_32 min_display,
 809                                struct fixed31_32 max_content,
 810                                struct fixed31_32 *out_x)
 811{
 812        struct fixed31_32 min_lum_pq;
 813        struct fixed31_32 max_lum_pq;
 814        struct fixed31_32 max_content_pq;
 815        struct fixed31_32 ks;
 816        struct fixed31_32 E1;
 817        struct fixed31_32 E2;
 818        struct fixed31_32 E3;
 819        struct fixed31_32 t;
 820        struct fixed31_32 t2;
 821        struct fixed31_32 t3;
 822        struct fixed31_32 two;
 823        struct fixed31_32 three;
 824        struct fixed31_32 temp1;
 825        struct fixed31_32 temp2;
 826        struct fixed31_32 a = dc_fixpt_from_fraction(15, 10);
 827        struct fixed31_32 b = dc_fixpt_from_fraction(5, 10);
 828        struct fixed31_32 epsilon = dc_fixpt_from_fraction(1, 1000000); // dc_fixpt_epsilon is a bit too small
 829
 830        if (dc_fixpt_eq(max_content, dc_fixpt_zero)) {
 831                *out_x = dc_fixpt_zero;
 832                return;
 833        }
 834
 835        compute_pq(input_x, &E1);
 836        compute_pq(dc_fixpt_div(min_display, max_content), &min_lum_pq);
 837        compute_pq(dc_fixpt_div(max_display, max_content), &max_lum_pq);
 838        compute_pq(dc_fixpt_one, &max_content_pq); // always 1? DAL2 code is weird
 839        a = dc_fixpt_div(dc_fixpt_add(dc_fixpt_one, b), max_content_pq); // (1+b)/maxContent
 840        ks = dc_fixpt_sub(dc_fixpt_mul(a, max_lum_pq), b); // a * max_lum_pq - b
 841
 842        if (dc_fixpt_lt(E1, ks))
 843                E2 = E1;
 844        else if (dc_fixpt_le(ks, E1) && dc_fixpt_le(E1, dc_fixpt_one)) {
 845                if (dc_fixpt_lt(epsilon, dc_fixpt_sub(dc_fixpt_one, ks)))
 846                        // t = (E1 - ks) / (1 - ks)
 847                        t = dc_fixpt_div(dc_fixpt_sub(E1, ks),
 848                                        dc_fixpt_sub(dc_fixpt_one, ks));
 849                else
 850                        t = dc_fixpt_zero;
 851
 852                two = dc_fixpt_from_int(2);
 853                three = dc_fixpt_from_int(3);
 854
 855                t2 = dc_fixpt_mul(t, t);
 856                t3 = dc_fixpt_mul(t2, t);
 857                temp1 = dc_fixpt_mul(two, t3);
 858                temp2 = dc_fixpt_mul(three, t2);
 859
 860                // (2t^3 - 3t^2 + 1) * ks
 861                E2 = dc_fixpt_mul(ks, dc_fixpt_add(dc_fixpt_one,
 862                                dc_fixpt_sub(temp1, temp2)));
 863
 864                // (-2t^3 + 3t^2) * max_lum_pq
 865                E2 = dc_fixpt_add(E2, dc_fixpt_mul(max_lum_pq,
 866                                dc_fixpt_sub(temp2, temp1)));
 867
 868                temp1 = dc_fixpt_mul(two, t2);
 869                temp2 = dc_fixpt_sub(dc_fixpt_one, ks);
 870
 871                // (t^3 - 2t^2 + t) * (1-ks)
 872                E2 = dc_fixpt_add(E2, dc_fixpt_mul(temp2,
 873                                dc_fixpt_add(t, dc_fixpt_sub(t3, temp1))));
 874        } else
 875                E2 = dc_fixpt_one;
 876
 877        temp1 = dc_fixpt_sub(dc_fixpt_one, E2);
 878        temp2 = dc_fixpt_mul(temp1, temp1);
 879        temp2 = dc_fixpt_mul(temp2, temp2);
 880        // temp2 = (1-E2)^4
 881
 882        E3 =  dc_fixpt_add(E2, dc_fixpt_mul(min_lum_pq, temp2));
 883        compute_de_pq(E3, out_x);
 884
 885        *out_x = dc_fixpt_div(*out_x, dc_fixpt_div(max_display, max_content));
 886}
 887
 888static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma,
 889                uint32_t hw_points_num,
 890                const struct hw_x_point *coordinate_x,
 891                const struct freesync_hdr_tf_params *fs_params)
 892{
 893        uint32_t i;
 894        struct pwl_float_data_ex *rgb = rgb_regamma;
 895        const struct hw_x_point *coord_x = coordinate_x;
 896        struct fixed31_32 scaledX = dc_fixpt_zero;
 897        struct fixed31_32 scaledX1 = dc_fixpt_zero;
 898        struct fixed31_32 max_display;
 899        struct fixed31_32 min_display;
 900        struct fixed31_32 max_content;
 901        struct fixed31_32 min_content;
 902        struct fixed31_32 clip = dc_fixpt_one;
 903        struct fixed31_32 output;
 904        bool use_eetf = false;
 905        bool is_clipped = false;
 906        struct fixed31_32 sdr_white_level;
 907
 908        if (fs_params->max_content == 0 ||
 909                        fs_params->max_display == 0)
 910                return false;
 911
 912        max_display = dc_fixpt_from_int(fs_params->max_display);
 913        min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000);
 914        max_content = dc_fixpt_from_int(fs_params->max_content);
 915        min_content = dc_fixpt_from_fraction(fs_params->min_content, 10000);
 916        sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level);
 917
 918        if (fs_params->min_display > 1000) // cap at 0.1 at the bottom
 919                min_display = dc_fixpt_from_fraction(1, 10);
 920        if (fs_params->max_display < 100) // cap at 100 at the top
 921                max_display = dc_fixpt_from_int(100);
 922
 923        if (fs_params->min_content < fs_params->min_display)
 924                use_eetf = true;
 925        else
 926                min_content = min_display;
 927
 928        if (fs_params->max_content > fs_params->max_display)
 929                use_eetf = true;
 930        else
 931                max_content = max_display;
 932
 933        if (!use_eetf)
 934                pow_buffer_ptr = 0; // see var definition for more info
 935        rgb += 32; // first 32 points have problems with fixed point, too small
 936        coord_x += 32;
 937        for (i = 32; i <= hw_points_num; i++) {
 938                if (!is_clipped) {
 939                        if (use_eetf) {
 940                                /*max content is equal 1 */
 941                                scaledX1 = dc_fixpt_div(coord_x->x,
 942                                                dc_fixpt_div(max_content, sdr_white_level));
 943                                hermite_spline_eetf(scaledX1, max_display, min_display,
 944                                                max_content, &scaledX);
 945                        } else
 946                                scaledX = dc_fixpt_div(coord_x->x,
 947                                                dc_fixpt_div(max_display, sdr_white_level));
 948
 949                        if (dc_fixpt_lt(scaledX, clip)) {
 950                                if (dc_fixpt_lt(scaledX, dc_fixpt_zero))
 951                                        output = dc_fixpt_zero;
 952                                else
 953                                        output = calculate_gamma22(scaledX);
 954
 955                                rgb->r = output;
 956                                rgb->g = output;
 957                                rgb->b = output;
 958                        } else {
 959                                is_clipped = true;
 960                                rgb->r = clip;
 961                                rgb->g = clip;
 962                                rgb->b = clip;
 963                        }
 964                } else {
 965                        rgb->r = clip;
 966                        rgb->g = clip;
 967                        rgb->b = clip;
 968                }
 969
 970                ++coord_x;
 971                ++rgb;
 972        }
 973        pow_buffer_ptr = -1;
 974
 975        return true;
 976}
 977
 978static bool build_degamma(struct pwl_float_data_ex *curve,
 979                uint32_t hw_points_num,
 980                const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type)
 981{
 982        uint32_t i;
 983        struct gamma_coefficients coeff;
 984        uint32_t begin_index, end_index;
 985        bool ret = false;
 986
 987        if (!build_coefficients(&coeff, type))
 988                goto release;
 989
 990        i = 0;
 991
 992        /* X points is 2^-25 to 2^7
 993         * De-gamma X is 2^-12 to 2^0 – we are skipping first -12-(-25) = 13 regions
 994         */
 995        begin_index = 13 * NUM_PTS_IN_REGION;
 996        end_index = begin_index + 12 * NUM_PTS_IN_REGION;
 997
 998        while (i != begin_index) {
 999                curve[i].r = dc_fixpt_zero;
1000                curve[i].g = dc_fixpt_zero;
1001                curve[i].b = dc_fixpt_zero;
1002                i++;
1003        }
1004
1005        while (i != end_index) {
1006                curve[i].r = translate_to_linear_space_ex(
1007                                coordinate_x[i].x, &coeff, 0);
1008                curve[i].g = curve[i].r;
1009                curve[i].b = curve[i].r;
1010                i++;
1011        }
1012        while (i != hw_points_num + 1) {
1013                curve[i].r = dc_fixpt_one;
1014                curve[i].g = dc_fixpt_one;
1015                curve[i].b = dc_fixpt_one;
1016                i++;
1017        }
1018        ret = true;
1019release:
1020        return ret;
1021}
1022
1023
1024
1025
1026
1027static void build_hlg_degamma(struct pwl_float_data_ex *degamma,
1028                uint32_t hw_points_num,
1029                const struct hw_x_point *coordinate_x,
1030                uint32_t sdr_white_level, uint32_t max_luminance_nits)
1031{
1032        uint32_t i;
1033
1034        struct pwl_float_data_ex *rgb = degamma;
1035        const struct hw_x_point *coord_x = coordinate_x;
1036
1037        i = 0;
1038        //check when i == 434
1039        while (i != hw_points_num + 1) {
1040                compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1041                rgb->g = rgb->r;
1042                rgb->b = rgb->r;
1043                ++coord_x;
1044                ++rgb;
1045                ++i;
1046        }
1047}
1048
1049
1050static void build_hlg_regamma(struct pwl_float_data_ex *regamma,
1051                uint32_t hw_points_num,
1052                const struct hw_x_point *coordinate_x,
1053                uint32_t sdr_white_level, uint32_t max_luminance_nits)
1054{
1055        uint32_t i;
1056
1057        struct pwl_float_data_ex *rgb = regamma;
1058        const struct hw_x_point *coord_x = coordinate_x;
1059
1060        i = 0;
1061
1062        //when i == 471
1063        while (i != hw_points_num + 1) {
1064                compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits);
1065                rgb->g = rgb->r;
1066                rgb->b = rgb->r;
1067                ++coord_x;
1068                ++rgb;
1069                ++i;
1070        }
1071}
1072
1073static void scale_gamma(struct pwl_float_data *pwl_rgb,
1074                const struct dc_gamma *ramp,
1075                struct dividers dividers)
1076{
1077        const struct fixed31_32 max_driver = dc_fixpt_from_int(0xFFFF);
1078        const struct fixed31_32 max_os = dc_fixpt_from_int(0xFF00);
1079        struct fixed31_32 scaler = max_os;
1080        uint32_t i;
1081        struct pwl_float_data *rgb = pwl_rgb;
1082        struct pwl_float_data *rgb_last = rgb + ramp->num_entries - 1;
1083
1084        i = 0;
1085
1086        do {
1087                if (dc_fixpt_lt(max_os, ramp->entries.red[i]) ||
1088                        dc_fixpt_lt(max_os, ramp->entries.green[i]) ||
1089                        dc_fixpt_lt(max_os, ramp->entries.blue[i])) {
1090                        scaler = max_driver;
1091                        break;
1092                }
1093                ++i;
1094        } while (i != ramp->num_entries);
1095
1096        i = 0;
1097
1098        do {
1099                rgb->r = dc_fixpt_div(
1100                        ramp->entries.red[i], scaler);
1101                rgb->g = dc_fixpt_div(
1102                        ramp->entries.green[i], scaler);
1103                rgb->b = dc_fixpt_div(
1104                        ramp->entries.blue[i], scaler);
1105
1106                ++rgb;
1107                ++i;
1108        } while (i != ramp->num_entries);
1109
1110        rgb->r = dc_fixpt_mul(rgb_last->r,
1111                        dividers.divider1);
1112        rgb->g = dc_fixpt_mul(rgb_last->g,
1113                        dividers.divider1);
1114        rgb->b = dc_fixpt_mul(rgb_last->b,
1115                        dividers.divider1);
1116
1117        ++rgb;
1118
1119        rgb->r = dc_fixpt_mul(rgb_last->r,
1120                        dividers.divider2);
1121        rgb->g = dc_fixpt_mul(rgb_last->g,
1122                        dividers.divider2);
1123        rgb->b = dc_fixpt_mul(rgb_last->b,
1124                        dividers.divider2);
1125
1126        ++rgb;
1127
1128        rgb->r = dc_fixpt_mul(rgb_last->r,
1129                        dividers.divider3);
1130        rgb->g = dc_fixpt_mul(rgb_last->g,
1131                        dividers.divider3);
1132        rgb->b = dc_fixpt_mul(rgb_last->b,
1133                        dividers.divider3);
1134}
1135
1136static void scale_gamma_dx(struct pwl_float_data *pwl_rgb,
1137                const struct dc_gamma *ramp,
1138                struct dividers dividers)
1139{
1140        uint32_t i;
1141        struct fixed31_32 min = dc_fixpt_zero;
1142        struct fixed31_32 max = dc_fixpt_one;
1143
1144        struct fixed31_32 delta = dc_fixpt_zero;
1145        struct fixed31_32 offset = dc_fixpt_zero;
1146
1147        for (i = 0 ; i < ramp->num_entries; i++) {
1148                if (dc_fixpt_lt(ramp->entries.red[i], min))
1149                        min = ramp->entries.red[i];
1150
1151                if (dc_fixpt_lt(ramp->entries.green[i], min))
1152                        min = ramp->entries.green[i];
1153
1154                if (dc_fixpt_lt(ramp->entries.blue[i], min))
1155                        min = ramp->entries.blue[i];
1156
1157                if (dc_fixpt_lt(max, ramp->entries.red[i]))
1158                        max = ramp->entries.red[i];
1159
1160                if (dc_fixpt_lt(max, ramp->entries.green[i]))
1161                        max = ramp->entries.green[i];
1162
1163                if (dc_fixpt_lt(max, ramp->entries.blue[i]))
1164                        max = ramp->entries.blue[i];
1165        }
1166
1167        if (dc_fixpt_lt(min, dc_fixpt_zero))
1168                delta = dc_fixpt_neg(min);
1169
1170        offset = dc_fixpt_add(min, max);
1171
1172        for (i = 0 ; i < ramp->num_entries; i++) {
1173                pwl_rgb[i].r = dc_fixpt_div(
1174                        dc_fixpt_add(
1175                                ramp->entries.red[i], delta), offset);
1176                pwl_rgb[i].g = dc_fixpt_div(
1177                        dc_fixpt_add(
1178                                ramp->entries.green[i], delta), offset);
1179                pwl_rgb[i].b = dc_fixpt_div(
1180                        dc_fixpt_add(
1181                                ramp->entries.blue[i], delta), offset);
1182
1183        }
1184
1185        pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
1186                                pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1187        pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
1188                                pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1189        pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
1190                                pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1191        ++i;
1192        pwl_rgb[i].r =  dc_fixpt_sub(dc_fixpt_mul_int(
1193                                pwl_rgb[i-1].r, 2), pwl_rgb[i-2].r);
1194        pwl_rgb[i].g =  dc_fixpt_sub(dc_fixpt_mul_int(
1195                                pwl_rgb[i-1].g, 2), pwl_rgb[i-2].g);
1196        pwl_rgb[i].b =  dc_fixpt_sub(dc_fixpt_mul_int(
1197                                pwl_rgb[i-1].b, 2), pwl_rgb[i-2].b);
1198}
1199
1200/* todo: all these scale_gamma functions are inherently the same but
1201 *  take different structures as params or different format for ramp
1202 *  values. We could probably implement it in a more generic fashion
1203 */
1204static void scale_user_regamma_ramp(struct pwl_float_data *pwl_rgb,
1205                const struct regamma_ramp *ramp,
1206                struct dividers dividers)
1207{
1208        unsigned short max_driver = 0xFFFF;
1209        unsigned short max_os = 0xFF00;
1210        unsigned short scaler = max_os;
1211        uint32_t i;
1212        struct pwl_float_data *rgb = pwl_rgb;
1213        struct pwl_float_data *rgb_last = rgb + GAMMA_RGB_256_ENTRIES - 1;
1214
1215        i = 0;
1216        do {
1217                if (ramp->gamma[i] > max_os ||
1218                                ramp->gamma[i + 256] > max_os ||
1219                                ramp->gamma[i + 512] > max_os) {
1220                        scaler = max_driver;
1221                        break;
1222                }
1223                i++;
1224        } while (i != GAMMA_RGB_256_ENTRIES);
1225
1226        i = 0;
1227        do {
1228                rgb->r = dc_fixpt_from_fraction(
1229                                ramp->gamma[i], scaler);
1230                rgb->g = dc_fixpt_from_fraction(
1231                                ramp->gamma[i + 256], scaler);
1232                rgb->b = dc_fixpt_from_fraction(
1233                                ramp->gamma[i + 512], scaler);
1234
1235                ++rgb;
1236                ++i;
1237        } while (i != GAMMA_RGB_256_ENTRIES);
1238
1239        rgb->r = dc_fixpt_mul(rgb_last->r,
1240                        dividers.divider1);
1241        rgb->g = dc_fixpt_mul(rgb_last->g,
1242                        dividers.divider1);
1243        rgb->b = dc_fixpt_mul(rgb_last->b,
1244                        dividers.divider1);
1245
1246        ++rgb;
1247
1248        rgb->r = dc_fixpt_mul(rgb_last->r,
1249                        dividers.divider2);
1250        rgb->g = dc_fixpt_mul(rgb_last->g,
1251                        dividers.divider2);
1252        rgb->b = dc_fixpt_mul(rgb_last->b,
1253                        dividers.divider2);
1254
1255        ++rgb;
1256
1257        rgb->r = dc_fixpt_mul(rgb_last->r,
1258                        dividers.divider3);
1259        rgb->g = dc_fixpt_mul(rgb_last->g,
1260                        dividers.divider3);
1261        rgb->b = dc_fixpt_mul(rgb_last->b,
1262                        dividers.divider3);
1263}
1264
1265/*
1266 * RS3+ color transform DDI - 1D LUT adjustment is composed with regamma here
1267 * Input is evenly distributed in the output color space as specified in
1268 * SetTimings
1269 *
1270 * Interpolation details:
1271 * 1D LUT has 4096 values which give curve correction in 0-1 float range
1272 * for evenly spaced points in 0-1 range. lut1D[index] gives correction
1273 * for index/4095.
1274 * First we find index for which:
1275 *      index/4095 < regamma_y < (index+1)/4095 =>
1276 *      index < 4095*regamma_y < index + 1
1277 * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
1278 * lut1 = lut1D[index], lut2 = lut1D[index+1]
1279 *
1280 * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
1281 *
1282 * Custom degamma on Linux uses the same interpolation math, so is handled here
1283 */
1284static void apply_lut_1d(
1285                const struct dc_gamma *ramp,
1286                uint32_t num_hw_points,
1287                struct dc_transfer_func_distributed_points *tf_pts)
1288{
1289        int i = 0;
1290        int color = 0;
1291        struct fixed31_32 *regamma_y;
1292        struct fixed31_32 norm_y;
1293        struct fixed31_32 lut1;
1294        struct fixed31_32 lut2;
1295        const int max_lut_index = 4095;
1296        const struct fixed31_32 max_lut_index_f =
1297                        dc_fixpt_from_int(max_lut_index);
1298        int32_t index = 0, index_next = 0;
1299        struct fixed31_32 index_f;
1300        struct fixed31_32 delta_lut;
1301        struct fixed31_32 delta_index;
1302
1303        if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
1304                return; // this is not expected
1305
1306        for (i = 0; i < num_hw_points; i++) {
1307                for (color = 0; color < 3; color++) {
1308                        if (color == 0)
1309                                regamma_y = &tf_pts->red[i];
1310                        else if (color == 1)
1311                                regamma_y = &tf_pts->green[i];
1312                        else
1313                                regamma_y = &tf_pts->blue[i];
1314
1315                        norm_y = dc_fixpt_mul(max_lut_index_f,
1316                                                   *regamma_y);
1317                        index = dc_fixpt_floor(norm_y);
1318                        index_f = dc_fixpt_from_int(index);
1319
1320                        if (index < 0 || index > max_lut_index)
1321                                continue;
1322
1323                        index_next = (index == max_lut_index) ? index : index+1;
1324
1325                        if (color == 0) {
1326                                lut1 = ramp->entries.red[index];
1327                                lut2 = ramp->entries.red[index_next];
1328                        } else if (color == 1) {
1329                                lut1 = ramp->entries.green[index];
1330                                lut2 = ramp->entries.green[index_next];
1331                        } else {
1332                                lut1 = ramp->entries.blue[index];
1333                                lut2 = ramp->entries.blue[index_next];
1334                        }
1335
1336                        // we have everything now, so interpolate
1337                        delta_lut = dc_fixpt_sub(lut2, lut1);
1338                        delta_index = dc_fixpt_sub(norm_y, index_f);
1339
1340                        *regamma_y = dc_fixpt_add(lut1,
1341                                dc_fixpt_mul(delta_index, delta_lut));
1342                }
1343        }
1344}
1345
1346static void build_evenly_distributed_points(
1347        struct gamma_pixel *points,
1348        uint32_t numberof_points,
1349        struct dividers dividers)
1350{
1351        struct gamma_pixel *p = points;
1352        struct gamma_pixel *p_last;
1353
1354        uint32_t i = 0;
1355
1356        // This function should not gets called with 0 as a parameter
1357        ASSERT(numberof_points > 0);
1358        p_last = p + numberof_points - 1;
1359
1360        do {
1361                struct fixed31_32 value = dc_fixpt_from_fraction(i,
1362                        numberof_points - 1);
1363
1364                p->r = value;
1365                p->g = value;
1366                p->b = value;
1367
1368                ++p;
1369                ++i;
1370        } while (i < numberof_points);
1371
1372        p->r = dc_fixpt_div(p_last->r, dividers.divider1);
1373        p->g = dc_fixpt_div(p_last->g, dividers.divider1);
1374        p->b = dc_fixpt_div(p_last->b, dividers.divider1);
1375
1376        ++p;
1377
1378        p->r = dc_fixpt_div(p_last->r, dividers.divider2);
1379        p->g = dc_fixpt_div(p_last->g, dividers.divider2);
1380        p->b = dc_fixpt_div(p_last->b, dividers.divider2);
1381
1382        ++p;
1383
1384        p->r = dc_fixpt_div(p_last->r, dividers.divider3);
1385        p->g = dc_fixpt_div(p_last->g, dividers.divider3);
1386        p->b = dc_fixpt_div(p_last->b, dividers.divider3);
1387}
1388
1389static inline void copy_rgb_regamma_to_coordinates_x(
1390                struct hw_x_point *coordinates_x,
1391                uint32_t hw_points_num,
1392                const struct pwl_float_data_ex *rgb_ex)
1393{
1394        struct hw_x_point *coords = coordinates_x;
1395        uint32_t i = 0;
1396        const struct pwl_float_data_ex *rgb_regamma = rgb_ex;
1397
1398        while (i <= hw_points_num + 1) {
1399                coords->regamma_y_red = rgb_regamma->r;
1400                coords->regamma_y_green = rgb_regamma->g;
1401                coords->regamma_y_blue = rgb_regamma->b;
1402
1403                ++coords;
1404                ++rgb_regamma;
1405                ++i;
1406        }
1407}
1408
1409static bool calculate_interpolated_hardware_curve(
1410        const struct dc_gamma *ramp,
1411        struct pixel_gamma_point *coeff128,
1412        struct pwl_float_data *rgb_user,
1413        const struct hw_x_point *coordinates_x,
1414        const struct gamma_pixel *axis_x,
1415        uint32_t number_of_points,
1416        struct dc_transfer_func_distributed_points *tf_pts)
1417{
1418
1419        const struct pixel_gamma_point *coeff = coeff128;
1420        uint32_t max_entries = 3 - 1;
1421
1422        uint32_t i = 0;
1423
1424        for (i = 0; i < 3; i++) {
1425                if (!build_custom_gamma_mapping_coefficients_worker(
1426                                ramp, coeff128, coordinates_x, axis_x, i,
1427                                number_of_points))
1428                        return false;
1429        }
1430
1431        i = 0;
1432        max_entries += ramp->num_entries;
1433
1434        /* TODO: float point case */
1435
1436        while (i <= number_of_points) {
1437                tf_pts->red[i] = calculate_mapped_value(
1438                        rgb_user, coeff, CHANNEL_NAME_RED, max_entries);
1439                tf_pts->green[i] = calculate_mapped_value(
1440                        rgb_user, coeff, CHANNEL_NAME_GREEN, max_entries);
1441                tf_pts->blue[i] = calculate_mapped_value(
1442                        rgb_user, coeff, CHANNEL_NAME_BLUE, max_entries);
1443
1444                ++coeff;
1445                ++i;
1446        }
1447
1448        return true;
1449}
1450
1451/* The "old" interpolation uses a complicated scheme to build an array of
1452 * coefficients while also using an array of 0-255 normalized to 0-1
1453 * Then there's another loop using both of the above + new scaled user ramp
1454 * and we concatenate them. It also searches for points of interpolation and
1455 * uses enums for positions.
1456 *
1457 * This function uses a different approach:
1458 * user ramp is always applied on X with 0/255, 1/255, 2/255, ..., 255/255
1459 * To find index for hwX , we notice the following:
1460 * i/255 <= hwX < (i+1)/255  <=> i <= 255*hwX < i+1
1461 * See apply_lut_1d which is the same principle, but on 4K entry 1D LUT
1462 *
1463 * Once the index is known, combined Y is simply:
1464 * user_ramp(index) + (hwX-index/255)*(user_ramp(index+1) - user_ramp(index)
1465 *
1466 * We should switch to this method in all cases, it's simpler and faster
1467 * ToDo one day - for now this only applies to ADL regamma to avoid regression
1468 * for regular use cases (sRGB and PQ)
1469 */
1470static void interpolate_user_regamma(uint32_t hw_points_num,
1471                struct pwl_float_data *rgb_user,
1472                bool apply_degamma,
1473                struct dc_transfer_func_distributed_points *tf_pts)
1474{
1475        uint32_t i;
1476        uint32_t color = 0;
1477        int32_t index;
1478        int32_t index_next;
1479        struct fixed31_32 *tf_point;
1480        struct fixed31_32 hw_x;
1481        struct fixed31_32 norm_factor =
1482                        dc_fixpt_from_int(255);
1483        struct fixed31_32 norm_x;
1484        struct fixed31_32 index_f;
1485        struct fixed31_32 lut1;
1486        struct fixed31_32 lut2;
1487        struct fixed31_32 delta_lut;
1488        struct fixed31_32 delta_index;
1489
1490        i = 0;
1491        /* fixed_pt library has problems handling too small values */
1492        while (i != 32) {
1493                tf_pts->red[i] = dc_fixpt_zero;
1494                tf_pts->green[i] = dc_fixpt_zero;
1495                tf_pts->blue[i] = dc_fixpt_zero;
1496                ++i;
1497        }
1498        while (i <= hw_points_num + 1) {
1499                for (color = 0; color < 3; color++) {
1500                        if (color == 0)
1501                                tf_point = &tf_pts->red[i];
1502                        else if (color == 1)
1503                                tf_point = &tf_pts->green[i];
1504                        else
1505                                tf_point = &tf_pts->blue[i];
1506
1507                        if (apply_degamma) {
1508                                if (color == 0)
1509                                        hw_x = coordinates_x[i].regamma_y_red;
1510                                else if (color == 1)
1511                                        hw_x = coordinates_x[i].regamma_y_green;
1512                                else
1513                                        hw_x = coordinates_x[i].regamma_y_blue;
1514                        } else
1515                                hw_x = coordinates_x[i].x;
1516
1517                        norm_x = dc_fixpt_mul(norm_factor, hw_x);
1518                        index = dc_fixpt_floor(norm_x);
1519                        if (index < 0 || index > 255)
1520                                continue;
1521
1522                        index_f = dc_fixpt_from_int(index);
1523                        index_next = (index == 255) ? index : index + 1;
1524
1525                        if (color == 0) {
1526                                lut1 = rgb_user[index].r;
1527                                lut2 = rgb_user[index_next].r;
1528                        } else if (color == 1) {
1529                                lut1 = rgb_user[index].g;
1530                                lut2 = rgb_user[index_next].g;
1531                        } else {
1532                                lut1 = rgb_user[index].b;
1533                                lut2 = rgb_user[index_next].b;
1534                        }
1535
1536                        // we have everything now, so interpolate
1537                        delta_lut = dc_fixpt_sub(lut2, lut1);
1538                        delta_index = dc_fixpt_sub(norm_x, index_f);
1539
1540                        *tf_point = dc_fixpt_add(lut1,
1541                                dc_fixpt_mul(delta_index, delta_lut));
1542                }
1543                ++i;
1544        }
1545}
1546
1547static void build_new_custom_resulted_curve(
1548        uint32_t hw_points_num,
1549        struct dc_transfer_func_distributed_points *tf_pts)
1550{
1551        uint32_t i;
1552
1553        i = 0;
1554
1555        while (i != hw_points_num + 1) {
1556                tf_pts->red[i] = dc_fixpt_clamp(
1557                        tf_pts->red[i], dc_fixpt_zero,
1558                        dc_fixpt_one);
1559                tf_pts->green[i] = dc_fixpt_clamp(
1560                        tf_pts->green[i], dc_fixpt_zero,
1561                        dc_fixpt_one);
1562                tf_pts->blue[i] = dc_fixpt_clamp(
1563                        tf_pts->blue[i], dc_fixpt_zero,
1564                        dc_fixpt_one);
1565
1566                ++i;
1567        }
1568}
1569
1570static void apply_degamma_for_user_regamma(struct pwl_float_data_ex *rgb_regamma,
1571                uint32_t hw_points_num)
1572{
1573        uint32_t i;
1574
1575        struct gamma_coefficients coeff;
1576        struct pwl_float_data_ex *rgb = rgb_regamma;
1577        const struct hw_x_point *coord_x = coordinates_x;
1578
1579        build_coefficients(&coeff, true);
1580
1581        i = 0;
1582        while (i != hw_points_num + 1) {
1583                rgb->r = translate_from_linear_space_ex(
1584                                coord_x->x, &coeff, 0);
1585                rgb->g = rgb->r;
1586                rgb->b = rgb->r;
1587                ++coord_x;
1588                ++rgb;
1589                ++i;
1590        }
1591}
1592
1593static bool map_regamma_hw_to_x_user(
1594        const struct dc_gamma *ramp,
1595        struct pixel_gamma_point *coeff128,
1596        struct pwl_float_data *rgb_user,
1597        struct hw_x_point *coords_x,
1598        const struct gamma_pixel *axis_x,
1599        const struct pwl_float_data_ex *rgb_regamma,
1600        uint32_t hw_points_num,
1601        struct dc_transfer_func_distributed_points *tf_pts,
1602        bool mapUserRamp)
1603{
1604        /* setup to spare calculated ideal regamma values */
1605
1606        int i = 0;
1607        struct hw_x_point *coords = coords_x;
1608        const struct pwl_float_data_ex *regamma = rgb_regamma;
1609
1610        if (ramp && mapUserRamp) {
1611                copy_rgb_regamma_to_coordinates_x(coords,
1612                                hw_points_num,
1613                                rgb_regamma);
1614
1615                calculate_interpolated_hardware_curve(
1616                        ramp, coeff128, rgb_user, coords, axis_x,
1617                        hw_points_num, tf_pts);
1618        } else {
1619                /* just copy current rgb_regamma into  tf_pts */
1620                while (i <= hw_points_num) {
1621                        tf_pts->red[i] = regamma->r;
1622                        tf_pts->green[i] = regamma->g;
1623                        tf_pts->blue[i] = regamma->b;
1624
1625                        ++regamma;
1626                        ++i;
1627                }
1628        }
1629
1630        /* this should be named differently, all it does is clamp to 0-1 */
1631        build_new_custom_resulted_curve(hw_points_num, tf_pts);
1632
1633        return true;
1634}
1635
1636#define _EXTRA_POINTS 3
1637
1638bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
1639                const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
1640                const struct freesync_hdr_tf_params *fs_params)
1641{
1642        struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1643        struct dividers dividers;
1644
1645        struct pwl_float_data *rgb_user = NULL;
1646        struct pwl_float_data_ex *rgb_regamma = NULL;
1647        struct gamma_pixel *axis_x = NULL;
1648        struct pixel_gamma_point *coeff = NULL;
1649        enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1650        bool ret = false;
1651
1652        if (output_tf->type == TF_TYPE_BYPASS)
1653                return false;
1654
1655        /* we can use hardcoded curve for plain SRGB TF */
1656        if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true &&
1657                        output_tf->tf == TRANSFER_FUNCTION_SRGB) {
1658                if (ramp == NULL)
1659                        return true;
1660                if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) ||
1661                                (!mapUserRamp && ramp->type == GAMMA_RGB_256))
1662                        return true;
1663        }
1664
1665        output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1666
1667        if (ramp && ramp->type != GAMMA_CS_TFM_1D &&
1668                        (mapUserRamp || ramp->type != GAMMA_RGB_256)) {
1669                rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1670                            sizeof(*rgb_user),
1671                            GFP_KERNEL);
1672                if (!rgb_user)
1673                        goto rgb_user_alloc_fail;
1674
1675                axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x),
1676                                GFP_KERNEL);
1677                if (!axis_x)
1678                        goto axis_x_alloc_fail;
1679
1680                dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1681                dividers.divider2 = dc_fixpt_from_int(2);
1682                dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1683
1684                build_evenly_distributed_points(
1685                                axis_x,
1686                                ramp->num_entries,
1687                                dividers);
1688
1689                if (ramp->type == GAMMA_RGB_256 && mapUserRamp)
1690                        scale_gamma(rgb_user, ramp, dividers);
1691                else if (ramp->type == GAMMA_RGB_FLOAT_1024)
1692                        scale_gamma_dx(rgb_user, ramp, dividers);
1693        }
1694
1695        rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1696                               sizeof(*rgb_regamma),
1697                               GFP_KERNEL);
1698        if (!rgb_regamma)
1699                goto rgb_regamma_alloc_fail;
1700
1701        coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1702                         GFP_KERNEL);
1703        if (!coeff)
1704                goto coeff_alloc_fail;
1705
1706        tf = output_tf->tf;
1707        if (tf == TRANSFER_FUNCTION_PQ) {
1708                tf_pts->end_exponent = 7;
1709                tf_pts->x_point_at_y1_red = 125;
1710                tf_pts->x_point_at_y1_green = 125;
1711                tf_pts->x_point_at_y1_blue = 125;
1712
1713                build_pq(rgb_regamma,
1714                                MAX_HW_POINTS,
1715                                coordinates_x,
1716                                output_tf->sdr_ref_white_level);
1717        } else if (tf == TRANSFER_FUNCTION_GAMMA22 &&
1718                        fs_params != NULL && fs_params->skip_tm == 0) {
1719                build_freesync_hdr(rgb_regamma,
1720                                MAX_HW_POINTS,
1721                                coordinates_x,
1722                                fs_params);
1723        } else if (tf == TRANSFER_FUNCTION_HLG) {
1724                build_freesync_hdr(rgb_regamma,
1725                                MAX_HW_POINTS,
1726                                coordinates_x,
1727                                fs_params);
1728
1729        } else {
1730                tf_pts->end_exponent = 0;
1731                tf_pts->x_point_at_y1_red = 1;
1732                tf_pts->x_point_at_y1_green = 1;
1733                tf_pts->x_point_at_y1_blue = 1;
1734
1735                build_regamma(rgb_regamma,
1736                                MAX_HW_POINTS,
1737                                coordinates_x, tf);
1738        }
1739        map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1740                        coordinates_x, axis_x, rgb_regamma,
1741                        MAX_HW_POINTS, tf_pts,
1742                        (mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) &&
1743                        (ramp && ramp->type != GAMMA_CS_TFM_1D));
1744
1745        if (ramp && ramp->type == GAMMA_CS_TFM_1D)
1746                apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1747
1748        ret = true;
1749
1750        kvfree(coeff);
1751coeff_alloc_fail:
1752        kvfree(rgb_regamma);
1753rgb_regamma_alloc_fail:
1754        kvfree(axis_x);
1755axis_x_alloc_fail:
1756        kvfree(rgb_user);
1757rgb_user_alloc_fail:
1758        return ret;
1759}
1760
1761bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf,
1762                const struct regamma_lut *regamma)
1763{
1764        struct gamma_coefficients coeff;
1765        const struct hw_x_point *coord_x = coordinates_x;
1766        uint32_t i = 0;
1767
1768        do {
1769                coeff.a0[i] = dc_fixpt_from_fraction(
1770                                regamma->coeff.A0[i], 10000000);
1771                coeff.a1[i] = dc_fixpt_from_fraction(
1772                                regamma->coeff.A1[i], 1000);
1773                coeff.a2[i] = dc_fixpt_from_fraction(
1774                                regamma->coeff.A2[i], 1000);
1775                coeff.a3[i] = dc_fixpt_from_fraction(
1776                                regamma->coeff.A3[i], 1000);
1777                coeff.user_gamma[i] = dc_fixpt_from_fraction(
1778                                regamma->coeff.gamma[i], 1000);
1779
1780                ++i;
1781        } while (i != 3);
1782
1783        i = 0;
1784        /* fixed_pt library has problems handling too small values */
1785        while (i != 32) {
1786                output_tf->tf_pts.red[i] = dc_fixpt_zero;
1787                output_tf->tf_pts.green[i] = dc_fixpt_zero;
1788                output_tf->tf_pts.blue[i] = dc_fixpt_zero;
1789                ++coord_x;
1790                ++i;
1791        }
1792        while (i != MAX_HW_POINTS + 1) {
1793                output_tf->tf_pts.red[i] = translate_from_linear_space_ex(
1794                                coord_x->x, &coeff, 0);
1795                output_tf->tf_pts.green[i] = translate_from_linear_space_ex(
1796                                coord_x->x, &coeff, 1);
1797                output_tf->tf_pts.blue[i] = translate_from_linear_space_ex(
1798                                coord_x->x, &coeff, 2);
1799                ++coord_x;
1800                ++i;
1801        }
1802
1803        // this function just clamps output to 0-1
1804        build_new_custom_resulted_curve(MAX_HW_POINTS, &output_tf->tf_pts);
1805        output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1806
1807        return true;
1808}
1809
1810bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf,
1811                const struct regamma_lut *regamma)
1812{
1813        struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts;
1814        struct dividers dividers;
1815
1816        struct pwl_float_data *rgb_user = NULL;
1817        struct pwl_float_data_ex *rgb_regamma = NULL;
1818        bool ret = false;
1819
1820        if (regamma == NULL)
1821                return false;
1822
1823        output_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1824
1825        rgb_user = kcalloc(GAMMA_RGB_256_ENTRIES + _EXTRA_POINTS,
1826                           sizeof(*rgb_user),
1827                           GFP_KERNEL);
1828        if (!rgb_user)
1829                goto rgb_user_alloc_fail;
1830
1831        rgb_regamma = kcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
1832                              sizeof(*rgb_regamma),
1833                              GFP_KERNEL);
1834        if (!rgb_regamma)
1835                goto rgb_regamma_alloc_fail;
1836
1837        dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1838        dividers.divider2 = dc_fixpt_from_int(2);
1839        dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1840
1841        scale_user_regamma_ramp(rgb_user, &regamma->ramp, dividers);
1842
1843        if (regamma->flags.bits.applyDegamma == 1) {
1844                apply_degamma_for_user_regamma(rgb_regamma, MAX_HW_POINTS);
1845                copy_rgb_regamma_to_coordinates_x(coordinates_x,
1846                                MAX_HW_POINTS, rgb_regamma);
1847        }
1848
1849        interpolate_user_regamma(MAX_HW_POINTS, rgb_user,
1850                        regamma->flags.bits.applyDegamma, tf_pts);
1851
1852        // no custom HDR curves!
1853        tf_pts->end_exponent = 0;
1854        tf_pts->x_point_at_y1_red = 1;
1855        tf_pts->x_point_at_y1_green = 1;
1856        tf_pts->x_point_at_y1_blue = 1;
1857
1858        // this function just clamps output to 0-1
1859        build_new_custom_resulted_curve(MAX_HW_POINTS, tf_pts);
1860
1861        ret = true;
1862
1863        kfree(rgb_regamma);
1864rgb_regamma_alloc_fail:
1865        kvfree(rgb_user);
1866rgb_user_alloc_fail:
1867        return ret;
1868}
1869
1870bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf,
1871                const struct dc_gamma *ramp, bool mapUserRamp)
1872{
1873        struct dc_transfer_func_distributed_points *tf_pts = &input_tf->tf_pts;
1874        struct dividers dividers;
1875        struct pwl_float_data *rgb_user = NULL;
1876        struct pwl_float_data_ex *curve = NULL;
1877        struct gamma_pixel *axis_x = NULL;
1878        struct pixel_gamma_point *coeff = NULL;
1879        enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1880        uint32_t i;
1881        bool ret = false;
1882
1883        if (input_tf->type == TF_TYPE_BYPASS)
1884                return false;
1885
1886        /* we can use hardcoded curve for plain SRGB TF
1887         * If linear, it's bypass if on user ramp
1888         */
1889        if (input_tf->type == TF_TYPE_PREDEFINED &&
1890                        (input_tf->tf == TRANSFER_FUNCTION_SRGB ||
1891                                        input_tf->tf == TRANSFER_FUNCTION_LINEAR) &&
1892                                        !mapUserRamp)
1893                return true;
1894
1895        input_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1896
1897        if (mapUserRamp && ramp && ramp->type == GAMMA_RGB_256) {
1898                rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS,
1899                                sizeof(*rgb_user),
1900                                GFP_KERNEL);
1901                if (!rgb_user)
1902                        goto rgb_user_alloc_fail;
1903
1904                axis_x = kvcalloc(ramp->num_entries + _EXTRA_POINTS, sizeof(*axis_x),
1905                                GFP_KERNEL);
1906                if (!axis_x)
1907                        goto axis_x_alloc_fail;
1908
1909                dividers.divider1 = dc_fixpt_from_fraction(3, 2);
1910                dividers.divider2 = dc_fixpt_from_int(2);
1911                dividers.divider3 = dc_fixpt_from_fraction(5, 2);
1912
1913                build_evenly_distributed_points(
1914                                axis_x,
1915                                ramp->num_entries,
1916                                dividers);
1917
1918                scale_gamma(rgb_user, ramp, dividers);
1919        }
1920
1921        curve = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*curve),
1922                        GFP_KERNEL);
1923        if (!curve)
1924                goto curve_alloc_fail;
1925
1926        coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff),
1927                        GFP_KERNEL);
1928        if (!coeff)
1929                goto coeff_alloc_fail;
1930
1931        tf = input_tf->tf;
1932
1933        if (tf == TRANSFER_FUNCTION_PQ)
1934                build_de_pq(curve,
1935                                MAX_HW_POINTS,
1936                                coordinates_x);
1937        else if (tf == TRANSFER_FUNCTION_SRGB ||
1938                tf == TRANSFER_FUNCTION_BT709 ||
1939                tf == TRANSFER_FUNCTION_GAMMA22 ||
1940                tf == TRANSFER_FUNCTION_GAMMA24 ||
1941                tf == TRANSFER_FUNCTION_GAMMA26)
1942                build_degamma(curve,
1943                                MAX_HW_POINTS,
1944                                coordinates_x,
1945                                tf);
1946        else if (tf == TRANSFER_FUNCTION_HLG)
1947                build_hlg_degamma(curve,
1948                                MAX_HW_POINTS,
1949                                coordinates_x,
1950                                80, 1000);
1951        else if (tf == TRANSFER_FUNCTION_LINEAR) {
1952                // just copy coordinates_x into curve
1953                i = 0;
1954                while (i != MAX_HW_POINTS + 1) {
1955                        curve[i].r = coordinates_x[i].x;
1956                        curve[i].g = curve[i].r;
1957                        curve[i].b = curve[i].r;
1958                        i++;
1959                }
1960        } else
1961                goto invalid_tf_fail;
1962
1963        tf_pts->end_exponent = 0;
1964        tf_pts->x_point_at_y1_red = 1;
1965        tf_pts->x_point_at_y1_green = 1;
1966        tf_pts->x_point_at_y1_blue = 1;
1967
1968        map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
1969                        coordinates_x, axis_x, curve,
1970                        MAX_HW_POINTS, tf_pts,
1971                        mapUserRamp && ramp && ramp->type == GAMMA_RGB_256);
1972        if (ramp->type == GAMMA_CUSTOM)
1973                apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
1974
1975        ret = true;
1976
1977invalid_tf_fail:
1978        kvfree(coeff);
1979coeff_alloc_fail:
1980        kvfree(curve);
1981curve_alloc_fail:
1982        kvfree(axis_x);
1983axis_x_alloc_fail:
1984        kvfree(rgb_user);
1985rgb_user_alloc_fail:
1986
1987        return ret;
1988}
1989
1990
1991bool  mod_color_calculate_curve(enum dc_transfer_func_predefined trans,
1992                                struct dc_transfer_func_distributed_points *points,
1993                                uint32_t sdr_ref_white_level)
1994{
1995        uint32_t i;
1996        bool ret = false;
1997        struct pwl_float_data_ex *rgb_regamma = NULL;
1998
1999        if (trans == TRANSFER_FUNCTION_UNITY ||
2000                trans == TRANSFER_FUNCTION_LINEAR) {
2001                points->end_exponent = 0;
2002                points->x_point_at_y1_red = 1;
2003                points->x_point_at_y1_green = 1;
2004                points->x_point_at_y1_blue = 1;
2005
2006                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2007                        points->red[i]    = coordinates_x[i].x;
2008                        points->green[i]  = coordinates_x[i].x;
2009                        points->blue[i]   = coordinates_x[i].x;
2010                }
2011                ret = true;
2012        } else if (trans == TRANSFER_FUNCTION_PQ) {
2013                rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2014                                       sizeof(*rgb_regamma),
2015                                       GFP_KERNEL);
2016                if (!rgb_regamma)
2017                        goto rgb_regamma_alloc_fail;
2018                points->end_exponent = 7;
2019                points->x_point_at_y1_red = 125;
2020                points->x_point_at_y1_green = 125;
2021                points->x_point_at_y1_blue = 125;
2022
2023
2024                build_pq(rgb_regamma,
2025                                MAX_HW_POINTS,
2026                                coordinates_x,
2027                                sdr_ref_white_level);
2028                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2029                        points->red[i]    = rgb_regamma[i].r;
2030                        points->green[i]  = rgb_regamma[i].g;
2031                        points->blue[i]   = rgb_regamma[i].b;
2032                }
2033                ret = true;
2034
2035                kvfree(rgb_regamma);
2036        } else if (trans == TRANSFER_FUNCTION_SRGB ||
2037                trans == TRANSFER_FUNCTION_BT709 ||
2038                trans == TRANSFER_FUNCTION_GAMMA22 ||
2039                trans == TRANSFER_FUNCTION_GAMMA24 ||
2040                trans == TRANSFER_FUNCTION_GAMMA26) {
2041                rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2042                                       sizeof(*rgb_regamma),
2043                                       GFP_KERNEL);
2044                if (!rgb_regamma)
2045                        goto rgb_regamma_alloc_fail;
2046                points->end_exponent = 0;
2047                points->x_point_at_y1_red = 1;
2048                points->x_point_at_y1_green = 1;
2049                points->x_point_at_y1_blue = 1;
2050
2051                build_regamma(rgb_regamma,
2052                                MAX_HW_POINTS,
2053                                coordinates_x,
2054                                trans);
2055                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2056                        points->red[i]    = rgb_regamma[i].r;
2057                        points->green[i]  = rgb_regamma[i].g;
2058                        points->blue[i]   = rgb_regamma[i].b;
2059                }
2060                ret = true;
2061
2062                kvfree(rgb_regamma);
2063        } else if (trans == TRANSFER_FUNCTION_HLG) {
2064                rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2065                                       sizeof(*rgb_regamma),
2066                                       GFP_KERNEL);
2067                if (!rgb_regamma)
2068                        goto rgb_regamma_alloc_fail;
2069                points->end_exponent = 4;
2070                points->x_point_at_y1_red = 12;
2071                points->x_point_at_y1_green = 12;
2072                points->x_point_at_y1_blue = 12;
2073
2074                build_hlg_regamma(rgb_regamma,
2075                                MAX_HW_POINTS,
2076                                coordinates_x,
2077                                80, 1000);
2078                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2079                        points->red[i]    = rgb_regamma[i].r;
2080                        points->green[i]  = rgb_regamma[i].g;
2081                        points->blue[i]   = rgb_regamma[i].b;
2082                }
2083                ret = true;
2084                kvfree(rgb_regamma);
2085        }
2086rgb_regamma_alloc_fail:
2087        return ret;
2088}
2089
2090
2091bool  mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans,
2092                                struct dc_transfer_func_distributed_points *points)
2093{
2094        uint32_t i;
2095        bool ret = false;
2096        struct pwl_float_data_ex *rgb_degamma = NULL;
2097
2098        if (trans == TRANSFER_FUNCTION_UNITY ||
2099                trans == TRANSFER_FUNCTION_LINEAR) {
2100
2101                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2102                        points->red[i]    = coordinates_x[i].x;
2103                        points->green[i]  = coordinates_x[i].x;
2104                        points->blue[i]   = coordinates_x[i].x;
2105                }
2106                ret = true;
2107        } else if (trans == TRANSFER_FUNCTION_PQ) {
2108                rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2109                                       sizeof(*rgb_degamma),
2110                                       GFP_KERNEL);
2111                if (!rgb_degamma)
2112                        goto rgb_degamma_alloc_fail;
2113
2114
2115                build_de_pq(rgb_degamma,
2116                                MAX_HW_POINTS,
2117                                coordinates_x);
2118                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2119                        points->red[i]    = rgb_degamma[i].r;
2120                        points->green[i]  = rgb_degamma[i].g;
2121                        points->blue[i]   = rgb_degamma[i].b;
2122                }
2123                ret = true;
2124
2125                kvfree(rgb_degamma);
2126        } else if (trans == TRANSFER_FUNCTION_SRGB ||
2127                trans == TRANSFER_FUNCTION_BT709 ||
2128                trans == TRANSFER_FUNCTION_GAMMA22 ||
2129                trans == TRANSFER_FUNCTION_GAMMA24 ||
2130                trans == TRANSFER_FUNCTION_GAMMA26) {
2131                rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2132                                       sizeof(*rgb_degamma),
2133                                       GFP_KERNEL);
2134                if (!rgb_degamma)
2135                        goto rgb_degamma_alloc_fail;
2136
2137                build_degamma(rgb_degamma,
2138                                MAX_HW_POINTS,
2139                                coordinates_x,
2140                                trans);
2141                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2142                        points->red[i]    = rgb_degamma[i].r;
2143                        points->green[i]  = rgb_degamma[i].g;
2144                        points->blue[i]   = rgb_degamma[i].b;
2145                }
2146                ret = true;
2147
2148                kvfree(rgb_degamma);
2149        } else if (trans == TRANSFER_FUNCTION_HLG) {
2150                rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS,
2151                                       sizeof(*rgb_degamma),
2152                                       GFP_KERNEL);
2153                if (!rgb_degamma)
2154                        goto rgb_degamma_alloc_fail;
2155
2156                build_hlg_degamma(rgb_degamma,
2157                                MAX_HW_POINTS,
2158                                coordinates_x,
2159                                80, 1000);
2160                for (i = 0; i <= MAX_HW_POINTS ; i++) {
2161                        points->red[i]    = rgb_degamma[i].r;
2162                        points->green[i]  = rgb_degamma[i].g;
2163                        points->blue[i]   = rgb_degamma[i].b;
2164                }
2165                ret = true;
2166                kvfree(rgb_degamma);
2167        }
2168        points->end_exponent = 0;
2169        points->x_point_at_y1_red = 1;
2170        points->x_point_at_y1_green = 1;
2171        points->x_point_at_y1_blue = 1;
2172
2173rgb_degamma_alloc_fail:
2174        return ret;
2175}
2176
2177
2178