root/sys/dev/pci/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c
/*
 * Copyright 2012-15 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: AMD
 *
 */

#include "dm_services.h"

/* include DCE11 register header files */
#include "dce/dce_11_0_d.h"
#include "dce/dce_11_0_sh_mask.h"

#include "dce110_transform_v.h"

static void power_on_lut(struct transform *xfm,
        bool power_on, bool inputgamma, bool regamma)
{
        uint32_t value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL);
        int i;

        if (power_on) {
                if (inputgamma)
                        set_reg_field_value(
                                value,
                                1,
                                DCFEV_MEM_PWR_CTRL,
                                COL_MAN_INPUT_GAMMA_MEM_PWR_DIS);
                if (regamma)
                        set_reg_field_value(
                                value,
                                1,
                                DCFEV_MEM_PWR_CTRL,
                                COL_MAN_GAMMA_CORR_MEM_PWR_DIS);
        } else {
                if (inputgamma)
                        set_reg_field_value(
                                value,
                                0,
                                DCFEV_MEM_PWR_CTRL,
                                COL_MAN_INPUT_GAMMA_MEM_PWR_DIS);
                if (regamma)
                        set_reg_field_value(
                                value,
                                0,
                                DCFEV_MEM_PWR_CTRL,
                                COL_MAN_GAMMA_CORR_MEM_PWR_DIS);
        }

        dm_write_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL, value);

        for (i = 0; i < 3; i++) {
                value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL);
                if (get_reg_field_value(value,
                                DCFEV_MEM_PWR_CTRL,
                                COL_MAN_INPUT_GAMMA_MEM_PWR_DIS) &&
                        get_reg_field_value(value,
                                        DCFEV_MEM_PWR_CTRL,
                                        COL_MAN_GAMMA_CORR_MEM_PWR_DIS))
                        break;

                udelay(2);
        }
}

static void set_bypass_input_gamma(struct dce_transform *xfm_dce)
{
        uint32_t value;

        value = dm_read_reg(xfm_dce->base.ctx,
                        mmCOL_MAN_INPUT_GAMMA_CONTROL1);

        set_reg_field_value(
                                value,
                                0,
                                COL_MAN_INPUT_GAMMA_CONTROL1,
                                INPUT_GAMMA_MODE);

        dm_write_reg(xfm_dce->base.ctx,
                        mmCOL_MAN_INPUT_GAMMA_CONTROL1, value);
}

static void configure_regamma_mode(struct dce_transform *xfm_dce, uint32_t mode)
{
        uint32_t value = 0;

        set_reg_field_value(
                                value,
                                mode,
                                GAMMA_CORR_CONTROL,
                                GAMMA_CORR_MODE);

        dm_write_reg(xfm_dce->base.ctx, mmGAMMA_CORR_CONTROL, 0);
}

/*
 *****************************************************************************
 *  Function: regamma_config_regions_and_segments
 *
 *     build regamma curve by using predefined hw points
 *     uses interface parameters ,like EDID coeff.
 *
 * @param   : parameters   interface parameters
 *  @return void
 *
 *  @note
 *
 *  @see
 *
 *****************************************************************************
 */
static void regamma_config_regions_and_segments(
        struct dce_transform *xfm_dce, const struct pwl_params *params)
{
        const struct gamma_curve *curve;
        uint32_t value = 0;

        {
                set_reg_field_value(
                        value,
                        params->arr_points[0].custom_float_x,
                        GAMMA_CORR_CNTLA_START_CNTL,
                        GAMMA_CORR_CNTLA_EXP_REGION_START);

                set_reg_field_value(
                        value,
                        0,
                        GAMMA_CORR_CNTLA_START_CNTL,
                        GAMMA_CORR_CNTLA_EXP_REGION_START_SEGMENT);

                dm_write_reg(xfm_dce->base.ctx, mmGAMMA_CORR_CNTLA_START_CNTL,
                                value);
        }
        {
                value = 0;
                set_reg_field_value(
                        value,
                        params->arr_points[0].custom_float_slope,
                        GAMMA_CORR_CNTLA_SLOPE_CNTL,
                        GAMMA_CORR_CNTLA_EXP_REGION_LINEAR_SLOPE);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_SLOPE_CNTL, value);
        }
        {
                value = 0;
                set_reg_field_value(
                        value,
                        params->arr_points[1].custom_float_x,
                        GAMMA_CORR_CNTLA_END_CNTL1,
                        GAMMA_CORR_CNTLA_EXP_REGION_END);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_END_CNTL1, value);
        }
        {
                value = 0;
                set_reg_field_value(
                        value,
                        params->arr_points[1].custom_float_slope,
                        GAMMA_CORR_CNTLA_END_CNTL2,
                        GAMMA_CORR_CNTLA_EXP_REGION_END_BASE);

                set_reg_field_value(
                        value,
                        params->arr_points[1].custom_float_y,
                        GAMMA_CORR_CNTLA_END_CNTL2,
                        GAMMA_CORR_CNTLA_EXP_REGION_END_SLOPE);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_END_CNTL2, value);
        }

        curve = params->arr_curve_points;

        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_0_1,
                        GAMMA_CORR_CNTLA_EXP_REGION0_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_0_1,
                        GAMMA_CORR_CNTLA_EXP_REGION0_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_0_1,
                        GAMMA_CORR_CNTLA_EXP_REGION1_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_0_1,
                        GAMMA_CORR_CNTLA_EXP_REGION1_NUM_SEGMENTS);

                dm_write_reg(
                                xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_0_1,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_2_3,
                        GAMMA_CORR_CNTLA_EXP_REGION2_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_2_3,
                        GAMMA_CORR_CNTLA_EXP_REGION2_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_2_3,
                        GAMMA_CORR_CNTLA_EXP_REGION3_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_2_3,
                        GAMMA_CORR_CNTLA_EXP_REGION3_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_2_3,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_4_5,
                        GAMMA_CORR_CNTLA_EXP_REGION4_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_4_5,
                        GAMMA_CORR_CNTLA_EXP_REGION4_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_4_5,
                        GAMMA_CORR_CNTLA_EXP_REGION5_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_4_5,
                        GAMMA_CORR_CNTLA_EXP_REGION5_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_4_5,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_6_7,
                        GAMMA_CORR_CNTLA_EXP_REGION6_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_6_7,
                        GAMMA_CORR_CNTLA_EXP_REGION6_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_6_7,
                        GAMMA_CORR_CNTLA_EXP_REGION7_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_6_7,
                        GAMMA_CORR_CNTLA_EXP_REGION7_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_6_7,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_8_9,
                        GAMMA_CORR_CNTLA_EXP_REGION8_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_8_9,
                        GAMMA_CORR_CNTLA_EXP_REGION8_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_8_9,
                        GAMMA_CORR_CNTLA_EXP_REGION9_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_8_9,
                        GAMMA_CORR_CNTLA_EXP_REGION9_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_8_9,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_10_11,
                        GAMMA_CORR_CNTLA_EXP_REGION10_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_10_11,
                        GAMMA_CORR_CNTLA_EXP_REGION10_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_10_11,
                        GAMMA_CORR_CNTLA_EXP_REGION11_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_10_11,
                        GAMMA_CORR_CNTLA_EXP_REGION11_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_10_11,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_12_13,
                        GAMMA_CORR_CNTLA_EXP_REGION12_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_12_13,
                        GAMMA_CORR_CNTLA_EXP_REGION12_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_12_13,
                        GAMMA_CORR_CNTLA_EXP_REGION13_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_12_13,
                        GAMMA_CORR_CNTLA_EXP_REGION13_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_12_13,
                        value);
        }

        curve += 2;
        {
                value = 0;
                set_reg_field_value(
                        value,
                        curve[0].offset,
                        GAMMA_CORR_CNTLA_REGION_14_15,
                        GAMMA_CORR_CNTLA_EXP_REGION14_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[0].segments_num,
                        GAMMA_CORR_CNTLA_REGION_14_15,
                        GAMMA_CORR_CNTLA_EXP_REGION14_NUM_SEGMENTS);

                set_reg_field_value(
                        value,
                        curve[1].offset,
                        GAMMA_CORR_CNTLA_REGION_14_15,
                        GAMMA_CORR_CNTLA_EXP_REGION15_LUT_OFFSET);

                set_reg_field_value(
                        value,
                        curve[1].segments_num,
                        GAMMA_CORR_CNTLA_REGION_14_15,
                        GAMMA_CORR_CNTLA_EXP_REGION15_NUM_SEGMENTS);

                dm_write_reg(xfm_dce->base.ctx,
                        mmGAMMA_CORR_CNTLA_REGION_14_15,
                        value);
        }
}

static void program_pwl(struct dce_transform *xfm_dce,
                const struct pwl_params *params)
{
        uint32_t value = 0;

        set_reg_field_value(
                value,
                7,
                GAMMA_CORR_LUT_WRITE_EN_MASK,
                GAMMA_CORR_LUT_WRITE_EN_MASK);

        dm_write_reg(xfm_dce->base.ctx,
                mmGAMMA_CORR_LUT_WRITE_EN_MASK, value);

        dm_write_reg(xfm_dce->base.ctx,
                mmGAMMA_CORR_LUT_INDEX, 0);

        /* Program REGAMMA_LUT_DATA */
        {
                const uint32_t addr = mmGAMMA_CORR_LUT_DATA;
                uint32_t i = 0;
                const struct pwl_result_data *rgb =
                                params->rgb_resulted;

                while (i != params->hw_points_num) {
                        dm_write_reg(xfm_dce->base.ctx, addr, rgb->red_reg);
                        dm_write_reg(xfm_dce->base.ctx, addr, rgb->green_reg);
                        dm_write_reg(xfm_dce->base.ctx, addr, rgb->blue_reg);

                        dm_write_reg(xfm_dce->base.ctx, addr,
                                rgb->delta_red_reg);
                        dm_write_reg(xfm_dce->base.ctx, addr,
                                rgb->delta_green_reg);
                        dm_write_reg(xfm_dce->base.ctx, addr,
                                rgb->delta_blue_reg);

                        ++rgb;
                        ++i;
                }
        }
}

void dce110_opp_program_regamma_pwl_v(
        struct transform *xfm,
        const struct pwl_params *params)
{
        struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm);

        /* Setup regions */
        regamma_config_regions_and_segments(xfm_dce, params);

        set_bypass_input_gamma(xfm_dce);

        /* Power on gamma LUT memory */
        power_on_lut(xfm, true, false, true);

        /* Program PWL */
        program_pwl(xfm_dce, params);

        /* program regamma config */
        configure_regamma_mode(xfm_dce, 1);

        /* Power return to auto back */
        power_on_lut(xfm, false, false, true);
}

void dce110_opp_power_on_regamma_lut_v(
        struct transform *xfm,
        bool power_on)
{
        uint32_t value = dm_read_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL);

        set_reg_field_value(
                value,
                0,
                DCFEV_MEM_PWR_CTRL,
                COL_MAN_GAMMA_CORR_MEM_PWR_FORCE);

        set_reg_field_value(
                value,
                power_on,
                DCFEV_MEM_PWR_CTRL,
                COL_MAN_GAMMA_CORR_MEM_PWR_DIS);

        set_reg_field_value(
                value,
                0,
                DCFEV_MEM_PWR_CTRL,
                COL_MAN_INPUT_GAMMA_MEM_PWR_FORCE);

        set_reg_field_value(
                value,
                power_on,
                DCFEV_MEM_PWR_CTRL,
                COL_MAN_INPUT_GAMMA_MEM_PWR_DIS);

        dm_write_reg(xfm->ctx, mmDCFEV_MEM_PWR_CTRL, value);
}

void dce110_opp_set_regamma_mode_v(
        struct transform *xfm,
        enum opp_regamma mode)
{
        // TODO: need to implement the function
}