diff options
author | Harry Wentland <harry.wentland@amd.com> | 2017-09-12 15:58:20 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-09-26 17:01:32 -0400 |
commit | 4562236b3bc0a28aeb6ee93b2d8a849a4c4e1c7c (patch) | |
tree | 84301c04dcaaa05c3318a8fe62cf62ab52ecc162 /drivers/gpu/drm/amd/display/dc/dce110 | |
parent | 9c5b2b0d409304c2e3c1f4d1c9bb4958e1d46f8f (diff) | |
download | linux-stable-4562236b3bc0a28aeb6ee93b2d8a849a4c4e1c7c.tar.gz linux-stable-4562236b3bc0a28aeb6ee93b2d8a849a4c4e1c7c.tar.bz2 linux-stable-4562236b3bc0a28aeb6ee93b2d8a849a4c4e1c7c.zip |
drm/amd/dc: Add dc display driver (v2)
Supported DCE versions: 8.0, 10.0, 11.0, 11.2
v2: rebase against 4.11
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dce110')
31 files changed, 13930 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dce110/Makefile b/drivers/gpu/drm/amd/display/dc/dce110/Makefile new file mode 100644 index 000000000000..cd7a9095fa06 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE110 = dce110_ipp.o dce110_ipp_cursor.o \ +dce110_ipp_gamma.o dce110_opp.o dce110_opp_csc.o \ +dce110_timing_generator.o dce110_opp_formatter.o dce110_opp_regamma.o \ +dce110_compressor.o dce110_mem_input.o dce110_hw_sequencer.o \ +dce110_resource.o \ +dce110_opp_regamma_v.o dce110_opp_csc_v.o dce110_timing_generator_v.o \ +dce110_mem_input_v.o dce110_opp_v.o dce110_transform_v.o + +AMD_DAL_DCE110 = $(addprefix $(AMDDALPATH)/dc/dce110/,$(DCE110)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE110) diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c new file mode 100644 index 000000000000..518150a414e2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c @@ -0,0 +1,859 @@ +/* + * 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 "gmc/gmc_8_2_sh_mask.h" +#include "gmc/gmc_8_2_d.h" + +#include "include/logger_interface.h" + +#include "dce110_compressor.h" + +#define DCP_REG(reg)\ + (reg + cp110->offsets.dcp_offset) +#define DMIF_REG(reg)\ + (reg + cp110->offsets.dmif_offset) + +static const struct dce110_compressor_reg_offsets reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG0_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG1_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), + .dmif_offset = + (mmDMIF_PG2_DPG_PIPE_DPM_CONTROL + - mmDMIF_PG0_DPG_PIPE_DPM_CONTROL), +} +}; + +static const uint32_t dce11_one_lpt_channel_max_resolution = 2560 * 1600; + +enum fbc_idle_force { + /* Bit 0 - Display registers updated */ + FBC_IDLE_FORCE_DISPLAY_REGISTER_UPDATE = 0x00000001, + + /* Bit 2 - FBC_GRPH_COMP_EN register updated */ + FBC_IDLE_FORCE_GRPH_COMP_EN = 0x00000002, + /* Bit 3 - FBC_SRC_SEL register updated */ + FBC_IDLE_FORCE_SRC_SEL_CHANGE = 0x00000004, + /* Bit 4 - FBC_MIN_COMPRESSION register updated */ + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE = 0x00000008, + /* Bit 5 - FBC_ALPHA_COMP_EN register updated */ + FBC_IDLE_FORCE_ALPHA_COMP_EN = 0x00000010, + /* Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated */ + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN = 0x00000020, + /* Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated */ + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF = 0x00000040, + + /* Bit 24 - Memory write to region 0 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION0 = 0x01000000, + /* Bit 25 - Memory write to region 1 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION1 = 0x02000000, + /* Bit 26 - Memory write to region 2 defined by MC registers */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION2 = 0x04000000, + /* Bit 27 - Memory write to region 3 defined by MC registers. */ + FBC_IDLE_FORCE_MEMORY_WRITE_TO_REGION3 = 0x08000000, + + /* Bit 28 - Memory write from any client other than MCIF */ + FBC_IDLE_FORCE_MEMORY_WRITE_OTHER_THAN_MCIF = 0x10000000, + /* Bit 29 - CG statics screen signal is inactive */ + FBC_IDLE_FORCE_CG_STATIC_SCREEN_IS_INACTIVE = 0x20000000, +}; + +static uint32_t lpt_size_alignment(struct dce110_compressor *cp110) +{ + /*LPT_ALIGNMENT (in bytes) = ROW_SIZE * #BANKS * # DRAM CHANNELS. */ + return cp110->base.raw_size * cp110->base.banks_num * + cp110->base.dram_channels_num; +} + +static uint32_t lpt_memory_control_config(struct dce110_compressor *cp110, + uint32_t lpt_control) +{ + /*LPT MC Config */ + if (cp110->base.options.bits.LPT_MC_CONFIG == 1) { + /* POSSIBLE VALUES for LPT NUM_PIPES (DRAM CHANNELS): + * 00 - 1 CHANNEL + * 01 - 2 CHANNELS + * 02 - 4 OR 6 CHANNELS + * (Only for discrete GPU, N/A for CZ) + * 03 - 8 OR 12 CHANNELS + * (Only for discrete GPU, N/A for CZ) */ + switch (cp110->base.dram_channels_num) { + case 2: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + case 1: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_PIPES); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT NUM_PIPES!!!", + __func__); + break; + } + + /* The mapping for LPT NUM_BANKS is in + * GRPH_CONTROL.GRPH_NUM_BANKS register field + * Specifies the number of memory banks for tiling + * purposes. Only applies to 2D and 3D tiling modes. + * POSSIBLE VALUES: + * 00 - DCP_GRPH_NUM_BANKS_2BANK: ADDR_SURF_2_BANK + * 01 - DCP_GRPH_NUM_BANKS_4BANK: ADDR_SURF_4_BANK + * 02 - DCP_GRPH_NUM_BANKS_8BANK: ADDR_SURF_8_BANK + * 03 - DCP_GRPH_NUM_BANKS_16BANK: ADDR_SURF_16_BANK */ + switch (cp110->base.banks_num) { + case 16: + set_reg_field_value( + lpt_control, + 3, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 8: + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 4: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + case 2: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_NUM_BANKS); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT NUM_BANKS!!!", + __func__); + break; + } + + /* The mapping is in DMIF_ADDR_CALC. + * ADDR_CONFIG_PIPE_INTERLEAVE_SIZE register field for + * Carrizo specifies the memory interleave per pipe. + * It effectively specifies the location of pipe bits in + * the memory address. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_PIPE_INTERLEAVE_256B: 256 byte + * interleave + * 01 - ADDR_CONFIG_PIPE_INTERLEAVE_512B: 512 byte + * interleave + */ + switch (cp110->base.channel_interleave_size) { + case 256: /*256B */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + case 512: /*512B */ + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_PIPE_INTERLEAVE_SIZE); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT INTERLEAVE_SIZE!!!", + __func__); + break; + } + + /* The mapping for LOW_POWER_TILING_ROW_SIZE is in + * DMIF_ADDR_CALC.ADDR_CONFIG_ROW_SIZE register field + * for Carrizo. Specifies the size of dram row in bytes. + * This should match up with NOOFCOLS field in + * MC_ARB_RAMCFG (ROW_SIZE = 4 * 2 ^^ columns). + * This register DMIF_ADDR_CALC is not used by the + * hardware as it is only used for addrlib assertions. + * POSSIBLE VALUES: + * 00 - ADDR_CONFIG_1KB_ROW: Treat 1KB as DRAM row + * boundary + * 01 - ADDR_CONFIG_2KB_ROW: Treat 2KB as DRAM row + * boundary + * 02 - ADDR_CONFIG_4KB_ROW: Treat 4KB as DRAM row + * boundary */ + switch (cp110->base.raw_size) { + case 4096: /*4 KB */ + set_reg_field_value( + lpt_control, + 2, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 2048: + set_reg_field_value( + lpt_control, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + case 1024: + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROW_SIZE); + break; + default: + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: Invalid LPT ROW_SIZE!!!", + __func__); + break; + } + } else { + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: LPT MC Configuration is not provided", + __func__); + } + + return lpt_control; +} + +static bool is_source_bigger_than_epanel_size( + struct dce110_compressor *cp110, + uint32_t source_view_width, + uint32_t source_view_height) +{ + if (cp110->base.embedded_panel_h_size != 0 && + cp110->base.embedded_panel_v_size != 0 && + ((source_view_width * source_view_height) > + (cp110->base.embedded_panel_h_size * + cp110->base.embedded_panel_v_size))) + return true; + + return false; +} + +static uint32_t align_to_chunks_number_per_line( + struct dce110_compressor *cp110, + uint32_t pixels) +{ + return 256 * ((pixels + 255) / 256); +} + +static void wait_for_fbc_state_changed( + struct dce110_compressor *cp110, + bool enabled) +{ + uint8_t counter = 0; + uint32_t addr = mmFBC_STATUS; + uint32_t value; + + while (counter < 10) { + value = dm_read_reg(cp110->base.ctx, addr); + if (get_reg_field_value( + value, + FBC_STATUS, + FBC_ENABLE_STATUS) == enabled) + break; + udelay(10); + counter++; + } + + if (counter == 10) { + dm_logger_write( + cp110->base.ctx->logger, LOG_WARNING, + "%s: wait counter exceeded, changes to HW not applied", + __func__); + } +} + +void dce110_compressor_power_up_fbc(struct compressor *compressor) +{ + uint32_t value; + uint32_t addr; + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value(value, 1, FBC_CNTL, FBC_EN); + set_reg_field_value(value, 2, FBC_CNTL, FBC_COHERENCY_MODE); + if (compressor->options.bits.CLK_GATING_DISABLED == 1) { + /* HW needs to do power measurement comparison. */ + set_reg_field_value( + value, + 0, + FBC_CNTL, + FBC_COMP_CLK_GATE_EN); + } + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_MODE; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_RLE_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_DPCM4_RGB_EN); + set_reg_field_value(value, 1, FBC_COMP_MODE, FBC_IND_EN); + dm_write_reg(compressor->ctx, addr, value); + + addr = mmFBC_COMP_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_COMP_CNTL, FBC_DEPTH_RGB08_EN); + dm_write_reg(compressor->ctx, addr, value); + /*FBC_MIN_COMPRESSION 0 ==> 2:1 */ + /* 1 ==> 4:1 */ + /* 2 ==> 8:1 */ + /* 0xF ==> 1:1 */ + set_reg_field_value(value, 0xF, FBC_COMP_CNTL, FBC_MIN_COMPRESSION); + dm_write_reg(compressor->ctx, addr, value); + compressor->min_compress_ratio = FBC_COMPRESS_RATIO_1TO1; + + value = 0; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT0, value); + + value = 0xFFFFFF; + dm_write_reg(compressor->ctx, mmFBC_IND_LUT1, value); +} + +void dce110_compressor_enable_fbc( + struct compressor *compressor, + uint32_t paths_num, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + (compressor->options.bits.DUMMY_BACKEND == 0) && + (!dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) && + (!is_source_bigger_than_epanel_size( + cp110, + params->source_view_width, + params->source_view_height))) { + + uint32_t addr; + uint32_t value; + + /* Before enabling FBC first need to enable LPT if applicable + * LPT state should always be changed (enable/disable) while FBC + * is disabled */ + if (compressor->options.bits.LPT_SUPPORT && (paths_num < 2) && + (params->source_view_width * + params->source_view_height <= + dce11_one_lpt_channel_max_resolution)) { + dce110_compressor_enable_lpt(compressor); + } + + addr = mmFBC_CNTL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + set_reg_field_value( + value, + params->inst, + FBC_CNTL, FBC_SRC_SEL); + dm_write_reg(compressor->ctx, addr, value); + + /* Keep track of enum controller_id FBC is attached to */ + compressor->is_enabled = true; + compressor->attached_inst = params->inst; + cp110->offsets = reg_offsets[params->inst - 1]; + + /*Toggle it as there is bug in HW */ + set_reg_field_value(value, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + set_reg_field_value(value, 1, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, addr, value); + + wait_for_fbc_state_changed(cp110, true); + } +} + +void dce110_compressor_disable_fbc(struct compressor *compressor) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + + if (compressor->options.bits.FBC_SUPPORT && + dce110_compressor_is_fbc_enabled_in_hw(compressor, NULL)) { + uint32_t reg_data; + /* Turn off compression */ + reg_data = dm_read_reg(compressor->ctx, mmFBC_CNTL); + set_reg_field_value(reg_data, 0, FBC_CNTL, FBC_GRPH_COMP_EN); + dm_write_reg(compressor->ctx, mmFBC_CNTL, reg_data); + + /* Reset enum controller_id to undefined */ + compressor->attached_inst = 0; + compressor->is_enabled = false; + + /* Whenever disabling FBC make sure LPT is disabled if LPT + * supported */ + if (compressor->options.bits.LPT_SUPPORT) + dce110_compressor_disable_lpt(compressor); + + wait_for_fbc_state_changed(cp110, false); + } +} + +bool dce110_compressor_is_fbc_enabled_in_hw( + struct compressor *compressor, + uint32_t *inst) +{ + /* Check the hardware register */ + uint32_t value; + + value = dm_read_reg(compressor->ctx, mmFBC_STATUS); + if (get_reg_field_value(value, FBC_STATUS, FBC_ENABLE_STATUS)) { + if (inst != NULL) + *inst = compressor->attached_inst; + return true; + } + + value = dm_read_reg(compressor->ctx, mmFBC_MISC); + if (get_reg_field_value(value, FBC_MISC, FBC_STOP_ON_HFLIP_EVENT)) { + value = dm_read_reg(compressor->ctx, mmFBC_CNTL); + + if (get_reg_field_value(value, FBC_CNTL, FBC_GRPH_COMP_EN)) { + if (inst != NULL) + *inst = + compressor->attached_inst; + return true; + } + } + return false; +} + +bool dce110_compressor_is_lpt_enabled_in_hw(struct compressor *compressor) +{ + /* Check the hardware register */ + uint32_t value = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + return get_reg_field_value( + value, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); +} + +void dce110_compressor_program_compressed_surface_address_and_pitch( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t value = 0; + uint32_t fbc_pitch = 0; + uint32_t compressed_surf_address_low_part = + compressor->compr_surface_address.addr.low_part; + + /* Clear content first. */ + dm_write_reg( + compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + 0); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), 0); + + if (compressor->options.bits.LPT_SUPPORT) { + uint32_t lpt_alignment = lpt_size_alignment(cp110); + + if (lpt_alignment != 0) { + compressed_surf_address_low_part = + ((compressed_surf_address_low_part + + (lpt_alignment - 1)) / lpt_alignment) + * lpt_alignment; + } + } + + /* Write address, HIGH has to be first. */ + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS_HIGH), + compressor->compr_surface_address.addr.high_part); + dm_write_reg(compressor->ctx, + DCP_REG(mmGRPH_COMPRESS_SURFACE_ADDRESS), + compressed_surf_address_low_part); + + fbc_pitch = align_to_chunks_number_per_line( + cp110, + params->source_view_width); + + if (compressor->min_compress_ratio == FBC_COMPRESS_RATIO_1TO1) + fbc_pitch = fbc_pitch / 8; + else + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Unexpected DCE11 compression ratio", + __func__); + + /* Clear content first. */ + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), 0); + + /* Write FBC Pitch. */ + set_reg_field_value( + value, + fbc_pitch, + GRPH_COMPRESS_PITCH, + GRPH_COMPRESS_PITCH); + dm_write_reg(compressor->ctx, DCP_REG(mmGRPH_COMPRESS_PITCH), value); + +} + +void dce110_compressor_disable_lpt(struct compressor *compressor) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t inx; + + /* Disable all pipes LPT Stutter */ + for (inx = 0; inx < 3; inx++) { + value = + dm_read_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 0, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg( + compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), + value); + } + /* Disable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, addr, value); + + /* Disable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); + + /* Clear selection of Channel(s) containing Compressed Surface */ + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 0xFFFFFFFF, + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, mmGMCON_LPT_TARGET, value); +} + +void dce110_compressor_enable_lpt(struct compressor *compressor) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t value; + uint32_t addr; + uint32_t value_control; + uint32_t channels; + + /* Enable LPT Stutter from Display pipe */ + value = dm_read_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH)); + set_reg_field_value( + value, + 1, + DPG_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, + DMIF_REG(mmDPG_PIPE_STUTTER_CONTROL_NONLPTCH), value); + + /* Enable Underlay pipe LPT Stutter */ + addr = mmDPGV0_PIPE_STUTTER_CONTROL_NONLPTCH; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_STUTTER_CONTROL_NONLPTCH, + STUTTER_ENABLE_NONLPTCH); + dm_write_reg(compressor->ctx, addr, value); + + /* Selection of Channel(s) containing Compressed Surface: 0xfffffff + * will disable LPT. + * STCTRL_LPT_TARGETn corresponds to channel n. */ + addr = mmLOW_POWER_TILING_CONTROL; + value_control = dm_read_reg(compressor->ctx, addr); + channels = get_reg_field_value(value_control, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + + addr = mmGMCON_LPT_TARGET; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + channels + 1, /* not mentioned in programming guide, + but follow DCE8.1 */ + GMCON_LPT_TARGET, + STCTRL_LPT_TARGET); + dm_write_reg(compressor->ctx, addr, value); + + /* Enable LPT */ + addr = mmLOW_POWER_TILING_CONTROL; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + 1, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ENABLE); + dm_write_reg(compressor->ctx, addr, value); +} + +void dce110_compressor_program_lpt_control( + struct compressor *compressor, + struct compr_addr_and_pitch_params *params) +{ + struct dce110_compressor *cp110 = TO_DCE110_COMPRESSOR(compressor); + uint32_t rows_per_channel; + uint32_t lpt_alignment; + uint32_t source_view_width; + uint32_t source_view_height; + uint32_t lpt_control = 0; + + if (!compressor->options.bits.LPT_SUPPORT) + return; + + lpt_control = dm_read_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL); + + /* POSSIBLE VALUES for Low Power Tiling Mode: + * 00 - Use channel 0 + * 01 - Use Channel 0 and 1 + * 02 - Use Channel 0,1,2,3 + * 03 - reserved */ + switch (compressor->lpt_channels_num) { + /* case 2: + * Use Channel 0 & 1 / Not used for DCE 11 */ + case 1: + /*Use Channel 0 for LPT for DCE 11 */ + set_reg_field_value( + lpt_control, + 0, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_MODE); + break; + default: + dm_logger_write( + compressor->ctx->logger, LOG_WARNING, + "%s: Invalid selected DRAM channels for LPT!!!", + __func__); + break; + } + + lpt_control = lpt_memory_control_config(cp110, lpt_control); + + /* Program LOW_POWER_TILING_ROWS_PER_CHAN field which depends on + * FBC compressed surface pitch. + * LOW_POWER_TILING_ROWS_PER_CHAN = Roundup ((Surface Height * + * Surface Pitch) / (Row Size * Number of Channels * + * Number of Banks)). */ + rows_per_channel = 0; + lpt_alignment = lpt_size_alignment(cp110); + source_view_width = + align_to_chunks_number_per_line( + cp110, + params->source_view_width); + source_view_height = (params->source_view_height + 1) & (~0x1); + + if (lpt_alignment != 0) { + rows_per_channel = source_view_width * source_view_height * 4; + rows_per_channel = + (rows_per_channel % lpt_alignment) ? + (rows_per_channel / lpt_alignment + 1) : + rows_per_channel / lpt_alignment; + } + + set_reg_field_value( + lpt_control, + rows_per_channel, + LOW_POWER_TILING_CONTROL, + LOW_POWER_TILING_ROWS_PER_CHAN); + + dm_write_reg(compressor->ctx, + mmLOW_POWER_TILING_CONTROL, lpt_control); +} + +/* + * DCE 11 Frame Buffer Compression Implementation + */ + +void dce110_compressor_set_fbc_invalidation_triggers( + struct compressor *compressor, + uint32_t fbc_trigger) +{ + /* Disable region hit event, FBC_MEMORY_REGION_MASK = 0 (bits 16-19) + * for DCE 11 regions cannot be used - does not work with S/G + */ + uint32_t addr = mmFBC_CLIENT_REGION_MASK; + uint32_t value = dm_read_reg(compressor->ctx, addr); + + set_reg_field_value( + value, + 0, + FBC_CLIENT_REGION_MASK, + FBC_MEMORY_REGION_MASK); + dm_write_reg(compressor->ctx, addr, value); + + /* Setup events when to clear all CSM entries (effectively marking + * current compressed data invalid) + * For DCE 11 CSM metadata 11111 means - "Not Compressed" + * Used as the initial value of the metadata sent to the compressor + * after invalidation, to indicate that the compressor should attempt + * to compress all chunks on the current pass. Also used when the chunk + * is not successfully written to memory. + * When this CSM value is detected, FBC reads from the uncompressed + * buffer. Set events according to passed in value, these events are + * valid for DCE11: + * - bit 0 - display register updated + * - bit 28 - memory write from any client except from MCIF + * - bit 29 - CG static screen signal is inactive + * In addition, DCE11.1 also needs to set new DCE11.1 specific events + * that are used to trigger invalidation on certain register changes, + * for example enabling of Alpha Compression may trigger invalidation of + * FBC once bit is set. These events are as follows: + * - Bit 2 - FBC_GRPH_COMP_EN register updated + * - Bit 3 - FBC_SRC_SEL register updated + * - Bit 4 - FBC_MIN_COMPRESSION register updated + * - Bit 5 - FBC_ALPHA_COMP_EN register updated + * - Bit 6 - FBC_ZERO_ALPHA_CHUNK_SKIP_EN register updated + * - Bit 7 - FBC_FORCE_COPY_TO_COMP_BUF register updated + */ + addr = mmFBC_IDLE_FORCE_CLEAR_MASK; + value = dm_read_reg(compressor->ctx, addr); + set_reg_field_value( + value, + fbc_trigger | + FBC_IDLE_FORCE_GRPH_COMP_EN | + FBC_IDLE_FORCE_SRC_SEL_CHANGE | + FBC_IDLE_FORCE_MIN_COMPRESSION_CHANGE | + FBC_IDLE_FORCE_ALPHA_COMP_EN | + FBC_IDLE_FORCE_ZERO_ALPHA_CHUNK_SKIP_EN | + FBC_IDLE_FORCE_FORCE_COPY_TO_COMP_BUF, + FBC_IDLE_FORCE_CLEAR_MASK, + FBC_IDLE_FORCE_CLEAR_MASK); + dm_write_reg(compressor->ctx, addr, value); +} + +bool dce110_compressor_construct(struct dce110_compressor *compressor, + struct dc_context *ctx) +{ + struct dc_bios *bp = ctx->dc_bios; + struct embedded_panel_info panel_info; + + compressor->base.options.bits.FBC_SUPPORT = true; + compressor->base.options.bits.LPT_SUPPORT = true; + /* For DCE 11 always use one DRAM channel for LPT */ + compressor->base.lpt_channels_num = 1; + compressor->base.options.bits.DUMMY_BACKEND = false; + + /* Check if this system has more than 1 DRAM channel; if only 1 then LPT + * should not be supported */ + if (compressor->base.memory_bus_width == 64) + compressor->base.options.bits.LPT_SUPPORT = false; + + compressor->base.options.bits.CLK_GATING_DISABLED = false; + + compressor->base.ctx = ctx; + compressor->base.embedded_panel_h_size = 0; + compressor->base.embedded_panel_v_size = 0; + compressor->base.memory_bus_width = ctx->asic_id.vram_width; + compressor->base.allocated_size = 0; + compressor->base.preferred_requested_size = 0; + compressor->base.min_compress_ratio = FBC_COMPRESS_RATIO_INVALID; + compressor->base.options.raw = 0; + compressor->base.banks_num = 0; + compressor->base.raw_size = 0; + compressor->base.channel_interleave_size = 0; + compressor->base.dram_channels_num = 0; + compressor->base.lpt_channels_num = 0; + compressor->base.attached_inst = 0; + compressor->base.is_enabled = false; + + if (BP_RESULT_OK == + bp->funcs->get_embedded_panel_info(bp, &panel_info)) { + compressor->base.embedded_panel_h_size = + panel_info.lcd_timing.horizontal_addressable; + compressor->base.embedded_panel_v_size = + panel_info.lcd_timing.vertical_addressable; + } + return true; +} + +struct compressor *dce110_compressor_create(struct dc_context *ctx) +{ + struct dce110_compressor *cp110 = + dm_alloc(sizeof(struct dce110_compressor)); + + if (!cp110) + return NULL; + + if (dce110_compressor_construct(cp110, ctx)) + return &cp110->base; + + BREAK_TO_DEBUGGER(); + dm_free(cp110); + return NULL; +} + +void dce110_compressor_destroy(struct compressor **compressor) +{ + dm_free(TO_DCE110_COMPRESSOR(*compressor)); + *compressor = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h new file mode 100644 index 000000000000..22af5be51581 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.h @@ -0,0 +1,78 @@ +/* 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_COMPRESSOR_DCE110_H__ +#define __DC_COMPRESSOR_DCE110_H__ + +#include "../inc/compressor.h" + +#define TO_DCE110_COMPRESSOR(compressor)\ + container_of(compressor, struct dce110_compressor, base) + +struct dce110_compressor_reg_offsets { + uint32_t dcp_offset; + uint32_t dmif_offset; +}; + +struct dce110_compressor { + struct compressor base; + struct dce110_compressor_reg_offsets offsets; +}; + +struct compressor *dce110_compressor_create(struct dc_context *ctx); + +bool dce110_compressor_construct(struct dce110_compressor *cp110, + struct dc_context *ctx); + +void dce110_compressor_destroy(struct compressor **cp); + +/* FBC RELATED */ +void dce110_compressor_power_up_fbc(struct compressor *cp); + +void dce110_compressor_enable_fbc(struct compressor *cp, uint32_t paths_num, + struct compr_addr_and_pitch_params *params); + +void dce110_compressor_disable_fbc(struct compressor *cp); + +void dce110_compressor_set_fbc_invalidation_triggers(struct compressor *cp, + uint32_t fbc_trigger); + +void dce110_compressor_program_compressed_surface_address_and_pitch( + struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce110_compressor_is_fbc_enabled_in_hw(struct compressor *cp, + uint32_t *fbc_mapped_crtc_id); + +/* LPT RELATED */ +void dce110_compressor_enable_lpt(struct compressor *cp); + +void dce110_compressor_disable_lpt(struct compressor *cp); + +void dce110_compressor_program_lpt_control(struct compressor *cp, + struct compr_addr_and_pitch_params *params); + +bool dce110_compressor_is_lpt_enabled_in_hw(struct compressor *cp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c new file mode 100644 index 000000000000..1a682996b531 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -0,0 +1,1978 @@ +/* + * Copyright 2015 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 "dc.h" +#include "dc_bios_types.h" +#include "core_types.h" +#include "core_status.h" +#include "resource.h" +#include "hw_sequencer.h" +#include "dm_helpers.h" +#include "dce110_hw_sequencer.h" +#include "dce110_timing_generator.h" + +#include "bios/bios_parser_helper.h" +#include "timing_generator.h" +#include "mem_input.h" +#include "opp.h" +#include "ipp.h" +#include "transform.h" +#include "stream_encoder.h" +#include "link_encoder.h" +#include "clock_source.h" +#include "gamma_calcs.h" +#include "audio.h" +#include "dce/dce_hwseq.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +struct dce110_hw_seq_reg_offsets { + uint32_t crtc; +}; + +static const struct dce110_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTCV_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_BLND(reg, id)\ + (reg + reg_offsets[id].blnd) + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +#define MAX_WATERMARK 0xFFFF +#define SAFE_NBP_MARK 0x7FFF + +/******************************************************************************* + * Private definitions + ******************************************************************************/ +/***************************PIPE_CONTROL***********************************/ +static void dce110_init_pte(struct dc_context *ctx) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = mmUNP_DVMM_PTE_CONTROL; + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dm_write_reg(ctx, addr, value); + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} +/**************************************************************************/ + +static void enable_display_pipe_clock_gating( + struct dc_context *ctx, + bool clock_gating) +{ + /*TODO*/ +} + +static bool dce110_enable_display_power_gating( + struct core_dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (controller_id == underlay_idx) + controller_id = CONTROLLER_ID_UNDERLAY0 - 1; + + if (power_gating != PIPE_GATING_CONTROL_INIT || controller_id == 0){ + + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + /* Revert MASTER_UPDATE_MODE to 0 because bios sets it 2 + * by default when command table is called + * + * Bios parser accepts controller_id = 6 as indicative of + * underlay pipe in dce110. But we do not support more + * than 3. + */ + if (controller_id < CONTROLLER_ID_MAX - 1) + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce110_init_pte(ctx); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + +static void build_prescale_params(struct ipp_prescale_params *prescale_params, + const struct core_surface *surface) +{ + prescale_params->mode = IPP_PRESCALE_MODE_FIXED_UNSIGNED; + + switch (surface->public.format) { + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: + prescale_params->scale = 0x2020; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + prescale_params->scale = 0x2008; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + prescale_params->scale = 0x2000; + break; + default: + ASSERT(false); + } +} + +static bool set_gamma_ramp( + struct input_pixel_processor *ipp, + struct output_pixel_processor *opp, + const struct core_gamma *ramp, + const struct core_surface *surface) +{ + struct ipp_prescale_params prescale_params = { 0 }; + struct pwl_params *regamma_params; + bool result = false; + + regamma_params = dm_alloc(sizeof(struct pwl_params)); + if (regamma_params == NULL) + goto regamma_alloc_fail; + + regamma_params->hw_points_num = GAMMA_HW_POINTS_NUM; + + opp->funcs->opp_power_on_regamma_lut(opp, true); + + if (ipp) { + build_prescale_params(&prescale_params, surface); + ipp->funcs->ipp_program_prescale(ipp, &prescale_params); + } + + if (ramp && calculate_regamma_params(regamma_params, ramp, surface)) { + + opp->funcs->opp_program_regamma_pwl(opp, regamma_params); + if (ipp) + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_HW_sRGB); + opp->funcs->opp_set_regamma_mode(opp, OPP_REGAMMA_USER); + } else { + if (ipp) + ipp->funcs->ipp_set_degamma(ipp, IPP_DEGAMMA_MODE_BYPASS); + opp->funcs->opp_set_regamma_mode(opp, OPP_REGAMMA_BYPASS); + } + + opp->funcs->opp_power_on_regamma_lut(opp, false); + + result = true; + + dm_free(regamma_params); + +regamma_alloc_fail: + return result; +} + +static enum dc_status bios_parser_crtc_source_select( + struct pipe_ctx *pipe_ctx) +{ + struct dc_bios *dcb; + /* call VBIOS table to set CRTC source for the HW + * encoder block + * note: video bios clears all FMT setting here. */ + struct bp_crtc_source_select crtc_source_select = {0}; + const struct core_sink *sink = pipe_ctx->stream->sink; + + crtc_source_select.engine_id = pipe_ctx->stream_enc->id; + crtc_source_select.controller_id = pipe_ctx->pipe_idx + 1; + /*TODO: Need to un-hardcode color depth, dp_audio and account for + * the case where signal and sink signal is different (translator + * encoder)*/ + crtc_source_select.signal = pipe_ctx->stream->signal; + crtc_source_select.enable_dp_audio = false; + crtc_source_select.sink_signal = pipe_ctx->stream->signal; + crtc_source_select.display_output_bit_depth = PANEL_8BIT_COLOR; + + dcb = sink->ctx->dc_bios; + + if (BP_RESULT_OK != dcb->funcs->crtc_source_select( + dcb, + &crtc_source_select)) { + return DC_ERROR_UNEXPECTED; + } + + return DC_OK; +} + +void dce110_update_info_frame(struct pipe_ctx *pipe_ctx) +{ + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->update_hdmi_info_packets( + pipe_ctx->stream_enc, + &pipe_ctx->encoder_info_frame); + else if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->update_dp_info_packets( + pipe_ctx->stream_enc, + &pipe_ctx->encoder_info_frame); +} + +void dce110_enable_stream(struct pipe_ctx *pipe_ctx) +{ + enum dc_lane_count lane_count = + pipe_ctx->stream->sink->link->public.cur_link_settings.lane_count; + + struct dc_crtc_timing *timing = &pipe_ctx->stream->public.timing; + struct core_link *link = pipe_ctx->stream->sink->link; + + /* 1. update AVI info frame (HDMI, DP) + * we always need to update info frame + */ + uint32_t active_total_with_borders; + uint32_t early_control = 0; + struct timing_generator *tg = pipe_ctx->tg; + + /* TODOFPGA may change to hwss.update_info_frame */ + dce110_update_info_frame(pipe_ctx); + /* enable early control to avoid corruption on DP monitor*/ + active_total_with_borders = + timing->h_addressable + + timing->h_border_left + + timing->h_border_right; + + if (lane_count != 0) + early_control = active_total_with_borders % lane_count; + + if (early_control == 0) + early_control = lane_count; + + tg->funcs->set_early_control(tg, early_control); + + /* enable audio only within mode set */ + if (pipe_ctx->audio != NULL) { + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->dp_audio_enable(pipe_ctx->stream_enc); + } + + /* For MST, there are multiply stream go to only one link. + * connect DIG back_end to front_end while enable_stream and + * disconnect them during disable_stream + * BY this, it is logic clean to separate stream and link */ + link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc, + pipe_ctx->stream_enc->id, true); + +} + +void dce110_disable_stream(struct pipe_ctx *pipe_ctx) +{ + struct core_stream *stream = pipe_ctx->stream; + struct core_link *link = stream->sink->link; + + if (pipe_ctx->audio) { + pipe_ctx->audio->funcs->az_disable(pipe_ctx->audio); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->dp_audio_disable( + pipe_ctx->stream_enc); + else + pipe_ctx->stream_enc->funcs->hdmi_audio_disable( + pipe_ctx->stream_enc); + + pipe_ctx->audio = NULL; + + /* TODO: notify audio driver for if audio modes list changed + * add audio mode list change flag */ + /* dal_audio_disable_azalia_audio_jack_presence(stream->audio, + * stream->stream_engine_id); + */ + } + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->stop_hdmi_info_packets( + pipe_ctx->stream_enc); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->stop_dp_info_packets( + pipe_ctx->stream_enc); + + pipe_ctx->stream_enc->funcs->audio_mute_control( + pipe_ctx->stream_enc, true); + + + /* blank at encoder level */ + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->dp_blank(pipe_ctx->stream_enc); + + link->link_enc->funcs->connect_dig_be_to_fe( + link->link_enc, + pipe_ctx->stream_enc->id, + false); + +} + +void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings) +{ + struct encoder_unblank_param params = { { 0 } }; + + /* only 3 items below are used by unblank */ + params.crtc_timing.pixel_clock = + pipe_ctx->stream->public.timing.pix_clk_khz; + params.link_settings.link_rate = link_settings->link_rate; + pipe_ctx->stream_enc->funcs->dp_unblank(pipe_ctx->stream_enc, ¶ms); +} + +static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) +{ + switch (crtc_id) { + case CONTROLLER_ID_D0: + return DTO_SOURCE_ID0; + case CONTROLLER_ID_D1: + return DTO_SOURCE_ID1; + case CONTROLLER_ID_D2: + return DTO_SOURCE_ID2; + case CONTROLLER_ID_D3: + return DTO_SOURCE_ID3; + case CONTROLLER_ID_D4: + return DTO_SOURCE_ID4; + case CONTROLLER_ID_D5: + return DTO_SOURCE_ID5; + default: + return DTO_SOURCE_UNKNOWN; + } +} + +static void build_audio_output( + const struct pipe_ctx *pipe_ctx, + struct audio_output *audio_output) +{ + const struct core_stream *stream = pipe_ctx->stream; + audio_output->engine_id = pipe_ctx->stream_enc->id; + + audio_output->signal = pipe_ctx->stream->signal; + + /* audio_crtc_info */ + + audio_output->crtc_info.h_total = + stream->public.timing.h_total; + + /* + * Audio packets are sent during actual CRTC blank physical signal, we + * need to specify actual active signal portion + */ + audio_output->crtc_info.h_active = + stream->public.timing.h_addressable + + stream->public.timing.h_border_left + + stream->public.timing.h_border_right; + + audio_output->crtc_info.v_active = + stream->public.timing.v_addressable + + stream->public.timing.v_border_top + + stream->public.timing.v_border_bottom; + + audio_output->crtc_info.pixel_repetition = 1; + + audio_output->crtc_info.interlaced = + stream->public.timing.flags.INTERLACE; + + audio_output->crtc_info.refresh_rate = + (stream->public.timing.pix_clk_khz*1000)/ + (stream->public.timing.h_total*stream->public.timing.v_total); + + audio_output->crtc_info.color_depth = + stream->public.timing.display_color_depth; + + audio_output->crtc_info.requested_pixel_clock = + pipe_ctx->pix_clk_params.requested_pix_clk; + + /* + * TODO - Investigate why calculated pixel clk has to be + * requested pixel clk + */ + audio_output->crtc_info.calculated_pixel_clock = + pipe_ctx->pix_clk_params.requested_pix_clk; + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT || + pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + audio_output->pll_info.dp_dto_source_clock_in_khz = + dal_display_clock_get_dp_ref_clk_frequency( + pipe_ctx->dis_clk); + } + + audio_output->pll_info.feed_back_divider = + pipe_ctx->pll_settings.feedback_divider; + + audio_output->pll_info.dto_source = + translate_to_dto_source( + pipe_ctx->pipe_idx + 1); + + /* TODO hard code to enable for now. Need get from stream */ + audio_output->pll_info.ss_enabled = true; + + audio_output->pll_info.ss_percentage = + pipe_ctx->pll_settings.ss_percentage; +} + +static void get_surface_visual_confirm_color(const struct pipe_ctx *pipe_ctx, + struct tg_color *color) +{ + uint32_t color_value = MAX_TG_COLOR_VALUE * (4 - pipe_ctx->pipe_idx) / 4; + + switch (pipe_ctx->scl_data.format) { + case PIXEL_FORMAT_ARGB8888: + /* set boarder color to red */ + color->color_r_cr = color_value; + break; + + case PIXEL_FORMAT_ARGB2101010: + /* set boarder color to blue */ + color->color_b_cb = color_value; + break; + case PIXEL_FORMAT_420BPP12: + /* set boarder color to green */ + color->color_g_y = color_value; + break; + case PIXEL_FORMAT_FP16: + /* set boarder color to white */ + color->color_r_cr = color_value; + color->color_b_cb = color_value; + color->color_g_y = color_value; + break; + default: + break; + } +} + +static void program_scaler(const struct core_dc *dc, + const struct pipe_ctx *pipe_ctx) +{ + struct tg_color color = {0}; + + if (dc->public.debug.surface_visual_confirm) + get_surface_visual_confirm_color(pipe_ctx, &color); + else + color_space_to_black_color(dc, + pipe_ctx->stream->public.output_color_space, + &color); + + pipe_ctx->xfm->funcs->transform_set_pixel_storage_depth( + pipe_ctx->xfm, + pipe_ctx->scl_data.lb_params.depth, + &pipe_ctx->stream->bit_depth_params); + + if (pipe_ctx->tg->funcs->set_overscan_blank_color) + pipe_ctx->tg->funcs->set_overscan_blank_color( + pipe_ctx->tg, + &color); + + pipe_ctx->xfm->funcs->transform_set_scaler(pipe_ctx->xfm, + &pipe_ctx->scl_data); +} + +static enum dc_status prog_pixclk_crtc_otg( + struct pipe_ctx *pipe_ctx, + struct validate_context *context, + struct core_dc *dc) +{ + struct core_stream *stream = pipe_ctx->stream; + struct pipe_ctx *pipe_ctx_old = &dc->current_context->res_ctx. + pipe_ctx[pipe_ctx->pipe_idx]; + struct tg_color black_color = {0}; + + if (!pipe_ctx_old->stream) { + + /* program blank color */ + color_space_to_black_color(dc, + stream->public.output_color_space, &black_color); + pipe_ctx->tg->funcs->set_blank_color( + pipe_ctx->tg, + &black_color); + /* + * Must blank CRTC after disabling power gating and before any + * programming, otherwise CRTC will be hung in bad state + */ + pipe_ctx->tg->funcs->set_blank(pipe_ctx->tg, true); + + if (false == pipe_ctx->clock_source->funcs->program_pix_clk( + pipe_ctx->clock_source, + &pipe_ctx->pix_clk_params, + &pipe_ctx->pll_settings)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + pipe_ctx->tg->funcs->program_timing( + pipe_ctx->tg, + &stream->public.timing, + true); + } + + if (!pipe_ctx_old->stream) { + if (false == pipe_ctx->tg->funcs->enable_crtc( + pipe_ctx->tg)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + } + + return DC_OK; +} + +static enum dc_status apply_single_controller_ctx_to_hw( + struct pipe_ctx *pipe_ctx, + struct validate_context *context, + struct core_dc *dc) +{ + struct core_stream *stream = pipe_ctx->stream; + struct pipe_ctx *pipe_ctx_old = &dc->current_context->res_ctx. + pipe_ctx[pipe_ctx->pipe_idx]; + + /* */ + dc->hwss.prog_pixclk_crtc_otg(pipe_ctx, context, dc); + + pipe_ctx->opp->funcs->opp_set_dyn_expansion( + pipe_ctx->opp, + COLOR_SPACE_YCBCR601, + stream->public.timing.display_color_depth, + pipe_ctx->stream->signal); + + pipe_ctx->opp->funcs->opp_program_fmt( + pipe_ctx->opp, + &stream->bit_depth_params, + &stream->clamping); + + /* FPGA does not program backend */ + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) + return DC_OK; + + /* TODO: move to stream encoder */ + if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) + if (DC_OK != bios_parser_crtc_source_select(pipe_ctx)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) + stream->sink->link->link_enc->funcs->setup( + stream->sink->link->link_enc, + pipe_ctx->stream->signal); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->dp_set_stream_attribute( + pipe_ctx->stream_enc, + &stream->public.timing, + stream->public.output_color_space); + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->hdmi_set_stream_attribute( + pipe_ctx->stream_enc, + &stream->public.timing, + stream->phy_pix_clk, + pipe_ctx->audio != NULL); + + if (dc_is_dvi_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->dvi_set_stream_attribute( + pipe_ctx->stream_enc, + &stream->public.timing, + (pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? + true : false); + + if (!pipe_ctx_old->stream) { + core_link_enable_stream(pipe_ctx); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + dce110_unblank_stream(pipe_ctx, + &stream->sink->link->public.cur_link_settings); + } + + pipe_ctx->scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + /* program_scaler and allocate_mem_input are not new asic */ + if (!pipe_ctx_old || memcmp(&pipe_ctx_old->scl_data, + &pipe_ctx->scl_data, + sizeof(struct scaler_data)) != 0) + program_scaler(dc, pipe_ctx); + + /* mst support - use total stream count */ + pipe_ctx->mi->funcs->allocate_mem_input( + pipe_ctx->mi, + stream->public.timing.h_total, + stream->public.timing.v_total, + stream->public.timing.pix_clk_khz, + context->target_count); + + return DC_OK; +} + +/******************************************************************************/ + +static void power_down_encoders(struct core_dc *dc) +{ + int i; + + for (i = 0; i < dc->link_count; i++) { + dc->links[i]->link_enc->funcs->disable_output( + dc->links[i]->link_enc, SIGNAL_TYPE_NONE); + } +} + +static void power_down_controllers(struct core_dc *dc) +{ + int i; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + dc->res_pool->timing_generators[i]->funcs->disable_crtc( + dc->res_pool->timing_generators[i]); + } +} + +static void power_down_clock_sources(struct core_dc *dc) +{ + int i; + + if (dc->res_pool->dp_clock_source->funcs->cs_power_down( + dc->res_pool->dp_clock_source) == false) + dm_error("Failed to power down pll! (dp clk src)\n"); + + for (i = 0; i < dc->res_pool->clk_src_count; i++) { + if (dc->res_pool->clock_sources[i]->funcs->cs_power_down( + dc->res_pool->clock_sources[i]) == false) + dm_error("Failed to power down pll! (clk src index=%d)\n", i); + } +} + +static void power_down_all_hw_blocks(struct core_dc *dc) +{ + power_down_encoders(dc); + + power_down_controllers(dc); + + power_down_clock_sources(dc); +} + +static void disable_vga_and_power_gate_all_controllers( + struct core_dc *dc) +{ + int i; + struct timing_generator *tg; + struct dc_context *ctx = dc->ctx; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + tg = dc->res_pool->timing_generators[i]; + + tg->funcs->disable_vga(tg); + + /* Enable CLOCK gating for each pipe BEFORE controller + * powergating. */ + enable_display_pipe_clock_gating(ctx, + true); + + dc->hwss.power_down_front_end( + dc, &dc->current_context->res_ctx.pipe_ctx[i]); + } +} + +/** + * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need: + * 1. Power down all DC HW blocks + * 2. Disable VGA engine on all controllers + * 3. Enable power gating for controller + * 4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS) + */ +void dce110_enable_accelerated_mode(struct core_dc *dc) +{ + power_down_all_hw_blocks(dc); + + disable_vga_and_power_gate_all_controllers(dc); + bios_set_scratch_acc_mode_change(dc->ctx->dc_bios); +} + +/** + * Call display_engine_clock_dce80 to perform the Dclk programming. + */ +void dce110_set_display_clock(struct validate_context *context) +{ + /* Program the display engine clock. + * Check DFS bypass mode support or not. DFSbypass feature is only when + * BIOS GPU info table reports support. */ + + if (/*dal_adapter_service_is_dfs_bypass_enabled()*/ false) { + /*TODO: set_display_clock_dfs_bypass( + hws, + path_set, + context->res_ctx.pool->display_clock, + context->res_ctx.min_clocks.min_dclk_khz);*/ + } else { + /* + * TODO: need to either port work around from DAL2 function + * getActualRequiredDisplayClock or program displayclock without + * calling vbios. Currently temporily work + * around by increasing the displclk by 15 percent + */ + dal_display_clock_set_clock( + context->res_ctx.pool->display_clock, + context->bw_results.dispclk_khz * 115 / 100); + } + + + /* TODO: When changing display engine clock, DMCU WaitLoop must be + * reconfigured in order to maintain the same delays within DMCU + * programming sequences. */ +} + +static uint32_t compute_pstate_blackout_duration( + struct bw_fixed blackout_duration, + const struct core_stream *stream) +{ + uint32_t total_dest_line_time_ns; + uint32_t pstate_blackout_duration_ns; + + pstate_blackout_duration_ns = 1000 * blackout_duration.value >> 24; + + total_dest_line_time_ns = 1000000UL * + stream->public.timing.h_total / + stream->public.timing.pix_clk_khz + + pstate_blackout_duration_ns; + + return total_dest_line_time_ns; +} + +/* get the index of the pipe_ctx if there were no gaps in the pipe_ctx array*/ +int get_bw_result_idx( + struct resource_context *res_ctx, + int pipe_idx) +{ + int i, collapsed_idx; + + if (res_ctx->pipe_ctx[pipe_idx].top_pipe) + return 3; + + collapsed_idx = 0; + for (i = 0; i < pipe_idx; i++) { + if (res_ctx->pipe_ctx[i].stream) + collapsed_idx++; + } + + return collapsed_idx; +} + +static bool is_watermark_set_a_greater( + const struct bw_watermarks *set_a, + const struct bw_watermarks *set_b) +{ + if (set_a->a_mark > set_b->a_mark + || set_a->b_mark > set_b->b_mark + || set_a->c_mark > set_b->c_mark + || set_a->d_mark > set_b->d_mark) + return true; + return false; +} + +static bool did_watermarks_increase( + struct pipe_ctx *pipe_ctx, + struct validate_context *context, + struct validate_context *old_context) +{ + int collapsed_pipe_idx = get_bw_result_idx(&context->res_ctx, + pipe_ctx->pipe_idx); + int old_collapsed_pipe_idx = get_bw_result_idx(&old_context->res_ctx, + pipe_ctx->pipe_idx); + struct pipe_ctx *old_pipe_ctx = &old_context->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; + + if (!old_pipe_ctx->stream) + return true; + + if (is_watermark_set_a_greater( + &context->bw_results.nbp_state_change_wm_ns[collapsed_pipe_idx], + &old_context->bw_results.nbp_state_change_wm_ns[old_collapsed_pipe_idx])) + return true; + if (is_watermark_set_a_greater( + &context->bw_results.stutter_exit_wm_ns[collapsed_pipe_idx], + &old_context->bw_results.stutter_exit_wm_ns[old_collapsed_pipe_idx])) + return true; + if (is_watermark_set_a_greater( + &context->bw_results.urgent_wm_ns[collapsed_pipe_idx], + &old_context->bw_results.urgent_wm_ns[old_collapsed_pipe_idx])) + return true; + + return false; +} + +static void program_wm_for_pipe(struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + int total_dest_line_time_ns = compute_pstate_blackout_duration( + dc->bw_vbios.blackout_duration, + pipe_ctx->stream); + int bw_result_idx = get_bw_result_idx(&context->res_ctx, + pipe_ctx->pipe_idx); + + pipe_ctx->mi->funcs->mem_input_program_display_marks( + pipe_ctx->mi, + context->bw_results.nbp_state_change_wm_ns[bw_result_idx], + context->bw_results.stutter_exit_wm_ns[bw_result_idx], + context->bw_results.urgent_wm_ns[bw_result_idx], + total_dest_line_time_ns); + + if (pipe_ctx->top_pipe) + pipe_ctx->mi->funcs->mem_input_program_chroma_display_marks( + pipe_ctx->mi, + context->bw_results.nbp_state_change_wm_ns[bw_result_idx + 1], + context->bw_results.stutter_exit_wm_ns[bw_result_idx + 1], + context->bw_results.urgent_wm_ns[bw_result_idx + 1], + total_dest_line_time_ns); +} + +void dce110_set_displaymarks( + const struct core_dc *dc, + struct validate_context *context) +{ + uint8_t i, num_pipes; + unsigned int underlay_idx = dc->res_pool->underlay_pipe_index; + + for (i = 0, num_pipes = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + uint32_t total_dest_line_time_ns; + + if (pipe_ctx->stream == NULL) + continue; + + total_dest_line_time_ns = compute_pstate_blackout_duration( + dc->bw_vbios.blackout_duration, pipe_ctx->stream); + pipe_ctx->mi->funcs->mem_input_program_display_marks( + pipe_ctx->mi, + context->bw_results.nbp_state_change_wm_ns[num_pipes], + context->bw_results.stutter_exit_wm_ns[num_pipes], + context->bw_results.urgent_wm_ns[num_pipes], + total_dest_line_time_ns); + if (i == underlay_idx) { + num_pipes++; + pipe_ctx->mi->funcs->mem_input_program_chroma_display_marks( + pipe_ctx->mi, + context->bw_results.nbp_state_change_wm_ns[num_pipes], + context->bw_results.stutter_exit_wm_ns[num_pipes], + context->bw_results.urgent_wm_ns[num_pipes], + total_dest_line_time_ns); + } + num_pipes++; + } +} + +static void set_safe_displaymarks(struct resource_context *res_ctx) +{ + int i; + int underlay_idx = res_ctx->pool->underlay_pipe_index; + struct bw_watermarks max_marks = { + MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK, MAX_WATERMARK }; + struct bw_watermarks nbp_marks = { + SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK, SAFE_NBP_MARK }; + + for (i = 0; i < MAX_PIPES; i++) { + if (res_ctx->pipe_ctx[i].stream == NULL) + continue; + + res_ctx->pipe_ctx[i].mi->funcs->mem_input_program_display_marks( + res_ctx->pipe_ctx[i].mi, + nbp_marks, + max_marks, + max_marks, + MAX_WATERMARK); + if (i == underlay_idx) + res_ctx->pipe_ctx[i].mi->funcs->mem_input_program_chroma_display_marks( + res_ctx->pipe_ctx[i].mi, + nbp_marks, + max_marks, + max_marks, + MAX_WATERMARK); + } +} + +static void switch_dp_clock_sources( + const struct core_dc *dc, + struct resource_context *res_ctx) +{ + uint8_t i; + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i]; + + if (pipe_ctx->stream == NULL || pipe_ctx->top_pipe) + continue; + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) { + struct clock_source *clk_src = + resource_find_used_clk_src_for_sharing( + res_ctx, pipe_ctx); + + if (clk_src && + clk_src != pipe_ctx->clock_source) { + resource_unreference_clock_source( + res_ctx, pipe_ctx->clock_source); + pipe_ctx->clock_source = clk_src; + resource_reference_clock_source(res_ctx, clk_src); + + dce_crtc_switch_to_clk_src(dc->hwseq, clk_src, i); + } + } + } +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ + +static void reset_single_pipe_hw_ctx( + const struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + core_link_disable_stream(pipe_ctx); + if (!pipe_ctx->tg->funcs->set_blank(pipe_ctx->tg, true)) { + dm_error("DC: failed to blank crtc!\n"); + BREAK_TO_DEBUGGER(); + } + pipe_ctx->tg->funcs->disable_crtc(pipe_ctx->tg); + pipe_ctx->mi->funcs->free_mem_input( + pipe_ctx->mi, context->target_count); + resource_unreference_clock_source( + &context->res_ctx, pipe_ctx->clock_source); + + dc->hwss.power_down_front_end((struct core_dc *)dc, pipe_ctx); + + pipe_ctx->stream = NULL; +} + +static void set_drr(struct pipe_ctx **pipe_ctx, + int num_pipes, int vmin, int vmax) +{ + int i = 0; + struct drr_params params = {0}; + + params.vertical_total_max = vmax; + params.vertical_total_min = vmin; + + /* TODO: If multiple pipes are to be supported, you need + * some GSL stuff + */ + + for (i = 0; i < num_pipes; i++) { + pipe_ctx[i]->tg->funcs->set_drr(pipe_ctx[i]->tg, ¶ms); + } +} + +static void set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, int value) +{ + unsigned int i; + + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->tg->funcs-> + set_static_screen_control(pipe_ctx[i]->tg, value); +} + +/* unit: in_khz before mode set, get pixel clock from context. ASIC register + * may not be programmed yet. + * TODO: after mode set, pre_mode_set = false, + * may read PLL register to get pixel clock + */ +static uint32_t get_max_pixel_clock_for_all_paths( + struct core_dc *dc, + struct validate_context *context, + bool pre_mode_set) +{ + uint32_t max_pix_clk = 0; + int i; + + if (!pre_mode_set) { + /* TODO: read ASIC register to get pixel clock */ + ASSERT(0); + } + + for (i = 0; i < MAX_PIPES; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + /* do not check under lay */ + if (pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->pix_clk_params.requested_pix_clk > max_pix_clk) + max_pix_clk = + pipe_ctx->pix_clk_params.requested_pix_clk; + } + + if (max_pix_clk == 0) + ASSERT(0); + + return max_pix_clk; +} + +/* + * Find clock state based on clock requested. if clock value is 0, simply + * set clock state as requested without finding clock state by clock value + */ +static void apply_min_clocks( + struct core_dc *dc, + struct validate_context *context, + enum clocks_state *clocks_state, + bool pre_mode_set) +{ + struct state_dependent_clocks req_clocks = {0}; + struct pipe_ctx *pipe_ctx; + int i; + + for (i = 0; i < MAX_PIPES; i++) { + pipe_ctx = &context->res_ctx.pipe_ctx[i]; + if (pipe_ctx->dis_clk != NULL) + break; + } + + if (!pre_mode_set) { + /* set clock_state without verification */ + if (dal_display_clock_set_min_clocks_state( + pipe_ctx->dis_clk, *clocks_state)) + return; + + /* TODOFPGA */ + } + + /* get the required state based on state dependent clocks: + * display clock and pixel clock + */ + req_clocks.display_clk_khz = context->bw_results.dispclk_khz; + + req_clocks.pixel_clk_khz = get_max_pixel_clock_for_all_paths( + dc, context, true); + + if (dal_display_clock_get_required_clocks_state( + pipe_ctx->dis_clk, &req_clocks, clocks_state)) { + dal_display_clock_set_min_clocks_state( + pipe_ctx->dis_clk, *clocks_state); + } else { + } +} + +static enum dc_status apply_ctx_to_hw_fpga( + struct core_dc *dc, + struct validate_context *context) +{ + enum dc_status status = DC_ERROR_UNEXPECTED; + int i; + + for (i = 0; i < context->res_ctx.pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) + continue; + + status = apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + if (status != DC_OK) + return status; + } + + return DC_OK; +} + +static void reset_hw_ctx_wrap( + struct core_dc *dc, + struct validate_context *context) +{ + int i; + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + for (i = 0; i < context->res_ctx.pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /* Note: We need to disable output if clock sources change, + * since bios does optimization and doesn't apply if changing + * PHY when not already disabled. + */ + + /* Skip underlay pipe since it will be handled in commit surface*/ + if (!pipe_ctx_old->stream || pipe_ctx_old->top_pipe) + continue; + + if (!pipe_ctx->stream || + pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + reset_single_pipe_hw_ctx( + dc, pipe_ctx_old, dc->current_context); + } +} + +/*TODO: const validate_context*/ +enum dc_status dce110_apply_ctx_to_hw( + struct core_dc *dc, + struct validate_context *context) +{ + struct dc_bios *dcb = dc->ctx->dc_bios; + enum dc_status status; + int i; + bool programmed_audio_dto = false; + enum clocks_state clocks_state = CLOCKS_STATE_INVALID; + + /* Reset old context */ + /* look up the targets that have been removed since last commit */ + dc->hwss.reset_hw_ctx_wrap(dc, context); + + /* Skip applying if no targets */ + if (context->target_count <= 0) + return DC_OK; + + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + apply_ctx_to_hw_fpga(dc, context); + return DC_OK; + } + + /* Apply new context */ + dcb->funcs->set_scratch_critical_state(dcb, true); + + /* below is for real asic only */ + for (i = 0; i < context->res_ctx.pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL || pipe_ctx->top_pipe) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) { + if (pipe_ctx_old->clock_source != pipe_ctx->clock_source) + dce_crtc_switch_to_clk_src(dc->hwseq, + pipe_ctx->clock_source, i); + continue; + } + + dc->hwss.enable_display_power_gating( + dc, i, dc->ctx->dc_bios, + PIPE_GATING_CONTROL_DISABLE); + } + + set_safe_displaymarks(&context->res_ctx); + /*TODO: when pplib works*/ + apply_min_clocks(dc, context, &clocks_state, true); + + if (context->bw_results.dispclk_khz + > dc->current_context->bw_results.dispclk_khz) + dc->hwss.set_display_clock(context); + + for (i = 0; i < context->res_ctx.pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx_old = + &dc->current_context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->stream == NULL) + continue; + + if (pipe_ctx->stream == pipe_ctx_old->stream) + continue; + + if (pipe_ctx->top_pipe) + continue; + + if (context->res_ctx.pipe_ctx[i].audio != NULL) { + /* Setup audio rate clock source */ + /* Issue: + * Audio lag happened on DP monitor when unplug a HDMI monitor + * + * Cause: + * In case of DP and HDMI connected or HDMI only, DCCG_AUDIO_DTO_SEL + * is set to either dto0 or dto1, audio should work fine. + * In case of DP connected only, DCCG_AUDIO_DTO_SEL should be dto1, + * set to dto0 will cause audio lag. + * + * Solution: + * Not optimized audio wall dto setup. When mode set, iterate pipe_ctx, + * find first available pipe with audio, setup audio wall DTO per topology + * instead of per pipe. + */ + struct audio_output audio_output; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + build_audio_output(pipe_ctx, &audio_output); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_enc->funcs->dp_audio_setup( + pipe_ctx->stream_enc, + pipe_ctx->audio->inst, + &pipe_ctx->stream->public.audio_info); + else + pipe_ctx->stream_enc->funcs->hdmi_audio_setup( + pipe_ctx->stream_enc, + pipe_ctx->audio->inst, + &pipe_ctx->stream->public.audio_info, + &audio_output.crtc_info); + + pipe_ctx->audio->funcs->az_configure( + pipe_ctx->audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &pipe_ctx->stream->public.audio_info); + + if (!programmed_audio_dto) { + pipe_ctx->audio->funcs->wall_dto_setup( + pipe_ctx->audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + programmed_audio_dto = true; + } + } + + status = apply_single_controller_ctx_to_hw( + pipe_ctx, + context, + dc); + + if (DC_OK != status) + return status; + } + + dc->hwss.set_displaymarks(dc, context); + + /* to save power */ + apply_min_clocks(dc, context, &clocks_state, false); + + dcb->funcs->set_scratch_critical_state(dcb, false); + + switch_dp_clock_sources(dc, &context->res_ctx); + + return DC_OK; +} + +/******************************************************************************* + * Front End programming + ******************************************************************************/ +static void set_default_colors(struct pipe_ctx *pipe_ctx) +{ + struct default_adjustment default_adjust = { 0 }; + + default_adjust.force_hw_default = false; + if (pipe_ctx->surface == NULL) + default_adjust.in_color_space = COLOR_SPACE_SRGB; + else + default_adjust.in_color_space = + pipe_ctx->surface->public.color_space; + if (pipe_ctx->stream == NULL) + default_adjust.out_color_space = COLOR_SPACE_SRGB; + else + default_adjust.out_color_space = + pipe_ctx->stream->public.output_color_space; + default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW; + default_adjust.surface_pixel_format = pipe_ctx->scl_data.format; + + /* display color depth */ + default_adjust.color_depth = + pipe_ctx->stream->public.timing.display_color_depth; + + /* Lb color depth */ + default_adjust.lb_color_depth = pipe_ctx->scl_data.lb_params.depth; + + pipe_ctx->opp->funcs->opp_set_csc_default( + pipe_ctx->opp, &default_adjust); +} + +static void program_blender(const struct core_dc *dc, + struct pipe_ctx *pipe_ctx) +{ + enum blnd_mode blender_mode = BLND_MODE_CURRENT_PIPE; + + if (pipe_ctx->bottom_pipe) { + if (pipe_ctx->bottom_pipe->surface->public.visible) { + if (pipe_ctx->surface->public.visible) + blender_mode = BLND_MODE_BLENDING; + else + blender_mode = BLND_MODE_OTHER_PIPE; + } + } + dce_set_blender_mode(dc->hwseq, pipe_ctx->pipe_idx, blender_mode); +} + +/** + * TODO REMOVE, USE UPDATE INSTEAD + */ +static void set_plane_config( + const struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct resource_context *res_ctx) +{ + struct mem_input *mi = pipe_ctx->mi; + struct core_surface *surface = pipe_ctx->surface; + struct xfm_grph_csc_adjustment adjust; + struct out_csc_color_matrix tbl_entry; + unsigned int i; + + memset(&adjust, 0, sizeof(adjust)); + memset(&tbl_entry, 0, sizeof(tbl_entry)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + dce_enable_fe_clock(dc->hwseq, pipe_ctx->pipe_idx, true); + + set_default_colors(pipe_ctx); + if (pipe_ctx->stream->public.csc_color_matrix.enable_adjustment + == true) { + tbl_entry.color_space = + pipe_ctx->stream->public.output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = + pipe_ctx->stream->public.csc_color_matrix.matrix[i]; + + pipe_ctx->opp->funcs->opp_set_csc_adjustment + (pipe_ctx->opp, &tbl_entry); + } + + if (pipe_ctx->stream->public.gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + adjust.temperature_matrix[0] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[0]; + adjust.temperature_matrix[1] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[1]; + adjust.temperature_matrix[2] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[2]; + adjust.temperature_matrix[3] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[4]; + adjust.temperature_matrix[4] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[5]; + adjust.temperature_matrix[5] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[6]; + adjust.temperature_matrix[6] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[8]; + adjust.temperature_matrix[7] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[9]; + adjust.temperature_matrix[8] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[10]; + } + + pipe_ctx->xfm->funcs->transform_set_gamut_remap(pipe_ctx->xfm, &adjust); + + pipe_ctx->scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + program_scaler(dc, pipe_ctx); + + program_blender(dc, pipe_ctx); + + mi->funcs->mem_input_program_surface_config( + mi, + surface->public.format, + &surface->public.tiling_info, + &surface->public.plane_size, + surface->public.rotation, + NULL, + false); + + if (dc->public.config.gpu_vm_support) + mi->funcs->mem_input_program_pte_vm( + pipe_ctx->mi, + surface->public.format, + &surface->public.tiling_info, + surface->public.rotation); +} + +static void update_plane_addr(const struct core_dc *dc, + struct pipe_ctx *pipe_ctx) +{ + struct core_surface *surface = pipe_ctx->surface; + + if (surface == NULL) + return; + + pipe_ctx->mi->funcs->mem_input_program_surface_flip_and_addr( + pipe_ctx->mi, + &surface->public.address, + surface->public.flip_immediate); + + surface->status.requested_address = surface->public.address; + + if (surface->public.visible) + pipe_ctx->tg->funcs->set_blank(pipe_ctx->tg, false); +} + +void dce110_update_pending_status(struct pipe_ctx *pipe_ctx) +{ + struct core_surface *surface = pipe_ctx->surface; + + if (surface == NULL) + return; + + surface->status.is_flip_pending = + pipe_ctx->mi->funcs->mem_input_is_flip_pending( + pipe_ctx->mi); + + if (surface->status.is_flip_pending && !surface->public.visible) + pipe_ctx->mi->current_address = pipe_ctx->mi->request_address; + + surface->status.current_address = pipe_ctx->mi->current_address; +} + +void dce110_power_down(struct core_dc *dc) +{ + power_down_all_hw_blocks(dc); + disable_vga_and_power_gate_all_controllers(dc); +} + +static bool wait_for_reset_trigger_to_occur( + struct dc_context *dc_ctx, + struct timing_generator *tg) +{ + bool rc = false; + + /* To avoid endless loop we wait at most + * frames_to_wait_on_triggered_reset frames for the reset to occur. */ + const uint32_t frames_to_wait_on_triggered_reset = 10; + uint32_t i; + + for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { + + if (!tg->funcs->is_counter_moving(tg)) { + DC_ERROR("TG counter is not moving!\n"); + break; + } + + if (tg->funcs->did_triggered_reset_occur(tg)) { + rc = true; + /* usually occurs at i=1 */ + DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", + i); + break; + } + + /* Wait for one frame. */ + tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE); + tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); + } + + if (false == rc) + DC_ERROR("GSL: Timeout on reset trigger!\n"); + + return rc; +} + +/* Enable timing synchronization for a group of Timing Generators. */ +static void dce110_enable_timing_synchronization( + struct core_dc *dc, + int group_index, + int group_size, + struct pipe_ctx *grouped_pipes[]) +{ + struct dc_context *dc_ctx = dc->ctx; + struct dcp_gsl_params gsl_params = { 0 }; + int i; + + DC_SYNC_INFO("GSL: Setting-up...\n"); + + /* Designate a single TG in the group as a master. + * Since HW doesn't care which one, we always assign + * the 1st one in the group. */ + gsl_params.gsl_group = 0; + gsl_params.gsl_master = grouped_pipes[0]->tg->inst; + + for (i = 0; i < group_size; i++) + grouped_pipes[i]->tg->funcs->setup_global_swap_lock( + grouped_pipes[i]->tg, &gsl_params); + + /* Reset slave controllers on master VSync */ + DC_SYNC_INFO("GSL: enabling trigger-reset\n"); + + for (i = 1 /* skip the master */; i < group_size; i++) + grouped_pipes[i]->tg->funcs->enable_reset_trigger( + grouped_pipes[i]->tg, gsl_params.gsl_group); + + + + for (i = 1 /* skip the master */; i < group_size; i++) { + DC_SYNC_INFO("GSL: waiting for reset to occur.\n"); + wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->tg); + /* Regardless of success of the wait above, remove the reset or + * the driver will start timing out on Display requests. */ + DC_SYNC_INFO("GSL: disabling trigger-reset.\n"); + grouped_pipes[i]->tg->funcs->disable_reset_trigger(grouped_pipes[i]->tg); + } + + + /* GSL Vblank synchronization is a one time sync mechanism, assumption + * is that the sync'ed displays will not drift out of sync over time*/ + DC_SYNC_INFO("GSL: Restoring register states.\n"); + for (i = 0; i < group_size; i++) + grouped_pipes[i]->tg->funcs->tear_down_global_swap_lock(grouped_pipes[i]->tg); + + DC_SYNC_INFO("GSL: Set-up complete.\n"); +} + +static void init_hw(struct core_dc *dc) +{ + int i; + struct dc_bios *bp; + struct transform *xfm; + + bp = dc->ctx->dc_bios; + for (i = 0; i < dc->res_pool->pipe_count; i++) { + xfm = dc->res_pool->transforms[i]; + xfm->funcs->transform_reset(xfm); + + dc->hwss.enable_display_power_gating( + dc, i, bp, + PIPE_GATING_CONTROL_INIT); + dc->hwss.enable_display_power_gating( + dc, i, bp, + PIPE_GATING_CONTROL_DISABLE); + dc->hwss.enable_display_pipe_clock_gating( + dc->ctx, + true); + } + + dce_clock_gating_power_up(dc->hwseq, false);; + /***************************************/ + + for (i = 0; i < dc->link_count; i++) { + /****************************************/ + /* Power up AND update implementation according to the + * required signal (which may be different from the + * default signal on connector). */ + struct core_link *link = dc->links[i]; + link->link_enc->funcs->hw_init(link->link_enc); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct timing_generator *tg = dc->res_pool->timing_generators[i]; + + tg->funcs->disable_vga(tg); + + /* Blank controller using driver code instead of + * command table. */ + tg->funcs->set_blank(tg, true); + } + + for (i = 0; i < dc->res_pool->audio_count; i++) { + struct audio *audio = dc->res_pool->audios[i]; + audio->funcs->hw_init(audio); + } +} + +/* TODO: move this to apply_ctx_tohw some how?*/ +static void dce110_power_on_pipe_if_needed( + struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + struct pipe_ctx *old_pipe_ctx = &dc->current_context->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; + struct dc_bios *dcb = dc->ctx->dc_bios; + struct tg_color black_color = {0}; + + if (!old_pipe_ctx->stream && pipe_ctx->stream) { + dc->hwss.enable_display_power_gating( + dc, + pipe_ctx->pipe_idx, + dcb, PIPE_GATING_CONTROL_DISABLE); + + /* + * This is for powering on underlay, so crtc does not + * need to be enabled + */ + + pipe_ctx->tg->funcs->program_timing(pipe_ctx->tg, + &pipe_ctx->stream->public.timing, + false); + + pipe_ctx->tg->funcs->enable_advanced_request( + pipe_ctx->tg, + true, + &pipe_ctx->stream->public.timing); + + pipe_ctx->mi->funcs->allocate_mem_input(pipe_ctx->mi, + pipe_ctx->stream->public.timing.h_total, + pipe_ctx->stream->public.timing.v_total, + pipe_ctx->stream->public.timing.pix_clk_khz, + context->target_count); + + /* TODO unhardcode*/ + color_space_to_black_color(dc, + COLOR_SPACE_YCBCR601, &black_color); + pipe_ctx->tg->funcs->set_blank_color( + pipe_ctx->tg, + &black_color); + } +} + +static void dce110_increase_watermarks_for_pipe( + struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + if (did_watermarks_increase(pipe_ctx, context, dc->current_context)) + program_wm_for_pipe(dc, pipe_ctx, context); +} + +static void dce110_set_bandwidth(struct core_dc *dc) +{ + int i; + + for (i = 0; i < dc->current_context->res_ctx.pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &dc->current_context->res_ctx.pipe_ctx[i]; + + if (!pipe_ctx->stream) + continue; + + program_wm_for_pipe(dc, pipe_ctx, dc->current_context); + } + + dc->hwss.set_display_clock(dc->current_context); +} + +static void dce110_program_front_end_for_pipe( + struct core_dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct mem_input *mi = pipe_ctx->mi; + struct pipe_ctx *old_pipe = NULL; + struct core_surface *surface = pipe_ctx->surface; + struct xfm_grph_csc_adjustment adjust; + struct out_csc_color_matrix tbl_entry; + unsigned int i; + + memset(&tbl_entry, 0, sizeof(tbl_entry)); + + if (dc->current_context) + old_pipe = &dc->current_context->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; + + memset(&adjust, 0, sizeof(adjust)); + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; + + dce_enable_fe_clock(dc->hwseq, pipe_ctx->pipe_idx, true); + + set_default_colors(pipe_ctx); + if (pipe_ctx->stream->public.csc_color_matrix.enable_adjustment + == true) { + tbl_entry.color_space = + pipe_ctx->stream->public.output_color_space; + + for (i = 0; i < 12; i++) + tbl_entry.regval[i] = + pipe_ctx->stream->public.csc_color_matrix.matrix[i]; + + pipe_ctx->opp->funcs->opp_set_csc_adjustment + (pipe_ctx->opp, &tbl_entry); + } + + if (pipe_ctx->stream->public.gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + adjust.temperature_matrix[0] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[0]; + adjust.temperature_matrix[1] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[1]; + adjust.temperature_matrix[2] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[2]; + adjust.temperature_matrix[3] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[4]; + adjust.temperature_matrix[4] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[5]; + adjust.temperature_matrix[5] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[6]; + adjust.temperature_matrix[6] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[8]; + adjust.temperature_matrix[7] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[9]; + adjust.temperature_matrix[8] = + pipe_ctx->stream-> + public.gamut_remap_matrix.matrix[10]; + } + + pipe_ctx->xfm->funcs->transform_set_gamut_remap(pipe_ctx->xfm, &adjust); + + pipe_ctx->scl_data.lb_params.alpha_en = pipe_ctx->bottom_pipe != 0; + if (old_pipe && memcmp(&old_pipe->scl_data, + &pipe_ctx->scl_data, + sizeof(struct scaler_data)) != 0) + program_scaler(dc, pipe_ctx); + + mi->funcs->mem_input_program_surface_config( + mi, + surface->public.format, + &surface->public.tiling_info, + &surface->public.plane_size, + surface->public.rotation, + false, + false); + + if (dc->public.config.gpu_vm_support) + mi->funcs->mem_input_program_pte_vm( + pipe_ctx->mi, + surface->public.format, + &surface->public.tiling_info, + surface->public.rotation); + + dm_logger_write(dc->ctx->logger, LOG_SURFACE, + "Pipe:%d 0x%x: addr hi:0x%x, " + "addr low:0x%x, " + "src: %d, %d, %d," + " %d; dst: %d, %d, %d, %d;" + "clip: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + pipe_ctx->surface, + pipe_ctx->surface->public.address.grph.addr.high_part, + pipe_ctx->surface->public.address.grph.addr.low_part, + pipe_ctx->surface->public.src_rect.x, + pipe_ctx->surface->public.src_rect.y, + pipe_ctx->surface->public.src_rect.width, + pipe_ctx->surface->public.src_rect.height, + pipe_ctx->surface->public.dst_rect.x, + pipe_ctx->surface->public.dst_rect.y, + pipe_ctx->surface->public.dst_rect.width, + pipe_ctx->surface->public.dst_rect.height, + pipe_ctx->surface->public.clip_rect.x, + pipe_ctx->surface->public.clip_rect.y, + pipe_ctx->surface->public.clip_rect.width, + pipe_ctx->surface->public.clip_rect.height); + + dm_logger_write(dc->ctx->logger, LOG_SURFACE, + "Pipe %d: width, height, x, y\n" + "viewport:%d, %d, %d, %d\n" + "recout: %d, %d, %d, %d\n", + pipe_ctx->pipe_idx, + pipe_ctx->scl_data.viewport.width, + pipe_ctx->scl_data.viewport.height, + pipe_ctx->scl_data.viewport.x, + pipe_ctx->scl_data.viewport.y, + pipe_ctx->scl_data.recout.width, + pipe_ctx->scl_data.recout.height, + pipe_ctx->scl_data.recout.x, + pipe_ctx->scl_data.recout.y); +} + + + +static void dce110_prepare_pipe_for_surface_commit( + struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) { + struct core_gamma *gamma = NULL; + + dc->hwss.increase_watermarks_for_pipe(dc, pipe_ctx, context); + + if (pipe_ctx->surface->public.gamma_correction) + gamma = DC_GAMMA_TO_CORE( + pipe_ctx->surface->public.gamma_correction); + + dc->hwss.set_gamma_correction( + pipe_ctx->ipp, + pipe_ctx->opp, + gamma, pipe_ctx->surface); +} + +static void dce110_prepare_pipe_for_context( + struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + dce110_power_on_pipe_if_needed(dc, pipe_ctx, context); + dce110_prepare_pipe_for_surface_commit(dc, pipe_ctx, context); +} + +static void dce110_apply_ctx_for_surface( + struct core_dc *dc, + struct core_surface *surface, + struct validate_context *context) +{ + int i; + + /* TODO remove when removing the surface reset workaroud*/ + if (!surface) + return; + + for (i = 0; i < context->res_ctx.pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (pipe_ctx->surface != surface) + continue; + + dce110_program_front_end_for_pipe(dc, pipe_ctx); + program_blender(dc, pipe_ctx); + + } + +} + +static void dce110_power_down_fe(struct core_dc *dc, struct pipe_ctx *pipe) +{ + int i; + + for (i = 0; i < dc->res_pool->pipe_count; i++) + if (&dc->current_context->res_ctx.pipe_ctx[i] == pipe) + break; + + if (i == dc->res_pool->pipe_count) + return; + + dc->hwss.enable_display_power_gating( + dc, i, dc->ctx->dc_bios, PIPE_GATING_CONTROL_ENABLE); + if (pipe->xfm) + pipe->xfm->funcs->transform_reset(pipe->xfm); + memset(&pipe->scl_data, 0, sizeof(struct scaler_data)); +} + +static const struct hw_sequencer_funcs dce110_funcs = { + .init_hw = init_hw, + .apply_ctx_to_hw = dce110_apply_ctx_to_hw, + .prepare_pipe_for_context = dce110_prepare_pipe_for_context, + .apply_ctx_for_surface = dce110_apply_ctx_for_surface, + .set_plane_config = set_plane_config, + .update_plane_addr = update_plane_addr, + .update_pending_status = dce110_update_pending_status, + .set_gamma_correction = set_gamma_ramp, + .power_down = dce110_power_down, + .enable_accelerated_mode = dce110_enable_accelerated_mode, + .enable_timing_synchronization = dce110_enable_timing_synchronization, + .update_info_frame = dce110_update_info_frame, + .enable_stream = dce110_enable_stream, + .disable_stream = dce110_disable_stream, + .unblank_stream = dce110_unblank_stream, + .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating, + .enable_display_power_gating = dce110_enable_display_power_gating, + .power_down_front_end = dce110_power_down_fe, + .pipe_control_lock = dce_pipe_control_lock, + .set_display_clock = dce110_set_display_clock, + .set_displaymarks = dce110_set_displaymarks, + .increase_watermarks_for_pipe = dce110_increase_watermarks_for_pipe, + .set_bandwidth = dce110_set_bandwidth, + .set_drr = set_drr, + .set_static_screen_control = set_static_screen_control, + .reset_hw_ctx_wrap = reset_hw_ctx_wrap, + .prog_pixclk_crtc_otg = prog_pixclk_crtc_otg, +}; + +bool dce110_hw_sequencer_construct(struct core_dc *dc) +{ + dc->hwss = dce110_funcs; + + return true; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h new file mode 100644 index 000000000000..a6b4d0d2429f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.h @@ -0,0 +1,62 @@ +/* +* 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_HWSS_DCE110_H__ +#define __DC_HWSS_DCE110_H__ + +#include "core_types.h" + +#define GAMMA_HW_POINTS_NUM 256 +struct core_dc; + +bool dce110_hw_sequencer_construct(struct core_dc *dc); + +enum dc_status dce110_apply_ctx_to_hw( + struct core_dc *dc, + struct validate_context *context); + +void dce110_set_display_clock(struct validate_context *context); + +void dce110_set_displaymarks( + const struct core_dc *dc, + struct validate_context *context); + +void dce110_enable_stream(struct pipe_ctx *pipe_ctx); + +void dce110_disable_stream(struct pipe_ctx *pipe_ctx); + +void dce110_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings); + +void dce110_update_info_frame(struct pipe_ctx *pipe_ctx); + +void dce110_enable_accelerated_mode(struct core_dc *dc); + +void dce110_power_down(struct core_dc *dc); + +void dce110_update_pending_status(struct pipe_ctx *pipe_ctx); + +#endif /* __DC_HWSS_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp.c new file mode 100644 index 000000000000..dd69f6060bb9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp.c @@ -0,0 +1,62 @@ +/* + * 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 "include/logger_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_ipp.h" + +static const struct ipp_funcs funcs = { + .ipp_cursor_set_attributes = dce110_ipp_cursor_set_attributes, + .ipp_cursor_set_position = dce110_ipp_cursor_set_position, + .ipp_program_prescale = dce110_ipp_program_prescale, + .ipp_set_degamma = dce110_ipp_set_degamma, +}; + +bool dce110_ipp_construct( + struct dce110_ipp* ipp, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offset) +{ + ipp->base.ctx = ctx; + + ipp->base.inst = inst; + + ipp->offsets = *offset; + + ipp->base.funcs = &funcs; + + return true; +} + +void dce110_ipp_destroy(struct input_pixel_processor **ipp) +{ + dm_free(TO_DCE110_IPP(*ipp)); + *ipp = NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp.h new file mode 100644 index 000000000000..60eebdecfa10 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp.h @@ -0,0 +1,76 @@ +/* + * 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_IPP_DCE110_H__ +#define __DC_IPP_DCE110_H__ + +#include "ipp.h" + +struct gamma_parameters; +struct dev_c_lut; + +#define TO_DCE110_IPP(input_pixel_processor)\ + container_of(input_pixel_processor, struct dce110_ipp, base) + +struct dce110_ipp_reg_offsets { + uint32_t dcp_offset; +}; + +struct dce110_ipp { + struct input_pixel_processor base; + struct dce110_ipp_reg_offsets offsets; +}; + +bool dce110_ipp_construct( + struct dce110_ipp* ipp, + struct dc_context *ctx, + enum controller_id id, + const struct dce110_ipp_reg_offsets *offset); + +void dce110_ipp_destroy(struct input_pixel_processor **ipp); + +/* CURSOR RELATED */ +void dce110_ipp_cursor_set_position( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position); + +bool dce110_ipp_cursor_set_attributes( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes); + +/* DEGAMMA RELATED */ +bool dce110_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode); + +void dce110_ipp_program_prescale( + struct input_pixel_processor *ipp, + struct ipp_prescale_params *params); +/* + * Helper functions to be resused in other ASICs + */ +void dce110_helper_select_lut(struct dce110_ipp *ipp110); + +#endif /*__DC_IPP_DCE110_H__*/ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp_cursor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp_cursor.c new file mode 100644 index 000000000000..95f6ca3ba5df --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp_cursor.c @@ -0,0 +1,253 @@ +/* + * 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 "include/logger_interface.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_ipp.h" + +#define CURSOR_COLOR_BLACK 0x00000000 +#define CURSOR_COLOR_WHITE 0xFFFFFFFF + +#define DCP_REG(reg)\ + (reg + ipp110->offsets.dcp_offset) + +static void enable( + struct dce110_ipp *ipp110, + bool enable); + +static void lock( + struct dce110_ipp *ipp110, + bool enable); + +static void program_position( + struct dce110_ipp *ipp110, + uint32_t x, + uint32_t y); + +static bool program_control( + struct dce110_ipp *ipp110, + enum dc_cursor_color_format color_format, + bool enable_magnification, + bool inverse_transparent_clamping); + +static void program_hotspot( + struct dce110_ipp *ipp110, + uint32_t x, + uint32_t y); + +static void program_size( + struct dce110_ipp *ipp110, + uint32_t width, + uint32_t height); + +static void program_address( + struct dce110_ipp *ipp110, + PHYSICAL_ADDRESS_LOC address); + +void dce110_ipp_cursor_set_position( + struct input_pixel_processor *ipp, + const struct dc_cursor_position *position) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + + /* lock cursor registers */ + lock(ipp110, true); + + /* Flag passed in structure differentiates cursor enable/disable. */ + /* Update if it differs from cached state. */ + enable(ipp110, position->enable); + + program_position(ipp110, position->x, position->y); + + if (position->hot_spot_enable) + program_hotspot( + ipp110, + position->x_hotspot, + position->y_hotspot); + + /* unlock cursor registers */ + lock(ipp110, false); +} + +bool dce110_ipp_cursor_set_attributes( + struct input_pixel_processor *ipp, + const struct dc_cursor_attributes *attributes) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + /* Lock cursor registers */ + lock(ipp110, true); + + /* Program cursor control */ + program_control( + ipp110, + attributes->color_format, + attributes->attribute_flags.bits.ENABLE_MAGNIFICATION, + attributes->attribute_flags.bits.INVERSE_TRANSPARENT_CLAMPING); + + /* Program hot spot coordinates */ + program_hotspot(ipp110, attributes->x_hot, attributes->y_hot); + + /* + * Program cursor size -- NOTE: HW spec specifies that HW register + * stores size as (height - 1, width - 1) + */ + program_size(ipp110, attributes->width-1, attributes->height-1); + + /* Program cursor surface address */ + program_address(ipp110, attributes->address); + + /* Unlock Cursor registers. */ + lock(ipp110, false); + + return true; +} + +static void enable( + struct dce110_ipp *ipp110, bool enable) +{ + uint32_t value = 0; + uint32_t addr = DCP_REG(mmCUR_CONTROL); + + value = dm_read_reg(ipp110->base.ctx, addr); + set_reg_field_value(value, enable, CUR_CONTROL, CURSOR_EN); + dm_write_reg(ipp110->base.ctx, addr, value); +} + +static void lock( + struct dce110_ipp *ipp110, bool lock) +{ + uint32_t value = 0; + uint32_t addr = DCP_REG(mmCUR_UPDATE); + + value = dm_read_reg(ipp110->base.ctx, addr); + set_reg_field_value(value, lock, CUR_UPDATE, CURSOR_UPDATE_LOCK); + dm_write_reg(ipp110->base.ctx, addr, value); +} + +static void program_position( + struct dce110_ipp *ipp110, + uint32_t x, + uint32_t y) +{ + uint32_t value = 0; + uint32_t addr = DCP_REG(mmCUR_POSITION); + + value = dm_read_reg(ipp110->base.ctx, addr); + set_reg_field_value(value, x, CUR_POSITION, CURSOR_X_POSITION); + set_reg_field_value(value, y, CUR_POSITION, CURSOR_Y_POSITION); + dm_write_reg(ipp110->base.ctx, addr, value); +} + +static bool program_control( + struct dce110_ipp *ipp110, + enum dc_cursor_color_format color_format, + bool enable_magnification, + bool inverse_transparent_clamping) +{ + uint32_t value = 0; + uint32_t addr = DCP_REG(mmCUR_CONTROL); + uint32_t mode = 0; + + switch (color_format) { + case CURSOR_MODE_MONO: + mode = 0; + break; + case CURSOR_MODE_COLOR_1BIT_AND: + mode = 1; + break; + case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: + mode = 2; + break; + case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA: + mode = 3; + break; + default: + return false; + } + + set_reg_field_value(value, mode, CUR_CONTROL, CURSOR_MODE); + set_reg_field_value(value, enable_magnification, + CUR_CONTROL, CURSOR_2X_MAGNIFY); + set_reg_field_value(value, inverse_transparent_clamping, + CUR_CONTROL, CUR_INV_TRANS_CLAMP); + dm_write_reg(ipp110->base.ctx, addr, value); + + if (color_format == CURSOR_MODE_MONO) { + addr = DCP_REG(mmCUR_COLOR1); + dm_write_reg(ipp110->base.ctx, addr, CURSOR_COLOR_BLACK); + addr = DCP_REG(mmCUR_COLOR2); + dm_write_reg(ipp110->base.ctx, addr, CURSOR_COLOR_WHITE); + } + return true; +} + +static void program_hotspot( + struct dce110_ipp *ipp110, + uint32_t x, + uint32_t y) +{ + uint32_t value = 0; + uint32_t addr = DCP_REG(mmCUR_HOT_SPOT); + + value = dm_read_reg(ipp110->base.ctx, addr); + set_reg_field_value(value, x, CUR_HOT_SPOT, CURSOR_HOT_SPOT_X); + set_reg_field_value(value, y, CUR_HOT_SPOT, CURSOR_HOT_SPOT_Y); + dm_write_reg(ipp110->base.ctx, addr, value); +} + +static void program_size( + struct dce110_ipp *ipp110, + uint32_t width, + uint32_t height) +{ + uint32_t value = 0; + uint32_t addr = DCP_REG(mmCUR_SIZE); + + value = dm_read_reg(ipp110->base.ctx, addr); + set_reg_field_value(value, width, CUR_SIZE, CURSOR_WIDTH); + set_reg_field_value(value, height, CUR_SIZE, CURSOR_HEIGHT); + dm_write_reg(ipp110->base.ctx, addr, value); +} + +static void program_address( + struct dce110_ipp *ipp110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t addr = DCP_REG(mmCUR_SURFACE_ADDRESS_HIGH); + /* SURFACE_ADDRESS_HIGH: Higher order bits (39:32) of hardware cursor + * surface base address in byte. It is 4K byte aligned. + * The correct way to program cursor surface address is to first write + * to CUR_SURFACE_ADDRESS_HIGH, and then write to CUR_SURFACE_ADDRESS */ + + dm_write_reg(ipp110->base.ctx, addr, address.high_part); + + addr = DCP_REG(mmCUR_SURFACE_ADDRESS); + dm_write_reg(ipp110->base.ctx, addr, address.low_part); +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp_gamma.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp_gamma.c new file mode 100644 index 000000000000..79a6a6dd72fc --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_ipp_gamma.c @@ -0,0 +1,303 @@ +/* + * 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 "include/logger_interface.h" +#include "include/fixed31_32.h" +#include "basics/conversion.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_ipp.h" +#include "gamma_types.h" + +#define DCP_REG(reg)\ + (reg + ipp110->offsets.dcp_offset) + +enum { + MAX_INPUT_LUT_ENTRY = 256 +}; + +/*PROTOTYPE DECLARATIONS*/ +static void set_lut_inc( + struct dce110_ipp *ipp110, + uint8_t inc, + bool is_float, + bool is_signed); + +bool dce110_ipp_set_degamma( + struct input_pixel_processor *ipp, + enum ipp_degamma_mode mode) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + + uint32_t value = 0; + + uint32_t degamma_type = (mode == IPP_DEGAMMA_MODE_HW_sRGB) ? 1 : 0; + + ASSERT(mode == IPP_DEGAMMA_MODE_BYPASS || + mode == IPP_DEGAMMA_MODE_HW_sRGB); + + set_reg_field_value( + value, + degamma_type, + DEGAMMA_CONTROL, + GRPH_DEGAMMA_MODE); + + set_reg_field_value( + value, + degamma_type, + DEGAMMA_CONTROL, + CURSOR_DEGAMMA_MODE); + + set_reg_field_value( + value, + degamma_type, + DEGAMMA_CONTROL, + CURSOR2_DEGAMMA_MODE); + + dm_write_reg(ipp110->base.ctx, DCP_REG(mmDEGAMMA_CONTROL), value); + + return true; +} + +void dce110_ipp_program_prescale( + struct input_pixel_processor *ipp, + struct ipp_prescale_params *params) +{ + struct dce110_ipp *ipp110 = TO_DCE110_IPP(ipp); + + uint32_t prescale_control = 0; + uint32_t prescale_value = 0; + uint32_t legacy_lut_control = 0; + + prescale_control = dm_read_reg(ipp110->base.ctx, + DCP_REG(mmPRESCALE_GRPH_CONTROL)); + + if (params->mode != IPP_PRESCALE_MODE_BYPASS) { + + set_reg_field_value( + prescale_control, + 0, + PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS); + + /* + * If prescale is in use, then legacy lut should + * be bypassed + */ + legacy_lut_control = dm_read_reg(ipp110->base.ctx, + DCP_REG(mmINPUT_GAMMA_CONTROL)); + + set_reg_field_value( + legacy_lut_control, + 1, + INPUT_GAMMA_CONTROL, + GRPH_INPUT_GAMMA_MODE); + + dm_write_reg(ipp110->base.ctx, + DCP_REG(mmINPUT_GAMMA_CONTROL), + legacy_lut_control); + } else { + set_reg_field_value( + prescale_control, + 1, + PRESCALE_GRPH_CONTROL, + GRPH_PRESCALE_BYPASS); + } + + set_reg_field_value( + prescale_value, + params->scale, + PRESCALE_VALUES_GRPH_R, + GRPH_PRESCALE_SCALE_R); + + set_reg_field_value( + prescale_value, + params->bias, + PRESCALE_VALUES_GRPH_R, + GRPH_PRESCALE_BIAS_R); + + dm_write_reg(ipp110->base.ctx, + DCP_REG(mmPRESCALE_GRPH_CONTROL), + prescale_control); + + dm_write_reg(ipp110->base.ctx, + DCP_REG(mmPRESCALE_VALUES_GRPH_R), + prescale_value); + + dm_write_reg(ipp110->base.ctx, + DCP_REG(mmPRESCALE_VALUES_GRPH_G), + prescale_value); + + dm_write_reg(ipp110->base.ctx, + DCP_REG(mmPRESCALE_VALUES_GRPH_B), + prescale_value); +} + +static void set_lut_inc( + struct dce110_ipp *ipp110, + uint8_t inc, + bool is_float, + bool is_signed) +{ + const uint32_t addr = DCP_REG(mmDC_LUT_CONTROL); + + uint32_t value = dm_read_reg(ipp110->base.ctx, addr); + + set_reg_field_value( + value, + inc, + DC_LUT_CONTROL, + DC_LUT_INC_R); + + set_reg_field_value( + value, + inc, + DC_LUT_CONTROL, + DC_LUT_INC_G); + + set_reg_field_value( + value, + inc, + DC_LUT_CONTROL, + DC_LUT_INC_B); + + set_reg_field_value( + value, + is_float, + DC_LUT_CONTROL, + DC_LUT_DATA_R_FLOAT_POINT_EN); + + set_reg_field_value( + value, + is_float, + DC_LUT_CONTROL, + DC_LUT_DATA_G_FLOAT_POINT_EN); + + set_reg_field_value( + value, + is_float, + DC_LUT_CONTROL, + DC_LUT_DATA_B_FLOAT_POINT_EN); + + set_reg_field_value( + value, + is_signed, + DC_LUT_CONTROL, + DC_LUT_DATA_R_SIGNED_EN); + + set_reg_field_value( + value, + is_signed, + DC_LUT_CONTROL, + DC_LUT_DATA_G_SIGNED_EN); + + set_reg_field_value( + value, + is_signed, + DC_LUT_CONTROL, + DC_LUT_DATA_B_SIGNED_EN); + + dm_write_reg(ipp110->base.ctx, addr, value); +} + +void dce110_helper_select_lut(struct dce110_ipp *ipp110) +{ + uint32_t value = 0; + + set_lut_inc(ipp110, 0, false, false); + + { + const uint32_t addr = DCP_REG(mmDC_LUT_WRITE_EN_MASK); + + value = dm_read_reg(ipp110->base.ctx, addr); + + /* enable all */ + set_reg_field_value( + value, + 0x7, + DC_LUT_WRITE_EN_MASK, + DC_LUT_WRITE_EN_MASK); + + dm_write_reg(ipp110->base.ctx, addr, value); + } + + { + const uint32_t addr = DCP_REG(mmDC_LUT_RW_MODE); + + value = dm_read_reg(ipp110->base.ctx, addr); + + set_reg_field_value( + value, + 0, + DC_LUT_RW_MODE, + DC_LUT_RW_MODE); + + dm_write_reg(ipp110->base.ctx, addr, value); + } + + { + const uint32_t addr = DCP_REG(mmDC_LUT_CONTROL); + + value = dm_read_reg(ipp110->base.ctx, addr); + + /* 00 - new u0.12 */ + set_reg_field_value( + value, + 3, + DC_LUT_CONTROL, + DC_LUT_DATA_R_FORMAT); + + set_reg_field_value( + value, + 3, + DC_LUT_CONTROL, + DC_LUT_DATA_G_FORMAT); + + set_reg_field_value( + value, + 3, + DC_LUT_CONTROL, + DC_LUT_DATA_B_FORMAT); + + dm_write_reg(ipp110->base.ctx, addr, value); + } + + { + const uint32_t addr = DCP_REG(mmDC_LUT_RW_INDEX); + + value = dm_read_reg(ipp110->base.ctx, addr); + + set_reg_field_value( + value, + 0, + DC_LUT_RW_INDEX, + DC_LUT_RW_INDEX); + + dm_write_reg(ipp110->base.ctx, addr, value); + } +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input.c new file mode 100644 index 000000000000..c0a68c6f585e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input.c @@ -0,0 +1,535 @@ +/* + * 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" +/* TODO: this needs to be looked at, used by Stella's workaround*/ +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" + +#include "include/logger_interface.h" + +#include "dce110_mem_input.h" + +#define DCP_REG(reg) (reg + mem_input110->offsets.dcp) +#define DMIF_REG(reg) (reg + mem_input110->offsets.dmif) +#define PIPE_REG(reg) (reg + mem_input110->offsets.pipe) + +static void program_sec_addr( + struct dce110_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp; + + /*high register MUST be programmed first*/ + temp = address.high_part & + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH__GRPH_SECONDARY_SURFACE_ADDRESS_HIGH_MASK; + set_reg_field_value(value, temp, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, + GRPH_SECONDARY_SURFACE_ADDRESS_HIGH); + dm_write_reg(mem_input110->base.ctx, + DCP_REG(mmGRPH_SECONDARY_SURFACE_ADDRESS_HIGH), value); + + value = 0; + temp = address.low_part >> + GRPH_SECONDARY_SURFACE_ADDRESS__GRPH_SECONDARY_SURFACE_ADDRESS__SHIFT; + set_reg_field_value(value, temp, + GRPH_SECONDARY_SURFACE_ADDRESS, + GRPH_SECONDARY_SURFACE_ADDRESS); + dm_write_reg(mem_input110->base.ctx, + DCP_REG(mmGRPH_SECONDARY_SURFACE_ADDRESS), value); +} + +static void program_pri_addr( + struct dce110_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp; + + /*high register MUST be programmed first*/ + temp = address.high_part & + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_MASK; + set_reg_field_value(value, temp, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH); + dm_write_reg(mem_input110->base.ctx, + DCP_REG(mmGRPH_PRIMARY_SURFACE_ADDRESS_HIGH), value); + + value = 0; + temp = address.low_part >> + GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS__SHIFT; + set_reg_field_value(value, temp, + GRPH_PRIMARY_SURFACE_ADDRESS, + GRPH_PRIMARY_SURFACE_ADDRESS); + dm_write_reg(mem_input110->base.ctx, + DCP_REG(mmGRPH_PRIMARY_SURFACE_ADDRESS), value); +} + +bool dce110_mem_input_is_flip_pending(struct mem_input *mem_input) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + uint32_t value; + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_UPDATE)); + + if (get_reg_field_value(value, GRPH_UPDATE, + GRPH_SURFACE_UPDATE_PENDING)) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +bool dce110_mem_input_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + + uint32_t value = 0; + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_FLIP_CONTROL)); + if (flip_immediate) { + set_reg_field_value(value, 1, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_IMMEDIATE_EN); + set_reg_field_value(value, 1, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_H_RETRACE_EN); + } else { + set_reg_field_value(value, 0, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_IMMEDIATE_EN); + set_reg_field_value(value, 0, GRPH_FLIP_CONTROL, GRPH_SURFACE_UPDATE_H_RETRACE_EN); + } + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_FLIP_CONTROL), value); + + switch (address->type) { + case PLN_ADDR_TYPE_GRAPHICS: + if (address->grph.addr.quad_part == 0) + break; + program_pri_addr(mem_input110, address->grph.addr); + break; + case PLN_ADDR_TYPE_GRPH_STEREO: + if (address->grph_stereo.left_addr.quad_part == 0 + || address->grph_stereo.right_addr.quad_part == 0) + break; + program_pri_addr(mem_input110, address->grph_stereo.left_addr); + program_sec_addr(mem_input110, address->grph_stereo.right_addr); + break; + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + break; + } + + mem_input->request_address = *address; + if (flip_immediate) + mem_input->current_address = *address; + + return true; +} + +/* Scatter Gather param tables */ +static const unsigned int dvmm_Hw_Setting_2DTiling[4][9] = { + { 8, 64, 64, 8, 8, 1, 4, 0, 0}, + { 16, 64, 32, 8, 16, 1, 8, 0, 0}, + { 32, 32, 32, 16, 16, 1, 8, 0, 0}, + { 64, 8, 32, 16, 16, 1, 8, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_1DTiling[4][9] = { + { 8, 512, 8, 1, 0, 1, 0, 0, 0}, /* 0 for invalid */ + { 16, 256, 8, 2, 0, 1, 0, 0, 0}, + { 32, 128, 8, 4, 0, 1, 0, 0, 0}, + { 64, 64, 8, 4, 0, 1, 0, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_Linear[4][9] = { + { 8, 4096, 1, 8, 0, 1, 0, 0, 0}, + { 16, 2048, 1, 8, 0, 1, 0, 0, 0}, + { 32, 1024, 1, 8, 0, 1, 0, 0, 0}, + { 64, 512, 1, 8, 0, 1, 0, 0, 0}, /* new for 64bpp from HW */ +}; + +/* Helper to get table entry from surface info */ +static const unsigned int *get_dvmm_hw_setting( + union dc_tiling_info *tiling_info, + enum surface_pixel_format format) +{ + enum bits_per_pixel { + bpp_8 = 0, + bpp_16, + bpp_32, + bpp_64 + } bpp; + + if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616) + bpp = bpp_64; + else if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB8888) + bpp = bpp_32; + else if (format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB1555) + bpp = bpp_16; + else + bpp = bpp_8; + + switch (tiling_info->gfx8.array_mode) { + case DC_ARRAY_1D_TILED_THIN1: + case DC_ARRAY_1D_TILED_THICK: + case DC_ARRAY_PRT_TILED_THIN1: + return dvmm_Hw_Setting_1DTiling[bpp]; + case DC_ARRAY_2D_TILED_THIN1: + case DC_ARRAY_2D_TILED_THICK: + case DC_ARRAY_2D_TILED_X_THICK: + case DC_ARRAY_PRT_2D_TILED_THIN1: + case DC_ARRAY_PRT_2D_TILED_THICK: + return dvmm_Hw_Setting_2DTiling[bpp]; + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + return dvmm_Hw_Setting_Linear[bpp]; + default: + return dvmm_Hw_Setting_2DTiling[bpp]; + } +} + +bool dce110_mem_input_program_pte_vm( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + const unsigned int *pte = get_dvmm_hw_setting(tiling_info, format); + + unsigned int page_width = 0; + unsigned int page_height = 0; + unsigned int temp_page_width = pte[1]; + unsigned int temp_page_height = pte[2]; + unsigned int min_pte_before_flip = 0; + uint32_t value = 0; + + while ((temp_page_width >>= 1) != 0) + page_width++; + while ((temp_page_height >>= 1) != 0) + page_height++; + + switch (rotation) { + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + min_pte_before_flip = pte[4]; + break; + default: + min_pte_before_flip = pte[3]; + break; + } + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_PIPE_OUTSTANDING_REQUEST_LIMIT)); + set_reg_field_value(value, 0xff, GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT, GRPH_PIPE_OUTSTANDING_REQUEST_LIMIT); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmGRPH_PIPE_OUTSTANDING_REQUEST_LIMIT), value); + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmDVMM_PTE_CONTROL)); + set_reg_field_value(value, page_width, DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH); + set_reg_field_value(value, page_height, DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT); + set_reg_field_value(value, min_pte_before_flip, DVMM_PTE_CONTROL, DVMM_MIN_PTE_BEFORE_FLIP); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmDVMM_PTE_CONTROL), value); + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmDVMM_PTE_ARB_CONTROL)); + set_reg_field_value(value, pte[5], DVMM_PTE_ARB_CONTROL, DVMM_PTE_REQ_PER_CHUNK); + set_reg_field_value(value, 0xff, DVMM_PTE_ARB_CONTROL, DVMM_MAX_PTE_REQ_OUTSTANDING); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmDVMM_PTE_ARB_CONTROL), value); + + return true; +} + +static void program_urgency_watermark( + const struct dc_context *ctx, + const uint32_t offset, + struct bw_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + /* register value */ + uint32_t urgency_cntl = 0; + uint32_t wm_mask_cntl = 0; + + uint32_t urgency_addr = offset + mmDPG_PIPE_URGENCY_CONTROL; + uint32_t wm_addr = offset + mmDPG_WATERMARK_MASK_CONTROL; + + /*Write mask to enable reading/writing of watermark set A*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPG_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value( + urgency_cntl, + marks_low.d_mark, + DPG_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value( + urgency_cntl, + total_dest_line_time_ns, + DPG_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + dm_write_reg(ctx, urgency_addr, urgency_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPG_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value(urgency_cntl, + marks_low.a_mark, + DPG_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value(urgency_cntl, + total_dest_line_time_ns, + DPG_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + dm_write_reg(ctx, urgency_addr, urgency_cntl); +} + +static void program_stutter_watermark( + const struct dc_context *ctx, + const uint32_t offset, + struct bw_watermarks marks) +{ + /* register value */ + uint32_t stutter_cntl = 0; + uint32_t wm_mask_cntl = 0; + + uint32_t stutter_addr = offset + mmDPG_PIPE_STUTTER_CONTROL; + uint32_t wm_addr = offset + mmDPG_WATERMARK_MASK_CONTROL; + + /*Write mask to enable reading/writing of watermark set A*/ + + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPG_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + + if (ctx->dc->debug.disable_stutter) { + set_reg_field_value(stutter_cntl, + 0, + DPG_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } else { + set_reg_field_value(stutter_cntl, + 1, + DPG_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } + + set_reg_field_value(stutter_cntl, + 1, + DPG_PIPE_STUTTER_CONTROL, + STUTTER_IGNORE_FBC); + + /*Write watermark set A*/ + set_reg_field_value(stutter_cntl, + marks.d_mark, + DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPG_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + + /*Write watermark set B*/ + set_reg_field_value(stutter_cntl, + marks.a_mark, + DPG_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); +} + +static void program_nbp_watermark( + const struct dc_context *ctx, + const uint32_t offset, + struct bw_watermarks marks) +{ + uint32_t value; + uint32_t addr; + /* Write mask to enable reading/writing of watermark set A */ + addr = offset + mmDPG_WATERMARK_MASK_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + 1, + DPG_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, addr, value); + + addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + 1, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, addr, value); + + /* Write watermark set A */ + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + marks.d_mark, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, addr, value); + + /* Write mask to enable reading/writing of watermark set B */ + addr = offset + mmDPG_WATERMARK_MASK_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + 2, + DPG_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, addr, value); + + addr = offset + mmDPG_PIPE_NB_PSTATE_CHANGE_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + 1, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, addr, value); + + /* Write watermark set B */ + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + marks.a_mark, + DPG_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, addr, value); +} + +void dce110_mem_input_program_display_marks( + struct mem_input *mem_input, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + struct dce110_mem_input *bm_dce110 = TO_DCE110_MEM_INPUT(mem_input); + + program_urgency_watermark( + mem_input->ctx, + bm_dce110->offsets.dmif, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark( + mem_input->ctx, + bm_dce110->offsets.dmif, + nbp); + + program_stutter_watermark( + mem_input->ctx, + bm_dce110->offsets.dmif, + stutter); +} + +static struct mem_input_funcs dce110_mem_input_funcs = { + .mem_input_program_display_marks = + dce110_mem_input_program_display_marks, + .allocate_mem_input = dce_mem_input_allocate_dmif, + .free_mem_input = dce_mem_input_free_dmif, + .mem_input_program_surface_flip_and_addr = + dce110_mem_input_program_surface_flip_and_addr, + .mem_input_program_pte_vm = + dce110_mem_input_program_pte_vm, + .mem_input_program_surface_config = + dce_mem_input_program_surface_config, + .mem_input_is_flip_pending = + dce110_mem_input_is_flip_pending, + .mem_input_update_dchub = NULL +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce110_mem_input_construct( + struct dce110_mem_input *mem_input110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets) +{ + /* supported stutter method + * STUTTER_MODE_ENHANCED + * STUTTER_MODE_QUAD_DMIF_BUFFER + * STUTTER_MODE_WATERMARK_NBP_STATE + */ + mem_input110->base.funcs = &dce110_mem_input_funcs; + mem_input110->base.ctx = ctx; + + mem_input110->base.inst = inst; + + mem_input110->offsets = *offsets; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input.h new file mode 100644 index 000000000000..83b2df93ce49 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input.h @@ -0,0 +1,131 @@ +/* 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_MEM_INPUT_DCE110_H__ +#define __DC_MEM_INPUT_DCE110_H__ + +#include "mem_input.h" + +#define TO_DCE110_MEM_INPUT(mi)\ + container_of(mi, struct dce110_mem_input, base) + +struct dce110_mem_input_reg_offsets { + uint32_t dcp; + uint32_t dmif; + uint32_t pipe; +}; + +struct dce110_mem_input { + struct mem_input base; + struct dce110_mem_input_reg_offsets offsets; +}; + +bool dce110_mem_input_construct( + struct dce110_mem_input *mem_input110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offsets); + +/* + * dce110_mem_input_program_display_marks + * + * This function will program nbp stutter and urgency watermarks to minimum + * allowable values + */ +void dce110_mem_input_program_display_marks( + struct mem_input *mem_input, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns); + +/* + * dce110_allocate_mem_input + * + * This function will allocate a dmif buffer and program required + * pixel duration for pipe + */ +void dce110_allocate_mem_input( + struct mem_input *mem_input, + uint32_t h_total,/* for current stream */ + uint32_t v_total,/* for current stream */ + uint32_t pix_clk_khz,/* for current stream */ + uint32_t total_stream_num); + +/* + * dce110_free_mem_input + * + * This function will deallocate a dmif buffer from pipe + */ +void dce110_free_mem_input( + struct mem_input *mem_input, + uint32_t total_stream_num); + +/* + * dce110_mem_input_program_surface_flip_and_addr + * + * This function programs hsync/vsync mode and surface address + */ +bool dce110_mem_input_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate); + +/* + * dce110_mem_input_program_surface_config + * + * This function will program surface tiling, size, rotation and pixel format + * to corresponding dcp registers. + */ +bool dce110_mem_input_program_surface_config( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizontal_mirror); + +/* + * dce110_mem_input_program_pte_vm + * + * This function will program pte vm registers. + */ +bool dce110_mem_input_program_pte_vm( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation); + +/* + * dce110_mem_input_is_flip_pending + * + * This function will wait until the surface update-pending bit is cleared. + * This is necessary when a flip immediate call is requested as we shouldn't + * return until the flip has actually occurred. + */ +bool dce110_mem_input_is_flip_pending( + struct mem_input *mem_input); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c new file mode 100644 index 000000000000..f0310bab4030 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c @@ -0,0 +1,1081 @@ +/* + * Copyright 2012-16 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" +/* TODO: this needs to be looked at, used by Stella's workaround*/ +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" + +#include "include/logger_interface.h" +#include "inc/bandwidth_calcs.h" + +#include "dce110_mem_input.h" + +#define DCP_REG(reg) (reg + mem_input110->offsets.dcp) +/*#define DMIF_REG(reg) (reg + mem_input110->offsets.dmif)*/ +/*#define PIPE_REG(reg) (reg + mem_input110->offsets.pipe)*/ + +static const struct dce110_mem_input_reg_offsets dce110_mi_v_reg_offsets[] = { + { + .dcp = 0, + .dmif = 0, + .pipe = 0, + } +}; + +static void set_flip_control( + struct dce110_mem_input *mem_input110, + bool immediate) +{ + uint32_t value = 0; + + value = dm_read_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_FLIP_CONTROL)); + + set_reg_field_value(value, 1, + UNP_FLIP_CONTROL, + GRPH_SURFACE_UPDATE_PENDING_MODE); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_FLIP_CONTROL), + value); +} + +/* chroma part */ +static void program_pri_addr_c( + struct dce110_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + /*high register MUST be programmed first*/ + temp = address.high_part & +UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_C), + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C__GRPH_PRIMARY_SURFACE_ADDRESS_C__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_C, + GRPH_PRIMARY_SURFACE_ADDRESS_C); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_C), + value); +} + +/* luma part */ +static void program_pri_addr_l( + struct dce110_mem_input *mem_input110, + PHYSICAL_ADDRESS_LOC address) +{ + uint32_t value = 0; + uint32_t temp = 0; + + /*high register MUST be programmed first*/ + temp = address.high_part & +UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L__GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L_MASK; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L, + GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH_L), + value); + + temp = 0; + value = 0; + temp = address.low_part >> + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L__GRPH_PRIMARY_SURFACE_ADDRESS_L__SHIFT; + + set_reg_field_value(value, temp, + UNP_GRPH_PRIMARY_SURFACE_ADDRESS_L, + GRPH_PRIMARY_SURFACE_ADDRESS_L); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_PRIMARY_SURFACE_ADDRESS_L), + value); +} + +static void program_addr( + struct dce110_mem_input *mem_input110, + const struct dc_plane_address *addr) +{ + switch (addr->type) { + case PLN_ADDR_TYPE_GRAPHICS: + program_pri_addr_l( + mem_input110, + addr->grph.addr); + break; + case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: + program_pri_addr_l( + mem_input110, + addr->video_progressive.luma_addr); + program_pri_addr_c( + mem_input110, + addr->video_progressive.chroma_addr); + break; + default: + /* not supported */ + BREAK_TO_DEBUGGER(); + } +} + +static void enable(struct dce110_mem_input *mem_input110) +{ + uint32_t value = 0; + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_GRPH_ENABLE)); + set_reg_field_value(value, 1, UNP_GRPH_ENABLE, GRPH_ENABLE); + dm_write_reg(mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_ENABLE), + value); +} + +static void program_tiling( + struct dce110_mem_input *mem_input110, + const union dc_tiling_info *info, + const enum surface_pixel_format pixel_format) +{ + uint32_t value = 0; + + set_reg_field_value(value, info->gfx8.num_banks, + UNP_GRPH_CONTROL, GRPH_NUM_BANKS); + + set_reg_field_value(value, info->gfx8.bank_width, + UNP_GRPH_CONTROL, GRPH_BANK_WIDTH_L); + + set_reg_field_value(value, info->gfx8.bank_height, + UNP_GRPH_CONTROL, GRPH_BANK_HEIGHT_L); + + set_reg_field_value(value, info->gfx8.tile_aspect, + UNP_GRPH_CONTROL, GRPH_MACRO_TILE_ASPECT_L); + + set_reg_field_value(value, info->gfx8.tile_split, + UNP_GRPH_CONTROL, GRPH_TILE_SPLIT_L); + + set_reg_field_value(value, info->gfx8.tile_mode, + UNP_GRPH_CONTROL, GRPH_MICRO_TILE_MODE_L); + + set_reg_field_value(value, info->gfx8.pipe_config, + UNP_GRPH_CONTROL, GRPH_PIPE_CONFIG); + + set_reg_field_value(value, info->gfx8.array_mode, + UNP_GRPH_CONTROL, GRPH_ARRAY_MODE); + + set_reg_field_value(value, 1, + UNP_GRPH_CONTROL, GRPH_COLOR_EXPANSION_MODE); + + set_reg_field_value(value, 0, + UNP_GRPH_CONTROL, GRPH_Z); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL, + value); + + value = 0; + + set_reg_field_value(value, info->gfx8.bank_width_c, + UNP_GRPH_CONTROL_C, GRPH_BANK_WIDTH_C); + + set_reg_field_value(value, info->gfx8.bank_height_c, + UNP_GRPH_CONTROL_C, GRPH_BANK_HEIGHT_C); + + set_reg_field_value(value, info->gfx8.tile_aspect_c, + UNP_GRPH_CONTROL_C, GRPH_MACRO_TILE_ASPECT_C); + + set_reg_field_value(value, info->gfx8.tile_split_c, + UNP_GRPH_CONTROL_C, GRPH_TILE_SPLIT_C); + + set_reg_field_value(value, info->gfx8.tile_mode_c, + UNP_GRPH_CONTROL_C, GRPH_MICRO_TILE_MODE_C); + + dm_write_reg( + mem_input110->base.ctx, + mmUNP_GRPH_CONTROL_C, + value); +} + +static void program_size_and_rotation( + struct dce110_mem_input *mem_input110, + enum dc_rotation_angle rotation, + const union plane_size *plane_size) +{ + uint32_t value = 0; + union plane_size local_size = *plane_size; + + if (rotation == ROTATION_ANGLE_90 || + rotation == ROTATION_ANGLE_270) { + + uint32_t swap; + swap = local_size.video.luma_size.x; + local_size.video.luma_size.x = + local_size.video.luma_size.y; + local_size.video.luma_size.y = swap; + + swap = local_size.video.luma_size.width; + local_size.video.luma_size.width = + local_size.video.luma_size.height; + local_size.video.luma_size.height = swap; + + swap = local_size.video.chroma_size.x; + local_size.video.chroma_size.x = + local_size.video.chroma_size.y; + local_size.video.chroma_size.y = swap; + + swap = local_size.video.chroma_size.width; + local_size.video.chroma_size.width = + local_size.video.chroma_size.height; + local_size.video.chroma_size.height = swap; + } + + value = 0; + set_reg_field_value(value, local_size.video.luma_pitch, + UNP_GRPH_PITCH_L, GRPH_PITCH_L); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_PITCH_L), + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_pitch, + UNP_GRPH_PITCH_C, GRPH_PITCH_C); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_PITCH_C), + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_L, GRPH_X_START_L); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_X_START_L), + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_X_START_C, GRPH_X_START_C); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_X_START_C), + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_L, GRPH_Y_START_L); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_Y_START_L), + value); + + value = 0; + set_reg_field_value(value, 0, + UNP_GRPH_Y_START_C, GRPH_Y_START_C); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_Y_START_C), + value); + + value = 0; + set_reg_field_value(value, local_size.video.luma_size.x + + local_size.video.luma_size.width, + UNP_GRPH_X_END_L, GRPH_X_END_L); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_X_END_L), + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_size.x + + local_size.video.chroma_size.width, + UNP_GRPH_X_END_C, GRPH_X_END_C); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_X_END_C), + value); + + value = 0; + set_reg_field_value(value, local_size.video.luma_size.y + + local_size.video.luma_size.height, + UNP_GRPH_Y_END_L, GRPH_Y_END_L); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_Y_END_L), + value); + + value = 0; + set_reg_field_value(value, local_size.video.chroma_size.y + + local_size.video.chroma_size.height, + UNP_GRPH_Y_END_C, GRPH_Y_END_C); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_Y_END_C), + value); + + value = 0; + switch (rotation) { + case ROTATION_ANGLE_90: + set_reg_field_value(value, 3, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case ROTATION_ANGLE_180: + set_reg_field_value(value, 2, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + case ROTATION_ANGLE_270: + set_reg_field_value(value, 1, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + default: + set_reg_field_value(value, 0, + UNP_HW_ROTATION, ROTATION_ANGLE); + break; + } + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_HW_ROTATION), + value); +} + +static void program_pixel_format( + struct dce110_mem_input *mem_input110, + enum surface_pixel_format format) +{ + if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) { + uint32_t value; + uint8_t grph_depth; + uint8_t grph_format; + + value = dm_read_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_CONTROL)); + + switch (format) { + case SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS: + grph_depth = 0; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_RGB565: + grph_depth = 1; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB8888: + case SURFACE_PIXEL_FORMAT_GRPH_BGRA8888: + grph_depth = 2; + grph_format = 0; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010_XR_BIAS: + grph_depth = 2; + grph_format = 1; + break; + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616: + case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F: + case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F: + grph_depth = 3; + grph_format = 0; + break; + default: + grph_depth = 2; + grph_format = 0; + break; + } + + set_reg_field_value( + value, + grph_depth, + UNP_GRPH_CONTROL, + GRPH_DEPTH); + set_reg_field_value( + value, + grph_format, + UNP_GRPH_CONTROL, + GRPH_FORMAT); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_CONTROL), + value); + + value = dm_read_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_CONTROL_EXP)); + + /* VIDEO FORMAT 0 */ + set_reg_field_value( + value, + 0, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_CONTROL_EXP), + value); + + } else { + /* Video 422 and 420 needs UNP_GRPH_CONTROL_EXP programmed */ + uint32_t value; + uint8_t video_format; + + value = dm_read_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_CONTROL_EXP)); + + switch (format) { + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr: + video_format = 2; + break; + case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb: + video_format = 3; + break; + default: + video_format = 0; + break; + } + + set_reg_field_value( + value, + video_format, + UNP_GRPH_CONTROL_EXP, + VIDEO_FORMAT); + + dm_write_reg( + mem_input110->base.ctx, + DCP_REG(mmUNP_GRPH_CONTROL_EXP), + value); + } +} + +bool dce110_mem_input_v_is_surface_pending(struct mem_input *mem_input) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + uint32_t value; + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_GRPH_UPDATE)); + + if (get_reg_field_value(value, UNP_GRPH_UPDATE, + GRPH_SURFACE_UPDATE_PENDING)) + return true; + + mem_input->current_address = mem_input->request_address; + return false; +} + +bool dce110_mem_input_v_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + + set_flip_control(mem_input110, flip_immediate); + program_addr(mem_input110, + address); + + mem_input->request_address = *address; + + return true; +} + +/* Scatter Gather param tables */ +static const unsigned int dvmm_Hw_Setting_2DTiling[4][9] = { + { 8, 64, 64, 8, 8, 1, 4, 0, 0}, + { 16, 64, 32, 8, 16, 1, 8, 0, 0}, + { 32, 32, 32, 16, 16, 1, 8, 0, 0}, + { 64, 8, 32, 16, 16, 1, 8, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_1DTiling[4][9] = { + { 8, 512, 8, 1, 0, 1, 0, 0, 0}, /* 0 for invalid */ + { 16, 256, 8, 2, 0, 1, 0, 0, 0}, + { 32, 128, 8, 4, 0, 1, 0, 0, 0}, + { 64, 64, 8, 4, 0, 1, 0, 0, 0}, /* fake */ +}; + +static const unsigned int dvmm_Hw_Setting_Linear[4][9] = { + { 8, 4096, 1, 8, 0, 1, 0, 0, 0}, + { 16, 2048, 1, 8, 0, 1, 0, 0, 0}, + { 32, 1024, 1, 8, 0, 1, 0, 0, 0}, + { 64, 512, 1, 8, 0, 1, 0, 0, 0}, /* new for 64bpp from HW */ +}; + +/* Helper to get table entry from surface info */ +static const unsigned int *get_dvmm_hw_setting( + union dc_tiling_info *tiling_info, + enum surface_pixel_format format, + bool chroma) +{ + enum bits_per_pixel { + bpp_8 = 0, + bpp_16, + bpp_32, + bpp_64 + } bpp; + + if (format >= SURFACE_PIXEL_FORMAT_INVALID) + bpp = bpp_32; + else if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + bpp = chroma ? bpp_16 : bpp_8; + else + bpp = bpp_8; + + switch (tiling_info->gfx8.array_mode) { + case DC_ARRAY_1D_TILED_THIN1: + case DC_ARRAY_1D_TILED_THICK: + case DC_ARRAY_PRT_TILED_THIN1: + return dvmm_Hw_Setting_1DTiling[bpp]; + case DC_ARRAY_2D_TILED_THIN1: + case DC_ARRAY_2D_TILED_THICK: + case DC_ARRAY_2D_TILED_X_THICK: + case DC_ARRAY_PRT_2D_TILED_THIN1: + case DC_ARRAY_PRT_2D_TILED_THICK: + return dvmm_Hw_Setting_2DTiling[bpp]; + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + return dvmm_Hw_Setting_Linear[bpp]; + default: + return dvmm_Hw_Setting_2DTiling[bpp]; + } +} + +bool dce110_mem_input_v_program_pte_vm( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + const unsigned int *pte = get_dvmm_hw_setting(tiling_info, format, false); + const unsigned int *pte_chroma = get_dvmm_hw_setting(tiling_info, format, true); + + unsigned int page_width = 0; + unsigned int page_height = 0; + unsigned int page_width_chroma = 0; + unsigned int page_height_chroma = 0; + unsigned int temp_page_width = pte[1]; + unsigned int temp_page_height = pte[2]; + unsigned int min_pte_before_flip = 0; + unsigned int min_pte_before_flip_chroma = 0; + uint32_t value = 0; + + while ((temp_page_width >>= 1) != 0) + page_width++; + while ((temp_page_height >>= 1) != 0) + page_height++; + + temp_page_width = pte_chroma[1]; + temp_page_height = pte_chroma[2]; + while ((temp_page_width >>= 1) != 0) + page_width_chroma++; + while ((temp_page_height >>= 1) != 0) + page_height_chroma++; + + switch (rotation) { + case ROTATION_ANGLE_90: + case ROTATION_ANGLE_270: + min_pte_before_flip = pte[4]; + min_pte_before_flip_chroma = pte_chroma[4]; + break; + default: + min_pte_before_flip = pte[3]; + min_pte_before_flip_chroma = pte_chroma[3]; + break; + } + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_PIPE_OUTSTANDING_REQUEST_LIMIT)); + /* TODO: un-hardcode requestlimit */ + set_reg_field_value(value, 0xff, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT_L); + set_reg_field_value(value, 0xff, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT, UNP_PIPE_OUTSTANDING_REQUEST_LIMIT_C); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmUNP_PIPE_OUTSTANDING_REQUEST_LIMIT), value); + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_CONTROL)); + set_reg_field_value(value, page_width, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_WIDTH); + set_reg_field_value(value, page_height, UNP_DVMM_PTE_CONTROL, DVMM_PAGE_HEIGHT); + set_reg_field_value(value, min_pte_before_flip, UNP_DVMM_PTE_CONTROL, DVMM_MIN_PTE_BEFORE_FLIP); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_CONTROL), value); + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_ARB_CONTROL)); + set_reg_field_value(value, pte[5], UNP_DVMM_PTE_ARB_CONTROL, DVMM_PTE_REQ_PER_CHUNK); + set_reg_field_value(value, 0xff, UNP_DVMM_PTE_ARB_CONTROL, DVMM_MAX_PTE_REQ_OUTSTANDING); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_ARB_CONTROL), value); + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_CONTROL_C)); + set_reg_field_value(value, page_width_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_PAGE_WIDTH_C); + set_reg_field_value(value, page_height_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_PAGE_HEIGHT_C); + set_reg_field_value(value, min_pte_before_flip_chroma, UNP_DVMM_PTE_CONTROL_C, DVMM_MIN_PTE_BEFORE_FLIP_C); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_CONTROL_C), value); + + value = dm_read_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_ARB_CONTROL_C)); + set_reg_field_value(value, pte_chroma[5], UNP_DVMM_PTE_ARB_CONTROL_C, DVMM_PTE_REQ_PER_CHUNK_C); + set_reg_field_value(value, 0xff, UNP_DVMM_PTE_ARB_CONTROL_C, DVMM_MAX_PTE_REQ_OUTSTANDING_C); + dm_write_reg(mem_input110->base.ctx, DCP_REG(mmUNP_DVMM_PTE_ARB_CONTROL_C), value); + + return true; +} + +bool dce110_mem_input_v_program_surface_config( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation, + struct dc_plane_dcc_param *dcc, + bool horizotal_mirror) +{ + struct dce110_mem_input *mem_input110 = TO_DCE110_MEM_INPUT(mem_input); + + enable(mem_input110); + program_tiling(mem_input110, tiling_info, format); + program_size_and_rotation(mem_input110, rotation, plane_size); + program_pixel_format(mem_input110, format); + + return true; +} + +static void program_urgency_watermark( + const struct dc_context *ctx, + const uint32_t urgency_addr, + const uint32_t wm_addr, + struct bw_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + /* register value */ + uint32_t urgency_cntl = 0; + uint32_t wm_mask_cntl = 0; + + /*Write mask to enable reading/writing of watermark set A*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value( + urgency_cntl, + marks_low.a_mark, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value( + urgency_cntl, + total_dest_line_time_ns, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + dm_write_reg(ctx, urgency_addr, urgency_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + URGENCY_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + urgency_cntl = dm_read_reg(ctx, urgency_addr); + + set_reg_field_value(urgency_cntl, + marks_low.b_mark, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_LOW_WATERMARK); + + set_reg_field_value(urgency_cntl, + total_dest_line_time_ns, + DPGV0_PIPE_URGENCY_CONTROL, + URGENCY_HIGH_WATERMARK); + + dm_write_reg(ctx, urgency_addr, urgency_cntl); +} + +static void program_urgency_watermark_l( + const struct dc_context *ctx, + struct bw_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark( + ctx, + mmDPGV0_PIPE_URGENCY_CONTROL, + mmDPGV0_WATERMARK_MASK_CONTROL, + marks_low, + total_dest_line_time_ns); +} + +static void program_urgency_watermark_c( + const struct dc_context *ctx, + struct bw_watermarks marks_low, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark( + ctx, + mmDPGV1_PIPE_URGENCY_CONTROL, + mmDPGV1_WATERMARK_MASK_CONTROL, + marks_low, + total_dest_line_time_ns); +} + +static void program_stutter_watermark( + const struct dc_context *ctx, + const uint32_t stutter_addr, + const uint32_t wm_addr, + struct bw_watermarks marks) +{ + /* register value */ + uint32_t stutter_cntl = 0; + uint32_t wm_mask_cntl = 0; + + /*Write mask to enable reading/writing of watermark set A*/ + + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + + if (ctx->dc->debug.disable_stutter) { + set_reg_field_value(stutter_cntl, + 0, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } else { + set_reg_field_value(stutter_cntl, + 1, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_ENABLE); + } + + set_reg_field_value(stutter_cntl, + 1, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_IGNORE_FBC); + + /*Write watermark set A*/ + set_reg_field_value(stutter_cntl, + marks.a_mark, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); + + /*Write mask to enable reading/writing of watermark set B*/ + wm_mask_cntl = dm_read_reg(ctx, wm_addr); + set_reg_field_value(wm_mask_cntl, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK_MASK); + dm_write_reg(ctx, wm_addr, wm_mask_cntl); + + stutter_cntl = dm_read_reg(ctx, stutter_addr); + /*Write watermark set B*/ + set_reg_field_value(stutter_cntl, + marks.b_mark, + DPGV0_PIPE_STUTTER_CONTROL, + STUTTER_EXIT_SELF_REFRESH_WATERMARK); + dm_write_reg(ctx, stutter_addr, stutter_cntl); +} + +static void program_stutter_watermark_l( + const struct dc_context *ctx, + struct bw_watermarks marks) +{ + program_stutter_watermark(ctx, + mmDPGV0_PIPE_STUTTER_CONTROL, + mmDPGV0_WATERMARK_MASK_CONTROL, + marks); +} + +static void program_stutter_watermark_c( + const struct dc_context *ctx, + struct bw_watermarks marks) +{ + program_stutter_watermark(ctx, + mmDPGV1_PIPE_STUTTER_CONTROL, + mmDPGV1_WATERMARK_MASK_CONTROL, + marks); +} + +static void program_nbp_watermark( + const struct dc_context *ctx, + const uint32_t wm_mask_ctrl_addr, + const uint32_t nbp_pstate_ctrl_addr, + struct bw_watermarks marks) +{ + uint32_t value; + + /* Write mask to enable reading/writing of watermark set A */ + + value = dm_read_reg(ctx, wm_mask_ctrl_addr); + + set_reg_field_value( + value, + 1, + DPGV0_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, wm_mask_ctrl_addr, value); + + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write watermark set A */ + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + marks.a_mark, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write mask to enable reading/writing of watermark set B */ + value = dm_read_reg(ctx, wm_mask_ctrl_addr); + set_reg_field_value( + value, + 2, + DPGV0_WATERMARK_MASK_CONTROL, + NB_PSTATE_CHANGE_WATERMARK_MASK); + dm_write_reg(ctx, wm_mask_ctrl_addr, value); + + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_ENABLE); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_URGENT_DURING_REQUEST); + set_reg_field_value( + value, + 1, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_NOT_SELF_REFRESH_DURING_REQUEST); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); + + /* Write watermark set B */ + value = dm_read_reg(ctx, nbp_pstate_ctrl_addr); + set_reg_field_value( + value, + marks.b_mark, + DPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + NB_PSTATE_CHANGE_WATERMARK); + dm_write_reg(ctx, nbp_pstate_ctrl_addr, value); +} + +static void program_nbp_watermark_l( + const struct dc_context *ctx, + struct bw_watermarks marks) +{ + program_nbp_watermark(ctx, + mmDPGV0_WATERMARK_MASK_CONTROL, + mmDPGV0_PIPE_NB_PSTATE_CHANGE_CONTROL, + marks); +} + +static void program_nbp_watermark_c( + const struct dc_context *ctx, + struct bw_watermarks marks) +{ + program_nbp_watermark(ctx, + mmDPGV1_WATERMARK_MASK_CONTROL, + mmDPGV1_PIPE_NB_PSTATE_CHANGE_CONTROL, + marks); +} + +void dce110_mem_input_v_program_display_marks( + struct mem_input *mem_input, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark_l( + mem_input->ctx, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark_l( + mem_input->ctx, + nbp); + + program_stutter_watermark_l( + mem_input->ctx, + stutter); + +} + +void dce110_mem_input_program_chroma_display_marks( + struct mem_input *mem_input, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns) +{ + program_urgency_watermark_c( + mem_input->ctx, + urgent, + total_dest_line_time_ns); + + program_nbp_watermark_c( + mem_input->ctx, + nbp); + + program_stutter_watermark_c( + mem_input->ctx, + stutter); +} + +void dce110_allocate_mem_input_v( + struct mem_input *mi, + uint32_t h_total,/* for current stream */ + uint32_t v_total,/* for current stream */ + uint32_t pix_clk_khz,/* for current stream */ + uint32_t total_stream_num) +{ + uint32_t addr; + uint32_t value; + uint32_t pix_dur; + if (pix_clk_khz != 0) { + addr = mmDPGV0_PIPE_ARBITRATION_CONTROL1; + value = dm_read_reg(mi->ctx, addr); + pix_dur = 1000000000ULL / pix_clk_khz; + set_reg_field_value( + value, + pix_dur, + DPGV0_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV1_PIPE_ARBITRATION_CONTROL1; + value = dm_read_reg(mi->ctx, addr); + pix_dur = 1000000000ULL / pix_clk_khz; + set_reg_field_value( + value, + pix_dur, + DPGV1_PIPE_ARBITRATION_CONTROL1, + PIXEL_DURATION); + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV0_PIPE_ARBITRATION_CONTROL2; + value = 0x4000800; + dm_write_reg(mi->ctx, addr, value); + + addr = mmDPGV1_PIPE_ARBITRATION_CONTROL2; + value = 0x4000800; + dm_write_reg(mi->ctx, addr, value); + } + +} + +void dce110_free_mem_input_v( + struct mem_input *mi, + uint32_t total_stream_num) +{ +} + +static struct mem_input_funcs dce110_mem_input_v_funcs = { + .mem_input_program_display_marks = + dce110_mem_input_v_program_display_marks, + .mem_input_program_chroma_display_marks = + dce110_mem_input_program_chroma_display_marks, + .allocate_mem_input = dce110_allocate_mem_input_v, + .free_mem_input = dce110_free_mem_input_v, + .mem_input_program_surface_flip_and_addr = + dce110_mem_input_v_program_surface_flip_and_addr, + .mem_input_program_pte_vm = + dce110_mem_input_v_program_pte_vm, + .mem_input_program_surface_config = + dce110_mem_input_v_program_surface_config, + .mem_input_is_flip_pending = + dce110_mem_input_v_is_surface_pending +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce110_mem_input_v_construct( + struct dce110_mem_input *mem_input110, + struct dc_context *ctx) +{ + mem_input110->base.funcs = &dce110_mem_input_v_funcs; + mem_input110->base.ctx = ctx; + + mem_input110->base.inst = 0; + + mem_input110->offsets = dce110_mi_v_reg_offsets[0]; + + return true; +} + +#if 0 +void dce110_mem_input_v_destroy(struct mem_input **mem_input) +{ + dm_free(TO_DCE110_MEM_INPUT(*mem_input)); + *mem_input = NULL; +} +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h new file mode 100644 index 000000000000..5b1796ccefc0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.h @@ -0,0 +1,94 @@ +/* Copyright 2012-16 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_MEM_INPUT_V_DCE110_H__ +#define __DC_MEM_INPUT_V_DCE110_H__ + +#include "mem_input.h" +#include "dce110_mem_input.h" + +bool dce110_mem_input_v_construct( + struct dce110_mem_input *mem_input110, + struct dc_context *ctx); + +/* + * This function will program nbp stutter and urgency watermarks to minimum + * allowable values + */ +void dce110_mem_input_v_program_display_marks( + struct mem_input *mem_input, + struct bw_watermarks nbp, + struct bw_watermarks stutter, + struct bw_watermarks urgent, + uint32_t total_dest_line_time_ns); + +/* + * This function will allocate a dmif buffer and program required + * pixel duration for pipe + */ +void dce110_allocate_mem_v_input( + struct mem_input *mem_input, + uint32_t h_total,/* for current stream */ + uint32_t v_total,/* for current stream */ + uint32_t pix_clk_khz,/* for current stream */ + uint32_t total_stream_num); + +/* + * This function will deallocate a dmif buffer from pipe + */ +void dce110_free_mem_v_input( + struct mem_input *mem_input, + uint32_t total_stream_num); + +/* + * This function programs hsync/vsync mode and surface address + */ +bool dce110_mem_input_v_program_surface_flip_and_addr( + struct mem_input *mem_input, + const struct dc_plane_address *address, + bool flip_immediate); + +/* + * dce110_mem_input_v_program_scatter_gather + * + * This function will program scatter gather registers. + */ +bool dce110_mem_input_v_program_pte_vm( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + enum dc_rotation_angle rotation); + +/* + * This function will program surface tiling, size, rotation and pixel format + * to corresponding dcp registers. + */ +bool dce110_mem_input_v_program_surface_config( + struct mem_input *mem_input, + enum surface_pixel_format format, + union dc_tiling_info *tiling_info, + union plane_size *plane_size, + enum dc_rotation_angle rotation); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp.c new file mode 100644 index 000000000000..698ec2f55d0f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp.c @@ -0,0 +1,77 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_opp.h" + +#include "gamma_types.h" + +enum { + MAX_LUT_ENTRY = 256, + MAX_NUMBER_OF_ENTRIES = 256 +}; + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static const struct opp_funcs funcs = { + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut, + .opp_set_csc_adjustment = dce110_opp_set_csc_adjustment, + .opp_set_csc_default = dce110_opp_set_csc_default, + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce110_opp_program_fmt, +}; + +bool dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_opp_reg_offsets *offsets) +{ + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; + + opp110->base.inst = inst; + + opp110->offsets = *offsets; + + return true; +} + +void dce110_opp_destroy(struct output_pixel_processor **opp) +{ + dm_free(FROM_DCE11_OPP(*opp)); + *opp = NULL; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp.h new file mode 100644 index 000000000000..2fbb2415cc97 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp.h @@ -0,0 +1,149 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCE110_H__ +#define __DC_OPP_DCE110_H__ + +#include "dc_types.h" +#include "opp.h" +#include "core_types.h" + +#include "gamma_types.h" /* decprecated */ + +struct gamma_parameters; + +#define FROM_DCE11_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +enum dce110_opp_reg_type { + DCE110_OPP_REG_DCP = 0, + DCE110_OPP_REG_DCFE, + DCE110_OPP_REG_FMT, + + DCE110_OPP_REG_MAX +}; + +struct dce110_regamma { + struct gamma_curve arr_curve_points[16]; + struct curve_points arr_points[3]; + uint32_t hw_points_num; + struct hw_x_point *coordinates_x; + struct pwl_result_data *rgb_resulted; + + /* re-gamma curve */ + struct pwl_float_data_ex *rgb_regamma; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 256 */ + struct pixel_gamma_point *coeff128; + struct pixel_gamma_point *coeff128_oem; + /* coeff used to map user evenly distributed points + * to our hardware points (predefined) for gamma 1025 */ + struct pixel_gamma_point *coeff128_dx; + /* evenly distributed points, gamma 256 software points 0-255 */ + struct gamma_pixel *axis_x_256; + /* evenly distributed points, gamma 1025 software points 0-1025 */ + struct gamma_pixel *axis_x_1025; + /* OEM supplied gamma for regamma LUT */ + struct pwl_float_data *rgb_oem; + /* user supplied gamma */ + struct pwl_float_data *rgb_user; + uint32_t extra_points; + bool use_half_points; + struct fixed31_32 x_max1; + struct fixed31_32 x_max2; + struct fixed31_32 x_min; + struct fixed31_32 divider1; + struct fixed31_32 divider2; + struct fixed31_32 divider3; +}; + +/* OPP RELATED */ +#define TO_DCE110_OPP(opp)\ + container_of(opp, struct dce110_opp, base) + +struct dce110_opp_reg_offsets { + uint32_t fmt_offset; + uint32_t fmt_mem_offset; + uint32_t dcp_offset; + uint32_t dcfe_offset; +}; + +struct dce110_opp { + struct output_pixel_processor base; + struct dce110_opp_reg_offsets offsets; + struct dce110_regamma regamma; +}; + +bool dce110_opp_construct(struct dce110_opp *opp110, + struct dc_context *ctx, + uint32_t inst, + const struct dce110_opp_reg_offsets *offsets); + +void dce110_opp_destroy(struct output_pixel_processor **opp); + +/* REGAMMA RELATED */ +void dce110_opp_power_on_regamma_lut( + struct output_pixel_processor *opp, + bool power_on); + +bool dce110_opp_program_regamma_pwl( + struct output_pixel_processor *opp, + const struct pwl_params *params); + +void dce110_opp_set_regamma_mode(struct output_pixel_processor *opp, + enum opp_regamma mode); + +void dce110_opp_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct out_csc_color_matrix *tbl_entry); + +void dce110_opp_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust); + +/* FORMATTER RELATED */ +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params); + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params); + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal); + +void dce110_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping); + +void dce110_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc.c new file mode 100644 index 000000000000..b46db202ddbb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc.c @@ -0,0 +1,363 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dce110_opp.h" +#include "basics/conversion.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define DCP_REG(reg)\ + (reg + opp110->offsets.dcp_offset) + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* TODO: correct values below */ +{ COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +static void program_color_matrix( + struct dce110_opp *opp110, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + struct dc_context *ctx = opp110->base.ctx; + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C11_C12); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C11); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12, + OUTPUT_CSC_C12); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C13_C14); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C13); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14, + OUTPUT_CSC_C14); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C21_C22); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C21); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22, + OUTPUT_CSC_C22); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C23_C24); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C23); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24, + OUTPUT_CSC_C24); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C31_C32); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C31); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32, + OUTPUT_CSC_C32); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_C33_C34); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C33); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34, + OUTPUT_CSC_C34); + + dm_write_reg(ctx, addr, value); + } +} + +static bool configure_graphics_mode( + struct dce110_opp *opp110, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum dc_color_space color_space) +{ + struct dc_context *ctx = opp110->base.ctx; + uint32_t addr = DCP_REG(mmOUTPUT_CSC_CONTROL); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) { + set_reg_field_value( + value, + 4, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + } else { + + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* TV RGB */ + set_reg_field_value( + value, + 1, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + default: + return false; + } + } + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* TV RGB */ + set_reg_field_value( + value, + 1, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + break; + default: + return false; + } + + } else + /* by pass */ + set_reg_field_value( + value, + 0, + OUTPUT_CSC_CONTROL, + OUTPUT_CSC_GRPH_MODE); + + addr = DCP_REG(mmOUTPUT_CSC_CONTROL); + dm_write_reg(ctx, addr, value); + + return true; +} + +void dce110_opp_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + program_color_matrix( + opp110, tbl_entry, GRAPHICS_CSC_ADJUST_TYPE_SW); + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode(opp110, config, GRAPHICS_CSC_ADJUST_TYPE_SW, + tbl_entry->color_space); +} + +void dce110_opp_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->out_color_space) + continue; + /* program the matrix with default values from this + * file */ + program_color_matrix(opp110, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix */ + + configure_graphics_mode(opp110, config, + default_adjust->csc_adjust_type, + default_adjust->out_color_space); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c new file mode 100644 index 000000000000..975466f6e424 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -0,0 +1,738 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" +#include "dce110_opp.h" +#include "basics/conversion.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" +#include "dce/dce_11_0_enum.h" + +enum { + OUTPUT_CSC_MATRIX_SIZE = 12 +}; + +/* constrast:0 - 2.0, default 1.0 */ +#define UNDERLAY_CONTRAST_DEFAULT 100 +#define UNDERLAY_CONTRAST_MAX 200 +#define UNDERLAY_CONTRAST_MIN 0 +#define UNDERLAY_CONTRAST_STEP 1 +#define UNDERLAY_CONTRAST_DIVIDER 100 + +/* Saturation: 0 - 2.0; default 1.0 */ +#define UNDERLAY_SATURATION_DEFAULT 100 /*1.00*/ +#define UNDERLAY_SATURATION_MIN 0 +#define UNDERLAY_SATURATION_MAX 200 /* 2.00 */ +#define UNDERLAY_SATURATION_STEP 1 /* 0.01 */ +/*actual max overlay saturation + * value = UNDERLAY_SATURATION_MAX /UNDERLAY_SATURATION_DIVIDER + */ + +/* Hue */ +#define UNDERLAY_HUE_DEFAULT 0 +#define UNDERLAY_HUE_MIN -300 +#define UNDERLAY_HUE_MAX 300 +#define UNDERLAY_HUE_STEP 5 +#define UNDERLAY_HUE_DIVIDER 10 /* HW range: -30 ~ +30 */ +#define UNDERLAY_SATURATION_DIVIDER 100 + +/* Brightness: in DAL usually -.25 ~ .25. + * In MMD is -100 to +100 in 16-235 range; which when scaled to full range is + * ~-116 to +116. When normalized this is about 0.4566. + * With 100 divider this becomes 46, but we may use another for better precision + * The ideal one is 100/219 ((100/255)*(255/219)), + * i.e. min/max = +-100, divider = 219 + * default 0.0 + */ +#define UNDERLAY_BRIGHTNESS_DEFAULT 0 +#define UNDERLAY_BRIGHTNESS_MIN -46 /* ~116/255 */ +#define UNDERLAY_BRIGHTNESS_MAX 46 +#define UNDERLAY_BRIGHTNESS_STEP 1 /* .01 */ +#define UNDERLAY_BRIGHTNESS_DIVIDER 100 + +static const struct out_csc_color_matrix global_color_matrix[] = { +{ COLOR_SPACE_SRGB, + { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, +{ COLOR_SPACE_SRGB_LIMITED, + { 0x1B60, 0, 0, 0x200, 0, 0x1B60, 0, 0x200, 0, 0, 0x1B60, 0x200} }, +{ COLOR_SPACE_YCBCR601, + { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x82F, 0x1012, 0x31F, 0x200, 0xFB47, + 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x5D2, 0x1394, 0x1FA, + 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, +/* TODO: correct values below */ +{ COLOR_SPACE_YCBCR601_LIMITED, { 0xE00, 0xF447, 0xFDB9, 0x1000, 0x991, + 0x12C9, 0x3A6, 0x200, 0xFB47, 0xF6B9, 0xE00, 0x1000} }, +{ COLOR_SPACE_YCBCR709_LIMITED, { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, + 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} } +}; + +enum csc_color_mode { + /* 00 - BITS2:0 Bypass */ + CSC_COLOR_MODE_GRAPHICS_BYPASS, + /* 01 - hard coded coefficient TV RGB */ + CSC_COLOR_MODE_GRAPHICS_PREDEFINED, + /* 04 - programmable OUTPUT CSC coefficient */ + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC, +}; + +static void program_color_matrix_v( + struct dce110_opp *opp110, + const struct out_csc_color_matrix *tbl_entry, + enum grph_color_adjust_option options) +{ + struct dc_context *ctx = opp110->base.ctx; + uint32_t cntl_value = dm_read_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL); + bool use_set_a = (get_reg_field_value(cntl_value, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE) != 4); + + set_reg_field_value( + cntl_value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (use_set_a) { + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C11_C12_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12_A, + OUTPUT_CSC_C11_A); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12_A, + OUTPUT_CSC_C12_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C13_C14_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14_A, + OUTPUT_CSC_C13_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14_A, + OUTPUT_CSC_C14_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C21_C22_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22_A, + OUTPUT_CSC_C21_A); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22_A, + OUTPUT_CSC_C22_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C23_C24_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24_A, + OUTPUT_CSC_C23_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24_A, + OUTPUT_CSC_C24_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C31_C32_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32_A, + OUTPUT_CSC_C31_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32_A, + OUTPUT_CSC_C32_A); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C33_C34_A; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34_A, + OUTPUT_CSC_C33_A); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34_A, + OUTPUT_CSC_C34_A); + + dm_write_reg(ctx, addr, value); + } + set_reg_field_value( + cntl_value, + 4, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + } else { + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C11_C12_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[0], + OUTPUT_CSC_C11_C12_B, + OUTPUT_CSC_C11_B); + + set_reg_field_value( + value, + tbl_entry->regval[1], + OUTPUT_CSC_C11_C12_B, + OUTPUT_CSC_C12_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C13_C14_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[2], + OUTPUT_CSC_C13_C14_B, + OUTPUT_CSC_C13_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[3], + OUTPUT_CSC_C13_C14_B, + OUTPUT_CSC_C14_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C21_C22_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[4], + OUTPUT_CSC_C21_C22_B, + OUTPUT_CSC_C21_B); + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[5], + OUTPUT_CSC_C21_C22_B, + OUTPUT_CSC_C22_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C23_C24_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[6], + OUTPUT_CSC_C23_C24_B, + OUTPUT_CSC_C23_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[7], + OUTPUT_CSC_C23_C24_B, + OUTPUT_CSC_C24_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C31_C32_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[8], + OUTPUT_CSC_C31_C32_B, + OUTPUT_CSC_C31_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[9], + OUTPUT_CSC_C31_C32_B, + OUTPUT_CSC_C32_B); + + dm_write_reg(ctx, addr, value); + } + { + uint32_t value = 0; + uint32_t addr = mmOUTPUT_CSC_C33_C34_B; + /* fixed S2.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[10], + OUTPUT_CSC_C33_C34_B, + OUTPUT_CSC_C33_B); + /* fixed S0.13 format */ + set_reg_field_value( + value, + tbl_entry->regval[11], + OUTPUT_CSC_C33_C34_B, + OUTPUT_CSC_C34_B); + + dm_write_reg(ctx, addr, value); + } + set_reg_field_value( + cntl_value, + 5, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + } + + dm_write_reg(ctx, mmCOL_MAN_OUTPUT_CSC_CONTROL, cntl_value); +} + +static bool configure_graphics_mode_v( + struct dce110_opp *opp110, + enum csc_color_mode config, + enum graphics_csc_adjust_type csc_adjust_type, + enum dc_color_space color_space) +{ + struct dc_context *ctx = opp110->base.ctx; + uint32_t addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_SW) { + if (config == CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC) + return true; + + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* not supported for underlay on CZ */ + return false; + + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + + } else if (csc_adjust_type == GRAPHICS_CSC_ADJUST_TYPE_HW) { + switch (color_space) { + case COLOR_SPACE_SRGB: + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_SRGB_LIMITED: + /* not supported for underlay on CZ */ + return false; + case COLOR_SPACE_YCBCR601: + case COLOR_SPACE_YPBPR601: + case COLOR_SPACE_YCBCR601_LIMITED: + /* YCbCr601 */ + set_reg_field_value( + value, + 2, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + case COLOR_SPACE_YCBCR709: + case COLOR_SPACE_YPBPR709: + case COLOR_SPACE_YCBCR709_LIMITED: + /* YCbCr709 */ + set_reg_field_value( + value, + 3, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + break; + default: + return false; + } + + } else + /* by pass */ + set_reg_field_value( + value, + 0, + COL_MAN_OUTPUT_CSC_CONTROL, + OUTPUT_CSC_MODE); + + addr = mmCOL_MAN_OUTPUT_CSC_CONTROL; + dm_write_reg(ctx, addr, value); + + return true; +} + +/*TODO: color depth is not correct when this is called*/ +static void set_Denormalization(struct output_pixel_processor *opp, + enum dc_color_depth color_depth) +{ + uint32_t value = dm_read_reg(opp->ctx, mmDENORM_CLAMP_CONTROL); + + switch (color_depth) { + case COLOR_DEPTH_888: + /* 255/256 for 8 bit output color depth */ + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_101010: + /* 1023/1024 for 10 bit output color depth */ + set_reg_field_value( + value, + 2, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + case COLOR_DEPTH_121212: + /* 4095/4096 for 12 bit output color depth */ + set_reg_field_value( + value, + 3, + DENORM_CLAMP_CONTROL, + DENORM_MODE); + break; + default: + /* not valid case */ + break; + } + + set_reg_field_value( + value, + 1, + DENORM_CLAMP_CONTROL, + DENORM_10BIT_OUT); + + dm_write_reg(opp->ctx, mmDENORM_CLAMP_CONTROL, value); +} + +struct input_csc_matrix { + enum dc_color_space color_space; + uint32_t regval[12]; +}; + +static const struct input_csc_matrix input_csc_matrix[] = { + {COLOR_SPACE_SRGB, +/*1_1 1_2 1_3 1_4 2_1 2_2 2_3 2_4 3_1 3_2 3_3 3_4 */ + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_SRGB_LIMITED, + {0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, + {COLOR_SPACE_YCBCR601, + {0x2cdd, 0x2000, 0x0, 0xe991, 0xe926, 0x2000, 0xf4fd, 0x10ef, + 0x0, 0x2000, 0x38b4, 0xe3a6} }, + {COLOR_SPACE_YCBCR601_LIMITED, + {0x3353, 0x2568, 0x0, 0xe400, 0xe5dc, 0x2568, 0xf367, 0x1108, + 0x0, 0x2568, 0x40de, 0xdd3a} }, + {COLOR_SPACE_YCBCR709, + {0x3265, 0x2000, 0, 0xe6ce, 0xf105, 0x2000, 0xfa01, 0xa7d, 0, + 0x2000, 0x3b61, 0xe24f} }, + {COLOR_SPACE_YCBCR709_LIMITED, + {0x39a6, 0x2568, 0, 0xe0d6, 0xeedd, 0x2568, 0xf925, 0x9a8, 0, + 0x2568, 0x43ee, 0xdbb2} } +}; + +static void program_input_csc( + struct output_pixel_processor *opp, enum dc_color_space color_space) +{ + int arr_size = sizeof(input_csc_matrix)/sizeof(struct input_csc_matrix); + struct dc_context *ctx = opp->ctx; + const uint32_t *regval = NULL; + bool use_set_a; + uint32_t value; + int i; + + for (i = 0; i < arr_size; i++) + if (input_csc_matrix[i].color_space == color_space) { + regval = input_csc_matrix[i].regval; + break; + } + if (regval == NULL) { + BREAK_TO_DEBUGGER(); + return; + } + + /* + * 1 == set A, the logic is 'if currently we're not using set A, + * then use set A, otherwise use set B' + */ + value = dm_read_reg(ctx, mmCOL_MAN_INPUT_CSC_CONTROL); + use_set_a = get_reg_field_value( + value, COL_MAN_INPUT_CSC_CONTROL, INPUT_CSC_MODE) != 1; + + if (use_set_a) { + /* fixed S2.13 format */ + value = 0; + set_reg_field_value( + value, regval[0], INPUT_CSC_C11_C12_A, INPUT_CSC_C11_A); + set_reg_field_value( + value, regval[1], INPUT_CSC_C11_C12_A, INPUT_CSC_C12_A); + dm_write_reg(ctx, mmINPUT_CSC_C11_C12_A, value); + + value = 0; + set_reg_field_value( + value, regval[2], INPUT_CSC_C13_C14_A, INPUT_CSC_C13_A); + set_reg_field_value( + value, regval[3], INPUT_CSC_C13_C14_A, INPUT_CSC_C14_A); + dm_write_reg(ctx, mmINPUT_CSC_C13_C14_A, value); + + value = 0; + set_reg_field_value( + value, regval[4], INPUT_CSC_C21_C22_A, INPUT_CSC_C21_A); + set_reg_field_value( + value, regval[5], INPUT_CSC_C21_C22_A, INPUT_CSC_C22_A); + dm_write_reg(ctx, mmINPUT_CSC_C21_C22_A, value); + + value = 0; + set_reg_field_value( + value, regval[6], INPUT_CSC_C23_C24_A, INPUT_CSC_C23_A); + set_reg_field_value( + value, regval[7], INPUT_CSC_C23_C24_A, INPUT_CSC_C24_A); + dm_write_reg(ctx, mmINPUT_CSC_C23_C24_A, value); + + value = 0; + set_reg_field_value( + value, regval[8], INPUT_CSC_C31_C32_A, INPUT_CSC_C31_A); + set_reg_field_value( + value, regval[9], INPUT_CSC_C31_C32_A, INPUT_CSC_C32_A); + dm_write_reg(ctx, mmINPUT_CSC_C31_C32_A, value); + + value = 0; + set_reg_field_value( + value, regval[10], INPUT_CSC_C33_C34_A, INPUT_CSC_C33_A); + set_reg_field_value( + value, regval[11], INPUT_CSC_C33_C34_A, INPUT_CSC_C34_A); + dm_write_reg(ctx, mmINPUT_CSC_C33_C34_A, value); + } else { + /* fixed S2.13 format */ + value = 0; + set_reg_field_value( + value, regval[0], INPUT_CSC_C11_C12_B, INPUT_CSC_C11_B); + set_reg_field_value( + value, regval[1], INPUT_CSC_C11_C12_B, INPUT_CSC_C12_B); + dm_write_reg(ctx, mmINPUT_CSC_C11_C12_B, value); + + value = 0; + set_reg_field_value( + value, regval[2], INPUT_CSC_C13_C14_B, INPUT_CSC_C13_B); + set_reg_field_value( + value, regval[3], INPUT_CSC_C13_C14_B, INPUT_CSC_C14_B); + dm_write_reg(ctx, mmINPUT_CSC_C13_C14_B, value); + + value = 0; + set_reg_field_value( + value, regval[4], INPUT_CSC_C21_C22_B, INPUT_CSC_C21_B); + set_reg_field_value( + value, regval[5], INPUT_CSC_C21_C22_B, INPUT_CSC_C22_B); + dm_write_reg(ctx, mmINPUT_CSC_C21_C22_B, value); + + value = 0; + set_reg_field_value( + value, regval[6], INPUT_CSC_C23_C24_B, INPUT_CSC_C23_B); + set_reg_field_value( + value, regval[7], INPUT_CSC_C23_C24_B, INPUT_CSC_C24_B); + dm_write_reg(ctx, mmINPUT_CSC_C23_C24_B, value); + + value = 0; + set_reg_field_value( + value, regval[8], INPUT_CSC_C31_C32_B, INPUT_CSC_C31_B); + set_reg_field_value( + value, regval[9], INPUT_CSC_C31_C32_B, INPUT_CSC_C32_B); + dm_write_reg(ctx, mmINPUT_CSC_C31_C32_B, value); + + value = 0; + set_reg_field_value( + value, regval[10], INPUT_CSC_C33_C34_B, INPUT_CSC_C33_B); + set_reg_field_value( + value, regval[11], INPUT_CSC_C33_C34_B, INPUT_CSC_C34_B); + dm_write_reg(ctx, mmINPUT_CSC_C33_C34_B, value); + } + + /* KK: leave INPUT_CSC_CONVERSION_MODE at default */ + value = 0; + /* + * select 8.4 input type instead of default 12.0. From the discussion + * with HW team, this format depends on the UNP surface format, so for + * 8-bit we should select 8.4 (4 bits truncated). For 10 it should be + * 10.2. For Carrizo we only support 8-bit surfaces on underlay pipe + * so we can always keep this at 8.4 (input_type=2). If the later asics + * start supporting 10+ bits, we will have a problem: surface + * programming including UNP_GRPH* is being done in DalISR after this, + * so either we pass surface format to here, or move this logic to ISR + */ + + set_reg_field_value( + value, 2, COL_MAN_INPUT_CSC_CONTROL, INPUT_CSC_INPUT_TYPE); + set_reg_field_value( + value, + use_set_a ? 1 : 2, + COL_MAN_INPUT_CSC_CONTROL, + INPUT_CSC_MODE); + + dm_write_reg(ctx, mmCOL_MAN_INPUT_CSC_CONTROL, value); +} + +void dce110_opp_v_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_PREDEFINED; + + if (default_adjust->force_hw_default == false) { + const struct out_csc_color_matrix *elm; + /* currently parameter not in use */ + enum grph_color_adjust_option option = + GRPH_COLOR_MATRIX_HW_DEFAULT; + uint32_t i; + /* + * HW default false we program locally defined matrix + * HW default true we use predefined hw matrix and we + * do not need to program matrix + * OEM wants the HW default via runtime parameter. + */ + option = GRPH_COLOR_MATRIX_SW; + + for (i = 0; i < ARRAY_SIZE(global_color_matrix); ++i) { + elm = &global_color_matrix[i]; + if (elm->color_space != default_adjust->out_color_space) + continue; + /* program the matrix with default values from this + * file + */ + program_color_matrix_v(opp110, elm, option); + config = CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + break; + } + } + + program_input_csc(opp, default_adjust->in_color_space); + + /* configure the what we programmed : + * 1. Default values from this file + * 2. Use hardware default from ROM_A and we do not need to program + * matrix + */ + + configure_graphics_mode_v(opp110, config, + default_adjust->csc_adjust_type, + default_adjust->out_color_space); + + set_Denormalization(opp, default_adjust->color_depth); +} + +void dce110_opp_v_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct out_csc_color_matrix *tbl_entry) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + enum csc_color_mode config = + CSC_COLOR_MODE_GRAPHICS_OUTPUT_CSC; + + program_color_matrix_v( + opp110, tbl_entry, GRAPHICS_CSC_ADJUST_TYPE_SW); + + /* We did everything ,now program DxOUTPUT_CSC_CONTROL */ + configure_graphics_mode_v(opp110, config, GRAPHICS_CSC_ADJUST_TYPE_SW, + tbl_entry->color_space); + + /*TODO: Check if denormalization is needed*/ + /*set_Denormalization(opp, adjust->color_depth);*/ +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_formatter.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_formatter.c new file mode 100644 index 000000000000..eac20775c23f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_formatter.c @@ -0,0 +1,627 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_opp.h" + +#define FMT_REG(reg)\ + (reg + opp110->offsets.fmt_offset) +/** + * set_truncation + * 1) set truncation depth: 0 for 18 bpp or 1 for 24 bpp + * 2) enable truncation + * 3) HW remove 12bit FMT support for DCE11 power saving reason. + */ +static void set_truncation( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + uint32_t value = 0; + uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + + /*Disable truncation*/ + value = dm_read_reg(opp110->base.ctx, addr); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); + set_reg_field_value(value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); + + dm_write_reg(opp110->base.ctx, addr, value); + + /* no 10bpc trunc on DCE11*/ + if (params->flags.TRUNCATE_ENABLED == 0 || + params->flags.TRUNCATE_DEPTH == 2) + return; + + /*Set truncation depth and Enable truncation*/ + set_reg_field_value(value, 1, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN); + set_reg_field_value(value, params->flags.TRUNCATE_MODE, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_MODE); + set_reg_field_value(value, params->flags.TRUNCATE_DEPTH, + FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_DEPTH); + + dm_write_reg(opp110->base.ctx, addr, value); + +} + +/** + * set_spatial_dither + * 1) set spatial dithering mode: pattern of seed + * 2) set spatical dithering depth: 0 for 18bpp or 1 for 24bpp + * 3) set random seed + * 4) set random mode + * lfsr is reset every frame or not reset + * RGB dithering method + * 0: RGB data are all dithered with x^28+x^3+1 + * 1: R data is dithered with x^28+x^3+1 + * G data is dithered with x^28+X^9+1 + * B data is dithered with x^28+x^13+1 + * enable high pass filter or not + * 5) enable spatical dithering + */ +static void set_spatial_dither( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + uint32_t depth_cntl_value = 0; + uint32_t fmt_cntl_value = 0; + uint32_t dither_r_value = 0; + uint32_t dither_g_value = 0; + uint32_t dither_b_value = 0; + + /*Disable spatial (random) dithering*/ + depth_cntl_value = dm_read_reg(opp110->base.ctx, addr); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_EN); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_MODE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_SPATIAL_DITHER_DEPTH); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_TEMPORAL_DITHER_EN); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_HIGHPASS_RANDOM_ENABLE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_FRAME_RANDOM_ENABLE); + set_reg_field_value(depth_cntl_value, 0, + FMT_BIT_DEPTH_CONTROL, FMT_RGB_RANDOM_ENABLE); + + dm_write_reg(opp110->base.ctx, addr, depth_cntl_value); + + /* no 10bpc on DCE11*/ + if (params->flags.SPATIAL_DITHER_ENABLED == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 2) + return; + + addr = FMT_REG(mmFMT_CONTROL); + fmt_cntl_value = dm_read_reg(opp110->base.ctx, addr); + /* only use FRAME_COUNTER_MAX if frameRandom == 1*/ + if (params->flags.FRAME_RANDOM == 1) { + if (params->flags.SPATIAL_DITHER_DEPTH == 0 || + params->flags.SPATIAL_DITHER_DEPTH == 1) { + set_reg_field_value(fmt_cntl_value, 15, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); + set_reg_field_value(fmt_cntl_value, 2, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); + } else if (params->flags.SPATIAL_DITHER_DEPTH == 2) { + set_reg_field_value(fmt_cntl_value, 3, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); + set_reg_field_value(fmt_cntl_value, 1, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); + } else + return; + } else { + set_reg_field_value(fmt_cntl_value, 0, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX); + set_reg_field_value(fmt_cntl_value, 0, + FMT_CONTROL, + FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP); + } + + dm_write_reg(opp110->base.ctx, addr, fmt_cntl_value); + + /*Set seed for random values for + * spatial dithering for R,G,B channels*/ + addr = FMT_REG(mmFMT_DITHER_RAND_R_SEED); + set_reg_field_value(dither_r_value, params->r_seed_value, + FMT_DITHER_RAND_R_SEED, + FMT_RAND_R_SEED); + dm_write_reg(opp110->base.ctx, addr, dither_r_value); + + addr = FMT_REG(mmFMT_DITHER_RAND_G_SEED); + set_reg_field_value(dither_g_value, + params->g_seed_value, + FMT_DITHER_RAND_G_SEED, + FMT_RAND_G_SEED); + dm_write_reg(opp110->base.ctx, addr, dither_g_value); + + addr = FMT_REG(mmFMT_DITHER_RAND_B_SEED); + set_reg_field_value(dither_b_value, params->b_seed_value, + FMT_DITHER_RAND_B_SEED, + FMT_RAND_B_SEED); + dm_write_reg(opp110->base.ctx, addr, dither_b_value); + + /* FMT_OFFSET_R_Cr 31:16 0x0 Setting the zero + * offset for the R/Cr channel, lower 4LSB + * is forced to zeros. Typically set to 0 + * RGB and 0x80000 YCbCr. + */ + /* FMT_OFFSET_G_Y 31:16 0x0 Setting the zero + * offset for the G/Y channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB + * and 0x80000 YCbCr. + */ + /* FMT_OFFSET_B_Cb 31:16 0x0 Setting the zero + * offset for the B/Cb channel, lower 4LSB is + * forced to zeros. Typically set to 0 RGB and + * 0x80000 YCbCr. + */ + + /*Set spatial dithering bit depth*/ + set_reg_field_value(depth_cntl_value, + params->flags.SPATIAL_DITHER_DEPTH, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_DEPTH); + + /* Set spatial dithering mode + * (default is Seed patterrn AAAA...) + */ + set_reg_field_value(depth_cntl_value, + params->flags.SPATIAL_DITHER_MODE, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_MODE); + + /*Reset only at startup*/ + set_reg_field_value(depth_cntl_value, + params->flags.FRAME_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_FRAME_RANDOM_ENABLE); + + /*Set RGB data dithered with x^28+x^3+1*/ + set_reg_field_value(depth_cntl_value, + params->flags.RGB_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_RGB_RANDOM_ENABLE); + + /*Disable High pass filter*/ + set_reg_field_value(depth_cntl_value, + params->flags.HIGHPASS_RANDOM, + FMT_BIT_DEPTH_CONTROL, + FMT_HIGHPASS_RANDOM_ENABLE); + + /*Enable spatial dithering*/ + set_reg_field_value(depth_cntl_value, + 1, + FMT_BIT_DEPTH_CONTROL, + FMT_SPATIAL_DITHER_EN); + + addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + dm_write_reg(opp110->base.ctx, addr, depth_cntl_value); + +} + +/** + * SetTemporalDither (Frame Modulation) + * 1) set temporal dither depth + * 2) select pattern: from hard-coded pattern or programmable pattern + * 3) select optimized strips for BGR or RGB LCD sub-pixel + * 4) set s matrix + * 5) set t matrix + * 6) set grey level for 0.25, 0.5, 0.75 + * 7) enable temporal dithering + */ +static void set_temporal_dither( + struct dce110_opp *opp110, + const struct bit_depth_reduction_params *params) +{ + uint32_t addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + uint32_t value; + + /*Disable temporal (frame modulation) dithering first*/ + value = dm_read_reg(opp110->base.ctx, addr); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_RESET); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_OFFSET); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL); + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_50FRC_SEL); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_75FRC_SEL); + + dm_write_reg(opp110->base.ctx, addr, value); + + /* no 10bpc dither on DCE11*/ + if (params->flags.FRAME_MODULATION_ENABLED == 0 || + params->flags.FRAME_MODULATION_DEPTH == 2) + return; + + /* Set temporal dithering depth*/ + set_reg_field_value(value, + params->flags.FRAME_MODULATION_DEPTH, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_DEPTH); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_RESET); + + set_reg_field_value(value, + 0, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_OFFSET); + + /*Select legacy pattern based on FRC and Temporal level*/ + addr = FMT_REG(mmFMT_TEMPORAL_DITHER_PATTERN_CONTROL); + dm_write_reg(opp110->base.ctx, addr, 0); + /*Set s matrix*/ + addr = FMT_REG( + mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_S_MATRIX); + dm_write_reg(opp110->base.ctx, addr, 0); + /*Set t matrix*/ + addr = FMT_REG( + mmFMT_TEMPORAL_DITHER_PROGRAMMABLE_PATTERN_T_MATRIX); + dm_write_reg(opp110->base.ctx, addr, 0); + + /*Select patterns for 0.25, 0.5 and 0.75 grey level*/ + set_reg_field_value(value, + params->flags.TEMPORAL_LEVEL, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_LEVEL); + + set_reg_field_value(value, + params->flags.FRC25, + FMT_BIT_DEPTH_CONTROL, + FMT_25FRC_SEL); + + set_reg_field_value(value, + params->flags.FRC50, + FMT_BIT_DEPTH_CONTROL, + FMT_50FRC_SEL); + + set_reg_field_value(value, + params->flags.FRC75, + FMT_BIT_DEPTH_CONTROL, + FMT_75FRC_SEL); + + /*Enable bit reduction by temporal (frame modulation) dithering*/ + set_reg_field_value(value, + 1, + FMT_BIT_DEPTH_CONTROL, + FMT_TEMPORAL_DITHER_EN); + + addr = FMT_REG(mmFMT_BIT_DEPTH_CONTROL); + dm_write_reg(opp110->base.ctx, addr, value); + +} + +/** + * Set Clamping + * 1) Set clamping format based on bpc - 0 for 6bpc (No clamping) + * 1 for 8 bpc + * 2 for 10 bpc + * 3 for 12 bpc + * 7 for programable + * 2) Enable clamp if Limited range requested + */ +void dce110_opp_set_clamping( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + uint32_t clamp_cntl_value = 0; + uint32_t red_clamp_value = 0; + uint32_t green_clamp_value = 0; + uint32_t blue_clamp_value = 0; + uint32_t addr = FMT_REG(mmFMT_CLAMP_CNTL); + + clamp_cntl_value = dm_read_reg(opp110->base.ctx, addr); + + set_reg_field_value(clamp_cntl_value, + 0, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 0, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + switch (params->clamping_level) { + case CLAMPING_FULL_RANGE: + break; + + case CLAMPING_LIMITED_RANGE_8BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + + case CLAMPING_LIMITED_RANGE_10BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 2, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + case CLAMPING_LIMITED_RANGE_12BPC: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 3, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + break; + case CLAMPING_LIMITED_RANGE_PROGRAMMABLE: + set_reg_field_value(clamp_cntl_value, + 1, + FMT_CLAMP_CNTL, + FMT_CLAMP_DATA_EN); + + set_reg_field_value(clamp_cntl_value, + 7, + FMT_CLAMP_CNTL, + FMT_CLAMP_COLOR_FORMAT); + + /*set the defaults*/ + set_reg_field_value(red_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_R, + FMT_CLAMP_LOWER_R); + + set_reg_field_value(red_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_R, + FMT_CLAMP_UPPER_R); + + addr = FMT_REG(mmFMT_CLAMP_COMPONENT_R); + dm_write_reg(opp110->base.ctx, addr, red_clamp_value); + + set_reg_field_value(green_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_G, + FMT_CLAMP_LOWER_G); + + set_reg_field_value(green_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_G, + FMT_CLAMP_UPPER_G); + + addr = FMT_REG(mmFMT_CLAMP_COMPONENT_G); + dm_write_reg(opp110->base.ctx, addr, green_clamp_value); + + set_reg_field_value(blue_clamp_value, + 0x10, + FMT_CLAMP_COMPONENT_B, + FMT_CLAMP_LOWER_B); + + set_reg_field_value(blue_clamp_value, + 0xFEF, + FMT_CLAMP_COMPONENT_B, + FMT_CLAMP_UPPER_B); + + addr = FMT_REG(mmFMT_CLAMP_COMPONENT_B); + dm_write_reg(opp110->base.ctx, addr, blue_clamp_value); + + break; + + default: + break; + } + + addr = FMT_REG(mmFMT_CLAMP_CNTL); + /*Set clamp control*/ + dm_write_reg(opp110->base.ctx, addr, clamp_cntl_value); + +} + +/** + * set_pixel_encoding + * + * Set Pixel Encoding + * 0: RGB 4:4:4 or YCbCr 4:4:4 or YOnly + * 1: YCbCr 4:2:2 + */ +static void set_pixel_encoding( + struct dce110_opp *opp110, + const struct clamping_and_pixel_encoding_params *params) +{ + uint32_t fmt_cntl_value; + uint32_t addr = FMT_REG(mmFMT_CONTROL); + + /*RGB 4:4:4 or YCbCr 4:4:4 - 0; YCbCr 4:2:2 -1.*/ + fmt_cntl_value = dm_read_reg(opp110->base.ctx, addr); + + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_PIXEL_ENCODING); + + if (params->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + set_reg_field_value(fmt_cntl_value, + 1, + FMT_CONTROL, + FMT_PIXEL_ENCODING); + + /*00 - Pixels drop mode ,01 - Pixels average mode*/ + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_SUBSAMPLING_MODE); + + /*00 - Cb before Cr ,01 - Cr before Cb*/ + set_reg_field_value(fmt_cntl_value, + 0, + FMT_CONTROL, + FMT_SUBSAMPLING_ORDER); + } + dm_write_reg(opp110->base.ctx, addr, fmt_cntl_value); + +} + +void dce110_opp_program_bit_depth_reduction( + struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + set_truncation(opp110, params); + set_spatial_dither(opp110, params); + set_temporal_dither(opp110, params); +} + +void dce110_opp_program_clamping_and_pixel_encoding( + struct output_pixel_processor *opp, + const struct clamping_and_pixel_encoding_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + dce110_opp_set_clamping(opp110, params); + set_pixel_encoding(opp110, params); +} + +void dce110_opp_set_dyn_expansion( + struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + uint32_t value; + bool enable_dyn_exp = false; + uint32_t addr = FMT_REG(mmFMT_DYNAMIC_EXP_CNTL); + + value = dm_read_reg(opp->ctx, addr); + + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + + /* From HW programming guide: + FMT_DYNAMIC_EXP_EN = 0 for limited RGB or YCbCr output + FMT_DYNAMIC_EXP_EN = 1 for RGB full range only*/ + if (color_sp == COLOR_SPACE_SRGB) + enable_dyn_exp = true; + + /*00 - 10-bit -> 12-bit dynamic expansion*/ + /*01 - 8-bit -> 12-bit dynamic expansion*/ + if (signal == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (color_dpth) { + case COLOR_DEPTH_888: + set_reg_field_value(value, enable_dyn_exp ? 1:0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 1, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + break; + case COLOR_DEPTH_101010: + set_reg_field_value(value, enable_dyn_exp ? 1:0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN); + set_reg_field_value(value, 0, + FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_MODE); + break; + case COLOR_DEPTH_121212: + break; + default: + break; + } + } + + dm_write_reg(opp->ctx, addr, value); +} + +void dce110_opp_program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping) +{ + /* dithering is affected by <CrtcSourceSelect>, hence should be + * programmed afterwards */ + dce110_opp_program_bit_depth_reduction( + opp, + fmt_bit_depth); + + dce110_opp_program_clamping_and_pixel_encoding( + opp, + clamping); + + return; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma.c new file mode 100644 index 000000000000..62051abcfe2f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma.c @@ -0,0 +1,537 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_opp.h" +#include "gamma_types.h" + +#define DCP_REG(reg)\ + (reg + opp110->offsets.dcp_offset) + +#define DCFE_REG(reg)\ + (reg + opp110->offsets.dcfe_offset) + +enum { + MAX_PWL_ENTRY = 128, + MAX_REGIONS_NUMBER = 16 + +}; + +/* + ***************************************************************************** + * Function: regamma_config_regions_and_segments + * + * build regamma curve by using predefined hw points + * uses interface parameters ,like EDID coeff. + * + * @param : parameters interface parameters + * @return void + * + * @note + * + * @see + * + ***************************************************************************** + */ +static void regamma_config_regions_and_segments( + struct dce110_opp *opp110, + 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, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START); + + set_reg_field_value( + value, + 0, + REGAMMA_CNTLA_START_CNTL, + REGAMMA_CNTLA_EXP_REGION_START_SEGMENT); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_START_CNTL), + value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[0].custom_float_slope, + REGAMMA_CNTLA_SLOPE_CNTL, + REGAMMA_CNTLA_EXP_REGION_LINEAR_SLOPE); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_SLOPE_CNTL), value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[1].custom_float_x, + REGAMMA_CNTLA_END_CNTL1, + REGAMMA_CNTLA_EXP_REGION_END); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_END_CNTL1), value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[2].custom_float_slope, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_BASE); + + set_reg_field_value( + value, + params->arr_points[1].custom_float_y, + REGAMMA_CNTLA_END_CNTL2, + REGAMMA_CNTLA_EXP_REGION_END_SLOPE); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_END_CNTL2), value); + } + + curve = params->arr_curve_points; + + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION0_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_0_1, + REGAMMA_CNTLA_EXP_REGION1_NUM_SEGMENTS); + + dm_write_reg( + opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_0_1), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION2_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_2_3, + REGAMMA_CNTLA_EXP_REGION3_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_2_3), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION4_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_4_5, + REGAMMA_CNTLA_EXP_REGION5_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_4_5), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION6_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_6_7, + REGAMMA_CNTLA_EXP_REGION7_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_6_7), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION8_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_8_9, + REGAMMA_CNTLA_EXP_REGION9_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_8_9), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION10_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_10_11, + REGAMMA_CNTLA_EXP_REGION11_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_10_11), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION12_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_12_13, + REGAMMA_CNTLA_EXP_REGION13_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_12_13), + value); + } + + curve += 2; + { + value = 0; + set_reg_field_value( + value, + curve[0].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_LUT_OFFSET); + + set_reg_field_value( + value, + curve[0].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION14_NUM_SEGMENTS); + + set_reg_field_value( + value, + curve[1].offset, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_LUT_OFFSET); + + set_reg_field_value( + value, + curve[1].segments_num, + REGAMMA_CNTLA_REGION_14_15, + REGAMMA_CNTLA_EXP_REGION15_NUM_SEGMENTS); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CNTLA_REGION_14_15), + value); + } +} + +static void program_pwl( + struct dce110_opp *opp110, + const struct pwl_params *params) +{ + uint32_t value; + + { + uint8_t max_tries = 10; + uint8_t counter = 0; + + /* Power on LUT memory */ + value = dm_read_reg(opp110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_CTRL)); + + set_reg_field_value( + value, + 1, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dm_write_reg(opp110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); + + while (counter < max_tries) { + value = + dm_read_reg( + opp110->base.ctx, + DCFE_REG(mmDCFE_MEM_PWR_STATUS)); + + if (get_reg_field_value( + value, + DCFE_MEM_PWR_STATUS, + DCP_REGAMMA_MEM_PWR_STATE) == 0) + break; + + ++counter; + } + + if (counter == max_tries) { + dm_logger_write(opp110->base.ctx->logger, LOG_WARNING, + "%s: regamma lut was not powered on " + "in a timely manner," + " programming still proceeds\n", + __func__); + } + } + + value = 0; + + set_reg_field_value( + value, + 7, + REGAMMA_LUT_WRITE_EN_MASK, + REGAMMA_LUT_WRITE_EN_MASK); + + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_LUT_WRITE_EN_MASK), value); + dm_write_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_LUT_INDEX), 0); + + /* Program REGAMMA_LUT_DATA */ + { + const uint32_t addr = DCP_REG(mmREGAMMA_LUT_DATA); + + uint32_t i = 0; + + const struct pwl_result_data *rgb = params->rgb_resulted; + + while (i != params->hw_points_num) { + dm_write_reg(opp110->base.ctx, addr, rgb->red_reg); + dm_write_reg(opp110->base.ctx, addr, rgb->green_reg); + dm_write_reg(opp110->base.ctx, addr, rgb->blue_reg); + + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_red_reg); + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_green_reg); + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } + + /* we are done with DCP LUT memory; re-enable low power mode */ + value = dm_read_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL)); + + set_reg_field_value( + value, + 0, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + dm_write_reg(opp110->base.ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); +} + +bool dce110_opp_program_regamma_pwl( + struct output_pixel_processor *opp, + const struct pwl_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + /* Setup regions */ + regamma_config_regions_and_segments(opp110, params); + + /* Program PWL */ + program_pwl(opp110, params); + + return true; +} + +void dce110_opp_power_on_regamma_lut( + struct output_pixel_processor *opp, + bool power_on) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + uint32_t value = + dm_read_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL)); + + set_reg_field_value( + value, + power_on, + DCFE_MEM_PWR_CTRL, + DCP_REGAMMA_MEM_PWR_DIS); + + set_reg_field_value( + value, + power_on, + DCFE_MEM_PWR_CTRL, + DCP_LUT_MEM_PWR_DIS); + + dm_write_reg(opp->ctx, DCFE_REG(mmDCFE_MEM_PWR_CTRL), value); +} + +void dce110_opp_set_regamma_mode(struct output_pixel_processor *opp, + enum opp_regamma mode) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + uint32_t value = dm_read_reg(opp110->base.ctx, + DCP_REG(mmREGAMMA_CONTROL)); + + set_reg_field_value( + value, + mode, + REGAMMA_CONTROL, + GRPH_REGAMMA_MODE); + + dm_write_reg(opp110->base.ctx, DCP_REG(mmREGAMMA_CONTROL), + value); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c new file mode 100644 index 000000000000..3b3a9175b2c3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_regamma_v.c @@ -0,0 +1,551 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_opp.h" +#include "gamma_types.h" + +static void power_on_lut(struct output_pixel_processor *opp, + bool power_on, bool inputgamma, bool regamma) +{ + uint32_t value = dm_read_reg(opp->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(opp->ctx, mmDCFEV_MEM_PWR_CTRL, value); + + for (i = 0; i < 3; i++) { + value = dm_read_reg(opp->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 dce110_opp *opp110) +{ + uint32_t value; + + value = dm_read_reg(opp110->base.ctx, + mmCOL_MAN_INPUT_GAMMA_CONTROL1); + + set_reg_field_value( + value, + 0, + COL_MAN_INPUT_GAMMA_CONTROL1, + INPUT_GAMMA_MODE); + + dm_write_reg(opp110->base.ctx, + mmCOL_MAN_INPUT_GAMMA_CONTROL1, value); +} + +static void configure_regamma_mode(struct dce110_opp *opp110, uint32_t mode) +{ + uint32_t value = 0; + + set_reg_field_value( + value, + mode, + GAMMA_CORR_CONTROL, + GAMMA_CORR_MODE); + + dm_write_reg(opp110->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 dce110_opp *opp110, 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(opp110->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(opp110->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(opp110->base.ctx, + mmGAMMA_CORR_CNTLA_END_CNTL1, value); + } + { + value = 0; + set_reg_field_value( + value, + params->arr_points[2].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(opp110->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( + opp110->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(opp110->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(opp110->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(opp110->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(opp110->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(opp110->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(opp110->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(opp110->base.ctx, + mmGAMMA_CORR_CNTLA_REGION_14_15, + value); + } +} + +static void program_pwl(struct dce110_opp *opp110, + 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(opp110->base.ctx, + mmGAMMA_CORR_LUT_WRITE_EN_MASK, value); + + dm_write_reg(opp110->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(opp110->base.ctx, addr, rgb->red_reg); + dm_write_reg(opp110->base.ctx, addr, rgb->green_reg); + dm_write_reg(opp110->base.ctx, addr, rgb->blue_reg); + + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_red_reg); + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_green_reg); + dm_write_reg(opp110->base.ctx, addr, + rgb->delta_blue_reg); + + ++rgb; + ++i; + } + } +} + +bool dce110_opp_program_regamma_pwl_v( + struct output_pixel_processor *opp, + const struct pwl_params *params) +{ + struct dce110_opp *opp110 = TO_DCE110_OPP(opp); + + /* Setup regions */ + regamma_config_regions_and_segments(opp110, params); + + set_bypass_input_gamma(opp110); + + /* Power on gamma LUT memory */ + power_on_lut(opp, true, false, true); + + /* Program PWL */ + program_pwl(opp110, params); + + /* program regamma config */ + configure_regamma_mode(opp110, 1); + + /* Power return to auto back */ + power_on_lut(opp, false, false, true); + + return true; +} + +void dce110_opp_power_on_regamma_lut_v( + struct output_pixel_processor *opp, + bool power_on) +{ + uint32_t value = dm_read_reg(opp->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(opp->ctx, mmDCFEV_MEM_PWR_CTRL, value); +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c new file mode 100644 index 000000000000..4b32397529ec --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.c @@ -0,0 +1,63 @@ +/* + * Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dm_services.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dce110_opp.h" +#include "dce110_opp_v.h" + +#include "gamma_types.h" + +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +static const struct opp_funcs funcs = { + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut_v, + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl_v, + .opp_set_csc_default = dce110_opp_v_set_csc_default, + .opp_set_csc_adjustment = dce110_opp_v_set_csc_adjustment, + .opp_set_dyn_expansion = dce110_opp_set_dyn_expansion, + .opp_set_regamma_mode = dce110_opp_set_regamma_mode, + .opp_destroy = dce110_opp_destroy, + .opp_program_fmt = dce110_opp_program_fmt, + .opp_program_bit_depth_reduction = + dce110_opp_program_bit_depth_reduction +}; + +bool dce110_opp_v_construct(struct dce110_opp *opp110, + struct dc_context *ctx) +{ + opp110->base.funcs = &funcs; + + opp110->base.ctx = ctx; + + return true; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h new file mode 100644 index 000000000000..dcdbf86fccc1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_v.h @@ -0,0 +1,56 @@ +/* Copyright 2012-15 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_OPP_DCE110_V_H__ +#define __DC_OPP_DCE110_V_H__ + +#include "dc_types.h" +#include "opp.h" +#include "core_types.h" + +#include "gamma_types.h" /* decprecated */ + +struct gamma_parameters; + +bool dce110_opp_v_construct(struct dce110_opp *opp110, + struct dc_context *ctx); + +/* underlay callbacks */ +void dce110_opp_v_set_csc_default( + struct output_pixel_processor *opp, + const struct default_adjustment *default_adjust); + +void dce110_opp_v_set_csc_adjustment( + struct output_pixel_processor *opp, + const struct out_csc_color_matrix *tbl_entry); + +bool dce110_opp_program_regamma_pwl_v( + struct output_pixel_processor *opp, + const struct pwl_params *params); + +void dce110_opp_power_on_regamma_lut_v( + struct output_pixel_processor *opp, + bool power_on); + +#endif /* __DC_OPP_DCE110_V_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c new file mode 100644 index 000000000000..959467fa421e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.c @@ -0,0 +1,1413 @@ +/* +* 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 "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "dce110/dce110_resource.h" + +#include "include/irq_service_interface.h" +#include "dce/dce_audio.h" +#include "dce110/dce110_timing_generator.h" +#include "irq/dce110/irq_service_dce110.h" +#include "dce110/dce110_timing_generator_v.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce110/dce110_mem_input.h" +#include "dce110/dce110_mem_input_v.h" +#include "dce110/dce110_ipp.h" +#include "dce/dce_transform.h" +#include "dce110/dce110_transform_v.h" +#include "dce110/dce110_opp.h" +#include "dce110/dce110_opp_v.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_hwseq.h" +#include "dce110/dce110_hw_sequencer.h" + +#include "reg_helper.h" + +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#ifndef mmMC_HUB_RDREQ_DMIF_LIMIT +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" +#endif + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +#ifndef mmBIOS_SCRATCH_2 + #define mmBIOS_SCRATCH_2 0x05CB + #define mmBIOS_SCRATCH_6 0x05CF +#endif + +#ifndef mmDP_DPHY_BS_SR_SWAP_CNTL + #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4ADC + #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4BDC + #define mmDP2_DP_DPHY_BS_SR_SWAP_CNTL 0x4CDC + #define mmDP3_DP_DPHY_BS_SR_SWAP_CNTL 0x4DDC + #define mmDP4_DP_DPHY_BS_SR_SWAP_CNTL 0x4EDC + #define mmDP5_DP_DPHY_BS_SR_SWAP_CNTL 0x4FDC + #define mmDP6_DP_DPHY_BS_SR_SWAP_CNTL 0x54DC +#endif + +#ifndef mmDP_DPHY_FAST_TRAINING + #define mmDP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP0_DP_DPHY_FAST_TRAINING 0x4ABC + #define mmDP1_DP_DPHY_FAST_TRAINING 0x4BBC + #define mmDP2_DP_DPHY_FAST_TRAINING 0x4CBC + #define mmDP3_DP_DPHY_FAST_TRAINING 0x4DBC + #define mmDP4_DP_DPHY_FAST_TRAINING 0x4EBC + #define mmDP5_DP_DPHY_FAST_TRAINING 0x4FBC + #define mmDP6_DP_DPHY_FAST_TRAINING 0x54BC +#endif + +#ifndef DPHY_RX_FAST_TRAINING_CAPABLE + #define DPHY_RX_FAST_TRAINING_CAPABLE 0x1 +#endif + +static const struct dce110_timing_generator_offsets dce110_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +static const struct dce110_mem_input_reg_offsets dce110_mi_reg_offsets[] = { + { + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE0_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE1_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE2_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + } +}; + + +static const struct dce110_ipp_reg_offsets dce110_ipp_reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP3_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP4_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP5_CUR_CONTROL - mmCUR_CONTROL), +} +}; + + + + +/* set register offset */ +#define SR(reg_name)\ + .reg_name = mm ## reg_name + +/* set register offset with instance */ +#define SRI(reg_name, block, id)\ + .reg_name = mm ## block ## id ## _ ## reg_name + + +#define transform_regs(id)\ +[id] = {\ + XFM_COMMON_REG_LIST_DCE110(id)\ +} + +static const struct dce_transform_registers xfm_regs[] = { + transform_regs(0), + transform_regs(1), + transform_regs(2) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define aux_regs(id)\ +[id] = {\ + AUX_REG_LIST(id)\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define hpd_regs(id)\ +[id] = {\ + HPD_REG_LIST(id)\ +} + +static const struct dce110_link_enc_hpd_registers link_enc_hpd_regs[] = { + hpd_regs(0), + hpd_regs(1), + hpd_regs(2), + hpd_regs(3), + hpd_regs(4), + hpd_regs(5) +}; + + +#define link_regs(id)\ +[id] = {\ + LE_DCE110_REG_LIST(id)\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6), +}; + +#define stream_enc_regs(id)\ +[id] = {\ + SE_COMMON_REG_LIST(id),\ + .TMDS_CNTL = 0,\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +#define audio_regs(id)\ +[id] = {\ + AUD_COMMON_REG_LIST(id)\ +} + +static const struct dce_audio_registers audio_regs[] = { + audio_regs(0), + audio_regs(1), + audio_regs(2), + audio_regs(3), + audio_regs(4), + audio_regs(5), + audio_regs(6), +}; + +static const struct dce_audio_shift audio_shift = { + AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_aduio_mask audio_mask = { + AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +/* AG TBD Needs to be reduced back to 3 pipes once dce10 hw sequencer implemented. */ +static const struct dce110_opp_reg_offsets dce110_opp_reg_offsets[] = { +{ + .fmt_offset = (mmFMT0_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT1_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT2_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .fmt_offset = (mmFMT3_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE3_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT4_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE4_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT5_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE5_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + +#define clk_src_regs(id)\ +[id] = {\ + CS_COMMON_REG_LIST_DCE_100_110(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0), + clk_src_regs(1), + clk_src_regs(2) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) +}; + +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 +}; + +static const struct resource_caps carrizo_resource_cap = { + .num_timing_generator = 3, + .num_video_plane = 1, + .num_audio = 3, + .num_stream_encoder = 3, + .num_pll = 2, +}; + +static const struct resource_caps stoney_resource_cap = { + .num_timing_generator = 2, + .num_video_plane = 1, + .num_audio = 3, + .num_stream_encoder = 3, + .num_pll = 2, +}; + +#define CTX ctx +#define REG(reg) mm ## reg + +#ifndef mmCC_DC_HDMI_STRAPS +#define mmCC_DC_HDMI_STRAPS 0x4819 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE_MASK 0x40 +#define CC_DC_HDMI_STRAPS__HDMI_DISABLE__SHIFT 0x6 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER_MASK 0x700 +#define CC_DC_HDMI_STRAPS__AUDIO_STREAM_NUMBER__SHIFT 0x8 +#endif + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + REG_GET_2(CC_DC_HDMI_STRAPS, + HDMI_DISABLE, &straps->hdmi_disable, + AUDIO_STREAM_NUMBER, &straps->audio_stream_number); + + REG_GET(DC_PINSTRAPS, DC_PINSTRAPS_AUDIO, &straps->dc_pinstraps_audio); +} + +static struct audio *create_audio( + struct dc_context *ctx, unsigned int inst) +{ + return dce_audio_create(ctx, inst, + &audio_regs[inst], &audio_shift, &audio_mask); +} + +static struct timing_generator *dce110_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + dm_alloc(sizeof(struct dce110_timing_generator)); + + if (!tg110) + return NULL; + + if (dce110_timing_generator_construct(tg110, ctx, instance, offsets)) + return &tg110->base; + + BREAK_TO_DEBUGGER(); + dm_free(tg110); + return NULL; +} + +static struct stream_encoder *dce110_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + dm_alloc(sizeof(struct dce110_stream_encoder)); + + if (!enc110) + return NULL; + + if (dce110_stream_encoder_construct( + enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], &se_shift, &se_mask)) + return &enc110->base; + + BREAK_TO_DEBUGGER(); + dm_free(enc110); + return NULL; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_stoney_reg = { + HWSEQ_ST_REG_LIST() +}; + +static const struct dce_hwseq_registers hwseq_cz_reg = { + HWSEQ_CZ_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE11_MASK_SH_LIST(__SHIFT), +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE11_MASK_SH_LIST(_MASK), +}; + +static struct dce_hwseq *dce110_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = dm_alloc(sizeof(struct dce_hwseq)); + + if (hws) { + hws->ctx = ctx; + hws->regs = ASIC_REV_IS_STONEY(ctx->asic_id.hw_internal_rev) ? + &hwseq_stoney_reg : &hwseq_cz_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + hws->wa.blnd_crtc_trigger = true; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce110_stream_encoder_create, + .create_hwseq = dce110_hwseq_create, +}; + +#define mi_inst_regs(id) { \ + MI_REG_LIST(id), \ + .MC_HUB_RDREQ_DMIF_LIMIT = mmMC_HUB_RDREQ_DMIF_LIMIT \ +} +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE_MASK_SH_LIST(__SHIFT), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE__SHIFT +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE_MASK_SH_LIST(_MASK), + .ENABLE = MC_HUB_RDREQ_DMIF_LIMIT__ENABLE_MASK +}; + +static struct mem_input *dce110_mem_input_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offset) +{ + struct dce110_mem_input *mem_input110 = + dm_alloc(sizeof(struct dce110_mem_input)); + + if (!mem_input110) + return NULL; + + if (dce110_mem_input_construct(mem_input110, ctx, inst, offset)) { + struct mem_input *mi = &mem_input110->base; + + mi->regs = &mi_regs[inst]; + mi->shifts = &mi_shifts; + mi->masks = &mi_masks; + mi->wa.single_head_rdreq_dmif_limit = 3; + return mi; + } + + BREAK_TO_DEBUGGER(); + dm_free(mem_input110); + return NULL; +} + +static void dce110_transform_destroy(struct transform **xfm) +{ + dm_free(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce110_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + dm_alloc(sizeof(struct dce_transform)); + + if (!transform) + return NULL; + + if (dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask)) + return &transform->base; + + BREAK_TO_DEBUGGER(); + dm_free(transform); + return NULL; +} + +static struct input_pixel_processor *dce110_ipp_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offsets) +{ + struct dce110_ipp *ipp = + dm_alloc(sizeof(struct dce110_ipp)); + + if (!ipp) + return NULL; + + if (dce110_ipp_construct(ipp, ctx, inst, offsets)) + return &ipp->base; + + BREAK_TO_DEBUGGER(); + dm_free(ipp); + return NULL; +} + +struct link_encoder *dce110_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + dm_alloc(sizeof(struct dce110_link_encoder)); + + if (!enc110) + return NULL; + + if (dce110_link_encoder_construct( + enc110, + enc_init_data, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source])) { + + enc110->base.features.ycbcr420_supported = false; + enc110->base.features.max_hdmi_pixel_clock = 594000; + return &enc110->base; + } + + BREAK_TO_DEBUGGER(); + dm_free(enc110); + return NULL; +} + +static struct output_pixel_processor *dce110_opp_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_opp_reg_offsets *offsets) +{ + struct dce110_opp *opp = + dm_alloc(sizeof(struct dce110_opp)); + + if (!opp) + return NULL; + + if (dce110_opp_construct(opp, + ctx, inst, offsets)) + return &opp->base; + + BREAK_TO_DEBUGGER(); + dm_free(opp); + return NULL; +} + +struct clock_source *dce110_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_regs *regs, + bool dp_clk_src) +{ + struct dce110_clk_src *clk_src = + dm_alloc(sizeof(struct dce110_clk_src)); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, + regs, &cs_shift, &cs_mask)) { + clk_src->base.dp_clk_src = dp_clk_src; + return &clk_src->base; + } + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce110_clock_source_destroy(struct clock_source **clk_src) +{ + struct dce110_clk_src *dce110_clk_src; + + if (!clk_src) + return; + + dce110_clk_src = TO_DCE110_CLK_SRC(*clk_src); + + if (dce110_clk_src->dp_ss_params) + dm_free(dce110_clk_src->dp_ss_params); + + if (dce110_clk_src->hdmi_ss_params) + dm_free(dce110_clk_src->hdmi_ss_params); + + if (dce110_clk_src->dvi_ss_params) + dm_free(dce110_clk_src->dvi_ss_params); + + dm_free(dce110_clk_src); + *clk_src = NULL; +} + +static void destruct(struct dce110_resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->base.pipe_count; i++) { + if (pool->base.opps[i] != NULL) + dce110_opp_destroy(&pool->base.opps[i]); + + if (pool->base.transforms[i] != NULL) + dce110_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce110_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + dm_free(TO_DCE110_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.timing_generators[i] != NULL) { + dm_free(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + dm_free(DCE110STRENC_FROM_STRENC(pool->base.stream_enc[i])); + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] != NULL) { + dce110_clock_source_destroy(&pool->base.clock_sources[i]); + } + } + + if (pool->base.dp_clock_source != NULL) + dce110_clock_source_destroy(&pool->base.dp_clock_source); + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i] != NULL) { + dce_aud_destroy(&pool->base.audios[i]); + } + } + + if (pool->base.display_clock != NULL) { + dal_display_clock_destroy(&pool->base.display_clock); + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } +} + + +static void get_pixel_clock_parameters( + const struct pipe_ctx *pipe_ctx, + struct pixel_clk_params *pixel_clk_params) +{ + const struct core_stream *stream = pipe_ctx->stream; + + /*TODO: is this halved for YCbCr 420? in that case we might want to move + * the pixel clock normalization for hdmi up to here instead of doing it + * in pll_adjust_pix_clk + */ + pixel_clk_params->requested_pix_clk = stream->public.timing.pix_clk_khz; + pixel_clk_params->encoder_object_id = stream->sink->link->link_enc->id; + pixel_clk_params->signal_type = pipe_ctx->stream->signal; + pixel_clk_params->controller_id = pipe_ctx->pipe_idx + 1; + /* TODO: un-hardcode*/ + pixel_clk_params->requested_sym_clk = LINK_RATE_LOW * + LINK_RATE_REF_FREQ_IN_KHZ; + pixel_clk_params->flags.ENABLE_SS = 0; + pixel_clk_params->color_depth = + stream->public.timing.display_color_depth; + pixel_clk_params->flags.DISPLAY_BLANKED = 1; + pixel_clk_params->flags.SUPPORT_YCBCR420 = (stream->public.timing.pixel_encoding == + PIXEL_ENCODING_YCBCR420); +} + +void dce110_resource_build_bit_depth_reduction_params( + const struct core_stream *stream, + struct bit_depth_reduction_params *fmt_bit_depth) +{ + memset(fmt_bit_depth, 0, sizeof(*fmt_bit_depth)); + + /*TODO: Need to un-hardcode, refer to function with same name + * in dal2 hw_sequencer*/ + + fmt_bit_depth->flags.TRUNCATE_ENABLED = 0; + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 0; + fmt_bit_depth->flags.FRAME_MODULATION_ENABLED = 0; + + /* Diagnostics need consistent CRC of the image, that means + * dithering should not be enabled for Diagnostics. */ + if (IS_DIAG_DC(stream->ctx->dce_environment) == false) { + switch (stream->public.timing.display_color_depth) { + case COLOR_DEPTH_666: + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; + fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 0; + break; + case COLOR_DEPTH_888: + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; + fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 1; + break; + case COLOR_DEPTH_101010: + fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED = 1; + fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH = 2; + break; + default: + break; + } + fmt_bit_depth->flags.RGB_RANDOM = 1; + fmt_bit_depth->flags.HIGHPASS_RANDOM = 1; + fmt_bit_depth->flags.TRUNCATE_ENABLED = 1; + fmt_bit_depth->flags.TRUNCATE_DEPTH = 2; + } + + return; +} + +enum dc_status dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx) +{ + get_pixel_clock_parameters(pipe_ctx, &pipe_ctx->pix_clk_params); + pipe_ctx->clock_source->funcs->get_pix_clk_dividers( + pipe_ctx->clock_source, + &pipe_ctx->pix_clk_params, + &pipe_ctx->pll_settings); + dce110_resource_build_bit_depth_reduction_params(pipe_ctx->stream, + &pipe_ctx->stream->bit_depth_params); + pipe_ctx->stream->clamping.pixel_encoding = pipe_ctx->stream->public.timing.pixel_encoding; + + return DC_OK; +} + +static bool is_surface_pixel_format_supported(struct pipe_ctx *pipe_ctx, unsigned int underlay_idx) +{ + if (pipe_ctx->pipe_idx != underlay_idx) + return true; + if (!pipe_ctx->surface) + return false; + if (pipe_ctx->surface->public.format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + return true; +} + +static enum dc_status validate_mapped_resource( + const struct core_dc *dc, + struct validate_context *context) +{ + enum dc_status status = DC_OK; + uint8_t i, j, k; + + for (i = 0; i < context->target_count; i++) { + struct core_target *target = context->targets[i]; + + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + struct core_link *link = stream->sink->link; + + if (resource_is_stream_unchanged(dc->current_context, stream)) + continue; + + for (k = 0; k < MAX_PIPES; k++) { + struct pipe_ctx *pipe_ctx = + &context->res_ctx.pipe_ctx[k]; + + if (context->res_ctx.pipe_ctx[k].stream != stream) + continue; + + if (!is_surface_pixel_format_supported(pipe_ctx, + context->res_ctx.pool->underlay_pipe_index)) + return DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED; + + if (!pipe_ctx->tg->funcs->validate_timing( + pipe_ctx->tg, &stream->public.timing)) + return DC_FAIL_CONTROLLER_VALIDATE; + + status = dce110_resource_build_pipe_hw_param(pipe_ctx); + + if (status != DC_OK) + return status; + + if (!link->link_enc->funcs->validate_output_with_stream( + link->link_enc, + pipe_ctx)) + return DC_FAIL_ENC_VALIDATE; + + /* TODO: validate audio ASIC caps, encoder */ + + status = dc_link_validate_mode_timing(stream, + link, + &stream->public.timing); + + if (status != DC_OK) + return status; + + resource_build_info_frame(pipe_ctx); + + /* do not need to validate non root pipes */ + break; + } + } + } + + return DC_OK; +} + +enum dc_status dce110_validate_bandwidth( + const struct core_dc *dc, + struct validate_context *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + dm_logger_write( + dc->ctx->logger, LOG_BANDWIDTH_CALCS, + "%s: start", + __func__); + + if (!bw_calcs( + dc->ctx, + &dc->bw_dceip, + &dc->bw_vbios, + context->res_ctx.pipe_ctx, + context->res_ctx.pool->pipe_count, + &context->bw_results)) + result = DC_FAIL_BANDWIDTH_VALIDATE; + else + result = DC_OK; + + if (result == DC_FAIL_BANDWIDTH_VALIDATE) + dm_logger_write(dc->ctx->logger, LOG_BANDWIDTH_VALIDATION, + "%s: %dx%d@%d Bandwidth validation failed!\n", + __func__, + context->targets[0]->public.streams[0]->timing.h_addressable, + context->targets[0]->public.streams[0]->timing.v_addressable, + context->targets[0]->public.streams[0]->timing.pix_clk_khz); + + if (memcmp(&dc->current_context->bw_results, + &context->bw_results, sizeof(context->bw_results))) { + struct log_entry log_entry; + dm_logger_open( + dc->ctx->logger, + &log_entry, + LOG_BANDWIDTH_CALCS); + dm_logger_append(&log_entry, "%s: finish,\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + __func__, + context->bw_results.nbp_state_change_wm_ns[0].b_mark, + context->bw_results.nbp_state_change_wm_ns[0].a_mark, + context->bw_results.urgent_wm_ns[0].b_mark, + context->bw_results.urgent_wm_ns[0].a_mark, + context->bw_results.stutter_exit_wm_ns[0].b_mark, + context->bw_results.stutter_exit_wm_ns[0].a_mark); + dm_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + context->bw_results.nbp_state_change_wm_ns[1].b_mark, + context->bw_results.nbp_state_change_wm_ns[1].a_mark, + context->bw_results.urgent_wm_ns[1].b_mark, + context->bw_results.urgent_wm_ns[1].a_mark, + context->bw_results.stutter_exit_wm_ns[1].b_mark, + context->bw_results.stutter_exit_wm_ns[1].a_mark); + dm_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n", + context->bw_results.nbp_state_change_wm_ns[2].b_mark, + context->bw_results.nbp_state_change_wm_ns[2].a_mark, + context->bw_results.urgent_wm_ns[2].b_mark, + context->bw_results.urgent_wm_ns[2].a_mark, + context->bw_results.stutter_exit_wm_ns[2].b_mark, + context->bw_results.stutter_exit_wm_ns[2].a_mark, + context->bw_results.stutter_mode_enable); + dm_logger_append(&log_entry, + "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" + "sclk: %d sclk_sleep: %d yclk: %d blackout_recovery_time_us: %d\n", + context->bw_results.cpuc_state_change_enable, + context->bw_results.cpup_state_change_enable, + context->bw_results.nbp_state_change_enable, + context->bw_results.all_displays_in_sync, + context->bw_results.dispclk_khz, + context->bw_results.required_sclk, + context->bw_results.required_sclk_deep_sleep, + context->bw_results.required_yclk, + context->bw_results.blackout_recovery_time_us); + dm_logger_close(&log_entry); + } + return result; +} + +static bool dce110_validate_surface_sets( + const struct dc_validation_set set[], + int set_count) +{ + int i; + + for (i = 0; i < set_count; i++) { + if (set[i].surface_count == 0) + continue; + + if (set[i].surface_count > 2) + return false; + + if (set[i].surfaces[0]->src_rect.width + != set[i].target->streams[0]->src.width + || set[i].surfaces[0]->src_rect.height + != set[i].target->streams[0]->src.height) + return false; + if (set[i].surfaces[0]->format + >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + + if (set[i].surface_count == 2) { + if (set[i].surfaces[1]->format + < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) + return false; + if (set[i].surfaces[1]->src_rect.width > 1920 + || set[i].surfaces[1]->src_rect.height > 1080) + return false; + + if (set[i].target->streams[0]->timing.pixel_encoding != PIXEL_ENCODING_RGB) + return false; + } + } + + return true; +} + +enum dc_status dce110_validate_with_context( + const struct core_dc *dc, + const struct dc_validation_set set[], + int set_count, + struct validate_context *context) +{ + struct dc_context *dc_ctx = dc->ctx; + enum dc_status result = DC_ERROR_UNEXPECTED; + int i; + + if (!dce110_validate_surface_sets(set, set_count)) + return DC_FAIL_SURFACE_VALIDATE; + + context->res_ctx.pool = dc->res_pool; + + for (i = 0; i < set_count; i++) { + context->targets[i] = DC_TARGET_TO_CORE(set[i].target); + dc_target_retain(&context->targets[i]->public); + context->target_count++; + } + + result = resource_map_pool_resources(dc, context); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, context); + + if (!resource_validate_attach_surfaces( + set, set_count, dc->current_context, context)) { + DC_ERROR("Failed to attach surface to target!\n"); + return DC_FAIL_ATTACH_SURFACES; + } + + if (result == DC_OK) + result = validate_mapped_resource(dc, context); + + if (result == DC_OK) + result = resource_build_scaling_params_for_context(dc, context); + + if (result == DC_OK) + result = dce110_validate_bandwidth(dc, context); + + return result; +} + +enum dc_status dce110_validate_guaranteed( + const struct core_dc *dc, + const struct dc_target *dc_target, + struct validate_context *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + + context->res_ctx.pool = dc->res_pool; + + context->targets[0] = DC_TARGET_TO_CORE(dc_target); + dc_target_retain(&context->targets[0]->public); + context->target_count++; + + result = resource_map_pool_resources(dc, context); + + if (result == DC_OK) + result = resource_map_clock_resources(dc, context); + + if (result == DC_OK) + result = validate_mapped_resource(dc, context); + + if (result == DC_OK) { + validate_guaranteed_copy_target( + context, dc->public.caps.max_targets); + result = resource_build_scaling_params_for_context(dc, context); + } + + if (result == DC_OK) + result = dce110_validate_bandwidth(dc, context); + + return result; +} + +static struct pipe_ctx *dce110_acquire_idle_pipe_for_layer( + struct resource_context *res_ctx, + struct core_stream *stream) +{ + unsigned int underlay_idx = res_ctx->pool->underlay_pipe_index; + struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[underlay_idx]; + + if (res_ctx->pipe_ctx[underlay_idx].stream) { + return NULL; + } + + pipe_ctx->tg = res_ctx->pool->timing_generators[underlay_idx]; + pipe_ctx->mi = res_ctx->pool->mis[underlay_idx]; + /*pipe_ctx->ipp = res_ctx->pool->ipps[underlay_idx];*/ + pipe_ctx->xfm = res_ctx->pool->transforms[underlay_idx]; + pipe_ctx->opp = res_ctx->pool->opps[underlay_idx]; + pipe_ctx->dis_clk = res_ctx->pool->display_clock; + pipe_ctx->pipe_idx = underlay_idx; + + pipe_ctx->stream = stream; + + return pipe_ctx; + +} + +static void dce110_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + destruct(dce110_pool); + dm_free(dce110_pool); + *pool = NULL; +} + + +static const struct resource_funcs dce110_res_pool_funcs = { + .destroy = dce110_destroy_resource_pool, + .link_enc_create = dce110_link_encoder_create, + .validate_with_context = dce110_validate_with_context, + .validate_guaranteed = dce110_validate_guaranteed, + .validate_bandwidth = dce110_validate_bandwidth, + .acquire_idle_pipe_for_layer = dce110_acquire_idle_pipe_for_layer, + .build_bit_depth_reduction_params = + dce110_resource_build_bit_depth_reduction_params +}; + +static void underlay_create(struct dc_context *ctx, struct resource_pool *pool) +{ + struct dce110_timing_generator *dce110_tgv = dm_alloc(sizeof (*dce110_tgv)); + struct dce_transform *dce110_xfmv = dm_alloc(sizeof (*dce110_xfmv)); + struct dce110_mem_input *dce110_miv = dm_alloc(sizeof (*dce110_miv)); + struct dce110_opp *dce110_oppv = dm_alloc(sizeof (*dce110_oppv)); + + dce110_opp_v_construct(dce110_oppv, ctx); + dce110_timing_generator_v_construct(dce110_tgv, ctx); + dce110_mem_input_v_construct(dce110_miv, ctx); + dce110_transform_v_construct(dce110_xfmv, ctx); + + pool->opps[pool->pipe_count] = &dce110_oppv->base; + pool->timing_generators[pool->pipe_count] = &dce110_tgv->base; + pool->mis[pool->pipe_count] = &dce110_miv->base; + pool->transforms[pool->pipe_count] = &dce110_xfmv->base; + pool->pipe_count++; + + /* update the public caps to indicate an underlay is available */ + ctx->dc->caps.max_slave_planes = 1; + ctx->dc->caps.max_slave_planes = 1; +} + +static void bw_calcs_data_update_from_pplib(struct core_dc *dc) +{ + struct dm_pp_clock_levels clks = {0}; + + /*do system clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &clks); + /* convert all the clock fro kHz to fix point mHz */ + dc->bw_vbios.high_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios.mid1_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels/8], 1000); + dc->bw_vbios.mid2_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*2/8], 1000); + dc->bw_vbios.mid3_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*3/8], 1000); + dc->bw_vbios.mid4_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*4/8], 1000); + dc->bw_vbios.mid5_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*5/8], 1000); + dc->bw_vbios.mid6_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels*6/8], 1000); + dc->bw_vbios.low_sclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + dc->sclk_lvls = clks; + + /*do display clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_DISPLAY_CLK, + &clks); + dc->bw_vbios.high_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1], 1000); + dc->bw_vbios.mid_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1], 1000); + dc->bw_vbios.low_voltage_max_dispclk = bw_frc_to_fixed( + clks.clocks_in_khz[0], 1000); + + /*do memory clock*/ + dm_pp_get_clock_levels_by_type( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &clks); + + dc->bw_vbios.low_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[0] * MEMORY_TYPE_MULTIPLIER, 1000); + dc->bw_vbios.mid_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels>>1] * MEMORY_TYPE_MULTIPLIER, + 1000); + dc->bw_vbios.high_yclk = bw_frc_to_fixed( + clks.clocks_in_khz[clks.num_levels-1] * MEMORY_TYPE_MULTIPLIER, + 1000); +} + +enum clocks_state dce110_resource_convert_clock_state_pp_to_dc( + enum dm_pp_clocks_state pp_clock_state) +{ + enum clocks_state dc_clocks_state = CLOCKS_STATE_INVALID; + + switch (pp_clock_state) { + case DM_PP_CLOCKS_STATE_INVALID: + dc_clocks_state = CLOCKS_STATE_INVALID; + break; + case DM_PP_CLOCKS_STATE_ULTRA_LOW: + dc_clocks_state = CLOCKS_STATE_ULTRA_LOW; + break; + case DM_PP_CLOCKS_STATE_LOW: + dc_clocks_state = CLOCKS_STATE_LOW; + break; + case DM_PP_CLOCKS_STATE_NOMINAL: + dc_clocks_state = CLOCKS_STATE_NOMINAL; + break; + case DM_PP_CLOCKS_STATE_PERFORMANCE: + dc_clocks_state = CLOCKS_STATE_PERFORMANCE; + break; + case DM_PP_CLOCKS_DPM_STATE_LEVEL_4: + dc_clocks_state = CLOCKS_DPM_STATE_LEVEL_4; + break; + case DM_PP_CLOCKS_DPM_STATE_LEVEL_5: + dc_clocks_state = CLOCKS_DPM_STATE_LEVEL_5; + break; + case DM_PP_CLOCKS_DPM_STATE_LEVEL_6: + dc_clocks_state = CLOCKS_DPM_STATE_LEVEL_6; + break; + case DM_PP_CLOCKS_DPM_STATE_LEVEL_7: + dc_clocks_state = CLOCKS_DPM_STATE_LEVEL_7; + break; + default: + dc_clocks_state = CLOCKS_STATE_INVALID; + break; + } + + return dc_clocks_state; +} + +const struct resource_caps *dce110_resource_cap( + struct hw_asic_id *asic_id) +{ + if (ASIC_REV_IS_STONEY(asic_id->hw_internal_rev)) + return &stoney_resource_cap; + else + return &carrizo_resource_cap; +} + +static bool construct( + uint8_t num_virtual_links, + struct core_dc *dc, + struct dce110_resource_pool *pool, + struct hw_asic_id asic_id) +{ + unsigned int i; + struct dc_context *ctx = dc->ctx; + struct firmware_info info; + struct dc_bios *bp; + struct dm_pp_static_clock_info static_clk_info = {0}; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = dce110_resource_cap(&ctx->asic_id); + pool->base.funcs = &dce110_res_pool_funcs; + + /************************************************* + * Resource + asic cap harcoding * + *************************************************/ + + pool->base.pipe_count = pool->base.res_cap->num_timing_generator; + pool->base.underlay_pipe_index = pool->base.pipe_count; + + dc->public.caps.max_downscale_ratio = 150; + dc->public.caps.i2c_speed_in_khz = 100; + + /************************************************* + * Create resources * + *************************************************/ + + bp = ctx->dc_bios; + + if ((bp->funcs->get_firmware_info(bp, &info) == BP_RESULT_OK) && + info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + + pool->base.clock_sources[0] = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[1] = + dce110_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, + &clk_src_regs[1], false); + + pool->base.clk_src_count = 2; + + /* TODO: find out if CZ support 3 PLLs */ + } + + if (pool->base.dp_clock_source == NULL) { + dm_error("DC: failed to create dp clock source!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + for (i = 0; i < pool->base.clk_src_count; i++) { + if (pool->base.clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + } + + pool->base.display_clock = dal_display_clock_dce110_create(ctx); + if (pool->base.display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + /* get static clock information for PPLIB or firmware, save + * max_clock_state + */ + if (dm_pp_get_static_clocks(ctx, &static_clk_info)) { + enum clocks_state max_clocks_state = + dce110_resource_convert_clock_state_pp_to_dc( + static_clk_info.max_clocks_state); + + dal_display_clock_store_max_clocks_state( + pool->base.display_clock, max_clocks_state); + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce110_create(&init_data); + if (!pool->base.irqs) + goto res_create_fail; + } + + for (i = 0; i < pool->base.pipe_count; i++) { + pool->base.timing_generators[i] = dce110_timing_generator_create( + ctx, i, &dce110_tg_offsets[i]); + if (pool->base.timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto res_create_fail; + } + + pool->base.mis[i] = dce110_mem_input_create(ctx, i, + &dce110_mi_reg_offsets[i]); + if (pool->base.mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto res_create_fail; + } + + pool->base.ipps[i] = dce110_ipp_create(ctx, i, &dce110_ipp_reg_offsets[i]); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto res_create_fail; + } + + pool->base.transforms[i] = dce110_transform_create(ctx, i); + if (pool->base.transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto res_create_fail; + } + + pool->base.opps[i] = dce110_opp_create(ctx, i, &dce110_opp_reg_offsets[i]); + if (pool->base.opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto res_create_fail; + } + } + + underlay_create(ctx, &pool->base); + + if (!resource_construct(num_virtual_links, dc, &pool->base, + &res_create_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + if (!dce110_hw_sequencer_construct(dc)) + goto res_create_fail; + + if (ASIC_REV_IS_STONEY(ctx->asic_id.hw_internal_rev)) + bw_calcs_init(&dc->bw_dceip, &dc->bw_vbios, BW_CALCS_VERSION_STONEY); + else + bw_calcs_init(&dc->bw_dceip, &dc->bw_vbios, BW_CALCS_VERSION_CARRIZO); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +res_create_fail: + destruct(pool); + return false; +} + +struct resource_pool *dce110_create_resource_pool( + uint8_t num_virtual_links, + struct core_dc *dc, + struct hw_asic_id asic_id) +{ + struct dce110_resource_pool *pool = + dm_alloc(sizeof(struct dce110_resource_pool)); + + if (!pool) + return NULL; + + if (construct(num_virtual_links, dc, pool, asic_id)) + return &pool->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h new file mode 100644 index 000000000000..535623aa0052 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_resource.h @@ -0,0 +1,56 @@ +/* +* 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_RESOURCE_DCE110_H__ +#define __DC_RESOURCE_DCE110_H__ + +#include "core_types.h" + +struct core_dc; +struct resource_pool; + +#define TO_DCE110_RES_POOL(pool)\ + container_of(pool, struct dce110_resource_pool, base) + +struct dce110_resource_pool { + struct resource_pool base; +}; + +enum dc_status dce110_resource_build_pipe_hw_param(struct pipe_ctx *pipe_ctx); + +enum clocks_state dce110_resource_convert_clock_state_pp_to_dc( + enum dm_pp_clocks_state pp_clock_state); + +void dce110_resource_build_bit_depth_reduction_params( + const struct core_stream *stream, + struct bit_depth_reduction_params *fmt_bit_depth); + +struct resource_pool *dce110_create_resource_pool( + uint8_t num_virtual_links, + struct core_dc *dc, + struct hw_asic_id asic_id); + +#endif /* __DC_RESOURCE_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c new file mode 100644 index 000000000000..b1c97125f6fb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.c @@ -0,0 +1,1953 @@ +/* + * 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 "dc_bios_types.h" +#include "dc.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce110_timing_generator.h" + +#include "timing_generator.h" + + +#define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 + +#define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) +#define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) + +#define CRTC_REG(reg) (reg + tg110->offsets.crtc) +#define DCP_REG(reg) (reg + tg110->offsets.dcp) + +/* Flowing register offsets are same in files of + * dce/dce_11_0_d.h + * dce/vi_polaris10_p/vi_polaris10_d.h + * + * So we can create dce110 timing generator to use it. + */ + + +/* +* apply_front_porch_workaround +* +* This is a workaround for a bug that has existed since R5xx and has not been +* fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. +*/ +static void dce110_timing_generator_apply_front_porch_workaround( + struct timing_generator *tg, + struct dc_crtc_timing *timing) +{ + if (timing->flags.INTERLACE == 1) { + if (timing->v_front_porch < 2) + timing->v_front_porch = 2; + } else { + if (timing->v_front_porch < 1) + timing->v_front_porch = 1; + } +} + +/** + ***************************************************************************** + * Function: is_in_vertical_blank + * + * @brief + * check the current status of CRTC to check if we are in Vertical Blank + * regioneased" state + * + * @return + * true if currently in blank region, false otherwise + * + ***************************************************************************** + */ +static bool dce110_timing_generator_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + addr = CRTC_REG(mmCRTC_STATUS); + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + +void dce110_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = CRTC_REG(mmCRTC_CONTROL); + + regval = dm_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTC_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dm_write_reg(tg->ctx, address, regval); +} + +/** + * Enable CRTC + * Enable CRTC - call ASIC Control Object to enable Timing generator. + */ +bool dce110_timing_generator_enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + /* + * 3 is used to make sure V_UPDATE occurs at the beginning of the first + * line of vertical front porch + */ + set_reg_field_value( + value, + 0, + CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_MODE), value); + + /* TODO: may want this on to catch underflow */ + value = 0; + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_MASTER_UPDATE_LOCK), value); + + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); + + return result == BP_RESULT_OK; +} + +void dce110_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_BLACK_COLOR); + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); +} + +/** + ***************************************************************************** + * Function: disable_stereo + * + * @brief + * Disables active stereo on controller + * Frame Packing need to be disabled in vBlank or when CRTC not running + ***************************************************************************** + */ +#if 0 +@TODOSTEREO +static void disable_stereo(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_3D_STRUCTURE_CONTROL); + uint32_t value = 0; + uint32_t test = 0; + uint32_t field = 0; + uint32_t struc_en = 0; + uint32_t struc_stereo_sel_ovr = 0; + + value = dm_read_reg(tg->ctx, addr); + struc_en = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_EN); + + struc_stereo_sel_ovr = get_reg_field_value( + value, + CRTC_3D_STRUCTURE_CONTROL, + CRTC_3D_STRUCTURE_STEREO_SEL_OVR); + + /* + * When disabling Frame Packing in 2 step mode, we need to program both + * registers at the same frame + * Programming it in the beginning of VActive makes sure we are ok + */ + + if (struc_en != 0 && struc_stereo_sel_ovr == 0) { + tg->funcs->wait_for_vblank(tg); + tg->funcs->wait_for_vactive(tg); + } + + value = 0; + dm_write_reg(tg->ctx, addr, value); + + addr = tg->regs[IDX_CRTC_STEREO_CONTROL]; + dm_write_reg(tg->ctx, addr, value); +} +#endif + +/** + * disable_crtc - call ASIC Control Object to disable Timing generator. + */ +bool dce110_timing_generator_disable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, false); + + /* Need to make sure stereo is disabled according to the DCE5.0 spec */ + + /* + * @TODOSTEREO call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + + return result == BP_RESULT_OK; +} + +/** +* program_horz_count_by_2 +* Programs DxCRTC_HORZ_COUNT_BY2_EN - 1 for DVI 30bpp mode, 0 otherwise +* +*/ +static void program_horz_count_by_2( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t regval; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + regval = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_COUNT_CONTROL)); + + set_reg_field_value(regval, 0, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + if (timing->flags.HORZ_COUNT_BY_TWO) + set_reg_field_value(regval, 1, CRTC_COUNT_CONTROL, + CRTC_HORZ_COUNT_BY2_EN); + + dm_write_reg(tg->ctx, + CRTC_REG(mmCRTC_COUNT_CONTROL), regval); +} + +/** + * program_timing_generator + * Program CRTC Timing Registers - DxCRTC_H_*, DxCRTC_V_*, Pixel repetition. + * Call ASIC Control Object to program Timings. + */ +bool dce110_timing_generator_program_timing_generator( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing) +{ + enum bp_result result; + struct bp_hw_crtc_timing_parameters bp_params; + struct dc_crtc_timing patched_crtc_timing; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + uint32_t vsync_offset = dc_crtc_timing->v_border_bottom + + dc_crtc_timing->v_front_porch; + uint32_t v_sync_start =dc_crtc_timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = dc_crtc_timing->h_border_right + + dc_crtc_timing->h_front_porch; + uint32_t h_sync_start = dc_crtc_timing->h_addressable + hsync_offset; + + memset(&bp_params, 0, sizeof(struct bp_hw_crtc_timing_parameters)); + + /* Due to an asic bug we need to apply the Front Porch workaround prior + * to programming the timing. + */ + + patched_crtc_timing = *dc_crtc_timing; + + dce110_timing_generator_apply_front_porch_workaround(tg, &patched_crtc_timing); + + bp_params.controller_id = tg110->controller_id; + + bp_params.h_total = patched_crtc_timing.h_total; + bp_params.h_addressable = + patched_crtc_timing.h_addressable; + bp_params.v_total = patched_crtc_timing.v_total; + bp_params.v_addressable = patched_crtc_timing.v_addressable; + + bp_params.h_sync_start = h_sync_start; + bp_params.h_sync_width = patched_crtc_timing.h_sync_width; + bp_params.v_sync_start = v_sync_start; + bp_params.v_sync_width = patched_crtc_timing.v_sync_width; + + /* Set overscan */ + bp_params.h_overscan_left = + patched_crtc_timing.h_border_left; + bp_params.h_overscan_right = + patched_crtc_timing.h_border_right; + bp_params.v_overscan_top = patched_crtc_timing.v_border_top; + bp_params.v_overscan_bottom = + patched_crtc_timing.v_border_bottom; + + /* Set flags */ + if (patched_crtc_timing.flags.HSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.HSYNC_POSITIVE_POLARITY = 1; + + if (patched_crtc_timing.flags.VSYNC_POSITIVE_POLARITY == 1) + bp_params.flags.VSYNC_POSITIVE_POLARITY = 1; + + if (patched_crtc_timing.flags.INTERLACE == 1) + bp_params.flags.INTERLACE = 1; + + if (patched_crtc_timing.flags.HORZ_COUNT_BY_TWO == 1) + bp_params.flags.HORZ_COUNT_BY_TWO = 1; + + result = tg->bp->funcs->program_crtc_timing(tg->bp, &bp_params); + + program_horz_count_by_2(tg, &patched_crtc_timing); + + tg110->base.funcs->enable_advanced_request(tg, true, &patched_crtc_timing); + + /* Enable stereo - only when we need to pack 3D frame. Other types + * of stereo handled in explicit call */ + + return result == BP_RESULT_OK; +} + +/** + ***************************************************************************** + * Function: set_drr + * + * @brief + * Program dynamic refresh rate registers m_DxCRTC_V_TOTAL_*. + * + * @param [in] pHwCrtcTiming: point to H + * wCrtcTiming struct + ***************************************************************************** + */ +void dce110_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + /* register values */ + uint32_t v_total_min = 0; + uint32_t v_total_max = 0; + uint32_t v_total_cntl = 0; + uint32_t static_screen_cntl = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + uint32_t addr = 0; + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + v_total_min = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + v_total_max = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_V_TOTAL_CONTROL); + v_total_cntl = dm_read_reg(tg->ctx, addr); + + addr = CRTC_REG(mmCRTC_STATIC_SCREEN_CONTROL); + static_screen_cntl = dm_read_reg(tg->ctx, addr); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + set_reg_field_value(v_total_max, + params->vertical_total_max - 1, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + + set_reg_field_value(v_total_min, + params->vertical_total_min - 1, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + + set_reg_field_value(v_total_cntl, + 1, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK_EN); + + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + + set_reg_field_value(static_screen_cntl, + 0x180, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + } else { + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_SET_V_TOTAL_MIN_MASK); + set_reg_field_value(static_screen_cntl, + 0, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + set_reg_field_value(v_total_min, + 0, + CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN); + set_reg_field_value(v_total_max, + 0, + CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MIN_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_V_TOTAL_MAX_SEL); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_ON_EVENT); + set_reg_field_value(v_total_cntl, + 0, + CRTC_V_TOTAL_CONTROL, + CRTC_FORCE_LOCK_TO_MASTER_VSYNC); + } + + addr = CRTC_REG(mmCRTC_V_TOTAL_MIN); + dm_write_reg(tg->ctx, addr, v_total_min); + + addr = CRTC_REG(mmCRTC_V_TOTAL_MAX); + dm_write_reg(tg->ctx, addr, v_total_max); + + addr = CRTC_REG(mmCRTC_V_TOTAL_CONTROL); + dm_write_reg(tg->ctx, addr, v_total_cntl); + + addr = CRTC_REG(mmCRTC_STATIC_SCREEN_CONTROL); + dm_write_reg(tg->ctx, addr, static_screen_cntl); +} + +void dce110_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t value) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t static_screen_cntl = 0; + uint32_t addr = 0; + + addr = CRTC_REG(mmCRTC_STATIC_SCREEN_CONTROL); + static_screen_cntl = dm_read_reg(tg->ctx, addr); + + set_reg_field_value(static_screen_cntl, + value, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK); + + set_reg_field_value(static_screen_cntl, + 2, + CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_FRAME_COUNT); + + dm_write_reg(tg->ctx, addr, static_screen_cntl); +} + +/* + * get_vblank_counter + * + * @brief + * Get counter for vertical blanks. use register CRTC_STATUS_FRAME_COUNT which + * holds the counter of frames. + * + * @param + * struct timing_generator *tg - [in] timing generator which controls the + * desired CRTC + * + * @return + * Counter of frames, which should equal to number of vblanks. + */ +uint32_t dce110_timing_generator_get_vblank_counter(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_STATUS_FRAME_COUNT); + uint32_t value = dm_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +/** + ***************************************************************************** + * Function: dce110_get_crtc_positions + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] v_position, h_position + ***************************************************************************** + */ + +void dce110_timing_generator_get_crtc_positions( + struct timing_generator *tg, + int32_t *h_position, + int32_t *v_position) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_STATUS_POSITION)); + + *h_position = get_reg_field_value( + value, + CRTC_STATUS_POSITION, + CRTC_HORZ_COUNT); + + *v_position = get_reg_field_value( + value, + CRTC_STATUS_POSITION, + CRTC_VERT_COUNT); +} + +/** + ***************************************************************************** + * Function: get_crtc_scanoutpos + * + * @brief + * Returns CRTC vertical/horizontal counters + * + * @param [out] vpos, hpos + ***************************************************************************** + */ +uint32_t dce110_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *vbl, + uint32_t *position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + /* TODO 1: Update the implementation once caller is updated + * WARNING!! This function is returning the whole register value + * because the caller is expecting it instead of proper vertical and + * horizontal position. This should be a temporary implementation + * until the caller is updated. */ + + /* TODO 2: re-use dce110_timing_generator_get_crtc_positions() */ + + *vbl = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_BLANK_START_END)); + + *position = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_STATUS_POSITION)); + + /* @TODO: return value should indicate if current + * crtc is inside vblank*/ + return 0; +} + +/* TODO: is it safe to assume that mask/shift of Primary and Underlay + * are the same? + * For example: today CRTC_H_TOTAL == CRTCV_H_TOTAL but is it always + * guaranteed? */ +void dce110_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start =timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = CRTC_REG(mmCRTC_H_TOTAL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTC_H_TOTAL, + CRTC_H_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_TOTAL); + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_H_BLANK_START_END); + value = dm_read_reg(ctx, addr); + + tmp = timing->h_total - + (h_sync_start + timing->h_border_left); + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + set_reg_field_value( + value, + tmp, + CRTC_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_V_BLANK_START_END); + value = dm_read_reg(ctx, addr); + + tmp = timing->v_total - (v_sync_start + timing->v_border_top); + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + set_reg_field_value( + value, + tmp, + CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dm_write_reg(ctx, addr, value); +} + +void dce110_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value; + uint32_t addr; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + enum test_pattern_color_format bit_depth; + enum test_pattern_dyn_range dyn_range; + enum test_pattern_mode mode; + /* color ramp generator mixes 16-bits color */ + uint32_t src_bpc = 16; + /* requested bpc */ + uint32_t dst_bpc; + uint32_t index; + /* RGB values of the color bars. + * Produce two RGB colors: RGB0 - white (all Fs) + * and RGB1 - black (all 0s) + * (three RGB components for two colors) + */ + uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0000}; + /* dest color (converted to the specified color format) */ + uint16_t dst_color[6]; + uint32_t inc_base; + + /* translate to bit depth */ + switch (color_depth) { + case COLOR_DEPTH_666: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; + break; + case COLOR_DEPTH_888: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + case COLOR_DEPTH_101010: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; + break; + case COLOR_DEPTH_121212: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; + break; + default: + bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; + break; + } + + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + { + dyn_range = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? + TEST_PATTERN_DYN_RANGE_CEA : + TEST_PATTERN_DYN_RANGE_VESA); + mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + + dm_write_reg(ctx, addr, value); + + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + dyn_range, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + dm_write_reg(ctx, addr, value); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + { + mode = (test_pattern == + CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? + TEST_PATTERN_MODE_VERTICALBARS : + TEST_PATTERN_MODE_HORIZONTALBARS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* adjust color to the required colorFormat */ + for (index = 0; index < 6; index++) { + /* dst = 2^dstBpc * src / 2^srcBpc = src >> + * (srcBpc - dstBpc); + */ + dst_color[index] = + src_color[index] >> (src_bpc - dst_bpc); + /* CRTC_TEST_PATTERN_DATA has 16 bits, + * lowest 6 are hardwired to ZERO + * color bits should be left aligned aligned to MSB + * XXXXXXXXXX000000 for 10 bit, + * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 + */ + dst_color[index] <<= (16 - dst_bpc); + } + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + dm_write_reg(ctx, addr, value); + + /* We have to write the mask before data, similar to pipeline. + * For example, for 8 bpc, if we want RGB0 to be magenta, + * and RGB1 to be cyan, + * we need to make 7 writes: + * MASK DATA + * 000001 00000000 00000000 set mask to R0 + * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 + * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 + * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 + * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 + * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 + * 100000 11111111 00000000 B1 255, 0xFF00 + * + * we will make a loop of 6 in which we prepare the mask, + * then write, then prepare the color for next write. + * first iteration will write mask only, + * but each next iteration color prepared in + * previous iteration will be written within new mask, + * the last component will written separately, + * mask is not changing between 6th and 7th write + * and color will be prepared by last iteration + */ + + /* write color, color values mask in CRTC_TEST_PATTERN_MASK + * is B1, G1, R1, B0, G0, R0 + */ + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_COLOR); + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + set_reg_field_value( + value, + (1 << index), + CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_MASK); + /* write color component */ + dm_write_reg(ctx, addr, value); + /* prepare next color component, + * will be written in the next iteration + */ + set_reg_field_value( + value, + dst_color[index], + CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_DATA); + } + /* write last color component, + * it's been already prepared in the loop + */ + dm_write_reg(ctx, addr, value); + + /* enable test pattern */ + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + + dm_write_reg(ctx, addr, value); + } + break; + + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + { + mode = (bit_depth == + TEST_PATTERN_COLOR_FORMAT_BPC_10 ? + TEST_PATTERN_MODE_DUALRAMP_RGB : + TEST_PATTERN_MODE_SINGLERAMP_RGB); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + dst_bpc = 6; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + dst_bpc = 8; + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + dst_bpc = 10; + break; + default: + dst_bpc = 8; + break; + } + + /* increment for the first ramp for one color gradation + * 1 gradation for 6-bit color is 2^10 + * gradations in 16-bit color + */ + inc_base = (src_bpc - dst_bpc); + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 8, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + set_reg_field_value( + value, + inc_base, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0); + set_reg_field_value( + value, + inc_base + 2, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC1); + set_reg_field_value( + value, + 8, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_HRES); + set_reg_field_value( + value, + 5, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES); + set_reg_field_value( + value, + 384 << 6, + CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_RAMP0_OFFSET); + } + break; + default: + break; + } + dm_write_reg(ctx, addr, value); + + value = 0; + addr = CRTC_REG(mmCRTC_TEST_PATTERN_COLOR); + dm_write_reg(ctx, addr, value); + + /* enable test pattern */ + addr = CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL); + value = 0; + + set_reg_field_value( + value, + 1, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN); + + set_reg_field_value( + value, + mode, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_MODE); + + set_reg_field_value( + value, + 0, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_DYNAMIC_RANGE); + /* add color depth translation here */ + set_reg_field_value( + value, + bit_depth, + CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_COLOR_FORMAT); + + dm_write_reg(ctx, addr, value); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + value = 0; + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_CONTROL), value); + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_COLOR), value); + dm_write_reg(ctx, CRTC_REG(mmCRTC_TEST_PATTERN_PARAMETERS), + value); + } + break; + default: + break; + } +} + +/** +* dce110_timing_generator_validate_timing +* The timing generators support a maximum display size of is 8192 x 8192 pixels, +* including both active display and blanking periods. Check H Total and V Total. +*/ +bool dce110_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal) +{ + uint32_t h_blank; + uint32_t h_back_porch; + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + ASSERT(timing != NULL); + + if (!timing) + return false; + + /* Check maximum number of pixels supported by Timing Generator + * (Currently will never fail, in order to fail needs display which + * needs more than 8192 horizontal and + * more than 8192 vertical total pixels) + */ + if (timing->h_total > tg110->max_h_total || + timing->v_total > tg110->max_v_total) + return false; + + h_blank = (timing->h_total - timing->h_addressable - + timing->h_border_right - + timing->h_border_left); + + if (h_blank < tg110->min_h_blank) + return false; + + if (timing->h_front_porch < tg110->min_h_front_porch) + return false; + + h_back_porch = h_blank - (h_sync_start - + timing->h_addressable - + timing->h_border_right - + timing->h_sync_width); + + if (h_back_porch < tg110->min_h_back_porch) + return false; + + return true; +} + +/** +* Wait till we are at the beginning of VBlank. +*/ +void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/** +* Wait till we are in VActive (anywhere in VActive) +*/ +void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + while (dce110_timing_generator_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/** + ***************************************************************************** + * Function: dce110_timing_generator_setup_global_swap_lock + * + * @brief + * Setups Global Swap Lock group for current pipe + * Pipe can join or leave GSL group, become a TimingServer or TimingClient + * + * @param [in] gsl_params: setup data + ***************************************************************************** + */ + +void dce110_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = DCP_REG(mmDCP_GSL_CONTROL); + uint32_t check_point = FLIP_READY_BACK_LOOKUP; + + value = dm_read_reg(tg->ctx, address); + + /* This pipe will belong to GSL Group zero. */ + set_reg_field_value(value, + 1, + DCP_GSL_CONTROL, + DCP_GSL0_EN); + + set_reg_field_value(value, + gsl_params->gsl_master == tg->inst, + DCP_GSL_CONTROL, + DCP_GSL_MASTER_EN); + + set_reg_field_value(value, + HFLIP_READY_DELAY, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_FORCE_DELAY); + + /* Keep signal low (pending high) during 6 lines. + * Also defines minimum interval before re-checking signal. */ + set_reg_field_value(value, + HFLIP_CHECK_DELAY, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_CHECK_DELAY); + + + { + uint32_t value_crtc_vtotal; + + value_crtc_vtotal = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_TOTAL)); + + set_reg_field_value(value, + 0,/* DCP_GSL_PURPOSE_SURFACE_FLIP */ + DCP_GSL_CONTROL, + DCP_GSL_SYNC_SOURCE); + + /* Checkpoint relative to end of frame */ + check_point = get_reg_field_value(value_crtc_vtotal, + CRTC_V_TOTAL, + CRTC_V_TOTAL); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_GSL_WINDOW), 0); + } + + set_reg_field_value(value, + 1, + DCP_GSL_CONTROL, + DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); + + dm_write_reg(tg->ctx, address, value); + + /********************************************************************/ + address = CRTC_REG(mmCRTC_GSL_CONTROL); + + value = 0; + set_reg_field_value(value, + check_point - FLIP_READY_BACK_LOOKUP, + CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM); + + set_reg_field_value(value, + VFLIP_READY_DELAY, + CRTC_GSL_CONTROL, + CRTC_GSL_FORCE_DELAY); + + dm_write_reg(tg->ctx, address, value); +} + +void dce110_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + /* Clear all the register writes done by + * dce110_timing_generator_setup_global_swap_lock + */ + + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t address = DCP_REG(mmDCP_GSL_CONTROL); + + value = 0; + + /* This pipe will belong to GSL Group zero. */ + /* Settig HW default values from reg specs */ + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL0_EN); + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_MASTER_EN); + + set_reg_field_value(value, + 0x2, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_FORCE_DELAY); + + set_reg_field_value(value, + 0x6, + DCP_GSL_CONTROL, + DCP_GSL_HSYNC_FLIP_CHECK_DELAY); + + /* Restore DCP_GSL_PURPOSE_SURFACE_FLIP */ + { + uint32_t value_crtc_vtotal; + + value_crtc_vtotal = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_TOTAL)); + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_SYNC_SOURCE); + } + + set_reg_field_value(value, + 0, + DCP_GSL_CONTROL, + DCP_GSL_DELAY_SURFACE_UPDATE_PENDING); + + dm_write_reg(tg->ctx, address, value); + + /********************************************************************/ + address = CRTC_REG(mmCRTC_GSL_CONTROL); + + value = 0; + set_reg_field_value(value, + 0, + CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM); + + set_reg_field_value(value, + 0x2, + CRTC_GSL_CONTROL, + CRTC_GSL_FORCE_DELAY); + + dm_write_reg(tg->ctx, address, value); +} +/** + ***************************************************************************** + * Function: is_counter_moving + * + * @brief + * check if the timing generator is currently going + * + * @return + * true if currently going, false if currently paused or stopped. + * + ***************************************************************************** + */ +bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg) +{ + uint32_t h1 = 0; + uint32_t h2 = 0; + uint32_t v1 = 0; + uint32_t v2 = 0; + + tg->funcs->get_position(tg, &h1, &v1); + tg->funcs->get_position(tg, &h2, &v2); + + if (h1 == h2 && v1 == v2) + return false; + else + return true; +} + +void dce110_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 0, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } else { + set_reg_field_value( + value, + 4, + CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PREFETCH_EN); + } + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_PROGRESSIVE_START_LINE_EARLY); + + set_reg_field_value( + value, + 1, + CRTC_START_LINE_CONTROL, + CRTC_INTERLACE_START_LINE_EARLY); + + dm_write_reg(tg->ctx, addr, value); +} + +/*TODO: Figure out if we need this function. */ +void dce110_timing_generator_set_lock_master(struct timing_generator *tg, + bool lock) +{ + struct dc_context *ctx = tg->ctx; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_MASTER_UPDATE_LOCK); + uint32_t value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + lock ? 1 : 0, + CRTC_MASTER_UPDATE_LOCK, + MASTER_UPDATE_LOCK); + + dm_write_reg(ctx, addr, value); +} + +void dce110_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source_tg_inst) +{ + uint32_t value; + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Setup trigger edge */ + { + uint32_t pol_value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_V_SYNC_A_CNTL)); + + /* Register spec has reversed definition: + * 0 for positive, 1 for negative */ + if (get_reg_field_value(pol_value, + CRTC_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL) == 0) { + rising_edge = 1; + } else { + falling_edge = 1; + } + } + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; + + set_reg_field_value(value, + trig_src_select, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + rising_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_RISING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + falling_edge, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL); + + set_reg_field_value(value, + 0, /* send every signal */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_FREQUENCY_SELECT); + + set_reg_field_value(value, + 0, /* no delay */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_DELAY); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); + + /**************************************************************/ + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 2, /* force H count to H_TOTAL and V count to V_TOTAL */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* TriggerB - we never use TriggerA */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_TRIG_SEL); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); +} + +void dce110_timing_generator_disable_reset_trigger( + struct timing_generator *tg) +{ + uint32_t value; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + set_reg_field_value(value, + 0, /* force counter now mode is disabled */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL), value); + + /********************************************************************/ + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL)); + + set_reg_field_value(value, + TRIGGER_SOURCE_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT); + + set_reg_field_value(value, + TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + CRTC_TRIGB_CNTL, + CRTC_TRIGB_POLARITY_SELECT); + + set_reg_field_value(value, + 1, /* clear trigger status */ + CRTC_TRIGB_CNTL, + CRTC_TRIGB_CLEAR); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_TRIGB_CNTL), value); +} + +/** + ***************************************************************************** + * @brief + * Checks whether CRTC triggered reset occurred + * + * @return + * true if triggered reset occurred, false otherwise + ***************************************************************************** + */ +bool dce110_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg(tg->ctx, + CRTC_REG(mmCRTC_FORCE_COUNT_NOW_CNTL)); + + return get_reg_field_value(value, + CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; +} + +/** + * dce110_timing_generator_disable_vga + * Turn OFF VGA Mode and Timing - DxVGA_CONTROL + * VGA Mode and VGA Timing is used by VBIOS on CRT Monitors; + */ +void dce110_timing_generator_disable_vga( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + switch (tg110->controller_id) { + case CONTROLLER_ID_D0: + addr = mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D1: + addr = mmD2VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + addr = mmD3VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + addr = mmD4VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + addr = mmD5VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + addr = mmD6VGA_CONTROL; + break; + default: + break; + } + value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); + set_reg_field_value( + value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); + set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); + + dm_write_reg(tg->ctx, addr, value); +} + +/** +* set_overscan_color_black +* +* @param :black_color is one of the color space +* :this routine will set overscan black color according to the color space. +* @return none +*/ + +void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t addr; + uint32_t value = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + set_reg_field_value( + value, + color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + set_reg_field_value( + value, + color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + addr = CRTC_REG(mmCRTC_OVERSCAN_COLOR); + dm_write_reg(ctx, addr, value); + addr = CRTC_REG(mmCRTC_BLACK_COLOR); + dm_write_reg(ctx, addr, value); + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + addr = CRTC_REG(mmCRTC_BLANK_DATA_COLOR); + dm_write_reg(ctx, addr, value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ + +} + +void dce110_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = CRTC_REG(mmCRTC_BLACK_COLOR); + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); + + addr = CRTC_REG(mmCRTC_BLANK_DATA_COLOR); + dm_write_reg(tg->ctx, addr, value); +} + +void dce110_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + set_reg_field_value( + value, + overscan_color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + overscan_color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + overscan_color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + addr = CRTC_REG(mmCRTC_OVERSCAN_COLOR); + dm_write_reg(ctx, addr, value); +} + +void dce110_tg_get_position(struct timing_generator *tg, + struct crtc_position *position) +{ + int32_t h_position; + int32_t v_position; + + dce110_timing_generator_get_crtc_positions(tg, &h_position, &v_position); + + position->horizontal_count = (uint32_t)h_position; + position->vertical_count = (uint32_t)v_position; +} + +void dce110_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce110_timing_generator_program_blanking(tg, timing); +} + +bool dce110_tg_is_blanked(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL)); + + if (get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + return true; + return false; +} + +bool dce110_tg_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + + set_reg_field_value( + value, + 1, + CRTC_DOUBLE_BUFFER_CONTROL, + CRTC_BLANK_DATA_DOUBLE_BUFFER_EN); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_DOUBLE_BUFFER_CONTROL), value); + value = 0; + + if (enable_blanking) { + int counter; + + set_reg_field_value( + value, + 1, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL), value); + + for (counter = 0; counter < 100; counter++) { + value = dm_read_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL)); + + if (get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + break; + + msleep(1); + } + + if (counter == 100) { + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "timing generator %d blank timing out.\n", + tg110->controller_id); + return false; + } + } else + dm_write_reg(tg->ctx, CRTC_REG(mmCRTC_BLANK_CONTROL), 0); + + return true; +} + +bool dce110_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + return dce110_timing_generator_validate_timing(tg, timing, SIGNAL_TYPE_NONE); +} + +void dce110_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce110_timing_generator_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce110_timing_generator_wait_for_vactive(tg); + break; + + default: + break; + } +} + +void dce110_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce110_tg_program_blank_color(tg, blank_color); + if (overscan_color != NULL) + dce110_tg_set_overscan_color(tg, overscan_color); +} + +static const struct timing_generator_funcs dce110_tg_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = dce110_tg_program_timing, + .enable_crtc = dce110_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + .is_counter_moving = dce110_timing_generator_is_counter_moving, + .get_position = dce110_timing_generator_get_crtc_positions, + .get_frame_count = dce110_timing_generator_get_vblank_counter, + .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce110_timing_generator_set_early_control, + .wait_for_state = dce110_tg_wait_for_state, + .set_blank = dce110_tg_set_blank, + .is_blanked = dce110_tg_is_blanked, + .set_colors = dce110_tg_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_program_blank_color, + .disable_vga = dce110_timing_generator_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_tear_down_global_swap_lock, + .enable_advanced_request = + dce110_timing_generator_enable_advanced_request, + .set_drr = + dce110_timing_generator_set_drr, + .set_static_screen_control = + dce110_timing_generator_set_static_screen_control, + .set_test_pattern = dce110_timing_generator_set_test_pattern + +}; + +bool dce110_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + if (!tg110) + return false; + + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + + tg110->offsets = *offsets; + + tg110->base.funcs = &dce110_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h new file mode 100644 index 000000000000..39906502ad5c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator.h @@ -0,0 +1,273 @@ +/* + * 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_TIMING_GENERATOR_DCE110_H__ +#define __DC_TIMING_GENERATOR_DCE110_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" +#include "../include/hw_sequencer_types.h" + +/* GSL Sync related values */ + +/* In VSync mode, after 4 units of time, master pipe will generate + * flip_ready signal */ +#define VFLIP_READY_DELAY 4 +/* In HSync mode, after 2 units of time, master pipe will generate + * flip_ready signal */ +#define HFLIP_READY_DELAY 2 +/* 6 lines delay between forcing flip and checking all pipes ready */ +#define HFLIP_CHECK_DELAY 6 +/* 3 lines before end of frame */ +#define FLIP_READY_BACK_LOOKUP 3 + +/* Trigger Source Select - ASIC-defendant, actual values for the + * register programming */ +enum trigger_source_select { + TRIGGER_SOURCE_SELECT_LOGIC_ZERO = 0, + TRIGGER_SOURCE_SELECT_CRTC_VSYNCA = 1, + TRIGGER_SOURCE_SELECT_CRTC_HSYNCA = 2, + TRIGGER_SOURCE_SELECT_CRTC_VSYNCB = 3, + TRIGGER_SOURCE_SELECT_CRTC_HSYNCB = 4, + TRIGGER_SOURCE_SELECT_GENERICF = 5, + TRIGGER_SOURCE_SELECT_GENERICE = 6, + TRIGGER_SOURCE_SELECT_VSYNCA = 7, + TRIGGER_SOURCE_SELECT_HSYNCA = 8, + TRIGGER_SOURCE_SELECT_VSYNCB = 9, + TRIGGER_SOURCE_SELECT_HSYNCB = 10, + TRIGGER_SOURCE_SELECT_HPD1 = 11, + TRIGGER_SOURCE_SELECT_HPD2 = 12, + TRIGGER_SOURCE_SELECT_GENERICD = 13, + TRIGGER_SOURCE_SELECT_GENERICC = 14, + TRIGGER_SOURCE_SELECT_VIDEO_CAPTURE = 15, + TRIGGER_SOURCE_SELECT_GSL_GROUP0 = 16, + TRIGGER_SOURCE_SELECT_GSL_GROUP1 = 17, + TRIGGER_SOURCE_SELECT_GSL_GROUP2 = 18, + TRIGGER_SOURCE_SELECT_BLONY = 19, + TRIGGER_SOURCE_SELECT_GENERICA = 20, + TRIGGER_SOURCE_SELECT_GENERICB = 21, + TRIGGER_SOURCE_SELECT_GSL_ALLOW_FLIP = 22, + TRIGGER_SOURCE_SELECT_MANUAL_TRIGGER = 23 +}; + +/* Trigger Source Select - ASIC-dependant, actual values for the + * register programming */ +enum trigger_polarity_select { + TRIGGER_POLARITY_SELECT_LOGIC_ZERO = 0, + TRIGGER_POLARITY_SELECT_CRTC = 1, + TRIGGER_POLARITY_SELECT_GENERICA = 2, + TRIGGER_POLARITY_SELECT_GENERICB = 3, + TRIGGER_POLARITY_SELECT_HSYNCA = 4, + TRIGGER_POLARITY_SELECT_HSYNCB = 5, + TRIGGER_POLARITY_SELECT_VIDEO_CAPTURE = 6, + TRIGGER_POLARITY_SELECT_GENERICC = 7 +}; + + +struct dce110_timing_generator_offsets { + int32_t crtc; + int32_t dcp; + + /* DCE80 use only */ + int32_t dmif; +}; + +struct dce110_timing_generator { + struct timing_generator base; + struct dce110_timing_generator_offsets offsets; + struct dce110_timing_generator_offsets derived_offsets; + + enum controller_id controller_id; + + uint32_t max_h_total; + uint32_t max_v_total; + + uint32_t min_h_blank; + uint32_t min_h_front_porch; + uint32_t min_h_back_porch; + + uint32_t min_h_sync_width; + uint32_t min_v_sync_width; + uint32_t min_v_blank; + +}; + +#define DCE110TG_FROM_TG(tg)\ + container_of(tg, struct dce110_timing_generator, base) + +bool dce110_timing_generator_construct( + struct dce110_timing_generator *tg, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +/* determine if given timing can be supported by TG */ +bool dce110_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal); + +/******** HW programming ************/ + +/* Program timing generator with given timing */ +bool dce110_timing_generator_program_timing_generator( + struct timing_generator *tg, + const struct dc_crtc_timing *dc_crtc_timing); + +/* Disable/Enable Timing Generator */ +bool dce110_timing_generator_enable_crtc(struct timing_generator *tg); +bool dce110_timing_generator_disable_crtc(struct timing_generator *tg); + +void dce110_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl); + +/**************** TG current status ******************/ + +/* return the current frame counter. Used by Linux kernel DRM */ +uint32_t dce110_timing_generator_get_vblank_counter( + struct timing_generator *tg); + +/* Get current H and V position */ +void dce110_timing_generator_get_crtc_positions( + struct timing_generator *tg, + int32_t *h_position, + int32_t *v_position); + +/* return true if TG counter is moving. false if TG is stopped */ +bool dce110_timing_generator_is_counter_moving(struct timing_generator *tg); + +/* wait until TG is in beginning of vertical blank region */ +void dce110_timing_generator_wait_for_vblank(struct timing_generator *tg); + +/* wait until TG is in beginning of active region */ +void dce110_timing_generator_wait_for_vactive(struct timing_generator *tg); + +/*********** Timing Generator Synchronization routines ****/ + +/* Setups Global Swap Lock group, TimingServer or TimingClient*/ +void dce110_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params); + +/* Clear all the register writes done by setup_global_swap_lock */ +void dce110_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg); + +/* Reset slave controllers on master VSync */ +void dce110_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source); + +/* disabling trigger-reset */ +void dce110_timing_generator_disable_reset_trigger( + struct timing_generator *tg); + +/* Checks whether CRTC triggered reset occurred */ +bool dce110_timing_generator_did_triggered_reset_occur( + struct timing_generator *tg); + +/******** Stuff to move to other virtual HW objects *****************/ +/* Move to enable accelerated mode */ +void dce110_timing_generator_disable_vga(struct timing_generator *tg); +/* TODO: Should we move it to transform */ +/* Fully program CRTC timing in timing generator */ +void dce110_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +/* TODO: Should we move it to opp? */ +/* Combine with below and move YUV/RGB color conversion to SW layer */ +void dce110_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color); +/* Combine with above and move YUV/RGB color conversion to SW layer */ +void dce110_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color); +void dce110_timing_generator_color_space_to_black_color( + enum dc_color_space colorspace, + struct tg_color *black_color); +/*************** End-of-move ********************/ + +/* Not called yet */ +void dce110_timing_generator_set_test_pattern( + struct timing_generator *tg, + /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' + * because this is not DP-specific (which is probably somewhere in DP + * encoder) */ + enum controller_dp_test_pattern test_pattern, + enum dc_color_depth color_depth); + +void dce110_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params); + +void dce110_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t value); + +uint32_t dce110_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *vbl, + uint32_t *position); + +void dce110_timing_generator_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing); + +void dce110_timing_generator_set_lock_master(struct timing_generator *tg, + bool lock); + +void dce110_tg_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color); + +void dce110_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color); + +void dce110_tg_get_position(struct timing_generator *tg, + struct crtc_position *position); + +void dce110_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios); + +bool dce110_tg_is_blanked(struct timing_generator *tg); + +bool dce110_tg_set_blank(struct timing_generator *tg, + bool enable_blanking); + +bool dce110_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +void dce110_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state); + +void dce110_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color); + +#endif /* __DC_TIMING_GENERATOR_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c new file mode 100644 index 000000000000..3bf3179e07c5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.c @@ -0,0 +1,743 @@ +#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 "dc_bios_types.h" +#include "dc.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce110_timing_generator.h" +#include "dce110_timing_generator_v.h" + +#include "timing_generator.h" + +/** ******************************************************************************** + * + * DCE11 Timing Generator Implementation + * + **********************************************************************************/ + +/** +* Enable CRTCV +*/ + +static bool dce110_timing_generator_v_enable_crtc(struct timing_generator *tg) +{ +/* +* Set MASTER_UPDATE_MODE to 0 +* This is needed for DRR, and also suggested to be default value by Syed. +*/ + + uint32_t value; + + value = 0; + set_reg_field_value(value, 0, + CRTCV_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE); + dm_write_reg(tg->ctx, + mmCRTCV_MASTER_UPDATE_MODE, value); + + /* TODO: may want this on for looking for underflow */ + value = 0; + dm_write_reg(tg->ctx, mmCRTCV_MASTER_UPDATE_MODE, value); + + value = 0; + set_reg_field_value(value, 1, + CRTCV_MASTER_EN, CRTC_MASTER_EN); + dm_write_reg(tg->ctx, + mmCRTCV_MASTER_EN, value); + + return true; +} + +static bool dce110_timing_generator_v_disable_crtc(struct timing_generator *tg) +{ + uint32_t value; + + value = dm_read_reg(tg->ctx, + mmCRTCV_CONTROL); + set_reg_field_value(value, 0, + CRTCV_CONTROL, CRTC_DISABLE_POINT_CNTL); + set_reg_field_value(value, 0, + CRTCV_CONTROL, CRTC_MASTER_EN); + dm_write_reg(tg->ctx, + mmCRTCV_CONTROL, value); + /* + * TODO: call this when adding stereo support + * tg->funcs->disable_stereo(tg); + */ + return true; +} + +static bool dce110_timing_generator_v_blank_crtc(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t addr = mmCRTCV_BLANK_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + uint8_t counter = 100; + + set_reg_field_value( + value, + 1, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dm_write_reg(tg->ctx, addr, value); + + while (counter > 0) { + value = dm_read_reg(tg->ctx, addr); + + if (get_reg_field_value( + value, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTCV_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + break; + + msleep(1); + counter--; + } + + if (!counter) { + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "timing generator %d blank timing out.\n", + tg110->controller_id); + return false; + } + + return true; +} + +static bool dce110_timing_generator_v_unblank_crtc(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_BLANK_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DATA_EN); + + set_reg_field_value( + value, + 0, + CRTCV_BLANK_CONTROL, + CRTC_BLANK_DE_MODE); + + dm_write_reg(tg->ctx, addr, value); + + return true; +} + +static bool dce110_timing_generator_v_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + + addr = mmCRTCV_STATUS; + value = dm_read_reg(tg->ctx, addr); + field = get_reg_field_value(value, CRTCV_STATUS, CRTC_V_BLANK); + return field == 1; +} + +static bool dce110_timing_generator_v_is_counter_moving(struct timing_generator *tg) +{ + uint32_t value; + uint32_t h1 = 0; + uint32_t h2 = 0; + uint32_t v1 = 0; + uint32_t v2 = 0; + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + h1 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + v1 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + h2 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + v2 = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); + + if (h1 == h2 && v1 == v2) + return false; + else + return true; +} + +static void dce110_timing_generator_v_wait_for_vblank(struct timing_generator *tg) +{ + /* We want to catch beginning of VBlank here, so if the first try are + * in VBlank, we might be very close to Active, in this case wait for + * another frame + */ + while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/** +* Wait till we are in VActive (anywhere in VActive) +*/ +static void dce110_timing_generator_v_wait_for_vactive(struct timing_generator *tg) +{ + while (dce110_timing_generator_v_is_in_vertical_blank(tg)) { + if (!dce110_timing_generator_v_is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +static void dce110_timing_generator_v_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce110_timing_generator_v_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce110_timing_generator_v_wait_for_vactive(tg); + break; + + default: + break; + } +} + +static void dce110_timing_generator_v_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t vsync_offset = timing->v_border_bottom + + timing->v_front_porch; + uint32_t v_sync_start = timing->v_addressable + vsync_offset; + + uint32_t hsync_offset = timing->h_border_right + + timing->h_front_porch; + uint32_t h_sync_start = timing->h_addressable + hsync_offset; + + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr = 0; + uint32_t tmp = 0; + + addr = mmCRTCV_H_TOTAL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->h_total - 1, + CRTCV_H_TOTAL, + CRTC_H_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_TOTAL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->v_total - 1, + CRTCV_V_TOTAL, + CRTC_V_TOTAL); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_BLANK_START_END; + value = dm_read_reg(ctx, addr); + + tmp = timing->h_total - + (h_sync_start + timing->h_border_left); + + set_reg_field_value( + value, + tmp, + CRTCV_H_BLANK_START_END, + CRTC_H_BLANK_END); + + tmp = tmp + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + set_reg_field_value( + value, + tmp, + CRTCV_H_BLANK_START_END, + CRTC_H_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_BLANK_START_END; + value = dm_read_reg(ctx, addr); + + tmp = timing->v_total - (v_sync_start + timing->v_border_top); + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_END); + + tmp = tmp + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + set_reg_field_value( + value, + tmp, + CRTCV_V_BLANK_START_END, + CRTC_V_BLANK_START); + + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_SYNC_A; + value = 0; + set_reg_field_value( + value, + timing->h_sync_width, + CRTCV_H_SYNC_A, + CRTC_H_SYNC_A_END); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_H_SYNC_A_CNTL; + value = dm_read_reg(ctx, addr); + if (timing->flags.HSYNC_POSITIVE_POLARITY) { + set_reg_field_value( + value, + 0, + CRTCV_H_SYNC_A_CNTL, + CRTC_H_SYNC_A_POL); + } else { + set_reg_field_value( + value, + 1, + CRTCV_H_SYNC_A_CNTL, + CRTC_H_SYNC_A_POL); + } + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_SYNC_A; + value = 0; + set_reg_field_value( + value, + timing->v_sync_width, + CRTCV_V_SYNC_A, + CRTC_V_SYNC_A_END); + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_V_SYNC_A_CNTL; + value = dm_read_reg(ctx, addr); + if (timing->flags.VSYNC_POSITIVE_POLARITY) { + set_reg_field_value( + value, + 0, + CRTCV_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL); + } else { + set_reg_field_value( + value, + 1, + CRTCV_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL); + } + dm_write_reg(ctx, addr, value); + + addr = mmCRTCV_INTERLACE_CONTROL; + value = dm_read_reg(ctx, addr); + set_reg_field_value( + value, + timing->flags.INTERLACE, + CRTCV_INTERLACE_CONTROL, + CRTC_INTERLACE_ENABLE); + dm_write_reg(ctx, addr, value); +} + +static void dce110_timing_generator_v_enable_advanced_request( + struct timing_generator *tg, + bool enable, + const struct dc_crtc_timing *timing) +{ + uint32_t addr = mmCRTCV_START_LINE_CONTROL; + uint32_t value = dm_read_reg(tg->ctx, addr); + + if (enable) { + if ((timing->v_sync_width + timing->v_front_porch) <= 3) { + set_reg_field_value( + value, + 3, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + } else { + set_reg_field_value( + value, + 4, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + } + set_reg_field_value( + value, + 0, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } else { + set_reg_field_value( + value, + 2, + CRTCV_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + set_reg_field_value( + value, + 1, + CRTCV_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + } + + dm_write_reg(tg->ctx, addr, value); +} + +static bool dce110_timing_generator_v_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + if (enable_blanking) + return dce110_timing_generator_v_blank_crtc(tg); + else + return dce110_timing_generator_v_unblank_crtc(tg); +} + +static void dce110_timing_generator_v_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce110_timing_generator_v_program_blanking(tg, timing); +} + +static void dce110_timing_generator_v_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + uint32_t addr = mmCRTCV_BLACK_COLOR; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t addr; + uint32_t value = 0; + + set_reg_field_value( + value, + color->color_b_cb, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + color->color_r_cr, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + set_reg_field_value( + value, + color->color_g_y, + CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + addr = mmCRTCV_OVERSCAN_COLOR; + dm_write_reg(ctx, addr, value); + addr = mmCRTCV_BLACK_COLOR; + dm_write_reg(ctx, addr, value); + /* This is desirable to have a constant DAC output voltage during the + * blank time that is higher than the 0 volt reference level that the + * DAC outputs when the NBLANK signal + * is asserted low, such as for output to an analog TV. */ + addr = mmCRTCV_BLANK_DATA_COLOR; + dm_write_reg(ctx, addr, value); + + /* TO DO we have to program EXT registers and we need to know LB DATA + * format because it is used when more 10 , i.e. 12 bits per color + * + * m_mmDxCRTC_OVERSCAN_COLOR_EXT + * m_mmDxCRTC_BLACK_COLOR_EXT + * m_mmDxCRTC_BLANK_DATA_COLOR_EXT + */ +} + +static void dce110_tg_v_program_blank_color(struct timing_generator *tg, + const struct tg_color *black_color) +{ + uint32_t addr = mmCRTCV_BLACK_COLOR; + uint32_t value = dm_read_reg(tg->ctx, addr); + + set_reg_field_value( + value, + black_color->color_b_cb, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB); + set_reg_field_value( + value, + black_color->color_g_y, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_G_Y); + set_reg_field_value( + value, + black_color->color_r_cr, + CRTCV_BLACK_COLOR, + CRTC_BLACK_COLOR_R_CR); + + dm_write_reg(tg->ctx, addr, value); + + addr = mmCRTCV_BLANK_DATA_COLOR; + dm_write_reg(tg->ctx, addr, value); +} + +static void dce110_timing_generator_v_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dc_context *ctx = tg->ctx; + uint32_t value = 0; + uint32_t addr; + + set_reg_field_value( + value, + overscan_color->color_b_cb, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE); + + set_reg_field_value( + value, + overscan_color->color_g_y, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_GREEN); + + set_reg_field_value( + value, + overscan_color->color_r_cr, + CRTCV_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_RED); + + addr = mmCRTCV_OVERSCAN_COLOR; + dm_write_reg(ctx, addr, value); +} + +static void dce110_timing_generator_v_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce110_tg_v_program_blank_color(tg, blank_color); + if (overscan_color != NULL) + dce110_timing_generator_v_set_overscan_color(tg, overscan_color); +} + +static void dce110_timing_generator_v_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + uint32_t regval; + uint32_t address = mmCRTC_CONTROL; + + regval = dm_read_reg(tg->ctx, address); + set_reg_field_value(regval, early_cntl, + CRTCV_CONTROL, CRTC_HBLANK_EARLY_CONTROL); + dm_write_reg(tg->ctx, address, regval); +} + +static void dce110_timing_generator_v_get_crtc_positions( + struct timing_generator *tg, + int32_t *h_position, + int32_t *v_position) +{ + uint32_t value; + + value = dm_read_reg(tg->ctx, mmCRTCV_STATUS_POSITION); + + *h_position = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_HORZ_COUNT); + + *v_position = get_reg_field_value( + value, + CRTCV_STATUS_POSITION, + CRTC_VERT_COUNT); +} + +static uint32_t dce110_timing_generator_v_get_vblank_counter(struct timing_generator *tg) +{ + uint32_t addr = mmCRTCV_STATUS_FRAME_COUNT; + uint32_t value = dm_read_reg(tg->ctx, addr); + uint32_t field = get_reg_field_value( + value, CRTCV_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +static bool dce110_timing_generator_v_did_triggered_reset_occur( + struct timing_generator *tg) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return false; +} + +static void dce110_timing_generator_v_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_enable_reset_trigger( + struct timing_generator *tg, + int source_tg_inst) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_disable_reset_trigger( + struct timing_generator *tg) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + dm_logger_write(tg->ctx->logger, LOG_ERROR, + "Timing Sync not supported on underlay pipe\n"); + return; +} + +static void dce110_timing_generator_v_disable_vga( + struct timing_generator *tg) +{ + return; +} + +static bool dce110_tg_v_is_blanked(struct timing_generator *tg) +{ + /* Signal comes from the primary pipe, underlay is never blanked. */ + return false; +} + +/** ******************************************************************************************** + * + * DCE11 Timing Generator Constructor / Destructor + * + *********************************************************************************************/ +static const struct timing_generator_funcs dce110_tg_v_funcs = { + .validate_timing = dce110_tg_validate_timing, + .program_timing = dce110_timing_generator_v_program_timing, + .enable_crtc = dce110_timing_generator_v_enable_crtc, + .disable_crtc = dce110_timing_generator_v_disable_crtc, + .is_counter_moving = dce110_timing_generator_v_is_counter_moving, + .get_position = dce110_timing_generator_v_get_crtc_positions, + .get_frame_count = dce110_timing_generator_v_get_vblank_counter, + .set_early_control = dce110_timing_generator_v_set_early_control, + .wait_for_state = dce110_timing_generator_v_wait_for_state, + .set_blank = dce110_timing_generator_v_set_blank, + .is_blanked = dce110_tg_v_is_blanked, + .set_colors = dce110_timing_generator_v_set_colors, + .set_overscan_blank_color = + dce110_timing_generator_v_set_overscan_color_black, + .set_blank_color = dce110_timing_generator_v_program_blank_color, + .disable_vga = dce110_timing_generator_v_disable_vga, + .did_triggered_reset_occur = + dce110_timing_generator_v_did_triggered_reset_occur, + .setup_global_swap_lock = + dce110_timing_generator_v_setup_global_swap_lock, + .enable_reset_trigger = dce110_timing_generator_v_enable_reset_trigger, + .disable_reset_trigger = dce110_timing_generator_v_disable_reset_trigger, + .tear_down_global_swap_lock = + dce110_timing_generator_v_tear_down_global_swap_lock, + .enable_advanced_request = + dce110_timing_generator_v_enable_advanced_request +}; + +bool dce110_timing_generator_v_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx) +{ + if (!tg110) + return false; + + tg110->controller_id = CONTROLLER_ID_UNDERLAY0; + + tg110->base.funcs = &dce110_tg_v_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + tg110->min_h_blank = 56; + tg110->min_h_front_porch = 4; + tg110->min_h_back_porch = 4; + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h new file mode 100644 index 000000000000..7e49ca8e26ad --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_timing_generator_v.h @@ -0,0 +1,33 @@ +/* + * 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_TIMING_GENERATOR_V_DCE110_H__ +#define __DC_TIMING_GENERATOR_V_DCE110_H__ + +bool dce110_timing_generator_v_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx); + +#endif /* __DC_TIMING_GENERATOR_V_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c new file mode 100644 index 000000000000..7d8cf7a58f46 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.c @@ -0,0 +1,704 @@ +/* + * 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 "dce110_transform_v.h" +#include "dm_services.h" +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#define SCLV_PHASES 64 + +struct sclv_ratios_inits { + uint32_t h_int_scale_ratio_luma; + uint32_t h_int_scale_ratio_chroma; + uint32_t v_int_scale_ratio_luma; + uint32_t v_int_scale_ratio_chroma; + struct init_int_and_frac h_init_luma; + struct init_int_and_frac h_init_chroma; + struct init_int_and_frac v_init_luma; + struct init_int_and_frac v_init_chroma; +}; + +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; + chroma_viewport->x = luma_viewport->x; + chroma_viewport->y = luma_viewport->y; + chroma_viewport->height = luma_viewport->height; + chroma_viewport->width = luma_viewport->width; + + if (scl_data->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 dce_transform *xfm_dce, + struct rect *luma_view_port, + struct rect *chroma_view_port) +{ + struct dc_context *ctx = xfm_dce->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); + } +} + +/* + * 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 dce_transform *xfm_dce, + const struct scaler_data *data) +{ + bool is_scaling_needed = false; + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t value = 0; + + 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); + + 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->format != PIXEL_FORMAT_420BPP12) { + 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 = 0; + /* + * 0 - Replaced out of bound pixels with black pixel + * (or any other required color) + * 1 - Replaced out of bound pixels with the edge pixel + */ + set_reg_field_value(value, 1, SCLV_CONTROL, SCL_BOUNDARY_MODE); + 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 dce_transform *xfm_dce, + const struct scaler_data *data) +{ + uint32_t overscan_left_right = 0; + uint32_t overscan_top_bottom = 0; + + int overscan_right = data->h_active - data->recout.x - data->recout.width; + int overscan_bottom = data->v_active - data->recout.y - data->recout.height; + + if (overscan_right < 0) { + BREAK_TO_DEBUGGER(); + overscan_right = 0; + } + if (overscan_bottom < 0) { + BREAK_TO_DEBUGGER(); + overscan_bottom = 0; + } + + set_reg_field_value(overscan_left_right, data->recout.x, + 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, data->recout.y, + 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(xfm_dce->base.ctx, + mmSCLV_EXT_OVERSCAN_LEFT_RIGHT, + overscan_left_right); + + dm_write_reg(xfm_dce->base.ctx, + mmSCLV_EXT_OVERSCAN_TOP_BOTTOM, + overscan_top_bottom); +} + +static void set_coeff_update_complete( + struct dce_transform *xfm_dce) +{ + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, mmSCLV_UPDATE); + set_reg_field_value(value, 1, SCLV_UPDATE, SCL_COEF_UPDATE_COMPLETE); + dm_write_reg(xfm_dce->base.ctx, mmSCLV_UPDATE, value); +} + +static void program_multi_taps_filter( + struct dce_transform *xfm_dce, + int taps, + const uint16_t *coeffs, + enum ram_filter_type filter_type) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + int i, phase, pair; + int array_idx = 0; + int taps_pairs = (taps + 1) / 2; + int phases_to_program = SCLV_PHASES / 2 + 1; + + uint32_t select = 0; + uint32_t power_ctl, power_ctl_off; + + if (!coeffs) + return; + + /*We need to disable power gating on coeff memory to do programming*/ + power_ctl = dm_read_reg(ctx, mmDCFEV_MEM_PWR_CTRL); + power_ctl_off = power_ctl; + set_reg_field_value(power_ctl_off, 1, DCFEV_MEM_PWR_CTRL, SCLV_COEFF_MEM_PWR_DIS); + dm_write_reg(ctx, mmDCFEV_MEM_PWR_CTRL, power_ctl_off); + + /*Wait to disable gating:*/ + for (i = 0; i < 10; i++) { + if (get_reg_field_value( + dm_read_reg(ctx, mmDCFEV_MEM_PWR_STATUS), + DCFEV_MEM_PWR_STATUS, + SCLV_COEFF_MEM_PWR_STATE) == 0) + break; + + udelay(1); + } + + set_reg_field_value(select, filter_type, SCLV_COEF_RAM_SELECT, SCL_C_RAM_FILTER_TYPE); + + 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, SCLV_COEF_RAM_SELECT, SCL_C_RAM_PHASE); + for (pair = 0; pair < taps_pairs; pair++) { + uint32_t data = 0; + + set_reg_field_value(select, pair, + SCLV_COEF_RAM_SELECT, SCL_C_RAM_TAP_PAIR_IDX); + + dm_write_reg(ctx, mmSCLV_COEF_RAM_SELECT, select); + + set_reg_field_value( + data, 1, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF_EN); + set_reg_field_value( + data, coeffs[array_idx], + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_EVEN_TAP_COEF); + + if (taps % 2 && pair == taps_pairs - 1) { + set_reg_field_value( + data, 0, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + array_idx++; + } else { + set_reg_field_value( + data, 1, + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF_EN); + set_reg_field_value( + data, coeffs[array_idx + 1], + SCLV_COEF_RAM_TAP_DATA, + SCL_C_RAM_ODD_TAP_COEF); + + array_idx += 2; + } + + dm_write_reg(ctx, mmSCLV_COEF_RAM_TAP_DATA, data); + } + } + + /*We need to restore power gating on coeff memory to initial state*/ + dm_write_reg(ctx, mmDCFEV_MEM_PWR_CTRL, power_ctl); +} + +static void calculate_inits( + struct dce_transform *xfm_dce, + const struct scaler_data *data, + struct sclv_ratios_inits *inits, + struct rect *luma_viewport, + struct rect *chroma_viewport) +{ + inits->h_int_scale_ratio_luma = + dal_fixed31_32_u2d19(data->ratios.horz) << 5; + inits->v_int_scale_ratio_luma = + dal_fixed31_32_u2d19(data->ratios.vert) << 5; + inits->h_int_scale_ratio_chroma = + dal_fixed31_32_u2d19(data->ratios.horz_c) << 5; + inits->v_int_scale_ratio_chroma = + dal_fixed31_32_u2d19(data->ratios.vert_c) << 5; + + inits->h_init_luma.integer = 1; + inits->v_init_luma.integer = 1; + inits->h_init_chroma.integer = 1; + inits->v_init_chroma.integer = 1; +} + +static void program_scl_ratios_inits( + struct dce_transform *xfm_dce, + struct sclv_ratios_inits *inits) +{ + struct dc_context *ctx = xfm_dce->base.ctx; + uint32_t addr = mmSCLV_HORZ_FILTER_SCALE_RATIO; + uint32_t value = 0; + + 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 = 0; + 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 = 0; + 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 = 0; + 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); + + addr = mmSCLV_HORZ_FILTER_INIT; + value = 0; + set_reg_field_value( + value, + inits->h_init_luma.fraction, + SCLV_HORZ_FILTER_INIT, + SCL_H_INIT_FRAC); + set_reg_field_value( + value, + inits->h_init_luma.integer, + SCLV_HORZ_FILTER_INIT, + SCL_H_INIT_INT); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_INIT; + value = 0; + set_reg_field_value( + value, + inits->v_init_luma.fraction, + SCLV_VERT_FILTER_INIT, + SCL_V_INIT_FRAC); + set_reg_field_value( + value, + inits->v_init_luma.integer, + SCLV_VERT_FILTER_INIT, + SCL_V_INIT_INT); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_HORZ_FILTER_INIT_C; + value = 0; + set_reg_field_value( + value, + inits->h_init_chroma.fraction, + SCLV_HORZ_FILTER_INIT_C, + SCL_H_INIT_FRAC_C); + set_reg_field_value( + value, + inits->h_init_chroma.integer, + SCLV_HORZ_FILTER_INIT_C, + SCL_H_INIT_INT_C); + dm_write_reg(ctx, addr, value); + + addr = mmSCLV_VERT_FILTER_INIT_C; + value = 0; + set_reg_field_value( + value, + inits->v_init_chroma.fraction, + SCLV_VERT_FILTER_INIT_C, + SCL_V_INIT_FRAC_C); + set_reg_field_value( + value, + inits->v_init_chroma.integer, + SCLV_VERT_FILTER_INIT_C, + SCL_V_INIT_INT_C); + dm_write_reg(ctx, addr, value); +} + +static const uint16_t *get_filter_coeffs_64p(int taps, struct fixed31_32 ratio) +{ + if (taps == 4) + return get_filter_4tap_64p(ratio); + else if (taps == 2) + return filter_2tap_64p; + else if (taps == 1) + return NULL; + else { + /* should never happen, bug */ + BREAK_TO_DEBUGGER(); + return NULL; + } +} + +static bool dce110_xfmv_power_up_line_buffer(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + uint32_t value; + + value = dm_read_reg(xfm_dce->base.ctx, mmLBV_MEMORY_CTRL); + + /*Use all three pieces of memory always*/ + set_reg_field_value(value, 0, LBV_MEMORY_CTRL, LB_MEMORY_CONFIG); + /*hard coded number DCE11 1712(0x6B0) Partitions: 720/960/1712*/ + set_reg_field_value(value, xfm_dce->lb_memory_size, LBV_MEMORY_CTRL, + LB_MEMORY_SIZE); + + dm_write_reg(xfm_dce->base.ctx, mmLBV_MEMORY_CTRL, value); + + return true; +} + +static void dce110_xfmv_set_scaler( + struct transform *xfm, + const struct scaler_data *data) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + bool is_scaling_required = false; + bool filter_updated = false; + const uint16_t *coeffs_v, *coeffs_h, *coeffs_h_c, *coeffs_v_c; + struct rect luma_viewport = {0}; + struct rect chroma_viewport = {0}; + + dce110_xfmv_power_up_line_buffer(xfm); + /* 1. 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); + + /* 2. Program overscan */ + program_overscan(xfm_dce, data); + + /* 3. Program taps and configuration */ + is_scaling_required = setup_scaling_configuration(xfm_dce, data); + + if (is_scaling_required) { + /* 4. Calculate and program ratio, filter initialization */ + + struct sclv_ratios_inits inits = { 0 }; + + calculate_inits( + xfm_dce, + data, + &inits, + &luma_viewport, + &chroma_viewport); + + program_scl_ratios_inits(xfm_dce, &inits); + + coeffs_v = get_filter_coeffs_64p(data->taps.v_taps, data->ratios.vert); + coeffs_h = get_filter_coeffs_64p(data->taps.h_taps, data->ratios.horz); + coeffs_v_c = get_filter_coeffs_64p(data->taps.v_taps_c, data->ratios.vert_c); + coeffs_h_c = get_filter_coeffs_64p(data->taps.h_taps_c, data->ratios.horz_c); + + if (coeffs_v != xfm_dce->filter_v + || coeffs_v_c != xfm_dce->filter_v_c + || coeffs_h != xfm_dce->filter_h + || coeffs_h_c != xfm_dce->filter_h_c) { + /* 5. Program vertical filters */ + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps, + coeffs_v, + FILTER_TYPE_RGB_Y_VERTICAL); + program_multi_taps_filter( + xfm_dce, + data->taps.v_taps_c, + coeffs_v_c, + FILTER_TYPE_CBCR_VERTICAL); + + /* 6. Program horizontal filters */ + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps, + coeffs_h, + FILTER_TYPE_RGB_Y_HORIZONTAL); + program_multi_taps_filter( + xfm_dce, + data->taps.h_taps_c, + coeffs_h_c, + FILTER_TYPE_CBCR_HORIZONTAL); + + xfm_dce->filter_v = coeffs_v; + xfm_dce->filter_v_c = coeffs_v_c; + xfm_dce->filter_h = coeffs_h; + xfm_dce->filter_h_c = coeffs_h_c; + filter_updated = true; + } + } + + /* 7. Program the viewport */ + program_viewport(xfm_dce, &luma_viewport, &chroma_viewport); + + /* 8. Set bit to flip to new coefficient memory */ + if (filter_updated) + set_coeff_update_complete(xfm_dce); +} + +static void dce110_xfmv_reset(struct transform *xfm) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + + xfm_dce->filter_h = NULL; + xfm_dce->filter_v = NULL; + xfm_dce->filter_h_c = NULL; + xfm_dce->filter_v_c = NULL; +} + +static void dce110_xfmv_set_gamut_remap( + struct transform *xfm, + const struct xfm_grph_csc_adjustment *adjust) +{ + /* DO NOTHING*/ +} + +static void dce110_xfmv_set_pixel_storage_depth( + struct transform *xfm, + enum lb_pixel_depth depth, + const struct bit_depth_reduction_params *bit_depth_params) +{ + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); + int pixel_depth, expan_mode; + uint32_t reg_data = 0; + + switch (depth) { + case LB_PIXEL_DEPTH_18BPP: + pixel_depth = 2; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_24BPP: + pixel_depth = 1; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_30BPP: + pixel_depth = 0; + expan_mode = 1; + break; + case LB_PIXEL_DEPTH_36BPP: + pixel_depth = 3; + expan_mode = 0; + break; + default: + BREAK_TO_DEBUGGER(); + break; + } + + set_reg_field_value( + reg_data, + expan_mode, + LBV_DATA_FORMAT, + PIXEL_EXPAN_MODE); + + set_reg_field_value( + reg_data, + pixel_depth, + LBV_DATA_FORMAT, + PIXEL_DEPTH); + + dm_write_reg(xfm->ctx, mmLBV_DATA_FORMAT, reg_data); + + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { + /*we should use unsupported capabilities + * unless it is required by w/a*/ + dm_logger_write(xfm->ctx->logger, LOG_WARNING, + "%s: Capability not supported", + __func__); + } +} + +static const struct transform_funcs dce110_xfmv_funcs = { + .transform_reset = dce110_xfmv_reset, + .transform_set_scaler = dce110_xfmv_set_scaler, + .transform_set_gamut_remap = + dce110_xfmv_set_gamut_remap, + .transform_set_pixel_storage_depth = + dce110_xfmv_set_pixel_storage_depth, + .transform_get_optimal_number_of_taps = + dce_transform_get_optimal_number_of_taps +}; +/*****************************************/ +/* Constructor, Destructor */ +/*****************************************/ + +bool dce110_transform_v_construct( + struct dce_transform *xfm_dce, + struct dc_context *ctx) +{ + xfm_dce->base.ctx = ctx; + + xfm_dce->base.funcs = &dce110_xfmv_funcs; + + xfm_dce->lb_pixel_depth_supported = + LB_PIXEL_DEPTH_18BPP | + LB_PIXEL_DEPTH_24BPP | + LB_PIXEL_DEPTH_30BPP; + + xfm_dce->prescaler_on = true; + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ + + return true; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h new file mode 100644 index 000000000000..267af34db3e5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_transform_v.h @@ -0,0 +1,37 @@ +/* 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_V_DCE110_H__ +#define __DAL_TRANSFORM_V_DCE110_H__ + +#include "../dce/dce_transform.h" + +#define LB_TOTAL_NUMBER_OF_ENTRIES 1712 +#define LB_BITS_PER_ENTRY 144 + +bool dce110_transform_v_construct( + struct dce_transform *xfm110, + struct dc_context *ctx); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_types.h b/drivers/gpu/drm/amd/display/dc/dce110/dce110_types.h new file mode 100644 index 000000000000..55f52382ddfb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_types.h @@ -0,0 +1,30 @@ +/* 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 _DCE110_TYPES_H_ +#define __DCE110_TYPES_H_ + +#define GAMMA_SEGMENTS_NUM 16 + +#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_DCE110_DCE110_TYPES_H_ */ |