Commit 508f5fcb authored by Leo Li's avatar Leo Li Committed by Alex Deucher

drm/amd/display: Compensate for XGMI SS downspread on dprefclk

[Why]
When XGMI is enabled, we need to adjust the dprefclk according to the
WAFL link's spread spectrum info. This is for VG20 (DCE121) only.

[How]
dce_clk_mgr already stores SS info, currently being used by audio clock.
Therefore, patch the clk_mgr's SS info with the xGMI SS info, if xGMI
is enabled. For display clock, adjust it during dce12_update_clocks()
before calling set_clock().

Since we rely on a mmhub register to reliably determine if xGMI is
enabled, the patching step needs to happen after resource_construct()
has initialized the hardware sequencer.
Signed-off-by: default avatarLeo Li <sunpeng.li@amd.com>
Reviewed-by: default avatarHersen Wu <hersenxs.wu@amd.com>
Acked-by: default avatarHarry Wentland <Harry.Wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 09f609c3
......@@ -450,6 +450,42 @@ void dce_clock_read_ss_info(struct dce_clk_mgr *clk_mgr_dce)
}
}
/**
* dce121_clock_patch_xgmi_ss_info() - Save XGMI spread spectrum info
* @clk_mgr: clock manager base structure
*
* Reads from VBIOS the XGMI spread spectrum info and saves it within
* the dce clock manager. This operation will overwrite the existing dprefclk
* SS values if the vBIOS query succeeds. Otherwise, it does nothing. It also
* sets the ->xgmi_enabled flag.
*/
void dce121_clock_patch_xgmi_ss_info(struct clk_mgr *clk_mgr)
{
struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr);
enum bp_result result;
struct spread_spectrum_info info = { { 0 } };
struct dc_bios *bp = clk_mgr_dce->base.ctx->dc_bios;
clk_mgr_dce->xgmi_enabled = false;
result = bp->funcs->get_spread_spectrum_info(bp, AS_SIGNAL_TYPE_XGMI,
0, &info);
if (result == BP_RESULT_OK && info.spread_spectrum_percentage != 0) {
clk_mgr_dce->xgmi_enabled = true;
clk_mgr_dce->ss_on_dprefclk = true;
clk_mgr_dce->dprefclk_ss_divider =
info.spread_percentage_divider;
if (info.type.CENTER_MODE == 0) {
/* Currently for DP Reference clock we
* need only SS percentage for
* downspread */
clk_mgr_dce->dprefclk_ss_percentage =
info.spread_spectrum_percentage;
}
}
}
void dce110_fill_display_configs(
const struct dc_state *context,
struct dm_pp_display_configuration *pp_display_cfg)
......@@ -710,6 +746,13 @@ static void dce12_update_clocks(struct clk_mgr *clk_mgr,
if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr->clks.dispclk_khz)) {
clock_voltage_req.clk_type = DM_PP_CLOCK_TYPE_DISPLAY_CLK;
/*
* When xGMI is enabled, the display clk needs to be adjusted
* with the WAFL link's SS percentage.
*/
if (clk_mgr_dce->xgmi_enabled)
patched_disp_clk = clk_mgr_adjust_dp_ref_freq_for_ss(
clk_mgr_dce, patched_disp_clk);
clock_voltage_req.clocks_in_khz = patched_disp_clk;
clk_mgr->clks.dispclk_khz = dce112_set_clock(clk_mgr, patched_disp_clk);
......@@ -875,6 +918,27 @@ struct clk_mgr *dce120_clk_mgr_create(struct dc_context *ctx)
return &clk_mgr_dce->base;
}
struct clk_mgr *dce121_clk_mgr_create(struct dc_context *ctx)
{
struct dce_clk_mgr *clk_mgr_dce = kzalloc(sizeof(*clk_mgr_dce),
GFP_KERNEL);
if (clk_mgr_dce == NULL) {
BREAK_TO_DEBUGGER();
return NULL;
}
memcpy(clk_mgr_dce->max_clks_by_state, dce120_max_clks_by_state,
sizeof(dce120_max_clks_by_state));
dce_clk_mgr_construct(clk_mgr_dce, ctx, NULL, NULL, NULL);
clk_mgr_dce->dprefclk_khz = 625000;
clk_mgr_dce->base.funcs = &dce120_funcs;
return &clk_mgr_dce->base;
}
void dce_clk_mgr_destroy(struct clk_mgr **clk_mgr)
{
struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(*clk_mgr);
......
......@@ -94,11 +94,37 @@ struct dce_clk_mgr {
* This is basically "Crystal Frequency In KHz" (XTALIN) frequency */
int dfs_bypass_disp_clk;
/* Flag for Enabled SS on DPREFCLK */
/**
* @ss_on_dprefclk:
*
* True if spread spectrum is enabled on the DP ref clock.
*/
bool ss_on_dprefclk;
/* DPREFCLK SS percentage (if down-spread enabled) */
/**
* @xgmi_enabled:
*
* True if xGMI is enabled. On VG20, both audio and display clocks need
* to be adjusted with the WAFL link's SS info if xGMI is enabled.
*/
bool xgmi_enabled;
/**
* @dprefclk_ss_percentage:
*
* DPREFCLK SS percentage (if down-spread enabled).
*
* Note that if XGMI is enabled, the SS info (percentage and divider)
* from the WAFL link is used instead. This is decided during
* dce_clk_mgr initialization.
*/
int dprefclk_ss_percentage;
/* DPREFCLK SS percentage Divider (100 or 1000) */
/**
* @dprefclk_ss_divider:
*
* DPREFCLK SS percentage Divider (100 or 1000).
*/
int dprefclk_ss_divider;
int dprefclk_khz;
......@@ -163,6 +189,9 @@ struct clk_mgr *dce112_clk_mgr_create(
struct clk_mgr *dce120_clk_mgr_create(struct dc_context *ctx);
struct clk_mgr *dce121_clk_mgr_create(struct dc_context *ctx);
void dce121_clock_patch_xgmi_ss_info(struct clk_mgr *clk_mgr);
void dce_clk_mgr_destroy(struct clk_mgr **clk_mgr);
int dentist_get_divider_from_did(int did);
......
......@@ -133,6 +133,10 @@
SR(DCHUB_AGP_TOP), \
BL_REG_LIST()
#define HWSEQ_VG20_REG_LIST() \
HWSEQ_DCE120_REG_LIST(),\
MMHUB_SR(MC_VM_XGMI_LFB_CNTL)
#define HWSEQ_DCE112_REG_LIST() \
HWSEQ_DCE10_REG_LIST(), \
HWSEQ_PIXEL_RATE_REG_LIST(CRTC), \
......@@ -298,6 +302,7 @@ struct dce_hwseq_registers {
uint32_t MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB;
uint32_t MC_VM_SYSTEM_APERTURE_LOW_ADDR;
uint32_t MC_VM_SYSTEM_APERTURE_HIGH_ADDR;
uint32_t MC_VM_XGMI_LFB_CNTL;
uint32_t AZALIA_AUDIO_DTO;
uint32_t AZALIA_CONTROLLER_CLOCK_GATING;
};
......@@ -382,6 +387,11 @@ struct dce_hwseq_registers {
HWS_SF(, LVTMA_PWRSEQ_CNTL, LVTMA_BLON, mask_sh), \
HWS_SF(, LVTMA_PWRSEQ_STATE, LVTMA_PWRSEQ_TARGET_STATE_R, mask_sh)
#define HWSEQ_VG20_MASK_SH_LIST(mask_sh)\
HWSEQ_DCE12_MASK_SH_LIST(mask_sh),\
HWS_SF(, MC_VM_XGMI_LFB_CNTL, PF_LFB_REGION, mask_sh),\
HWS_SF(, MC_VM_XGMI_LFB_CNTL, PF_MAX_REGION, mask_sh)
#define HWSEQ_DCN_MASK_SH_LIST(mask_sh)\
HWSEQ_PIXEL_RATE_MASK_SH_LIST(mask_sh, OTG0_),\
HWS_SF1(OTG0_, PHYPLL_PIXEL_RATE_CNTL, PHYPLL_PIXEL_RATE_SOURCE, mask_sh), \
......@@ -470,6 +480,8 @@ struct dce_hwseq_registers {
type PHYSICAL_PAGE_NUMBER_MSB;\
type PHYSICAL_PAGE_NUMBER_LSB;\
type LOGICAL_ADDR; \
type PF_LFB_REGION;\
type PF_MAX_REGION;\
type ENABLE_L1_TLB;\
type SYSTEM_ACCESS_MODE;\
type LVTMA_BLON;\
......
......@@ -244,6 +244,21 @@ static void dce120_update_dchub(
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
......
......@@ -30,6 +30,7 @@
struct dc;
bool dce121_xgmi_enabled(struct dce_hwseq *hws);
void dce120_hw_sequencer_construct(struct dc *dc);
#endif /* __DC_HWSS_DCE112_H__ */
......
......@@ -62,6 +62,8 @@
#include "soc15_hw_ip.h"
#include "vega10_ip_offset.h"
#include "nbio/nbio_6_1_offset.h"
#include "mmhub/mmhub_9_4_0_offset.h"
#include "mmhub/mmhub_9_4_0_sh_mask.h"
#include "reg_helper.h"
#include "dce100/dce100_resource.h"
......@@ -139,6 +141,17 @@ static const struct dce110_timing_generator_offsets dce120_tg_offsets[] = {
.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 *********************/
......@@ -681,6 +694,19 @@ 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)
{
......@@ -695,6 +721,20 @@ static struct dce_hwseq *dce120_hwseq_create(
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,
......@@ -702,6 +742,14 @@ static const struct resource_create_funcs res_create_funcs = {
.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),
......@@ -911,7 +959,8 @@ static bool construct(
int j;
struct dc_context *ctx = dc->ctx;
struct irq_service_init_data irq_init_data;
bool harvest_enabled = ASICREV_IS_VEGA20_P(ctx->asic_id.hw_internal_rev);
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;
......@@ -975,7 +1024,11 @@ static bool construct(
}
}
pool->base.clk_mgr = dce120_clk_mgr_create(ctx);
if (is_vg20)
pool->base.clk_mgr = dce121_clk_mgr_create(ctx);
else
pool->base.clk_mgr = dce120_clk_mgr_create(ctx);
if (pool->base.clk_mgr == NULL) {
dm_error("DC: failed to create display clock!\n");
BREAK_TO_DEBUGGER();
......@@ -1008,14 +1061,14 @@ static bool construct(
if (!pool->base.irqs)
goto irqs_create_fail;
/* retrieve valid pipe fuses */
if (harvest_enabled)
/* 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 (harvest_enabled) {
if (is_vg20) {
if ((pipe_fuses & (1 << i)) != 0) {
dm_error("DC: skip invalid pipe %d!\n", i);
continue;
......@@ -1093,10 +1146,24 @@ static bool construct(
pool->base.pipe_count = j;
pool->base.timing_generator_count = j;
if (!resource_construct(num_virtual_links, dc, &pool->base,
&res_create_funcs))
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;
/*
* This is a bit of a hack. The xGMI enabled info is used to determine
* if audio and display clocks need to be adjusted with the WAFL link's
* SS info. This is a responsiblity of the clk_mgr. But since MMHUB is
* under hwseq, and the relevant register is in MMHUB, we have to do it
* here.
*/
if (is_vg20 && dce121_xgmi_enabled(dc->hwseq))
dce121_clock_patch_xgmi_ss_info(pool->base.clk_mgr);
/* Create hardware sequencer */
if (!dce120_hw_sequencer_create(dc))
goto controller_create_fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment