diff options
author | Harry Wentland <harry.wentland@amd.com> | 2017-05-08 15:19:06 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-09-26 18:06:48 -0400 |
commit | 70ccab604049bbb995a57ab3b7fe8a3c2fdbb736 (patch) | |
tree | e127d03a03b793414151e79aac67e06216850efd /drivers/gpu/drm | |
parent | 74c49c7ac14f3a7cc500be959709f3473a6a49e7 (diff) | |
download | linux-70ccab604049bbb995a57ab3b7fe8a3c2fdbb736.tar.gz linux-70ccab604049bbb995a57ab3b7fe8a3c2fdbb736.tar.bz2 linux-70ccab604049bbb995a57ab3b7fe8a3c2fdbb736.zip |
drm/amdgpu/display: Add core dc support for DCN
Core display support for DCN.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm')
17 files changed, 11467 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/Makefile b/drivers/gpu/drm/amd/display/dc/dcn10/Makefile new file mode 100644 index 000000000000..2c43ad7a6f3f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for DCN. + +DCN10 = dcn10_resource.o dcn10_ipp.o dcn10_hw_sequencer.o \ + dcn10_transform.o dcn10_opp.o dcn10_timing_generator.o \ + dcn10_mem_input.o dcn10_mpc.o + +AMD_DAL_DCN10 = $(addprefix $(AMDDALPATH)/dc/dcn10/,$(DCN10)) + +AMD_DISPLAY_FILES += $(AMD_DAL_DCN10) diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c new file mode 100644 index 000000000000..fb4eb4364bc7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -0,0 +1,1866 @@ +/* + * Copyright 2016 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_dc.h" +#include "core_types.h" +#include "core_status.h" +#include "resource.h" +#include "hw_sequencer.h" +#include "dcn10_hw_sequencer.h" +#include "dce110/dce110_hw_sequencer.h" +#include "abm.h" + +#include "dcn10/dcn10_transform.h" +#include "dcn10/dcn10_mpc.h" +#include "dcn10/dcn10_timing_generator.h" + +#include "mem_input.h" +#include "timing_generator.h" +#include "opp.h" +#include "ipp.h" + +#include "dc_bios_types.h" + +#include "raven1/DCN/dcn_1_0_offset.h" +#include "raven1/DCN/dcn_1_0_sh_mask.h" +#include "vega10/soc15ip.h" + +#include "custom_float.h" + + +struct dcn10_hwseq_reg_offsets { + uint32_t dchubp; + uint32_t dpp; + uint32_t otg; + uint32_t vtg; + uint32_t fmt; +}; + +/* TODO: move to resource */ +static const struct dcn10_hwseq_reg_offsets reg_offsets[] = { + { + .dchubp = (mmHUBP0_DCHUBP_CNTL - mmHUBP0_DCHUBP_CNTL), + .dpp = (mmCM0_CM_DGAM_CONTROL - mmCM0_CM_DGAM_CONTROL), + .otg = (mmOTG0_OTG_CONTROL - mmOTG0_OTG_CONTROL), + .vtg = (mmVTG0_CONTROL - mmVTG0_CONTROL), + .fmt = (mmFMT0_FMT_BIT_DEPTH_CONTROL - + mmFMT0_FMT_BIT_DEPTH_CONTROL), + }, + { + .dchubp = (mmHUBP1_DCHUBP_CNTL - mmHUBP0_DCHUBP_CNTL), + .dpp = (mmCM1_CM_DGAM_CONTROL - mmCM0_CM_DGAM_CONTROL), + .otg = (mmOTG1_OTG_CONTROL - mmOTG0_OTG_CONTROL), + .vtg = (mmVTG1_CONTROL - mmVTG0_CONTROL), + .fmt = (mmFMT1_FMT_BIT_DEPTH_CONTROL - + mmFMT0_FMT_BIT_DEPTH_CONTROL), + }, + { + .dchubp = (mmHUBP2_DCHUBP_CNTL - mmHUBP0_DCHUBP_CNTL), + .dpp = (mmCM2_CM_DGAM_CONTROL - mmCM0_CM_DGAM_CONTROL), + .otg = (mmOTG2_OTG_CONTROL - mmOTG0_OTG_CONTROL), + .vtg = (mmVTG2_CONTROL - mmVTG0_CONTROL), + .fmt = (mmFMT2_FMT_BIT_DEPTH_CONTROL - + mmFMT0_FMT_BIT_DEPTH_CONTROL), + }, + { + .dchubp = (mmHUBP3_DCHUBP_CNTL - mmHUBP0_DCHUBP_CNTL), + .dpp = (mmCM3_CM_DGAM_CONTROL - mmCM0_CM_DGAM_CONTROL), + .otg = (mmOTG3_OTG_CONTROL - mmOTG0_OTG_CONTROL), + .vtg = (mmVTG3_CONTROL - mmVTG0_CONTROL), + .fmt = (mmFMT3_FMT_BIT_DEPTH_CONTROL - + mmFMT0_FMT_BIT_DEPTH_CONTROL), + } +}; + +#define HWSEQ_REG_UPDATE_N(reg_name, n, ...) \ + generic_reg_update_soc15(ctx, inst_offset, reg_name, n, __VA_ARGS__) + +#define HWSEQ_REG_SET_N(reg_name, n, ...) \ + generic_reg_set_soc15(ctx, inst_offset, reg_name, n, __VA_ARGS__) + +#define HWSEQ_REG_UPDATE(reg, field, val) \ + HWSEQ_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) + +#define HWSEQ_REG_UPDATE_2(reg, field1, val1, field2, val2) \ + HWSEQ_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) + +#define HWSEQ_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ + HWSEQ_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) + + +#define HWSEQ_REG_SET(reg, field, val) \ + HWSEQ_REG_SET_N(reg, 1, FD(reg##__##field), val) + +/* TODO should be moved to OTG */ +static void lock_otg_master_update( + struct dc_context *ctx, + uint8_t inst) +{ + uint32_t inst_offset = reg_offsets[inst].otg; + + HWSEQ_REG_UPDATE(OTG0_OTG_GLOBAL_CONTROL0, + OTG_MASTER_UPDATE_LOCK_SEL, inst); + + /* unlock master locker */ + HWSEQ_REG_UPDATE(OTG0_OTG_MASTER_UPDATE_LOCK, + OTG_MASTER_UPDATE_LOCK, 1); + + /* wait for unlock happens */ + if (!wait_reg(ctx, inst_offset, OTG0_OTG_MASTER_UPDATE_LOCK, UPDATE_LOCK_STATUS, 1)) + BREAK_TO_DEBUGGER(); + +} + +static bool unlock_master_tg_and_wait( + struct dc_context *ctx, + uint8_t inst) +{ + uint32_t inst_offset = reg_offsets[inst].otg; + + HWSEQ_REG_UPDATE(OTG0_OTG_GLOBAL_SYNC_STATUS, + VUPDATE_NO_LOCK_EVENT_CLEAR, 1); + HWSEQ_REG_UPDATE(OTG0_OTG_MASTER_UPDATE_LOCK, OTG_MASTER_UPDATE_LOCK, 0); + + if (!wait_reg(ctx, inst_offset, OTG0_OTG_GLOBAL_SYNC_STATUS, VUPDATE_NO_LOCK_EVENT_OCCURRED, 1)) { + dm_logger_write(ctx->logger, LOG_ERROR, + "wait for VUPDATE_NO_LOCK_EVENT_OCCURRED failed\n"); + BREAK_TO_DEBUGGER(); + return false; + } + return true; +} + +/* TODO: should be moved to OTG ? */ +static void unlock_otg_master( + struct dc_context *ctx, + uint8_t inst) +{ + uint32_t inst_offset = reg_offsets[inst].otg; + + /* unlock master locker */ + HWSEQ_REG_UPDATE(OTG0_OTG_MASTER_UPDATE_LOCK, + OTG_MASTER_UPDATE_LOCK, 0); +} + + +static void wait_no_outstanding_request( + struct dc_context *ctx, + uint8_t plane_id) +{ + uint32_t inst_offset = reg_offsets[plane_id].dchubp; + + if (!wait_reg(ctx, inst_offset, HUBP0_DCHUBP_CNTL, HUBP_NO_OUTSTANDING_REQ, 1)) + BREAK_TO_DEBUGGER(); +} + +static void disable_clocks( + struct dc_context *ctx, + uint8_t plane_id) +{ + uint32_t inst_offset = reg_offsets[plane_id].dchubp; + + generic_reg_update_soc15(ctx, inst_offset, HUBP0_HUBP_CLK_CNTL, 1, + FD(HUBP0_HUBP_CLK_CNTL__HUBP_CLOCK_ENABLE), 0); + + inst_offset = reg_offsets[plane_id].dpp; + generic_reg_update_soc15(ctx, inst_offset, DPP_TOP0_DPP_CONTROL, 1, + FD(DPP_TOP0_DPP_CONTROL__DPP_CLOCK_ENABLE), 0); +} + +/* TODO: This is one time program during system boot up, + * this should be done within BIOS or CAIL + */ +static void dchubp_map_fb_to_mc(struct dc_context *ctx) +{ + /* TODO: do not know where to program + * DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB + */ + /* + * TODO: For real ASIC, FB_OFFSET may be need change to the same value + * as FB_BASE. Need re-visit this for real ASIC. + */ + dm_write_reg_soc15(ctx, mmDCHUBBUB_SDPIF_FB_BASE, 0, 0x80); + dm_write_reg_soc15(ctx, mmDCHUBBUB_SDPIF_FB_OFFSET, 0, 0); + dm_write_reg_soc15(ctx, mmDCHUBBUB_SDPIF_FB_TOP, 0, 0xFF); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_CFG0, 7, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_DATA_RESPONSE_STATUS_CLEAR), 0, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_REQ_CREDIT_ERROR_CLEAR), 0, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_FLUSH_REQ_CREDIT_EN), 0, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_REQ_CREDIT_EN), 0, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_PORT_CONTROL), 1, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_UNIT_ID_BITMASK), 0xd3, + FD(DCHUBBUB_SDPIF_CFG0__SDPIF_CREDIT_DISCONNECT_DELAY), 0xc); + + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_CFG1, 4, + FD(DCHUBBUB_SDPIF_CFG1__SDPIF_INSIDE_FB_IO), 0, + FD(DCHUBBUB_SDPIF_CFG1__SDPIF_INSIDE_FB_VC), 6, + FD(DCHUBBUB_SDPIF_CFG1__SDPIF_OUTSIDE_FB_IO), 1, + FD(DCHUBBUB_SDPIF_CFG1__SDPIF_OUTSIDE_FB_VC), 6); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_FB_BASE, 1, + FD(DCHUBBUB_SDPIF_FB_BASE__SDPIF_FB_BASE), 0x000080); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_FB_TOP, 1, + FD(DCHUBBUB_SDPIF_FB_TOP__SDPIF_FB_TOP), 0x0000ff); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_AGP_BOT, 1, + FD(DCHUBBUB_SDPIF_AGP_BOT__SDPIF_AGP_BOT), 0x0000040); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_AGP_TOP, 1, + FD(DCHUBBUB_SDPIF_AGP_TOP__SDPIF_AGP_TOP), 0x00001ff); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_AGP_BASE, 1, + FD(DCHUBBUB_SDPIF_AGP_BASE__SDPIF_AGP_BASE), 0x0000080); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_APER_TOP, 1, + FD(DCHUBBUB_SDPIF_APER_TOP__SDPIF_APER_TOP), 0x00007ff); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_APER_DEF_0, 1, + FD(DCHUBBUB_SDPIF_APER_DEF_0__SDPIF_APER_DEF_0), 0xdeadbeef); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_RELOC_LO_0, 2, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_0__SDPIF_MARC_EN_0), 0, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_0__SDPIF_MARC_RELOC_LO_0), 0x90000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_LENGTH_LO_0, 1, + FD(DCHUBBUB_SDPIF_MARC_LENGTH_LO_0__SDPIF_MARC_LENGTH_LO_0), 0x10000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_BASE_LO_1, 1, + FD(DCHUBBUB_SDPIF_MARC_BASE_LO_1__SDPIF_MARC_BASE_LO_1), 0x10000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_RELOC_LO_1, 2, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_1__SDPIF_MARC_EN_1), 0, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_1__SDPIF_MARC_RELOC_LO_1), 0xa0000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_LENGTH_LO_1, 1, + FD(DCHUBBUB_SDPIF_MARC_LENGTH_LO_1__SDPIF_MARC_LENGTH_LO_1), 0x10000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_BASE_LO_2, 1, + FD(DCHUBBUB_SDPIF_MARC_BASE_LO_2__SDPIF_MARC_BASE_LO_2), 0x20000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_RELOC_LO_2, 2, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_2__SDPIF_MARC_EN_2), 0, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_2__SDPIF_MARC_RELOC_LO_2), 0xb0000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_LENGTH_LO_2, 1, + FD(DCHUBBUB_SDPIF_MARC_LENGTH_LO_2__SDPIF_MARC_LENGTH_LO_2), 0x10000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_BASE_LO_3, 1, + FD(DCHUBBUB_SDPIF_MARC_BASE_LO_3__SDPIF_MARC_BASE_LO_3), 0x30000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_RELOC_LO_3, 2, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_3__SDPIF_MARC_EN_3), 0, + FD(DCHUBBUB_SDPIF_MARC_RELOC_LO_3__SDPIF_MARC_RELOC_LO_3), 0xc0000); + + generic_reg_set_soc15(ctx, 0, DCHUBBUB_SDPIF_MARC_LENGTH_LO_3, 1, + FD(DCHUBBUB_SDPIF_MARC_LENGTH_LO_3__SDPIF_MARC_LENGTH_LO_3), 0x10000); + + /* TODO: Is DCN_VM_SYSTEM_APERTURE address one time programming? + * Are all 4 hubp programmed with the same address? + */ + dm_write_reg_soc15(ctx, mmHUBPREQ0_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ0_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ0_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, 0, 0x100000); + dm_write_reg_soc15(ctx, mmHUBPREQ0_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ0_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ0_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, 0, 0); + + dm_write_reg_soc15(ctx, mmHUBPREQ1_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ1_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ1_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, 0, 0x100000); + dm_write_reg_soc15(ctx, mmHUBPREQ1_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ1_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ1_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, 0, 0); + + dm_write_reg_soc15(ctx, mmHUBPREQ2_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ2_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ2_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, 0, 0x100000); + dm_write_reg_soc15(ctx, mmHUBPREQ2_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ2_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ2_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, 0, 0); + + dm_write_reg_soc15(ctx, mmHUBPREQ3_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ3_DCN_VM_SYSTEM_APERTURE_LOW_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ3_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_LSB, 0, 0x100000); + dm_write_reg_soc15(ctx, mmHUBPREQ3_DCN_VM_SYSTEM_APERTURE_HIGH_ADDR_MSB, 0, 0); + dm_write_reg_soc15(ctx, mmHUBPREQ3_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, 0, 0x80000); + dm_write_reg_soc15(ctx, mmHUBPREQ3_DCN_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, 0, 0); +} + +/* TODO: This is one time program during system boot up, + * this should be done within BIOS + */ +static void dchubup_setup_timer(struct dc_context *ctx) +{ + dm_write_reg_soc15(ctx, mmREFCLK_CNTL, 0, 0); + + generic_reg_update_soc15(ctx, 0, DCHUBBUB_GLOBAL_TIMER_CNTL, 1, + FD(DCHUBBUB_GLOBAL_TIMER_CNTL__DCHUBBUB_GLOBAL_TIMER_ENABLE), 1); +} + +/* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG + * VTG is within DCHUBBUB which is commond block share by each pipe HUBP. + * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG + */ +static void select_vtg( + struct dc_context *ctx, + uint8_t plane_id, + uint8_t inst) +{ + uint32_t inst_offset = reg_offsets[plane_id].dchubp; + + HWSEQ_REG_UPDATE(HUBP0_DCHUBP_CNTL, HUBP_VTG_SEL, inst); +} + +static void enable_dcfclk( + struct dc_context *ctx, + uint8_t plane_id, + uint32_t requested_pix_clk, + bool dppclk_div) +{ + uint32_t inst_offset = reg_offsets[plane_id].dchubp; + + HWSEQ_REG_UPDATE(HUBP0_HUBP_CLK_CNTL, HUBP_CLOCK_ENABLE, 1); +} + +static void enable_dppclk( + struct dc_context *ctx, + uint8_t plane_id, + uint32_t requested_pix_clk, + bool dppclk_div) +{ + uint32_t inst_offset = reg_offsets[plane_id].dpp; + + dm_logger_write(ctx->logger, LOG_SURFACE, + "dppclk_rate_control for pipe %d programed to %d\n", + plane_id, + dppclk_div); + + /* TODO: find condition for DPP clock to DISPCLK or 1/2 DISPCLK */ + if (dppclk_div) { + /* 1/2 DISPCLK*/ + HWSEQ_REG_UPDATE_2(DPP_TOP0_DPP_CONTROL, + DPPCLK_RATE_CONTROL, 1, + DPP_CLOCK_ENABLE, 1); + } else { + /* DISPCLK */ + HWSEQ_REG_UPDATE_2(DPP_TOP0_DPP_CONTROL, + DPPCLK_RATE_CONTROL, 0, + DPP_CLOCK_ENABLE, 1); + } +} + +static void enable_power_gating_plane( + struct dc_context *ctx, + bool enable) +{ + uint32_t inst_offset = 0; /* each register only has one instance */ + bool force_on = 1; /* disable power gating */ + + if (enable) + force_on = 0; + + /* DCHUBP0/1/2/3 */ + HWSEQ_REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, force_on); + HWSEQ_REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, force_on); + HWSEQ_REG_UPDATE(DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, force_on); + HWSEQ_REG_UPDATE(DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, force_on); + + /* DPP0/1/2/3 */ + HWSEQ_REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, force_on); + HWSEQ_REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, force_on); + HWSEQ_REG_UPDATE(DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, force_on); + HWSEQ_REG_UPDATE(DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, force_on); + + if (ctx->dc->debug.disable_clock_gate) { + /* probably better to just write entire register to 0xffff to + * ensure all clock gating is disabled + */ + HWSEQ_REG_UPDATE_3(DCCG_GATE_DISABLE_CNTL, + DISPCLK_R_DCCG_GATE_DISABLE, 1, + DPREFCLK_R_DCCG_GATE_DISABLE, 1, + REFCLK_R_DIG_GATE_DISABLE, 1); + HWSEQ_REG_UPDATE(DCFCLK_CNTL, + DCFCLK_GATE_DIS, 1); + } + +} + +static void dpp_pg_control( + struct dc_context *ctx, + unsigned int dpp_inst, + bool power_on) +{ + uint32_t inst_offset = 0; + uint32_t power_gate = power_on ? 0 : 1; + uint32_t pwr_status = power_on ? 0 : 2; + + if (ctx->dc->debug.disable_dpp_power_gate) + return; + + switch (dpp_inst) { + case 0: /* DPP0 */ + HWSEQ_REG_UPDATE(DOMAIN1_PG_CONFIG, + DOMAIN1_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN1_PG_STATUS, + DOMAIN1_PGFSM_PWR_STATUS, pwr_status); + break; + case 1: /* DPP1 */ + HWSEQ_REG_UPDATE(DOMAIN3_PG_CONFIG, + DOMAIN3_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN3_PG_STATUS, + DOMAIN3_PGFSM_PWR_STATUS, pwr_status); + break; + case 2: /* DPP2 */ + HWSEQ_REG_UPDATE(DOMAIN5_PG_CONFIG, + DOMAIN5_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN5_PG_STATUS, + DOMAIN5_PGFSM_PWR_STATUS, pwr_status); + break; + case 3: /* DPP3 */ + HWSEQ_REG_UPDATE(DOMAIN7_PG_CONFIG, + DOMAIN7_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN7_PG_STATUS, + DOMAIN7_PGFSM_PWR_STATUS, pwr_status); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +static void hubp_pg_control( + struct dc_context *ctx, + unsigned int hubp_inst, + bool power_on) +{ + uint32_t inst_offset = 0; + uint32_t power_gate = power_on ? 0 : 1; + uint32_t pwr_status = power_on ? 0 : 2; + + if (ctx->dc->debug.disable_hubp_power_gate) + return; + + switch (hubp_inst) { + case 0: /* DCHUBP0 */ + HWSEQ_REG_UPDATE(DOMAIN0_PG_CONFIG, + DOMAIN0_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN0_PG_STATUS, + DOMAIN0_PGFSM_PWR_STATUS, pwr_status); + break; + case 1: /* DCHUBP1 */ + HWSEQ_REG_UPDATE(DOMAIN2_PG_CONFIG, + DOMAIN2_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN2_PG_STATUS, + DOMAIN2_PGFSM_PWR_STATUS, pwr_status); + break; + case 2: /* DCHUBP2 */ + HWSEQ_REG_UPDATE(DOMAIN4_PG_CONFIG, + DOMAIN4_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN4_PG_STATUS, + DOMAIN4_PGFSM_PWR_STATUS, pwr_status); + break; + case 3: /* DCHUBP3 */ + HWSEQ_REG_UPDATE(DOMAIN6_PG_CONFIG, + DOMAIN6_POWER_GATE, power_gate); + + wait_reg(ctx, 0, DOMAIN6_PG_STATUS, + DOMAIN6_PGFSM_PWR_STATUS, pwr_status); + break; + default: + BREAK_TO_DEBUGGER(); + break; + } +} + +static void power_on_plane( + struct dc_context *ctx, + uint8_t plane_id, + uint8_t inst) +{ + uint32_t inst_offset = 0; + + /* disable clock power gating */ + + /* DCCG_GATE_DISABLE_CNTL only has one instance */ + HWSEQ_REG_UPDATE_2(DCCG_GATE_DISABLE_CNTL, + DISPCLK_DCCG_GATE_DISABLE, 1, + DPPCLK_GATE_DISABLE, 1); + /* DCFCLK_CNTL only has one instance */ + HWSEQ_REG_UPDATE(DCFCLK_CNTL, + DCFCLK_GATE_DIS, 1); + + HWSEQ_REG_SET(DC_IP_REQUEST_CNTL, + IP_REQUEST_EN, 1); + dpp_pg_control(ctx, plane_id, true); + hubp_pg_control(ctx, plane_id, true); + HWSEQ_REG_SET(DC_IP_REQUEST_CNTL, + IP_REQUEST_EN, 0); + + if (ctx->dc->debug.disable_clock_gate) { + HWSEQ_REG_UPDATE(DCCG_GATE_DISABLE_CNTL, + DISPCLK_DCCG_GATE_DISABLE, 0); + } else { + /* DCCG_GATE_DISABLE_CNTL only has one instance. inst_offset = 0 */ + HWSEQ_REG_UPDATE_2(DCCG_GATE_DISABLE_CNTL, + DISPCLK_DCCG_GATE_DISABLE, 0, + DPPCLK_GATE_DISABLE, 0); + /* DCFCLK_CNTL only has one instance. inst_offset = 0 */ + HWSEQ_REG_UPDATE(DCFCLK_CNTL, + DCFCLK_GATE_DIS, 0); + } +} + +/* fully check bios enabledisplaypowergating table. dal only need dce init + * other power, clock gate register will be handle by dal itself. + * further may be put within init_hw + */ +static bool dcn10_enable_display_power_gating( + struct core_dc *dc, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + /* TODOFPGA */ +#if 0 + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce110_init_pte(ctx); +#endif + + return true; +} + +static void bios_golden_init(struct core_dc *dc) +{ + struct dc_bios *bp = dc->ctx->dc_bios; + int i; + + /* initialize dcn global */ + bp->funcs->enable_disp_power_gating(bp, + CONTROLLER_ID_D0, ASIC_PIPE_INIT); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + /* initialize dcn per pipe */ + bp->funcs->enable_disp_power_gating(bp, + CONTROLLER_ID_D0 + i, ASIC_PIPE_DISABLE); + } +} + +static void init_hw(struct core_dc *dc) +{ + int i; + struct dc_bios *bp; + struct transform *xfm; + struct abm *abm; + + bp = dc->ctx->dc_bios; + + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + /* TODO: this will be moved to Diag or BIOS */ + dchubup_setup_timer(dc->ctx); + + /* TODO: dchubp_map_fb_to_mc will be moved to dchub interface + * between dc and kmd + */ + dchubp_map_fb_to_mc(dc->ctx); + + enable_power_gating_plane(dc->ctx, true); + return; + } + /* end of FPGA. Below if real ASIC */ + + bios_golden_init(dc); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + xfm = dc->res_pool->transforms[i]; + xfm->funcs->transform_reset(xfm); + + /* TODOFPGA: may need later */ +#if 0 + xfm->funcs->transform_power_up(xfm); + dc->hwss.enable_display_pipe_clock_gating( + dc->ctx, + true); +#endif + } + /* TODOFPGA: light sleep */ +#if 0 + dc->hwss.clock_gating_power_up(dc->ctx, false); +#endif + + 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); + hwss_wait_for_blank_complete(tg); + } + + for (i = 0; i < dc->res_pool->audio_count; i++) { + struct audio *audio = dc->res_pool->audios[i]; + + audio->funcs->hw_init(audio); + } + + abm = dc->res_pool->abm; + if (abm != NULL) { + abm->funcs->init_backlight(abm); + abm->funcs->abm_init(abm); + } + + /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ + generic_reg_set_soc15(dc->ctx, 0, DIO_MEM_PWR_CTRL, 7, + FD(DIO_MEM_PWR_CTRL__HDMI0_MEM_PWR_FORCE), 0, + FD(DIO_MEM_PWR_CTRL__HDMI1_MEM_PWR_FORCE), 0, + FD(DIO_MEM_PWR_CTRL__HDMI2_MEM_PWR_FORCE), 0, + FD(DIO_MEM_PWR_CTRL__HDMI3_MEM_PWR_FORCE), 0, + FD(DIO_MEM_PWR_CTRL__HDMI4_MEM_PWR_FORCE), 0, + FD(DIO_MEM_PWR_CTRL__HDMI5_MEM_PWR_FORCE), 0, + FD(DIO_MEM_PWR_CTRL__HDMI6_MEM_PWR_FORCE), 0); + + /* This power gating should be one-time program for DAL. + * It can only change by registry key + * TODO: new task will for this. + * if power gating is disable, power_on_plane and power_off_plane + * should be skip. Otherwise, hand will be met in power_off_plane + */ + + enable_power_gating_plane(dc->ctx, true); +} + +static enum dc_status dcn10_prog_pixclk_crtc_otg( + struct pipe_ctx *pipe_ctx, + struct validate_context *context, + struct core_dc *dc) +{ + struct core_stream *stream = pipe_ctx->stream; + enum dc_color_space color_space; + struct tg_color black_color = {0}; + bool enableStereo = stream->public.timing.timing_3d_format == TIMING_3D_FORMAT_NONE ? + false:true; + bool rightEyePolarity = stream->public.timing.flags.RIGHT_EYE_3D_POLARITY; + + + /* by upper caller loop, pipe0 is parent pipe and be called first. + * back end is set up by for pipe0. Other children pipe share back end + * with pipe 0. No program is needed. + */ + if (pipe_ctx->top_pipe != NULL) + return DC_OK; + + /* TODO check if timing_changed, disable stream if timing changed */ + + /* HW program guide assume display already disable + * by unplug sequence. OTG assume stop. + */ + pipe_ctx->tg->funcs->enable_optc_clock(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->dlg_otg_param.vready_offset = pipe_ctx->pipe_dlg_param.vready_offset; + pipe_ctx->tg->dlg_otg_param.vstartup_start = pipe_ctx->pipe_dlg_param.vstartup_start; + pipe_ctx->tg->dlg_otg_param.vupdate_offset = pipe_ctx->pipe_dlg_param.vupdate_offset; + pipe_ctx->tg->dlg_otg_param.vupdate_width = pipe_ctx->pipe_dlg_param.vupdate_width; + + pipe_ctx->tg->dlg_otg_param.signal = pipe_ctx->stream->signal; + + pipe_ctx->tg->funcs->program_timing( + pipe_ctx->tg, + &stream->public.timing, + true); + + pipe_ctx->opp->funcs->opp_set_stereo_polarity( + pipe_ctx->opp, + enableStereo, + rightEyePolarity); + +#if 0 /* move to after enable_crtc */ + /* TODO: OPP FMT, ABM. etc. should be done here. */ + /* or FPGA now. instance 0 only. TODO: move to opp.c */ + + inst_offset = reg_offsets[pipe_ctx->tg->inst].fmt; + + pipe_ctx->opp->funcs->opp_program_fmt( + pipe_ctx->opp, + &stream->bit_depth_params, + &stream->clamping); +#endif + /* program otg blank color */ + color_space = stream->public.output_color_space; + color_space_to_black_color(dc, color_space, &black_color); + pipe_ctx->tg->funcs->set_blank_color( + pipe_ctx->tg, + &black_color); + + pipe_ctx->tg->funcs->set_blank(pipe_ctx->tg, true); + hwss_wait_for_blank_complete(pipe_ctx->tg); + + /* VTG is within DCHUB command block. DCFCLK is always on */ + if (false == pipe_ctx->tg->funcs->enable_crtc(pipe_ctx->tg)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + /* TODO program crtc source select for non-virtual signal*/ + /* TODO program FMT */ + /* TODO setup link_enc */ + /* TODO set stream attributes */ + /* TODO program audio */ + /* TODO enable stream if timing changed */ + /* TODO unblank stream if DP */ + + return DC_OK; +} + +static void reset_back_end_for_pipe( + struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + int i; + struct dc_bios *bp; + + bp = dc->ctx->dc_bios; + + if (pipe_ctx->stream_enc == NULL) { + pipe_ctx->stream = NULL; + return; + } + + /* TODOFPGA break core_link_disable_stream into 2 functions: + * disable_stream and disable_link. disable_link will disable PHYPLL + * which is used by otg. Move disable_link after disable_crtc + */ + if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) + core_link_disable_stream(pipe_ctx); + + /* by upper caller loop, parent pipe: pipe0, will be reset last. + * back end share by all pipes and will be disable only when disable + * parent pipe. + */ + if (pipe_ctx->top_pipe == NULL) { + pipe_ctx->tg->funcs->disable_crtc(pipe_ctx->tg); + + pipe_ctx->tg->funcs->enable_optc_clock(pipe_ctx->tg, false); + } + + if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) + resource_unreference_clock_source( + &context->res_ctx, dc->res_pool, + &pipe_ctx->clock_source); + + for (i = 0; i < dc->res_pool->pipe_count; i++) + if (&dc->current_context->res_ctx.pipe_ctx[i] == pipe_ctx) + break; + + if (i == dc->res_pool->pipe_count) + return; + + pipe_ctx->stream = NULL; +} + +static void reset_front_end_for_pipe( + struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context) +{ + struct dcn10_mpc *mpc = TO_DCN10_MPC(dc->res_pool->mpc); + struct mpc_tree_cfg *tree_cfg = NULL; + + if (!pipe_ctx->surface) + return; + + lock_otg_master_update(dc->ctx, pipe_ctx->tg->inst); + + /* TODO: build stream pipes group id. For now, use stream otg + * id as pipe group id + */ + tree_cfg = &context->res_ctx.mpc_tree[pipe_ctx->mpc_idx]; + + if (pipe_ctx->top_pipe == NULL) + dcn10_delete_mpc_tree(mpc, tree_cfg); + else { + if (dcn10_remove_dpp(mpc, tree_cfg, pipe_ctx->pipe_idx)) + pipe_ctx->top_pipe->bottom_pipe = NULL; + else { + dm_logger_write(dc->ctx->logger, LOG_RESOURCE, + "%s: failed to find dpp to be removed!\n", + __func__); + } + } + + pipe_ctx->top_pipe = NULL; + pipe_ctx->bottom_pipe = NULL; + pipe_ctx->mpc_idx = -1; + + unlock_master_tg_and_wait(dc->ctx, pipe_ctx->tg->inst); + + pipe_ctx->mi->funcs->disable_request(pipe_ctx->mi); + + wait_no_outstanding_request(dc->ctx, pipe_ctx->pipe_idx); + + wait_mpcc_idle(mpc, pipe_ctx->pipe_idx); + + disable_clocks(dc->ctx, pipe_ctx->pipe_idx); + + pipe_ctx->xfm->funcs->transform_reset(pipe_ctx->xfm); + + dm_logger_write(dc->ctx->logger, LOG_DC, + "Reset front end for pipe %d\n", + pipe_ctx->pipe_idx); + + pipe_ctx->surface = NULL; +} + +static void reset_hw_ctx(struct core_dc *dc, + struct validate_context *context, + void (*reset)(struct core_dc *dc, + struct pipe_ctx *pipe_ctx, + struct validate_context *context)) +{ + int i; + + for (i = dc->res_pool->pipe_count - 1; i >= 0 ; 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_old->stream) + continue; + + if (!pipe_ctx->stream || + pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) + reset(dc, pipe_ctx_old, dc->current_context); + } +} + +static void reset_hw_ctx_wrap( + struct core_dc *dc, + struct validate_context *context) +{ + /* Reset Front End*/ < |