Adds scaler, viewport, gamut remap, and pixel depth programming. Signed-off-by: Harry Wentland <harry.wentland@xxxxxxx> Reviewed-by: Alex Deucher <alexander.deucher@xxxxxxx> --- .../gpu/drm/amd/dal/dc/dce110/dce110_transform.c | 91 +++ .../gpu/drm/amd/dal/dc/dce110/dce110_transform.h | 87 +++ .../amd/dal/dc/dce110/dce110_transform_bit_depth.c | 851 +++++++++++++++++++++ .../amd/dal/dc/dce110/dce110_transform_bit_depth.h | 51 ++ .../drm/amd/dal/dc/dce110/dce110_transform_gamut.c | 296 +++++++ .../drm/amd/dal/dc/dce110/dce110_transform_scl.c | 818 ++++++++++++++++++++ .../drm/amd/dal/dc/dce110/dce110_transform_sclv.c | 531 +++++++++++++ 7 files changed, 2725 insertions(+) create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c new file mode 100644 index 000000000000..2654a965065d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.c @@ -0,0 +1,91 @@ +/* + * 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 "dc_types.h" +#include "core_types.h" + +#include "include/grph_object_id.h" +#include "include/fixed31_32.h" +#include "include/logger_interface.h" + +#include "dce110_transform.h" + +#include "dce110_transform_bit_depth.h" + +static struct transform_funcs dce110_transform_funcs = { + .transform_power_up = + dce110_transform_power_up, + .transform_set_scaler = + dce110_transform_set_scaler, + .transform_set_scaler_bypass = + dce110_transform_set_scaler_bypass, + .transform_update_viewport = + dce110_transform_update_viewport, + .transform_set_scaler_filter = + dce110_transform_set_scaler_filter, + .transform_set_gamut_remap = + dce110_transform_set_gamut_remap, + .transform_set_pixel_storage_depth = + dce110_transform_set_pixel_storage_depth, + .transform_get_current_pixel_storage_depth = + dce110_transform_get_current_pixel_storage_depth +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce110_transform_construct( + struct dce110_transform *xfm110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_transform_reg_offsets *reg_offsets) +{ + xfm110->base.ctx = ctx; + + xfm110->base.inst = inst; + xfm110->base.funcs = &dce110_transform_funcs; + + xfm110->offsets = *reg_offsets; + + xfm110->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + return true; +} + +bool dce110_transform_power_up(struct transform *xfm) +{ + return dce110_transform_power_up_line_buffer(xfm); +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h new file mode 100644 index 000000000000..117aca337f9d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform.h @@ -0,0 +1,87 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_TRANSFORM_DCE110_H__ +#define __DAL_TRANSFORM_DCE110_H__ + +#include "inc/transform.h" +#include "include/grph_csc_types.h" + +#define TO_DCE110_TRANSFORM(transform)\ + container_of(transform, struct dce110_transform, base) + +struct dce110_transform_reg_offsets { + uint32_t scl_offset; + uint32_t dcfe_offset; + uint32_t dcp_offset; + uint32_t lb_offset; +}; + +struct dce110_transform { + struct transform base; + struct dce110_transform_reg_offsets offsets; + + uint32_t lb_pixel_depth_supported; +}; + +bool dce110_transform_construct(struct dce110_transform *xfm110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_transform_reg_offsets *reg_offsets); + +bool dce110_transform_power_up(struct transform *xfm); + +/* SCALER RELATED */ +bool dce110_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data); + +void dce110_transform_set_scaler_bypass(struct transform *xfm); + +bool dce110_transform_update_viewport( + struct transform *xfm, + const struct rect *view_port, + bool is_fbc_attached); + +void dce110_transform_set_scaler_filter( + struct transform *xfm, + struct scaler_filter *filter); + +/* GAMUT RELATED */ +void dce110_transform_set_gamut_remap( + struct transform *xfm, + const struct grph_csc_adjustment *adjust); + +/* BIT DEPTH RELATED */ +bool dce110_transform_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params); + +bool dce110_transform_get_current_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth *depth); + + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c new file mode 100644 index 000000000000..fb5ef6d2c859 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.c @@ -0,0 +1,851 @@ +/* + * 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.h" +#include "opp.h" +#include "include/logger_interface.h" +#include "include/fixed32_32.h" + +#define DCP_REG(reg)\ + (reg + xfm110->offsets.dcp_offset) + +#define LB_REG(reg)\ + (reg + xfm110->offsets.lb_offset) + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +enum dcp_out_trunc_round_mode { + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_MODE_ROUND +}; + +enum dcp_out_trunc_round_depth { + DCP_OUT_TRUNC_ROUND_DEPTH_14BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_13BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_11BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_9BIT, + DCP_OUT_TRUNC_ROUND_DEPTH_8BIT +}; + +/* defines the various methods of bit reduction available for use */ +enum dcp_bit_depth_reduction_mode { + DCP_BIT_DEPTH_REDUCTION_MODE_DITHER, + DCP_BIT_DEPTH_REDUCTION_MODE_ROUND, + DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE, + DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED, + DCP_BIT_DEPTH_REDUCTION_MODE_INVALID +}; + +enum dcp_spatial_dither_mode { + DCP_SPATIAL_DITHER_MODE_AAAA, + DCP_SPATIAL_DITHER_MODE_A_AA_A, + DCP_SPATIAL_DITHER_MODE_AABBAABB, + DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC, + DCP_SPATIAL_DITHER_MODE_INVALID +}; + +enum dcp_spatial_dither_depth { + DCP_SPATIAL_DITHER_DEPTH_30BPP, + DCP_SPATIAL_DITHER_DEPTH_24BPP +}; + +static bool set_clamp( + struct dce110_transform *xfm110, + enum dc_color_depth depth); + +static bool set_round( + struct dce110_transform *xfm110, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth); + +static bool set_dither( + struct dce110_transform *xfm110, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable); + +/** + ******************************************************************************* + * dce110_transform_bit_depth_reduction_program + * + * @brief + * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, + * Dither) for dce110 + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @return + * true if succeeds. + ******************************************************************************* + */ +static bool program_bit_depth_reduction( + struct dce110_transform *xfm110, + enum dc_color_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + enum dcp_bit_depth_reduction_mode depth_reduction_mode; + enum dcp_spatial_dither_mode spatial_dither_mode; + bool frame_random_enable; + bool rgb_random_enable; + bool highpass_random_enable; + + if (depth > COLOR_DEPTH_121212) { + ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ + return false; + } + + if (bit_depth_params->flags.SPATIAL_DITHER_ENABLED) { + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DITHER; + frame_random_enable = true; + rgb_random_enable = true; + highpass_random_enable = true; + + } else { + depth_reduction_mode = DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED; + frame_random_enable = false; + rgb_random_enable = false; + highpass_random_enable = false; + } + + spatial_dither_mode = DCP_SPATIAL_DITHER_MODE_A_AA_A; + + if (!set_clamp(xfm110, depth)) { + /* Failure in set_clamp() */ + ASSERT_CRITICAL(false); + return false; + } + + switch (depth_reduction_mode) { + case DCP_BIT_DEPTH_REDUCTION_MODE_DITHER: + /* Spatial Dither: Set round/truncate to bypass (12bit), + * enable Dither (30bpp) */ + set_round(xfm110, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm110, true, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_ROUND: + /* Round: Enable round (10bit), disable Dither */ + set_round(xfm110, + DCP_OUT_TRUNC_ROUND_MODE_ROUND, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm110, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + case DCP_BIT_DEPTH_REDUCTION_MODE_TRUNCATE: /* Truncate */ + /* Truncate: Enable truncate (10bit), disable Dither */ + set_round(xfm110, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_10BIT); + + set_dither(xfm110, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + + case DCP_BIT_DEPTH_REDUCTION_MODE_DISABLED: /* Disabled */ + /* Truncate: Set round/truncate to bypass (12bit), + * disable Dither */ + set_round(xfm110, + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE, + DCP_OUT_TRUNC_ROUND_DEPTH_12BIT); + + set_dither(xfm110, false, spatial_dither_mode, + DCP_SPATIAL_DITHER_DEPTH_30BPP, frame_random_enable, + rgb_random_enable, highpass_random_enable); + break; + default: + /* Invalid DCP Depth reduction mode */ + ASSERT_CRITICAL(false); + break; + } + + return true; +} + +/** + ******************************************************************************* + * set_clamp + * + * @param depth : bit depth to set the clamp to (should match denorm) + * + * @brief + * Programs clamp according to panel bit depth. + * + * @return + * true if succeeds + * + ******************************************************************************* + */ +static bool set_clamp( + struct dce110_transform *xfm110, + enum dc_color_depth depth) +{ + uint32_t clamp_max = 0; + + /* At the clamp block the data will be MSB aligned, so we set the max + * clamp accordingly. + * For example, the max value for 6 bits MSB aligned (14 bit bus) would + * be "11 1111 0000 0000" in binary, so 0x3F00. + */ + switch (depth) { + case COLOR_DEPTH_666: + /* 6bit MSB aligned on 14 bit bus '11 1111 0000 0000' */ + clamp_max = 0x3F00; + break; + case COLOR_DEPTH_888: + /* 8bit MSB aligned on 14 bit bus '11 1111 1100 0000' */ + clamp_max = 0x3FC0; + break; + case COLOR_DEPTH_101010: + /* 10bit MSB aligned on 14 bit bus '11 1111 1111 1100' */ + clamp_max = 0x3FFC; + break; + case COLOR_DEPTH_121212: + /* 12bit MSB aligned on 14 bit bus '11 1111 1111 1111' */ + clamp_max = 0x3FFF; + break; + default: + ASSERT_CRITICAL(false); /* Invalid clamp bit depth */ + return false; + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_B_CB, + OUT_CLAMP_MIN_B_CB); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_B_CB, + OUT_CLAMP_MAX_B_CB); + + dm_write_reg(xfm110->base.ctx, + DCP_REG(mmOUT_CLAMP_CONTROL_B_CB), + value); + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_G_Y, + OUT_CLAMP_MIN_G_Y); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_G_Y, + OUT_CLAMP_MAX_G_Y); + + dm_write_reg(xfm110->base.ctx, + DCP_REG(mmOUT_CLAMP_CONTROL_G_Y), + value); + } + + { + uint32_t value = 0; + /* always set min to 0 */ + set_reg_field_value( + value, + 0, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MIN_R_CR); + + set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + + dm_write_reg(xfm110->base.ctx, + DCP_REG(mmOUT_CLAMP_CONTROL_R_CR), + value); + } + + return true; +} + +/** + ******************************************************************************* + * set_round + * + * @brief + * Programs Round/Truncate + * + * @param [in] mode :round or truncate + * @param [in] depth :bit depth to round/truncate to + OUT_ROUND_TRUNC_MODE 3:0 0xA Output data round or truncate mode + POSSIBLE VALUES: + 00 - truncate to u0.12 + 01 - truncate to u0.11 + 02 - truncate to u0.10 + 03 - truncate to u0.9 + 04 - truncate to u0.8 + 05 - reserved + 06 - truncate to u0.14 + 07 - truncate to u0.13 set_reg_field_value( + value, + clamp_max, + OUT_CLAMP_CONTROL_R_CR, + OUT_CLAMP_MAX_R_CR); + 08 - round to u0.12 + 09 - round to u0.11 + 10 - round to u0.10 + 11 - round to u0.9 + 12 - round to u0.8 + 13 - reserved + 14 - round to u0.14 + 15 - round to u0.13 + + * @return + * true if succeeds. + ******************************************************************************* + */ +static bool set_round( + struct dce110_transform *xfm110, + enum dcp_out_trunc_round_mode mode, + enum dcp_out_trunc_round_depth depth) +{ + uint32_t depth_bits = 0; + uint32_t mode_bit = 0; + /* zero out all bits */ + uint32_t value = 0; + + /* set up bit depth */ + switch (depth) { + case DCP_OUT_TRUNC_ROUND_DEPTH_14BIT: + depth_bits = 6; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_13BIT: + depth_bits = 7; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_12BIT: + depth_bits = 0; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_11BIT: + depth_bits = 1; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_10BIT: + depth_bits = 2; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_9BIT: + depth_bits = 3; + break; + case DCP_OUT_TRUNC_ROUND_DEPTH_8BIT: + depth_bits = 4; + break; + default: + /* Invalid dcp_out_trunc_round_depth */ + ASSERT_CRITICAL(false); + return false; + } + + set_reg_field_value( + value, + depth_bits, + OUT_ROUND_CONTROL, + OUT_ROUND_TRUNC_MODE); + + /* set up round or truncate */ + switch (mode) { + case DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE: + mode_bit = 0; + break; + case DCP_OUT_TRUNC_ROUND_MODE_ROUND: + mode_bit = 1; + break; + default: + /* Invalid dcp_out_trunc_round_mode */ + ASSERT_CRITICAL(false); + return false; + } + + depth_bits |= mode_bit << 3; + + set_reg_field_value( + value, + depth_bits, + OUT_ROUND_CONTROL, + OUT_ROUND_TRUNC_MODE); + + /* write the register */ + dm_write_reg(xfm110->base.ctx, + DCP_REG(mmOUT_ROUND_CONTROL), + value); + + return true; +} + +/** + ******************************************************************************* + * set_dither + * + * @brief + * Programs Dither + * + * @param [in] dither_enable : enable dither + * @param [in] dither_mode : dither mode to set + * @param [in] dither_depth : bit depth to dither to + * @param [in] frame_random_enable : enable frame random + * @param [in] rgb_random_enable : enable rgb random + * @param [in] highpass_random_enable : enable highpass random + * + * @return + * true if succeeds. + ******************************************************************************* + */ + +static bool set_dither( + struct dce110_transform *xfm110, + bool dither_enable, + enum dcp_spatial_dither_mode dither_mode, + enum dcp_spatial_dither_depth dither_depth, + bool frame_random_enable, + bool rgb_random_enable, + bool highpass_random_enable) +{ + uint32_t dither_depth_bits = 0; + uint32_t dither_mode_bits = 0; + /* zero out all bits */ + uint32_t value = 0; + + /* set up the fields */ + if (dither_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_EN); + + switch (dither_mode) { + case DCP_SPATIAL_DITHER_MODE_AAAA: + dither_mode_bits = 0; + break; + case DCP_SPATIAL_DITHER_MODE_A_AA_A: + dither_mode_bits = 1; + break; + case DCP_SPATIAL_DITHER_MODE_AABBAABB: + dither_mode_bits = 2; + break; + case DCP_SPATIAL_DITHER_MODE_AABBCCAABBCC: + dither_mode_bits = 3; + break; + default: + /* Invalid dcp_spatial_dither_mode */ + ASSERT_CRITICAL(false); + return false; + + } + set_reg_field_value( + value, + dither_mode_bits, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_MODE); + + switch (dither_depth) { + case DCP_SPATIAL_DITHER_DEPTH_30BPP: + dither_depth_bits = 0; + break; + case DCP_SPATIAL_DITHER_DEPTH_24BPP: + dither_depth_bits = 1; + break; + default: + /* Invalid dcp_spatial_dither_depth */ + ASSERT_CRITICAL(false); + return false; + } + + set_reg_field_value( + value, + dither_depth_bits, + DCP_SPATIAL_DITHER_CNTL, + DCP_SPATIAL_DITHER_DEPTH); + + if (frame_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_FRAME_RANDOM_ENABLE); + + if (rgb_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_RGB_RANDOM_ENABLE); + + if (highpass_random_enable) + set_reg_field_value( + value, + 1, + DCP_SPATIAL_DITHER_CNTL, + DCP_HIGHPASS_RANDOM_ENABLE); + + /* write the register */ + dm_write_reg(xfm110->base.ctx, + DCP_REG(mmDCP_SPATIAL_DITHER_CNTL), + value); + + return true; +} + +bool dce110_transform_get_max_num_of_supported_lines( + struct dce110_transform *xfm110, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines) +{ + uint32_t pixels_per_entries = 0; + uint32_t max_pixels_supports = 0; + + if (pixel_width == 0) + return false; + + /* Find number of pixels that can fit into a single LB entry and + * take floor of the value since we cannot store a single pixel + * across multiple entries. */ + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 18; + break; + + case LB_PIXEL_DEPTH_24BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 24; + break; + + case LB_PIXEL_DEPTH_30BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 30; + break; + + case LB_PIXEL_DEPTH_36BPP: + pixels_per_entries = LB_BITS_PER_ENTRY / 36; + break; + + default: + dal_logger_write(xfm110->base.ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid LB pixel depth", + __func__); + break; + } + + if (pixels_per_entries == 0) + return false; + + max_pixels_supports = pixels_per_entries * LB_TOTAL_NUMBER_OF_ENTRIES; + + *lines = max_pixels_supports / pixel_width; + return true; +} + +void dce110_transform_enable_alpha( + struct dce110_transform *xfm110, + bool enable) +{ + struct dc_context *ctx = xfm110->base.ctx; + uint32_t value; + uint32_t addr = LB_REG(mmLB_DATA_FORMAT); + + value = dm_read_reg(ctx, addr); + + if (enable == 1) + set_reg_field_value( + value, + 1, + LB_DATA_FORMAT, + ALPHA_EN); + else + set_reg_field_value( + value, + 0, + LB_DATA_FORMAT, + ALPHA_EN); + + dm_write_reg(ctx, addr, value); +} + +static enum lb_pixel_depth translate_display_bpp_to_lb_depth( + uint32_t display_bpp) +{ + switch (display_bpp) { + case 18: + return LB_PIXEL_DEPTH_18BPP; + case 24: + return LB_PIXEL_DEPTH_24BPP; + case 36: + case 42: + case 48: + return LB_PIXEL_DEPTH_36BPP; + case 30: + default: + return LB_PIXEL_DEPTH_30BPP; + } +} + +bool dce110_transform_get_next_lower_pixel_storage_depth( + struct dce110_transform *xfm110, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth) +{ + enum lb_pixel_depth depth_req_by_display = + translate_display_bpp_to_lb_depth(display_bpp); + uint32_t current_required_depth = depth_req_by_display; + uint32_t current_depth = depth; + + /* if required display depth < current we could go down, for example + * from LB_PIXEL_DEPTH_30BPP to LB_PIXEL_DEPTH_24BPP + */ + if (current_required_depth < current_depth) { + current_depth = current_depth >> 1; + if (xfm110->lb_pixel_depth_supported & current_depth) { + *lower_depth = current_depth; + return true; + } + } + return false; +} + +bool dce110_transform_is_prefetch_enabled( + struct dce110_transform *xfm110) +{ + uint32_t value = dm_read_reg( + xfm110->base.ctx, LB_REG(mmLB_DATA_FORMAT)); + + if (get_reg_field_value(value, LB_DATA_FORMAT, PREFETCH) == 1) + return true; + + return false; +} + +bool dce110_transform_get_current_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth *depth) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + uint32_t value = 0; + + if (depth == NULL) + return false; + + value = dm_read_reg( + xfm->ctx, + LB_REG(mmLB_DATA_FORMAT)); + + switch (get_reg_field_value(value, LB_DATA_FORMAT, PIXEL_DEPTH)) { + case 0: + *depth = LB_PIXEL_DEPTH_30BPP; + break; + case 1: + *depth = LB_PIXEL_DEPTH_24BPP; + break; + case 2: + *depth = LB_PIXEL_DEPTH_18BPP; + break; + case 3: + *depth = LB_PIXEL_DEPTH_36BPP; + break; + default: + dal_logger_write(xfm->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid LB pixel depth", + __func__); + *depth = LB_PIXEL_DEPTH_30BPP; + break; + } + return true; + +} + +static void set_denormalization( + struct dce110_transform *xfm110, + enum dc_color_depth depth) +{ + uint32_t value = dm_read_reg(xfm110->base.ctx, + DCP_REG(mmDENORM_CONTROL)); + + switch (depth) { + case COLOR_DEPTH_666: + /* 63/64 for 6 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_888: + /* Unity for 8 bit output color depth + * because prescale is disabled by default */ + set_reg_field_value( + value, + 0, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 5, + DENORM_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_141414: + case COLOR_DEPTH_161616: + default: + /* not valid used case! */ + break; + } + + dm_write_reg(xfm110->base.ctx, + DCP_REG(mmDENORM_CONTROL), + value); + +} + +bool dce110_transform_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + bool ret = true; + uint32_t value; + enum dc_color_depth color_depth; + + value = dm_read_reg( + xfm->ctx, + LB_REG(mmLB_DATA_FORMAT)); + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + color_depth = COLOR_DEPTH_666; + set_reg_field_value(value, 2, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_24BPP: + color_depth = COLOR_DEPTH_888; + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_30BPP: + color_depth = COLOR_DEPTH_101010; + set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 1, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + case LB_PIXEL_DEPTH_36BPP: + color_depth = COLOR_DEPTH_121212; + set_reg_field_value(value, 3, LB_DATA_FORMAT, PIXEL_DEPTH); + set_reg_field_value(value, 0, LB_DATA_FORMAT, PIXEL_EXPAN_MODE); + break; + default: + ret = false; + break; + } + + if (ret == true) { + set_denormalization(xfm110, color_depth); + ret = program_bit_depth_reduction(xfm110, color_depth, + bit_depth_params); + + set_reg_field_value(value, 0, LB_DATA_FORMAT, ALPHA_EN); + dm_write_reg( + xfm->ctx, LB_REG(mmLB_DATA_FORMAT), value); + if (!(xfm110->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dal_logger_write(xfm->ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_GPU, + "%s: Capability not supported", + __func__); + } + } + + return ret; +} + +/* LB_MEMORY_CONFIG + * 00 - Use all three pieces of memory + * 01 - Use only one piece of memory of total 720x144 bits + * 10 - Use two pieces of memory of total 960x144 bits + * 11 - reserved + * + * LB_MEMORY_SIZE + * Total entries of LB memory. + * This number should be larger than 960. The default value is 1712(0x6B0) */ +bool dce110_transform_power_up_line_buffer(struct transform *xfm) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + uint32_t value; + + value = dm_read_reg(xfm110->base.ctx, LB_REG(mmLB_MEMORY_CTRL)); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LB_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value(value, LB_TOTAL_NUMBER_OF_ENTRIES, LB_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dm_write_reg(xfm110->base.ctx, LB_REG(mmLB_MEMORY_CTRL), value); + + return true; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h new file mode 100644 index 000000000000..ff100cc7f30f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_bit_depth.h @@ -0,0 +1,51 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_TRANSFORM_BIT_DEPTH_DCE110_H__ +#define __DC_TRANSFORM_BIT_DEPTH_DCE110_H__ + +#include "dce110_transform.h" + +bool dce110_transform_power_up_line_buffer(struct transform *xfm); + +bool dce110_transform_get_max_num_of_supported_lines( + struct dce110_transform *xfm110, + enum lb_pixel_depth depth, + uint32_t pixel_width, + uint32_t *lines); + +void dce110_transform_enable_alpha( + struct dce110_transform *xfm110, + bool enable); + +bool dce110_transform_get_next_lower_pixel_storage_depth( + struct dce110_transform *xfm110, + uint32_t display_bpp, + enum lb_pixel_depth depth, + enum lb_pixel_depth *lower_depth); + +bool dce110_transform_is_prefetch_enabled( + struct dce110_transform *xfm110); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c new file mode 100644 index 000000000000..05309c9f9482 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_gamut.c @@ -0,0 +1,296 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dce110_transform.h" +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "include/fixed31_32.h" +#include "basics/conversion.h" +#include "include/grph_object_id.h" + +enum { + GAMUT_MATRIX_SIZE = 12 +}; + +#define DCP_REG(reg)\ + (reg + xfm110->offsets.dcp_offset) + +#define DISP_BRIGHTNESS_DEFAULT_HW 0 +#define DISP_BRIGHTNESS_MIN_HW -25 +#define DISP_BRIGHTNESS_MAX_HW 25 +#define DISP_BRIGHTNESS_STEP_HW 1 +#define DISP_BRIGHTNESS_HW_DIVIDER 100 + +#define DISP_HUE_DEFAULT_HW 0 +#define DISP_HUE_MIN_HW -30 +#define DISP_HUE_MAX_HW 30 +#define DISP_HUE_STEP_HW 1 +#define DISP_HUE_HW_DIVIDER 1 + +#define DISP_CONTRAST_DEFAULT_HW 100 +#define DISP_CONTRAST_MIN_HW 50 +#define DISP_CONTRAST_MAX_HW 150 +#define DISP_CONTRAST_STEP_HW 1 +#define DISP_CONTRAST_HW_DIVIDER 100 + +#define DISP_SATURATION_DEFAULT_HW 100 +#define DISP_SATURATION_MIN_HW 0 +#define DISP_SATURATION_MAX_HW 200 +#define DISP_SATURATION_STEP_HW 1 +#define DISP_SATURATION_HW_DIVIDER 100 + +#define DISP_KELVIN_DEGRES_DEFAULT 6500 +#define DISP_KELVIN_DEGRES_MIN 4000 +#define DISP_KELVIN_DEGRES_MAX 10000 +#define DISP_KELVIN_DEGRES_STEP 100 +#define DISP_KELVIN_HW_DIVIDER 10000 + +static void program_gamut_remap( + struct dce110_transform *xfm110, + const uint16_t *reg_val) +{ + struct dc_context *ctx = xfm110->base.ctx; + uint32_t value = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_CONTROL); + + /* the register controls ovl also */ + value = dm_read_reg(ctx, addr); + + if (reg_val) { + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C11_C12); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[0], + GAMUT_REMAP_C11_C12, + GAMUT_REMAP_C11); + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[1], + GAMUT_REMAP_C11_C12, + GAMUT_REMAP_C12); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C13_C14); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[2], + GAMUT_REMAP_C13_C14, + GAMUT_REMAP_C13); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[3], + GAMUT_REMAP_C13_C14, + GAMUT_REMAP_C14); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C21_C22); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[4], + GAMUT_REMAP_C21_C22, + GAMUT_REMAP_C21); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[5], + GAMUT_REMAP_C21_C22, + GAMUT_REMAP_C22); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C23_C24); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[6], + GAMUT_REMAP_C23_C24, + GAMUT_REMAP_C23); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[7], + GAMUT_REMAP_C23_C24, + GAMUT_REMAP_C24); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C31_C32); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[8], + GAMUT_REMAP_C31_C32, + GAMUT_REMAP_C31); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[9], + GAMUT_REMAP_C31_C32, + GAMUT_REMAP_C32); + + dm_write_reg(ctx, addr, reg_data); + } + { + uint32_t reg_data = 0; + uint32_t addr = DCP_REG(mmGAMUT_REMAP_C33_C34); + + /* fixed S2.13 format */ + set_reg_field_value( + reg_data, + reg_val[10], + GAMUT_REMAP_C33_C34, + GAMUT_REMAP_C33); + + /* fixed S0.13 format */ + set_reg_field_value( + reg_data, + reg_val[11], + GAMUT_REMAP_C33_C34, + GAMUT_REMAP_C34); + + dm_write_reg(ctx, addr, reg_data); + } + + set_reg_field_value( + value, + 1, + GAMUT_REMAP_CONTROL, + GRPH_GAMUT_REMAP_MODE); + + } else + set_reg_field_value( + value, + 0, + GAMUT_REMAP_CONTROL, + GRPH_GAMUT_REMAP_MODE); + + addr = DCP_REG(mmGAMUT_REMAP_CONTROL); + dm_write_reg(ctx, addr, value); + +} + +/** + ***************************************************************************** + * Function: dal_transform_wide_gamut_set_gamut_remap + * + * @param [in] const struct grph_csc_adjustment *adjust + * + * @return + * void + * + * @note calculate and apply color temperature adjustment to in Rgb color space + * + * @see + * + ***************************************************************************** + */ +void dce110_transform_set_gamut_remap( + struct transform *xfm, + const struct grph_csc_adjustment *adjust) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + + if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW || + adjust->temperature_divider == 0) + program_gamut_remap(xfm110, NULL); + else { + struct fixed31_32 arr_matrix[GAMUT_MATRIX_SIZE]; + uint16_t arr_reg_val[GAMUT_MATRIX_SIZE]; + + arr_matrix[0] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[0], + adjust->temperature_divider); + arr_matrix[1] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[1], + adjust->temperature_divider); + arr_matrix[2] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[2], + adjust->temperature_divider); + arr_matrix[3] = dal_fixed31_32_zero; + + arr_matrix[4] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[3], + adjust->temperature_divider); + arr_matrix[5] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[4], + adjust->temperature_divider); + arr_matrix[6] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[5], + adjust->temperature_divider); + arr_matrix[7] = dal_fixed31_32_zero; + + arr_matrix[8] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[6], + adjust->temperature_divider); + arr_matrix[9] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[7], + adjust->temperature_divider); + arr_matrix[10] = + dal_fixed31_32_from_fraction( + adjust->temperature_matrix[8], + adjust->temperature_divider); + arr_matrix[11] = dal_fixed31_32_zero; + + convert_float_matrix( + arr_reg_val, arr_matrix, GAMUT_MATRIX_SIZE); + + program_gamut_remap(xfm110, arr_reg_val); + } +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c new file mode 100644 index 000000000000..7c15a13cb9fc --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_scl.c @@ -0,0 +1,818 @@ +/* + * 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.h" + +#define UP_SCALER_RATIO_MAX 16000 +#define DOWN_SCALER_RATIO_MAX 250 +#define SCALER_RATIO_DIVIDER 1000 + +#define SCL_REG(reg)\ + (reg + xfm110->offsets.scl_offset) + +#define DCFE_REG(reg)\ + (reg + xfm110->offsets.dcfe_offset) + +static void disable_enhanced_sharpness(struct dce110_transform *xfm110) +{ + uint32_t value; + + value = dm_read_reg(xfm110->base.ctx, + SCL_REG(mmSCL_F_SHARP_CONTROL)); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_HF_SHARP_EN); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_VF_SHARP_EN); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_HF_SHARP_SCALE_FACTOR); + + set_reg_field_value(value, 0, + SCL_F_SHARP_CONTROL, SCL_VF_SHARP_SCALE_FACTOR); + + dm_write_reg(xfm110->base.ctx, + SCL_REG(mmSCL_F_SHARP_CONTROL), value); +} + +/** +* Function: +* void setup_scaling_configuration +* +* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps +* Input: data +* +* Output: + void +*/ +static bool setup_scaling_configuration( + struct dce110_transform *xfm110, + const struct scaler_data *data) +{ + struct dc_context *ctx = xfm110->base.ctx; + uint32_t addr; + uint32_t value; + + if (data->taps.h_taps + data->taps.v_taps <= 2) { + dce110_transform_set_scaler_bypass(&xfm110->base); + return false; + } + + { + addr = SCL_REG(mmSCL_MODE); + value = dm_read_reg(ctx, addr); + + if (data->dal_pixel_format <= PIXEL_FORMAT_GRPH_END) + set_reg_field_value(value, 1, SCL_MODE, SCL_MODE); + else + set_reg_field_value(value, 2, SCL_MODE, SCL_MODE); + + set_reg_field_value(value, 1, SCL_MODE, SCL_PSCL_EN); + + dm_write_reg(ctx, addr, value); + } + { + addr = SCL_REG(mmSCL_TAP_CONTROL); + value = dm_read_reg(ctx, addr); + + set_reg_field_value(value, data->taps.h_taps - 1, + SCL_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.v_taps - 1, + SCL_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + + dm_write_reg(ctx, addr, value); + } + { + addr = SCL_REG(mmSCL_CONTROL); + value = dm_read_reg(ctx, addr); + /* 1 - Replaced out of bound pixels with edge */ + set_reg_field_value(value, 1, SCL_CONTROL, SCL_BOUNDARY_MODE); + + /* 1 - Replaced out of bound pixels with the edge pixel. */ + dm_write_reg(ctx, addr, value); + } + + return true; +} + +/** +* Function: +* void program_overscan +* +* Purpose: Programs overscan border +* Input: overscan +* +* Output: + void +*/ +static void program_overscan( + struct dce110_transform *xfm110, + const struct overscan_info *overscan) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + set_reg_field_value(overscan_left_right, overscan->left, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan->right, + EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, overscan->top, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan->bottom, + EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dm_write_reg(xfm110->base.ctx, + SCL_REG(mmEXT_OVERSCAN_LEFT_RIGHT), + overscan_left_right); + + dm_write_reg(xfm110->base.ctx, + SCL_REG(mmEXT_OVERSCAN_TOP_BOTTOM), + overscan_top_bottom); +} + +static void program_two_taps_filter( + struct dce110_transform *xfm110, + bool enable, + bool vertical) +{ + uint32_t addr; + uint32_t value; + /* 1: Hard coded 2 tap filter + * 0: Programmable 2 tap filter from coefficient RAM + */ + if (vertical) { + addr = SCL_REG(mmSCL_VERT_FILTER_CONTROL); + value = dm_read_reg(xfm110->base.ctx, addr); + set_reg_field_value( + value, + enable ? 1 : 0, + SCL_VERT_FILTER_CONTROL, + SCL_V_2TAP_HARDCODE_COEF_EN); + + } else { + addr = SCL_REG(mmSCL_HORZ_FILTER_CONTROL); + value = dm_read_reg(xfm110->base.ctx, addr); + set_reg_field_value( + value, + enable ? 1 : 0, + SCL_HORZ_FILTER_CONTROL, + SCL_H_2TAP_HARDCODE_COEF_EN); + } + + dm_write_reg(xfm110->base.ctx, addr, value); +} + +static void set_coeff_update_complete(struct dce110_transform *xfm110) +{ + uint32_t value; + uint32_t addr = SCL_REG(mmSCL_UPDATE); + + value = dm_read_reg(xfm110->base.ctx, addr); + set_reg_field_value(value, 1, + SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dm_write_reg(xfm110->base.ctx, addr, value); +} + +static void program_filter( + struct dce110_transform *xfm110, + enum ram_filter_type filter_type, + struct scaler_filter_params *scl_filter_params, + uint32_t *coeffs, + uint32_t coeffs_num) +{ + uint32_t phase = 0; + uint32_t array_idx = 0; + uint32_t pair = 0; + + uint32_t taps_pairs = (scl_filter_params->taps + 1) / 2; + uint32_t phases_to_program = scl_filter_params->phases / 2 + 1; + + uint32_t i; + uint32_t addr; + uint32_t select_addr; + uint32_t select; + uint32_t data; + /* We need to disable power gating on coeff memory to do programming */ + + uint32_t pwr_ctrl_orig; + uint32_t pwr_ctrl_off; + + addr = DCFE_REG(mmDCFE_MEM_PWR_CTRL); + pwr_ctrl_orig = dm_read_reg(xfm110->base.ctx, addr); + pwr_ctrl_off = pwr_ctrl_orig; + set_reg_field_value( + pwr_ctrl_off, + 1, + DCFE_MEM_PWR_CTRL, + SCL_COEFF_MEM_PWR_DIS); + dm_write_reg(xfm110->base.ctx, addr, pwr_ctrl_off); + + addr = DCFE_REG(mmDCFE_MEM_PWR_STATUS); + /* Wait to disable gating: */ + for (i = 0; + i < 10 && + get_reg_field_value( + dm_read_reg(xfm110->base.ctx, addr), + DCFE_MEM_PWR_STATUS, + SCL_COEFF_MEM_PWR_STATE); + i++) + dm_delay_in_microseconds(xfm110->base.ctx, 1); + + ASSERT(i < 10); + + select_addr = SCL_REG(mmSCL_COEF_RAM_SELECT); + select = dm_read_reg(xfm110->base.ctx, select_addr); + + set_reg_field_value( + select, + filter_type, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_FILTER_TYPE); + set_reg_field_value( + select, + 0, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_TAP_PAIR_IDX); + set_reg_field_value( + select, + 0, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_PHASE); + + data = 0; + + for (phase = 0; phase < phases_to_program; phase++) { + /* we always program N/2 + 1 phases, total phases N, but N/2-1 + * are just mirror phase 0 is unique and phase N/2 is unique + * if N is even + */ + + set_reg_field_value( + select, + phase, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_PHASE); + + for (pair = 0; pair < taps_pairs; pair++) { + set_reg_field_value( + select, + pair, + SCL_COEF_RAM_SELECT, + SCL_C_RAM_TAP_PAIR_IDX); + dm_write_reg(xfm110->base.ctx, select_addr, select); + + /* even tap write enable */ + set_reg_field_value( + data, + 1, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF_EN); + /* even tap data */ + set_reg_field_value( + data, + coeffs[array_idx], + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF); + + /* if we have odd number of taps and the last pair is + * here then we do not need to program + */ + if (scl_filter_params->taps % 2 && + pair == taps_pairs - 1) { + /* odd tap write disable */ + set_reg_field_value( + data, + 0, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + set_reg_field_value( + data, + 0, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + array_idx += 1; + } else { + /* odd tap write enable */ + set_reg_field_value( + data, + 1, + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + /* dbg_val: 0x1000 / sclFilterParams->taps; */ + set_reg_field_value( + data, + coeffs[array_idx + 1], + SCL_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + + array_idx += 2; + } + + dm_write_reg( + xfm110->base.ctx, + SCL_REG(mmSCL_COEF_RAM_TAP_DATA), + data); + } + } + + ASSERT(coeffs_num == array_idx); + + /* reset the power gating register */ + dm_write_reg( + xfm110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_CTRL), + pwr_ctrl_orig); + + set_coeff_update_complete(xfm110); +} + +/* + * + * Populates an array with filter coefficients in 1.1.12 fixed point form +*/ +static bool get_filter_coefficients( + struct dce110_transform *xfm110, + uint32_t taps, + uint32_t **data_tab, + uint32_t *data_size) +{ + uint32_t num = 0; + uint32_t i; + const struct fixed31_32 *filter = + dal_scaler_filter_get( + xfm110->base.filter, + data_tab, + &num); + uint32_t *data_row; + + if (!filter) { + BREAK_TO_DEBUGGER(); + return false; + } + data_row = *data_tab; + + for (i = 0; i < num; ++i) { + /* req. format sign fixed 1.1.12, the values are always between + * [-1; 1] + * + * Each phase is mirrored as follows : + * 0 : Phase 0 + * 1 : Phase 1 or Phase 64 - 1 / 128 - 1 + * N : Phase N or Phase 64 - N / 128 - N + * + * Convert from Fixed31_32 to 1.1.12 by using floor on value + * shifted by number of required fractional bits(12) + */ + struct fixed31_32 value = filter[i]; + + data_row[i] = + dal_fixed31_32_floor(dal_fixed31_32_shl(value, 12)) & + 0x3FFC; + } + *data_size = num; + + return true; +} + +static bool program_multi_taps_filter( + struct dce110_transform *xfm110, + const struct scaler_data *data, + bool horizontal) +{ + struct scaler_filter_params filter_params; + enum ram_filter_type filter_type; + uint32_t src_size; + uint32_t dst_size; + + uint32_t *filter_data = NULL; + uint32_t filter_data_size = 0; + + /* 16 phases total for DCE11 */ + filter_params.phases = 16; + + if (horizontal) { + filter_params.taps = data->taps.h_taps; + filter_params.sharpness = data->h_sharpness; + filter_params.flags.bits.HORIZONTAL = 1; + + src_size = data->viewport.width; + dst_size = + dal_fixed31_32_floor( + dal_fixed31_32_div( + dal_fixed31_32_from_int( + data->viewport.width), + data->ratios->horz)); + + filter_type = FILTER_TYPE_RGB_Y_HORIZONTAL; + } else { + filter_params.taps = data->taps.v_taps; + filter_params.sharpness = data->v_sharpness; + filter_params.flags.bits.HORIZONTAL = 0; + + src_size = data->viewport.height; + dst_size = + dal_fixed31_32_floor( + dal_fixed31_32_div( + dal_fixed31_32_from_int( + data->viewport.height), + data->ratios->vert)); + + filter_type = FILTER_TYPE_RGB_Y_VERTICAL; + } + + /* 1. Generate the coefficients */ + if (!dal_scaler_filter_generate( + xfm110->base.filter, + &filter_params, + src_size, + dst_size)) + return false; + + /* 2. Convert coefficients to fixed point format 1.12 (note coeff. + * could be negative(!) and range is [ from -1 to 1 ]) */ + if (!get_filter_coefficients( + xfm110, + filter_params.taps, + &filter_data, + &filter_data_size)) + return false; + + /* 3. Program the filter */ + program_filter( + xfm110, + filter_type, + &filter_params, + filter_data, + filter_data_size); + + /* 4. Program the alpha if necessary */ + if (data->flags.bits.SHOULD_PROGRAM_ALPHA) { + if (horizontal) + filter_type = FILTER_TYPE_ALPHA_HORIZONTAL; + else + filter_type = FILTER_TYPE_ALPHA_VERTICAL; + + program_filter( + xfm110, + filter_type, + &filter_params, + filter_data, + filter_data_size); + } + + return true; +} + +static void program_viewport( + struct dce110_transform *xfm110, + const struct rect *view_port) +{ + struct dc_context *ctx = xfm110->base.ctx; + uint32_t value = 0; + uint32_t addr = 0; + + addr = SCL_REG(mmVIEWPORT_START); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + view_port->x, + VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + view_port->y, + VIEWPORT_START, + VIEWPORT_Y_START); + dm_write_reg(ctx, addr, value); + + addr = SCL_REG(mmVIEWPORT_SIZE); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + view_port->height, + VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + view_port->width, + VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dm_write_reg(ctx, addr, value); + + /* TODO: add stereo support */ +} + +static void calculate_inits( + struct dce110_transform *xfm110, + const struct scaler_data *data, + struct scl_ratios_inits *inits) +{ + struct fixed31_32 h_init; + struct fixed31_32 v_init; + struct fixed31_32 v_init_bot; + + inits->bottom_enable = 0; + inits->h_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios->horz) << 5; + inits->v_int_scale_ratio = + dal_fixed31_32_u2d19(data->ratios->vert) << 5; + + h_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->horz, + dal_fixed31_32_from_int(data->taps.h_taps + 1)), + 2); + inits->h_init.integer = dal_fixed31_32_floor(h_init); + inits->h_init.fraction = dal_fixed31_32_u0d19(h_init) << 5; + + v_init = + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->vert, + dal_fixed31_32_from_int(data->taps.v_taps + 1)), + 2); + inits->v_init.integer = dal_fixed31_32_floor(v_init); + inits->v_init.fraction = dal_fixed31_32_u0d19(v_init) << 5; + + if (data->flags.bits.INTERLACED) { + v_init_bot = + dal_fixed31_32_add( + dal_fixed31_32_div_int( + dal_fixed31_32_add( + data->ratios->vert, + dal_fixed31_32_from_int( + data->taps.v_taps + 1)), + 2), + data->ratios->vert); + inits->v_init_bottom.integer = dal_fixed31_32_floor(v_init_bot); + inits->v_init_bottom.fraction = + dal_fixed31_32_u0d19(v_init_bot) << 5; + + inits->bottom_enable = 1; + } +} + +static void program_scl_ratios_inits( + struct dce110_transform *xfm110, + struct scl_ratios_inits *inits) +{ + uint32_t addr = SCL_REG(mmSCL_HORZ_FILTER_SCALE_RATIO); + uint32_t value = 0; + + set_reg_field_value( + value, + inits->h_int_scale_ratio, + SCL_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dm_write_reg(xfm110->base.ctx, addr, value); + + addr = SCL_REG(mmSCL_VERT_FILTER_SCALE_RATIO); + value = 0; + set_reg_field_value( + value, + inits->v_int_scale_ratio, + SCL_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dm_write_reg(xfm110->base.ctx, addr, value); + + addr = SCL_REG(mmSCL_HORZ_FILTER_INIT); + value = 0; + set_reg_field_value( + value, + inits->h_init.integer, + SCL_HORZ_FILTER_INIT, + SCL_H_INIT_INT); + set_reg_field_value( + value, + inits->h_init.fraction, + SCL_HORZ_FILTER_INIT, + SCL_H_INIT_FRAC); + dm_write_reg(xfm110->base.ctx, addr, value); + + addr = SCL_REG(mmSCL_VERT_FILTER_INIT); + value = 0; + set_reg_field_value( + value, + inits->v_init.integer, + SCL_VERT_FILTER_INIT, + SCL_V_INIT_INT); + set_reg_field_value( + value, + inits->v_init.fraction, + SCL_VERT_FILTER_INIT, + SCL_V_INIT_FRAC); + dm_write_reg(xfm110->base.ctx, addr, value); + + if (inits->bottom_enable) { + addr = SCL_REG(mmSCL_VERT_FILTER_INIT_BOT); + value = 0; + set_reg_field_value( + value, + inits->v_init_bottom.integer, + SCL_VERT_FILTER_INIT_BOT, + SCL_V_INIT_INT_BOT); + set_reg_field_value( + value, + inits->v_init_bottom.fraction, + SCL_VERT_FILTER_INIT_BOT, + SCL_V_INIT_FRAC_BOT); + dm_write_reg(xfm110->base.ctx, addr, value); + } + + addr = SCL_REG(mmSCL_AUTOMATIC_MODE_CONTROL); + value = 0; + set_reg_field_value( + value, + 0, + SCL_AUTOMATIC_MODE_CONTROL, + SCL_V_CALC_AUTO_RATIO_EN); + set_reg_field_value( + value, + 0, + SCL_AUTOMATIC_MODE_CONTROL, + SCL_H_CALC_AUTO_RATIO_EN); + dm_write_reg(xfm110->base.ctx, addr, value); +} + +static void get_viewport( + struct dce110_transform *xfm110, + struct rect *current_view_port) +{ + uint32_t value_start; + uint32_t value_size; + + if (current_view_port == NULL) + return; + + value_start = dm_read_reg(xfm110->base.ctx, SCL_REG(mmVIEWPORT_START)); + value_size = dm_read_reg(xfm110->base.ctx, SCL_REG(mmVIEWPORT_SIZE)); + + current_view_port->x = get_reg_field_value( + value_start, + VIEWPORT_START, + VIEWPORT_X_START); + current_view_port->y = get_reg_field_value( + value_start, + VIEWPORT_START, + VIEWPORT_Y_START); + current_view_port->height = get_reg_field_value( + value_size, + VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + current_view_port->width = get_reg_field_value( + value_size, + VIEWPORT_SIZE, + VIEWPORT_WIDTH); +} + + +bool dce110_transform_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + bool is_scaling_required; + struct dc_context *ctx = xfm->ctx; + + { + uint32_t addr = SCL_REG(mmSCL_BYPASS_CONTROL); + uint32_t value = dm_read_reg(xfm->ctx, addr); + + set_reg_field_value( + value, + 0, + SCL_BYPASS_CONTROL, + SCL_BYPASS_MODE); + dm_write_reg(xfm->ctx, addr, value); + } + + disable_enhanced_sharpness(xfm110); + + /* 3. Program overscan */ + program_overscan(xfm110, &data->overscan); + + /* 4. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm110, data); + if (is_scaling_required) { + /* 5. Calculate and program ratio, filter initialization */ + struct scl_ratios_inits inits = { 0 }; + + calculate_inits(xfm110, data, &inits); + + program_scl_ratios_inits(xfm110, &inits); + + /* 6. Program vertical filters */ + if (data->taps.v_taps > 2) { + program_two_taps_filter(xfm110, false, true); + + if (!program_multi_taps_filter(xfm110, data, false)) { + dal_logger_write(ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed vertical taps programming\n"); + return false; + } + } else + program_two_taps_filter(xfm110, true, true); + + /* 7. Program horizontal filters */ + if (data->taps.h_taps > 2) { + program_two_taps_filter(xfm110, false, false); + + if (!program_multi_taps_filter(xfm110, data, true)) { + dal_logger_write(ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed horizontal taps programming\n"); + return false; + } + } else + program_two_taps_filter(xfm110, true, false); + } + + return true; +} + +void dce110_transform_set_scaler_bypass(struct transform *xfm) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + uint32_t sclv_mode; + + disable_enhanced_sharpness(xfm110); + + sclv_mode = dm_read_reg(xfm->ctx, SCL_REG(mmSCL_MODE)); + set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_MODE); + set_reg_field_value(sclv_mode, 0, SCL_MODE, SCL_PSCL_EN); + dm_write_reg(xfm->ctx, SCL_REG(mmSCL_MODE), sclv_mode); +} + +bool dce110_transform_update_viewport( + struct transform *xfm, + const struct rect *view_port, + bool is_fbc_attached) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + bool program_req = false; + struct rect current_view_port; + + if (view_port == NULL) + return program_req; + + get_viewport(xfm110, ¤t_view_port); + + if (current_view_port.x != view_port->x || + current_view_port.y != view_port->y || + current_view_port.height != view_port->height || + current_view_port.width != view_port->width) + program_req = true; + + if (program_req) { + /*underlay viewport is programmed with scaler + *program_viewport function pointer is not exposed*/ + program_viewport(xfm110, view_port); + } + + return program_req; +} + +void dce110_transform_set_scaler_filter( + struct transform *xfm, + struct scaler_filter *filter) +{ + xfm->filter = filter; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c new file mode 100644 index 000000000000..1968296bd596 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_transform_sclv.c @@ -0,0 +1,531 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_transform.h" + +#define NOT_IMPLEMENTED() DAL_LOGGER_NOT_IMPL(LOG_MINOR_COMPONENT_CONTROLLER,\ + "TRANSFORM SCALER:%s()\n", __func__) + +/* +***************************************************************************** +* Function: calculateViewport +* +* @brief +* Calculates all of the data required to set the viewport +* +* @param [in] pData: scaler settings data +* @param [out] pLumaVp: luma viewport information +* @param [out] pChromaVp: chroma viewport information +* @param [out] srcResCx2: source chroma resolution times 2 - for multi-taps +* +***************************************************************************** +*/ +static void calculate_viewport( + const struct scaler_data *scl_data, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + /*Do not set chroma vp for rgb444 pixel format*/ + luma_viewport->x = scl_data->viewport.x - scl_data->viewport.x % 2; + luma_viewport->y = scl_data->viewport.y - scl_data->viewport.y % 2; + luma_viewport->width = + scl_data->viewport.width - scl_data->viewport.width % 2; + luma_viewport->height = + scl_data->viewport.height - scl_data->viewport.height % 2; + + + if (scl_data->dal_pixel_format == PIXEL_FORMAT_422BPP16) { + luma_viewport->width += luma_viewport->width % 2; + + chroma_viewport->x = luma_viewport->x / 2; + chroma_viewport->width = luma_viewport->width / 2; + } else if (scl_data->dal_pixel_format == PIXEL_FORMAT_420BPP12) { + luma_viewport->height += luma_viewport->height % 2; + luma_viewport->width += luma_viewport->width % 2; + /*for 420 video chroma is 1/4 the area of luma, scaled + *vertically and horizontally + */ + chroma_viewport->x = luma_viewport->x / 2; + chroma_viewport->y = luma_viewport->y / 2; + chroma_viewport->height = luma_viewport->height / 2; + chroma_viewport->width = luma_viewport->width / 2; + } +} + + +static void program_viewport( + struct dce110_transform *xfm110, + struct rect *luma_view_port, + struct rect *chroma_view_port) +{ + struct dc_context *ctx = xfm110->base.ctx; + uint32_t value = 0; + uint32_t addr = 0; + + if (luma_view_port->width != 0 && luma_view_port->height != 0) { + addr = mmSCLV_VIEWPORT_START; + value = 0; + set_reg_field_value( + value, + luma_view_port->x, + SCLV_VIEWPORT_START, + VIEWPORT_X_START); + set_reg_field_value( + value, + luma_view_port->y, + SCLV_VIEWPORT_START, + VIEWPORT_Y_START); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VIEWPORT_SIZE; + value = 0; + set_reg_field_value( + value, + luma_view_port->height, + SCLV_VIEWPORT_SIZE, + VIEWPORT_HEIGHT); + set_reg_field_value( + value, + luma_view_port->width, + SCLV_VIEWPORT_SIZE, + VIEWPORT_WIDTH); + dm_write_reg(ctx, addr, value); + } + + if (chroma_view_port->width != 0 && chroma_view_port->height != 0) { + addr = mmSCLV_VIEWPORT_START_C; + value = 0; + set_reg_field_value( + value, + chroma_view_port->x, + SCLV_VIEWPORT_START_C, + VIEWPORT_X_START_C); + set_reg_field_value( + value, + chroma_view_port->y, + SCLV_VIEWPORT_START_C, + VIEWPORT_Y_START_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VIEWPORT_SIZE_C; + value = 0; + set_reg_field_value( + value, + chroma_view_port->height, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_HEIGHT_C); + set_reg_field_value( + value, + chroma_view_port->width, + SCLV_VIEWPORT_SIZE_C, + VIEWPORT_WIDTH_C); + dm_write_reg(ctx, addr, value); + } + /* TODO: add stereo support */ +} + + +/* Until and For MPO video play story, to reduce time for implementation, + * below limits are applied for now: 2_TAPS only + * Use auto-calculated filter values + * Following routines will be empty for now: + * + * programSclRatiosInits -- calcualate scaler ratio manually + * calculateInits --- calcualate scaler ratio manually + * programFilter -- multi-taps + * GetOptimalNumberOfTaps -- will hard coded to 2 TAPS + * GetNextLowerNumberOfTaps -- will hard coded to 2TAPS + * validateRequestedScaleRatio - used by GetOptimalNumberOfTaps internally + */ + +/** +* Function: +* void setup_scaling_configuration +* +* Purpose: setup scaling mode : bypass, RGb, YCbCr and nummber of taps +* Input: data +* +* Output: + void +*/ +static bool setup_scaling_configuration( + struct dce110_transform *xfm110, + const struct scaler_data *data) +{ + bool is_scaling_needed = false; + struct dc_context *ctx = xfm110->base.ctx; + uint32_t value = 0; + + if (data->taps.h_taps + data->taps.v_taps > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN); + is_scaling_needed = true; + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); + } + + if (data->taps.h_taps_c + data->taps.v_taps_c > 2) { + set_reg_field_value(value, 1, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 1, SCLV_MODE, SCL_PSCL_EN_C); + is_scaling_needed = true; + } else if (data->dal_pixel_format != PIXEL_FORMAT_420BPP12 && + data->dal_pixel_format != PIXEL_FORMAT_422BPP16) { + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_MODE), + SCLV_MODE, + SCL_MODE_C); + set_reg_field_value( + value, + get_reg_field_value(value, SCLV_MODE, SCL_PSCL_EN), + SCLV_MODE, + SCL_PSCL_EN_C); + } else { + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); + } + dm_write_reg(ctx, mmSCLV_MODE, value); + + { + value = dm_read_reg(ctx, mmSCLV_TAP_CONTROL); + + set_reg_field_value(value, data->taps.h_taps - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.v_taps - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS); + + set_reg_field_value(value, data->taps.h_taps_c - 1, + SCLV_TAP_CONTROL, SCL_H_NUM_OF_TAPS_C); + + set_reg_field_value(value, data->taps.v_taps_c - 1, + SCLV_TAP_CONTROL, SCL_V_NUM_OF_TAPS_C); + + dm_write_reg(ctx, mmSCLV_TAP_CONTROL, value); + } + + { + /* we can ignore this register because we are ok with hw + * default 0 -- change to 1 according to dal2 code*/ + value = dm_read_reg(ctx, mmSCLV_CONTROL); + /* 0 - Replaced out of bound pixels with black pixel + * (or any other required color) */ + set_reg_field_value(value, 1, SCLV_CONTROL, SCL_BOUNDARY_MODE); + + /* 1 - Replaced out of bound pixels with the edge pixel. */ + dm_write_reg(ctx, mmSCLV_CONTROL, value); + } + + return is_scaling_needed; +} + +/** +* Function: +* void program_overscan +* +* Purpose: Programs overscan border +* Input: overscan +* +* Output: + void +*/ +static void program_overscan( + struct dce110_transform *xfm110, + const struct overscan_info *overscan) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + set_reg_field_value(overscan_left_right, overscan->left, + SCLV_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_LEFT); + + set_reg_field_value(overscan_left_right, overscan->right, + SCLV_EXT_OVERSCAN_LEFT_RIGHT, EXT_OVERSCAN_RIGHT); + + set_reg_field_value(overscan_top_bottom, overscan->top, + SCLV_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_TOP); + + set_reg_field_value(overscan_top_bottom, overscan->bottom, + SCLV_EXT_OVERSCAN_TOP_BOTTOM, EXT_OVERSCAN_BOTTOM); + + dm_write_reg(xfm110->base.ctx, + mmSCLV_EXT_OVERSCAN_LEFT_RIGHT, + overscan_left_right); + + dm_write_reg(xfm110->base.ctx, + mmSCLV_EXT_OVERSCAN_TOP_BOTTOM, + overscan_top_bottom); +} +/* +static void setup_auto_scaling(struct dce110_transform *xfm110) +{ + uint32_t value = 0; + set_reg_field_value(value, 1, SCLV_AUTOMATIC_MODE_CONTROL, + SCL_V_CALC_AUTO_RATIO_EN); + set_reg_field_value(value, 1, SCLV_AUTOMATIC_MODE_CONTROL, + SCL_H_CALC_AUTO_RATIO_EN); + dal_write_reg(xfm->ctx, + xfm->regs[IDX_SCL_AUTOMATIC_MODE_CONTROL], + value); +} +*/ + +static void program_two_taps_filter_horz( + struct dce110_transform *xfm110, + bool hardcode_coff) +{ + uint32_t value = 0; + + if (hardcode_coff) + set_reg_field_value( + value, + 1, + SCLV_HORZ_FILTER_CONTROL, + SCL_H_2TAP_HARDCODE_COEF_EN); + + dm_write_reg(xfm110->base.ctx, + mmSCLV_HORZ_FILTER_CONTROL, + value); +} + +static void program_two_taps_filter_vert( + struct dce110_transform *xfm110, + bool hardcode_coff) +{ + uint32_t value = 0; + + if (hardcode_coff) + set_reg_field_value(value, 1, SCLV_VERT_FILTER_CONTROL, + SCL_V_2TAP_HARDCODE_COEF_EN); + + dm_write_reg(xfm110->base.ctx, + mmSCLV_VERT_FILTER_CONTROL, + value); +} + +static void set_coeff_update_complete( + struct dce110_transform *xfm110) +{ + /*TODO: Until now, only scaler bypass, up-scaler 2 -TAPS coeff auto + * calculation are implemented. Coefficient RAM is not used + * Do not check this flag yet + */ + + /*uint32_t value; + uint32_t addr = xfm->regs[IDX_SCL_UPDATE]; + + value = dal_read_reg(xfm->ctx, addr); + set_reg_field_value(value, 0, + SCL_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dal_write_reg(xfm->ctx, addr, value);*/ +} + +static bool program_multi_taps_filter( + struct dce110_transform *xfm110, + const struct scaler_data *data, + bool horizontal) +{ + struct dc_context *ctx = xfm110->base.ctx; + + NOT_IMPLEMENTED(); + return false; +} + +static void calculate_inits( + struct dce110_transform *xfm110, + const struct scaler_data *data, + struct sclv_ratios_inits *inits, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + if (data->dal_pixel_format == PIXEL_FORMAT_420BPP12 || + data->dal_pixel_format == PIXEL_FORMAT_422BPP16) + inits->chroma_enable = true; + + /* TODO: implement rest of this function properly */ + if (inits->chroma_enable) { + inits->h_int_scale_ratio_luma = 0x1000000; + inits->v_int_scale_ratio_luma = 0x1000000; + inits->h_int_scale_ratio_chroma = 0x800000; + inits->v_int_scale_ratio_chroma = 0x800000; + } +} + +static void program_scl_ratios_inits( + struct dce110_transform *xfm110, + struct sclv_ratios_inits *inits) +{ + struct dc_context *ctx = xfm110->base.ctx; + uint32_t addr = mmSCLV_HORZ_FILTER_SCALE_RATIO; + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + inits->h_int_scale_ratio_luma, + SCLV_HORZ_FILTER_SCALE_RATIO, + SCL_H_SCALE_RATIO); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_SCALE_RATIO; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + inits->v_int_scale_ratio_luma, + SCLV_VERT_FILTER_SCALE_RATIO, + SCL_V_SCALE_RATIO); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_SCALE_RATIO_C; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + inits->h_int_scale_ratio_chroma, + SCLV_HORZ_FILTER_SCALE_RATIO_C, + SCL_H_SCALE_RATIO_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_SCALE_RATIO_C; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + inits->v_int_scale_ratio_chroma, + SCLV_VERT_FILTER_SCALE_RATIO_C, + SCL_V_SCALE_RATIO_C); + dm_write_reg(ctx, addr, value); +} + +void dce110_transform_underlay_set_scalerv_bypass(struct transform *xfm) +{ + uint32_t addr = mmSCLV_MODE; + uint32_t value = dm_read_reg(xfm->ctx, addr); + + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE); + set_reg_field_value(value, 0, SCLV_MODE, SCL_MODE_C); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN); + set_reg_field_value(value, 0, SCLV_MODE, SCL_PSCL_EN_C); + dm_write_reg(xfm->ctx, addr, value); +} + +bool dce110_transform_underlay_is_scaling_enabled(struct transform *xfm) +{ + uint32_t value = dm_read_reg(xfm->ctx, mmSCLV_MODE); + uint8_t scl_mode = get_reg_field_value(value, SCLV_MODE, SCL_MODE); + + return scl_mode == 0; +} + +/* TODO: sync this one with DAL2 */ +bool dce110_transform_underlay_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce110_transform *xfm110 = TO_DCE110_TRANSFORM(xfm); + bool is_scaling_required; + struct rect luma_viewport = {0}; + struct rect chroma_viewport = {0}; + struct dc_context *ctx = xfm->ctx; + + /* 1. Lock Scaler TODO: enable?*/ + /*set_scaler_update_lock(xfm, true);*/ + + /* 2. Calculate viewport, viewport programming should happen after init + * calculations as they may require an adjustment in the viewport. + */ + + calculate_viewport(data, &luma_viewport, &chroma_viewport); + + /* 3. Program overscan */ + program_overscan(xfm110, &data->overscan); + + /* 4. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm110, data); + + if (is_scaling_required) { + /* 5. Calculate and program ratio, filter initialization */ + + struct sclv_ratios_inits inits = { 0 }; + + calculate_inits( + xfm110, + data, + &inits, + &luma_viewport, + &chroma_viewport); + + program_scl_ratios_inits(xfm110, &inits); + + /*scaler coeff of 2-TAPS use hardware auto calculated value*/ + + /* 6. Program vertical filters */ + if (data->taps.v_taps > 2) { + program_two_taps_filter_vert(xfm110, false); + + if (!program_multi_taps_filter(xfm110, data, false)) { + dal_logger_write(ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed vertical taps programming\n"); + return false; + } + } else + program_two_taps_filter_vert(xfm110, true); + + /* 7. Program horizontal filters */ + if (data->taps.h_taps > 2) { + program_two_taps_filter_horz(xfm110, false); + + if (!program_multi_taps_filter(xfm110, data, true)) { + dal_logger_write(ctx->logger, + LOG_MAJOR_DCP, + LOG_MINOR_DCP_SCALER, + "Failed horizontal taps programming\n"); + return false; + } + } else + program_two_taps_filter_horz(xfm110, true); + } + + /* 8. Program the viewport */ + if (data->flags.bits.SHOULD_PROGRAM_VIEWPORT) + program_viewport(xfm110, &luma_viewport, &chroma_viewport); + + /* 9. Unlock the Scaler TODO: enable?*/ + /* Every call to "set_scaler_update_lock(xfm, TRUE)" + * must have a corresponding call to + * "set_scaler_update_lock(xfm, FALSE)" */ + /*set_scaler_update_lock(xfm, false);*/ + + /* TODO: investigate purpose/need of SHOULD_UNLOCK */ + if (data->flags.bits.SHOULD_UNLOCK == false) + set_coeff_update_complete(xfm110); + + return true; +} + -- 2.1.4 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel