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.h" 52#include "dcn314_smu.h" 53 54 55#include "logger_types.h" 56#undef DC_LOGGER 57#define DC_LOGGER \ 58 clk_mgr->base.base.ctx->logger 59 60 61#define MAX_INSTANCE 7 62#define MAX_SEGMENT 8 63 64struct IP_BASE_INSTANCE { 65 unsigned int segment[MAX_SEGMENT]; 66}; 67 68struct IP_BASE { 69 struct IP_BASE_INSTANCE instance[MAX_INSTANCE]; 70}; 71 72static const struct IP_BASE CLK_BASE = { { { { 0x00016C00, 0x02401800, 0, 0, 0, 0, 0, 0 } }, 73 { { 0x00016E00, 0x02401C00, 0, 0, 0, 0, 0, 0 } }, 74 { { 0x00017000, 0x02402000, 0, 0, 0, 0, 0, 0 } }, 75 { { 0x00017200, 0x02402400, 0, 0, 0, 0, 0, 0 } }, 76 { { 0x0001B000, 0x0242D800, 0, 0, 0, 0, 0, 0 } }, 77 { { 0x0001B200, 0x0242DC00, 0, 0, 0, 0, 0, 0 } }, 78 { { 0x0001B400, 0x0242E000, 0, 0, 0, 0, 0, 0 } } } }; 79 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; 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 /* 222 * if it is safe to lower, but we are already in the lower state, we don't have to do anything 223 * also if safe to lower is false, we just go in the higher state 224 */ 225 if (safe_to_lower) { 226 if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && 227 new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { 228 dcn314_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); 229 dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); 230 clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; 231 } 232 233 if (clk_mgr_base->clks.dtbclk_en && !new_clocks->dtbclk_en) { 234 dcn314_smu_set_dtbclk(clk_mgr, false); 235 clk_mgr_base->clks.dtbclk_en = new_clocks->dtbclk_en; 236 } 237 /* check that we're not already in lower */ 238 if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_LOW_POWER) { 239 display_count = dcn314_get_active_display_cnt_wa(dc, context); 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 dcn314_disable_otg_wa(clk_mgr_base, context, safe_to_lower, true); 298 299 clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz; 300 dcn314_smu_set_dispclk(clk_mgr, clk_mgr_base->clks.dispclk_khz); 301 dcn314_disable_otg_wa(clk_mgr_base, context, safe_to_lower, false); 302 303 update_dispclk = true; 304 } 305 306 if (dpp_clock_lowered) { 307 // increase per DPP DTO before lowering global dppclk 308 dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower); 309 dcn314_smu_set_dppclk(clk_mgr, clk_mgr_base->clks.dppclk_khz); 310 } else { 311 // increase global DPPCLK before lowering per DPP DTO 312 if (update_dppclk || update_dispclk) 313 dcn314_smu_set_dppclk(clk_mgr, clk_mgr_base->clks.dppclk_khz); 314 // always update dtos unless clock is lowered and not safe to lower 315 if (new_clocks->dppclk_khz >= dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz) 316 dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower); 317 } 318 319 // notify DMCUB of latest clocks 320 memset(&cmd, 0, sizeof(cmd)); 321 cmd.notify_clocks.header.type = DMUB_CMD__CLK_MGR; 322 cmd.notify_clocks.header.sub_type = DMUB_CMD__CLK_MGR_NOTIFY_CLOCKS; 323 cmd.notify_clocks.clocks.dcfclk_khz = clk_mgr_base->clks.dcfclk_khz; 324 cmd.notify_clocks.clocks.dcfclk_deep_sleep_khz = 325 clk_mgr_base->clks.dcfclk_deep_sleep_khz; 326 cmd.notify_clocks.clocks.dispclk_khz = clk_mgr_base->clks.dispclk_khz; 327 cmd.notify_clocks.clocks.dppclk_khz = clk_mgr_base->clks.dppclk_khz; 328 329 dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); 330} 331 332static int get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mgr) 333{ 334 /* get FbMult value */ 335 struct fixed31_32 pll_req; 336 unsigned int fbmult_frac_val = 0; 337 unsigned int fbmult_int_val = 0; 338 339 /* 340 * Register value of fbmult is in 8.16 format, we are converting to 314.32 341 * to leverage the fix point operations available in driver 342 */ 343 344 REG_GET(CLK1_CLK_PLL_REQ, FbMult_frac, &fbmult_frac_val); /* 16 bit fractional part*/ 345 REG_GET(CLK1_CLK_PLL_REQ, FbMult_int, &fbmult_int_val); /* 8 bit integer part */ 346 347 pll_req = dc_fixpt_from_int(fbmult_int_val); 348 349 /* 350 * since fractional part is only 16 bit in register definition but is 32 bit 351 * in our fix point definiton, need to shift left by 16 to obtain correct value 352 */ 353 pll_req.value |= fbmult_frac_val << 16; 354 355 /* multiply by REFCLK period */ 356 pll_req = dc_fixpt_mul_int(pll_req, clk_mgr->dfs_ref_freq_khz); 357 358 /* integer part is now VCO frequency in kHz */ 359 return dc_fixpt_floor(pll_req); 360} 361 362static void dcn314_enable_pme_wa(struct clk_mgr *clk_mgr_base) 363{ 364 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 365 366 dcn314_smu_enable_pme_wa(clk_mgr); 367} 368 369bool dcn314_are_clock_states_equal(struct dc_clocks *a, 370 struct dc_clocks *b) 371{ 372 if (a->dispclk_khz != b->dispclk_khz) 373 return false; 374 else if (a->dppclk_khz != b->dppclk_khz) 375 return false; 376 else if (a->dcfclk_khz != b->dcfclk_khz) 377 return false; 378 else if (a->dcfclk_deep_sleep_khz != b->dcfclk_deep_sleep_khz) 379 return false; 380 else if (a->zstate_support != b->zstate_support) 381 return false; 382 else if (a->dtbclk_en != b->dtbclk_en) 383 return false; 384 385 return true; 386} 387 388static void dcn314_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass, 389 struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info) 390{ 391 return; 392} 393 394static struct clk_bw_params dcn314_bw_params = { 395 .vram_type = Ddr4MemType, 396 .num_channels = 1, 397 .clk_table = { 398 .num_entries = 4, 399 }, 400 401}; 402 403static struct wm_table ddr5_wm_table = { 404 .entries = { 405 { 406 .wm_inst = WM_A, 407 .wm_type = WM_TYPE_PSTATE_CHG, 408 .pstate_latency_us = 11.72, 409 .sr_exit_time_us = 12.5, 410 .sr_enter_plus_exit_time_us = 14.5, 411 .valid = true, 412 }, 413 { 414 .wm_inst = WM_B, 415 .wm_type = WM_TYPE_PSTATE_CHG, 416 .pstate_latency_us = 11.72, 417 .sr_exit_time_us = 12.5, 418 .sr_enter_plus_exit_time_us = 14.5, 419 .valid = true, 420 }, 421 { 422 .wm_inst = WM_C, 423 .wm_type = WM_TYPE_PSTATE_CHG, 424 .pstate_latency_us = 11.72, 425 .sr_exit_time_us = 12.5, 426 .sr_enter_plus_exit_time_us = 14.5, 427 .valid = true, 428 }, 429 { 430 .wm_inst = WM_D, 431 .wm_type = WM_TYPE_PSTATE_CHG, 432 .pstate_latency_us = 11.72, 433 .sr_exit_time_us = 12.5, 434 .sr_enter_plus_exit_time_us = 14.5, 435 .valid = true, 436 }, 437 } 438}; 439 440static struct wm_table lpddr5_wm_table = { 441 .entries = { 442 { 443 .wm_inst = WM_A, 444 .wm_type = WM_TYPE_PSTATE_CHG, 445 .pstate_latency_us = 11.65333, 446 .sr_exit_time_us = 30.0, 447 .sr_enter_plus_exit_time_us = 32.0, 448 .valid = true, 449 }, 450 { 451 .wm_inst = WM_B, 452 .wm_type = WM_TYPE_PSTATE_CHG, 453 .pstate_latency_us = 11.65333, 454 .sr_exit_time_us = 30.0, 455 .sr_enter_plus_exit_time_us = 32.0, 456 .valid = true, 457 }, 458 { 459 .wm_inst = WM_C, 460 .wm_type = WM_TYPE_PSTATE_CHG, 461 .pstate_latency_us = 11.65333, 462 .sr_exit_time_us = 30.0, 463 .sr_enter_plus_exit_time_us = 32.0, 464 .valid = true, 465 }, 466 { 467 .wm_inst = WM_D, 468 .wm_type = WM_TYPE_PSTATE_CHG, 469 .pstate_latency_us = 11.65333, 470 .sr_exit_time_us = 30.0, 471 .sr_enter_plus_exit_time_us = 32.0, 472 .valid = true, 473 }, 474 } 475}; 476 477static DpmClocks314_t dummy_clocks; 478 479static struct dcn314_watermarks dummy_wms = { 0 }; 480 481static struct dcn314_ss_info_table ss_info_table = { 482 .ss_divider = 1000, 483 .ss_percentage = {0, 0, 375, 375, 375} 484}; 485 486static void dcn314_build_watermark_ranges(struct clk_bw_params *bw_params, struct dcn314_watermarks *table) 487{ 488 int i, num_valid_sets; 489 490 num_valid_sets = 0; 491 492 for (i = 0; i < WM_SET_COUNT; i++) { 493 /* skip empty entries, the smu array has no holes*/ 494 if (!bw_params->wm_table.entries[i].valid) 495 continue; 496 497 table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmSetting = bw_params->wm_table.entries[i].wm_inst; 498 table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmType = bw_params->wm_table.entries[i].wm_type; 499 /* We will not select WM based on fclk, so leave it as unconstrained */ 500 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinClock = 0; 501 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxClock = 0xFFFF; 502 503 if (table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmType == WM_TYPE_PSTATE_CHG) { 504 if (i == 0) 505 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinMclk = 0; 506 else { 507 /* add 1 to make it non-overlapping with next lvl */ 508 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinMclk = 509 bw_params->clk_table.entries[i - 1].dcfclk_mhz + 1; 510 } 511 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxMclk = 512 bw_params->clk_table.entries[i].dcfclk_mhz; 513 514 } else { 515 /* unconstrained for memory retraining */ 516 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinClock = 0; 517 table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxClock = 0xFFFF; 518 519 /* Modify previous watermark range to cover up to max */ 520 table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxClock = 0xFFFF; 521 } 522 num_valid_sets++; 523 } 524 525 ASSERT(num_valid_sets != 0); /* Must have at least one set of valid watermarks */ 526 527 /* modify the min and max to make sure we cover the whole range*/ 528 table->WatermarkRow[WM_DCFCLK][0].MinMclk = 0; 529 table->WatermarkRow[WM_DCFCLK][0].MinClock = 0; 530 table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxMclk = 0xFFFF; 531 table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxClock = 0xFFFF; 532 533 /* This is for writeback only, does not matter currently as no writeback support*/ 534 table->WatermarkRow[WM_SOCCLK][0].WmSetting = WM_A; 535 table->WatermarkRow[WM_SOCCLK][0].MinClock = 0; 536 table->WatermarkRow[WM_SOCCLK][0].MaxClock = 0xFFFF; 537 table->WatermarkRow[WM_SOCCLK][0].MinMclk = 0; 538 table->WatermarkRow[WM_SOCCLK][0].MaxMclk = 0xFFFF; 539} 540 541static void dcn314_notify_wm_ranges(struct clk_mgr *clk_mgr_base) 542{ 543 struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); 544 struct clk_mgr_dcn314 *clk_mgr_dcn314 = TO_CLK_MGR_DCN314(clk_mgr); 545 struct dcn314_watermarks *table = clk_mgr_dcn314->smu_wm_set.wm_set; 546 547 if (!clk_mgr->smu_ver) 548 return; 549 550 if (!table || clk_mgr_dcn314->smu_wm_set.mc_address.quad_part == 0) 551 return; 552 553 memset(table, 0, sizeof(*table)); 554 555 dcn314_build_watermark_ranges(clk_mgr_base->bw_params, table); 556 557 dcn314_smu_set_dram_addr_high(clk_mgr, 558 clk_mgr_dcn314->smu_wm_set.mc_address.high_part); 559 dcn314_smu_set_dram_addr_low(clk_mgr, 560 clk_mgr_dcn314->smu_wm_set.mc_address.low_part); 561 dcn314_smu_transfer_wm_table_dram_2_smu(clk_mgr); 562} 563 564static void dcn314_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, 565 struct dcn314_smu_dpm_clks *smu_dpm_clks) 566{ 567 DpmClocks314_t *table = smu_dpm_clks->dpm_clks; 568 569 if (!clk_mgr->smu_ver) 570 return; 571 572 if (!table || smu_dpm_clks->mc_address.quad_part == 0) 573 return; 574 575 memset(table, 0, sizeof(*table)); 576 577 dcn314_smu_set_dram_addr_high(clk_mgr, 578 smu_dpm_clks->mc_address.high_part); 579 dcn314_smu_set_dram_addr_low(clk_mgr, 580 smu_dpm_clks->mc_address.low_part); 581 dcn314_smu_transfer_dpm_table_smu_2_dram(clk_mgr); 582} 583 584static inline bool is_valid_clock_value(uint32_t clock_value) 585{ 586 return clock_value > 1 && clock_value < 100000; 587} 588 589static unsigned int convert_wck_ratio(uint8_t wck_ratio) 590{ 591 switch (wck_ratio) { 592 case WCK_RATIO_1_2: 593 return 2; 594 595 case WCK_RATIO_1_4: 596 return 4; 597 598 default: 599 break; 600 } 601 return 1; 602} 603 604static uint32_t find_max_clk_value(const uint32_t clocks[], uint32_t num_clocks) 605{ 606 uint32_t max = 0; 607 int i; 608 609 for (i = 0; i < num_clocks; ++i) { 610 if (clocks[i] > max) 611 max = clocks[i]; 612 } 613 614 return max; 615} 616 617static void dcn314_clk_mgr_helper_populate_bw_params(struct clk_mgr_internal *clk_mgr, 618 struct integrated_info *bios_info, 619 const DpmClocks314_t *clock_table) 620{ 621 struct clk_bw_params *bw_params = clk_mgr->base.bw_params; 622 struct clk_limit_table_entry def_max = bw_params->clk_table.entries[bw_params->clk_table.num_entries - 1]; 623 uint32_t max_pstate = 0, max_fclk = 0, min_pstate = 0, max_dispclk = 0, max_dppclk = 0; 624 int i; 625 626 /* Find highest valid fclk pstate */ 627 for (i = 0; i < clock_table->NumDfPstatesEnabled; i++) { 628 if (is_valid_clock_value(clock_table->DfPstateTable[i].FClk) && 629 clock_table->DfPstateTable[i].FClk > max_fclk) { 630 max_fclk = clock_table->DfPstateTable[i].FClk; 631 max_pstate = i; 632 } 633 } 634 635 /* We expect the table to contain at least one valid fclk entry. */ 636 ASSERT(is_valid_clock_value(max_fclk)); 637 638 /* Dispclk and dppclk can be max at any voltage, same number of levels for both */ 639 if (clock_table->NumDispClkLevelsEnabled <= NUM_DISPCLK_DPM_LEVELS && 640 clock_table->NumDispClkLevelsEnabled <= NUM_DPPCLK_DPM_LEVELS) { 641 max_dispclk = find_max_clk_value(clock_table->DispClocks, clock_table->NumDispClkLevelsEnabled); 642 max_dppclk = find_max_clk_value(clock_table->DppClocks, clock_table->NumDispClkLevelsEnabled); 643 } else { 644 /* Invalid number of entries in the table from PMFW. */ 645 ASSERT(0); 646 } 647 648 /* Base the clock table on dcfclk, need at least one entry regardless of pmfw table */ 649 for (i = 0; i < clock_table->NumDcfClkLevelsEnabled; i++) { 650 uint32_t min_fclk = clock_table->DfPstateTable[0].FClk; 651 int j; 652 653 for (j = 1; j < clock_table->NumDfPstatesEnabled; j++) { 654 if (is_valid_clock_value(clock_table->DfPstateTable[j].FClk) && 655 clock_table->DfPstateTable[j].FClk < min_fclk && 656 clock_table->DfPstateTable[j].Voltage <= clock_table->SocVoltage[i]) { 657 min_fclk = clock_table->DfPstateTable[j].FClk; 658 min_pstate = j; 659 } 660 } 661 662 /* First search defaults for the clocks we don't read using closest lower or equal default dcfclk */ 663 for (j = bw_params->clk_table.num_entries - 1; j > 0; j--) 664 if (bw_params->clk_table.entries[j].dcfclk_mhz <= clock_table->DcfClocks[i]) 665 break; 666 667 bw_params->clk_table.entries[i].phyclk_mhz = bw_params->clk_table.entries[j].phyclk_mhz; 668 bw_params->clk_table.entries[i].phyclk_d18_mhz = bw_params->clk_table.entries[j].phyclk_d18_mhz; 669 bw_params->clk_table.entries[i].dtbclk_mhz = bw_params->clk_table.entries[j].dtbclk_mhz; 670 671 /* Now update clocks we do read */ 672 bw_params->clk_table.entries[i].fclk_mhz = min_fclk; 673 bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[min_pstate].MemClk; 674 bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[min_pstate].Voltage; 675 bw_params->clk_table.entries[i].dcfclk_mhz = clock_table->DcfClocks[i]; 676 bw_params->clk_table.entries[i].socclk_mhz = clock_table->SocClocks[i]; 677 bw_params->clk_table.entries[i].dispclk_mhz = max_dispclk; 678 bw_params->clk_table.entries[i].dppclk_mhz = max_dppclk; 679 bw_params->clk_table.entries[i].wck_ratio = convert_wck_ratio( 680 clock_table->DfPstateTable[min_pstate].WckRatio); 681 } 682 683 /* Make sure to include at least one entry at highest pstate */ 684 if (max_pstate != min_pstate || i == 0) { 685 if (i > MAX_NUM_DPM_LVL - 1) 686 i = MAX_NUM_DPM_LVL - 1; 687 688 bw_params->clk_table.entries[i].fclk_mhz = max_fclk; 689 bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[max_pstate].MemClk; 690 bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[max_pstate].Voltage; 691 bw_params->clk_table.entries[i].dcfclk_mhz = find_max_clk_value(clock_table->DcfClocks, NUM_DCFCLK_DPM_LEVELS); 692 bw_params->clk_table.entries[i].socclk_mhz = find_max_clk_value(clock_table->SocClocks, NUM_SOCCLK_DPM_LEVELS); 693 bw_params->clk_table.entries[i].dispclk_mhz = max_dispclk; 694 bw_params->clk_table.entries[i].dppclk_mhz = max_dppclk; 695 bw_params->clk_table.entries[i].wck_ratio = convert_wck_ratio( 696 clock_table->DfPstateTable[max_pstate].WckRatio); 697 i++; 698 } 699 bw_params->clk_table.num_entries = i--; 700 701 /* Make sure all highest clocks are included*/ 702 bw_params->clk_table.entries[i].socclk_mhz = find_max_clk_value(clock_table->SocClocks, NUM_SOCCLK_DPM_LEVELS); 703 bw_params->clk_table.entries[i].dispclk_mhz = find_max_clk_value(clock_table->DispClocks, NUM_DISPCLK_DPM_LEVELS); 704 bw_params->clk_table.entries[i].dppclk_mhz = find_max_clk_value(clock_table->DppClocks, NUM_DPPCLK_DPM_LEVELS); 705 ASSERT(clock_table->DcfClocks[i] == find_max_clk_value(clock_table->DcfClocks, NUM_DCFCLK_DPM_LEVELS)); 706 bw_params->clk_table.entries[i].phyclk_mhz = def_max.phyclk_mhz; 707 bw_params->clk_table.entries[i].phyclk_d18_mhz = def_max.phyclk_d18_mhz; 708 bw_params->clk_table.entries[i].dtbclk_mhz = def_max.dtbclk_mhz; 709 710 /* 711 * Set any 0 clocks to max default setting. Not an issue for 712 * power since we aren't doing switching in such case anyway 713 */ 714 for (i = 0; i < bw_params->clk_table.num_entries; i++) { 715 if (!bw_params->clk_table.entries[i].fclk_mhz) { 716 bw_params->clk_table.entries[i].fclk_mhz = def_max.fclk_mhz; 717 bw_params->clk_table.entries[i].memclk_mhz = def_max.memclk_mhz; 718 bw_params->clk_table.entries[i].voltage = def_max.voltage; 719 } 720 if (!bw_params->clk_table.entries[i].dcfclk_mhz) 721 bw_params->clk_table.entries[i].dcfclk_mhz = def_max.dcfclk_mhz; 722 if (!bw_params->clk_table.entries[i].socclk_mhz) 723 bw_params->clk_table.entries[i].socclk_mhz = def_max.socclk_mhz; 724 if (!bw_params->clk_table.entries[i].dispclk_mhz) 725 bw_params->clk_table.entries[i].dispclk_mhz = def_max.dispclk_mhz; 726 if (!bw_params->clk_table.entries[i].dppclk_mhz) 727 bw_params->clk_table.entries[i].dppclk_mhz = def_max.dppclk_mhz; 728 if (!bw_params->clk_table.entries[i].phyclk_mhz) 729 bw_params->clk_table.entries[i].phyclk_mhz = def_max.phyclk_mhz; 730 if (!bw_params->clk_table.entries[i].phyclk_d18_mhz) 731 bw_params->clk_table.entries[i].phyclk_d18_mhz = def_max.phyclk_d18_mhz; 732 if (!bw_params->clk_table.entries[i].dtbclk_mhz) 733 bw_params->clk_table.entries[i].dtbclk_mhz = def_max.dtbclk_mhz; 734 } 735 ASSERT(bw_params->clk_table.entries[i-1].dcfclk_mhz); 736 bw_params->vram_type = bios_info->memory_type; 737 738 bw_params->dram_channel_width_bytes = bios_info->memory_type == 0x22 ? 8 : 4; 739 bw_params->num_channels = bios_info->ma_channel_number ? bios_info->ma_channel_number : 4; 740 741 for (i = 0; i < WM_SET_COUNT; i++) { 742 bw_params->wm_table.entries[i].wm_inst = i; 743 744 if (i >= bw_params->clk_table.num_entries) { 745 bw_params->wm_table.entries[i].valid = false; 746 continue; 747 } 748 749 bw_params->wm_table.entries[i].wm_type = WM_TYPE_PSTATE_CHG; 750 bw_params->wm_table.entries[i].valid = true; 751 } 752} 753 754static struct clk_mgr_funcs dcn314_funcs = { 755 .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, 756 .get_dtb_ref_clk_frequency = dcn31_get_dtb_ref_freq_khz, 757 .update_clocks = dcn314_update_clocks, 758 .init_clocks = dcn314_init_clocks, 759 .enable_pme_wa = dcn314_enable_pme_wa, 760 .are_clock_states_equal = dcn314_are_clock_states_equal, 761 .notify_wm_ranges = dcn314_notify_wm_ranges 762}; 763extern struct clk_mgr_funcs dcn3_fpga_funcs; 764 765static void dcn314_read_ss_info_from_lut(struct clk_mgr_internal *clk_mgr) 766{ 767 uint32_t clock_source; 768 //uint32_t ssc_enable; 769 770 REG_GET(CLK1_CLK2_BYPASS_CNTL, CLK2_BYPASS_SEL, &clock_source); 771 //REG_GET(CLK6_0_CLK6_spll_field_8, spll_ssc_en, &ssc_enable); 772 773 if (dcn314_is_spll_ssc_enabled(&clk_mgr->base) && (clock_source < ARRAY_SIZE(ss_info_table.ss_percentage))) { 774 clk_mgr->dprefclk_ss_percentage = ss_info_table.ss_percentage[clock_source]; 775 776 if (clk_mgr->dprefclk_ss_percentage != 0) { 777 clk_mgr->ss_on_dprefclk = true; 778 clk_mgr->dprefclk_ss_divider = ss_info_table.ss_divider; 779 } 780 } 781} 782 783void dcn314_clk_mgr_construct( 784 struct dc_context *ctx, 785 struct clk_mgr_dcn314 *clk_mgr, 786 struct pp_smu_funcs *pp_smu, 787 struct dccg *dccg) 788{ 789 struct dcn314_smu_dpm_clks smu_dpm_clks = { 0 }; 790 struct clk_log_info log_info = {0}; 791 792 clk_mgr->base.base.ctx = ctx; 793 clk_mgr->base.base.funcs = &dcn314_funcs; 794 795 clk_mgr->base.pp_smu = pp_smu; 796 797 clk_mgr->base.dccg = dccg; 798 clk_mgr->base.dfs_bypass_disp_clk = 0; 799 800 clk_mgr->base.dprefclk_ss_percentage = 0; 801 clk_mgr->base.dprefclk_ss_divider = 1000; 802 clk_mgr->base.ss_on_dprefclk = false; 803 clk_mgr->base.dfs_ref_freq_khz = 48000; 804 805 clk_mgr->smu_wm_set.wm_set = (struct dcn314_watermarks *)dm_helpers_allocate_gpu_mem( 806 clk_mgr->base.base.ctx, 807 DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 808 sizeof(struct dcn314_watermarks), 809 &clk_mgr->smu_wm_set.mc_address.quad_part); 810 811 if (!clk_mgr->smu_wm_set.wm_set) { 812 clk_mgr->smu_wm_set.wm_set = &dummy_wms; 813 clk_mgr->smu_wm_set.mc_address.quad_part = 0; 814 } 815 ASSERT(clk_mgr->smu_wm_set.wm_set); 816 817 smu_dpm_clks.dpm_clks = (DpmClocks314_t *)dm_helpers_allocate_gpu_mem( 818 clk_mgr->base.base.ctx, 819 DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 820 sizeof(DpmClocks314_t), 821 &smu_dpm_clks.mc_address.quad_part); 822 823 if (smu_dpm_clks.dpm_clks == NULL) { 824 smu_dpm_clks.dpm_clks = &dummy_clocks; 825 smu_dpm_clks.mc_address.quad_part = 0; 826 } 827 828 ASSERT(smu_dpm_clks.dpm_clks); 829 830 clk_mgr->base.smu_ver = dcn314_smu_get_smu_version(&clk_mgr->base); 831 832 if (clk_mgr->base.smu_ver) 833 clk_mgr->base.smu_present = true; 834 835 /* TODO: Check we get what we expect during bringup */ 836 clk_mgr->base.base.dentist_vco_freq_khz = get_vco_frequency_from_reg(&clk_mgr->base); 837 838 if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) 839 dcn314_bw_params.wm_table = lpddr5_wm_table; 840 else 841 dcn314_bw_params.wm_table = ddr5_wm_table; 842 843 /* Saved clocks configured at boot for debug purposes */ 844 dcn314_dump_clk_registers(&clk_mgr->base.base.boot_snapshot, 845 &clk_mgr->base.base, &log_info); 846 847 clk_mgr->base.base.dprefclk_khz = 600000; 848 clk_mgr->base.base.clks.ref_dtbclk_khz = 600000; 849 dce_clock_read_ss_info(&clk_mgr->base); 850 dcn314_read_ss_info_from_lut(&clk_mgr->base); 851 /*if bios enabled SS, driver needs to adjust dtb clock, only enable with correct bios*/ 852 853 clk_mgr->base.base.bw_params = &dcn314_bw_params; 854 855 if (clk_mgr->base.base.ctx->dc->debug.pstate_enabled) { 856 int i; 857 858 dcn314_get_dpm_table_from_smu(&clk_mgr->base, &smu_dpm_clks); 859 DC_LOG_SMU("NumDcfClkLevelsEnabled: %d\n" 860 "NumDispClkLevelsEnabled: %d\n" 861 "NumSocClkLevelsEnabled: %d\n" 862 "VcnClkLevelsEnabled: %d\n" 863 "NumDfPst atesEnabled: %d\n" 864 "MinGfxClk: %d\n" 865 "MaxGfxClk: %d\n", 866 smu_dpm_clks.dpm_clks->NumDcfClkLevelsEnabled, 867 smu_dpm_clks.dpm_clks->NumDispClkLevelsEnabled, 868 smu_dpm_clks.dpm_clks->NumSocClkLevelsEnabled, 869 smu_dpm_clks.dpm_clks->VcnClkLevelsEnabled, 870 smu_dpm_clks.dpm_clks->NumDfPstatesEnabled, 871 smu_dpm_clks.dpm_clks->MinGfxClk, 872 smu_dpm_clks.dpm_clks->MaxGfxClk); 873 for (i = 0; i < smu_dpm_clks.dpm_clks->NumDcfClkLevelsEnabled; i++) { 874 DC_LOG_SMU("smu_dpm_clks.dpm_clks->DcfClocks[%d] = %d\n", 875 i, 876 smu_dpm_clks.dpm_clks->DcfClocks[i]); 877 } 878 for (i = 0; i < smu_dpm_clks.dpm_clks->NumDispClkLevelsEnabled; i++) { 879 DC_LOG_SMU("smu_dpm_clks.dpm_clks->DispClocks[%d] = %d\n", 880 i, smu_dpm_clks.dpm_clks->DispClocks[i]); 881 } 882 for (i = 0; i < smu_dpm_clks.dpm_clks->NumSocClkLevelsEnabled; i++) { 883 DC_LOG_SMU("smu_dpm_clks.dpm_clks->SocClocks[%d] = %d\n", 884 i, smu_dpm_clks.dpm_clks->SocClocks[i]); 885 } 886 for (i = 0; i < NUM_SOC_VOLTAGE_LEVELS; i++) 887 DC_LOG_SMU("smu_dpm_clks.dpm_clks->SocVoltage[%d] = %d\n", 888 i, smu_dpm_clks.dpm_clks->SocVoltage[i]); 889 890 for (i = 0; i < NUM_DF_PSTATE_LEVELS; i++) { 891 DC_LOG_SMU("smu_dpm_clks.dpm_clks.DfPstateTable[%d].FClk = %d\n" 892 "smu_dpm_clks.dpm_clks->DfPstateTable[%d].MemClk= %d\n" 893 "smu_dpm_clks.dpm_clks->DfPstateTable[%d].Voltage = %d\n", 894 i, smu_dpm_clks.dpm_clks->DfPstateTable[i].FClk, 895 i, smu_dpm_clks.dpm_clks->DfPstateTable[i].MemClk, 896 i, smu_dpm_clks.dpm_clks->DfPstateTable[i].Voltage); 897 } 898 899 if (ctx->dc_bios && ctx->dc_bios->integrated_info && ctx->dc->config.use_default_clock_table == false) { 900 dcn314_clk_mgr_helper_populate_bw_params( 901 &clk_mgr->base, 902 ctx->dc_bios->integrated_info, 903 smu_dpm_clks.dpm_clks); 904 } 905 } 906 907 if (smu_dpm_clks.dpm_clks && smu_dpm_clks.mc_address.quad_part != 0) 908 dm_helpers_free_gpu_mem(clk_mgr->base.base.ctx, DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 909 smu_dpm_clks.dpm_clks); 910} 911 912void dcn314_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr_int) 913{ 914 struct clk_mgr_dcn314 *clk_mgr = TO_CLK_MGR_DCN314(clk_mgr_int); 915 916 if (clk_mgr->smu_wm_set.wm_set && clk_mgr->smu_wm_set.mc_address.quad_part != 0) 917 dm_helpers_free_gpu_mem(clk_mgr_int->base.ctx, DC_MEM_ALLOC_TYPE_FRAME_BUFFER, 918 clk_mgr->smu_wm_set.wm_set); 919} 920