Commit 9a70eba7 authored by Dmytro Laktyushkin's avatar Dmytro Laktyushkin Committed by Alex Deucher

drm/amd/display: consolidate dce8-11.2 display clock code

Signed-off-by: default avatarDmytro Laktyushkin <Dmytro.Laktyushkin@amd.com>
Reviewed-by: default avatarTony Cheng <Tony.Cheng@amd.com>
Acked-by: default avatarHarry Wentland <Harry.Wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent e9c58bb4
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
# offset/shift/mask stored in dce_hw struct # offset/shift/mask stored in dce_hw struct
DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o dce_hwseq.o \ DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o dce_hwseq.o \
dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o \
dce_clocks.o
AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE)) AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE))
......
...@@ -888,7 +888,9 @@ static const struct audio_funcs funcs = { ...@@ -888,7 +888,9 @@ static const struct audio_funcs funcs = {
void dce_aud_destroy(struct audio **audio) void dce_aud_destroy(struct audio **audio)
{ {
dm_free(*audio); struct dce_audio *aud = DCE_AUD(*audio);
dm_free(aud);
*audio = NULL; *audio = NULL;
} }
......
/* /*
* Copyright 2012-15 Advanced Micro Devices, Inc. * Copyright 2012-16 Advanced Micro Devices, Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -23,36 +23,23 @@ ...@@ -23,36 +23,23 @@
* *
*/ */
#include "dce_clocks.h"
#include "dm_services.h" #include "dm_services.h"
#include "reg_helper.h"
#include "dce/dce_11_0_d.h" #include "fixed32_32.h"
#include "dce/dce_11_0_sh_mask.h" #include "bios_parser_interface.h"
#include "include/bios_parser_interface.h"
#include "include/fixed32_32.h"
#include "include/logger_interface.h"
#include "../divider_range.h"
#include "display_clock_dce110.h"
#include "dc.h" #include "dc.h"
#define FROM_DISPLAY_CLOCK(base) \
container_of(base, struct display_clock_dce110, disp_clk_base)
#define PSR_SET_WAITLOOP 0x31 #define REG(reg) \
(clk_dce->regs->reg)
#undef FN
#define FN(reg_name, field_name) \
clk_dce->clk_shift->field_name, clk_dce->clk_mask->field_name
static struct state_dependent_clocks max_clks_by_state[] = { #define CTX \
/*ClocksStateInvalid - should not be used*/ clk_dce->base.ctx
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 },
/*ClocksStateLow*/
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 },
/*ClocksStateNominal*/
{ .display_clk_khz = 467000, .pixel_clk_khz = 400000 },
/*ClocksStatePerformance*/
{ .display_clk_khz = 643000, .pixel_clk_khz = 400000 } };
/* Starting point for each divider range.*/ /* Starting point for each divider range.*/
enum divider_range_start { enum divider_range_start {
...@@ -62,14 +49,6 @@ enum divider_range_start { ...@@ -62,14 +49,6 @@ enum divider_range_start {
DIVIDER_RANGE_SCALE_FACTOR = 100 /* Results are scaled up by 100.*/ DIVIDER_RANGE_SCALE_FACTOR = 100 /* Results are scaled up by 100.*/
}; };
/* Array identifiers and count for the divider ranges.*/
enum divider_range_count {
DIVIDER_RANGE_01 = 0,
DIVIDER_RANGE_02,
DIVIDER_RANGE_03,
DIVIDER_RANGE_MAX /* == 3*/
};
/* Ranges for divider identifiers (Divider ID or DID) /* Ranges for divider identifiers (Divider ID or DID)
mmDENTIST_DISPCLK_CNTL.DENTIST_DISPCLK_WDIVIDER*/ mmDENTIST_DISPCLK_CNTL.DENTIST_DISPCLK_WDIVIDER*/
enum divider_id_register_setting { enum divider_id_register_setting {
...@@ -88,83 +67,33 @@ enum divider_range_step_size { ...@@ -88,83 +67,33 @@ enum divider_range_step_size {
DIVIDER_RANGE_03_STEP_SIZE = 100 /* 1.00 */ DIVIDER_RANGE_03_STEP_SIZE = 100 /* 1.00 */
}; };
union dce110_dmcu_psr_config_data_wait_loop_reg1 {
struct {
unsigned int waitLoop:16; /* [15:0] */
unsigned int reserved:16; /* [31:16] */
} bits;
unsigned int u32All;
};
static struct divider_range divider_ranges[DIVIDER_RANGE_MAX];
#define DCE110_DFS_BYPASS_THRESHOLD_KHZ 400000
/*****************************************************************************
* static functions
*****************************************************************************/
static bool dce110_set_min_clocks_state( static uint32_t dce_clocks_get_dp_ref_freq(struct display_clock *clk)
struct display_clock *dc,
enum dm_pp_clocks_state clocks_state)
{ {
struct dm_pp_power_level_change_request level_change_req = { struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk);
clocks_state }; int dprefclk_wdivider;
int dprefclk_src_sel;
if (clocks_state > dc->max_clks_state) { int dp_ref_clk_khz = 600000;
/*Requested state exceeds max supported state.*/ int target_div = INVALID_DIVIDER;
dm_logger_write(dc->ctx->logger, LOG_WARNING,
"Requested state exceeds max supported state");
return false;
} else if (clocks_state == dc->cur_min_clks_state) {
/*if we're trying to set the same state, we can just return
* since nothing needs to be done*/
return true;
}
/* get max clock state from PPLIB */
if (dm_pp_apply_power_level_change_request(dc->ctx, &level_change_req))
dc->cur_min_clks_state = clocks_state;
return true;
}
static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc)
{
uint32_t dispclk_cntl_value;
uint32_t dp_ref_clk_cntl_value;
uint32_t dp_ref_clk_cntl_src_sel_value;
uint32_t dp_ref_clk_khz = 600000;
uint32_t target_div = INVALID_DIVIDER;
struct display_clock_dce110 *disp_clk = FROM_DISPLAY_CLOCK(dc);
/* ASSERT DP Reference Clock source is from DFS*/ /* ASSERT DP Reference Clock source is from DFS*/
dp_ref_clk_cntl_value = dm_read_reg(dc->ctx, REG_GET(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, &dprefclk_src_sel);
mmDPREFCLK_CNTL); ASSERT(dprefclk_src_sel == 0);
dp_ref_clk_cntl_src_sel_value =
get_reg_field_value(
dp_ref_clk_cntl_value,
DPREFCLK_CNTL, DPREFCLK_SRC_SEL);
ASSERT(dp_ref_clk_cntl_src_sel_value == 0);
/* Read the mmDENTIST_DISPCLK_CNTL to get the currently /* Read the mmDENTIST_DISPCLK_CNTL to get the currently
* programmed DID DENTIST_DPREFCLK_WDIVIDER*/ * programmed DID DENTIST_DPREFCLK_WDIVIDER*/
dispclk_cntl_value = dm_read_reg(dc->ctx, REG_GET(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, &dprefclk_wdivider);
mmDENTIST_DISPCLK_CNTL);
/* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/ /* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/
target_div = dal_divider_range_get_divider( target_div = dal_divider_range_get_divider(
divider_ranges, clk_dce->divider_ranges,
DIVIDER_RANGE_MAX, DIVIDER_RANGE_MAX,
get_reg_field_value(dispclk_cntl_value, dprefclk_wdivider);
DENTIST_DISPCLK_CNTL,
DENTIST_DPREFCLK_WDIVIDER));
if (target_div != INVALID_DIVIDER) { if (target_div != INVALID_DIVIDER) {
/* Calculate the current DFS clock, in kHz.*/ /* Calculate the current DFS clock, in kHz.*/
dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR
* disp_clk->dentist_vco_freq_khz) / target_div; * clk_dce->dentist_vco_freq_khz) / target_div;
} }
/* SW will adjust DP REF Clock average value for all purposes /* SW will adjust DP REF Clock average value for all purposes
...@@ -179,11 +108,11 @@ static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc) ...@@ -179,11 +108,11 @@ static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc)
(should not be case with CIK) then SW should program all rates (should not be case with CIK) then SW should program all rates
generated according to average value (case as with previous ASICs) generated according to average value (case as with previous ASICs)
*/ */
if ((disp_clk->ss_on_gpu_pll) && (disp_clk->gpu_pll_ss_divider != 0)) { if (clk_dce->ss_on_gpu_pll && clk_dce->gpu_pll_ss_divider != 0) {
struct fixed32_32 ss_percentage = dal_fixed32_32_div_int( struct fixed32_32 ss_percentage = dal_fixed32_32_div_int(
dal_fixed32_32_from_fraction( dal_fixed32_32_from_fraction(
disp_clk->gpu_pll_ss_percentage, clk_dce->gpu_pll_ss_percentage,
disp_clk->gpu_pll_ss_divider), 200); clk_dce->gpu_pll_ss_divider), 200);
struct fixed32_32 adj_dp_ref_clk_khz; struct fixed32_32 adj_dp_ref_clk_khz;
ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one, ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one,
...@@ -198,48 +127,188 @@ static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc) ...@@ -198,48 +127,188 @@ static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc)
return dp_ref_clk_khz; return dp_ref_clk_khz;
} }
static void destroy(struct display_clock **base) static enum dm_pp_clocks_state dce_get_required_clocks_state(
struct display_clock *clk,
struct state_dependent_clocks *req_clocks)
{ {
struct display_clock_dce110 *dc110; struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk);
int i;
enum dm_pp_clocks_state low_req_clk;
dc110 = DCLCK110_FROM_BASE(*base); /* Iterate from highest supported to lowest valid state, and update
* lowest RequiredState with the lowest state that satisfies
* all required clocks
*/
for (i = clk->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--)
if (req_clocks->display_clk_khz >
clk_dce->max_clks_by_state[i].display_clk_khz
|| req_clocks->pixel_clk_khz >
clk_dce->max_clks_by_state[i].pixel_clk_khz)
break;
dm_free(dc110); low_req_clk = i + 1;
if (low_req_clk > clk->max_clks_state) {
dm_logger_write(clk->ctx->logger, LOG_WARNING,
"%s: clocks unsupported", __func__);
low_req_clk = DM_PP_CLOCKS_STATE_INVALID;
}
*base = NULL; return low_req_clk;
} }
static bool display_clock_integrated_info_construct( static bool dce_clock_set_min_clocks_state(
struct display_clock_dce110 *disp_clk) struct display_clock *clk,
enum dm_pp_clocks_state clocks_state)
{ {
struct dc_debug *debug = &disp_clk->disp_clk_base.ctx->dc->debug; struct dm_pp_power_level_change_request level_change_req = {
struct dc_bios *bp = disp_clk->disp_clk_base.ctx->dc_bios; clocks_state };
struct integrated_info info;
struct firmware_info fw_info;
uint32_t i;
struct display_clock *base = &disp_clk->disp_clk_base;
memset(&info, 0, sizeof(struct integrated_info)); if (clocks_state > clk->max_clks_state) {
memset(&fw_info, 0, sizeof(struct firmware_info)); /*Requested state exceeds max supported state.*/
dm_logger_write(clk->ctx->logger, LOG_WARNING,
"Requested state exceeds max supported state");
return false;
} else if (clocks_state == clk->cur_min_clks_state) {
/*if we're trying to set the same state, we can just return
* since nothing needs to be done*/
return true;
}
/* get max clock state from PPLIB */
if (dm_pp_apply_power_level_change_request(clk->ctx, &level_change_req))
clk->cur_min_clks_state = clocks_state;
return true;
}
static void dce_set_clock(
struct display_clock *clk,
uint32_t requested_clk_khz)
{
struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk);
struct bp_pixel_clock_parameters pxl_clk_params = { 0 };
struct dc_bios *bp = clk->ctx->dc_bios;
/* Make sure requested clock isn't lower than minimum threshold*/
if (requested_clk_khz > 0)
requested_clk_khz = dm_max(requested_clk_khz,
clk_dce->dentist_vco_freq_khz / 64);
/* Prepare to program display clock*/
pxl_clk_params.target_pixel_clock = requested_clk_khz;
pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS;
bp->funcs->program_display_engine_pll(bp, &pxl_clk_params);
if (clk_dce->dfs_bypass_enabled) {
/* Cache the fixed display clock*/
clk_dce->dfs_bypass_disp_clk =
pxl_clk_params.dfs_bypass_display_clock;
}
/* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.*/
if (requested_clk_khz == 0)
clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
}
#define PSR_SET_WAITLOOP 0x31
union dce110_dmcu_psr_config_data_wait_loop_reg1 {
struct {
unsigned int wait_loop:16; /* [15:0] */
unsigned int reserved:16; /* [31:16] */
} bits;
unsigned int u32;
};
static void dce_psr_wait_loop(
struct dce_disp_clk *clk_dce, unsigned int display_clk_khz)
{
struct dc_context *ctx = clk_dce->base.ctx;
union dce110_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1;
/* waitDMCUReadyForCmd */
REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 100);
masterCmdData1.u32 = 0;
masterCmdData1.bits.wait_loop = display_clk_khz / 1000 / 7;
dm_write_reg(ctx, REG(MASTER_COMM_DATA_REG1), masterCmdData1.u32);
/* setDMCUParam_Cmd */
REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, PSR_SET_WAITLOOP);
/* notifyDMCUMsg */
REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1);
}
static void dce_psr_set_clock(
struct display_clock *clk,
uint32_t requested_clk_khz)
{
struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk);
dce_set_clock(clk, requested_clk_khz);
dce_psr_wait_loop(clk_dce, requested_clk_khz);
}
static void polaris_set_clock(
struct display_clock *clk,
uint32_t requested_clk_khz)
{
struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(clk);
struct bp_set_dce_clock_parameters dce_clk_params;
struct dc_bios *bp = clk->ctx->dc_bios;
/* Prepare to program display clock*/
memset(&dce_clk_params, 0, sizeof(dce_clk_params));
/* Make sure requested clock isn't lower than minimum threshold*/
if (requested_clk_khz > 0)
requested_clk_khz = dm_max(requested_clk_khz,
clk_dce->dentist_vco_freq_khz / 64);
dce_clk_params.target_clock_frequency = requested_clk_khz;
dce_clk_params.pll_id = CLOCK_SOURCE_ID_DFS;
dce_clk_params.clock_type = DCECLOCK_TYPE_DISPLAY_CLOCK;
bp->funcs->set_dce_clock(bp, &dce_clk_params);
/* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.*/
if (requested_clk_khz == 0)
clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
/*Program DP ref Clock*/
/*VBIOS will determine DPREFCLK frequency, so we don't set it*/
dce_clk_params.target_clock_frequency = 0;
dce_clk_params.clock_type = DCECLOCK_TYPE_DPREFCLK;
dce_clk_params.flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK = 0;
bp->funcs->set_dce_clock(bp, &dce_clk_params);
}
static void dce_clock_read_integrated_info(struct dce_disp_clk *clk_dce)
{
struct dc_debug *debug = &clk_dce->base.ctx->dc->debug;
struct dc_bios *bp = clk_dce->base.ctx->dc_bios;
struct integrated_info info = { 0 };
struct firmware_info fw_info = { 0 };
int i;
if (bp->integrated_info) if (bp->integrated_info)
info = *bp->integrated_info; info = *bp->integrated_info;
disp_clk->dentist_vco_freq_khz = info.dentist_vco_freq; clk_dce->dentist_vco_freq_khz = info.dentist_vco_freq;
if (disp_clk->dentist_vco_freq_khz == 0) { if (clk_dce->dentist_vco_freq_khz == 0) {
bp->funcs->get_firmware_info(bp, &fw_info); bp->funcs->get_firmware_info(bp, &fw_info);
disp_clk->dentist_vco_freq_khz = clk_dce->dentist_vco_freq_khz =
fw_info.smu_gpu_pll_output_freq; fw_info.smu_gpu_pll_output_freq;
if (disp_clk->dentist_vco_freq_khz == 0) if (clk_dce->dentist_vco_freq_khz == 0)
disp_clk->dentist_vco_freq_khz = 3600000; clk_dce->dentist_vco_freq_khz = 3600000;
} }
base->min_display_clk_threshold_khz =
disp_clk->dentist_vco_freq_khz / 64;
if (bp->integrated_info == NULL)
return false;
/*update the maximum display clock for each power state*/ /*update the maximum display clock for each power state*/
for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID; enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID;
...@@ -268,249 +337,183 @@ static bool display_clock_integrated_info_construct( ...@@ -268,249 +337,183 @@ static bool display_clock_integrated_info_construct(
/*Do not allow bad VBIOS/SBIOS to override with invalid values, /*Do not allow bad VBIOS/SBIOS to override with invalid values,
* check for > 100MHz*/ * check for > 100MHz*/
if (info.disp_clk_voltage[i].max_supported_clk >= 100000) { if (info.disp_clk_voltage[i].max_supported_clk >= 100000)
max_clks_by_state[clk_state].display_clk_khz = clk_dce->max_clks_by_state[clk_state].display_clk_khz =
info.disp_clk_voltage[i].max_supported_clk; info.disp_clk_voltage[i].max_supported_clk;
}
} }
disp_clk->dfs_bypass_enabled = false;
if (!debug->disable_dfs_bypass) if (!debug->disable_dfs_bypass)
if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE)
disp_clk->dfs_bypass_enabled = true; clk_dce->dfs_bypass_enabled = true;
disp_clk->use_max_disp_clk = debug->max_disp_clk; clk_dce->use_max_disp_clk = debug->max_disp_clk;
return true;
} }
static enum dm_pp_clocks_state get_required_clocks_state( static void dce_clock_read_ss_info(struct dce_disp_clk *clk_dce)
struct display_clock *dc,
struct state_dependent_clocks *req_clocks)
{ {
int32_t i; struct dc_bios *bp = clk_dce->base.ctx->dc_bios;
struct display_clock_dce110 *disp_clk = DCLCK110_FROM_BASE(dc); int ss_info_num = bp->funcs->get_ss_entry_number(
enum dm_pp_clocks_state low_req_clk = dc->max_clks_state; bp, AS_SIGNAL_TYPE_GPU_PLL);
if (!req_clocks) { if (ss_info_num) {
/* NULL pointer*/ struct spread_spectrum_info info = { 0 };
dm_logger_write(dc->ctx->logger, LOG_WARNING, enum bp_result result = bp->funcs->get_spread_spectrum_info(
"%s: Invalid parameter", bp, AS_SIGNAL_TYPE_GPU_PLL, 0, &info);
__func__);
return DM_PP_CLOCKS_STATE_INVALID; /* Based on VBIOS, VBIOS will keep entry for GPU PLL SS
} * even if SS not enabled and in that case
* SSInfo.spreadSpectrumPercentage !=0 would be sign
* that SS is enabled
*/
if (result == BP_RESULT_OK &&
info.spread_spectrum_percentage != 0) {
clk_dce->ss_on_gpu_pll = true;
clk_dce->gpu_pll_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_dce->gpu_pll_ss_percentage =
info.spread_spectrum_percentage;
}
}
/* Iterate from highest supported to lowest valid state, and update
* lowest RequiredState with the lowest state that satisfies
* all required clocks
*/
for (i = dc->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; --i) {
if ((req_clocks->display_clk_khz <=
max_clks_by_state[i].display_clk_khz) &&
(req_clocks->pixel_clk_khz <=
max_clks_by_state[i].pixel_clk_khz))
low_req_clk = i;
} }
return low_req_clk;
} }
static void psr_wait_loop(struct dc_context *ctx, unsigned int display_clk_khz) static const struct display_clock_funcs dce112_funcs = {
{ .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq,
unsigned int dmcu_max_retry_on_wait_reg_ready = 801; .get_required_clocks_state = dce_get_required_clocks_state,
unsigned int dmcu_wait_reg_ready_interval = 100; .set_min_clocks_state = dce_clock_set_min_clocks_state,
unsigned int regValue; .set_clock = polaris_set_clock
uint32_t masterCmd; };
uint32_t masterComCntl;
union dce110_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1;
/* waitDMCUReadyForCmd */
do {
dm_delay_in_microseconds(ctx, dmcu_wait_reg_ready_interval);
regValue = dm_read_reg(ctx, mmMASTER_COMM_CNTL_REG);
dmcu_max_retry_on_wait_reg_ready--;
} while
/* expected value is 0, loop while not 0*/
((MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK & regValue) &&
dmcu_max_retry_on_wait_reg_ready > 0);
masterCmdData1.u32All = 0;
masterCmdData1.bits.waitLoop = display_clk_khz / 1000 / 7;
dm_write_reg(ctx, mmMASTER_COMM_DATA_REG1, masterCmdData1.u32All);
/* setDMCUParam_Cmd */
masterCmd = dm_read_reg(ctx, mmMASTER_COMM_CMD_REG);
set_reg_field_value(
masterCmd,
PSR_SET_WAITLOOP,
MASTER_COMM_CMD_REG,
MASTER_COMM_CMD_REG_BYTE0);
dm_write_reg(ctx, mmMASTER_COMM_CMD_REG, masterCmd);
/* notifyDMCUMsg */
masterComCntl = dm_read_reg(ctx, mmMASTER_COMM_CNTL_REG);
set_reg_field_value(
masterComCntl,
1,
MASTER_COMM_CNTL_REG,
MASTER_COMM_INTERRUPT);
dm_write_reg(ctx, mmMASTER_COMM_CNTL_REG, masterComCntl);
}
static void dce110_set_clock(
struct display_clock *base,
uint32_t requested_clk_khz)
{
struct bp_pixel_clock_parameters pxl_clk_params;
struct display_clock_dce110 *dc = DCLCK110_FROM_BASE(base);
struct dc_bios *bp = base->ctx->dc_bios;
/* Prepare to program display clock*/
memset(&pxl_clk_params, 0, sizeof(pxl_clk_params));
/* Make sure requested clock isn't lower than minimum threshold*/
if (requested_clk_khz > 0)
requested_clk_khz = dm_max(requested_clk_khz,
base->min_display_clk_threshold_khz);
pxl_clk_params.target_pixel_clock = requested_clk_khz;
pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS;
bp->funcs->program_display_engine_pll(bp, &pxl_clk_params);
if (dc->dfs_bypass_enabled) {
/* Cache the fixed display clock*/
dc->dfs_bypass_disp_clk =
pxl_clk_params.dfs_bypass_display_clock;
}
/* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.*/
if (requested_clk_khz == 0)
base->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
psr_wait_loop(base->ctx, requested_clk_khz);
}
static const struct display_clock_funcs dce110_funcs = {
.get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq,
.get_required_clocks_state = dce_get_required_clocks_state,
.set_min_clocks_state = dce_clock_set_min_clocks_state,
.set_clock = dce_psr_set_clock
};
static const struct display_clock_funcs funcs = { static const struct display_clock_funcs dce_funcs = {
.destroy = destroy, .get_dp_ref_clk_frequency = dce_clocks_get_dp_ref_freq,
.get_dp_ref_clk_frequency = get_dp_ref_clk_frequency, .get_required_clocks_state = dce_get_required_clocks_state,
.get_required_clocks_state = get_required_clocks_state, .set_min_clocks_state = dce_clock_set_min_clocks_state,
.set_clock = dce110_set_clock, .set_clock = dce_set_clock
.set_min_clocks_state = dce110_set_min_clocks_state
}; };
static bool dal_display_clock_dce110_construct( static void dce_disp_clk_construct(
struct display_clock_dce110 *dc110, struct dce_disp_clk *clk_dce,
struct dc_context *ctx) struct dc_context *ctx,
const struct dce_disp_clk_registers *regs,
const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask)
{ {
struct display_clock *dc_base = &dc110->disp_clk_base; struct display_clock *base = &clk_dce->base;
struct dc_bios *bp = ctx->dc_bios;
dc_base->ctx = ctx; base->ctx = ctx;
dc_base->min_display_clk_threshold_khz = 0; base->funcs = &dce_funcs;
dc_base->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID; clk_dce->regs = regs;
clk_dce->clk_shift = clk_shift;
clk_dce->clk_mask = clk_mask;
dc_base->funcs = &funcs; clk_dce->dfs_bypass_disp_clk = 0;
clk_dce->gpu_pll_ss_percentage = 0;
clk_dce->gpu_pll_ss_divider = 1000;
clk_dce->ss_on_gpu_pll = false;
base->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
base->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID;
dc110->dfs_bypass_disp_clk = 0; dce_clock_read_integrated_info(clk_dce);
dce_clock_read_ss_info(clk_dce);
if (!display_clock_integrated_info_construct(dc110))
dm_logger_write(dc_base->ctx->logger, LOG_WARNING,
"Cannot obtain VBIOS integrated info\n");
dc110->gpu_pll_ss_percentage = 0;
dc110->gpu_pll_ss_divider = 1000;
dc110->ss_on_gpu_pll = false;
/* Initially set max clocks state to nominal. This should be updated by
* via a pplib call to DAL IRI eventually calling a
* DisplayEngineClock_Dce110::StoreMaxClocksState(). This call will come in
* on PPLIB init. This is from DCE5x. in case HW wants to use mixed method.*/
dc_base->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
dal_divider_range_construct( dal_divider_range_construct(
&divider_ranges[DIVIDER_RANGE_01], &clk_dce->divider_ranges[DIVIDER_RANGE_01],
DIVIDER_RANGE_01_START, DIVIDER_RANGE_01_START,
DIVIDER_RANGE_01_STEP_SIZE, DIVIDER_RANGE_01_STEP_SIZE,
DIVIDER_RANGE_01_BASE_DIVIDER_ID, DIVIDER_RANGE_01_BASE_DIVIDER_ID,
DIVIDER_RANGE_02_BASE_DIVIDER_ID); DIVIDER_RANGE_02_BASE_DIVIDER_ID);
dal_divider_range_construct( dal_divider_range_construct(
&divider_ranges[DIVIDER_RANGE_02], &clk_dce->divider_ranges[DIVIDER_RANGE_02],
DIVIDER_RANGE_02_START, DIVIDER_RANGE_02_START,
DIVIDER_RANGE_02_STEP_SIZE, DIVIDER_RANGE_02_STEP_SIZE,
DIVIDER_RANGE_02_BASE_DIVIDER_ID, DIVIDER_RANGE_02_BASE_DIVIDER_ID,
DIVIDER_RANGE_03_BASE_DIVIDER_ID); DIVIDER_RANGE_03_BASE_DIVIDER_ID);
dal_divider_range_construct( dal_divider_range_construct(
&divider_ranges[DIVIDER_RANGE_03], &clk_dce->divider_ranges[DIVIDER_RANGE_03],
DIVIDER_RANGE_03_START, DIVIDER_RANGE_03_START,
DIVIDER_RANGE_03_STEP_SIZE, DIVIDER_RANGE_03_STEP_SIZE,
DIVIDER_RANGE_03_BASE_DIVIDER_ID, DIVIDER_RANGE_03_BASE_DIVIDER_ID,
DIVIDER_RANGE_MAX_DIVIDER_ID); DIVIDER_RANGE_MAX_DIVIDER_ID);
}
{ struct display_clock *dce_disp_clk_create(
uint32_t ss_info_num = struct dc_context *ctx,
bp->funcs->get_ss_entry_number(bp, const struct dce_disp_clk_registers *regs,
AS_SIGNAL_TYPE_GPU_PLL); const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask)
if (ss_info_num) { {
struct spread_spectrum_info info; struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce));
enum bp_result result;
memset(&info, 0, sizeof(info));
result = bp->funcs->get_spread_spectrum_info(bp,
AS_SIGNAL_TYPE_GPU_PLL,
0,
&info);
/* Based on VBIOS, VBIOS will keep entry for GPU PLL SS
* even if SS not enabled and in that case
* SSInfo.spreadSpectrumPercentage !=0 would be sign
* that SS is enabled
*/
if (result == BP_RESULT_OK &&
info.spread_spectrum_percentage != 0) {
dc110->ss_on_gpu_pll = true;
dc110->gpu_pll_ss_divider =
info.spread_percentage_divider;
if (info.type.CENTER_MODE == 0) {
/* Currently for DP Reference clock we
* need only SS percentage for
* downspread */
dc110->gpu_pll_ss_percentage =
info.spread_spectrum_percentage;
}
}
} if (clk_dce == NULL) {
BREAK_TO_DEBUGGER();
return NULL;
} }
return true; dce_disp_clk_construct(
} clk_dce, ctx, regs, clk_shift, clk_mask);
/***************************************************************************** return &clk_dce->base;
* public functions }
*****************************************************************************/
struct display_clock *dal_display_clock_dce110_create( struct display_clock *dce110_disp_clk_create(
struct dc_context *ctx) struct dc_context *ctx,
const struct dce_disp_clk_registers *regs,
const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask)
{ {
struct display_clock_dce110 *dc110; struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce));
dc110 = dm_alloc(sizeof(struct display_clock_dce110)); if (clk_dce == NULL) {
BREAK_TO_DEBUGGER();
return NULL;
}
dce_disp_clk_construct(
clk_dce, ctx, regs, clk_shift, clk_mask);
clk_dce->base.funcs = &dce110_funcs;
return &clk_dce->base;
}
struct display_clock *dce112_disp_clk_create(
struct dc_context *ctx,
const struct dce_disp_clk_registers *regs,
const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask)
{
struct dce_disp_clk *clk_dce = dm_alloc(sizeof(*clk_dce));
if (dc110 == NULL) if (clk_dce == NULL) {
BREAK_TO_DEBUGGER();
return NULL; return NULL;
}
dce_disp_clk_construct(
clk_dce, ctx, regs, clk_shift, clk_mask);
if (dal_display_clock_dce110_construct(dc110, ctx)) clk_dce->base.funcs = &dce112_funcs;
return &dc110->disp_clk_base;
dm_free(dc110); return &clk_dce->base;
}
void dce_disp_clk_destroy(struct display_clock **disp_clk)
{
struct dce_disp_clk *clk_dce = TO_DCE_CLOCKS(*disp_clk);
return NULL; dm_free(clk_dce);
*disp_clk = NULL;
} }
/* /*
* Copyright 2012-15 Advanced Micro Devices, Inc. * Copyright 2012-16 Advanced Micro Devices, Inc.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
...@@ -22,29 +22,112 @@ ...@@ -22,29 +22,112 @@
* Authors: AMD * Authors: AMD
* *
*/ */
#ifndef __DAL_DISPLAY_CLOCK_DCE110_H__
#define __DAL_DISPLAY_CLOCK_DCE110_H__
#ifndef _DCE_CLOCKS_H_
#define _DCE_CLOCKS_H_
#include "display_clock_interface.h" #include "display_clock_interface.h"
#include "../gpu/divider_range.h"
#define TO_DCE_CLOCKS(clocks)\
container_of(clocks, struct dce_disp_clk, base)
#define CLK_COMMON_REG_LIST_DCE_BASE() \
.DPREFCLK_CNTL = mmDPREFCLK_CNTL, \
.DENTIST_DISPCLK_CNTL = mmDENTIST_DISPCLK_CNTL, \
.MASTER_COMM_DATA_REG1 = mmMASTER_COMM_DATA_REG1, \
.MASTER_COMM_CMD_REG = mmMASTER_COMM_CMD_REG, \
.MASTER_COMM_CNTL_REG = mmMASTER_COMM_CNTL_REG
#define CLK_SF(reg_name, field_name, post_fix)\
.field_name = reg_name ## __ ## field_name ## post_fix
#define CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \
CLK_SF(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, mask_sh), \
CLK_SF(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, mask_sh), \
CLK_SF(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, mask_sh), \
CLK_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh)
#define CLK_REG_FIELD_LIST(type) \
type DPREFCLK_SRC_SEL; \
type DENTIST_DPREFCLK_WDIVIDER; \
type MASTER_COMM_CMD_REG_BYTE0; \
type MASTER_COMM_INTERRUPT
struct dce_disp_clk_shift {
CLK_REG_FIELD_LIST(uint8_t);
};
struct dce_disp_clk_mask {
CLK_REG_FIELD_LIST(uint32_t);
};
struct dce_disp_clk_registers {
uint32_t DPREFCLK_CNTL;
uint32_t DENTIST_DISPCLK_CNTL;
uint32_t MASTER_COMM_DATA_REG1;
uint32_t MASTER_COMM_CMD_REG;
uint32_t MASTER_COMM_CNTL_REG;
};
/* Array identifiers and count for the divider ranges.*/
enum divider_range_count {
DIVIDER_RANGE_01 = 0,
DIVIDER_RANGE_02,
DIVIDER_RANGE_03,
DIVIDER_RANGE_MAX /* == 3*/
};
struct dce_disp_clk {
struct display_clock base;
const struct dce_disp_clk_registers *regs;
const struct dce_disp_clk_shift *clk_shift;
const struct dce_disp_clk_mask *clk_mask;
struct state_dependent_clocks max_clks_by_state[DM_PP_CLOCKS_MAX_STATES];
struct divider_range divider_ranges[DIVIDER_RANGE_MAX];
struct display_clock_dce110 {
struct display_clock disp_clk_base;
bool use_max_disp_clk; bool use_max_disp_clk;
uint32_t dentist_vco_freq_khz; uint32_t dentist_vco_freq_khz;
/* Cache the status of DFS-bypass feature*/ /* Cache the status of DFS-bypass feature*/
bool dfs_bypass_enabled; bool dfs_bypass_enabled;
/* Cache the display clock returned by VBIOS if DFS-bypass is enabled.
* This is basically "Crystal Frequency In KHz" (XTALIN) frequency */
uint32_t dfs_bypass_disp_clk;
/* Flag for Enabled SS on GPU PLL */
bool ss_on_gpu_pll;
/* GPU PLL SS percentage (if down-spread enabled) */ /* GPU PLL SS percentage (if down-spread enabled) */
uint32_t gpu_pll_ss_percentage; uint32_t gpu_pll_ss_percentage;
/* GPU PLL SS percentage Divider (100 or 1000) */ /* GPU PLL SS percentage Divider (100 or 1000) */
uint32_t gpu_pll_ss_divider; uint32_t gpu_pll_ss_divider;
/* Flag for Enabled SS on GPU PLL */
bool ss_on_gpu_pll;
/* Cache the display clock returned by VBIOS if DFS-bypass is enabled.
* This is basically "Crystal Frequency In KHz" (XTALIN) frequency */
uint32_t dfs_bypass_disp_clk;
}; };
#define DCLCK110_FROM_BASE(dc_base) \
container_of(dc_base, struct display_clock_dce110, disp_clk_base)
#endif /* __DAL_DISPLAY_CLOCK_DCE110_H__ */ struct display_clock *dce_disp_clk_create(
struct dc_context *ctx,
const struct dce_disp_clk_registers *regs,
const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask);
struct display_clock *dce110_disp_clk_create(
struct dc_context *ctx,
const struct dce_disp_clk_registers *regs,
const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask);
struct display_clock *dce112_disp_clk_create(
struct dc_context *ctx,
const struct dce_disp_clk_registers *regs,
const struct dce_disp_clk_shift *clk_shift,
const struct dce_disp_clk_mask *clk_mask);
void dce_disp_clk_destroy(struct display_clock **disp_clk);
#endif /* _DCE_CLOCKS_H_ */
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "dce110/dce110_ipp.h" #include "dce110/dce110_ipp.h"
#include "dce/dce_transform.h" #include "dce/dce_transform.h"
#include "dce110/dce110_opp.h" #include "dce110/dce110_opp.h"
#include "dce/dce_clocks.h"
#include "dce/dce_clock_source.h" #include "dce/dce_clock_source.h"
#include "dce/dce_audio.h" #include "dce/dce_audio.h"
#include "dce/dce_hwseq.h" #include "dce/dce_hwseq.h"
...@@ -200,6 +201,18 @@ static const struct dce110_ipp_reg_offsets dce100_ipp_reg_offsets[] = { ...@@ -200,6 +201,18 @@ static const struct dce110_ipp_reg_offsets dce100_ipp_reg_offsets[] = {
.reg_name = mm ## block ## id ## _ ## reg_name .reg_name = mm ## block ## id ## _ ## reg_name
static const struct dce_disp_clk_registers disp_clk_regs = {
CLK_COMMON_REG_LIST_DCE_BASE()
};
static const struct dce_disp_clk_shift disp_clk_shift = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT)
};
static const struct dce_disp_clk_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
#define transform_regs(id)\ #define transform_regs(id)\
[id] = {\ [id] = {\
XFM_COMMON_REG_LIST_DCE100(id)\ XFM_COMMON_REG_LIST_DCE100(id)\
...@@ -717,9 +730,7 @@ static void destruct(struct dce110_resource_pool *pool) ...@@ -717,9 +730,7 @@ static void destruct(struct dce110_resource_pool *pool)
} }
if (pool->base.display_clock != NULL) if (pool->base.display_clock != NULL)
pool->base.display_clock->funcs->destroy( dce_disp_clk_destroy(&pool->base.display_clock);
&pool->base.display_clock);
pool->base.display_clock = NULL;
if (pool->base.irqs != NULL) if (pool->base.irqs != NULL)
dal_irq_service_destroy(&pool->base.irqs); dal_irq_service_destroy(&pool->base.irqs);
...@@ -970,7 +981,10 @@ static bool construct( ...@@ -970,7 +981,10 @@ static bool construct(
} }
} }
pool->base.display_clock = dal_display_clock_dce110_create(ctx); pool->base.display_clock = dce_disp_clk_create(ctx,
&disp_clk_regs,
&disp_clk_shift,
&disp_clk_mask);
if (pool->base.display_clock == NULL) { if (pool->base.display_clock == NULL) {
dm_error("DC: failed to create display clock!\n"); dm_error("DC: failed to create display clock!\n");
BREAK_TO_DEBUGGER(); BREAK_TO_DEBUGGER();
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "dce110/dce110_transform_v.h" #include "dce110/dce110_transform_v.h"
#include "dce110/dce110_opp.h" #include "dce110/dce110_opp.h"
#include "dce110/dce110_opp_v.h" #include "dce110/dce110_opp_v.h"
#include "dce/dce_clocks.h"
#include "dce/dce_clock_source.h" #include "dce/dce_clock_source.h"
#include "dce/dce_hwseq.h" #include "dce/dce_hwseq.h"
#include "dce110/dce110_hw_sequencer.h" #include "dce110/dce110_hw_sequencer.h"
...@@ -187,6 +188,17 @@ static const struct dce110_ipp_reg_offsets dce110_ipp_reg_offsets[] = { ...@@ -187,6 +188,17 @@ static const struct dce110_ipp_reg_offsets dce110_ipp_reg_offsets[] = {
#define SRI(reg_name, block, id)\ #define SRI(reg_name, block, id)\
.reg_name = mm ## block ## id ## _ ## reg_name .reg_name = mm ## block ## id ## _ ## reg_name
static const struct dce_disp_clk_registers disp_clk_regs = {
CLK_COMMON_REG_LIST_DCE_BASE()
};
static const struct dce_disp_clk_shift disp_clk_shift = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT)
};
static const struct dce_disp_clk_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
#define transform_regs(id)\ #define transform_regs(id)\
[id] = {\ [id] = {\
...@@ -699,11 +711,8 @@ static void destruct(struct dce110_resource_pool *pool) ...@@ -699,11 +711,8 @@ static void destruct(struct dce110_resource_pool *pool)
} }
} }
if (pool->base.display_clock != NULL) { if (pool->base.display_clock != NULL)
pool->base.display_clock->funcs->destroy( dce_disp_clk_destroy(&pool->base.display_clock);
&pool->base.display_clock);
pool->base.display_clock = NULL;
}
if (pool->base.irqs != NULL) { if (pool->base.irqs != NULL) {
dal_irq_service_destroy(&pool->base.irqs); dal_irq_service_destroy(&pool->base.irqs);
...@@ -1261,7 +1270,10 @@ static bool construct( ...@@ -1261,7 +1270,10 @@ static bool construct(
} }
} }
pool->base.display_clock = dal_display_clock_dce110_create(ctx); pool->base.display_clock = dce110_disp_clk_create(ctx,
&disp_clk_regs,
&disp_clk_shift,
&disp_clk_mask);
if (pool->base.display_clock == NULL) { if (pool->base.display_clock == NULL) {
dm_error("DC: failed to create display clock!\n"); dm_error("DC: failed to create display clock!\n");
BREAK_TO_DEBUGGER(); BREAK_TO_DEBUGGER();
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "dce/dce_audio.h" #include "dce/dce_audio.h"
#include "dce112/dce112_opp.h" #include "dce112/dce112_opp.h"
#include "dce110/dce110_ipp.h" #include "dce110/dce110_ipp.h"
#include "dce/dce_clocks.h"
#include "dce/dce_clock_source.h" #include "dce/dce_clock_source.h"
#include "dce/dce_hwseq.h" #include "dce/dce_hwseq.h"
...@@ -204,6 +205,19 @@ static const struct dce110_ipp_reg_offsets ipp_reg_offsets[] = { ...@@ -204,6 +205,19 @@ static const struct dce110_ipp_reg_offsets ipp_reg_offsets[] = {
#define SRI(reg_name, block, id)\ #define SRI(reg_name, block, id)\
.reg_name = mm ## block ## id ## _ ## reg_name .reg_name = mm ## block ## id ## _ ## reg_name
static const struct dce_disp_clk_registers disp_clk_regs = {
CLK_COMMON_REG_LIST_DCE_BASE()
};
static const struct dce_disp_clk_shift disp_clk_shift = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT)
};
static const struct dce_disp_clk_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
#define transform_regs(id)\ #define transform_regs(id)\
[id] = {\ [id] = {\
XFM_COMMON_REG_LIST_DCE110(id)\ XFM_COMMON_REG_LIST_DCE110(id)\
...@@ -733,11 +747,8 @@ static void destruct(struct dce110_resource_pool *pool) ...@@ -733,11 +747,8 @@ static void destruct(struct dce110_resource_pool *pool)
} }
} }
if (pool->base.display_clock != NULL) { if (pool->base.display_clock != NULL)
pool->base.display_clock->funcs->destroy( dce_disp_clk_destroy(&pool->base.display_clock);
&pool->base.display_clock);
pool->base.display_clock = NULL;
}
if (pool->base.irqs != NULL) { if (pool->base.irqs != NULL) {
dal_irq_service_destroy(&pool->base.irqs); dal_irq_service_destroy(&pool->base.irqs);
...@@ -1299,9 +1310,10 @@ static bool construct( ...@@ -1299,9 +1310,10 @@ static bool construct(
} }
} }
pool->base.display_clock = dal_display_clock_dce112_create( pool->base.display_clock = dce112_disp_clk_create(ctx,
ctx); &disp_clk_regs,
&disp_clk_shift,
&disp_clk_mask);
if (pool->base.display_clock == NULL) { if (pool->base.display_clock == NULL) {
dm_error("DC: failed to create display clock!\n"); dm_error("DC: failed to create display clock!\n");
BREAK_TO_DEBUGGER(); BREAK_TO_DEBUGGER();
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "dce/dce_transform.h" #include "dce/dce_transform.h"
#include "dce80/dce80_opp.h" #include "dce80/dce80_opp.h"
#include "dce110/dce110_ipp.h" #include "dce110/dce110_ipp.h"
#include "dce/dce_clocks.h"
#include "dce/dce_clock_source.h" #include "dce/dce_clock_source.h"
#include "dce/dce_audio.h" #include "dce/dce_audio.h"
#include "dce/dce_hwseq.h" #include "dce/dce_hwseq.h"
...@@ -215,6 +216,19 @@ static const struct dce110_ipp_reg_offsets ipp_reg_offsets[] = { ...@@ -215,6 +216,19 @@ static const struct dce110_ipp_reg_offsets ipp_reg_offsets[] = {
#define SRI(reg_name, block, id)\ #define SRI(reg_name, block, id)\
.reg_name = mm ## block ## id ## _ ## reg_name .reg_name = mm ## block ## id ## _ ## reg_name
static const struct dce_disp_clk_registers disp_clk_regs = {
CLK_COMMON_REG_LIST_DCE_BASE()
};
static const struct dce_disp_clk_shift disp_clk_shift = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(__SHIFT)
};
static const struct dce_disp_clk_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
#define transform_regs(id)\ #define transform_regs(id)\
[id] = {\ [id] = {\
XFM_COMMON_REG_LIST_DCE_BASE(id)\ XFM_COMMON_REG_LIST_DCE_BASE(id)\
...@@ -656,11 +670,8 @@ static void destruct(struct dce110_resource_pool *pool) ...@@ -656,11 +670,8 @@ static void destruct(struct dce110_resource_pool *pool)
} }
} }
if (pool->base.display_clock != NULL) { if (pool->base.display_clock != NULL)
pool->base.display_clock->funcs->destroy( dce_disp_clk_destroy(&pool->base.display_clock);
&pool->base.display_clock);
pool->base.display_clock = NULL;
}
if (pool->base.irqs != NULL) { if (pool->base.irqs != NULL) {
dal_irq_service_destroy(&pool->base.irqs); dal_irq_service_destroy(&pool->base.irqs);
...@@ -857,47 +868,6 @@ static const struct resource_funcs dce80_res_pool_funcs = { ...@@ -857,47 +868,6 @@ static const struct resource_funcs dce80_res_pool_funcs = {
.validate_bandwidth = dce80_validate_bandwidth .validate_bandwidth = dce80_validate_bandwidth
}; };
static enum dm_pp_clocks_state dce80_resource_convert_clock_state_pp_to_dc(
enum dm_pp_clocks_state pp_clock_state)
{
enum dm_pp_clocks_state dc_clocks_state = DM_PP_CLOCKS_STATE_INVALID;
switch (pp_clock_state) {
case DM_PP_CLOCKS_STATE_INVALID:
dc_clocks_state = DM_PP_CLOCKS_STATE_INVALID;
break;
case DM_PP_CLOCKS_STATE_ULTRA_LOW:
dc_clocks_state = DM_PP_CLOCKS_STATE_ULTRA_LOW;
break;
case DM_PP_CLOCKS_STATE_LOW:
dc_clocks_state = DM_PP_CLOCKS_STATE_LOW;
break;
case DM_PP_CLOCKS_STATE_NOMINAL:
dc_clocks_state = DM_PP_CLOCKS_STATE_NOMINAL;
break;
case DM_PP_CLOCKS_STATE_PERFORMANCE:
dc_clocks_state = DM_PP_CLOCKS_STATE_PERFORMANCE;
break;
case DM_PP_CLOCKS_DPM_STATE_LEVEL_4:
dc_clocks_state = DM_PP_CLOCKS_DPM_STATE_LEVEL_4;
break;
case DM_PP_CLOCKS_DPM_STATE_LEVEL_5:
dc_clocks_state = DM_PP_CLOCKS_DPM_STATE_LEVEL_5;
break;
case DM_PP_CLOCKS_DPM_STATE_LEVEL_6:
dc_clocks_state = DM_PP_CLOCKS_DPM_STATE_LEVEL_6;
break;
case DM_PP_CLOCKS_DPM_STATE_LEVEL_7:
dc_clocks_state = DM_PP_CLOCKS_DPM_STATE_LEVEL_7;
break;
default:
dc_clocks_state = DM_PP_CLOCKS_STATE_INVALID;
break;
}
return dc_clocks_state;
}
static bool construct( static bool construct(
uint8_t num_virtual_links, uint8_t num_virtual_links,
struct core_dc *dc, struct core_dc *dc,
...@@ -967,7 +937,10 @@ static bool construct( ...@@ -967,7 +937,10 @@ static bool construct(
} }
} }
pool->base.display_clock = dal_display_clock_dce80_create(ctx); pool->base.display_clock = dce_disp_clk_create(ctx,
&disp_clk_regs,
&disp_clk_shift,
&disp_clk_mask);
if (pool->base.display_clock == NULL) { if (pool->base.display_clock == NULL) {
dm_error("DC: failed to create display clock!\n"); dm_error("DC: failed to create display clock!\n");
BREAK_TO_DEBUGGER(); BREAK_TO_DEBUGGER();
...@@ -977,8 +950,7 @@ static bool construct( ...@@ -977,8 +950,7 @@ static bool construct(
if (dm_pp_get_static_clocks(ctx, &static_clk_info)) if (dm_pp_get_static_clocks(ctx, &static_clk_info))
pool->base.display_clock->max_clks_state = pool->base.display_clock->max_clks_state =
dce80_resource_convert_clock_state_pp_to_dc( static_clk_info.max_clocks_state;
static_clk_info.max_clocks_state);
{ {
struct irq_service_init_data init_data; struct irq_service_init_data init_data;
......
...@@ -43,15 +43,17 @@ enum dm_pp_clocks_state { ...@@ -43,15 +43,17 @@ enum dm_pp_clocks_state {
/* Starting from DCE11, Max 8 levels of DPM state supported. */ /* Starting from DCE11, Max 8 levels of DPM state supported. */
DM_PP_CLOCKS_DPM_STATE_LEVEL_INVALID = DM_PP_CLOCKS_STATE_INVALID, DM_PP_CLOCKS_DPM_STATE_LEVEL_INVALID = DM_PP_CLOCKS_STATE_INVALID,
DM_PP_CLOCKS_DPM_STATE_LEVEL_0 = DM_PP_CLOCKS_STATE_ULTRA_LOW, DM_PP_CLOCKS_DPM_STATE_LEVEL_0,
DM_PP_CLOCKS_DPM_STATE_LEVEL_1 = DM_PP_CLOCKS_STATE_LOW, DM_PP_CLOCKS_DPM_STATE_LEVEL_1,
DM_PP_CLOCKS_DPM_STATE_LEVEL_2 = DM_PP_CLOCKS_STATE_NOMINAL, DM_PP_CLOCKS_DPM_STATE_LEVEL_2,
/* to be backward compatible */ /* to be backward compatible */
DM_PP_CLOCKS_DPM_STATE_LEVEL_3 = DM_PP_CLOCKS_STATE_PERFORMANCE, DM_PP_CLOCKS_DPM_STATE_LEVEL_3,
DM_PP_CLOCKS_DPM_STATE_LEVEL_4 = DM_PP_CLOCKS_DPM_STATE_LEVEL_3 + 1, DM_PP_CLOCKS_DPM_STATE_LEVEL_4,
DM_PP_CLOCKS_DPM_STATE_LEVEL_5 = DM_PP_CLOCKS_DPM_STATE_LEVEL_4 + 1, DM_PP_CLOCKS_DPM_STATE_LEVEL_5,
DM_PP_CLOCKS_DPM_STATE_LEVEL_6 = DM_PP_CLOCKS_DPM_STATE_LEVEL_5 + 1, DM_PP_CLOCKS_DPM_STATE_LEVEL_6,
DM_PP_CLOCKS_DPM_STATE_LEVEL_7 = DM_PP_CLOCKS_DPM_STATE_LEVEL_6 + 1, DM_PP_CLOCKS_DPM_STATE_LEVEL_7,
DM_PP_CLOCKS_MAX_STATES
}; };
struct dm_pp_gpu_clock_range { struct dm_pp_gpu_clock_range {
......
...@@ -9,24 +9,9 @@ AMD_DAL_GPU = $(addprefix $(AMDDALPATH)/dc/gpu/,$(GPU)) ...@@ -9,24 +9,9 @@ AMD_DAL_GPU = $(addprefix $(AMDDALPATH)/dc/gpu/,$(GPU))
AMD_DISPLAY_FILES += $(AMD_DAL_GPU) AMD_DISPLAY_FILES += $(AMD_DAL_GPU)
###############################################################################
# DCE 80 family
###############################################################################
GPU_DCE80 = display_clock_dce80.o
AMD_DAL_GPU_DCE80 = $(addprefix $(AMDDALPATH)/dc/gpu/dce80/,$(GPU_DCE80))
AMD_DISPLAY_FILES += $(AMD_DAL_GPU_DCE80)
############################################################################### ###############################################################################
# DCE 110 family # DCE 110 family
############################################################################### ###############################################################################
GPU_DCE110 = display_clock_dce110.o
AMD_DAL_GPU_DCE110 = $(addprefix $(AMDDALPATH)/dc/gpu/dce110/,$(GPU_DCE110))
AMD_DISPLAY_FILES += $(AMD_DAL_GPU_DCE110)
GPU_DCE112 = display_clock_dce112.o GPU_DCE112 = display_clock_dce112.o
......
...@@ -71,102 +71,6 @@ enum divider_range_step_size { ...@@ -71,102 +71,6 @@ enum divider_range_step_size {
static struct divider_range divider_ranges[DIVIDER_RANGE_MAX]; static struct divider_range divider_ranges[DIVIDER_RANGE_MAX];
#define dce112_DFS_BYPASS_THRESHOLD_KHZ 400000
static bool dce112_set_min_clocks_state(
struct display_clock *dc,
enum dm_pp_clocks_state clocks_state)
{
struct dm_pp_power_level_change_request level_change_req = {
clocks_state };
if (clocks_state > dc->max_clks_state) {
/*Requested state exceeds max supported state.*/
dm_logger_write(dc->ctx->logger, LOG_WARNING,
"Requested state exceeds max supported state");
return false;
} else if (clocks_state == dc->cur_min_clks_state) {
/*if we're trying to set the same state, we can just return
* since nothing needs to be done*/
return true;
}
/* get max clock state from PPLIB */
if (dm_pp_apply_power_level_change_request(dc->ctx, &level_change_req))
dc->cur_min_clks_state = clocks_state;
return true;
}
static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc)
{
uint32_t dispclk_cntl_value;
uint32_t dp_ref_clk_cntl_value;
uint32_t dp_ref_clk_cntl_src_sel_value;
uint32_t dp_ref_clk_khz = 600000;
uint32_t target_div = INVALID_DIVIDER;
struct display_clock_dce112 *disp_clk = FROM_DISPLAY_CLOCK(dc);
/* ASSERT DP Reference Clock source is from DFS*/
dp_ref_clk_cntl_value = dm_read_reg(dc->ctx,
mmDPREFCLK_CNTL);
dp_ref_clk_cntl_src_sel_value =
get_reg_field_value(
dp_ref_clk_cntl_value,
DPREFCLK_CNTL, DPREFCLK_SRC_SEL);
ASSERT(dp_ref_clk_cntl_src_sel_value == 0);
/* Read the mmDENTIST_DISPCLK_CNTL to get the currently
* programmed DID DENTIST_DPREFCLK_WDIVIDER*/
dispclk_cntl_value = dm_read_reg(dc->ctx,
mmDENTIST_DISPCLK_CNTL);
/* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/
target_div = dal_divider_range_get_divider(
divider_ranges,
DIVIDER_RANGE_MAX,
get_reg_field_value(dispclk_cntl_value,
DENTIST_DISPCLK_CNTL,
DENTIST_DPREFCLK_WDIVIDER));
if (target_div != INVALID_DIVIDER) {
/* Calculate the current DFS clock, in kHz.*/
dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR
* disp_clk->dentist_vco_freq_khz) / target_div;
}
/* SW will adjust DP REF Clock average value for all purposes
* (DP DTO / DP Audio DTO and DP GTC)
if clock is spread for all cases:
-if SS enabled on DP Ref clock and HW de-spreading enabled with SW
calculations for DS_INCR/DS_MODULO (this is planned to be default case)
-if SS enabled on DP Ref clock and HW de-spreading enabled with HW
calculations (not planned to be used, but average clock should still
be valid)
-if SS enabled on DP Ref clock and HW de-spreading disabled
(should not be case with CIK) then SW should program all rates
generated according to average value (case as with previous ASICs)
*/
if ((disp_clk->ss_on_gpu_pll) && (disp_clk->gpu_pll_ss_divider != 0)) {
struct fixed32_32 ss_percentage = dal_fixed32_32_div_int(
dal_fixed32_32_from_fraction(
disp_clk->gpu_pll_ss_percentage,
disp_clk->gpu_pll_ss_divider), 200);
struct fixed32_32 adj_dp_ref_clk_khz;
ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one,
ss_percentage);
adj_dp_ref_clk_khz =
dal_fixed32_32_mul_int(
ss_percentage,
dp_ref_clk_khz);
dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz);
}
return dp_ref_clk_khz;
}
void dispclk_dce112_destroy(struct display_clock **base) void dispclk_dce112_destroy(struct display_clock **base)
{ {
...@@ -233,38 +137,6 @@ static bool display_clock_integrated_info_construct( ...@@ -233,38 +137,6 @@ static bool display_clock_integrated_info_construct(
return true; return true;
} }
enum dm_pp_clocks_state dispclk_dce112_get_required_clocks_state(
struct display_clock *dc,
struct state_dependent_clocks *req_clocks)
{
int32_t i;
struct display_clock_dce112 *disp_clk = DCLCK112_FROM_BASE(dc);
enum dm_pp_clocks_state low_req_clk = dc->max_clks_state;
if (!req_clocks) {
/* NULL pointer*/
dm_logger_write(dc->ctx->logger, LOG_WARNING,
"%s: Invalid parameter",
__func__);
return DM_PP_CLOCKS_STATE_INVALID;
}
/* Iterate from highest supported to lowest valid state, and update
* lowest RequiredState with the lowest state that satisfies
* all required clocks
*/
for (i = dc->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; --i) {
if ((req_clocks->display_clk_khz <=
(disp_clk->max_clks_by_state + i)->
display_clk_khz) &&
(req_clocks->pixel_clk_khz <=
(disp_clk->max_clks_by_state + i)->
pixel_clk_khz))
low_req_clk = i;
}
return low_req_clk;
}
void dce112_set_clock( void dce112_set_clock(
struct display_clock *base, struct display_clock *base,
uint32_t requested_clk_khz) uint32_t requested_clk_khz)
...@@ -304,10 +176,7 @@ void dce112_set_clock( ...@@ -304,10 +176,7 @@ void dce112_set_clock(
static const struct display_clock_funcs funcs = { static const struct display_clock_funcs funcs = {
.destroy = dispclk_dce112_destroy, .destroy = dispclk_dce112_destroy,
.get_dp_ref_clk_frequency = get_dp_ref_clk_frequency,
.get_required_clocks_state = dispclk_dce112_get_required_clocks_state,
.set_clock = dce112_set_clock, .set_clock = dce112_set_clock,
.set_min_clocks_state = dce112_set_min_clocks_state
}; };
bool dal_display_clock_dce112_construct( bool dal_display_clock_dce112_construct(
...@@ -406,24 +275,3 @@ bool dal_display_clock_dce112_construct( ...@@ -406,24 +275,3 @@ bool dal_display_clock_dce112_construct(
return true; return true;
} }
/*****************************************************************************
* public functions
*****************************************************************************/
struct display_clock *dal_display_clock_dce112_create(
struct dc_context *ctx)
{
struct display_clock_dce112 *dc112;
dc112 = dm_alloc(sizeof(struct display_clock_dce112));
if (dc112 == NULL)
return NULL;
if (dal_display_clock_dce112_construct(dc112, ctx))
return &dc112->disp_clk_base;
dm_free(dc112);
return NULL;
}
...@@ -71,23 +71,9 @@ bool dal_display_clock_dce112_construct( ...@@ -71,23 +71,9 @@ bool dal_display_clock_dce112_construct(
void dispclk_dce112_destroy(struct display_clock **base); void dispclk_dce112_destroy(struct display_clock **base);
enum dm_pp_clocks_state dispclk_dce112_get_min_clocks_state(
struct display_clock *base);
enum dm_pp_clocks_state dispclk_dce112_get_required_clocks_state(
struct display_clock *dc,
struct state_dependent_clocks *req_clocks);
void dce112_set_clock( void dce112_set_clock(
struct display_clock *base, struct display_clock *base,
uint32_t requested_clk_khz); uint32_t requested_clk_khz);
bool dispclk_dce112_set_min_clocks_state(
struct display_clock *base,
enum dm_pp_clocks_state clocks_state);
void dispclk_dce112_store_max_clocks_state(
struct display_clock *base,
enum dm_pp_clocks_state max_clocks_state);
#endif /* __DAL_DISPLAY_CLOCK_DCE112_H__ */ #endif /* __DAL_DISPLAY_CLOCK_DCE112_H__ */
/*
* 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_8_0_d.h"
#include "dce/dce_8_0_sh_mask.h"
#include "include/bios_parser_interface.h"
#include "include/fixed32_32.h"
#include "include/logger_interface.h"
#include "../divider_range.h"
#include "display_clock_dce80.h"
#include "dc.h"
#define DCE80_DFS_BYPASS_THRESHOLD_KHZ 100000
/* Max clock values for each state indexed by "enum clocks_state": */
static struct state_dependent_clocks max_clks_by_state[] = {
/* ClocksStateInvalid - should not be used */
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/* ClocksStateUltraLow - not expected to be used for DCE 8.0 */
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/* ClocksStateLow */
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000},
/* ClocksStateNominal */
{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 },
/* ClocksStatePerformance */
{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } };
/* Starting point for each divider range.*/
enum divider_range_start {
DIVIDER_RANGE_01_START = 200, /* 2.00*/
DIVIDER_RANGE_02_START = 1600, /* 16.00*/
DIVIDER_RANGE_03_START = 3200, /* 32.00*/
DIVIDER_RANGE_SCALE_FACTOR = 100 /* Results are scaled up by 100.*/
};
/* Ranges for divider identifiers (Divider ID or DID)
mmDENTIST_DISPCLK_CNTL.DENTIST_DISPCLK_WDIVIDER*/
enum divider_id_register_setting {
DIVIDER_RANGE_01_BASE_DIVIDER_ID = 0X08,
DIVIDER_RANGE_02_BASE_DIVIDER_ID = 0X40,
DIVIDER_RANGE_03_BASE_DIVIDER_ID = 0X60,
DIVIDER_RANGE_MAX_DIVIDER_ID = 0X80
};
/* Step size between each divider within a range.
Incrementing the DENTIST_DISPCLK_WDIVIDER by one
will increment the divider by this much.*/
enum divider_range_step_size {
DIVIDER_RANGE_01_STEP_SIZE = 25, /* 0.25*/
DIVIDER_RANGE_02_STEP_SIZE = 50, /* 0.50*/
DIVIDER_RANGE_03_STEP_SIZE = 100 /* 1.00 */
};
/* Array identifiers and count for the divider ranges.*/
enum divider_range_count {
DIVIDER_RANGE_01 = 0,
DIVIDER_RANGE_02,
DIVIDER_RANGE_03,
DIVIDER_RANGE_MAX /* == 3*/
};
static struct divider_range divider_ranges[DIVIDER_RANGE_MAX];
#define FROM_DISPLAY_CLOCK(base) \
container_of(base, struct display_clock_dce80, disp_clk)
static void dce80_set_clock(
struct display_clock *dc,
uint32_t requested_clk_khz)
{
struct bp_pixel_clock_parameters pxl_clk_params;
struct dc_bios *bp = dc->ctx->dc_bios;
/* Prepare to program display clock*/
memset(&pxl_clk_params, 0, sizeof(pxl_clk_params));
pxl_clk_params.target_pixel_clock = requested_clk_khz;
pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS;
bp->funcs->program_display_engine_pll(bp, &pxl_clk_params);
/* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.*/
if (requested_clk_khz == 0)
dc->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
}
static enum dm_pp_clocks_state get_required_clocks_state
(struct display_clock *dc,
struct state_dependent_clocks *req_clocks)
{
int32_t i;
enum dm_pp_clocks_state low_req_clk = dc->max_clks_state;
if (!req_clocks) {
/* NULL pointer*/
BREAK_TO_DEBUGGER();
return DM_PP_CLOCKS_STATE_INVALID;
}
/* Iterate from highest supported to lowest valid state, and update
* lowest RequiredState with the lowest state that satisfies
* all required clocks
*/
for (i = dc->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; --i) {
if ((req_clocks->display_clk_khz <=
max_clks_by_state[i].display_clk_khz) &&
(req_clocks->pixel_clk_khz <=
max_clks_by_state[i].pixel_clk_khz))
low_req_clk = i;
}
return low_req_clk;
}
static bool dce80_set_min_clocks_state(
struct display_clock *dc,
enum dm_pp_clocks_state clocks_state)
{
struct dm_pp_power_level_change_request level_change_req = {
clocks_state };
if (clocks_state > dc->max_clks_state) {
/*Requested state exceeds max supported state.*/
dm_logger_write(dc->ctx->logger, LOG_WARNING,
"Requested state exceeds max supported state");
return false;
} else if (clocks_state == dc->cur_min_clks_state) {
/*if we're trying to set the same state, we can just return
* since nothing needs to be done*/
return true;
}
/* get max clock state from PPLIB */
if (dm_pp_apply_power_level_change_request(dc->ctx, &level_change_req))
dc->cur_min_clks_state = clocks_state;
return true;
}
static uint32_t get_dp_ref_clk_frequency(struct display_clock *dc)
{
uint32_t dispclk_cntl_value;
uint32_t dp_ref_clk_cntl_value;
uint32_t dp_ref_clk_cntl_src_sel_value;
uint32_t dp_ref_clk_khz = 600000;
uint32_t target_div = INVALID_DIVIDER;
struct display_clock_dce80 *disp_clk = FROM_DISPLAY_CLOCK(dc);
/* ASSERT DP Reference Clock source is from DFS*/
dp_ref_clk_cntl_value = dm_read_reg(dc->ctx,
mmDPREFCLK_CNTL);
dp_ref_clk_cntl_src_sel_value =
get_reg_field_value(
dp_ref_clk_cntl_value,
DPREFCLK_CNTL, DPREFCLK_SRC_SEL);
ASSERT(dp_ref_clk_cntl_src_sel_value == 0);
/* Read the mmDENTIST_DISPCLK_CNTL to get the currently
* programmed DID DENTIST_DPREFCLK_WDIVIDER*/
dispclk_cntl_value = dm_read_reg(dc->ctx,
mmDENTIST_DISPCLK_CNTL);
/* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/
target_div = dal_divider_range_get_divider(
divider_ranges,
DIVIDER_RANGE_MAX,
get_reg_field_value(dispclk_cntl_value,
DENTIST_DISPCLK_CNTL,
DENTIST_DPREFCLK_WDIVIDER));
if (target_div != INVALID_DIVIDER) {
/* Calculate the current DFS clock, in kHz.*/
dp_ref_clk_khz = (DIVIDER_RANGE_SCALE_FACTOR
* disp_clk->dentist_vco_freq_khz) / target_div;
}
/* SW will adjust DP REF Clock average value for all purposes
* (DP DTO / DP Audio DTO and DP GTC)
if clock is spread for all cases:
-if SS enabled on DP Ref clock and HW de-spreading enabled with SW
calculations for DS_INCR/DS_MODULO (this is planned to be default case)
-if SS enabled on DP Ref clock and HW de-spreading enabled with HW
calculations (not planned to be used, but average clock should still
be valid)
-if SS enabled on DP Ref clock and HW de-spreading disabled
(should not be case with CIK) then SW should program all rates
generated according to average value (case as with previous ASICs)
*/
if ((disp_clk->ss_on_gpu_pll) && (disp_clk->gpu_pll_ss_divider != 0)) {
struct fixed32_32 ss_percentage = dal_fixed32_32_div_int(
dal_fixed32_32_from_fraction(
disp_clk->gpu_pll_ss_percentage,
disp_clk->gpu_pll_ss_divider), 200);
struct fixed32_32 adj_dp_ref_clk_khz;
ss_percentage = dal_fixed32_32_sub(dal_fixed32_32_one,
ss_percentage);
adj_dp_ref_clk_khz =
dal_fixed32_32_mul_int(
ss_percentage,
dp_ref_clk_khz);
dp_ref_clk_khz = dal_fixed32_32_floor(adj_dp_ref_clk_khz);
}
return dp_ref_clk_khz;
}
static void display_clock_ss_construct(
struct display_clock_dce80 *disp_clk)
{
struct dc_bios *bp = disp_clk->disp_clk.ctx->dc_bios;
uint32_t ss_entry_num = bp->funcs->get_ss_entry_number(bp,
AS_SIGNAL_TYPE_GPU_PLL);
/*Read SS Info from VBIOS SS Info table for DP Reference Clock spread.*/
if (ss_entry_num > 0) {/* Should be only one entry */
struct spread_spectrum_info ss_info;
enum bp_result res;
memset(&ss_info, 0, sizeof(struct spread_spectrum_info));
res = bp->funcs->get_spread_spectrum_info(bp,
AS_SIGNAL_TYPE_GPU_PLL, 0, &ss_info);
/* Based on VBIOS, VBIOS will keep entry for GPU PLL SS even if
* SS not enabled and in that case
* SSInfo.spreadSpectrumPercentage !=0 would be
* sign that SS is enabled*/
if (res == BP_RESULT_OK && ss_info.spread_spectrum_percentage != 0) {
disp_clk->ss_on_gpu_pll = true;
disp_clk->gpu_pll_ss_divider =
ss_info.spread_percentage_divider;
if (ss_info.type.CENTER_MODE == 0)
/* Currently we need only SS
* percentage for down-spread*/
disp_clk->gpu_pll_ss_percentage =
ss_info.spread_spectrum_percentage;
}
}
}
static bool display_clock_integrated_info_construct(
struct display_clock_dce80 *disp_clk)
{
struct dc_debug *debug = &disp_clk->disp_clk.ctx->dc->debug;
struct dc_bios *bp = disp_clk->disp_clk.ctx->dc_bios;
struct integrated_info info = { { { 0 } } };
struct firmware_info fw_info = { { 0 } };
uint32_t i;
if (bp->integrated_info)
info = *bp->integrated_info;
disp_clk->dentist_vco_freq_khz = info.dentist_vco_freq;
if (disp_clk->dentist_vco_freq_khz == 0) {
bp->funcs->get_firmware_info(bp, &fw_info);
disp_clk->dentist_vco_freq_khz =
fw_info.smu_gpu_pll_output_freq;
if (disp_clk->dentist_vco_freq_khz == 0)
disp_clk->dentist_vco_freq_khz = 3600000;
}
disp_clk->disp_clk.min_display_clk_threshold_khz =
disp_clk->dentist_vco_freq_khz / 64;
/* TODO: initialise disp_clk->dfs_bypass_disp_clk */
/*update the maximum display clock for each power state*/
for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID;
switch (i) {
case 0:
clk_state = DM_PP_CLOCKS_STATE_ULTRA_LOW;
break;
case 1:
clk_state = DM_PP_CLOCKS_STATE_LOW;
break;
case 2:
clk_state = DM_PP_CLOCKS_STATE_NOMINAL;
break;
case 3:
clk_state = DM_PP_CLOCKS_STATE_PERFORMANCE;
break;
default:
clk_state = DM_PP_CLOCKS_STATE_INVALID;
break;
}
/*Do not allow bad VBIOS/SBIOS to override with invalid values,
* check for > 100MHz*/
if (info.disp_clk_voltage[i].max_supported_clk >= 100000) {
max_clks_by_state[clk_state].display_clk_khz =
info.disp_clk_voltage[i].max_supported_clk;
}
}
disp_clk->dfs_bypass_enabled = false;
if (!debug->disable_dfs_bypass)
if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE)
disp_clk->dfs_bypass_enabled = true;
disp_clk->use_max_disp_clk = debug->max_disp_clk;
return true;
}
static void destroy(struct display_clock **dc)
{
struct display_clock_dce80 *disp_clk;
disp_clk = FROM_DISPLAY_CLOCK(*dc);
dm_free(disp_clk);
*dc = NULL;
}
static const struct display_clock_funcs funcs = {
.destroy = destroy,
.get_dp_ref_clk_frequency = get_dp_ref_clk_frequency,
.get_required_clocks_state = get_required_clocks_state,
.set_clock = dce80_set_clock,
.set_min_clocks_state = dce80_set_min_clocks_state
};
struct display_clock *dal_display_clock_dce80_create(
struct dc_context *ctx)
{
struct display_clock_dce80 *disp_clk;
struct display_clock *dc_base;
disp_clk = dm_alloc(sizeof(struct display_clock_dce80));
if (disp_clk == NULL)
return NULL;
dc_base = &disp_clk->disp_clk;
dc_base->ctx = ctx;
dc_base->min_display_clk_threshold_khz = 0;
dc_base->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID;
dc_base->funcs = &funcs;
/*
* set_dp_ref_clock_source
* set_clock_state
* get_clock_state
* get_dfs_bypass_threshold
*/
disp_clk->gpu_pll_ss_percentage = 0;
disp_clk->gpu_pll_ss_divider = 1000;
disp_clk->ss_on_gpu_pll = false;
disp_clk->dfs_bypass_enabled = false;
disp_clk->dfs_bypass_disp_clk = 0;
disp_clk->use_max_disp_clk = true;/* false will hang the system! */
/* Initially set max clocks state to nominal. This should be updated by
* via a pplib call to DAL IRI eventually calling a
* DisplayEngineClock_Dce50::StoreMaxClocksState(). This call will come in
* on PPLIB init. This is from DCE5x. in case HW wants to use mixed method.*/
dc_base->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
/* Initially set current min clocks state to invalid since we
* cannot make any assumption about PPLIB's initial state. This will be updated
* by HWSS via SetMinClocksState() on first mode set prior to programming
* state dependent clocks.*/
disp_clk->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID;
display_clock_ss_construct(disp_clk);
if (!display_clock_integrated_info_construct(disp_clk)) {
dm_logger_write(dc_base->ctx->logger, LOG_WARNING,
"Cannot obtain VBIOS integrated info");
}
dal_divider_range_construct(
&divider_ranges[DIVIDER_RANGE_01],
DIVIDER_RANGE_01_START,
DIVIDER_RANGE_01_STEP_SIZE,
DIVIDER_RANGE_01_BASE_DIVIDER_ID,
DIVIDER_RANGE_02_BASE_DIVIDER_ID);
dal_divider_range_construct(
&divider_ranges[DIVIDER_RANGE_02],
DIVIDER_RANGE_02_START,
DIVIDER_RANGE_02_STEP_SIZE,
DIVIDER_RANGE_02_BASE_DIVIDER_ID,
DIVIDER_RANGE_03_BASE_DIVIDER_ID);
dal_divider_range_construct(
&divider_ranges[DIVIDER_RANGE_03],
DIVIDER_RANGE_03_START,
DIVIDER_RANGE_03_STEP_SIZE,
DIVIDER_RANGE_03_BASE_DIVIDER_ID,
DIVIDER_RANGE_MAX_DIVIDER_ID);
dm_free(disp_clk);
return NULL;
}
/*
* Copyright 2012-15 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __DAL_DISPLAY_CLOCK_DCE80_H__
#define __DAL_DISPLAY_CLOCK_DCE80_H__
#include "display_clock_interface.h"
struct display_clock_dce80 {
struct display_clock disp_clk;
/* DFS input - GPUPLL VCO frequency - from VBIOS Firmware info. */
uint32_t dentist_vco_freq_khz;
/* GPU PLL SS percentage (if down-spread enabled)*/
uint32_t gpu_pll_ss_percentage;
/* GPU PLL SS percentage Divider (100 or 1000)*/
uint32_t gpu_pll_ss_divider;
/* Flag for Enabled SS on GPU PLL*/
bool ss_on_gpu_pll;
/* Current minimum display block clocks state*/
enum dm_pp_clocks_state cur_min_clks_state;
/* DFS-bypass feature variable
Cache the status of DFS-bypass feature*/
bool dfs_bypass_enabled;
/* Cache the display clock returned by VBIOS if DFS-bypass is enabled.
* This is basically "Crystal Frequency In KHz" (XTALIN) frequency */
uint32_t dfs_bypass_disp_clk;
bool use_max_disp_clk;
};
struct display_clock *dal_display_clock_dce80_create(
struct dc_context *ctx);
#endif /* __DAL_DISPLAY_CLOCK_DCE80_H__ */
...@@ -41,10 +41,9 @@ struct state_dependent_clocks { ...@@ -41,10 +41,9 @@ struct state_dependent_clocks {
struct display_clock { struct display_clock {
struct dc_context *ctx; struct dc_context *ctx;
const struct display_clock_funcs *funcs; const struct display_clock_funcs *funcs;
uint32_t min_display_clk_threshold_khz;
/* Max display block clocks state*/
enum dm_pp_clocks_state max_clks_state;
int min_display_clk_threshold_khz;
enum dm_pp_clocks_state max_clks_state;
enum dm_pp_clocks_state cur_min_clks_state; enum dm_pp_clocks_state cur_min_clks_state;
}; };
...@@ -61,15 +60,7 @@ struct display_clock_funcs { ...@@ -61,15 +60,7 @@ struct display_clock_funcs {
}; };
struct display_clock *dal_display_clock_dce112_create(
struct dc_context *ctx);
struct display_clock *dal_display_clock_dce110_create(
struct dc_context *ctx);
struct display_clock *dal_display_clock_dce80_create(
struct dc_context *ctx);
void dal_display_clock_destroy(struct display_clock **to_destroy); void dal_display_clock_destroy(struct display_clock **to_destroy);
#endif /* __DISPLAY_CLOCK_INTERFACE_H__ */ #endif /* __DISPLAY_CLOCK_INTERFACE_H__ */
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