diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/dce120')
7 files changed, 2941 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dce120/Makefile b/drivers/gpu/drm/amd/display/dc/dce120/Makefile new file mode 100644 index 000000000..a9cc4b732 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/Makefile @@ -0,0 +1,34 @@ +# +# Copyright 2017 Advanced Micro Devices, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + + +CFLAGS_$(AMDDALPATH)/dc/dce120/dce120_resource.o = $(call cc-disable-warning, override-init) + +DCE120 = dce120_resource.o dce120_timing_generator.o \ +dce120_hw_sequencer.o + +AMD_DAL_DCE120 = $(addprefix $(AMDDALPATH)/dc/dce120/,$(DCE120)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCE120) diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c new file mode 100644 index 000000000..d4afe6c82 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.c @@ -0,0 +1,271 @@ +/* + * 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 "core_types.h" +#include "dce120_hw_sequencer.h" +#include "dce/dce_hwseq.h" + +#include "dce110/dce110_hw_sequencer.h" + +#include "dce/dce_12_0_offset.h" +#include "dce/dce_12_0_sh_mask.h" +#include "soc15_hw_ip.h" +#include "vega10_ip_offset.h" +#include "reg_helper.h" + +#define CTX \ + hws->ctx +#define REG(reg)\ + hws->regs->reg + +#undef FN +#define FN(reg_name, field_name) \ + hws->shifts->field_name, hws->masks->field_name + +struct dce120_hw_seq_reg_offsets { + uint32_t crtc; +}; + +#if 0 +static const struct dce120_hw_seq_reg_offsets reg_offsets[] = { +{ + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC3_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC4_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +}, +{ + .crtc = (mmCRTC5_CRTC_GSL_CONTROL - mmCRTC0_CRTC_GSL_CONTROL), +} +}; + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + +#define CNTL_ID(controller_id)\ + controller_id +/******************************************************************************* + * Private definitions + ******************************************************************************/ +static void dce120_init_pte(struct dc_context *ctx, uint8_t controller_id) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; +/* + addr = mmDCP0_DVMM_PTE_CONTROL + controller_id * + (mmDCP1_DVMM_PTE_CONTROL- mmDCP0_DVMM_PTE_CONTROL); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, 0, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value_soc15( + value, 1, DCP, controller_id, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value_soc15( + value, 1, DCP, controller_id, + 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); + } +} +#endif + +static bool dce120_enable_display_power_gating( + struct dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + /* disable for bringup */ +#if 0 + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + struct dc_context *ctx = dc->ctx; + + 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 (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 + */ + dm_write_reg(ctx, + HW_REG_CRTC(mmCRTC0_CRTC_MASTER_UPDATE_MODE, controller_id), + 0); + } + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce120_init_pte(ctx, controller_id); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +#endif + return false; +} + +static void dce120_update_dchub( + struct dce_hwseq *hws, + struct dchub_init_data *dh_data) +{ + /* TODO: port code from dal2 */ + switch (dh_data->fb_mode) { + case FRAME_BUFFER_MODE_ZFB_ONLY: + /*For ZFB case need to put DCHUB FB BASE and TOP upside down to indicate ZFB mode*/ + REG_UPDATE_2(DCHUB_FB_LOCATION, + FB_TOP, 0, + FB_BASE, 0x0FFFF); + + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, (dh_data->zfb_mc_base_addr + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_MIXED_ZFB_AND_LOCAL: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, dh_data->zfb_phys_addr_base >> 22); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, dh_data->zfb_mc_base_addr >> 22); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, (dh_data->zfb_mc_base_addr + dh_data->zfb_size_in_byte - 1) >> 22); + break; + case FRAME_BUFFER_MODE_LOCAL_ONLY: + /*Should not touch FB LOCATION (done by VBIOS on AsicInit table)*/ + REG_UPDATE(DCHUB_AGP_BASE, + AGP_BASE, 0); + + REG_UPDATE(DCHUB_AGP_BOT, + AGP_BOT, 0x03FFFF); + + REG_UPDATE(DCHUB_AGP_TOP, + AGP_TOP, 0); + break; + default: + break; + } + + dh_data->dchub_initialzied = true; + dh_data->dchub_info_valid = false; +} + +/** + * dce121_xgmi_enabled() - Check if xGMI is enabled + * @hws: DCE hardware sequencer object + * + * Return true if xGMI is enabled. False otherwise. + */ +bool dce121_xgmi_enabled(struct dce_hwseq *hws) +{ + uint32_t pf_max_region; + + REG_GET(MC_VM_XGMI_LFB_CNTL, PF_MAX_REGION, &pf_max_region); + /* PF_MAX_REGION == 0 means xgmi is disabled */ + return !!pf_max_region; +} + +void dce120_hw_sequencer_construct(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce110_hw_sequencer_construct(dc); + dc->hwseq->funcs.enable_display_power_gating = dce120_enable_display_power_gating; + dc->hwss.update_dchub = dce120_update_dchub; +} + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h new file mode 100644 index 000000000..bc0245347 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_hw_sequencer.h @@ -0,0 +1,38 @@ +/* +* 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_DCE120_H__ +#define __DC_HWSS_DCE120_H__ + +#include "core_types.h" +#include "hw_sequencer_private.h" + +struct dc; + +bool dce121_xgmi_enabled(struct dce_hwseq *hws); +void dce120_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE112_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c new file mode 100644 index 000000000..af631085e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.c @@ -0,0 +1,1287 @@ +/* +* Copyright 2012-15 Advanced Micro Devices, Inc.cls +* + * + * 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 "stream_encoder.h" +#include "resource.h" +#include "include/irq_service_interface.h" +#include "dce120_resource.h" + +#include "dce112/dce112_resource.h" + +#include "dce110/dce110_resource.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce120_timing_generator.h" +#include "irq/dce120/irq_service_dce120.h" +#include "dce/dce_opp.h" +#include "dce/dce_clock_source.h" +#include "dce/dce_ipp.h" +#include "dce/dce_mem_input.h" +#include "dce/dce_panel_cntl.h" + +#include "dce110/dce110_hw_sequencer.h" +#include "dce120/dce120_hw_sequencer.h" +#include "dce/dce_transform.h" +#include "clk_mgr.h" +#include "dce/dce_audio.h" +#include "dce/dce_link_encoder.h" +#include "dce/dce_stream_encoder.h" +#include "dce/dce_hwseq.h" +#include "dce/dce_abm.h" +#include "dce/dce_dmcu.h" +#include "dce/dce_aux.h" +#include "dce/dce_i2c.h" + +#include "dce/dce_12_0_offset.h" +#include "dce/dce_12_0_sh_mask.h" +#include "soc15_hw_ip.h" +#include "vega10_ip_offset.h" +#include "nbio/nbio_6_1_offset.h" +#include "mmhub/mmhub_1_0_offset.h" +#include "mmhub/mmhub_1_0_sh_mask.h" +#include "reg_helper.h" + +#include "dce100/dce100_resource.h" + +#ifndef mmDP0_DP_DPHY_INTERNAL_CTRL + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x210f + #define mmDP0_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x220f + #define mmDP1_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x230f + #define mmDP2_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x240f + #define mmDP3_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x250f + #define mmDP4_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x260f + #define mmDP5_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x270f + #define mmDP6_DP_DPHY_INTERNAL_CTRL_BASE_IDX 2 +#endif + +enum dce120_clk_src_array_id { + DCE120_CLK_SRC_PLL0, + DCE120_CLK_SRC_PLL1, + DCE120_CLK_SRC_PLL2, + DCE120_CLK_SRC_PLL3, + DCE120_CLK_SRC_PLL4, + DCE120_CLK_SRC_PLL5, + + DCE120_CLK_SRC_TOTAL +}; + +static const struct dce110_timing_generator_offsets dce120_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC0_CRTC_CONTROL), + } +}; + +/* begin ********************* + * macros to expend register list macro defined in HW object header file */ + +#define BASE_INNER(seg) \ + DCE_BASE__INST0_SEG ## seg + +#define NBIO_BASE_INNER(seg) \ + NBIF_BASE__INST0_SEG ## seg + +#define NBIO_BASE(seg) \ + NBIO_BASE_INNER(seg) + +/* compile time expand base address. */ +#define BASE(seg) \ + BASE_INNER(seg) + +#define SR(reg_name)\ + .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +#define SRI(reg_name, block, id)\ + .reg_name = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +/* MMHUB */ +#define MMHUB_BASE_INNER(seg) \ + MMHUB_BASE__INST0_SEG ## seg + +#define MMHUB_BASE(seg) \ + MMHUB_BASE_INNER(seg) + +#define MMHUB_SR(reg_name)\ + .reg_name = MMHUB_BASE(mm ## reg_name ## _BASE_IDX) + \ + mm ## reg_name + +/* macros to expend register list macro defined in HW object header file + * end *********************/ + + +static const struct dce_dmcu_registers dmcu_regs = { + DMCU_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_dmcu_shift dmcu_shift = { + DMCU_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_dmcu_mask dmcu_mask = { + DMCU_MASK_SH_LIST_DCE110(_MASK) +}; + +static const struct dce_abm_registers abm_regs = { + ABM_DCE110_COMMON_REG_LIST() +}; + +static const struct dce_abm_shift abm_shift = { + ABM_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_abm_mask abm_mask = { + ABM_MASK_SH_LIST_DCE110(_MASK) +}; + +#define ipp_regs(id)\ +[id] = {\ + IPP_DCE110_REG_LIST_DCE_BASE(id)\ +} + +static const struct dce_ipp_registers ipp_regs[] = { + ipp_regs(0), + ipp_regs(1), + ipp_regs(2), + ipp_regs(3), + ipp_regs(4), + ipp_regs(5) +}; + +static const struct dce_ipp_shift ipp_shift = { + IPP_DCE120_MASK_SH_LIST_SOC_BASE(__SHIFT) +}; + +static const struct dce_ipp_mask ipp_mask = { + IPP_DCE120_MASK_SH_LIST_SOC_BASE(_MASK) +}; + +#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), + transform_regs(3), + transform_regs(4), + transform_regs(5) +}; + +static const struct dce_transform_shift xfm_shift = { + XFM_COMMON_MASK_SH_LIST_SOC_BASE(__SHIFT) +}; + +static const struct dce_transform_mask xfm_mask = { + XFM_COMMON_MASK_SH_LIST_SOC_BASE(_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_DCE120_REG_LIST(id), \ + SRI(DP_DPHY_INTERNAL_CTRL, DP, 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), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5) +}; + +static const struct dce_stream_encoder_shift se_shift = { + SE_COMMON_MASK_SH_LIST_DCE120(__SHIFT) +}; + +static const struct dce_stream_encoder_mask se_mask = { + SE_COMMON_MASK_SH_LIST_DCE120(_MASK) +}; + +static const struct dce_panel_cntl_registers panel_cntl_regs[] = { + { DCE_PANEL_CNTL_REG_LIST() } +}; + +static const struct dce_panel_cntl_shift panel_cntl_shift = { + DCE_PANEL_CNTL_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_panel_cntl_mask panel_cntl_mask = { + DCE_PANEL_CNTL_MASK_SH_LIST(_MASK) +}; + +static const struct dce110_aux_registers_shift aux_shift = { + DCE12_AUX_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce110_aux_registers_mask aux_mask = { + DCE12_AUX_MASK_SH_LIST(_MASK) +}; + +#define opp_regs(id)\ +[id] = {\ + OPP_DCE_120_REG_LIST(id),\ +} + +static const struct dce_opp_registers opp_regs[] = { + opp_regs(0), + opp_regs(1), + opp_regs(2), + opp_regs(3), + opp_regs(4), + opp_regs(5) +}; + +static const struct dce_opp_shift opp_shift = { + OPP_COMMON_MASK_SH_LIST_DCE_120(__SHIFT) +}; + +static const struct dce_opp_mask opp_mask = { + OPP_COMMON_MASK_SH_LIST_DCE_120(_MASK) +}; + #define aux_engine_regs(id)\ +[id] = {\ + AUX_COMMON_REG_LIST(id), \ + .AUX_RESET_MASK = 0 \ +} + +static const struct dce110_aux_registers aux_engine_regs[] = { + aux_engine_regs(0), + aux_engine_regs(1), + aux_engine_regs(2), + aux_engine_regs(3), + aux_engine_regs(4), + aux_engine_regs(5) +}; + +#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), +}; + +#define DCE120_AUD_COMMON_MASK_SH_LIST(mask_sh)\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_INDEX, AZALIA_ENDPOINT_REG_INDEX, mask_sh),\ + SF(AZF0ENDPOINT0_AZALIA_F0_CODEC_ENDPOINT_DATA, AZALIA_ENDPOINT_REG_DATA, mask_sh),\ + AUD_COMMON_MASK_SH_LIST_BASE(mask_sh) + +static const struct dce_audio_shift audio_shift = { + DCE120_AUD_COMMON_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_audio_mask audio_mask = { + DCE120_AUD_COMMON_MASK_SH_LIST(_MASK) +}; + +static int map_transmitter_id_to_phy_instance( + enum transmitter transmitter) +{ + switch (transmitter) { + case TRANSMITTER_UNIPHY_A: + return 0; + case TRANSMITTER_UNIPHY_B: + return 1; + case TRANSMITTER_UNIPHY_C: + return 2; + case TRANSMITTER_UNIPHY_D: + return 3; + case TRANSMITTER_UNIPHY_E: + return 4; + case TRANSMITTER_UNIPHY_F: + return 5; + case TRANSMITTER_UNIPHY_G: + return 6; + default: + ASSERT(0); + return 0; + } +} + +#define clk_src_regs(index, id)\ +[index] = {\ + CS_COMMON_REG_LIST_DCE_112(id),\ +} + +static const struct dce110_clk_src_regs clk_src_regs[] = { + clk_src_regs(0, A), + clk_src_regs(1, B), + clk_src_regs(2, C), + clk_src_regs(3, D), + clk_src_regs(4, E), + clk_src_regs(5, F) +}; + +static const struct dce110_clk_src_shift cs_shift = { + CS_COMMON_MASK_SH_LIST_DCE_112(__SHIFT) +}; + +static const struct dce110_clk_src_mask cs_mask = { + CS_COMMON_MASK_SH_LIST_DCE_112(_MASK) +}; + +static struct output_pixel_processor *dce120_opp_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce110_opp *opp = + kzalloc(sizeof(struct dce110_opp), GFP_KERNEL); + + if (!opp) + return NULL; + + dce110_opp_construct(opp, + ctx, inst, &opp_regs[inst], &opp_shift, &opp_mask); + return &opp->base; +} +static struct dce_aux *dce120_aux_engine_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct aux_engine_dce110 *aux_engine = + kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + + if (!aux_engine) + return NULL; + + dce110_aux_engine_construct(aux_engine, ctx, inst, + SW_AUX_TIMEOUT_PERIOD_MULTIPLIER * AUX_TIMEOUT_PERIOD, + &aux_engine_regs[inst], + &aux_mask, + &aux_shift, + ctx->dc->caps.extended_aux_timeout_support); + + return &aux_engine->base; +} +#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) } + +static const struct dce_i2c_registers i2c_hw_regs[] = { + i2c_inst_regs(1), + i2c_inst_regs(2), + i2c_inst_regs(3), + i2c_inst_regs(4), + i2c_inst_regs(5), + i2c_inst_regs(6), +}; + +static const struct dce_i2c_shift i2c_shifts = { + I2C_COMMON_MASK_SH_LIST_DCE110(__SHIFT) +}; + +static const struct dce_i2c_mask i2c_masks = { + I2C_COMMON_MASK_SH_LIST_DCE110(_MASK) +}; + +static struct dce_i2c_hw *dce120_i2c_hw_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_i2c_hw *dce_i2c_hw = + kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + + if (!dce_i2c_hw) + return NULL; + + dce112_i2c_hw_construct(dce_i2c_hw, ctx, inst, + &i2c_hw_regs[inst], &i2c_shifts, &i2c_masks); + + return dce_i2c_hw; +} +static const struct bios_registers bios_regs = { + .BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3 + NBIO_BASE(mmBIOS_SCRATCH_3_BASE_IDX), + .BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX) +}; + +static const struct resource_caps res_cap = { + .num_timing_generator = 6, + .num_audio = 7, + .num_stream_encoder = 6, + .num_pll = 6, + .num_ddc = 6, +}; + +static const struct dc_plane_cap plane_cap = { + .type = DC_PLANE_TYPE_DCE_RGB, + + .pixel_format_support = { + .argb8888 = true, + .nv12 = false, + .fp16 = true + }, + + .max_upscale_factor = { + .argb8888 = 16000, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { + .argb8888 = 250, + .nv12 = 1, + .fp16 = 1 + } +}; + +static const struct dc_debug_options debug_defaults = { + .disable_clock_gate = true, +}; + +static struct clock_source *dce120_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 = + kzalloc(sizeof(*clk_src), GFP_KERNEL); + + if (!clk_src) + return NULL; + + if (dce112_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; + } + + kfree(clk_src); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void dce120_clock_source_destroy(struct clock_source **clk_src) +{ + kfree(TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + + +static bool dce120_hw_sequencer_create(struct dc *dc) +{ + /* All registers used by dce11.2 match those in dce11 in offset and + * structure + */ + dce120_hw_sequencer_construct(dc); + + /*TODO Move to separate file and Override what is needed */ + + return true; +} + +static struct timing_generator *dce120_timing_generator_create( + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + kzalloc(sizeof(struct dce110_timing_generator), GFP_KERNEL); + + if (!tg110) + return NULL; + + dce120_timing_generator_construct(tg110, ctx, instance, offsets); + return &tg110->base; +} + +static void dce120_transform_destroy(struct transform **xfm) +{ + kfree(TO_DCE_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static void dce120_resource_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) + dce120_transform_destroy(&pool->base.transforms[i]); + + if (pool->base.ipps[i] != NULL) + dce_ipp_destroy(&pool->base.ipps[i]); + + if (pool->base.mis[i] != NULL) { + kfree(TO_DCE_MEM_INPUT(pool->base.mis[i])); + pool->base.mis[i] = NULL; + } + + if (pool->base.irqs != NULL) { + dal_irq_service_destroy(&pool->base.irqs); + } + + if (pool->base.timing_generators[i] != NULL) { + kfree(DCE110TG_FROM_TG(pool->base.timing_generators[i])); + pool->base.timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + if (pool->base.engines[i] != NULL) + dce110_engine_destroy(&pool->base.engines[i]); + if (pool->base.hw_i2cs[i] != NULL) { + kfree(pool->base.hw_i2cs[i]); + pool->base.hw_i2cs[i] = NULL; + } + if (pool->base.sw_i2cs[i] != NULL) { + kfree(pool->base.sw_i2cs[i]); + pool->base.sw_i2cs[i] = NULL; + } + } + + for (i = 0; i < pool->base.audio_count; i++) { + if (pool->base.audios[i]) + dce_aud_destroy(&pool->base.audios[i]); + } + + for (i = 0; i < pool->base.stream_enc_count; i++) { + if (pool->base.stream_enc[i] != NULL) + kfree(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) + dce120_clock_source_destroy( + &pool->base.clock_sources[i]); + } + + if (pool->base.dp_clock_source != NULL) + dce120_clock_source_destroy(&pool->base.dp_clock_source); + + if (pool->base.abm != NULL) + dce_abm_destroy(&pool->base.abm); + + if (pool->base.dmcu != NULL) + dce_dmcu_destroy(&pool->base.dmcu); +} + +static void read_dce_straps( + struct dc_context *ctx, + struct resource_straps *straps) +{ + uint32_t reg_val = dm_read_reg_soc15(ctx, mmCC_DC_MISC_STRAPS, 0); + + straps->audio_stream_number = get_reg_field_value(reg_val, + CC_DC_MISC_STRAPS, + AUDIO_STREAM_NUMBER); + straps->hdmi_disable = get_reg_field_value(reg_val, + CC_DC_MISC_STRAPS, + HDMI_DISABLE); + + reg_val = dm_read_reg_soc15(ctx, mmDC_PINSTRAPS, 0); + straps->dc_pinstraps_audio = get_reg_field_value(reg_val, + DC_PINSTRAPS, + 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 const struct encoder_feature_support link_enc_feature = { + .max_hdmi_deep_color = COLOR_DEPTH_121212, + .max_hdmi_pixel_clock = 600000, + .hdmi_ycbcr420_supported = true, + .dp_ycbcr420_supported = false, + .flags.bits.IS_HBR2_CAPABLE = true, + .flags.bits.IS_HBR3_CAPABLE = true, + .flags.bits.IS_TPS3_CAPABLE = true, + .flags.bits.IS_TPS4_CAPABLE = true, +}; + +static struct link_encoder *dce120_link_encoder_create( + struct dc_context *ctx, + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL); + int link_regs_id; + + if (!enc110) + return NULL; + + link_regs_id = + map_transmitter_id_to_phy_instance(enc_init_data->transmitter); + + dce110_link_encoder_construct(enc110, + enc_init_data, + &link_enc_feature, + &link_enc_regs[link_regs_id], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_hpd_regs[enc_init_data->hpd_source]); + + return &enc110->base; +} + +static struct panel_cntl *dce120_panel_cntl_create(const struct panel_cntl_init_data *init_data) +{ + struct dce_panel_cntl *panel_cntl = + kzalloc(sizeof(struct dce_panel_cntl), GFP_KERNEL); + + if (!panel_cntl) + return NULL; + + dce_panel_cntl_construct(panel_cntl, + init_data, + &panel_cntl_regs[init_data->inst], + &panel_cntl_shift, + &panel_cntl_mask); + + return &panel_cntl->base; +} + +static struct input_pixel_processor *dce120_ipp_create( + struct dc_context *ctx, uint32_t inst) +{ + struct dce_ipp *ipp = kzalloc(sizeof(struct dce_ipp), GFP_KERNEL); + + if (!ipp) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce_ipp_construct(ipp, ctx, inst, + &ipp_regs[inst], &ipp_shift, &ipp_mask); + return &ipp->base; +} + +static struct stream_encoder *dce120_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dce110_stream_encoder *enc110 = + kzalloc(sizeof(struct dce110_stream_encoder), GFP_KERNEL); + + if (!enc110) + return NULL; + + dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id, + &stream_enc_regs[eng_id], + &se_shift, &se_mask); + return &enc110->base; +} + +#define SRII(reg_name, block, id)\ + .reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + mm ## block ## id ## _ ## reg_name + +static const struct dce_hwseq_registers hwseq_reg = { + HWSEQ_DCE120_REG_LIST() +}; + +static const struct dce_hwseq_shift hwseq_shift = { + HWSEQ_DCE12_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask hwseq_mask = { + HWSEQ_DCE12_MASK_SH_LIST(_MASK) +}; + +/* HWSEQ regs for VG20 */ +static const struct dce_hwseq_registers dce121_hwseq_reg = { + HWSEQ_VG20_REG_LIST() +}; + +static const struct dce_hwseq_shift dce121_hwseq_shift = { + HWSEQ_VG20_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_hwseq_mask dce121_hwseq_mask = { + HWSEQ_VG20_MASK_SH_LIST(_MASK) +}; + +static struct dce_hwseq *dce120_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &hwseq_reg; + hws->shifts = &hwseq_shift; + hws->masks = &hwseq_mask; + } + return hws; +} + +static struct dce_hwseq *dce121_hwseq_create( + struct dc_context *ctx) +{ + struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + + if (hws) { + hws->ctx = ctx; + hws->regs = &dce121_hwseq_reg; + hws->shifts = &dce121_hwseq_shift; + hws->masks = &dce121_hwseq_mask; + } + return hws; +} + +static const struct resource_create_funcs res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce120_stream_encoder_create, + .create_hwseq = dce120_hwseq_create, +}; + +static const struct resource_create_funcs dce121_res_create_funcs = { + .read_dce_straps = read_dce_straps, + .create_audio = create_audio, + .create_stream_encoder = dce120_stream_encoder_create, + .create_hwseq = dce121_hwseq_create, +}; + + +#define mi_inst_regs(id) { MI_DCE12_REG_LIST(id) } +static const struct dce_mem_input_registers mi_regs[] = { + mi_inst_regs(0), + mi_inst_regs(1), + mi_inst_regs(2), + mi_inst_regs(3), + mi_inst_regs(4), + mi_inst_regs(5), +}; + +static const struct dce_mem_input_shift mi_shifts = { + MI_DCE12_MASK_SH_LIST(__SHIFT) +}; + +static const struct dce_mem_input_mask mi_masks = { + MI_DCE12_MASK_SH_LIST(_MASK) +}; + +static struct mem_input *dce120_mem_input_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_mem_input *dce_mi = kzalloc(sizeof(struct dce_mem_input), + GFP_KERNEL); + + if (!dce_mi) { + BREAK_TO_DEBUGGER(); + return NULL; + } + + dce120_mem_input_construct(dce_mi, ctx, inst, &mi_regs[inst], &mi_shifts, &mi_masks); + return &dce_mi->base; +} + +static struct transform *dce120_transform_create( + struct dc_context *ctx, + uint32_t inst) +{ + struct dce_transform *transform = + kzalloc(sizeof(struct dce_transform), GFP_KERNEL); + + if (!transform) + return NULL; + + dce_transform_construct(transform, ctx, inst, + &xfm_regs[inst], &xfm_shift, &xfm_mask); + transform->lb_memory_size = 0x1404; /*5124*/ + return &transform->base; +} + +static void dce120_destroy_resource_pool(struct resource_pool **pool) +{ + struct dce110_resource_pool *dce110_pool = TO_DCE110_RES_POOL(*pool); + + dce120_resource_destruct(dce110_pool); + kfree(dce110_pool); + *pool = NULL; +} + +static const struct resource_funcs dce120_res_pool_funcs = { + .destroy = dce120_destroy_resource_pool, + .link_enc_create = dce120_link_encoder_create, + .panel_cntl_create = dce120_panel_cntl_create, + .validate_bandwidth = dce112_validate_bandwidth, + .validate_plane = dce100_validate_plane, + .add_stream_to_ctx = dce112_add_stream_to_ctx, + .find_first_free_match_stream_enc_for_link = dce110_find_first_free_match_stream_enc_for_link +}; + +static void bw_calcs_data_update_from_pplib(struct dc *dc) +{ + struct dm_pp_clock_levels_with_latency eng_clks = {0}; + struct dm_pp_clock_levels_with_latency mem_clks = {0}; + struct dm_pp_wm_sets_with_clock_ranges clk_ranges = {0}; + int i; + unsigned int clk; + unsigned int latency; + /*original logic in dal3*/ + int memory_type_multiplier = MEMORY_TYPE_MULTIPLIER_CZ; + + /*do system clock*/ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, + &eng_clks) || eng_clks.num_levels == 0) { + + eng_clks.num_levels = 8; + clk = 300000; + + for (i = 0; i < eng_clks.num_levels; i++) { + eng_clks.data[i].clocks_in_khz = clk; + clk += 100000; + } + } + + /* convert all the clock fro kHz to fix point mHz TODO: wloop data */ + dc->bw_vbios->high_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels-1].clocks_in_khz, 1000); + dc->bw_vbios->mid1_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels/8].clocks_in_khz, 1000); + dc->bw_vbios->mid2_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*2/8].clocks_in_khz, 1000); + dc->bw_vbios->mid3_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz, 1000); + dc->bw_vbios->mid4_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*4/8].clocks_in_khz, 1000); + dc->bw_vbios->mid5_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*5/8].clocks_in_khz, 1000); + dc->bw_vbios->mid6_sclk = bw_frc_to_fixed( + eng_clks.data[eng_clks.num_levels*6/8].clocks_in_khz, 1000); + dc->bw_vbios->low_sclk = bw_frc_to_fixed( + eng_clks.data[0].clocks_in_khz, 1000); + + /*do memory clock*/ + if (!dm_pp_get_clock_levels_by_type_with_latency( + dc->ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, + &mem_clks) || mem_clks.num_levels == 0) { + + mem_clks.num_levels = 3; + clk = 250000; + latency = 45; + + for (i = 0; i < eng_clks.num_levels; i++) { + mem_clks.data[i].clocks_in_khz = clk; + mem_clks.data[i].latency_in_us = latency; + clk += 500000; + latency -= 5; + } + + } + + /* we don't need to call PPLIB for validation clock since they + * also give us the highest sclk and highest mclk (UMA clock). + * ALSO always convert UMA clock (from PPLIB) to YCLK (HW formula): + * YCLK = UMACLK*m_memoryTypeMultiplier + */ + if (dc->bw_vbios->memory_type == bw_def_hbm) + memory_type_multiplier = MEMORY_TYPE_HBM; + + dc->bw_vbios->low_yclk = bw_frc_to_fixed( + mem_clks.data[0].clocks_in_khz * memory_type_multiplier, 1000); + dc->bw_vbios->mid_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz * memory_type_multiplier, + 1000); + dc->bw_vbios->high_yclk = bw_frc_to_fixed( + mem_clks.data[mem_clks.num_levels-1].clocks_in_khz * memory_type_multiplier, + 1000); + + /* Now notify PPLib/SMU about which Watermarks sets they should select + * depending on DPM state they are in. And update BW MGR GFX Engine and + * Memory clock member variables for Watermarks calculations for each + * Watermark Set + */ + clk_ranges.num_wm_sets = 4; + clk_ranges.wm_clk_ranges[0].wm_set_id = WM_SET_A; + clk_ranges.wm_clk_ranges[0].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[0].wm_min_mem_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[0].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[1].wm_set_id = WM_SET_B; + clk_ranges.wm_clk_ranges[1].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[1].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[1].wm_min_mem_clk_in_khz = + mem_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[1].wm_max_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz - 1; + + clk_ranges.wm_clk_ranges[2].wm_set_id = WM_SET_C; + clk_ranges.wm_clk_ranges[2].wm_min_eng_clk_in_khz = + eng_clks.data[0].clocks_in_khz; + clk_ranges.wm_clk_ranges[2].wm_max_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz - 1; + clk_ranges.wm_clk_ranges[2].wm_min_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[2].wm_max_mem_clk_in_khz = 5000000; + + clk_ranges.wm_clk_ranges[3].wm_set_id = WM_SET_D; + clk_ranges.wm_clk_ranges[3].wm_min_eng_clk_in_khz = + eng_clks.data[eng_clks.num_levels*3/8].clocks_in_khz; + /* 5 GHz instead of data[7].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_eng_clk_in_khz = 5000000; + clk_ranges.wm_clk_ranges[3].wm_min_mem_clk_in_khz = + mem_clks.data[mem_clks.num_levels>>1].clocks_in_khz; + /* 5 GHz instead of data[2].clockInKHz to cover Overdrive */ + clk_ranges.wm_clk_ranges[3].wm_max_mem_clk_in_khz = 5000000; + + /* Notify PP Lib/SMU which Watermarks to use for which clock ranges */ + dm_pp_notify_wm_clock_changes(dc->ctx, &clk_ranges); +} + +static uint32_t read_pipe_fuses(struct dc_context *ctx) +{ + uint32_t value = dm_read_reg_soc15(ctx, mmCC_DC_PIPE_DIS, 0); + /* VG20 support max 6 pipes */ + value = value & 0x3f; + return value; +} + +static bool dce120_resource_construct( + uint8_t num_virtual_links, + struct dc *dc, + struct dce110_resource_pool *pool) +{ + unsigned int i; + int j; + struct dc_context *ctx = dc->ctx; + struct irq_service_init_data irq_init_data; + static const struct resource_create_funcs *res_funcs; + bool is_vg20 = ASICREV_IS_VEGA20_P(ctx->asic_id.hw_internal_rev); + uint32_t pipe_fuses; + + ctx->dc_bios->regs = &bios_regs; + + pool->base.res_cap = &res_cap; + pool->base.funcs = &dce120_res_pool_funcs; + + /* TODO: Fill more data from GreenlandAsicCapability.cpp */ + pool->base.pipe_count = res_cap.num_timing_generator; + pool->base.timing_generator_count = pool->base.res_cap->num_timing_generator; + pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE; + + dc->caps.max_downscale_ratio = 200; + dc->caps.i2c_speed_in_khz = 100; + dc->caps.i2c_speed_in_khz_hdcp = 100; /*1.4 w/a not applied by default*/ + dc->caps.max_cursor_size = 128; + dc->caps.min_horizontal_blanking_period = 80; + dc->caps.dual_link_dvi = true; + dc->caps.psp_setup_panel_mode = true; + dc->caps.extended_aux_timeout_support = false; + dc->debug = debug_defaults; + + /************************************************* + * Create resources * + *************************************************/ + + pool->base.clock_sources[DCE120_CLK_SRC_PLL0] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL0, + &clk_src_regs[0], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL1] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL1, + &clk_src_regs[1], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL2] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL2, + &clk_src_regs[2], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL3] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL3, + &clk_src_regs[3], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL4] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL4, + &clk_src_regs[4], false); + pool->base.clock_sources[DCE120_CLK_SRC_PLL5] = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_COMBO_PHY_PLL5, + &clk_src_regs[5], false); + pool->base.clk_src_count = DCE120_CLK_SRC_TOTAL; + + pool->base.dp_clock_source = + dce120_clock_source_create(ctx, ctx->dc_bios, + CLOCK_SOURCE_ID_DP_DTO, + &clk_src_regs[0], true); + + 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 clk_src_create_fail; + } + } + + pool->base.dmcu = dce_dmcu_create(ctx, + &dmcu_regs, + &dmcu_shift, + &dmcu_mask); + if (pool->base.dmcu == NULL) { + dm_error("DC: failed to create dmcu!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + pool->base.abm = dce_abm_create(ctx, + &abm_regs, + &abm_shift, + &abm_mask); + if (pool->base.abm == NULL) { + dm_error("DC: failed to create abm!\n"); + BREAK_TO_DEBUGGER(); + goto res_create_fail; + } + + + irq_init_data.ctx = dc->ctx; + pool->base.irqs = dal_irq_service_dce120_create(&irq_init_data); + if (!pool->base.irqs) + goto irqs_create_fail; + + /* VG20: Pipe harvesting enabled, retrieve valid pipe fuses */ + if (is_vg20) + pipe_fuses = read_pipe_fuses(ctx); + + /* index to valid pipe resource */ + j = 0; + for (i = 0; i < pool->base.pipe_count; i++) { + if (is_vg20) { + if ((pipe_fuses & (1 << i)) != 0) { + dm_error("DC: skip invalid pipe %d!\n", i); + continue; + } + } + + pool->base.timing_generators[j] = + dce120_timing_generator_create( + ctx, + i, + &dce120_tg_offsets[i]); + if (pool->base.timing_generators[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto controller_create_fail; + } + + pool->base.mis[j] = dce120_mem_input_create(ctx, i); + + if (pool->base.mis[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto controller_create_fail; + } + + pool->base.ipps[j] = dce120_ipp_create(ctx, i); + if (pool->base.ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto controller_create_fail; + } + + pool->base.transforms[j] = dce120_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[j] = dce120_opp_create( + ctx, + i); + if (pool->base.opps[j] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + } + + /* check next valid pipe */ + j++; + } + + for (i = 0; i < pool->base.res_cap->num_ddc; i++) { + pool->base.engines[i] = dce120_aux_engine_create(ctx, i); + if (pool->base.engines[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create aux engine!!\n"); + goto res_create_fail; + } + pool->base.hw_i2cs[i] = dce120_i2c_hw_create(ctx, i); + if (pool->base.hw_i2cs[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC:failed to create i2c engine!!\n"); + goto res_create_fail; + } + pool->base.sw_i2cs[i] = NULL; + } + + /* valid pipe num */ + pool->base.pipe_count = j; + pool->base.timing_generator_count = j; + + if (is_vg20) + res_funcs = &dce121_res_create_funcs; + else + res_funcs = &res_create_funcs; + + if (!resource_construct(num_virtual_links, dc, &pool->base, res_funcs)) + goto res_create_fail; + + /* Create hardware sequencer */ + if (!dce120_hw_sequencer_create(dc)) + goto controller_create_fail; + + dc->caps.max_planes = pool->base.pipe_count; + + for (i = 0; i < dc->caps.max_planes; ++i) + dc->caps.planes[i] = plane_cap; + + bw_calcs_init(dc->bw_dceip, dc->bw_vbios, dc->ctx->asic_id); + + bw_calcs_data_update_from_pplib(dc); + + return true; + +irqs_create_fail: +controller_create_fail: +clk_src_create_fail: +res_create_fail: + + dce120_resource_destruct(pool); + + return false; +} + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc) +{ + struct dce110_resource_pool *pool = + kzalloc(sizeof(struct dce110_resource_pool), GFP_KERNEL); + + if (!pool) + return NULL; + + if (dce120_resource_construct(num_virtual_links, dc, pool)) + return &pool->base; + + kfree(pool); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h new file mode 100644 index 000000000..3d1f3cf01 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_resource.h @@ -0,0 +1,39 @@ +/* +* 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_DCE120_H__ +#define __DC_RESOURCE_DCE120_H__ + +#include "core_types.h" + +struct dc; +struct resource_pool; + +struct resource_pool *dce120_create_resource_pool( + uint8_t num_virtual_links, + struct dc *dc); + +#endif /* __DC_RESOURCE_DCE120_H__ */ + diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c new file mode 100644 index 000000000..4af0c7009 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.c @@ -0,0 +1,1232 @@ +/* + * 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_12_0_offset.h" +#include "dce/dce_12_0_sh_mask.h" +#include "soc15_hw_ip.h" +#include "vega10_ip_offset.h" + +#include "dc_types.h" +#include "dc_bios_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" +#include "dce120_timing_generator.h" + +#include "timing_generator.h" + +#define CRTC_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) + +#define CRTC_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) + +#define CRTC_REG_UPDATE(reg, field, val) \ + CRTC_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define CRTC_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + CRTC_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define CRTC_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +#define CRTC_REG_UPDATE_4(reg, field1, val1, field2, val2, field3, val3, field4, val4) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4) + +#define CRTC_REG_UPDATE_5(reg, field1, val1, field2, val2, field3, val3, field4, val4, field5, val5) \ + CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4, FD(reg##__##field5), val5) + +#define CRTC_REG_SET(reg, field, val) \ + CRTC_REG_SET_N(reg, 1, FD(reg##__##field), val) + +#define CRTC_REG_SET_2(reg, field1, val1, field2, val2) \ + CRTC_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define CRTC_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ + CRTC_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + +/* + ***************************************************************************** + * 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 dce120_timing_generator_is_in_vertical_blank( + struct timing_generator *tg) +{ + uint32_t field = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS, + tg110->offsets.crtc); + + field = get_reg_field_value(value, CRTC0_CRTC_STATUS, CRTC_V_BLANK); + return field == 1; +} + + +/* determine if given timing can be supported by TG */ +static bool dce120_timing_generator_validate_timing( + struct timing_generator *tg, + const struct dc_crtc_timing *timing, + enum signal_type signal) +{ + uint32_t interlace_factor = timing->flags.INTERLACE ? 2 : 1; + uint32_t v_blank = + (timing->v_total - timing->v_addressable - + timing->v_border_top - timing->v_border_bottom) * + interlace_factor; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + if (!dce110_timing_generator_validate_timing( + tg, + timing, + signal)) + return false; + + + if (v_blank < tg110->min_v_blank || + timing->h_sync_width < tg110->min_h_sync_width || + timing->v_sync_width < tg110->min_v_sync_width) + return false; + + return true; +} + +static bool dce120_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + return dce120_timing_generator_validate_timing(tg, timing, SIGNAL_TYPE_NONE); +} + +/******** HW programming ************/ +/* Disable/Enable Timing Generator */ +static bool dce120_timing_generator_enable_crtc(struct timing_generator *tg) +{ + enum bp_result result; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Set MASTER_UPDATE_MODE to 0 + * This is needed for DRR, and also suggested to be default value by Syed.*/ + + CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_MODE, + MASTER_UPDATE_MODE, 0); + + CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_LOCK, + UNDERFLOW_UPDATE_LOCK, 0); + + /* TODO API for AtomFirmware didn't change*/ + result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); + + return result == BP_RESULT_OK; +} + +static void dce120_timing_generator_set_early_control( + struct timing_generator *tg, + uint32_t early_cntl) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE(CRTC0_CRTC_CONTROL, + CRTC_HBLANK_EARLY_CONTROL, early_cntl); +} + +/**************** TG current status ******************/ + +/* return the current frame counter. Used by Linux kernel DRM */ +static uint32_t dce120_timing_generator_get_vblank_counter( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_FRAME_COUNT, + tg110->offsets.crtc); + uint32_t field = get_reg_field_value( + value, CRTC0_CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); + + return field; +} + +/* Get current H and V position */ +static void dce120_timing_generator_get_crtc_position( + struct timing_generator *tg, + struct crtc_position *position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_STATUS_POSITION, + tg110->offsets.crtc); + + position->horizontal_count = get_reg_field_value(value, + CRTC0_CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); + + position->vertical_count = get_reg_field_value(value, + CRTC0_CRTC_STATUS_POSITION, CRTC_VERT_COUNT); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_NOM_VERT_POSITION, + tg110->offsets.crtc); + + position->nominal_vcount = get_reg_field_value(value, + CRTC0_CRTC_NOM_VERT_POSITION, CRTC_VERT_COUNT_NOM); +} + +/* wait until TG is in beginning of vertical blank region */ +static void dce120_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 (dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } + + while (!dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/* wait until TG is in beginning of active region */ +static void dce120_timing_generator_wait_for_vactive(struct timing_generator *tg) +{ + while (dce120_timing_generator_is_in_vertical_blank(tg)) { + if (!tg->funcs->is_counter_moving(tg)) { + /* error - no point to wait if counter is not moving */ + break; + } + } +} + +/*********** Timing Generator Synchronization routines ****/ + +/* Setups Global Swap Lock group, TimingServer or TimingClient*/ +static void dce120_timing_generator_setup_global_swap_lock( + struct timing_generator *tg, + const struct dcp_gsl_params *gsl_params) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value_crtc_vtotal = + dm_read_reg_soc15(tg->ctx, + mmCRTC0_CRTC_V_TOTAL, + tg110->offsets.crtc); + /* Checkpoint relative to end of frame */ + uint32_t check_point = + get_reg_field_value(value_crtc_vtotal, + CRTC0_CRTC_V_TOTAL, + CRTC_V_TOTAL); + + + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_GSL_WINDOW, tg110->offsets.crtc, 0); + + CRTC_REG_UPDATE_N(DCP0_DCP_GSL_CONTROL, 6, + /* This pipe will belong to GSL Group zero. */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 1, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), gsl_params->gsl_master == tg->inst, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, + /* Keep signal low (pending high) during 6 lines. + * Also defines minimum interval before re-checking signal. */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, + /* DCP_GSL_PURPOSE_SURFACE_FLIP */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 1); + + CRTC_REG_SET_2( + CRTC0_CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM, check_point - FLIP_READY_BACK_LOOKUP, + CRTC_GSL_FORCE_DELAY, VFLIP_READY_DELAY); +} + +/* Clear all the register writes done by setup_global_swap_lock */ +static void dce120_timing_generator_tear_down_global_swap_lock( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Settig HW default values from reg specs */ + CRTC_REG_SET_N(DCP0_DCP_GSL_CONTROL, 6, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, + /* DCP_GSL_PURPOSE_SURFACE_FLIP */ + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, + FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 0); + + CRTC_REG_SET_2(CRTC0_CRTC_GSL_CONTROL, + CRTC_GSL_CHECK_LINE_NUM, 0, + CRTC_GSL_FORCE_DELAY, 0x2); /*TODO Why this value here ?*/ +} + +/* Reset slave controllers on master VSync */ +static void dce120_timing_generator_enable_reset_trigger( + struct timing_generator *tg, + int source) +{ + enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t rising_edge = 0; + uint32_t falling_edge = 0; + /* Setup trigger edge */ + uint32_t pol_value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_SYNC_A_CNTL, + tg110->offsets.crtc); + + /* Register spec has reversed definition: + * 0 for positive, 1 for negative */ + if (get_reg_field_value(pol_value, + CRTC0_CRTC_V_SYNC_A_CNTL, + CRTC_V_SYNC_A_POL) == 0) { + rising_edge = 1; + } else { + falling_edge = 1; + } + + /* TODO What about other sources ?*/ + trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; + + CRTC_REG_UPDATE_N(CRTC0_CRTC_TRIGB_CNTL, 7, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_SOURCE_SELECT), trig_src_select, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_POLARITY_SELECT), TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_RISING_EDGE_DETECT_CNTL), rising_edge, + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL), falling_edge, + /* send every signal */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FREQUENCY_SELECT), 0, + /* no delay */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_DELAY), 0, + /* clear trigger status */ + FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_CLEAR), 1); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE, 2, + CRTC_FORCE_COUNT_NOW_TRIG_SEL, 1, + CRTC_FORCE_COUNT_NOW_CLEAR, 1); +} + +/* disabling trigger-reset */ +static void dce120_timing_generator_disable_reset_trigger( + struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_MODE, 0, + CRTC_FORCE_COUNT_NOW_CLEAR, 1); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_TRIGB_CNTL, + CRTC_TRIGB_SOURCE_SELECT, TRIGGER_SOURCE_SELECT_LOGIC_ZERO, + CRTC_TRIGB_POLARITY_SELECT, TRIGGER_POLARITY_SELECT_LOGIC_ZERO, + /* clear trigger status */ + CRTC_TRIGB_CLEAR, 1); + +} + +/* Checks whether CRTC triggered reset occurred */ +static bool dce120_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_soc15( + tg->ctx, + mmCRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + tg110->offsets.crtc); + + return get_reg_field_value(value, + CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, + CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; +} + + +/******** Stuff to move to other virtual HW objects *****************/ +/* Move to enable accelerated mode */ +static void dce120_timing_generator_disable_vga(struct timing_generator *tg) +{ + uint32_t offset = 0; + uint32_t value = 0; + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + switch (tg110->controller_id) { + case CONTROLLER_ID_D0: + offset = 0; + break; + case CONTROLLER_ID_D1: + offset = mmD2VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D2: + offset = mmD3VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D3: + offset = mmD4VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D4: + offset = mmD5VGA_CONTROL - mmD1VGA_CONTROL; + break; + case CONTROLLER_ID_D5: + offset = mmD6VGA_CONTROL - mmD1VGA_CONTROL; + break; + default: + break; + } + + value = dm_read_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset); + + 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_soc15(tg->ctx, mmD1VGA_CONTROL, offset, value); +} +/* TODO: Should we move it to transform */ +/* Fully program CRTC timing in timing generator */ +static void dce120_timing_generator_program_blanking( + struct timing_generator *tg, + const struct dc_crtc_timing *timing) +{ + uint32_t tmp1 = 0; + uint32_t tmp2 = 0; + 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); + + CRTC_REG_UPDATE( + CRTC0_CRTC_H_TOTAL, + CRTC_H_TOTAL, + timing->h_total - 1); + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL, + CRTC_V_TOTAL, + timing->v_total - 1); + + /* In case of V_TOTAL_CONTROL is on, make sure V_TOTAL_MAX and + * V_TOTAL_MIN are equal to V_TOTAL. + */ + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, + timing->v_total - 1); + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, + timing->v_total - 1); + + tmp1 = timing->h_total - + (h_sync_start + timing->h_border_left); + tmp2 = tmp1 + timing->h_addressable + + timing->h_border_left + timing->h_border_right; + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_H_BLANK_START_END, + CRTC_H_BLANK_END, tmp1, + CRTC_H_BLANK_START, tmp2); + + tmp1 = timing->v_total - (v_sync_start + timing->v_border_top); + tmp2 = tmp1 + timing->v_addressable + timing->v_border_top + + timing->v_border_bottom; + + CRTC_REG_UPDATE_2( + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END, tmp1, + CRTC_V_BLANK_START, tmp2); +} + +/* TODO: Should we move it to opp? */ +/* Combine with below and move YUV/RGB color conversion to SW layer */ +static void dce120_timing_generator_program_blank_color( + struct timing_generator *tg, + const struct tg_color *black_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, + CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, + CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); +} +/* Combine with above and move YUV/RGB color conversion to SW layer */ +static void dce120_timing_generator_set_overscan_color_black( + struct timing_generator *tg, + const struct tg_color *color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = 0; + CRTC_REG_SET_3( + CRTC0_CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE, color->color_b_cb, + CRTC_OVERSCAN_COLOR_GREEN, color->color_g_y, + CRTC_OVERSCAN_COLOR_RED, color->color_r_cr); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_OVERSCAN_COLOR, + tg110->offsets.crtc); + + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLACK_COLOR, + tg110->offsets.crtc, + 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. */ + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_DATA_COLOR, + tg110->offsets.crtc, + 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 dce120_timing_generator_set_drr( + struct timing_generator *tg, + const struct drr_params *params) +{ + + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + if (params != NULL && + params->vertical_total_max > 0 && + params->vertical_total_min > 0) { + + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, params->vertical_total_min - 1); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, params->vertical_total_max - 1); + CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 6, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 1, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 1, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK_EN), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0x180); + + } else { + CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 5, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, + FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MIN, + CRTC_V_TOTAL_MIN, 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_V_TOTAL_MAX, + CRTC_V_TOTAL_MAX, 0); + CRTC_REG_UPDATE( + CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, + 0); + } +} + +static void dce120_timing_generator_get_crtc_scanoutpos( + struct timing_generator *tg, + uint32_t *v_blank_start, + uint32_t *v_blank_end, + uint32_t *h_position, + uint32_t *v_position) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + struct crtc_position position; + + uint32_t v_blank_start_end = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_V_BLANK_START_END, + tg110->offsets.crtc); + + *v_blank_start = get_reg_field_value(v_blank_start_end, + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_START); + *v_blank_end = get_reg_field_value(v_blank_start_end, + CRTC0_CRTC_V_BLANK_START_END, + CRTC_V_BLANK_END); + + dce120_timing_generator_get_crtc_position( + tg, &position); + + *h_position = position.horizontal_count; + *v_position = position.vertical_count; +} + +static void dce120_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 v_sync_width_and_b_porch = + timing->v_total - timing->v_addressable - + timing->v_border_bottom - timing->v_front_porch; + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_START_LINE_CONTROL, + tg110->offsets.crtc); + + set_reg_field_value( + value, + enable ? 0 : 1, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_LEGACY_REQUESTOR_EN); + + /* Program advanced line position acc.to the best case from fetching data perspective to hide MC latency + * and prefilling Line Buffer in V Blank (to 10 lines as LB can store max 10 lines) + */ + if (v_sync_width_and_b_porch > 10) + v_sync_width_and_b_porch = 10; + + set_reg_field_value( + value, + v_sync_width_and_b_porch, + CRTC0_CRTC_START_LINE_CONTROL, + CRTC_ADVANCED_START_LINE_POSITION); + + dm_write_reg_soc15(tg->ctx, + mmCRTC0_CRTC_START_LINE_CONTROL, + tg110->offsets.crtc, + value); +} + +static void dce120_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 value = 0; + + CRTC_REG_UPDATE_3( + CRTC0_CRTC_BLACK_COLOR, + CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, + CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, + CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); + + value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLACK_COLOR, + tg110->offsets.crtc); + dm_write_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_DATA_COLOR, + tg110->offsets.crtc, + value); +} + +static void dce120_tg_set_overscan_color(struct timing_generator *tg, + const struct tg_color *overscan_color) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_SET_3( + CRTC0_CRTC_OVERSCAN_COLOR, + CRTC_OVERSCAN_COLOR_BLUE, overscan_color->color_b_cb, + CRTC_OVERSCAN_COLOR_GREEN, overscan_color->color_g_y, + CRTC_OVERSCAN_COLOR_RED, overscan_color->color_r_cr); +} + +static void dce120_tg_program_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing, + int vready_offset, + int vstartup_start, + int vupdate_offset, + int vupdate_width, + const enum signal_type signal, + bool use_vbios) +{ + if (use_vbios) + dce110_timing_generator_program_timing_generator(tg, timing); + else + dce120_timing_generator_program_blanking(tg, timing); +} + +static bool dce120_tg_is_blanked(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value = dm_read_reg_soc15( + tg->ctx, + mmCRTC0_CRTC_BLANK_CONTROL, + tg110->offsets.crtc); + + if (get_reg_field_value( + value, + CRTC0_CRTC_BLANK_CONTROL, + CRTC_BLANK_DATA_EN) == 1 && + get_reg_field_value( + value, + CRTC0_CRTC_BLANK_CONTROL, + CRTC_CURRENT_BLANK_STATE) == 1) + return true; + + return false; +} + +static void dce120_tg_set_blank(struct timing_generator *tg, + bool enable_blanking) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + CRTC_REG_SET( + CRTC0_CRTC_DOUBLE_BUFFER_CONTROL, + CRTC_BLANK_DATA_DOUBLE_BUFFER_EN, 1); + + if (enable_blanking) + CRTC_REG_SET(CRTC0_CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN, 1); + else + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_BLANK_CONTROL, + tg110->offsets.crtc, 0); +} + +bool dce120_tg_validate_timing(struct timing_generator *tg, + const struct dc_crtc_timing *timing); + +static void dce120_tg_wait_for_state(struct timing_generator *tg, + enum crtc_state state) +{ + switch (state) { + case CRTC_STATE_VBLANK: + dce120_timing_generator_wait_for_vblank(tg); + break; + + case CRTC_STATE_VACTIVE: + dce120_timing_generator_wait_for_vactive(tg); + break; + + default: + break; + } +} + +static void dce120_tg_set_colors(struct timing_generator *tg, + const struct tg_color *blank_color, + const struct tg_color *overscan_color) +{ + if (blank_color != NULL) + dce120_tg_program_blank_color(tg, blank_color); + + if (overscan_color != NULL) + dce120_tg_set_overscan_color(tg, overscan_color); +} + +static void dce120_timing_generator_set_static_screen_control( + struct timing_generator *tg, + uint32_t event_triggers, + uint32_t num_frames) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + // By register spec, it only takes 8 bit value + if (num_frames > 0xFF) + num_frames = 0xFF; + + CRTC_REG_UPDATE_2(CRTC0_CRTC_STATIC_SCREEN_CONTROL, + CRTC_STATIC_SCREEN_EVENT_MASK, event_triggers, + CRTC_STATIC_SCREEN_FRAME_COUNT, num_frames); +} + +static void dce120_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; + 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; + + CRTC_REG_UPDATE_2(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_HRES, 6); + + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, dyn_range, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + 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); + } + + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, 0); + + /* 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; + for (index = 0; index < 6; index++) { + /* prepare color mask, first write PATTERN_DATA + * will have all zeros + */ + set_reg_field_value( + value, + (1 << index), + CRTC0_CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_MASK); + /* write color component */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + /* prepare next color component, + * will be written in the next iteration + */ + set_reg_field_value( + value, + dst_color[index], + CRTC0_CRTC_TEST_PATTERN_COLOR, + CRTC_TEST_PATTERN_DATA); + } + /* write last color component, + * it's been already prepared in the loop + */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + + /* enable test pattern */ + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + 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); + + switch (bit_depth) { + case TEST_PATTERN_COLOR_FORMAT_BPC_6: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, 0, + CRTC_TEST_PATTERN_HRES, 6, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_8: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, 0, + CRTC_TEST_PATTERN_HRES, 8, + CRTC_TEST_PATTERN_VRES, 6, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_10: + { + CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, + CRTC_TEST_PATTERN_INC0, inc_base, + CRTC_TEST_PATTERN_INC1, inc_base + 2, + CRTC_TEST_PATTERN_HRES, 8, + CRTC_TEST_PATTERN_VRES, 5, + CRTC_TEST_PATTERN_RAMP0_OFFSET, 384 << 6); + } + break; + default: + break; + } + + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, 0); + + /* enable test pattern */ + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, 0); + + CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, + CRTC_TEST_PATTERN_EN, 1, + CRTC_TEST_PATTERN_MODE, mode, + CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, + CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); + } + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + { + value = 0; + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, value); + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); + dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, value); + } + break; + default: + break; + } +} + +static bool dce120_arm_vert_intr( + struct timing_generator *tg, + uint8_t width) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t v_blank_start, v_blank_end, h_position, v_position; + + tg->funcs->get_scanoutpos( + tg, + &v_blank_start, + &v_blank_end, + &h_position, + &v_position); + + if (v_blank_start == 0 || v_blank_end == 0) + return false; + + CRTC_REG_SET_2( + CRTC0_CRTC_VERTICAL_INTERRUPT0_POSITION, + CRTC_VERTICAL_INTERRUPT0_LINE_START, v_blank_start, + CRTC_VERTICAL_INTERRUPT0_LINE_END, v_blank_start + width); + + return true; +} + + +static bool dce120_is_tg_enabled(struct timing_generator *tg) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value, field; + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CONTROL, + tg110->offsets.crtc); + field = get_reg_field_value(value, CRTC0_CRTC_CONTROL, + CRTC_CURRENT_MASTER_EN_STATE); + + return field == 1; +} + +static bool dce120_configure_crc(struct timing_generator *tg, + const struct crc_params *params) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + + /* Cannot configure crc on a CRTC that is disabled */ + if (!dce120_is_tg_enabled(tg)) + return false; + + /* First, disable CRC before we configure it. */ + dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC_CNTL, + tg110->offsets.crtc, 0); + + if (!params->enable) + return true; + + /* Program frame boundaries */ + /* Window A x axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWA_X_CONTROL, + CRTC_CRC0_WINDOWA_X_START, params->windowa_x_start, + CRTC_CRC0_WINDOWA_X_END, params->windowa_x_end); + + /* Window A y axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWA_Y_CONTROL, + CRTC_CRC0_WINDOWA_Y_START, params->windowa_y_start, + CRTC_CRC0_WINDOWA_Y_END, params->windowa_y_end); + + /* Window B x axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWB_X_CONTROL, + CRTC_CRC0_WINDOWB_X_START, params->windowb_x_start, + CRTC_CRC0_WINDOWB_X_END, params->windowb_x_end); + + /* Window B y axis start and end. */ + CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWB_Y_CONTROL, + CRTC_CRC0_WINDOWB_Y_START, params->windowb_y_start, + CRTC_CRC0_WINDOWB_Y_END, params->windowb_y_end); + + /* Set crc mode and selection, and enable. Only using CRC0*/ + CRTC_REG_UPDATE_3(CRTC0_CRTC_CRC_CNTL, + CRTC_CRC_EN, params->continuous_mode ? 1 : 0, + CRTC_CRC0_SELECT, params->selection, + CRTC_CRC_EN, 1); + + return true; +} + +static bool dce120_get_crc(struct timing_generator *tg, uint32_t *r_cr, + uint32_t *g_y, uint32_t *b_cb) +{ + struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); + uint32_t value, field; + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC_CNTL, + tg110->offsets.crtc); + field = get_reg_field_value(value, CRTC0_CRTC_CRC_CNTL, CRTC_CRC_EN); + + /* Early return if CRC is not enabled for this CRTC */ + if (!field) + return false; + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC0_DATA_RG, + tg110->offsets.crtc); + *r_cr = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_RG, CRC0_R_CR); + *g_y = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_RG, CRC0_G_Y); + + value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC0_DATA_B, + tg110->offsets.crtc); + *b_cb = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_B, CRC0_B_CB); + + return true; +} + +static const struct timing_generator_funcs dce120_tg_funcs = { + .validate_timing = dce120_tg_validate_timing, + .program_timing = dce120_tg_program_timing, + .enable_crtc = dce120_timing_generator_enable_crtc, + .disable_crtc = dce110_timing_generator_disable_crtc, + /* used by enable_timing_synchronization. Not need for FPGA */ + .is_counter_moving = dce110_timing_generator_is_counter_moving, + /* never be called */ + .get_position = dce120_timing_generator_get_crtc_position, + .get_frame_count = dce120_timing_generator_get_vblank_counter, + .get_scanoutpos = dce120_timing_generator_get_crtc_scanoutpos, + .set_early_control = dce120_timing_generator_set_early_control, + /* used by enable_timing_synchronization. Not need for FPGA */ + .wait_for_state = dce120_tg_wait_for_state, + .set_blank = dce120_tg_set_blank, + .is_blanked = dce120_tg_is_blanked, + /* never be called */ + .set_colors = dce120_tg_set_colors, + .set_overscan_blank_color = dce120_timing_generator_set_overscan_color_black, + .set_blank_color = dce120_timing_generator_program_blank_color, + .disable_vga = dce120_timing_generator_disable_vga, + .did_triggered_reset_occur = dce120_timing_generator_did_triggered_reset_occur, + .setup_global_swap_lock = dce120_timing_generator_setup_global_swap_lock, + .enable_reset_trigger = dce120_timing_generator_enable_reset_trigger, + .disable_reset_trigger = dce120_timing_generator_disable_reset_trigger, + .tear_down_global_swap_lock = dce120_timing_generator_tear_down_global_swap_lock, + .enable_advanced_request = dce120_timing_generator_enable_advanced_request, + .set_drr = dce120_timing_generator_set_drr, + .get_last_used_drr_vtotal = NULL, + .set_static_screen_control = dce120_timing_generator_set_static_screen_control, + .set_test_pattern = dce120_timing_generator_set_test_pattern, + .arm_vert_intr = dce120_arm_vert_intr, + .is_tg_enabled = dce120_is_tg_enabled, + .configure_crc = dce120_configure_crc, + .get_crc = dce120_get_crc, +}; + + +void dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + tg110->controller_id = CONTROLLER_ID_D0 + instance; + tg110->base.inst = instance; + + tg110->offsets = *offsets; + + tg110->base.funcs = &dce120_tg_funcs; + + tg110->base.ctx = ctx; + tg110->base.bp = ctx->dc_bios; + + tg110->max_h_total = CRTC0_CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; + tg110->max_v_total = CRTC0_CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; + + /*//CRTC requires a minimum HBLANK = 32 pixels and o + * Minimum HSYNC = 8 pixels*/ + tg110->min_h_blank = 32; + /*DCE12_CRTC_Block_ARch.doc*/ + tg110->min_h_front_porch = 0; + tg110->min_h_back_porch = 0; + + tg110->min_h_sync_width = 4; + tg110->min_v_sync_width = 1; + tg110->min_v_blank = 3; +} diff --git a/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h new file mode 100644 index 000000000..549d70b23 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dce120/dce120_timing_generator.h @@ -0,0 +1,40 @@ +/* + * 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_DCE120_H__ +#define __DC_TIMING_GENERATOR_DCE120_H__ + +#include "timing_generator.h" +#include "../include/grph_object_id.h" +#include "dce110/dce110_timing_generator.h" + + +void dce120_timing_generator_construct( + struct dce110_timing_generator *tg110, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets); + +#endif /* __DC_TIMING_GENERATOR_DCE120_H__ */ |