aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/2436-drm-amd-display-Add-DCN2-clk-mgr.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/2436-drm-amd-display-Add-DCN2-clk-mgr.patch')
-rw-r--r--meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/2436-drm-amd-display-Add-DCN2-clk-mgr.patch1321
1 files changed, 1321 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/2436-drm-amd-display-Add-DCN2-clk-mgr.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/2436-drm-amd-display-Add-DCN2-clk-mgr.patch
new file mode 100644
index 00000000..badf5a41
--- /dev/null
+++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/2436-drm-amd-display-Add-DCN2-clk-mgr.patch
@@ -0,0 +1,1321 @@
+From 42101b1c316e8942b0c867f680caccf7c0c8385c Mon Sep 17 00:00:00 2001
+From: Harry Wentland <harry.wentland@amd.com>
+Date: Tue, 7 May 2019 14:57:07 -0500
+Subject: [PATCH 2436/2940] drm/amd/display: Add DCN2 clk mgr
+
+Adds support for handling of clocking relevant to the DCN2 block,
+including programming of the DCCG (Display Controller Clock Generator)
+block:
+
+HW Blocks:
+
+ +--------+ +--------+
+ | DIO | | DCCG |
+ +--------+ +--------+
+
+Signed-off-by: Harry Wentland <harry.wentland@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+---
+ .../gpu/drm/amd/display/dc/clk_mgr/Makefile | 12 +
+ .../gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 32 +-
+ .../display/dc/clk_mgr/dce100/dce_clk_mgr.h | 22 -
+ .../display/dc/clk_mgr/dcn10/rv1_clk_mgr.c | 25 +-
+ .../dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.c | 58 ++-
+ .../display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c | 384 ++++++++++++++++++
+ .../display/dc/clk_mgr/dcn20/dcn20_clk_mgr.h | 44 ++
+ .../drm/amd/display/dc/dce/dce_clock_source.c | 108 +++++
+ .../drm/amd/display/dc/dce/dce_clock_source.h | 42 ++
+ .../gpu/drm/amd/display/dc/dcn20/dcn20_dccg.c | 157 +++++++
+ .../gpu/drm/amd/display/dc/dcn20/dcn20_dccg.h | 116 ++++++
+ .../amd/display/dc/inc/hw/clk_mgr_internal.h | 82 +++-
+ 12 files changed, 999 insertions(+), 83 deletions(-)
+ create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c
+ create mode 100644 drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.h
+ create mode 100644 drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.c
+ create mode 100644 drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.h
+
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/Makefile b/drivers/gpu/drm/amd/display/dc/clk_mgr/Makefile
+index 650e2b88c917..003c27767e9c 100644
+--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/Makefile
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/Makefile
+@@ -73,3 +73,15 @@ AMD_DAL_CLK_MGR_DCN10 = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn10/,$(CLK_MGR_DC
+
+ AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN10)
+ endif
++
++ifdef CONFIG_DRM_AMD_DC_DCN2_0
++###############################################################################
++# DCN20
++###############################################################################
++CLK_MGR_DCN20 = dcn20_clk_mgr.o
++
++AMD_DAL_CLK_MGR_DCN20 = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn20/,$(CLK_MGR_DCN20))
++
++AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN20)
++endif
++
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
+index eb2204d42337..27d407a9b452 100644
+--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
+@@ -34,31 +34,7 @@
+ #include "dce120/dce120_clk_mgr.h"
+ #include "dcn10/rv1_clk_mgr.h"
+ #include "dcn10/rv2_clk_mgr.h"
+-
+-
+-int clk_mgr_helper_get_active_display_cnt(
+- struct dc *dc,
+- struct dc_state *context)
+-{
+- int i, display_count;
+-
+- display_count = 0;
+- for (i = 0; i < context->stream_count; i++) {
+- const struct dc_stream_state *stream = context->streams[i];
+-
+- /*
+- * Only notify active stream or virtual stream.
+- * Need to notify virtual stream to work around
+- * headless case. HPD does not fire when system is in
+- * S0i2.
+- */
+- if (!stream->dpms_off || stream->signal == SIGNAL_TYPE_VIRTUAL)
+- display_count++;
+- }
+-
+- return display_count;
+-}
+-
++#include "dcn20/dcn20_clk_mgr.h"
+
+ struct clk_mgr *dc_clk_mgr_create(struct dc_context *ctx, struct pp_smu_funcs *pp_smu, struct dccg *dccg)
+ {
+@@ -117,6 +93,12 @@ struct clk_mgr *dc_clk_mgr_create(struct dc_context *ctx, struct pp_smu_funcs *p
+ break;
+ #endif /* Family RV */
+
++#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
++ case FAMILY_NV:
++ dcn20_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg);
++ break;
++#endif /* Family NV */
++
+ default:
+ ASSERT(0); /* Unknown Asic */
+ break;
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h
+index f3bc7ab68aab..f6622f58f62e 100644
+--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h
+@@ -29,28 +29,6 @@
+
+ #include "dc.h"
+
+-/* Starting DID for each range */
+-enum dentist_base_divider_id {
+- DENTIST_BASE_DID_1 = 0x08,
+- DENTIST_BASE_DID_2 = 0x40,
+- DENTIST_BASE_DID_3 = 0x60,
+- DENTIST_BASE_DID_4 = 0x7e,
+- DENTIST_MAX_DID = 0x7f
+-};
+-
+-/* Starting point and step size for each divider range.*/
+-enum dentist_divider_range {
+- DENTIST_DIVIDER_RANGE_1_START = 8, /* 2.00 */
+- DENTIST_DIVIDER_RANGE_1_STEP = 1, /* 0.25 */
+- DENTIST_DIVIDER_RANGE_2_START = 64, /* 16.00 */
+- DENTIST_DIVIDER_RANGE_2_STEP = 2, /* 0.50 */
+- DENTIST_DIVIDER_RANGE_3_START = 128, /* 32.00 */
+- DENTIST_DIVIDER_RANGE_3_STEP = 4, /* 1.00 */
+- DENTIST_DIVIDER_RANGE_4_START = 248, /* 62.00 */
+- DENTIST_DIVIDER_RANGE_4_STEP = 264, /* 66.00 */
+- DENTIST_DIVIDER_RANGE_SCALE_FACTOR = 4
+-};
+-
+ /* functions shared by other dce clk mgrs */
+ int dce_adjust_dp_ref_freq_for_ss(struct clk_mgr_internal *clk_mgr_dce, int dp_ref_clk_khz);
+ int dce_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr_base);
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr.c
+index a8e175cb0fe2..0a083a5e89ca 100644
+--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr.c
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr.c
+@@ -114,6 +114,29 @@ static void ramp_up_dispclk_with_dpp(struct clk_mgr_internal *clk_mgr, struct dc
+ clk_mgr->base.clks.max_supported_dppclk_khz = new_clocks->max_supported_dppclk_khz;
+ }
+
++static int get_active_display_cnt(
++ struct dc *dc,
++ struct dc_state *context)
++{
++ int i, display_count;
++
++ display_count = 0;
++ for (i = 0; i < context->stream_count; i++) {
++ const struct dc_stream_state *stream = context->streams[i];
++
++ /*
++ * Only notify active stream or virtual stream.
++ * Need to notify virtual stream to work around
++ * headless case. HPD does not fire when system is in
++ * S0i2.
++ */
++ if (!stream->dpms_off || stream->signal == SIGNAL_TYPE_VIRTUAL)
++ display_count++;
++ }
++
++ return display_count;
++}
++
+ static void rv1_update_clocks(struct clk_mgr *clk_mgr_base,
+ struct dc_state *context,
+ bool safe_to_lower)
+@@ -133,7 +156,7 @@ static void rv1_update_clocks(struct clk_mgr *clk_mgr_base,
+
+ pp_smu = &clk_mgr->pp_smu->rv_funcs;
+
+- display_count = clk_mgr_helper_get_active_display_cnt(dc, context);
++ display_count = get_active_display_cnt(dc, context);
+
+ if (display_count == 0)
+ enter_display_off = true;
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.c
+index 1897e91c8ccb..196087072063 100644
+--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.c
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn10/rv1_clk_mgr_vbios_smu.c
+@@ -68,59 +68,57 @@ static const struct IP_BASE MP1_BASE = { { { { 0x00016000, 0, 0, 0, 0 } },
+ #define VBIOSSMC_MSG_SetDispclkFreq 0x4
+ #define VBIOSSMC_MSG_SetDprefclkFreq 0x5
+
+-int rv1_vbios_smu_send_msg_with_param(struct clk_mgr_internal *clk_mgr, unsigned int msg_id, unsigned int param)
++int rv1_vbios_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
+ {
++
++ int actual_dispclk_set_khz = -1;
++ struct dc *core_dc = clk_mgr->base.ctx->dc;
++ struct dmcu *dmcu = core_dc->res_pool->dmcu;
++
+ /* First clear response register */
++ //dm_write_reg(ctx, mmMP1_SMN_C2PMSG_91, 0);
+ REG_WRITE(MP1_SMN_C2PMSG_91, 0);
+
+ /* Set the parameter register for the SMU message, unit is Mhz */
+- REG_WRITE(MP1_SMN_C2PMSG_83, param);
++ //dm_write_reg(ctx, mmMP1_SMN_C2PMSG_83, requested_dispclk_khz / 1000);
++ REG_WRITE(MP1_SMN_C2PMSG_83, requested_dispclk_khz / 1000);
+
+ /* Trigger the message transaction by writing the message ID */
+- REG_WRITE(MP1_SMN_C2PMSG_67, msg_id);
++ //dm_write_reg(ctx, mmMP1_SMN_C2PMSG_67, VBIOSSMC_MSG_SetDispclkFreq);
++ REG_WRITE(MP1_SMN_C2PMSG_67, VBIOSSMC_MSG_SetDispclkFreq);
+
+ REG_WAIT(MP1_SMN_C2PMSG_91, CONTENT, 1, 10, 200000);
+
+ /* Actual dispclk set is returned in the parameter register */
+- return REG_READ(MP1_SMN_C2PMSG_83);
+-}
+-
+-int rv1_vbios_smu_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_dispclk_khz)
+-{
+- int actual_dispclk_set_mhz = -1;
+- struct dc *core_dc = clk_mgr->base.ctx->dc;
+- struct dmcu *dmcu = core_dc->res_pool->dmcu;
+-
+- /* Unit of SMU msg parameter is Mhz */
+- actual_dispclk_set_mhz = rv1_vbios_smu_send_msg_with_param(
+- clk_mgr,
+- VBIOSSMC_MSG_SetDispclkFreq,
+- requested_dispclk_khz / 1000);
+-
+- /* Actual dispclk set is returned in the parameter register */
+- actual_dispclk_set_mhz = REG_READ(MP1_SMN_C2PMSG_83) * 1000;
++ actual_dispclk_set_khz = REG_READ(MP1_SMN_C2PMSG_83) * 1000;
+
+ if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) {
+ if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) {
+- if (clk_mgr->dfs_bypass_disp_clk != actual_dispclk_set_mhz)
++ if (clk_mgr->dfs_bypass_disp_clk != actual_dispclk_set_khz)
+ dmcu->funcs->set_psr_wait_loop(dmcu,
+- actual_dispclk_set_mhz / 7);
++ actual_dispclk_set_khz / 1000 / 7);
+ }
+ }
+
+- return actual_dispclk_set_mhz * 1000;
++ return actual_dispclk_set_khz;
+ }
+
+ int rv1_vbios_smu_set_dprefclk(struct clk_mgr_internal *clk_mgr)
+ {
+- int actual_dprefclk_set_mhz = -1;
++ int actual_dprefclk_set_khz = -1;
++
++ REG_WRITE(MP1_SMN_C2PMSG_91, 0);
++
++ /* Set the parameter register for the SMU message */
++ REG_WRITE(MP1_SMN_C2PMSG_83, clk_mgr->base.dprefclk_khz / 1000);
+
+- actual_dprefclk_set_mhz = rv1_vbios_smu_send_msg_with_param(
+- clk_mgr,
+- VBIOSSMC_MSG_SetDprefclkFreq,
+- clk_mgr->base.dprefclk_khz / 1000);
++ /* Trigger the message transaction by writing the message ID */
++ REG_WRITE(MP1_SMN_C2PMSG_67, VBIOSSMC_MSG_SetDprefclkFreq);
++
++ /* Wait for SMU response */
++ REG_WAIT(MP1_SMN_C2PMSG_91, CONTENT, 1, 10, 200000);
+
+- /* TODO: add code for programing DP DTO, currently this is down by command table */
++ actual_dprefclk_set_khz = REG_READ(MP1_SMN_C2PMSG_83) * 1000;
+
+- return actual_dprefclk_set_mhz * 1000;
++ return actual_dprefclk_set_khz;
+ }
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c
+new file mode 100644
+index 000000000000..9d0336a5f83f
+--- /dev/null
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c
+@@ -0,0 +1,384 @@
++/*
++ * Copyright 2018 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 "dccg.h"
++#include "clk_mgr_internal.h"
++
++
++#include "dcn20/dcn20_clk_mgr.h"
++#include "dce100/dce_clk_mgr.h"
++#include "reg_helper.h"
++#include "core_types.h"
++#include "dm_helpers.h"
++
++#include "navi10_ip_offset.h"
++#include "dcn/dcn_2_0_0_offset.h"
++#include "dcn/dcn_2_0_0_sh_mask.h"
++#include "clk/clk_11_0_0_offset.h"
++#include "clk/clk_11_0_0_sh_mask.h"
++
++#undef FN
++#define FN(reg_name, field_name) \
++ clk_mgr->clk_mgr_shift->field_name, clk_mgr->clk_mgr_mask->field_name
++
++#define REG(reg) \
++ (clk_mgr->regs->reg)
++
++#define BASE_INNER(seg) DCN_BASE__INST0_SEG ## seg
++
++#define BASE(seg) BASE_INNER(seg)
++
++#define SR(reg_name)\
++ .reg_name = BASE(mm ## reg_name ## _BASE_IDX) + \
++ mm ## reg_name
++
++#define CLK_BASE_INNER(seg) \
++ CLK_BASE__INST0_SEG ## seg
++
++
++static const struct clk_mgr_registers clk_mgr_regs = {
++ CLK_REG_LIST_NV10()
++};
++
++static const struct clk_mgr_shift clk_mgr_shift = {
++ CLK_MASK_SH_LIST_NV10(__SHIFT)
++};
++
++static const struct clk_mgr_mask clk_mgr_mask = {
++ CLK_MASK_SH_LIST_NV10(_MASK)
++};
++
++static uint32_t dentist_get_did_from_divider(int divider)
++{
++ uint32_t divider_id;
++
++ /* we want to floor here to get higher clock than required rather than lower */
++ if (divider < DENTIST_DIVIDER_RANGE_2_START) {
++ if (divider < DENTIST_DIVIDER_RANGE_1_START)
++ divider_id = DENTIST_BASE_DID_1;
++ else
++ divider_id = DENTIST_BASE_DID_1
++ + (divider - DENTIST_DIVIDER_RANGE_1_START)
++ / DENTIST_DIVIDER_RANGE_1_STEP;
++ } else if (divider < DENTIST_DIVIDER_RANGE_3_START) {
++ divider_id = DENTIST_BASE_DID_2
++ + (divider - DENTIST_DIVIDER_RANGE_2_START)
++ / DENTIST_DIVIDER_RANGE_2_STEP;
++ } else if (divider < DENTIST_DIVIDER_RANGE_4_START) {
++ divider_id = DENTIST_BASE_DID_3
++ + (divider - DENTIST_DIVIDER_RANGE_3_START)
++ / DENTIST_DIVIDER_RANGE_3_STEP;
++ } else {
++ divider_id = DENTIST_BASE_DID_4
++ + (divider - DENTIST_DIVIDER_RANGE_4_START)
++ / DENTIST_DIVIDER_RANGE_4_STEP;
++ if (divider_id > DENTIST_MAX_DID)
++ divider_id = DENTIST_MAX_DID;
++ }
++
++ return divider_id;
++}
++
++static int get_active_display_cnt(
++ struct dc *dc,
++ struct dc_state *context)
++{
++ int i, display_count;
++
++ display_count = 0;
++ for (i = 0; i < context->stream_count; i++) {
++ const struct dc_stream_state *stream = context->streams[i];
++
++ /*
++ * Only notify active stream or virtual stream.
++ * Need to notify virtual stream to work around
++ * headless case. HPD does not fire when system is in
++ * S0i2.
++ */
++ if (!stream->dpms_off || stream->signal == SIGNAL_TYPE_VIRTUAL)
++ display_count++;
++ }
++
++ return display_count;
++}
++
++static void update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr,
++ struct dc_state *context)
++{
++ int i;
++
++ clk_mgr->dccg->ref_dppclk = clk_mgr->base.clks.dppclk_khz;
++ for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) {
++ int dpp_inst, dppclk_khz;
++
++ if (!context->res_ctx.pipe_ctx[i].plane_state)
++ continue;
++
++ dpp_inst = context->res_ctx.pipe_ctx[i].plane_res.dpp->inst;
++ dppclk_khz = context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz;
++ clk_mgr->dccg->funcs->update_dpp_dto(
++ clk_mgr->dccg, dpp_inst, dppclk_khz);
++ }
++}
++
++static void update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr)
++{
++ int dpp_divider = DENTIST_DIVIDER_RANGE_SCALE_FACTOR
++ * clk_mgr->dentist_vco_freq_khz / clk_mgr->base.clks.dppclk_khz;
++ int disp_divider = DENTIST_DIVIDER_RANGE_SCALE_FACTOR
++ * clk_mgr->dentist_vco_freq_khz / clk_mgr->base.clks.dispclk_khz;
++
++ uint32_t dppclk_wdivider = dentist_get_did_from_divider(dpp_divider);
++ uint32_t dispclk_wdivider = dentist_get_did_from_divider(disp_divider);
++
++ REG_UPDATE(DENTIST_DISPCLK_CNTL,
++ DENTIST_DISPCLK_WDIVIDER, dispclk_wdivider);
++// REG_WAIT(DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_CHG_DONE, 1, 5, 100);
++ REG_UPDATE(DENTIST_DISPCLK_CNTL,
++ DENTIST_DPPCLK_WDIVIDER, dppclk_wdivider);
++ REG_WAIT(DENTIST_DISPCLK_CNTL, DENTIST_DPPCLK_CHG_DONE, 1, 5, 100);
++}
++
++
++void dcn2_update_clocks(struct clk_mgr *clk_mgr_base,
++ struct dc_state *context,
++ bool safe_to_lower)
++{
++ struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
++ struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk;
++ struct dc *dc = clk_mgr_base->ctx->dc;
++ struct pp_smu_funcs_nv *pp_smu = NULL;
++ int display_count;
++ bool update_dppclk = false;
++ bool update_dispclk = false;
++ bool enter_display_off = false;
++ bool dpp_clock_lowered = false;
++
++ display_count = get_active_display_cnt(dc, context);
++ if (dc->res_pool->pp_smu)
++ pp_smu = &dc->res_pool->pp_smu->nv_funcs;
++
++ if (display_count == 0)
++ enter_display_off = true;
++
++ if (enter_display_off == safe_to_lower) {
++ if (pp_smu && pp_smu->set_display_count)
++ pp_smu->set_display_count(&pp_smu->pp_smu, display_count);
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->phyclk_khz, clk_mgr_base->clks.phyclk_khz)) {
++ clk_mgr_base->clks.phyclk_khz = new_clocks->phyclk_khz;
++ if (pp_smu && pp_smu->set_voltage_by_freq)
++ pp_smu->set_voltage_by_freq(&pp_smu->pp_smu, PP_SMU_NV_PHYCLK, clk_mgr_base->clks.phyclk_khz / 1000);
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, clk_mgr_base->clks.dcfclk_khz)) {
++ clk_mgr_base->clks.dcfclk_khz = new_clocks->dcfclk_khz;
++ if (pp_smu && pp_smu->set_hard_min_dcfclk_by_freq)
++ pp_smu->set_hard_min_dcfclk_by_freq(&pp_smu->pp_smu, clk_mgr_base->clks.dcfclk_khz / 1000);
++ }
++
++ if (should_set_clock(safe_to_lower,
++ new_clocks->dcfclk_deep_sleep_khz, clk_mgr_base->clks.dcfclk_deep_sleep_khz)) {
++ clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz;
++ if (pp_smu && pp_smu->set_min_deep_sleep_dcfclk)
++ pp_smu->set_min_deep_sleep_dcfclk(&pp_smu->pp_smu, clk_mgr_base->clks.dcfclk_deep_sleep_khz / 1000);
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->socclk_khz, clk_mgr_base->clks.socclk_khz)) {
++ clk_mgr_base->clks.socclk_khz = new_clocks->socclk_khz;
++ if (pp_smu && pp_smu->set_hard_min_socclk_by_freq)
++ pp_smu->set_hard_min_socclk_by_freq(&pp_smu->pp_smu, clk_mgr_base->clks.socclk_khz / 1000);
++ }
++
++ if (!safe_to_lower && pp_smu && pp_smu->set_pstate_handshake_support)
++ pp_smu->set_pstate_handshake_support(&pp_smu->pp_smu, false);
++ else if (safe_to_lower && pp_smu && pp_smu->set_pstate_handshake_support)
++ pp_smu->set_pstate_handshake_support(&pp_smu->pp_smu, clk_mgr_base->clks.p_state_change_support);
++
++ if (should_set_clock(safe_to_lower, new_clocks->dramclk_khz, clk_mgr_base->clks.dramclk_khz)) {
++ clk_mgr_base->clks.dramclk_khz = new_clocks->dramclk_khz;
++ if (pp_smu && pp_smu->set_hard_min_uclk_by_freq)
++ pp_smu->set_hard_min_uclk_by_freq(&pp_smu->pp_smu, clk_mgr_base->clks.dramclk_khz / 1000);
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->base.clks.dppclk_khz)) {
++ if (clk_mgr->base.clks.dppclk_khz > new_clocks->dppclk_khz)
++ dpp_clock_lowered = true;
++ clk_mgr->base.clks.dppclk_khz = new_clocks->dppclk_khz;
++
++ if (pp_smu && pp_smu->set_voltage_by_freq)
++ pp_smu->set_voltage_by_freq(&pp_smu->pp_smu, PP_SMU_NV_PIXELCLK, clk_mgr_base->clks.dppclk_khz / 1000);
++
++ update_dppclk = true;
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz)) {
++ clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz;
++ if (pp_smu && pp_smu->set_voltage_by_freq)
++ pp_smu->set_voltage_by_freq(&pp_smu->pp_smu, PP_SMU_NV_DISPCLK, clk_mgr_base->clks.dispclk_khz / 1000);
++
++ update_dispclk = true;
++ }
++
++ if (dpp_clock_lowered) {
++ // if clock is being lowered, increase DTO before lowering refclk
++ update_clocks_update_dpp_dto(clk_mgr, context);
++ update_clocks_update_dentist(clk_mgr);
++ } else {
++ // if clock is being raised, increase refclk before lowering DTO
++ if (update_dppclk || update_dispclk)
++ update_clocks_update_dentist(clk_mgr);
++ if (update_dppclk)
++ update_clocks_update_dpp_dto(clk_mgr, context);
++
++ }
++}
++
++void dcn2_update_clocks_fpga(struct clk_mgr *clk_mgr,
++ struct dc_state *context,
++ bool safe_to_lower)
++{
++ struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk;
++
++ if (should_set_clock(safe_to_lower, new_clocks->phyclk_khz, clk_mgr->clks.phyclk_khz)) {
++ clk_mgr->clks.phyclk_khz = new_clocks->phyclk_khz;
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, clk_mgr->clks.dcfclk_khz)) {
++ clk_mgr->clks.dcfclk_khz = new_clocks->dcfclk_khz;
++ }
++
++ if (should_set_clock(safe_to_lower,
++ new_clocks->dcfclk_deep_sleep_khz, clk_mgr->clks.dcfclk_deep_sleep_khz)) {
++ clk_mgr->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz;
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->socclk_khz, clk_mgr->clks.socclk_khz)) {
++ clk_mgr->clks.socclk_khz = new_clocks->socclk_khz;
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dramclk_khz, clk_mgr->clks.dramclk_khz)) {
++ clk_mgr->clks.dramclk_khz = new_clocks->dramclk_khz;
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->clks.dppclk_khz)) {
++ clk_mgr->clks.dppclk_khz = new_clocks->dppclk_khz;
++ }
++
++ /* Add 250MHz as safety margin */
++ if (should_set_clock(safe_to_lower, new_clocks->fclk_khz + 250000, clk_mgr->clks.fclk_khz)) {
++ clk_mgr->clks.fclk_khz = new_clocks->fclk_khz + 250000;
++ }
++
++ if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr->clks.dispclk_khz)) {
++ clk_mgr->clks.dispclk_khz = new_clocks->dispclk_khz;
++ }
++
++ /* Both fclk and dppclk ref are run on the same scemi clock so we
++ * need to keep the same value for both
++ */
++ if (clk_mgr->clks.fclk_khz > clk_mgr->clks.dppclk_khz)
++ clk_mgr->clks.dppclk_khz = clk_mgr->clks.fclk_khz;
++
++ dm_set_dcn_clocks(clk_mgr->ctx, &clk_mgr->clks);
++}
++
++void dcn2_init_clocks(struct clk_mgr *clk_mgr)
++{
++ memset(&(clk_mgr->clks), 0, sizeof(struct dc_clocks));
++}
++
++static struct clk_mgr_funcs dcn2_funcs = {
++ .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz,
++ .update_clocks = dcn2_update_clocks,
++ .init_clocks = dcn2_init_clocks
++};
++
++
++void dcn20_clk_mgr_construct(
++ struct dc_context *ctx,
++ struct clk_mgr_internal *clk_mgr,
++ struct pp_smu_funcs *pp_smu,
++ struct dccg *dccg)
++{
++ clk_mgr->base.ctx = ctx;
++ clk_mgr->base.funcs = &dcn2_funcs;
++ clk_mgr->regs = &clk_mgr_regs;
++ clk_mgr->clk_mgr_shift = &clk_mgr_shift;
++ clk_mgr->clk_mgr_mask = &clk_mgr_mask;
++
++ clk_mgr->dccg = dccg;
++ clk_mgr->dfs_bypass_disp_clk = 0;
++
++ clk_mgr->dprefclk_ss_percentage = 0;
++ clk_mgr->dprefclk_ss_divider = 1000;
++ clk_mgr->ss_on_dprefclk = false;
++
++ clk_mgr->base.dprefclk_khz = 700000; // 700 MHz planned if VCO is 3.85 GHz, will be retrieved
++
++ if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) {
++ dcn2_funcs.update_clocks = dcn2_update_clocks_fpga;
++ clk_mgr->dentist_vco_freq_khz = 3850000;
++
++ } else {
++ /* DFS Slice 2 should be used for DPREFCLK */
++ int dprefclk_did = REG_READ(CLK3_CLK2_DFS_CNTL);
++ /* Convert DPREFCLK DFS Slice DID to actual divider*/
++ int target_div = dentist_get_divider_from_did(dprefclk_did);
++
++ /* get FbMult value */
++ uint32_t pll_req_reg = REG_READ(CLK3_CLK_PLL_REQ);
++ struct fixed31_32 pll_req;
++
++ /* set up a fixed-point number
++ * this works because the int part is on the right edge of the register
++ * and the frac part is on the left edge
++ */
++ pll_req = dc_fixpt_from_int(pll_req_reg & clk_mgr->clk_mgr_mask->FbMult_int);
++ pll_req.value |= pll_req_reg & clk_mgr->clk_mgr_mask->FbMult_frac;
++
++ /* multiply by REFCLK period */
++ pll_req = dc_fixpt_mul_int(pll_req, 100000);
++
++ /* integer part is now VCO frequency in kHz */
++ clk_mgr->dentist_vco_freq_khz = dc_fixpt_floor(pll_req);
++
++ /* in case we don't get a value from the register, use default */
++ if (clk_mgr->dentist_vco_freq_khz == 0)
++ clk_mgr->dentist_vco_freq_khz = 3850000;
++
++ /* Calculate the DPREFCLK in kHz.*/
++ clk_mgr->base.dprefclk_khz = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
++ * clk_mgr->dentist_vco_freq_khz) / target_div;
++ }
++ //Integrated_info table does not exist on dGPU projects so should not be referenced
++ //anywhere in code for dGPUs.
++ //Also there is no plan for now that DFS BYPASS will be used on NV10/12/14.
++ clk_mgr->dfs_bypass_enabled = false;
++
++ dce_clock_read_ss_info(clk_mgr);
++}
++
+diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.h
+new file mode 100644
+index 000000000000..a3479f96eb9b
+--- /dev/null
++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.h
+@@ -0,0 +1,44 @@
++/*
++ * Copyright 2018 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 __DCN20_CLK_MGR_H__
++#define __DCN20_CLK_MGR_H__
++
++void dcn2_update_clocks(struct clk_mgr *dccg,
++ struct dc_state *context,
++ bool safe_to_lower);
++
++void dcn2_update_clocks_fpga(struct clk_mgr *clk_mgr,
++ struct dc_state *context,
++ bool safe_to_lower);
++
++void dcn2_init_clocks(struct clk_mgr *clk_mgr);
++
++void dcn20_clk_mgr_construct(struct dc_context *ctx,
++ struct clk_mgr_internal *clk_mgr,
++ struct pp_smu_funcs *pp_smu,
++ struct dccg *dccg);
++
++#endif //__DCN20_CLK_MGR_H__
+diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
+index 01efcddea359..bf8cfd9b3e8f 100644
+--- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
+@@ -53,6 +53,8 @@
+ #define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1
+ #define MAX_PLL_CALC_ERROR 0xFFFFFFFF
+
++#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))
++
+ static const struct spread_spectrum_data *get_ss_data_entry(
+ struct dce110_clk_src *clk_src,
+ enum signal_type signal,
+@@ -1000,6 +1002,95 @@ static bool get_pixel_clk_frequency_100hz(
+ return false;
+ }
+
++#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
++
++/* this table is use to find *1.001 and /1.001 pixel rates from non-precise pixel rate */
++struct pixel_rate_range_table_entry {
++ unsigned int range_min_khz;
++ unsigned int range_max_khz;
++ unsigned int target_pixel_rate_khz;
++ unsigned short mult_factor;
++ unsigned short div_factor;
++};
++
++static const struct pixel_rate_range_table_entry video_optimized_pixel_rates[] = {
++ // /1.001 rates
++ {25170, 25180, 25200, 1000, 1001}, //25.2MHz -> 25.17
++ {59340, 59350, 59400, 1000, 1001}, //59.4Mhz -> 59.340
++ {74170, 74180, 74250, 1000, 1001}, //74.25Mhz -> 74.1758
++ {125870, 125880, 126000, 1000, 1001}, //126Mhz -> 125.87
++ {148350, 148360, 148500, 1000, 1001}, //148.5Mhz -> 148.3516
++ {167830, 167840, 168000, 1000, 1001}, //168Mhz -> 167.83
++ {222520, 222530, 222750, 1000, 1001}, //222.75Mhz -> 222.527
++ {257140, 257150, 257400, 1000, 1001}, //257.4Mhz -> 257.1429
++ {296700, 296710, 297000, 1000, 1001}, //297Mhz -> 296.7033
++ {342850, 342860, 343200, 1000, 1001}, //343.2Mhz -> 342.857
++ {395600, 395610, 396000, 1000, 1001}, //396Mhz -> 395.6
++ {409090, 409100, 409500, 1000, 1001}, //409.5Mhz -> 409.091
++ {445050, 445060, 445500, 1000, 1001}, //445.5Mhz -> 445.055
++ {467530, 467540, 468000, 1000, 1001}, //468Mhz -> 467.5325
++ {519230, 519240, 519750, 1000, 1001}, //519.75Mhz -> 519.231
++ {525970, 525980, 526500, 1000, 1001}, //526.5Mhz -> 525.974
++ {545450, 545460, 546000, 1000, 1001}, //546Mhz -> 545.455
++ {593400, 593410, 594000, 1000, 1001}, //594Mhz -> 593.4066
++ {623370, 623380, 624000, 1000, 1001}, //624Mhz -> 623.377
++ {692300, 692310, 693000, 1000, 1001}, //693Mhz -> 692.308
++ {701290, 701300, 702000, 1000, 1001}, //702Mhz -> 701.2987
++ {791200, 791210, 792000, 1000, 1001}, //792Mhz -> 791.209
++ {890100, 890110, 891000, 1000, 1001}, //891Mhz -> 890.1099
++ {1186810, 1186820, 1188000, 1000, 1001},//1188Mhz -> 1186.8131
++
++ // *1.001 rates
++ {27020, 27030, 27000, 1001, 1000}, //27Mhz
++ {54050, 54060, 54000, 1001, 1000}, //54Mhz
++ {108100, 108110, 108000, 1001, 1000},//108Mhz
++};
++
++static const struct pixel_rate_range_table_entry *look_up_in_video_optimized_rate_tlb(
++ unsigned int pixel_rate_khz)
++{
++ int i;
++
++ for (i = 0; i < NUM_ELEMENTS(video_optimized_pixel_rates); i++) {
++ const struct pixel_rate_range_table_entry *e = &video_optimized_pixel_rates[i];
++
++ if (e->range_min_khz <= pixel_rate_khz && pixel_rate_khz <= e->range_max_khz) {
++ return e;
++ }
++ }
++
++ return NULL;
++}
++
++static bool dcn20_program_pix_clk(
++ struct clock_source *clock_source,
++ struct pixel_clk_params *pix_clk_params,
++ struct pll_settings *pll_settings)
++{
++ struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source);
++ unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0;
++ unsigned int dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz;
++ const struct pixel_rate_range_table_entry *e =
++ look_up_in_video_optimized_rate_tlb(pll_settings->actual_pix_clk_100hz / 10);
++
++ dce112_program_pix_clk(clock_source, pix_clk_params, pll_settings);
++
++ if (e) {
++ /* Set DTO values: phase = target clock, modulo = reference clock */
++ REG_WRITE(PHASE[inst], e->target_pixel_rate_khz * e->mult_factor);
++ REG_WRITE(MODULO[inst], dp_dto_ref_khz * e->div_factor);
++ }
++
++ return true;
++}
++
++static const struct clock_source_funcs dcn20_clk_src_funcs = {
++ .cs_power_down = dce110_clock_source_power_down,
++ .program_pix_clk = dcn20_program_pix_clk,
++ .get_pix_clk_dividers = dce112_get_pix_clk_dividers
++};
++#endif
++
+ /*****************************************/
+ /* Constructor */
+ /*****************************************/
+@@ -1376,3 +1467,20 @@ bool dce112_clk_src_construct(
+ return true;
+ }
+
++#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
++bool dcn20_clk_src_construct(
++ struct dce110_clk_src *clk_src,
++ struct dc_context *ctx,
++ struct dc_bios *bios,
++ enum clock_source_id id,
++ const struct dce110_clk_src_regs *regs,
++ const struct dce110_clk_src_shift *cs_shift,
++ const struct dce110_clk_src_mask *cs_mask)
++{
++ bool ret = dce112_clk_src_construct(clk_src, ctx, bios, id, regs, cs_shift, cs_mask);
++
++ clk_src->base.funcs = &dcn20_clk_src_funcs;
++
++ return ret;
++}
++#endif
+diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h
+index 1ed7695a76d3..adae03b1f3a7 100644
+--- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h
++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.h
+@@ -55,6 +55,37 @@
+ CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\
+ CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_PIXCLK_DOUBLE_RATE_ENABLE, mask_sh)
+
++#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
++#define CS_COMMON_REG_LIST_DCN2_0(index, pllid) \
++ SRI(PIXCLK_RESYNC_CNTL, PHYPLL, pllid),\
++ SRII(PHASE, DP_DTO, 0),\
++ SRII(PHASE, DP_DTO, 1),\
++ SRII(PHASE, DP_DTO, 2),\
++ SRII(PHASE, DP_DTO, 3),\
++ SRII(PHASE, DP_DTO, 4),\
++ SRII(PHASE, DP_DTO, 5),\
++ SRII(MODULO, DP_DTO, 0),\
++ SRII(MODULO, DP_DTO, 1),\
++ SRII(MODULO, DP_DTO, 2),\
++ SRII(MODULO, DP_DTO, 3),\
++ SRII(MODULO, DP_DTO, 4),\
++ SRII(MODULO, DP_DTO, 5),\
++ SRII(PIXEL_RATE_CNTL, OTG, 0),\
++ SRII(PIXEL_RATE_CNTL, OTG, 1),\
++ SRII(PIXEL_RATE_CNTL, OTG, 2),\
++ SRII(PIXEL_RATE_CNTL, OTG, 3),\
++ SRII(PIXEL_RATE_CNTL, OTG, 4),\
++ SRII(PIXEL_RATE_CNTL, OTG, 5)
++#endif
++
++#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
++#define CS_COMMON_MASK_SH_LIST_DCN2_0(mask_sh)\
++ CS_SF(DP_DTO0_PHASE, DP_DTO0_PHASE, mask_sh),\
++ CS_SF(DP_DTO0_MODULO, DP_DTO0_MODULO, mask_sh),\
++ CS_SF(PHYPLLA_PIXCLK_RESYNC_CNTL, PHYPLLA_DCCG_DEEP_COLOR_CNTL, mask_sh),\
++ CS_SF(OTG0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE, mask_sh)
++#endif
++
+ #if defined(CONFIG_DRM_AMD_DC_DCN1_0)
+
+ #define CS_COMMON_REG_LIST_DCN1_0(index, pllid) \
+@@ -153,4 +184,15 @@ bool dce112_clk_src_construct(
+ const struct dce110_clk_src_shift *cs_shift,
+ const struct dce110_clk_src_mask *cs_mask);
+
++#if defined(CONFIG_DRM_AMD_DC_DCN2_0)
++bool dcn20_clk_src_construct(
++ struct dce110_clk_src *clk_src,
++ struct dc_context *ctx,
++ struct dc_bios *bios,
++ enum clock_source_id id,
++ const struct dce110_clk_src_regs *regs,
++ const struct dce110_clk_src_shift *cs_shift,
++ const struct dce110_clk_src_mask *cs_mask);
++#endif
++
+ #endif
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.c
+new file mode 100644
+index 000000000000..23362dd4b6d3
+--- /dev/null
++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.c
+@@ -0,0 +1,157 @@
++/*
++ * Copyright 2018 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 "reg_helper.h"
++#include "core_types.h"
++#include "dcn20_dccg.h"
++
++#define TO_DCN_DCCG(dccg)\
++ container_of(dccg, struct dcn_dccg, base)
++
++#define REG(reg) \
++ (dccg_dcn->regs->reg)
++
++#undef FN
++#define FN(reg_name, field_name) \
++ dccg_dcn->dccg_shift->field_name, dccg_dcn->dccg_mask->field_name
++
++#define CTX \
++ dccg_dcn->base.ctx
++#define DC_LOGGER \
++ dccg->ctx->logger
++
++void dccg2_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppclk)
++{
++ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
++
++ if (dccg->ref_dppclk && req_dppclk) {
++ int ref_dppclk = dccg->ref_dppclk;
++
++ ASSERT(req_dppclk <= ref_dppclk);
++ /* need to clamp to 8 bits */
++ if (ref_dppclk > 0xff) {
++ int divider = (ref_dppclk + 0xfe) / 0xff;
++
++ ref_dppclk /= divider;
++ req_dppclk = (req_dppclk + divider - 1) / divider;
++ if (req_dppclk > ref_dppclk)
++ req_dppclk = ref_dppclk;
++ }
++ REG_SET_2(DPPCLK_DTO_PARAM[dpp_inst], 0,
++ DPPCLK0_DTO_PHASE, req_dppclk,
++ DPPCLK0_DTO_MODULO, ref_dppclk);
++ REG_UPDATE(DPPCLK_DTO_CTRL,
++ DPPCLK_DTO_ENABLE[dpp_inst], 1);
++ } else {
++ REG_UPDATE(DPPCLK_DTO_CTRL,
++ DPPCLK_DTO_ENABLE[dpp_inst], 0);
++ }
++}
++
++void dccg2_get_dccg_ref_freq(struct dccg *dccg,
++ unsigned int xtalin_freq_inKhz,
++ unsigned int *dccg_ref_freq_inKhz)
++{
++ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
++ uint32_t clk_en = 0;
++ uint32_t clk_sel = 0;
++
++ REG_GET_2(REFCLK_CNTL, REFCLK_CLOCK_EN, &clk_en, REFCLK_SRC_SEL, &clk_sel);
++
++ if (clk_en != 0) {
++ // DCN20 has never been validated for non-xtalin as reference
++ // frequency. There's actually no way for DC to determine what
++ // frequency a non-xtalin source is.
++ ASSERT_CRITICAL(false);
++ }
++
++ *dccg_ref_freq_inKhz = xtalin_freq_inKhz;
++
++ return;
++}
++
++void dccg2_init(struct dccg *dccg)
++{
++ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
++
++ // Fallthrough intentional to program all available dpp_dto's
++ switch (dccg_dcn->base.ctx->dc->res_pool->pipe_count) {
++ case 6:
++ REG_UPDATE(DPPCLK_DTO_CTRL, DPPCLK_DTO_DB_EN[5], 1);
++ case 5:
++ REG_UPDATE(DPPCLK_DTO_CTRL, DPPCLK_DTO_DB_EN[4], 1);
++ case 4:
++ REG_UPDATE(DPPCLK_DTO_CTRL, DPPCLK_DTO_DB_EN[3], 1);
++ case 3:
++ REG_UPDATE(DPPCLK_DTO_CTRL, DPPCLK_DTO_DB_EN[2], 1);
++ case 2:
++ REG_UPDATE(DPPCLK_DTO_CTRL, DPPCLK_DTO_DB_EN[1], 1);
++ case 1:
++ REG_UPDATE(DPPCLK_DTO_CTRL, DPPCLK_DTO_DB_EN[0], 1);
++ break;
++ default:
++ ASSERT(false);
++ break;
++ }
++}
++
++static const struct dccg_funcs dccg2_funcs = {
++ .update_dpp_dto = dccg2_update_dpp_dto,
++ .get_dccg_ref_freq = dccg2_get_dccg_ref_freq,
++ .dccg_init = dccg2_init
++};
++
++struct dccg *dccg2_create(
++ struct dc_context *ctx,
++ const struct dccg_registers *regs,
++ const struct dccg_shift *dccg_shift,
++ const struct dccg_mask *dccg_mask)
++{
++ struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_KERNEL);
++ struct dccg *base;
++
++ if (dccg_dcn == NULL) {
++ BREAK_TO_DEBUGGER();
++ return NULL;
++ }
++
++ base = &dccg_dcn->base;
++ base->ctx = ctx;
++ base->funcs = &dccg2_funcs;
++
++ dccg_dcn->regs = regs;
++ dccg_dcn->dccg_shift = dccg_shift;
++ dccg_dcn->dccg_mask = dccg_mask;
++
++ return &dccg_dcn->base;
++}
++
++void dcn_dccg_destroy(struct dccg **dccg)
++{
++ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(*dccg);
++
++ kfree(dccg_dcn);
++ *dccg = NULL;
++}
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.h b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.h
+new file mode 100644
+index 000000000000..2205cb0204e7
+--- /dev/null
++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_dccg.h
+@@ -0,0 +1,116 @@
++/*
++ * Copyright 2018 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 __DCN20_DCCG_H__
++#define __DCN20_DCCG_H__
++
++#include "dccg.h"
++
++#define DCCG_COMMON_REG_LIST_DCN_BASE() \
++ SR(DPPCLK_DTO_CTRL),\
++ DCCG_SRII(DTO_PARAM, DPPCLK, 0),\
++ DCCG_SRII(DTO_PARAM, DPPCLK, 1),\
++ DCCG_SRII(DTO_PARAM, DPPCLK, 2),\
++ DCCG_SRII(DTO_PARAM, DPPCLK, 3),\
++ SR(REFCLK_CNTL)
++
++#define DCCG_REG_LIST_DCN2() \
++ DCCG_COMMON_REG_LIST_DCN_BASE(),\
++ DCCG_SRII(DTO_PARAM, DPPCLK, 4),\
++ DCCG_SRII(DTO_PARAM, DPPCLK, 5)
++
++#define DCCG_SF(reg_name, field_name, post_fix)\
++ .field_name = reg_name ## __ ## field_name ## post_fix
++
++#define DCCG_SFI(reg_name, field_name, field_prefix, inst, post_fix)\
++ .field_prefix ## _ ## field_name[inst] = reg_name ## __ ## field_prefix ## inst ## _ ## field_name ## post_fix
++
++#define DCCG_COMMON_MASK_SH_LIST_DCN_COMMON_BASE(mask_sh) \
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 0, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 1, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 1, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 2, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 2, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 3, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 3, mask_sh),\
++ DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_PHASE, mask_sh),\
++ DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_MODULO, mask_sh),\
++ DCCG_SF(REFCLK_CNTL, REFCLK_CLOCK_EN, mask_sh),\
++ DCCG_SF(REFCLK_CNTL, REFCLK_SRC_SEL, mask_sh)
++
++#define DCCG_MASK_SH_LIST_DCN2(mask_sh) \
++ DCCG_COMMON_MASK_SH_LIST_DCN_COMMON_BASE(mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 4, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 4, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 5, mask_sh),\
++ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 5, mask_sh)
++
++#define DCCG_REG_FIELD_LIST(type) \
++ type DPPCLK0_DTO_PHASE;\
++ type DPPCLK0_DTO_MODULO;\
++ type DPPCLK_DTO_ENABLE[6];\
++ type DPPCLK_DTO_DB_EN[6];\
++ type REFCLK_CLOCK_EN;\
++ type REFCLK_SRC_SEL;
++
++struct dccg_shift {
++ DCCG_REG_FIELD_LIST(uint8_t)
++};
++
++struct dccg_mask {
++ DCCG_REG_FIELD_LIST(uint32_t)
++};
++
++struct dccg_registers {
++ uint32_t DPPCLK_DTO_CTRL;
++ uint32_t DPPCLK_DTO_PARAM[6];
++ uint32_t REFCLK_CNTL;
++};
++
++struct dcn_dccg {
++ struct dccg base;
++ const struct dccg_registers *regs;
++ const struct dccg_shift *dccg_shift;
++ const struct dccg_mask *dccg_mask;
++};
++
++void dccg2_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppclk);
++
++void dccg2_get_dccg_ref_freq(struct dccg *dccg,
++ unsigned int xtalin_freq_inKhz,
++ unsigned int *dccg_ref_freq_inKhz);
++
++void dccg2_init(struct dccg *dccg);
++
++struct dccg *dccg2_create(
++ struct dc_context *ctx,
++ const struct dccg_registers *regs,
++ const struct dccg_shift *dccg_shift,
++ const struct dccg_mask *dccg_mask);
++
++void dcn_dccg_destroy(struct dccg **dccg);
++
++#endif //__DCN20_DCCG_H__
+diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
+index 6e189b1283aa..c322e4697242 100644
+--- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
++++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
+@@ -27,6 +27,7 @@
+ #define __DAL_CLK_MGR_INTERNAL_H__
+
+ #include "clk_mgr.h"
++#include "dc.h"
+
+ /*
+ * only thing needed from here is MEMORY_TYPE_MULTIPLIER_CZ, which is also
+@@ -34,6 +35,29 @@
+ */
+ #include "resource.h"
+
++
++/* Starting DID for each range */
++enum dentist_base_divider_id {
++ DENTIST_BASE_DID_1 = 0x08,
++ DENTIST_BASE_DID_2 = 0x40,
++ DENTIST_BASE_DID_3 = 0x60,
++ DENTIST_BASE_DID_4 = 0x7e,
++ DENTIST_MAX_DID = 0x7f
++};
++
++/* Starting point and step size for each divider range.*/
++enum dentist_divider_range {
++ DENTIST_DIVIDER_RANGE_1_START = 8, /* 2.00 */
++ DENTIST_DIVIDER_RANGE_1_STEP = 1, /* 0.25 */
++ DENTIST_DIVIDER_RANGE_2_START = 64, /* 16.00 */
++ DENTIST_DIVIDER_RANGE_2_STEP = 2, /* 0.50 */
++ DENTIST_DIVIDER_RANGE_3_START = 128, /* 32.00 */
++ DENTIST_DIVIDER_RANGE_3_STEP = 4, /* 1.00 */
++ DENTIST_DIVIDER_RANGE_4_START = 248, /* 62.00 */
++ DENTIST_DIVIDER_RANGE_4_STEP = 264, /* 66.00 */
++ DENTIST_DIVIDER_RANGE_SCALE_FACTOR = 4
++};
++
+ /*
+ ***************************************************************************************
+ ****************** Clock Manager Private Macros and Defines ***************************
+@@ -65,6 +89,18 @@
+ #define CLK_COMMON_REG_LIST_DCN_BASE() \
+ SR(DENTIST_DISPCLK_CNTL)
+
++#define VBIOS_SMU_MSG_BOX_REG_LIST_RV() \
++ .MP1_SMN_C2PMSG_91 = mmMP1_SMN_C2PMSG_91, \
++ .MP1_SMN_C2PMSG_83 = mmMP1_SMN_C2PMSG_83, \
++ .MP1_SMN_C2PMSG_67 = mmMP1_SMN_C2PMSG_67
++
++#ifdef CONFIG_DRM_AMD_DC_DCN2_0
++#define CLK_REG_LIST_NV10() \
++ SR(DENTIST_DISPCLK_CNTL), \
++ CLK_SRI(CLK3_CLK_PLL_REQ, CLK3, 0), \
++ CLK_SRI(CLK3_CLK2_DFS_CNTL, CLK3, 0)
++#endif
++
+ #define CLK_SF(reg_name, field_name, post_fix)\
+ .field_name = reg_name ## __ ## field_name ## post_fix
+
+@@ -82,6 +118,17 @@
+ CLK_SF(MP1_SMN_C2PMSG_83, CONTENT, mask_sh),\
+ CLK_SF(MP1_SMN_C2PMSG_91, CONTENT, mask_sh),
+
++#ifdef CONFIG_DRM_AMD_DC_DCN2_0
++#define CLK_COMMON_MASK_SH_LIST_DCN20_BASE(mask_sh) \
++ CLK_COMMON_MASK_SH_LIST_DCN_COMMON_BASE(mask_sh),\
++ CLK_SF(DENTIST_DISPCLK_CNTL, DENTIST_DPPCLK_WDIVIDER, mask_sh),\
++ CLK_SF(DENTIST_DISPCLK_CNTL, DENTIST_DPPCLK_CHG_DONE, mask_sh)
++
++#define CLK_MASK_SH_LIST_NV10(mask_sh) \
++ CLK_COMMON_MASK_SH_LIST_DCN20_BASE(mask_sh),\
++ CLK_SF(CLK3_0_CLK3_CLK_PLL_REQ, FbMult_int, mask_sh),\
++ CLK_SF(CLK3_0_CLK3_CLK_PLL_REQ, FbMult_frac, mask_sh)
++#endif
+
+ #define CLK_REG_FIELD_LIST(type) \
+ type DPREFCLK_SRC_SEL; \
+@@ -94,21 +141,46 @@
+ ****************** Clock Manager Private Structures ***********************************
+ ***************************************************************************************
+ */
++#ifdef CONFIG_DRM_AMD_DC_DCN2_0
++#define CLK20_REG_FIELD_LIST(type) \
++ type DENTIST_DPPCLK_WDIVIDER; \
++ type DENTIST_DPPCLK_CHG_DONE; \
++ type FbMult_int; \
++ type FbMult_frac;
++#endif
+
+-struct clk_mgr_registers {
+- uint32_t DPREFCLK_CNTL;
+- uint32_t DENTIST_DISPCLK_CNTL;
+-
+-};
++#define VBIOS_SMU_REG_FIELD_LIST(type) \
++ type CONTENT;
+
+ struct clk_mgr_shift {
+ CLK_REG_FIELD_LIST(uint8_t)
++#ifdef CONFIG_DRM_AMD_DC_DCN2_0
++ CLK20_REG_FIELD_LIST(uint8_t)
++#endif
++ VBIOS_SMU_REG_FIELD_LIST(uint32_t)
+ };
+
+ struct clk_mgr_mask {
+ CLK_REG_FIELD_LIST(uint32_t)
++#ifdef CONFIG_DRM_AMD_DC_DCN2_0
++ CLK20_REG_FIELD_LIST(uint32_t)
++#endif
++ VBIOS_SMU_REG_FIELD_LIST(uint32_t)
+ };
+
++struct clk_mgr_registers {
++ uint32_t DPREFCLK_CNTL;
++ uint32_t DENTIST_DISPCLK_CNTL;
++
++#ifdef CONFIG_DRM_AMD_DC_DCN2_0
++ uint32_t CLK3_CLK2_DFS_CNTL;
++ uint32_t CLK3_CLK_PLL_REQ;
++#endif
++
++ uint32_t MP1_SMN_C2PMSG_67;
++ uint32_t MP1_SMN_C2PMSG_83;
++ uint32_t MP1_SMN_C2PMSG_91;
++};
+
+ struct state_dependent_clocks {
+ int display_clk_khz;
+--
+2.17.1
+