FILE
dcn314_clk_mgr.c
File preview with syntax highlighting.
linux-6.19.14/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
1// SPDX-License-Identifier: MIT 2/* 3 * Copyright 2022 Advanced Micro Devices, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: AMD 24 * 25 */ 26 27 28 29#include "dcn314_clk_mgr.h" 30 31#include "dccg.h" 32#include "clk_mgr_internal.h" 33 34// For dce12_get_dp_ref_freq_khz 35#include "dce100/dce_clk_mgr.h" 36 37// For dcn20_update_clocks_update_dpp_dto 38#include "dcn20/dcn20_clk_mgr.h" 39 40 41 42#include "reg_helper.h" 43#include "core_types.h" 44#include "dm_helpers.h" 45 46/* TODO: remove this include once we ported over remaining clk mgr functions*/ 47#include "dcn30/dcn30_clk_mgr.h" 48#include "dcn31/dcn31_clk_mgr.h" 49 50#include "dc_dmub_srv.h" 51#include "link_service.h" 52#include "dcn314_smu.h" 53 54 55#include "logger_types.h" 56 57 58#define MAX_INSTANCE 7 59#define MAX_SEGMENT 8 60 61struct IP_BASE_INSTANCE { 62 unsigned int segment[MAX_SEGMENT]; 63}; 64 65struct IP_BASE { 66 struct IP_BASE_INSTANCE instance[MAX_INSTANCE]; 67}; 68 69static const struct IP_BASE CLK_BASE = { { { { 0x00016C00, 0x02401800, 0, 0, 0, 0, 0, 0 } }, 70 { { 0x00016E00, 0x02401C00, 0, 0, 0, 0, 0, 0 } }, 71 { { 0x00017000, 0x02402000, 0, 0, 0, 0, 0, 0 } }, 72 { { 0x00017200, 0x02402400, 0, 0, 0, 0, 0, 0 } }, 73 { { 0x0001B000, 0x0242D800, 0, 0, 0, 0, 0, 0 } }, 74 { { 0x0001B200, 0x0242DC00, 0, 0, 0, 0, 0, 0 } }, 75 { { 0x0001B400, 0x0242E000, 0, 0, 0, 0, 0, 0 } } } }; 76 77#undef DC_LOGGER 78#define DC_LOGGER \ 79 clk_mgr->base.base.ctx->logger 80#define regCLK1_CLK_PLL_REQ 0x0237 81#define regCLK1_CLK_PLL_REQ_BASE_IDX 0 82 83#define CLK1_CLK_PLL_REQ__FbMult_int__SHIFT 0x0 84#define CLK1_CLK_PLL_REQ__PllSpineDiv__SHIFT 0xc 85#define CLK1_CLK_PLL_REQ__FbMult_frac__SHIFT 0x10 86#define CLK1_CLK_PLL_REQ__FbMult_int_MASK 0x000001FFL 87#define CLK1_CLK_PLL_REQ__PllSpineDiv_MASK 0x0000F000L 88#define CLK1_CLK_PLL_REQ__FbMult_frac_MASK 0xFFFF0000L 89 90#define regCLK1_CLK2_BYPASS_CNTL 0x029c 91#define regCLK1_CLK2_BYPASS_CNTL_BASE_IDX 0 92 93#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_SEL__SHIFT 0x0 94#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_DIV__SHIFT 0x10 95#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_SEL_MASK 0x00000007L 96#define CLK1_CLK2_BYPASS_CNTL__CLK2_BYPASS_DIV_MASK 0x000F0000L 97 98#define regCLK6_0_CLK6_spll_field_8 0x464b 99#define regCLK6_0_CLK6_spll_field_8_BASE_IDX 0 100 101#define CLK6_0_CLK6_spll_field_8__spll_ssc_en__SHIFT 0xd 102#define CLK6_0_CLK6_spll_field_8__spll_ssc_en_MASK 0x00002000L 103 104#define REG(reg_name) \ 105 (CLK_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name) 106 107#define TO_CLK_MGR_DCN314(clk_mgr)\ 108 container_of(clk_mgr, struct clk_mgr_dcn314, base) 109 110static int dcn314_get_active_display_cnt_wa( 111 struct dc *dc, 112 struct dc_state *context) 113{ 114 int i, display_count; 115 bool tmds_present = false; 116 117 display_count = 0; 118 for (i = 0; i < context->stream_count; i++) { 119 const struct dc_stream_state *stream = context->streams[i]; 120 121 if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || 122 stream->signal == SIGNAL_TYPE_DVI_SINGLE_LINK || 123 stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) 124 tmds_present = true; 125 126 /* Checking stream / link detection ensuring that PHY is active*/ 127 if (dc_is_dp_signal(stream->signal) && !stream->dpms_off) 128 display_count++; 129 130 } 131 132 for (i = 0; i < dc->link_count; i++) { 133 const struct dc_link *link = dc->links[i]; 134 135 /* abusing the fact that the dig and phy are coupled to see if the phy is enabled */ 136 if (link->link_enc && link->link_enc->funcs->is_dig_enabled && 137 link->link_enc->funcs->is_dig_enabled(link->link_enc)) 138 display_count++; 139 } 140 141 /* WA for hang on HDMI after display off back on*/ 142 if (display_count == 0 && tmds_present) 143 display_count = 1; 144 145 return display_count; 146} 147 148static void dcn314_disable_otg_wa(struct clk_mgr *clk_mgr_base, struct dc_state *context, 149 bool safe_to_lower, bool disable) 150{ 151 struct dc *dc = clk_mgr_base->ctx->dc; 152 int i; 153 154 for (i = 0; i < dc->res_pool->pipe_count; ++i) { 155 struct pipe_ctx *pipe = safe_to_lower 156 ? &context->res_ctx.pipe_ctx[i] 157 : &dc->current_state->res_ctx.pipe_ctx[i]; 158 159 if (pipe->top_pipe || pipe->prev_odm_pipe) 160 continue; 161 if (pipe->stream && (pipe->stream->dpms_off || dc_is_virtual_signal(pipe->stream->signal))) { 162 if (disable) { 163 if (pipe->stream_res.tg && pipe->stream_res.tg->funcs->immediate_disable_crtc) 164 pipe->stream_res.tg->funcs->immediate_disable_crtc(pipe->stream_res.tg); 165 166 reset_sync_context_for_pipe(dc, context, i); 167 } else { 168 pipe->stream_res.tg->funcs->enable_crtc(pipe->stream_res.tg); 169 } 170 } 171 } 172} 173 174bool dcn314_is_spll_ssc_enabled(struct clk_mgr *clk_mgr_base) 175{ 176 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 177 uint32_t ssc_enable; 178 179 REG_GET(CLK6_0_CLK6_spll_field_8, spll_ssc_en, &ssc_enable); 180 181 return ssc_enable == 1; 182} 183 184void dcn314_init_clocks(struct clk_mgr *clk_mgr) 185{ 186 struct clk_mgr_internal *clk_mgr_int = TO_CLK_MGR_INTERNAL(clk_mgr); 187 uint32_t ref_dtbclk = clk_mgr->clks.ref_dtbclk_khz; 188 189 memset(&(clk_mgr->clks), 0, sizeof(struct dc_clocks)); 190 // Assumption is that boot state always supports pstate 191 clk_mgr->clks.ref_dtbclk_khz = ref_dtbclk; // restore ref_dtbclk 192 clk_mgr->clks.p_state_change_support = true; 193 clk_mgr->clks.prev_p_state_change_support = true; 194 clk_mgr->clks.pwr_state = DCN_PWR_STATE_UNKNOWN; 195 clk_mgr->clks.zstate_support = DCN_ZSTATE_SUPPORT_UNKNOWN; 196 197 // to adjust dp_dto reference clock if ssc is enable otherwise to apply dprefclk 198 if (dcn314_is_spll_ssc_enabled(clk_mgr)) 199 clk_mgr->dp_dto_source_clock_in_khz = 200 dce_adjust_dp_ref_freq_for_ss(clk_mgr_int, clk_mgr->dprefclk_khz); 201 else 202 clk_mgr->dp_dto_source_clock_in_khz = clk_mgr->dprefclk_khz; 203} 204 205void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, 206 struct dc_state *context, 207 bool safe_to_lower) 208{ 209 union dmub_rb_cmd cmd; 210 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 211 struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk; 212 struct dc *dc = clk_mgr_base->ctx->dc; 213 int display_count = 0; 214 bool update_dppclk = false; 215 bool update_dispclk = false; 216 bool dpp_clock_lowered = false; 217 218 if (dc->work_arounds.skip_clock_update) 219 return; 220 221 display_count = dcn314_get_active_display_cnt_wa(dc, context); 222 /* 223 * if it is safe to lower, but we are already in the lower state, we don't have to do anything 224 * also if safe to lower is false, we just go in the higher state 225 */ 226 if (safe_to_lower) { 227 if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && 228 new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { 229 dcn314_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); 230 dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); 231 clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; 232 } 233 234 if (clk_mgr_base->clks.dtbclk_en && !new_clocks->dtbclk_en) { 235 dcn314_smu_set_dtbclk(clk_mgr, false); 236 clk_mgr_base->clks.dtbclk_en = new_clocks->dtbclk_en; 237 } 238 /* check that we're not already in lower */ 239 if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_LOW_POWER) { 240 /* if we can go lower, go lower */ 241 if (display_count == 0) { 242 union display_idle_optimization_u idle_info = { 0 }; 243 idle_info.idle_info.df_request_disabled = 1; 244 idle_info.idle_info.phy_ref_clk_off = 1; 245 idle_info.idle_info.s0i2_rdy = 1; 246 dcn314_smu_set_display_idle_optimization(clk_mgr, idle_info.data); 247 /* update power state */ 248 clk_mgr_base->clks.pwr_state = DCN_PWR_STATE_LOW_POWER; 249 } 250 } 251 } else { 252 if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && 253 new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { 254 dcn314_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); 255 dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); 256 clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; 257 } 258 259 if (!clk_mgr_base->clks.dtbclk_en && new_clocks->dtbclk_en) { 260 dcn314_smu_set_dtbclk(clk_mgr, true); 261 clk_mgr_base->clks.dtbclk_en = new_clocks->dtbclk_en; 262 } 263 264 /* check that we're not already in D0 */ 265 if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_MISSION_MODE) { 266 union display_idle_optimization_u idle_info = { 0 }; 267 268 dcn314_smu_set_display_idle_optimization(clk_mgr, idle_info.data); 269 /* update power state */ 270 clk_mgr_base->clks.pwr_state = DCN_PWR_STATE_MISSION_MODE; 271 } 272 } 273 274 if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, clk_mgr_base->clks.dcfclk_khz)) { 275 clk_mgr_base->clks.dcfclk_khz = new_clocks->dcfclk_khz; 276 dcn314_smu_set_hard_min_dcfclk(clk_mgr, clk_mgr_base->clks.dcfclk_khz); 277 } 278 279 if (should_set_clock(safe_to_lower, 280 new_clocks->dcfclk_deep_sleep_khz, clk_mgr_base->clks.dcfclk_deep_sleep_khz)) { 281 clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz; 282 dcn314_smu_set_min_deep_sleep_dcfclk(clk_mgr, clk_mgr_base->clks.dcfclk_deep_sleep_khz); 283 } 284 285 // workaround: Limit dppclk to 100Mhz to avoid lower eDP panel switch to plus 4K monitor underflow. 286 if (new_clocks->dppclk_khz < 100000) 287 new_clocks->dppclk_khz = 100000; 288 289 if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->base.clks.dppclk_khz)) { 290 if (clk_mgr->base.clks.dppclk_khz > new_clocks->dppclk_khz) 291 dpp_clock_lowered = true; 292 clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz; 293 update_dppclk = true; 294 } 295 296 if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz) && 297 (new_clocks->dispclk_khz > 0 || (safe_to_lower && display_count == 0))) { 298 int requested_dispclk_khz = new_clocks->dispclk_khz; 299 300 dcn314_disable_otg_wa(clk_mgr_base, context, safe_to_lower, true); 301 302 /* Clamp the requested clock to PMFW based on their limit. */ 303 if (dc->debug.min_disp_clk_khz > 0 && requested_dispclk_khz < dc->debug.min_disp_clk_khz) 304 requested_dispclk_khz = dc->debug.min_disp_clk_khz; 305 306 dcn314_smu_set_dispclk(clk_mgr, requested_dispclk_khz); 307 clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz; 308 dcn314_disable_otg_wa(clk_mgr_base, context, safe_to_lower, false); 309 310 update_dispclk = true; 311 } 312 313 if (dpp_clock_lowered) { 314 // increase per DPP DTO before lowering global dppclk 315 dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower); 316 dcn314_smu_set_dppclk(clk_mgr, clk_mgr_base->clks.dppclk_khz); 317 } else { 318 // increase global DPPCLK before lowering per DPP DTO 319 if (update_dppclk || update_dispclk) 320 dcn314_smu_set_dppclk(clk_mgr, clk_mgr_base->clks.dppclk_khz); 321 // always update dtos unless clock is lowered and not safe to lower 322 if (new_clocks->dppclk_khz >= dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz) 323 dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower); 324 } 325 326 // notify DMCUB of latest clocks 327 memset(&cmd, 0, sizeof(cmd)); 328 cmd.notify_clocks.header.type = DMUB_CMD__CLK_MGR; 329 cmd.notify_clocks.header.sub_type = DMUB_CMD__CLK_MGR_NOTIFY_CLOCKS; 330 cmd.notify_clocks.clocks.dcfclk_khz = clk_mgr_base->clks.dcfclk_khz; 331 cmd.notify_clocks.clocks.dcfclk_deep_sleep_khz = 332 clk_mgr_base->clks.dcfclk_deep_sleep_khz; 333 cmd.notify_clocks.clocks.dispclk_khz = clk_mgr_base->clks.dispclk_khz; 334 cmd.notify_clocks.clocks.dppclk_khz = clk_mgr_base->clks.dppclk_khz; 335 336 dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); 337} 338 339static int get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mgr) 340{ 341 /* get FbMult value */ 342 struct fixed31_32 pll_req; 343 unsigned int fbmult_frac_val = 0; 344 unsigned int fbmult_int_val = 0; 345 346 /* 347 * Register value of fbmult is in 8.16 format, we are converting to 314.32 348 * to leverage the fix point operations available in driver 349 */ 350 351 REG_GET(CLK1_CLK_PLL_REQ, FbMult_frac, &fbmult_frac_val); /* 16 bit fractional part*/ 352 REG_GET(CLK1_CLK_PLL_REQ, FbMult_int, &fbmult_int_val); /* 8 bit integer part */ 353 354 pll_req = dc_fixpt_from_int(fbmult_int_val); 355 356 /* 357 * since fractional part is only 16 bit in register definition but is 32 bit 358 * in our fix point definiton, need to shift left by 16 to obtain correct value 359 */ 360 pll_req.value |= fbmult_frac_val << 16; 361 362 /* multiply by REFCLK period */ 363 pll_req = dc_fixpt_mul_int(pll_req, clk_mgr->dfs_ref_freq_khz); 364 365 /* integer part is now VCO frequency in kHz */ 366 return dc_fixpt_floor(pll_req); 367} 368 369static void dcn314_enable_pme_wa(struct clk_mgr *clk_mgr_base) 370{ 371 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 372 373 dcn314_smu_enable_pme_wa(clk_mgr); 374} 375 376bool dcn314_are_clock_states_equal(struct dc_clocks *a, 377 struct dc_clocks *b) 378{ 379 if (a->dispclk_khz != b->dispclk_khz) 380 return false; 381 else if (a->dppclk_khz != b->dppclk_khz) 382 return false; 383 else if (a->dcfclk_khz != b->dcfclk_khz) 384 return false; 385 else if (a->dcfclk_deep_sleep_khz != b->dcfclk_deep_sleep_khz) 386 return false; 387 else if (a->zstate_support != b->zstate_support) 388 return false; 389 else if (a->dtbclk_en != b->dtbclk_en) 390 return false; 391 392 return true; 393} 394 395static void dcn314_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, 396 struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) 397{ 398 return; 399} 400 401static struct clk_bw_params dcn314_bw_params = { 402 .vram_type = Ddr4MemType, 403 .num_channels = 1, 404 .clk_table = { 405 .num_entries = 4, 406 }, 407 408}; 409 410static struct wm_table ddr5_wm_table = { 411 .entries = { 412 { 413 .wm_inst = WM_A, 414 .wm_type = WM_TYPE_PSTATE_CHG, 415 .pstate_latency_us = 11.72, 416 .sr_exit_time_us = 12.5, 417 .sr_enter_plus_exit_time_us = 14.5, 418 .valid = true, 419 }, 420 { 421 .wm_inst = WM_B, 422 .wm_type = WM_TYPE_PSTATE_CHG, 423 .pstate_latency_us = 11.72, 424 .sr_exit_time_us = 12.5, 425 .sr_enter_plus_exit_time_us = 14.5, 426 .valid = true, 427 }, 428 { 429 .wm_inst = WM_C, 430 .wm_type = WM_TYPE_PSTATE_CHG, 431 .pstate_latency_us = 11.72, 432 .sr_exit_time_us = 12.5, 433 .sr_enter_plus_exit_time_us = 14.5, 434 .valid = true, 435 }, 436 { 437 .wm_inst = WM_D, 438 .wm_type = WM_TYPE_PSTATE_CHG, 439 .pstate_latency_us = 11.72, 440 .sr_exit_time_us = 12.5, 441 .sr_enter_plus_exit_time_us = 14.5, 442 .valid = true, 443 }, 444 } 445}; 446 447static struct wm_table lpddr5_wm_table = { 448 .entries = { 449 { 450 .wm_inst = WM_A, 451 .wm_type = WM_TYPE_PSTATE_CHG, 452 .pstate_latency_us = 11.65333, 453 .sr_exit_time_us = 30.0, 454 .sr_enter_plus_exit_time_us = 32.0, 455 .valid = true, 456 }, 457 { 458 .wm_inst = WM_B, 459 .wm_type = WM_TYPE_PSTATE_CHG, 460 .pstate_latency_us = 11.65333, 461 .sr_exit_time_us = 30.0, 462 .sr_enter_plus_exit_time_us = 32.0, 463 .valid = true, 464 }, 465 { 466 .wm_inst = WM_C, 467 .wm_type = WM_TYPE_PSTATE_CHG, 468 .pstate_latency_us = 11.65333, 469 .sr_exit_time_us = 30.0, 470 .sr_enter_plus_exit_time_us = 32.0, 471 .valid = true, 472 }, 473 { 474 .wm_inst = WM_D, 475 .wm_type = WM_TYPE_PSTATE_CHG, 476 .pstate_latency_us = 11.65333, 477 .sr_exit_time_us = 30.0, 478 .sr_enter_plus_exit_time_us = 32.0, 479 .valid = true, 480 }, 481 } 482}; 483 484static DpmClocks314_t dummy_clocks; 485 486static struct dcn314_watermarks dummy_wms = { 0 }; 487 488static struct dcn314_ss_info_table ss_info_table = { 489 .ss_divider = 1000, 490 .ss_percentage = {0, 0, 375, 375, 375} 491}; 492 493static void dcn314_build_watermark_ranges(struct clk_bw_params *bw_params, struct dcn314_watermarks *table) 494{ 495 int i, num_valid_sets; 496 497 num_valid_sets = 0; 498 499 for (i = 0; i < WM_SET_COUNT; i++) { 500 /* skip empty entries, the smu array has no holes*/ 501 if (!bw_params->wm_table.entries[i].valid) 502 continue; 503 504 table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmSetting = bw_params->wm_table.entries[i].wm_inst; 505 table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmType = bw_params->wm_table.entries[i].wm_type; 506 /* We will not select WM based on fclk, so leave it as unconstrained */ 507 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinClock = 0; 508 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxClock = 0xFFFF; 509 510 if (table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmType == WM_TYPE_PSTATE_CHG) { 511 if (i == 0) 512 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinMclk = 0; 513 else { 514 /* add 1 to make it non-overlapping with next lvl */ 515 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinMclk = 516 bw_params->clk_table.entries[i - 1].dcfclk_mhz + 1; 517 } 518 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxMclk = 519 bw_params->clk_table.entries[i].dcfclk_mhz; 520 521 } else { 522 /* unconstrained for memory retraining */ 523 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinClock = 0; 524 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxClock = 0xFFFF; 525 526 /* Modify previous watermark range to cover up to max */ 527 table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxClock = 0xFFFF; 528 } 529 num_valid_sets++; 530 } 531 532 ASSERT(num_valid_sets != 0); /* Must have at least one set of valid watermarks */ 533 534 /* modify the min and max to make sure we cover the whole range*/ 535 table->WatermarkRow[WM_DCFCLK][0].MinMclk = 0; 536 table->WatermarkRow[WM_DCFCLK][0].MinClock = 0; 537 table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxMclk = 0xFFFF; 538 table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxClock = 0xFFFF; 539 540 /* This is for writeback only, does not matter currently as no writeback support*/ 541 table->WatermarkRow[WM_SOCCLK][0].WmSetting = WM_A; 542 table->WatermarkRow[WM_SOCCLK][0].MinClock = 0; 543 table->WatermarkRow[WM_SOCCLK][0].MaxClock = 0xFFFF; 544 table->WatermarkRow[WM_SOCCLK][0].MinMclk = 0; 545 table->WatermarkRow[WM_SOCCLK][0].MaxMclk = 0xFFFF; 546} 547 548static void dcn314_notify_wm_ranges(struct clk_mgr *clk_mgr_base) 549{ 550 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 551 struct clk_mgr_dcn314 *clk_mgr_dcn314 = TO_CLK_MGR_DCN314(clk_mgr); 552 struct dcn314_watermarks *table = clk_mgr_dcn314->smu_wm_set.wm_set; 553 554 if (!clk_mgr->smu_ver) 555 return; 556 557 if (!table || clk_mgr_dcn314->smu_wm_set.mc_address.quad_part == 0) 558 return; 559 560 memset(table, 0, sizeof(*table)); 561 562 dcn314_build_watermark_ranges(clk_mgr_base->bw_params, table); 563 564 dcn314_smu_set_dram_addr_high(clk_mgr, 565 clk_mgr_dcn314->smu_wm_set.mc_address.high_part); 566 dcn314_smu_set_dram_addr_low(clk_mgr, 567 clk_mgr_dcn314->smu_wm_set.mc_address.low_part); 568 dcn314_smu_transfer_wm_table_dram_2_smu(clk_mgr); 569} 570 571static void dcn314_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, 572 struct dcn314_smu_dpm_clks *smu_dpm_clks) 573{ 574 DpmClocks314_t *table = smu_dpm_clks->dpm_clks; 575 576 if (!clk_mgr->smu_ver) 577 return; 578 579 if (!table || smu_dpm_clks->mc_address.quad_part == 0) 580 return; 581 582 memset(table, 0, sizeof(*table)); 583 584 dcn314_smu_set_dram_addr_high(clk_mgr, 585 smu_dpm_clks->mc_address.high_part); 586 dcn314_smu_set_dram_addr_low(clk_mgr, 587 smu_dpm_clks->mc_address.low_part); 588 dcn314_smu_transfer_dpm_table_smu_2_dram(clk_mgr); 589} 590 591static inline bool is_valid_clock_value(uint32_t clock_value) 592{ 593 return clock_value > 1 && clock_value < 100000; 594} 595 596static unsigned int convert_wck_ratio(uint8_t wck_ratio) 597{ 598 switch (wck_ratio) { 599 case WCK_RATIO_1_2: 600 return 2; 601 602 case WCK_RATIO_1_4: 603 return 4; 604 605 default: 606 break; 607 } 608 return 1; 609} 610 611static uint32_t find_max_clk_value(const uint32_t clocks[], uint32_t num_clocks) 612{ 613 uint32_t max = 0; 614 int i; 615 616 for (i = 0; i < num_clocks; ++i) { 617 if (clocks[i] > max) 618 max = clocks[i]; 619 } 620 621 return max; 622} 623 624static void dcn314_clk_mgr_helper_populate_bw_params(struct clk_mgr_internal *clk_mgr, 625 struct integrated_info *bios_info, 626 const DpmClocks314_t *clock_table) 627{ 628 struct clk_bw_params *bw_params = clk_mgr->base.bw_params; 629 struct clk_limit_table_entry def_max = bw_params->clk_table.entries[bw_params->clk_table.num_entries - 1]; 630 uint32_t max_pstate = 0, max_fclk = 0, min_pstate = 0, max_dispclk = 0, max_dppclk = 0; 631 int i; 632 633 /* Find highest valid fclk pstate */ 634 for (i = 0; i < clock_table->NumDfPstatesEnabled; i++) { 635 if (is_valid_clock_value(clock_table->DfPstateTable[i].FClk) && 636 clock_table->DfPstateTable[i].FClk > max_fclk) { 637 max_fclk = clock_table->DfPstateTable[i].FClk; 638 max_pstate = i; 639 } 640 } 641 642 /* We expect the table to contain at least one valid fclk entry. */ 643 ASSERT(is_valid_clock_value(max_fclk)); 644 645 /* Dispclk and dppclk can be max at any voltage, same number of levels for both */ 646 if (clock_table->NumDispClkLevelsEnabled <= NUM_DISPCLK_DPM_LEVELS && 647 clock_table->NumDispClkLevelsEnabled <= NUM_DPPCLK_DPM_LEVELS) { 648 max_dispclk = find_max_clk_value(clock_table->DispClocks, clock_table->NumDispClkLevelsEnabled); 649 max_dppclk = find_max_clk_value(clock_table->DppClocks, clock_table->NumDispClkLevelsEnabled); 650 } else { 651 /* Invalid number of entries in the table from PMFW. */ 652 ASSERT(0); 653 } 654 655 /* Base the clock table on dcfclk, need at least one entry regardless of pmfw table */ 656 for (i = 0; i < clock_table->NumDcfClkLevelsEnabled; i++) { 657 uint32_t min_fclk = clock_table->DfPstateTable[0].FClk; 658 int j; 659 660 for (j = 1; j < clock_table->NumDfPstatesEnabled; j++) { 661 if (is_valid_clock_value(clock_table->DfPstateTable[j].FClk) && 662 clock_table->DfPstateTable[j].FClk < min_fclk && 663 clock_table->DfPstateTable[j].Voltage <= clock_table->SocVoltage[i]) { 664 min_fclk = clock_table->DfPstateTable[j].FClk; 665 min_pstate = j; 666 } 667 } 668 669 /* First search defaults for the clocks we don't read using closest lower or equal default dcfclk */ 670 for (j = bw_params->clk_table.num_entries - 1; j > 0; j--) 671 if (bw_params->clk_table.entries[j].dcfclk_mhz <= clock_table->DcfClocks[i]) 672 break; 673 674 bw_params->clk_table.entries[i].phyclk_mhz = bw_params->clk_table.entries[j].phyclk_mhz; 675 bw_params->clk_table.entries[i].phyclk_d18_mhz = bw_params->clk_table.entries[j].phyclk_d18_mhz; 676 bw_params->clk_table.entries[i].dtbclk_mhz = bw_params->clk_table.entries[j].dtbclk_mhz; 677 678 /* Now update clocks we do read */ 679 bw_params->clk_table.entries[i].fclk_mhz = min_fclk; 680 bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[min_pstate].MemClk; 681 bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[min_pstate].Voltage; 682 bw_params->clk_table.entries[i].dcfclk_mhz = clock_table->DcfClocks[i]; 683 bw_params->clk_table.entries[i].socclk_mhz = clock_table->SocClocks[i]; 684 bw_params->clk_table.entries[i].dispclk_mhz = max_dispclk; 685 bw_params->clk_table.entries[i].dppclk_mhz = max_dppclk; 686 bw_params->clk_table.entries[i].wck_ratio = convert_wck_ratio( 687 clock_table->DfPstateTable[min_pstate].WckRatio); 688 } 689 690 /* Make sure to include at least one entry at highest pstate */ 691 if (max_pstate != min_pstate || i == 0) { 692 if (i > MAX_NUM_DPM_LVL - 1) 693 i = MAX_NUM_DPM_LVL - 1; 694 695 bw_params->clk_table.entries[i].fclk_mhz = max_fclk; 696 bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[max_pstate].MemClk; 697 bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[max_pstate].Voltage; 698 bw_params->clk_table.entries[i].dcfclk_mhz = find_max_clk_value(clock_table->DcfClocks, NUM_DCFCLK_DPM_LEVELS); 699 bw_params->clk_table.entries[i].socclk_mhz = find_max_clk_value(clock_table->SocClocks, NUM_SOCCLK_DPM_LEVELS); 700 bw_params->clk_table.entries[i].dispclk_mhz = max_dispclk; 701 bw_params->clk_table.entries[i].dppclk_mhz = max_dppclk; 702 bw_params->clk_table.entries[i].wck_ratio = convert_wck_ratio( 703 clock_table->DfPstateTable[max_pstate].WckRatio); 704 i++; 705 } 706 bw_params->clk_table.num_entries = i--; 707 708 /* Make sure all highest clocks are included*/ 709 bw_params->clk_table.entries[i].socclk_mhz = find_max_clk_value(clock_table->SocClocks, NUM_SOCCLK_DPM_LEVELS); 710 bw_params->clk_table.entries[i].dispclk_mhz = find_max_clk_value(clock_table->DispClocks, NUM_DISPCLK_DPM_LEVELS); 711 bw_params->clk_table.entries[i].dppclk_mhz = find_max_clk_value(clock_table->DppClocks, NUM_DPPCLK_DPM_LEVELS); 712 ASSERT(clock_table->DcfClocks[i] == find_max_clk_value(clock_table->DcfClocks, NUM_DCFCLK_DPM_LEVELS)); 713 bw_params->clk_table.entries[i].phyclk_mhz = def_max.phyclk_mhz; 714 bw_params->clk_table.entries[i].phyclk_d18_mhz = def_max.phyclk_d18_mhz; 715 bw_params->clk_table.entries[i].dtbclk_mhz = def_max.dtbclk_mhz; 716 717 /* 718 * Set any 0 clocks to max default setting. Not an issue for 719 * power since we aren't doing switching in such case anyway 720 */ 721 for (i = 0; i < bw_params->clk_table.num_entries; i++) { 722 if (!bw_params->clk_table.entries[i].fclk_mhz) { 723 bw_params->clk_table.entries[i].fclk_mhz = def_max.fclk_mhz; 724 bw_params->clk_table.entries[i].memclk_mhz = def_max.memclk_mhz; 725 bw_params->clk_table.entries[i].voltage = def_max.voltage; 726 } 727 if (!bw_params->clk_table.entries[i].dcfclk_mhz) 728 bw_params->clk_table.entries[i].dcfclk_mhz = def_max.dcfclk_mhz; 729 if (!bw_params->clk_table.entries[i].socclk_mhz) 730 bw_params->clk_table.entries[i].socclk_mhz = def_max.socclk_mhz; 731 if (!bw_params->clk_table.entries[i].dispclk_mhz) 732 bw_params->clk_table.entries[i].dispclk_mhz = def_max.dispclk_mhz; 733 if (!bw_params->clk_table.entries[i].dppclk_mhz) 734 bw_params->clk_table.entries[i].dppclk_mhz = def_max.dppclk_mhz; 735 if (!bw_params->clk_table.entries[i].phyclk_mhz) 736 bw_params->clk_table.entries[i].phyclk_mhz = def_max.phyclk_mhz; 737 if (!bw_params->clk_table.entries[i].phyclk_d18_mhz) 738 bw_params->clk_table.entries[i].phyclk_d18_mhz = def_max.phyclk_d18_mhz; 739 if (!bw_params->clk_table.entries[i].dtbclk_mhz) 740 bw_params->clk_table.entries[i].dtbclk_mhz = def_max.dtbclk_mhz; 741 } 742 ASSERT(bw_params->clk_table.entries[i-1].dcfclk_mhz); 743 bw_params->vram_type = bios_info->memory_type; 744 745 bw_params->dram_channel_width_bytes = bios_info->memory_type == 0x22 ? 8 : 4; 746 bw_params->num_channels = bios_info->ma_channel_number ? bios_info->ma_channel_number : 4; 747 748 for (i = 0; i < WM_SET_COUNT; i++) { 749 bw_params->wm_table.entries[i].wm_inst = i; 750 751 if (i >= bw_params->clk_table.num_entries) { 752 bw_params->wm_table.entries[i].valid = false; 753 continue; 754 } 755 756 bw_params->wm_table.entries[i].wm_type = WM_TYPE_PSTATE_CHG; 757 bw_params->wm_table.entries[i].valid = true; 758 } 759} 760 761static struct clk_mgr_funcs dcn314_funcs = { 762 .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, 763 .get_dtb_ref_clk_frequency = dcn31_get_dtb_ref_freq_khz, 764 .update_clocks = dcn314_update_clocks, 765 .init_clocks = dcn314_init_clocks, 766 .enable_pme_wa = dcn314_enable_pme_wa, 767 .are_clock_states_equal = dcn314_are_clock_states_equal, 768 .notify_wm_ranges = dcn314_notify_wm_ranges 769}; 770extern struct clk_mgr_funcs dcn3_fpga_funcs; 771 772static void dcn314_read_ss_info_from_lut(struct clk_mgr_internal *clk_mgr) 773{ 774 uint32_t clock_source; 775 //uint32_t ssc_enable; 776 777 REG_GET(CLK1_CLK2_BYPASS_CNTL, CLK2_BYPASS_SEL, &clock_source); 778 //REG_GET(CLK6_0_CLK6_spll_field_8, spll_ssc_en, &ssc_enable); 779 780 if (dcn314_is_spll_ssc_enabled(&clk_mgr->base) && (clock_source < ARRAY_SIZE(ss_info_table.ss_percentage))) { 781 clk_mgr->dprefclk_ss_percentage = ss_info_table.ss_percentage[clock_source]; 782 783 if (clk_mgr->dprefclk_ss_percentage != 0) { 784 clk_mgr->ss_on_dprefclk = true; 785 clk_mgr->dprefclk_ss_divider = ss_info_table.ss_divider; 786 } 787 } 788} 789 790void dcn314_clk_mgr_construct( 791 struct dc_context *ctx, 792 struct clk_mgr_dcn314 *clk_mgr, 793 struct pp_smu_funcs *pp_smu, 794 struct dccg *dccg) 795{ 796 struct dcn314_smu_dpm_clks smu_dpm_clks = { 0 }; 797 struct clk_log_info log_info = {0}; 798 799 clk_mgr->base.base.ctx = ctx; 800 clk_mgr->base.base.funcs = &dcn314_funcs; 801 802 clk_mgr->base.pp_smu = pp_smu; 803 804 clk_mgr->base.dccg = dccg; 805 clk_mgr->base.dfs_bypass_disp_clk = 0; 806 807 clk_mgr->base.dprefclk_ss_percentage = 0; 808 clk_mgr->base.dprefclk_ss_divider = 1000; 809 clk_mgr->base.ss_on_dprefclk = false; 810 clk_mgr->base.dfs_ref_freq_khz = 48000; 811 812 clk_mgr->smu_wm_set.wm_set = (struct dcn314_watermarks *)dm_helpers_allocate_gpu_mem( 813 clk_mgr->base.base.ctx, 814 DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 815 sizeof(struct dcn314_watermarks), 816 &clk_mgr->smu_wm_set.mc_address.quad_part); 817 818 if (!clk_mgr->smu_wm_set.wm_set) { 819 clk_mgr->smu_wm_set.wm_set = &dummy_wms; 820 clk_mgr->smu_wm_set.mc_address.quad_part = 0; 821 } 822 ASSERT(clk_mgr->smu_wm_set.wm_set); 823 824 smu_dpm_clks.dpm_clks = (DpmClocks314_t *)dm_helpers_allocate_gpu_mem( 825 clk_mgr->base.base.ctx, 826 DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 827 sizeof(DpmClocks314_t), 828 &smu_dpm_clks.mc_address.quad_part); 829 830 if (smu_dpm_clks.dpm_clks == NULL) { 831 smu_dpm_clks.dpm_clks = &dummy_clocks; 832 smu_dpm_clks.mc_address.quad_part = 0; 833 } 834 835 ASSERT(smu_dpm_clks.dpm_clks); 836 837 clk_mgr->base.smu_ver = dcn314_smu_get_smu_version(&clk_mgr->base); 838 839 if (clk_mgr->base.smu_ver) 840 clk_mgr->base.smu_present = true; 841 842 /* TODO: Check we get what we expect during bringup */ 843 clk_mgr->base.base.dentist_vco_freq_khz = get_vco_frequency_from_reg(&clk_mgr->base); 844 845 if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) 846 dcn314_bw_params.wm_table = lpddr5_wm_table; 847 else 848 dcn314_bw_params.wm_table = ddr5_wm_table; 849 850 /* Saved clocks configured at boot for debug purposes */ 851 dcn314_dump_clk_registers(&clk_mgr->base.base.boot_snapshot, 852 &clk_mgr->base.base, &log_info); 853 854 clk_mgr->base.base.dprefclk_khz = 600000; 855 clk_mgr->base.base.clks.ref_dtbclk_khz = 600000; 856 dce_clock_read_ss_info(&clk_mgr->base); 857 dcn314_read_ss_info_from_lut(&clk_mgr->base); 858 /*if bios enabled SS, driver needs to adjust dtb clock, only enable with correct bios*/ 859 860 clk_mgr->base.base.bw_params = &dcn314_bw_params; 861 862 if (clk_mgr->base.base.ctx->dc->debug.pstate_enabled) { 863 int i; 864 865 dcn314_get_dpm_table_from_smu(&clk_mgr->base, &smu_dpm_clks); 866 DC_LOG_SMU("NumDcfClkLevelsEnabled: %d\n" 867 "NumDispClkLevelsEnabled: %d\n" 868 "NumSocClkLevelsEnabled: %d\n" 869 "VcnClkLevelsEnabled: %d\n" 870 "NumDfPst atesEnabled: %d\n" 871 "MinGfxClk: %d\n" 872 "MaxGfxClk: %d\n", 873 smu_dpm_clks.dpm_clks->NumDcfClkLevelsEnabled, 874 smu_dpm_clks.dpm_clks->NumDispClkLevelsEnabled, 875 smu_dpm_clks.dpm_clks->NumSocClkLevelsEnabled, 876 smu_dpm_clks.dpm_clks->VcnClkLevelsEnabled, 877 smu_dpm_clks.dpm_clks->NumDfPstatesEnabled, 878 smu_dpm_clks.dpm_clks->MinGfxClk, 879 smu_dpm_clks.dpm_clks->MaxGfxClk); 880 for (i = 0; i < smu_dpm_clks.dpm_clks->NumDcfClkLevelsEnabled; i++) { 881 DC_LOG_SMU("smu_dpm_clks.dpm_clks->DcfClocks[%d] = %d\n", 882 i, 883 smu_dpm_clks.dpm_clks->DcfClocks[i]); 884 } 885 for (i = 0; i < smu_dpm_clks.dpm_clks->NumDispClkLevelsEnabled; i++) { 886 DC_LOG_SMU("smu_dpm_clks.dpm_clks->DispClocks[%d] = %d\n", 887 i, smu_dpm_clks.dpm_clks->DispClocks[i]); 888 } 889 for (i = 0; i < smu_dpm_clks.dpm_clks->NumSocClkLevelsEnabled; i++) { 890 DC_LOG_SMU("smu_dpm_clks.dpm_clks->SocClocks[%d] = %d\n", 891 i, smu_dpm_clks.dpm_clks->SocClocks[i]); 892 } 893 for (i = 0; i < NUM_SOC_VOLTAGE_LEVELS; i++) 894 DC_LOG_SMU("smu_dpm_clks.dpm_clks->SocVoltage[%d] = %d\n", 895 i, smu_dpm_clks.dpm_clks->SocVoltage[i]); 896 897 for (i = 0; i < NUM_DF_PSTATE_LEVELS; i++) { 898 DC_LOG_SMU("smu_dpm_clks.dpm_clks.DfPstateTable[%d].FClk = %d\n" 899 "smu_dpm_clks.dpm_clks->DfPstateTable[%d].MemClk= %d\n" 900 "smu_dpm_clks.dpm_clks->DfPstateTable[%d].Voltage = %d\n", 901 i, smu_dpm_clks.dpm_clks->DfPstateTable[i].FClk, 902 i, smu_dpm_clks.dpm_clks->DfPstateTable[i].MemClk, 903 i, smu_dpm_clks.dpm_clks->DfPstateTable[i].Voltage); 904 } 905 906 if (ctx->dc_bios->integrated_info && ctx->dc->config.use_default_clock_table == false) { 907 dcn314_clk_mgr_helper_populate_bw_params( 908 &clk_mgr->base, 909 ctx->dc_bios->integrated_info, 910 smu_dpm_clks.dpm_clks); 911 } 912 } 913 914 if (smu_dpm_clks.dpm_clks && smu_dpm_clks.mc_address.quad_part != 0) 915 dm_helpers_free_gpu_mem(clk_mgr->base.base.ctx, DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 916 smu_dpm_clks.dpm_clks); 917} 918 919void dcn314_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr_int) 920{ 921 struct clk_mgr_dcn314 *clk_mgr = TO_CLK_MGR_DCN314(clk_mgr_int); 922 923 if (clk_mgr->smu_wm_set.wm_set && clk_mgr->smu_wm_set.mc_address.quad_part != 0) 924 dm_helpers_free_gpu_mem(clk_mgr_int->base.ctx, DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 925 clk_mgr->smu_wm_set.wm_set); 926}
~/
Read-only file preview.