|
|
|
@@ -14,6 +14,7 @@
|
|
|
|
|
#include "core_types.h"
|
|
|
|
|
#include "dm_helpers.h"
|
|
|
|
|
#include "link.h"
|
|
|
|
|
#include "dc_state_priv.h"
|
|
|
|
|
#include "atomfirmware.h"
|
|
|
|
|
|
|
|
|
|
#include "dcn401_smu14_driver_if.h"
|
|
|
|
@@ -29,6 +30,7 @@
|
|
|
|
|
#define mmCLK01_CLK0_CLK2_DFS_CNTL 0x16E6F
|
|
|
|
|
#define mmCLK01_CLK0_CLK3_DFS_CNTL 0x16E72
|
|
|
|
|
#define mmCLK01_CLK0_CLK4_DFS_CNTL 0x16E75
|
|
|
|
|
#define mmCLK20_CLK2_CLK2_DFS_CNTL 0x1B051
|
|
|
|
|
|
|
|
|
|
#define CLK0_CLK_PLL_REQ__FbMult_int_MASK 0x000001ffUL
|
|
|
|
|
#define CLK0_CLK_PLL_REQ__PllSpineDiv_MASK 0x0000f000UL
|
|
|
|
@@ -302,6 +304,197 @@ void dcn401_init_clocks(struct clk_mgr *clk_mgr_base)
|
|
|
|
|
dcn401_build_wm_range_table(clk_mgr_base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dcn401_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass,
|
|
|
|
|
struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info)
|
|
|
|
|
{
|
|
|
|
|
struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
|
|
|
|
|
uint32_t dprefclk_did = 0;
|
|
|
|
|
uint32_t dcfclk_did = 0;
|
|
|
|
|
uint32_t dtbclk_did = 0;
|
|
|
|
|
uint32_t dispclk_did = 0;
|
|
|
|
|
uint32_t dppclk_did = 0;
|
|
|
|
|
uint32_t fclk_did = 0;
|
|
|
|
|
uint32_t target_div = 0;
|
|
|
|
|
|
|
|
|
|
/* DFS Slice 0 is used for DISPCLK */
|
|
|
|
|
dispclk_did = REG_READ(CLK0_CLK0_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 1 is used for DPPCLK */
|
|
|
|
|
dppclk_did = REG_READ(CLK0_CLK1_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 2 is used for DPREFCLK */
|
|
|
|
|
dprefclk_did = REG_READ(CLK0_CLK2_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 3 is used for DCFCLK */
|
|
|
|
|
dcfclk_did = REG_READ(CLK0_CLK3_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 4 is used for DTBCLK */
|
|
|
|
|
dtbclk_did = REG_READ(CLK0_CLK4_DFS_CNTL);
|
|
|
|
|
/* DFS Slice _ is used for FCLK */
|
|
|
|
|
fclk_did = REG_READ(CLK2_CLK2_DFS_CNTL);
|
|
|
|
|
|
|
|
|
|
/* Convert DISPCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dispclk_did);
|
|
|
|
|
//Get dispclk in khz
|
|
|
|
|
regs_and_bypass->dispclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DISPCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dppclk_did);
|
|
|
|
|
//Get dppclk in khz
|
|
|
|
|
regs_and_bypass->dppclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DPREFCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dprefclk_did);
|
|
|
|
|
//Get dprefclk in khz
|
|
|
|
|
regs_and_bypass->dprefclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DCFCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dcfclk_did);
|
|
|
|
|
//Get dcfclk in khz
|
|
|
|
|
regs_and_bypass->dcfclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DTBCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dtbclk_did);
|
|
|
|
|
//Get dtbclk in khz
|
|
|
|
|
regs_and_bypass->dtbclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DTBCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(fclk_did);
|
|
|
|
|
//Get fclk in khz
|
|
|
|
|
regs_and_bypass->fclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool dcn401_check_native_scaling(struct pipe_ctx *pipe)
|
|
|
|
|
{
|
|
|
|
|
bool is_native_scaling = false;
|
|
|
|
|
int width = pipe->plane_state->src_rect.width;
|
|
|
|
|
int height = pipe->plane_state->src_rect.height;
|
|
|
|
|
|
|
|
|
|
if (pipe->stream->timing.h_addressable == width &&
|
|
|
|
|
pipe->stream->timing.v_addressable == height &&
|
|
|
|
|
pipe->plane_state->dst_rect.width == width &&
|
|
|
|
|
pipe->plane_state->dst_rect.height == height)
|
|
|
|
|
is_native_scaling = true;
|
|
|
|
|
|
|
|
|
|
return is_native_scaling;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dcn401_auto_dpm_test_log(
|
|
|
|
|
struct dc_clocks *new_clocks,
|
|
|
|
|
struct clk_mgr_internal *clk_mgr,
|
|
|
|
|
struct dc_state *context)
|
|
|
|
|
{
|
|
|
|
|
unsigned int mall_ss_size_bytes;
|
|
|
|
|
int dramclk_khz_override, fclk_khz_override, num_fclk_levels;
|
|
|
|
|
|
|
|
|
|
struct pipe_ctx *pipe_ctx_list[MAX_PIPES];
|
|
|
|
|
int active_pipe_count = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_PIPES; i++) {
|
|
|
|
|
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
|
|
|
|
|
|
|
|
|
|
if (pipe_ctx->stream && dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) {
|
|
|
|
|
pipe_ctx_list[active_pipe_count] = pipe_ctx;
|
|
|
|
|
active_pipe_count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msleep(5);
|
|
|
|
|
|
|
|
|
|
mall_ss_size_bytes = context->bw_ctx.bw.dcn.mall_ss_size_bytes;
|
|
|
|
|
|
|
|
|
|
struct clk_log_info log_info = {0};
|
|
|
|
|
struct clk_state_registers_and_bypass clk_register_dump;
|
|
|
|
|
|
|
|
|
|
dcn401_dump_clk_registers(&clk_register_dump, &clk_mgr->base, &log_info);
|
|
|
|
|
|
|
|
|
|
// Overrides for these clocks in case there is no p_state change support
|
|
|
|
|
dramclk_khz_override = new_clocks->dramclk_khz;
|
|
|
|
|
fclk_khz_override = new_clocks->fclk_khz;
|
|
|
|
|
|
|
|
|
|
num_fclk_levels = clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_fclk_levels - 1;
|
|
|
|
|
|
|
|
|
|
if (!new_clocks->p_state_change_support)
|
|
|
|
|
dramclk_khz_override = clk_mgr->base.bw_params->max_memclk_mhz * 1000;
|
|
|
|
|
|
|
|
|
|
if (!new_clocks->fclk_p_state_change_support)
|
|
|
|
|
fclk_khz_override = clk_mgr->base.bw_params->clk_table.entries[num_fclk_levels].fclk_mhz * 1000;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// IMPORTANT: When adding more clocks to these logs, do NOT put a newline
|
|
|
|
|
// anywhere other than at the very end of the string.
|
|
|
|
|
//
|
|
|
|
|
// Formatting example (make sure to have " - " between each entry):
|
|
|
|
|
//
|
|
|
|
|
// AutoDPMTest: clk1:%d - clk2:%d - clk3:%d - clk4:%d\n"
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
if (active_pipe_count > 0 &&
|
|
|
|
|
new_clocks->dramclk_khz > 0 &&
|
|
|
|
|
new_clocks->fclk_khz > 0 &&
|
|
|
|
|
new_clocks->dcfclk_khz > 0 &&
|
|
|
|
|
new_clocks->dppclk_khz > 0) {
|
|
|
|
|
|
|
|
|
|
uint32_t pix_clk_list[MAX_PIPES] = {0};
|
|
|
|
|
int p_state_list[MAX_PIPES] = {0};
|
|
|
|
|
int disp_src_width_list[MAX_PIPES] = {0};
|
|
|
|
|
int disp_src_height_list[MAX_PIPES] = {0};
|
|
|
|
|
uint64_t disp_src_refresh_list[MAX_PIPES] = {0};
|
|
|
|
|
bool is_scaled_list[MAX_PIPES] = {0};
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < active_pipe_count; i++) {
|
|
|
|
|
struct pipe_ctx *curr_pipe_ctx = pipe_ctx_list[i];
|
|
|
|
|
uint64_t refresh_rate;
|
|
|
|
|
|
|
|
|
|
pix_clk_list[i] = curr_pipe_ctx->stream->timing.pix_clk_100hz;
|
|
|
|
|
p_state_list[i] = curr_pipe_ctx->p_state_type;
|
|
|
|
|
|
|
|
|
|
refresh_rate = (curr_pipe_ctx->stream->timing.pix_clk_100hz * (uint64_t)100 +
|
|
|
|
|
curr_pipe_ctx->stream->timing.v_total
|
|
|
|
|
* (uint64_t) curr_pipe_ctx->stream->timing.h_total - (uint64_t)1);
|
|
|
|
|
refresh_rate = div_u64(refresh_rate, curr_pipe_ctx->stream->timing.v_total);
|
|
|
|
|
refresh_rate = div_u64(refresh_rate, curr_pipe_ctx->stream->timing.h_total);
|
|
|
|
|
disp_src_refresh_list[i] = refresh_rate;
|
|
|
|
|
|
|
|
|
|
if (curr_pipe_ctx->plane_state) {
|
|
|
|
|
is_scaled_list[i] = !(dcn401_check_native_scaling(curr_pipe_ctx));
|
|
|
|
|
disp_src_width_list[i] = curr_pipe_ctx->plane_state->src_rect.width;
|
|
|
|
|
disp_src_height_list[i] = curr_pipe_ctx->plane_state->src_rect.height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DC_LOG_AUTO_DPM_TEST("AutoDPMTest: dramclk:%d - fclk:%d - "
|
|
|
|
|
"dcfclk:%d - dppclk:%d - dispclk_hw:%d - "
|
|
|
|
|
"dppclk_hw:%d - dprefclk_hw:%d - dcfclk_hw:%d - "
|
|
|
|
|
"dtbclk_hw:%d - fclk_hw:%d - pix_clk_0:%d - pix_clk_1:%d - "
|
|
|
|
|
"pix_clk_2:%d - pix_clk_3:%d - mall_ss_size:%d - p_state_type_0:%d - "
|
|
|
|
|
"p_state_type_1:%d - p_state_type_2:%d - p_state_type_3:%d - "
|
|
|
|
|
"pix_width_0:%d - pix_height_0:%d - refresh_rate_0:%lld - is_scaled_0:%d - "
|
|
|
|
|
"pix_width_1:%d - pix_height_1:%d - refresh_rate_1:%lld - is_scaled_1:%d - "
|
|
|
|
|
"pix_width_2:%d - pix_height_2:%d - refresh_rate_2:%lld - is_scaled_2:%d - "
|
|
|
|
|
"pix_width_3:%d - pix_height_3:%d - refresh_rate_3:%lld - is_scaled_3:%d - LOG_END\n",
|
|
|
|
|
dramclk_khz_override,
|
|
|
|
|
fclk_khz_override,
|
|
|
|
|
new_clocks->dcfclk_khz,
|
|
|
|
|
new_clocks->dppclk_khz,
|
|
|
|
|
clk_register_dump.dispclk,
|
|
|
|
|
clk_register_dump.dppclk,
|
|
|
|
|
clk_register_dump.dprefclk,
|
|
|
|
|
clk_register_dump.dcfclk,
|
|
|
|
|
clk_register_dump.dtbclk,
|
|
|
|
|
clk_register_dump.fclk,
|
|
|
|
|
pix_clk_list[0], pix_clk_list[1], pix_clk_list[3], pix_clk_list[2],
|
|
|
|
|
mall_ss_size_bytes,
|
|
|
|
|
p_state_list[0], p_state_list[1], p_state_list[2], p_state_list[3],
|
|
|
|
|
disp_src_width_list[0], disp_src_height_list[0], disp_src_refresh_list[0], is_scaled_list[0],
|
|
|
|
|
disp_src_width_list[1], disp_src_height_list[1], disp_src_refresh_list[1], is_scaled_list[1],
|
|
|
|
|
disp_src_width_list[2], disp_src_height_list[2], disp_src_refresh_list[2], is_scaled_list[2],
|
|
|
|
|
disp_src_width_list[3], disp_src_height_list[3], disp_src_refresh_list[3], is_scaled_list[3]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dcn401_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr,
|
|
|
|
|
struct dc_state *context,
|
|
|
|
|
int ref_dtbclk_khz)
|
|
|
|
@@ -1194,6 +1387,10 @@ static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base,
|
|
|
|
|
|
|
|
|
|
/* execute sequence */
|
|
|
|
|
dcn401_execute_block_sequence(clk_mgr_base, num_steps);
|
|
|
|
|
|
|
|
|
|
if (dc->config.enable_auto_dpm_test_logs)
|
|
|
|
|
dcn401_auto_dpm_test_log(&context->bw_ctx.bw.dcn.clk, TO_CLK_MGR_INTERNAL(clk_mgr_base), context);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1218,59 +1415,6 @@ static uint32_t dcn401_get_vco_frequency_from_reg(struct clk_mgr_internal *clk_m
|
|
|
|
|
return dc_fixpt_floor(pll_req);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dcn401_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass,
|
|
|
|
|
struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info)
|
|
|
|
|
{
|
|
|
|
|
struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
|
|
|
|
|
uint32_t dprefclk_did = 0;
|
|
|
|
|
uint32_t dcfclk_did = 0;
|
|
|
|
|
uint32_t dtbclk_did = 0;
|
|
|
|
|
uint32_t dispclk_did = 0;
|
|
|
|
|
uint32_t dppclk_did = 0;
|
|
|
|
|
uint32_t target_div = 0;
|
|
|
|
|
|
|
|
|
|
/* DFS Slice 0 is used for DISPCLK */
|
|
|
|
|
dispclk_did = REG_READ(CLK0_CLK0_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 1 is used for DPPCLK */
|
|
|
|
|
dppclk_did = REG_READ(CLK0_CLK1_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 2 is used for DPREFCLK */
|
|
|
|
|
dprefclk_did = REG_READ(CLK0_CLK2_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 3 is used for DCFCLK */
|
|
|
|
|
dcfclk_did = REG_READ(CLK0_CLK3_DFS_CNTL);
|
|
|
|
|
/* DFS Slice 4 is used for DTBCLK */
|
|
|
|
|
dtbclk_did = REG_READ(CLK0_CLK4_DFS_CNTL);
|
|
|
|
|
|
|
|
|
|
/* Convert DISPCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dispclk_did);
|
|
|
|
|
//Get dispclk in khz
|
|
|
|
|
regs_and_bypass->dispclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DISPCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dppclk_did);
|
|
|
|
|
//Get dppclk in khz
|
|
|
|
|
regs_and_bypass->dppclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DPREFCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dprefclk_did);
|
|
|
|
|
//Get dprefclk in khz
|
|
|
|
|
regs_and_bypass->dprefclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DCFCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dcfclk_did);
|
|
|
|
|
//Get dcfclk in khz
|
|
|
|
|
regs_and_bypass->dcfclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
|
|
|
|
|
/* Convert DTBCLK DFS Slice DID to divider*/
|
|
|
|
|
target_div = dentist_get_divider_from_did(dtbclk_did);
|
|
|
|
|
//Get dtbclk in khz
|
|
|
|
|
regs_and_bypass->dtbclk = (DENTIST_DIVIDER_RANGE_SCALE_FACTOR
|
|
|
|
|
* clk_mgr->base.dentist_vco_freq_khz) / target_div;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dcn401_clock_read_ss_info(struct clk_mgr_internal *clk_mgr)
|
|
|
|
|
{
|
|
|
|
|
struct dc_bios *bp = clk_mgr->base.ctx->dc_bios;
|
|
|
|
|