/* * Copyright 2012-16 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 *
*/
/* 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)
*/ staticint clk_mgr_adjust_dp_ref_freq_for_ss(struct dce_clk_mgr *clk_mgr_dce, int dp_ref_clk_khz)
{ if (clk_mgr_dce->ss_on_dprefclk && clk_mgr_dce->dprefclk_ss_divider != 0) { struct fixed31_32 ss_percentage = dc_fixpt_div_int(
dc_fixpt_from_fraction(clk_mgr_dce->dprefclk_ss_percentage,
clk_mgr_dce->dprefclk_ss_divider), 200); struct fixed31_32 adj_dp_ref_clk_khz;
staticint dce_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr)
{ struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); int dprefclk_wdivider; int dprefclk_src_sel; int dp_ref_clk_khz = 600000; int target_div;
/* ASSERT DP Reference Clock source is from DFS*/
REG_GET(DPREFCLK_CNTL, DPREFCLK_SRC_SEL, &dprefclk_src_sel);
ASSERT(dprefclk_src_sel == 0);
/* Read the mmDENTIST_DISPCLK_CNTL to get the currently
* programmed DID DENTIST_DPREFCLK_WDIVIDER*/
REG_GET(DENTIST_DISPCLK_CNTL, DENTIST_DPREFCLK_WDIVIDER, &dprefclk_wdivider);
/* Convert DENTIST_DPREFCLK_WDIVIDERto actual divider*/
target_div = dentist_get_divider_from_did(dprefclk_wdivider);
/* Calculate the current DFS clock, in kHz.*/
dp_ref_clk_khz = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
* clk_mgr_dce->dentist_vco_freq_khz) / target_div;
/* unit: in_khz before mode set, get pixel clock from context. ASIC register * may not be programmed yet
*/ static uint32_t get_max_pixel_clock_for_all_paths(struct dc_state *context)
{
uint32_t max_pix_clk = 0; int i;
for (i = 0; i < MAX_PIPES; i++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
if (pipe_ctx->stream == NULL) continue;
/* do not check under lay */ if (pipe_ctx->top_pipe) continue;
/* raise clock state for HBR3/2 if required. Confirmed with HW DCE/DPCS * logic for HBR3 still needs Nominal (0.8V) on VDDC rail
*/ if (dc_is_dp_signal(pipe_ctx->stream->signal) &&
pipe_ctx->stream_res.pix_clk_params.requested_sym_clk > max_pix_clk)
max_pix_clk = pipe_ctx->stream_res.pix_clk_params.requested_sym_clk;
}
/* Iterate from highest supported to lowest valid state, and update * lowest RequiredState with the lowest state that satisfies * all required clocks
*/ for (i = clk_mgr_dce->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--) if (context->bw_ctx.bw.dce.dispclk_khz >
clk_mgr_dce->max_clks_by_state[i].display_clk_khz
|| max_pix_clk >
clk_mgr_dce->max_clks_by_state[i].pixel_clk_khz) break;
low_req_clk = i + 1; if (low_req_clk > clk_mgr_dce->max_clks_state) { /* set max clock state for high phyclock, invalid on exceeding display clock */ if (clk_mgr_dce->max_clks_by_state[clk_mgr_dce->max_clks_state].display_clk_khz
< context->bw_ctx.bw.dce.dispclk_khz)
low_req_clk = DM_PP_CLOCKS_STATE_INVALID; else
low_req_clk = clk_mgr_dce->max_clks_state;
}
if (clk_mgr_dce->dfs_bypass_active) { /* Cache the fixed display clock*/
clk_mgr_dce->dfs_bypass_disp_clk =
pxl_clk_params.dfs_bypass_display_clock;
actual_clock = 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_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu))
dmcu->funcs->set_psr_wait_loop(dmcu, actual_clock / 1000 / 7);
return actual_clock;
}
int dce112_set_clock(struct clk_mgr *clk_mgr, int requested_clk_khz)
{ struct dce_clk_mgr *clk_mgr_dce = TO_DCE_CLK_MGR(clk_mgr); struct bp_set_dce_clock_parameters dce_clk_params; struct dc_bios *bp = clk_mgr->ctx->dc_bios; struct dc *core_dc = clk_mgr->ctx->dc; struct dmcu *dmcu = core_dc->res_pool->dmcu; int actual_clock = requested_clk_khz; /* 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 = max(requested_clk_khz,
clk_mgr_dce->dentist_vco_freq_khz / 62);
/* 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_mgr_dce->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;
/*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;
/*Do not allow bad VBIOS/SBIOS to override with invalid values,
* check for > 100MHz*/ if (info.disp_clk_voltage[i].max_supported_clk >= 100000)
clk_mgr_dce->max_clks_by_state[clk_state].display_clk_khz =
info.disp_clk_voltage[i].max_supported_clk;
}
if (!debug->disable_dfs_bypass && bp->integrated_info) if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE)
clk_mgr_dce->dfs_bypass_enabled = true;
}
if (ss_info_num) { struct spread_spectrum_info info = { { 0 } }; enum bp_result 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) {
clk_mgr_dce->ss_on_dprefclk = true;
clk_mgr_dce->dprefclk_ss_divider = info.spread_percentage_divider;
if (info.type.CENTER_MODE == 0) { /* TODO: Currently for DP Reference clock we * need only SS percentage for
* downspread */
clk_mgr_dce->dprefclk_ss_percentage =
info.spread_spectrum_percentage;
}
return;
}
result = bp->funcs->get_spread_spectrum_info(
bp, AS_SIGNAL_TYPE_DISPLAY_PORT, 0, &info);
/* Based on VBIOS, VBIOS will keep entry for DPREFCLK 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_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;
} if (clk_mgr_dce->base.ctx->dc->debug.ignore_dpref_ss)
clk_mgr_dce->dprefclk_ss_percentage = 0;
}
}
}
/** * 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;
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( conststruct dc_state *context, struct dm_pp_display_configuration *pp_display_cfg)
{ int j; int num_cfgs = 0;
for (j = 0; j < context->stream_count; j++) { int k;
if (min_vertical_blank_time > vertical_blank_time)
min_vertical_blank_time = vertical_blank_time;
}
return min_vertical_blank_time;
}
staticint determine_sclk_from_bounding_box( conststruct dc *dc, int required_sclk)
{ int i;
/* * Some asics do not give us sclk levels, so we just report the actual * required sclk
*/ if (dc->sclk_lvls.num_levels == 0) return required_sclk;
for (i = 0; i < dc->sclk_lvls.num_levels; i++) { if (dc->sclk_lvls.clocks_in_khz[i] >= required_sclk) return dc->sclk_lvls.clocks_in_khz[i];
} /* * even maximum level could not satisfy requirement, this * is unexpected at this stage, should have been caught at * validation time
*/
ASSERT(0); return dc->sclk_lvls.clocks_in_khz[dc->sclk_lvls.num_levels - 1];
}
/* * As workaround for >4x4K lightup set dcfclock to min_engine_clock value. * This is not required for less than 5 displays, * thus don't request decfclk in dc to avoid impact * on power saving. *
*/
pp_display_cfg->min_dcfclock_khz = (context->stream_count > 4) ?
pp_display_cfg->min_engine_clock_khz : 0;
/*TODO: W/A for dal3 linux, investigate why this works */ if (!clk_mgr_dce->dfs_bypass_active)
patched_disp_clk = patched_disp_clk * 115 / 100;
level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); /* get max clock state from PPLIB */ if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower)
|| level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { if (dm_pp_apply_power_level_change_request(clk_mgr->ctx, &level_change_req))
clk_mgr_dce->cur_min_clks_state = level_change_req.power_level;
}
/*TODO: W/A for dal3 linux, investigate why this works */ if (!clk_mgr_dce->dfs_bypass_active)
patched_disp_clk = patched_disp_clk * 115 / 100;
level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); /* get max clock state from PPLIB */ if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower)
|| level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { if (dm_pp_apply_power_level_change_request(clk_mgr->ctx, &level_change_req))
clk_mgr_dce->cur_min_clks_state = level_change_req.power_level;
}
/*TODO: W/A for dal3 linux, investigate why this works */ if (!clk_mgr_dce->dfs_bypass_active)
patched_disp_clk = patched_disp_clk * 115 / 100;
level_change_req.power_level = dce_get_required_clocks_state(clk_mgr, context); /* get max clock state from PPLIB */ if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower)
|| level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { if (dm_pp_apply_power_level_change_request(clk_mgr->ctx, &level_change_req))
clk_mgr_dce->cur_min_clks_state = level_change_req.power_level;
}
/*TODO: W/A for dal3 linux, investigate why this works */ if (!clk_mgr_dce->dfs_bypass_active)
patched_disp_clk = patched_disp_clk * 115 / 100;
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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.