1 /*
2  * Copyright 2020 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: AMD
23  *
24  */
25 
26 #include "dccg.h"
27 #include "clk_mgr_internal.h"
28 
29 // For dce12_get_dp_ref_freq_khz
30 #include "dce100/dce_clk_mgr.h"
31 
32 // For dcn20_update_clocks_update_dpp_dto
33 #include "dcn20/dcn20_clk_mgr.h"
34 
35 
36 
37 #include "vg_clk_mgr.h"
38 #include "reg_helper.h"
39 #include "core_types.h"
40 #include "dm_helpers.h"
41 
42 #include "atomfirmware.h"
43 #include "vangogh_ip_offset.h"
44 #include "clk/clk_11_5_0_offset.h"
45 #include "clk/clk_11_5_0_sh_mask.h"
46 
47 /* Constants */
48 
49 #define LPDDR_MEM_RETRAIN_LATENCY 4.977 /* Number obtained from LPDDR4 Training Counter Requirement doc */
50 
51 /* Macros */
52 
53 #define REG(reg_name) \
54 	(CLK_BASE.instance[0].segment[mm ## reg_name ## _BASE_IDX] + mm ## reg_name)
55 
56 /* TODO: evaluate how to lower or disable all dcn clocks in screen off case */
57 int vg_get_active_display_cnt_wa(
58 		struct dc *dc,
59 		struct dc_state *context)
60 {
61 	int i, display_count;
62 	bool tmds_present = false;
63 
64 	display_count = 0;
65 	for (i = 0; i < context->stream_count; i++) {
66 		const struct dc_stream_state *stream = context->streams[i];
67 
68 		if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
69 				stream->signal == SIGNAL_TYPE_DVI_SINGLE_LINK ||
70 				stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK)
71 			tmds_present = true;
72 	}
73 
74 	for (i = 0; i < dc->link_count; i++) {
75 		const struct dc_link *link = dc->links[i];
76 
77 		/* abusing the fact that the dig and phy are coupled to see if the phy is enabled */
78 		if (link->link_enc->funcs->is_dig_enabled &&
79 				link->link_enc->funcs->is_dig_enabled(link->link_enc))
80 			display_count++;
81 	}
82 
83 	/* WA for hang on HDMI after display off back back on*/
84 	if (display_count == 0 && tmds_present)
85 		display_count = 1;
86 
87 	return display_count;
88 }
89 
90 void vg_update_clocks(struct clk_mgr *clk_mgr_base,
91 			struct dc_state *context,
92 			bool safe_to_lower)
93 {
94 	struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
95 	struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk;
96 	struct dc *dc = clk_mgr_base->ctx->dc;
97 	int display_count;
98 	bool update_dppclk = false;
99 	bool update_dispclk = false;
100 	bool dpp_clock_lowered = false;
101 
102 	if (dc->work_arounds.skip_clock_update)
103 		return;
104 
105 	/*
106 	 * if it is safe to lower, but we are already in the lower state, we don't have to do anything
107 	 * also if safe to lower is false, we just go in the higher state
108 	 */
109 	if (safe_to_lower) {
110 		/* check that we're not already in lower */
111 		if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_LOW_POWER) {
112 
113 			display_count = vg_get_active_display_cnt_wa(dc, context);
114 			/* if we can go lower, go lower */
115 			if (display_count == 0) {
116 				union display_idle_optimization_u idle_info = { 0 };
117 
118 				idle_info.idle_info.df_request_disabled = 1;
119 				idle_info.idle_info.phy_ref_clk_off = 1;
120 
121 				dcn301_smu_set_display_idle_optimization(clk_mgr, idle_info.data);
122 				/* update power state */
123 				clk_mgr_base->clks.pwr_state = DCN_PWR_STATE_LOW_POWER;
124 			}
125 		}
126 	} else {
127 		/* check that we're not already in D0 */
128 		if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_MISSION_MODE) {
129 			union display_idle_optimization_u idle_info = { 0 };
130 
131 			dcn301_smu_set_display_idle_optimization(clk_mgr, idle_info.data);
132 			/* update power state */
133 			clk_mgr_base->clks.pwr_state = DCN_PWR_STATE_MISSION_MODE;
134 		}
135 	}
136 
137 	if (should_set_clock(safe_to_lower, new_clocks->dcfclk_khz, clk_mgr_base->clks.dcfclk_khz)) {
138 		clk_mgr_base->clks.dcfclk_khz = new_clocks->dcfclk_khz;
139 		dcn301_smu_set_hard_min_dcfclk(clk_mgr, clk_mgr_base->clks.dcfclk_khz);
140 	}
141 
142 	if (should_set_clock(safe_to_lower,
143 			new_clocks->dcfclk_deep_sleep_khz, clk_mgr_base->clks.dcfclk_deep_sleep_khz)) {
144 		clk_mgr_base->clks.dcfclk_deep_sleep_khz = new_clocks->dcfclk_deep_sleep_khz;
145 		dcn301_smu_set_min_deep_sleep_dcfclk(clk_mgr, clk_mgr_base->clks.dcfclk_deep_sleep_khz);
146 	}
147 
148 	// workaround: Limit dppclk to 100Mhz to avoid lower eDP panel switch to plus 4K monitor underflow.
149 	if (!IS_DIAG_DC(dc->ctx->dce_environment)) {
150 		if (new_clocks->dppclk_khz < 100000)
151 			new_clocks->dppclk_khz = 100000;
152 	}
153 
154 	if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->base.clks.dppclk_khz)) {
155 		if (clk_mgr->base.clks.dppclk_khz > new_clocks->dppclk_khz)
156 			dpp_clock_lowered = true;
157 		clk_mgr_base->clks.dppclk_khz = new_clocks->dppclk_khz;
158 		update_dppclk = true;
159 	}
160 
161 	if (should_set_clock(safe_to_lower, new_clocks->dispclk_khz, clk_mgr_base->clks.dispclk_khz)) {
162 		clk_mgr_base->clks.dispclk_khz = new_clocks->dispclk_khz;
163 		dcn301_smu_set_dispclk(clk_mgr, clk_mgr_base->clks.dispclk_khz);
164 
165 		update_dispclk = true;
166 	}
167 
168 	if (dpp_clock_lowered) {
169 		// increase per DPP DTO before lowering global dppclk
170 		dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower);
171 		dcn301_smu_set_dppclk(clk_mgr, clk_mgr_base->clks.dppclk_khz);
172 	} else {
173 		// increase global DPPCLK before lowering per DPP DTO
174 		if (update_dppclk || update_dispclk)
175 			dcn301_smu_set_dppclk(clk_mgr, clk_mgr_base->clks.dppclk_khz);
176 		// always update dtos unless clock is lowered and not safe to lower
177 		if (new_clocks->dppclk_khz >= dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz)
178 			dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower);
179 	}
180 }
181 
182 
183 static int get_vco_frequency_from_reg(struct clk_mgr_internal *clk_mgr)
184 {
185 	/* get FbMult value */
186 	struct fixed31_32 pll_req;
187 	unsigned int fbmult_frac_val = 0;
188 	unsigned int fbmult_int_val = 0;
189 
190 
191 	/*
192 	 * Register value of fbmult is in 8.16 format, we are converting to 31.32
193 	 * to leverage the fix point operations available in driver
194 	 */
195 
196 	REG_GET(CLK1_0_CLK1_CLK_PLL_REQ, FbMult_frac, &fbmult_frac_val); /* 16 bit fractional part*/
197 	REG_GET(CLK1_0_CLK1_CLK_PLL_REQ, FbMult_int, &fbmult_int_val); /* 8 bit integer part */
198 
199 	pll_req = dc_fixpt_from_int(fbmult_int_val);
200 
201 	/*
202 	 * since fractional part is only 16 bit in register definition but is 32 bit
203 	 * in our fix point definiton, need to shift left by 16 to obtain correct value
204 	 */
205 	pll_req.value |= fbmult_frac_val << 16;
206 
207 	/* multiply by REFCLK period */
208 	pll_req = dc_fixpt_mul_int(pll_req, clk_mgr->dfs_ref_freq_khz);
209 
210 	/* integer part is now VCO frequency in kHz */
211 	return dc_fixpt_floor(pll_req);
212 }
213 
214 static void vg_dump_clk_registers_internal(struct dcn301_clk_internal *internal, struct clk_mgr *clk_mgr_base)
215 {
216 	struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
217 
218 	internal->CLK1_CLK3_CURRENT_CNT = REG_READ(CLK1_0_CLK1_CLK3_CURRENT_CNT);
219 	internal->CLK1_CLK3_BYPASS_CNTL = REG_READ(CLK1_0_CLK1_CLK3_BYPASS_CNTL);
220 
221 	internal->CLK1_CLK3_DS_CNTL = REG_READ(CLK1_0_CLK1_CLK3_DS_CNTL);	//dcf deep sleep divider
222 	internal->CLK1_CLK3_ALLOW_DS = REG_READ(CLK1_0_CLK1_CLK3_ALLOW_DS);
223 
224 	internal->CLK1_CLK1_CURRENT_CNT = REG_READ(CLK1_0_CLK1_CLK1_CURRENT_CNT);
225 	internal->CLK1_CLK1_BYPASS_CNTL = REG_READ(CLK1_0_CLK1_CLK1_BYPASS_CNTL);
226 
227 	internal->CLK1_CLK2_CURRENT_CNT = REG_READ(CLK1_0_CLK1_CLK2_CURRENT_CNT);
228 	internal->CLK1_CLK2_BYPASS_CNTL = REG_READ(CLK1_0_CLK1_CLK2_BYPASS_CNTL);
229 
230 	internal->CLK1_CLK0_CURRENT_CNT = REG_READ(CLK1_0_CLK1_CLK0_CURRENT_CNT);
231 	internal->CLK1_CLK0_BYPASS_CNTL = REG_READ(CLK1_0_CLK1_CLK0_BYPASS_CNTL);
232 }
233 
234 /* This function collect raw clk register values */
235 static void vg_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass,
236 		struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info)
237 {
238 	struct dcn301_clk_internal internal = {0};
239 	char *bypass_clks[5] = {"0x0 DFS", "0x1 REFCLK", "0x2 ERROR", "0x3 400 FCH", "0x4 600 FCH"};
240 	unsigned int chars_printed = 0;
241 	unsigned int remaining_buffer = log_info->bufSize;
242 
243 	vg_dump_clk_registers_internal(&internal, clk_mgr_base);
244 
245 	regs_and_bypass->dcfclk = internal.CLK1_CLK3_CURRENT_CNT / 10;
246 	regs_and_bypass->dcf_deep_sleep_divider = internal.CLK1_CLK3_DS_CNTL / 10;
247 	regs_and_bypass->dcf_deep_sleep_allow = internal.CLK1_CLK3_ALLOW_DS;
248 	regs_and_bypass->dprefclk = internal.CLK1_CLK2_CURRENT_CNT / 10;
249 	regs_and_bypass->dispclk = internal.CLK1_CLK0_CURRENT_CNT / 10;
250 	regs_and_bypass->dppclk = internal.CLK1_CLK1_CURRENT_CNT / 10;
251 
252 	regs_and_bypass->dppclk_bypass = internal.CLK1_CLK1_BYPASS_CNTL & 0x0007;
253 	if (regs_and_bypass->dppclk_bypass < 0 || regs_and_bypass->dppclk_bypass > 4)
254 		regs_and_bypass->dppclk_bypass = 0;
255 	regs_and_bypass->dcfclk_bypass = internal.CLK1_CLK3_BYPASS_CNTL & 0x0007;
256 	if (regs_and_bypass->dcfclk_bypass < 0 || regs_and_bypass->dcfclk_bypass > 4)
257 		regs_and_bypass->dcfclk_bypass = 0;
258 	regs_and_bypass->dispclk_bypass = internal.CLK1_CLK0_BYPASS_CNTL & 0x0007;
259 	if (regs_and_bypass->dispclk_bypass < 0 || regs_and_bypass->dispclk_bypass > 4)
260 		regs_and_bypass->dispclk_bypass = 0;
261 	regs_and_bypass->dprefclk_bypass = internal.CLK1_CLK2_BYPASS_CNTL & 0x0007;
262 	if (regs_and_bypass->dprefclk_bypass < 0 || regs_and_bypass->dprefclk_bypass > 4)
263 		regs_and_bypass->dprefclk_bypass = 0;
264 
265 	if (log_info->enabled) {
266 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "clk_type,clk_value,deepsleep_cntl,deepsleep_allow,bypass\n");
267 		remaining_buffer -= chars_printed;
268 		*log_info->sum_chars_printed += chars_printed;
269 		log_info->pBuf += chars_printed;
270 
271 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "dcfclk,%d,%d,%d,%s\n",
272 			regs_and_bypass->dcfclk,
273 			regs_and_bypass->dcf_deep_sleep_divider,
274 			regs_and_bypass->dcf_deep_sleep_allow,
275 			bypass_clks[(int) regs_and_bypass->dcfclk_bypass]);
276 		remaining_buffer -= chars_printed;
277 		*log_info->sum_chars_printed += chars_printed;
278 		log_info->pBuf += chars_printed;
279 
280 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "dprefclk,%d,N/A,N/A,%s\n",
281 			regs_and_bypass->dprefclk,
282 			bypass_clks[(int) regs_and_bypass->dprefclk_bypass]);
283 		remaining_buffer -= chars_printed;
284 		*log_info->sum_chars_printed += chars_printed;
285 		log_info->pBuf += chars_printed;
286 
287 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "dispclk,%d,N/A,N/A,%s\n",
288 			regs_and_bypass->dispclk,
289 			bypass_clks[(int) regs_and_bypass->dispclk_bypass]);
290 		remaining_buffer -= chars_printed;
291 		*log_info->sum_chars_printed += chars_printed;
292 		log_info->pBuf += chars_printed;
293 
294 		//split
295 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "SPLIT\n");
296 		remaining_buffer -= chars_printed;
297 		*log_info->sum_chars_printed += chars_printed;
298 		log_info->pBuf += chars_printed;
299 
300 		// REGISTER VALUES
301 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "reg_name,value,clk_type\n");
302 		remaining_buffer -= chars_printed;
303 		*log_info->sum_chars_printed += chars_printed;
304 		log_info->pBuf += chars_printed;
305 
306 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK3_CURRENT_CNT,%d,dcfclk\n",
307 				internal.CLK1_CLK3_CURRENT_CNT);
308 		remaining_buffer -= chars_printed;
309 		*log_info->sum_chars_printed += chars_printed;
310 		log_info->pBuf += chars_printed;
311 
312 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK3_DS_CNTL,%d,dcf_deep_sleep_divider\n",
313 					internal.CLK1_CLK3_DS_CNTL);
314 		remaining_buffer -= chars_printed;
315 		*log_info->sum_chars_printed += chars_printed;
316 		log_info->pBuf += chars_printed;
317 
318 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK3_ALLOW_DS,%d,dcf_deep_sleep_allow\n",
319 					internal.CLK1_CLK3_ALLOW_DS);
320 		remaining_buffer -= chars_printed;
321 		*log_info->sum_chars_printed += chars_printed;
322 		log_info->pBuf += chars_printed;
323 
324 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK2_CURRENT_CNT,%d,dprefclk\n",
325 					internal.CLK1_CLK2_CURRENT_CNT);
326 		remaining_buffer -= chars_printed;
327 		*log_info->sum_chars_printed += chars_printed;
328 		log_info->pBuf += chars_printed;
329 
330 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK0_CURRENT_CNT,%d,dispclk\n",
331 					internal.CLK1_CLK0_CURRENT_CNT);
332 		remaining_buffer -= chars_printed;
333 		*log_info->sum_chars_printed += chars_printed;
334 		log_info->pBuf += chars_printed;
335 
336 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK1_CURRENT_CNT,%d,dppclk\n",
337 					internal.CLK1_CLK1_CURRENT_CNT);
338 		remaining_buffer -= chars_printed;
339 		*log_info->sum_chars_printed += chars_printed;
340 		log_info->pBuf += chars_printed;
341 
342 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK3_BYPASS_CNTL,%d,dcfclk_bypass\n",
343 					internal.CLK1_CLK3_BYPASS_CNTL);
344 		remaining_buffer -= chars_printed;
345 		*log_info->sum_chars_printed += chars_printed;
346 		log_info->pBuf += chars_printed;
347 
348 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK2_BYPASS_CNTL,%d,dprefclk_bypass\n",
349 					internal.CLK1_CLK2_BYPASS_CNTL);
350 		remaining_buffer -= chars_printed;
351 		*log_info->sum_chars_printed += chars_printed;
352 		log_info->pBuf += chars_printed;
353 
354 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK0_BYPASS_CNTL,%d,dispclk_bypass\n",
355 					internal.CLK1_CLK0_BYPASS_CNTL);
356 		remaining_buffer -= chars_printed;
357 		*log_info->sum_chars_printed += chars_printed;
358 		log_info->pBuf += chars_printed;
359 
360 		chars_printed = snprintf_count(log_info->pBuf, remaining_buffer, "CLK1_CLK1_BYPASS_CNTL,%d,dppclk_bypass\n",
361 					internal.CLK1_CLK1_BYPASS_CNTL);
362 		remaining_buffer -= chars_printed;
363 		*log_info->sum_chars_printed += chars_printed;
364 		log_info->pBuf += chars_printed;
365 	}
366 }
367 
368 /* This function produce translated logical clk state values*/
369 void vg_get_clk_states(struct clk_mgr *clk_mgr_base, struct clk_states *s)
370 {
371 
372 	struct clk_state_registers_and_bypass sb = { 0 };
373 	struct clk_log_info log_info = { 0 };
374 
375 	vg_dump_clk_registers(&sb, clk_mgr_base, &log_info);
376 
377 	s->dprefclk_khz = sb.dprefclk * 1000;
378 }
379 
380 void vg_enable_pme_wa(struct clk_mgr *clk_mgr_base)
381 {
382 	struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
383 
384 	dcn301_smu_enable_pme_wa(clk_mgr);
385 }
386 
387 void vg_init_clocks(struct clk_mgr *clk_mgr)
388 {
389 	memset(&(clk_mgr->clks), 0, sizeof(struct dc_clocks));
390 	// Assumption is that boot state always supports pstate
391 	clk_mgr->clks.p_state_change_support = true;
392 	clk_mgr->clks.prev_p_state_change_support = true;
393 	clk_mgr->clks.pwr_state = DCN_PWR_STATE_UNKNOWN;
394 }
395 
396 static void vg_build_watermark_ranges(struct clk_bw_params *bw_params, struct watermarks *table)
397 {
398 	int i, num_valid_sets;
399 
400 	num_valid_sets = 0;
401 
402 	for (i = 0; i < WM_SET_COUNT; i++) {
403 		/* skip empty entries, the smu array has no holes*/
404 		if (!bw_params->wm_table.entries[i].valid)
405 			continue;
406 
407 		table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmSetting = bw_params->wm_table.entries[i].wm_inst;
408 		table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmType = bw_params->wm_table.entries[i].wm_type;
409 		/* We will not select WM based on fclk, so leave it as unconstrained */
410 		table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinClock = 0;
411 		table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxClock = 0xFFFF;
412 
413 		if (table->WatermarkRow[WM_DCFCLK][num_valid_sets].WmType == WM_TYPE_PSTATE_CHG) {
414 			if (i == 0)
415 				table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinMclk = 0;
416 			else {
417 				/* add 1 to make it non-overlapping with next lvl */
418 				table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinMclk =
419 						bw_params->clk_table.entries[i - 1].dcfclk_mhz + 1;
420 			}
421 			table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxMclk =
422 					bw_params->clk_table.entries[i].dcfclk_mhz;
423 
424 		} else {
425 			/* unconstrained for memory retraining */
426 			table->WatermarkRow[WM_DCFCLK][num_valid_sets].MinClock = 0;
427 			table->WatermarkRow[WM_DCFCLK][num_valid_sets].MaxClock = 0xFFFF;
428 
429 			/* Modify previous watermark range to cover up to max */
430 			table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxClock = 0xFFFF;
431 		}
432 		num_valid_sets++;
433 	}
434 
435 	ASSERT(num_valid_sets != 0); /* Must have at least one set of valid watermarks */
436 
437 	/* modify the min and max to make sure we cover the whole range*/
438 	table->WatermarkRow[WM_DCFCLK][0].MinMclk = 0;
439 	table->WatermarkRow[WM_DCFCLK][0].MinClock = 0;
440 	table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxMclk = 0xFFFF;
441 	table->WatermarkRow[WM_DCFCLK][num_valid_sets - 1].MaxClock = 0xFFFF;
442 
443 	/* This is for writeback only, does not matter currently as no writeback support*/
444 	table->WatermarkRow[WM_SOCCLK][0].WmSetting = WM_A;
445 	table->WatermarkRow[WM_SOCCLK][0].MinClock = 0;
446 	table->WatermarkRow[WM_SOCCLK][0].MaxClock = 0xFFFF;
447 	table->WatermarkRow[WM_SOCCLK][0].MinMclk = 0;
448 	table->WatermarkRow[WM_SOCCLK][0].MaxMclk = 0xFFFF;
449 }
450 
451 
452 void vg_notify_wm_ranges(struct clk_mgr *clk_mgr_base)
453 {
454 	struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
455 	struct watermarks *table = clk_mgr_base->smu_wm_set.wm_set;
456 
457 	if (!clk_mgr->smu_ver)
458 		return;
459 
460 	if (!table || clk_mgr_base->smu_wm_set.mc_address.quad_part == 0)
461 		return;
462 
463 	memset(table, 0, sizeof(*table));
464 
465 	vg_build_watermark_ranges(clk_mgr_base->bw_params, table);
466 
467 	dcn301_smu_set_dram_addr_high(clk_mgr,
468 			clk_mgr_base->smu_wm_set.mc_address.high_part);
469 	dcn301_smu_set_dram_addr_low(clk_mgr,
470 			clk_mgr_base->smu_wm_set.mc_address.low_part);
471 	dcn301_smu_transfer_wm_table_dram_2_smu(clk_mgr);
472 }
473 
474 static bool vg_are_clock_states_equal(struct dc_clocks *a,
475 		struct dc_clocks *b)
476 {
477 	if (a->dispclk_khz != b->dispclk_khz)
478 		return false;
479 	else if (a->dppclk_khz != b->dppclk_khz)
480 		return false;
481 	else if (a->dcfclk_khz != b->dcfclk_khz)
482 		return false;
483 	else if (a->dcfclk_deep_sleep_khz != b->dcfclk_deep_sleep_khz)
484 		return false;
485 
486 	return true;
487 }
488 
489 
490 static struct clk_mgr_funcs vg_funcs = {
491 	.get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz,
492 	.update_clocks = vg_update_clocks,
493 	.init_clocks = vg_init_clocks,
494 	.enable_pme_wa = vg_enable_pme_wa,
495 	.are_clock_states_equal = vg_are_clock_states_equal,
496 	.notify_wm_ranges = vg_notify_wm_ranges
497 };
498 
499 static struct clk_bw_params vg_bw_params = {
500 	.vram_type = Ddr4MemType,
501 	.num_channels = 1,
502 	.clk_table = {
503 		.entries = {
504 			{
505 				.voltage = 0,
506 				.dcfclk_mhz = 400,
507 				.fclk_mhz = 400,
508 				.memclk_mhz = 800,
509 				.socclk_mhz = 0,
510 			},
511 			{
512 				.voltage = 0,
513 				.dcfclk_mhz = 483,
514 				.fclk_mhz = 800,
515 				.memclk_mhz = 1600,
516 				.socclk_mhz = 0,
517 			},
518 			{
519 				.voltage = 0,
520 				.dcfclk_mhz = 602,
521 				.fclk_mhz = 1067,
522 				.memclk_mhz = 1067,
523 				.socclk_mhz = 0,
524 			},
525 			{
526 				.voltage = 0,
527 				.dcfclk_mhz = 738,
528 				.fclk_mhz = 1333,
529 				.memclk_mhz = 1600,
530 				.socclk_mhz = 0,
531 			},
532 		},
533 
534 		.num_entries = 4,
535 	},
536 
537 };
538 
539 static struct wm_table ddr4_wm_table = {
540 	.entries = {
541 		{
542 			.wm_inst = WM_A,
543 			.wm_type = WM_TYPE_PSTATE_CHG,
544 			.pstate_latency_us = 11.72,
545 			.sr_exit_time_us = 6.09,
546 			.sr_enter_plus_exit_time_us = 7.14,
547 			.valid = true,
548 		},
549 		{
550 			.wm_inst = WM_B,
551 			.wm_type = WM_TYPE_PSTATE_CHG,
552 			.pstate_latency_us = 11.72,
553 			.sr_exit_time_us = 10.12,
554 			.sr_enter_plus_exit_time_us = 11.48,
555 			.valid = true,
556 		},
557 		{
558 			.wm_inst = WM_C,
559 			.wm_type = WM_TYPE_PSTATE_CHG,
560 			.pstate_latency_us = 11.72,
561 			.sr_exit_time_us = 10.12,
562 			.sr_enter_plus_exit_time_us = 11.48,
563 			.valid = true,
564 		},
565 		{
566 			.wm_inst = WM_D,
567 			.wm_type = WM_TYPE_PSTATE_CHG,
568 			.pstate_latency_us = 11.72,
569 			.sr_exit_time_us = 10.12,
570 			.sr_enter_plus_exit_time_us = 11.48,
571 			.valid = true,
572 		},
573 	}
574 };
575 
576 static struct wm_table lpddr5_wm_table = {
577 	.entries = {
578 		{
579 			.wm_inst = WM_A,
580 			.wm_type = WM_TYPE_PSTATE_CHG,
581 			.pstate_latency_us = 11.65333,
582 			.sr_exit_time_us = 5.32,
583 			.sr_enter_plus_exit_time_us = 6.38,
584 			.valid = true,
585 		},
586 		{
587 			.wm_inst = WM_B,
588 			.wm_type = WM_TYPE_PSTATE_CHG,
589 			.pstate_latency_us = 11.65333,
590 			.sr_exit_time_us = 9.82,
591 			.sr_enter_plus_exit_time_us = 11.196,
592 			.valid = true,
593 		},
594 		{
595 			.wm_inst = WM_C,
596 			.wm_type = WM_TYPE_PSTATE_CHG,
597 			.pstate_latency_us = 11.65333,
598 			.sr_exit_time_us = 9.89,
599 			.sr_enter_plus_exit_time_us = 11.24,
600 			.valid = true,
601 		},
602 		{
603 			.wm_inst = WM_D,
604 			.wm_type = WM_TYPE_PSTATE_CHG,
605 			.pstate_latency_us = 11.65333,
606 			.sr_exit_time_us = 9.748,
607 			.sr_enter_plus_exit_time_us = 11.102,
608 			.valid = true,
609 		},
610 	}
611 };
612 
613 
614 static unsigned int find_dcfclk_for_voltage(const struct vg_dpm_clocks *clock_table,
615 		unsigned int voltage)
616 {
617 	int i;
618 
619 	for (i = 0; i < VG_NUM_SOC_VOLTAGE_LEVELS; i++) {
620 		if (clock_table->SocVoltage[i] == voltage)
621 			return clock_table->DcfClocks[i];
622 	}
623 
624 	ASSERT(0);
625 	return 0;
626 }
627 
628 void vg_clk_mgr_helper_populate_bw_params(
629 		struct clk_mgr_internal *clk_mgr,
630 		struct integrated_info *bios_info,
631 		const struct vg_dpm_clocks *clock_table)
632 {
633 	int i, j;
634 	struct clk_bw_params *bw_params = clk_mgr->base.bw_params;
635 
636 	j = -1;
637 
638 	ASSERT(VG_NUM_FCLK_DPM_LEVELS <= MAX_NUM_DPM_LVL);
639 
640 	/* Find lowest DPM, FCLK is filled in reverse order*/
641 
642 	for (i = VG_NUM_FCLK_DPM_LEVELS - 1; i >= 0; i--) {
643 		if (clock_table->DfPstateTable[i].fclk != 0) {
644 			j = i;
645 			break;
646 		}
647 	}
648 
649 	if (j == -1) {
650 		/* clock table is all 0s, just use our own hardcode */
651 		ASSERT(0);
652 		return;
653 	}
654 
655 	bw_params->clk_table.num_entries = j + 1;
656 
657 	for (i = 0; i < bw_params->clk_table.num_entries; i++, j--) {
658 		bw_params->clk_table.entries[i].fclk_mhz = clock_table->DfPstateTable[j].fclk;
659 		bw_params->clk_table.entries[i].memclk_mhz = clock_table->DfPstateTable[j].memclk;
660 		bw_params->clk_table.entries[i].voltage = clock_table->DfPstateTable[j].voltage;
661 		bw_params->clk_table.entries[i].dcfclk_mhz = find_dcfclk_for_voltage(clock_table, clock_table->DfPstateTable[j].voltage);
662 	}
663 
664 	bw_params->vram_type = bios_info->memory_type;
665 	bw_params->num_channels = bios_info->ma_channel_number;
666 
667 	for (i = 0; i < WM_SET_COUNT; i++) {
668 		bw_params->wm_table.entries[i].wm_inst = i;
669 
670 		if (i >= bw_params->clk_table.num_entries) {
671 			bw_params->wm_table.entries[i].valid = false;
672 			continue;
673 		}
674 
675 		bw_params->wm_table.entries[i].wm_type = WM_TYPE_PSTATE_CHG;
676 		bw_params->wm_table.entries[i].valid = true;
677 	}
678 
679 	if (bw_params->vram_type == LpDdr4MemType) {
680 		/*
681 		 * WM set D will be re-purposed for memory retraining
682 		 */
683 		bw_params->wm_table.entries[WM_D].pstate_latency_us = LPDDR_MEM_RETRAIN_LATENCY;
684 		bw_params->wm_table.entries[WM_D].wm_inst = WM_D;
685 		bw_params->wm_table.entries[WM_D].wm_type = WM_TYPE_RETRAINING;
686 		bw_params->wm_table.entries[WM_D].valid = true;
687 	}
688 
689 }
690 
691 /* Temporary Place holder until we can get them from fuse */
692 static struct vg_dpm_clocks dummy_clocks = {
693 		.DcfClocks = { 201, 403, 403, 403, 403, 403, 403 },
694 		.SocClocks = { 400, 600, 600, 600, 600, 600, 600 },
695 		.SocVoltage = { 2800, 2860, 2860, 2860, 2860, 2860, 2860, 2860 },
696 		.DfPstateTable = {
697 				{ .fclk = 400,  .memclk = 400, .voltage = 2800 },
698 				{ .fclk = 400,  .memclk = 400, .voltage = 2800 },
699 				{ .fclk = 400,  .memclk = 400, .voltage = 2800 },
700 				{ .fclk = 400,  .memclk = 400, .voltage = 2800 }
701 		}
702 };
703 
704 static struct watermarks dummy_wms = { 0 };
705 
706 void vg_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr,
707 		struct smu_dpm_clks *smu_dpm_clks)
708 {
709 	struct vg_dpm_clocks *table = smu_dpm_clks->dpm_clks;
710 
711 	if (!clk_mgr->smu_ver)
712 		return;
713 
714 	if (!table || smu_dpm_clks->mc_address.quad_part == 0)
715 		return;
716 
717 	memset(table, 0, sizeof(*table));
718 
719 	dcn301_smu_set_dram_addr_high(clk_mgr,
720 			smu_dpm_clks->mc_address.high_part);
721 	dcn301_smu_set_dram_addr_low(clk_mgr,
722 			smu_dpm_clks->mc_address.low_part);
723 	dcn301_smu_transfer_dpm_table_smu_2_dram(clk_mgr);
724 }
725 
726 void vg_clk_mgr_construct(
727 		struct dc_context *ctx,
728 		struct clk_mgr_internal *clk_mgr,
729 		struct pp_smu_funcs *pp_smu,
730 		struct dccg *dccg)
731 {
732 	struct smu_dpm_clks smu_dpm_clks = { 0 };
733 
734 	clk_mgr->base.ctx = ctx;
735 	clk_mgr->base.funcs = &vg_funcs;
736 
737 	clk_mgr->pp_smu = pp_smu;
738 
739 	clk_mgr->dccg = dccg;
740 	clk_mgr->dfs_bypass_disp_clk = 0;
741 
742 	clk_mgr->dprefclk_ss_percentage = 0;
743 	clk_mgr->dprefclk_ss_divider = 1000;
744 	clk_mgr->ss_on_dprefclk = false;
745 	clk_mgr->dfs_ref_freq_khz = 48000;
746 
747 	clk_mgr->base.smu_wm_set.wm_set = (struct watermarks *)dm_helpers_allocate_gpu_mem(
748 				clk_mgr->base.ctx,
749 				DC_MEM_ALLOC_TYPE_FRAME_BUFFER,
750 				sizeof(struct watermarks),
751 				&clk_mgr->base.smu_wm_set.mc_address.quad_part);
752 
753 	if (clk_mgr->base.smu_wm_set.wm_set == 0) {
754 		clk_mgr->base.smu_wm_set.wm_set = &dummy_wms;
755 		clk_mgr->base.smu_wm_set.mc_address.quad_part = 0;
756 	}
757 	ASSERT(clk_mgr->base.smu_wm_set.wm_set);
758 
759 	smu_dpm_clks.dpm_clks = (struct vg_dpm_clocks *)dm_helpers_allocate_gpu_mem(
760 				clk_mgr->base.ctx,
761 				DC_MEM_ALLOC_TYPE_FRAME_BUFFER,
762 				sizeof(struct vg_dpm_clocks),
763 				&smu_dpm_clks.mc_address.quad_part);
764 
765 	if (smu_dpm_clks.dpm_clks == NULL) {
766 		smu_dpm_clks.dpm_clks = &dummy_clocks;
767 		smu_dpm_clks.mc_address.quad_part = 0;
768 	}
769 
770 	ASSERT(smu_dpm_clks.dpm_clks);
771 
772 	if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) {
773 		vg_funcs.update_clocks = dcn2_update_clocks_fpga;
774 		clk_mgr->base.dentist_vco_freq_khz = 3600000;
775 	} else {
776 		struct clk_log_info log_info = {0};
777 
778 		clk_mgr->smu_ver = dcn301_smu_get_smu_version(clk_mgr);
779 
780 		if (clk_mgr->smu_ver)
781 			clk_mgr->smu_present = true;
782 
783 		/* TODO: Check we get what we expect during bringup */
784 		clk_mgr->base.dentist_vco_freq_khz = get_vco_frequency_from_reg(clk_mgr);
785 
786 		/* in case we don't get a value from the register, use default */
787 		if (clk_mgr->base.dentist_vco_freq_khz == 0)
788 			clk_mgr->base.dentist_vco_freq_khz = 3600000;
789 
790 		if (ctx->dc_bios->integrated_info->memory_type == LpDdr5MemType) {
791 			vg_bw_params.wm_table = lpddr5_wm_table;
792 		} else {
793 			vg_bw_params.wm_table = ddr4_wm_table;
794 		}
795 		/* Saved clocks configured at boot for debug purposes */
796 		vg_dump_clk_registers(&clk_mgr->base.boot_snapshot, &clk_mgr->base, &log_info);
797 	}
798 
799 	clk_mgr->base.dprefclk_khz = 600000;
800 	dce_clock_read_ss_info(clk_mgr);
801 
802 	clk_mgr->base.bw_params = &vg_bw_params;
803 
804 	vg_get_dpm_table_from_smu(clk_mgr, &smu_dpm_clks);
805 	if (ctx->dc_bios && ctx->dc_bios->integrated_info) {
806 		vg_clk_mgr_helper_populate_bw_params(
807 				clk_mgr,
808 				ctx->dc_bios->integrated_info,
809 				smu_dpm_clks.dpm_clks);
810 	}
811 
812 	if (smu_dpm_clks.dpm_clks && smu_dpm_clks.mc_address.quad_part != 0)
813 		dm_helpers_free_gpu_mem(clk_mgr->base.ctx, DC_MEM_ALLOC_TYPE_FRAME_BUFFER,
814 				smu_dpm_clks.dpm_clks);
815 /*
816 	if (!IS_FPGA_MAXIMUS_DC(ctx->dce_environment) && clk_mgr->smu_ver) {
817 		 enable powerfeatures when displaycount goes to 0
818 		dcn301_smu_enable_phy_refclk_pwrdwn(clk_mgr, !debug->disable_48mhz_pwrdwn);
819 	}
820 */
821 }
822 
823 void vg_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr)
824 {
825 	if (clk_mgr->base.smu_wm_set.wm_set && clk_mgr->base.smu_wm_set.mc_address.quad_part != 0)
826 		dm_helpers_free_gpu_mem(clk_mgr->base.ctx, DC_MEM_ALLOC_TYPE_FRAME_BUFFER,
827 				clk_mgr->base.smu_wm_set.wm_set);
828 }
829