1 /*
2 * Copyright 2018 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 #include <linux/delay.h>
26 #include "dm_services.h"
27 #include "dcn20/dcn20_hubbub.h"
28 #include "dcn21_hubbub.h"
29 #include "reg_helper.h"
30 
31 #define REG(reg)\
32 	hubbub1->regs->reg
33 #define DC_LOGGER \
34 	hubbub1->base.ctx->logger
35 #define CTX \
36 	hubbub1->base.ctx
37 
38 #undef FN
39 #define FN(reg_name, field_name) \
40 	hubbub1->shifts->field_name, hubbub1->masks->field_name
41 
42 #define REG(reg)\
43 	hubbub1->regs->reg
44 
45 #define CTX \
46 	hubbub1->base.ctx
47 
48 #undef FN
49 #define FN(reg_name, field_name) \
50 	hubbub1->shifts->field_name, hubbub1->masks->field_name
51 
52 #ifdef NUM_VMID
53 #undef NUM_VMID
54 #endif
55 #define NUM_VMID 16
56 
57 static uint32_t convert_and_clamp(
58 	uint32_t wm_ns,
59 	uint32_t refclk_mhz,
60 	uint32_t clamp_value)
61 {
62 	uint32_t ret_val = 0;
63 	ret_val = wm_ns * refclk_mhz;
64 	ret_val /= 1000;
65 
66 	if (ret_val > clamp_value)
67 		ret_val = clamp_value;
68 
69 	return ret_val;
70 }
71 
72 void dcn21_dchvm_init(struct hubbub *hubbub)
73 {
74 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
75 	uint32_t riommu_active;
76 	int i;
77 
78 	//Init DCHVM block
79 	REG_UPDATE(DCHVM_CTRL0, HOSTVM_INIT_REQ, 1);
80 
81 	//Poll until RIOMMU_ACTIVE = 1
82 	for (i = 0; i < 100; i++) {
83 		REG_GET(DCHVM_RIOMMU_STAT0, RIOMMU_ACTIVE, &riommu_active);
84 
85 		if (riommu_active)
86 			break;
87 		else
88 			udelay(5);
89 	}
90 
91 	if (riommu_active) {
92 		//Reflect the power status of DCHUBBUB
93 		REG_UPDATE(DCHVM_RIOMMU_CTRL0, HOSTVM_POWERSTATUS, 1);
94 
95 		//Start rIOMMU prefetching
96 		REG_UPDATE(DCHVM_RIOMMU_CTRL0, HOSTVM_PREFETCH_REQ, 1);
97 
98 		// Enable dynamic clock gating
99 		REG_UPDATE_4(DCHVM_CLK_CTRL,
100 						HVM_DISPCLK_R_GATE_DIS, 0,
101 						HVM_DISPCLK_G_GATE_DIS, 0,
102 						HVM_DCFCLK_R_GATE_DIS, 0,
103 						HVM_DCFCLK_G_GATE_DIS, 0);
104 
105 		//Poll until HOSTVM_PREFETCH_DONE = 1
106 		REG_WAIT(DCHVM_RIOMMU_STAT0, HOSTVM_PREFETCH_DONE, 1, 5, 100);
107 	}
108 }
109 
110 int hubbub21_init_dchub(struct hubbub *hubbub,
111 		struct dcn_hubbub_phys_addr_config *pa_config)
112 {
113 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
114 	struct dcn_vmid_page_table_config phys_config;
115 
116 	REG_SET(DCN_VM_FB_LOCATION_BASE, 0,
117 			FB_BASE, pa_config->system_aperture.fb_base >> 24);
118 	REG_SET(DCN_VM_FB_LOCATION_TOP, 0,
119 			FB_TOP, pa_config->system_aperture.fb_top >> 24);
120 	REG_SET(DCN_VM_FB_OFFSET, 0,
121 			FB_OFFSET, pa_config->system_aperture.fb_offset >> 24);
122 	REG_SET(DCN_VM_AGP_BOT, 0,
123 			AGP_BOT, pa_config->system_aperture.agp_bot >> 24);
124 	REG_SET(DCN_VM_AGP_TOP, 0,
125 			AGP_TOP, pa_config->system_aperture.agp_top >> 24);
126 	REG_SET(DCN_VM_AGP_BASE, 0,
127 			AGP_BASE, pa_config->system_aperture.agp_base >> 24);
128 
129 	if (pa_config->gart_config.page_table_start_addr != pa_config->gart_config.page_table_end_addr) {
130 		phys_config.page_table_start_addr = pa_config->gart_config.page_table_start_addr >> 12;
131 		phys_config.page_table_end_addr = pa_config->gart_config.page_table_end_addr >> 12;
132 		phys_config.page_table_base_addr = pa_config->gart_config.page_table_base_addr | 1; //Note: hack
133 		phys_config.depth = 0;
134 		phys_config.block_size = 0;
135 		// Init VMID 0 based on PA config
136 		dcn20_vmid_setup(&hubbub1->vmid[0], &phys_config);
137 	}
138 
139 	dcn21_dchvm_init(hubbub);
140 
141 	return NUM_VMID;
142 }
143 
144 bool hubbub21_program_urgent_watermarks(
145 		struct hubbub *hubbub,
146 		struct dcn_watermark_set *watermarks,
147 		unsigned int refclk_mhz,
148 		bool safe_to_lower)
149 {
150 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
151 	uint32_t prog_wm_value;
152 	bool wm_pending = false;
153 
154 	/* Repeat for water mark set A, B, C and D. */
155 	/* clock state A */
156 	if (safe_to_lower || watermarks->a.urgent_ns > hubbub1->watermarks.a.urgent_ns) {
157 		hubbub1->watermarks.a.urgent_ns = watermarks->a.urgent_ns;
158 		prog_wm_value = convert_and_clamp(watermarks->a.urgent_ns,
159 				refclk_mhz, 0x1fffff);
160 		REG_SET_2(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, 0,
161 				DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, prog_wm_value,
162 				DCHUBBUB_ARB_VM_ROW_URGENCY_WATERMARK_A, prog_wm_value);
163 
164 		DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_A calculated =%d\n"
165 			"HW register value = 0x%x\n",
166 			watermarks->a.urgent_ns, prog_wm_value);
167 	} else if (watermarks->a.urgent_ns < hubbub1->watermarks.a.urgent_ns)
168 		wm_pending = true;
169 
170 	/* determine the transfer time for a quantity of data for a particular requestor.*/
171 	if (safe_to_lower || watermarks->a.frac_urg_bw_flip
172 			> hubbub1->watermarks.a.frac_urg_bw_flip) {
173 		hubbub1->watermarks.a.frac_urg_bw_flip = watermarks->a.frac_urg_bw_flip;
174 
175 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, 0,
176 				DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, watermarks->a.frac_urg_bw_flip);
177 	} else if (watermarks->a.frac_urg_bw_flip
178 			< hubbub1->watermarks.a.frac_urg_bw_flip)
179 		wm_pending = true;
180 
181 	if (safe_to_lower || watermarks->a.frac_urg_bw_nom
182 			> hubbub1->watermarks.a.frac_urg_bw_nom) {
183 		hubbub1->watermarks.a.frac_urg_bw_nom = watermarks->a.frac_urg_bw_nom;
184 
185 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, 0,
186 				DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, watermarks->a.frac_urg_bw_nom);
187 	} else if (watermarks->a.frac_urg_bw_nom
188 			< hubbub1->watermarks.a.frac_urg_bw_nom)
189 		wm_pending = true;
190 
191 	if (safe_to_lower || watermarks->a.urgent_latency_ns > hubbub1->watermarks.a.urgent_latency_ns) {
192 		hubbub1->watermarks.a.urgent_latency_ns = watermarks->a.urgent_latency_ns;
193 		prog_wm_value = convert_and_clamp(watermarks->a.urgent_latency_ns,
194 				refclk_mhz, 0x1fffff);
195 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, 0,
196 				DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, prog_wm_value);
197 	} else if (watermarks->a.urgent_latency_ns < hubbub1->watermarks.a.urgent_latency_ns)
198 		wm_pending = true;
199 
200 	/* clock state B */
201 	if (safe_to_lower || watermarks->b.urgent_ns > hubbub1->watermarks.b.urgent_ns) {
202 		hubbub1->watermarks.b.urgent_ns = watermarks->b.urgent_ns;
203 		prog_wm_value = convert_and_clamp(watermarks->b.urgent_ns,
204 				refclk_mhz, 0x1fffff);
205 		REG_SET_2(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, 0,
206 				DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, prog_wm_value,
207 				DCHUBBUB_ARB_VM_ROW_URGENCY_WATERMARK_B, prog_wm_value);
208 
209 		DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_B calculated =%d\n"
210 			"HW register value = 0x%x\n",
211 			watermarks->b.urgent_ns, prog_wm_value);
212 	} else if (watermarks->b.urgent_ns < hubbub1->watermarks.b.urgent_ns)
213 		wm_pending = true;
214 
215 	/* determine the transfer time for a quantity of data for a particular requestor.*/
216 	if (safe_to_lower || watermarks->a.frac_urg_bw_flip
217 			> hubbub1->watermarks.a.frac_urg_bw_flip) {
218 		hubbub1->watermarks.a.frac_urg_bw_flip = watermarks->a.frac_urg_bw_flip;
219 
220 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, 0,
221 				DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, watermarks->a.frac_urg_bw_flip);
222 	} else if (watermarks->a.frac_urg_bw_flip
223 			< hubbub1->watermarks.a.frac_urg_bw_flip)
224 		wm_pending = true;
225 
226 	if (safe_to_lower || watermarks->a.frac_urg_bw_nom
227 			> hubbub1->watermarks.a.frac_urg_bw_nom) {
228 		hubbub1->watermarks.a.frac_urg_bw_nom = watermarks->a.frac_urg_bw_nom;
229 
230 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, 0,
231 				DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, watermarks->a.frac_urg_bw_nom);
232 	} else if (watermarks->a.frac_urg_bw_nom
233 			< hubbub1->watermarks.a.frac_urg_bw_nom)
234 		wm_pending = true;
235 
236 	if (safe_to_lower || watermarks->b.urgent_latency_ns > hubbub1->watermarks.b.urgent_latency_ns) {
237 		hubbub1->watermarks.b.urgent_latency_ns = watermarks->b.urgent_latency_ns;
238 		prog_wm_value = convert_and_clamp(watermarks->b.urgent_latency_ns,
239 				refclk_mhz, 0x1fffff);
240 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, 0,
241 				DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, prog_wm_value);
242 	} else if (watermarks->b.urgent_latency_ns < hubbub1->watermarks.b.urgent_latency_ns)
243 		wm_pending = true;
244 
245 	/* clock state C */
246 	if (safe_to_lower || watermarks->c.urgent_ns > hubbub1->watermarks.c.urgent_ns) {
247 		hubbub1->watermarks.c.urgent_ns = watermarks->c.urgent_ns;
248 		prog_wm_value = convert_and_clamp(watermarks->c.urgent_ns,
249 				refclk_mhz, 0x1fffff);
250 		REG_SET_2(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C, 0,
251 				DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C, prog_wm_value,
252 				DCHUBBUB_ARB_VM_ROW_URGENCY_WATERMARK_C, prog_wm_value);
253 
254 		DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_C calculated =%d\n"
255 			"HW register value = 0x%x\n",
256 			watermarks->c.urgent_ns, prog_wm_value);
257 	} else if (watermarks->c.urgent_ns < hubbub1->watermarks.c.urgent_ns)
258 		wm_pending = true;
259 
260 	/* determine the transfer time for a quantity of data for a particular requestor.*/
261 	if (safe_to_lower || watermarks->a.frac_urg_bw_flip
262 			> hubbub1->watermarks.a.frac_urg_bw_flip) {
263 		hubbub1->watermarks.a.frac_urg_bw_flip = watermarks->a.frac_urg_bw_flip;
264 
265 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_C, 0,
266 				DCHUBBUB_ARB_FRAC_URG_BW_FLIP_C, watermarks->a.frac_urg_bw_flip);
267 	} else if (watermarks->a.frac_urg_bw_flip
268 			< hubbub1->watermarks.a.frac_urg_bw_flip)
269 		wm_pending = true;
270 
271 	if (safe_to_lower || watermarks->a.frac_urg_bw_nom
272 			> hubbub1->watermarks.a.frac_urg_bw_nom) {
273 		hubbub1->watermarks.a.frac_urg_bw_nom = watermarks->a.frac_urg_bw_nom;
274 
275 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_C, 0,
276 				DCHUBBUB_ARB_FRAC_URG_BW_NOM_C, watermarks->a.frac_urg_bw_nom);
277 	} else if (watermarks->a.frac_urg_bw_nom
278 			< hubbub1->watermarks.a.frac_urg_bw_nom)
279 		wm_pending = true;
280 
281 	if (safe_to_lower || watermarks->c.urgent_latency_ns > hubbub1->watermarks.c.urgent_latency_ns) {
282 		hubbub1->watermarks.c.urgent_latency_ns = watermarks->c.urgent_latency_ns;
283 		prog_wm_value = convert_and_clamp(watermarks->c.urgent_latency_ns,
284 				refclk_mhz, 0x1fffff);
285 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_C, 0,
286 				DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_C, prog_wm_value);
287 	} else if (watermarks->c.urgent_latency_ns < hubbub1->watermarks.c.urgent_latency_ns)
288 		wm_pending = true;
289 
290 	/* clock state D */
291 	if (safe_to_lower || watermarks->d.urgent_ns > hubbub1->watermarks.d.urgent_ns) {
292 		hubbub1->watermarks.d.urgent_ns = watermarks->d.urgent_ns;
293 		prog_wm_value = convert_and_clamp(watermarks->d.urgent_ns,
294 				refclk_mhz, 0x1fffff);
295 		REG_SET_2(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D, 0,
296 				DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D, prog_wm_value,
297 				DCHUBBUB_ARB_VM_ROW_URGENCY_WATERMARK_D, prog_wm_value);
298 
299 		DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_D calculated =%d\n"
300 			"HW register value = 0x%x\n",
301 			watermarks->d.urgent_ns, prog_wm_value);
302 	} else if (watermarks->d.urgent_ns < hubbub1->watermarks.d.urgent_ns)
303 		wm_pending = true;
304 
305 	/* determine the transfer time for a quantity of data for a particular requestor.*/
306 	if (safe_to_lower || watermarks->a.frac_urg_bw_flip
307 			> hubbub1->watermarks.a.frac_urg_bw_flip) {
308 		hubbub1->watermarks.a.frac_urg_bw_flip = watermarks->a.frac_urg_bw_flip;
309 
310 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_D, 0,
311 				DCHUBBUB_ARB_FRAC_URG_BW_FLIP_D, watermarks->a.frac_urg_bw_flip);
312 	} else if (watermarks->a.frac_urg_bw_flip
313 			< hubbub1->watermarks.a.frac_urg_bw_flip)
314 		wm_pending = true;
315 
316 	if (safe_to_lower || watermarks->a.frac_urg_bw_nom
317 			> hubbub1->watermarks.a.frac_urg_bw_nom) {
318 		hubbub1->watermarks.a.frac_urg_bw_nom = watermarks->a.frac_urg_bw_nom;
319 
320 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_D, 0,
321 				DCHUBBUB_ARB_FRAC_URG_BW_NOM_D, watermarks->a.frac_urg_bw_nom);
322 	} else if (watermarks->a.frac_urg_bw_nom
323 			< hubbub1->watermarks.a.frac_urg_bw_nom)
324 		wm_pending = true;
325 
326 	if (safe_to_lower || watermarks->d.urgent_latency_ns > hubbub1->watermarks.d.urgent_latency_ns) {
327 		hubbub1->watermarks.d.urgent_latency_ns = watermarks->d.urgent_latency_ns;
328 		prog_wm_value = convert_and_clamp(watermarks->d.urgent_latency_ns,
329 				refclk_mhz, 0x1fffff);
330 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_D, 0,
331 				DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_D, prog_wm_value);
332 	} else if (watermarks->d.urgent_latency_ns < hubbub1->watermarks.d.urgent_latency_ns)
333 		wm_pending = true;
334 
335 	return wm_pending;
336 }
337 
338 bool hubbub21_program_stutter_watermarks(
339 		struct hubbub *hubbub,
340 		struct dcn_watermark_set *watermarks,
341 		unsigned int refclk_mhz,
342 		bool safe_to_lower)
343 {
344 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
345 	uint32_t prog_wm_value;
346 	bool wm_pending = false;
347 
348 	/* clock state A */
349 	if (safe_to_lower || watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns
350 			> hubbub1->watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns) {
351 		hubbub1->watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns =
352 				watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns;
353 		prog_wm_value = convert_and_clamp(
354 				watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns,
355 				refclk_mhz, 0x1fffff);
356 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, 0,
357 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, prog_wm_value,
358 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_ENTER_WATERMARK_A, prog_wm_value);
359 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_A calculated =%d\n"
360 			"HW register value = 0x%x\n",
361 			watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value);
362 	} else if (watermarks->a.cstate_pstate.cstate_enter_plus_exit_ns
363 			< hubbub1->watermarks.a.cstate_pstate.cstate_enter_plus_exit_ns)
364 		wm_pending = true;
365 
366 	if (safe_to_lower || watermarks->a.cstate_pstate.cstate_exit_ns
367 			> hubbub1->watermarks.a.cstate_pstate.cstate_exit_ns) {
368 		hubbub1->watermarks.a.cstate_pstate.cstate_exit_ns =
369 				watermarks->a.cstate_pstate.cstate_exit_ns;
370 		prog_wm_value = convert_and_clamp(
371 				watermarks->a.cstate_pstate.cstate_exit_ns,
372 				refclk_mhz, 0x1fffff);
373 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, 0,
374 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, prog_wm_value,
375 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_EXIT_WATERMARK_A, prog_wm_value);
376 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_A calculated =%d\n"
377 			"HW register value = 0x%x\n",
378 			watermarks->a.cstate_pstate.cstate_exit_ns, prog_wm_value);
379 	} else if (watermarks->a.cstate_pstate.cstate_exit_ns
380 			< hubbub1->watermarks.a.cstate_pstate.cstate_exit_ns)
381 		wm_pending = true;
382 
383 	/* clock state B */
384 	if (safe_to_lower || watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns
385 			> hubbub1->watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns) {
386 		hubbub1->watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns =
387 				watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns;
388 		prog_wm_value = convert_and_clamp(
389 				watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns,
390 				refclk_mhz, 0x1fffff);
391 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, 0,
392 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, prog_wm_value,
393 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_ENTER_WATERMARK_B, prog_wm_value);
394 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_B calculated =%d\n"
395 			"HW register value = 0x%x\n",
396 			watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value);
397 	} else if (watermarks->b.cstate_pstate.cstate_enter_plus_exit_ns
398 			< hubbub1->watermarks.b.cstate_pstate.cstate_enter_plus_exit_ns)
399 		wm_pending = true;
400 
401 	if (safe_to_lower || watermarks->b.cstate_pstate.cstate_exit_ns
402 			> hubbub1->watermarks.b.cstate_pstate.cstate_exit_ns) {
403 		hubbub1->watermarks.b.cstate_pstate.cstate_exit_ns =
404 				watermarks->b.cstate_pstate.cstate_exit_ns;
405 		prog_wm_value = convert_and_clamp(
406 				watermarks->b.cstate_pstate.cstate_exit_ns,
407 				refclk_mhz, 0x1fffff);
408 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, 0,
409 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, prog_wm_value,
410 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_EXIT_WATERMARK_A, prog_wm_value);
411 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_B calculated =%d\n"
412 			"HW register value = 0x%x\n",
413 			watermarks->b.cstate_pstate.cstate_exit_ns, prog_wm_value);
414 	} else if (watermarks->b.cstate_pstate.cstate_exit_ns
415 			< hubbub1->watermarks.b.cstate_pstate.cstate_exit_ns)
416 		wm_pending = true;
417 
418 	/* clock state C */
419 	if (safe_to_lower || watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns
420 			> hubbub1->watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns) {
421 		hubbub1->watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns =
422 				watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns;
423 		prog_wm_value = convert_and_clamp(
424 				watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns,
425 				refclk_mhz, 0x1fffff);
426 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C, 0,
427 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C, prog_wm_value,
428 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_ENTER_WATERMARK_C, prog_wm_value);
429 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_C calculated =%d\n"
430 			"HW register value = 0x%x\n",
431 			watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value);
432 	} else if (watermarks->c.cstate_pstate.cstate_enter_plus_exit_ns
433 			< hubbub1->watermarks.c.cstate_pstate.cstate_enter_plus_exit_ns)
434 		wm_pending = true;
435 
436 	if (safe_to_lower || watermarks->c.cstate_pstate.cstate_exit_ns
437 			> hubbub1->watermarks.c.cstate_pstate.cstate_exit_ns) {
438 		hubbub1->watermarks.c.cstate_pstate.cstate_exit_ns =
439 				watermarks->c.cstate_pstate.cstate_exit_ns;
440 		prog_wm_value = convert_and_clamp(
441 				watermarks->c.cstate_pstate.cstate_exit_ns,
442 				refclk_mhz, 0x1fffff);
443 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C, 0,
444 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C, prog_wm_value,
445 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_EXIT_WATERMARK_A, prog_wm_value);
446 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_C calculated =%d\n"
447 			"HW register value = 0x%x\n",
448 			watermarks->c.cstate_pstate.cstate_exit_ns, prog_wm_value);
449 	} else if (watermarks->c.cstate_pstate.cstate_exit_ns
450 			< hubbub1->watermarks.c.cstate_pstate.cstate_exit_ns)
451 		wm_pending = true;
452 
453 	/* clock state D */
454 	if (safe_to_lower || watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns
455 			> hubbub1->watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns) {
456 		hubbub1->watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns =
457 				watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns;
458 		prog_wm_value = convert_and_clamp(
459 				watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns,
460 				refclk_mhz, 0x1fffff);
461 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D, 0,
462 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D, prog_wm_value,
463 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_ENTER_WATERMARK_D, prog_wm_value);
464 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_D calculated =%d\n"
465 			"HW register value = 0x%x\n",
466 			watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns, prog_wm_value);
467 	} else if (watermarks->d.cstate_pstate.cstate_enter_plus_exit_ns
468 			< hubbub1->watermarks.d.cstate_pstate.cstate_enter_plus_exit_ns)
469 		wm_pending = true;
470 
471 	if (safe_to_lower || watermarks->d.cstate_pstate.cstate_exit_ns
472 			> hubbub1->watermarks.d.cstate_pstate.cstate_exit_ns) {
473 		hubbub1->watermarks.d.cstate_pstate.cstate_exit_ns =
474 				watermarks->d.cstate_pstate.cstate_exit_ns;
475 		prog_wm_value = convert_and_clamp(
476 				watermarks->d.cstate_pstate.cstate_exit_ns,
477 				refclk_mhz, 0x1fffff);
478 		REG_SET_2(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D, 0,
479 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D, prog_wm_value,
480 				DCHUBBUB_ARB_VM_ROW_ALLOW_SR_EXIT_WATERMARK_A, prog_wm_value);
481 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_D calculated =%d\n"
482 			"HW register value = 0x%x\n",
483 			watermarks->d.cstate_pstate.cstate_exit_ns, prog_wm_value);
484 	} else if (watermarks->d.cstate_pstate.cstate_exit_ns
485 			< hubbub1->watermarks.d.cstate_pstate.cstate_exit_ns)
486 		wm_pending = true;
487 
488 	return wm_pending;
489 }
490 
491 bool hubbub21_program_pstate_watermarks(
492 		struct hubbub *hubbub,
493 		struct dcn_watermark_set *watermarks,
494 		unsigned int refclk_mhz,
495 		bool safe_to_lower)
496 {
497 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
498 	uint32_t prog_wm_value;
499 
500 	bool wm_pending = false;
501 
502 	/* clock state A */
503 	if (safe_to_lower || watermarks->a.cstate_pstate.pstate_change_ns
504 			> hubbub1->watermarks.a.cstate_pstate.pstate_change_ns) {
505 		hubbub1->watermarks.a.cstate_pstate.pstate_change_ns =
506 				watermarks->a.cstate_pstate.pstate_change_ns;
507 		prog_wm_value = convert_and_clamp(
508 				watermarks->a.cstate_pstate.pstate_change_ns,
509 				refclk_mhz, 0x1fffff);
510 		REG_SET_2(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, 0,
511 				DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, prog_wm_value,
512 				DCHUBBUB_ARB_VM_ROW_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, prog_wm_value);
513 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_A calculated =%d\n"
514 			"HW register value = 0x%x\n\n",
515 			watermarks->a.cstate_pstate.pstate_change_ns, prog_wm_value);
516 	} else if (watermarks->a.cstate_pstate.pstate_change_ns
517 			< hubbub1->watermarks.a.cstate_pstate.pstate_change_ns)
518 		wm_pending = true;
519 
520 	/* clock state B */
521 	if (safe_to_lower || watermarks->b.cstate_pstate.pstate_change_ns
522 			> hubbub1->watermarks.b.cstate_pstate.pstate_change_ns) {
523 		hubbub1->watermarks.b.cstate_pstate.pstate_change_ns =
524 				watermarks->b.cstate_pstate.pstate_change_ns;
525 		prog_wm_value = convert_and_clamp(
526 				watermarks->b.cstate_pstate.pstate_change_ns,
527 				refclk_mhz, 0x1fffff);
528 		REG_SET_2(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, 0,
529 				DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, prog_wm_value,
530 				DCHUBBUB_ARB_VM_ROW_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, prog_wm_value);
531 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_B calculated =%d\n"
532 			"HW register value = 0x%x\n\n",
533 			watermarks->b.cstate_pstate.pstate_change_ns, prog_wm_value);
534 	} else if (watermarks->b.cstate_pstate.pstate_change_ns
535 			< hubbub1->watermarks.b.cstate_pstate.pstate_change_ns)
536 		wm_pending = false;
537 
538 	/* clock state C */
539 	if (safe_to_lower || watermarks->c.cstate_pstate.pstate_change_ns
540 			> hubbub1->watermarks.c.cstate_pstate.pstate_change_ns) {
541 		hubbub1->watermarks.c.cstate_pstate.pstate_change_ns =
542 				watermarks->c.cstate_pstate.pstate_change_ns;
543 		prog_wm_value = convert_and_clamp(
544 				watermarks->c.cstate_pstate.pstate_change_ns,
545 				refclk_mhz, 0x1fffff);
546 		REG_SET_2(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, 0,
547 				DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, prog_wm_value,
548 				DCHUBBUB_ARB_VM_ROW_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, prog_wm_value);
549 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_C calculated =%d\n"
550 			"HW register value = 0x%x\n\n",
551 			watermarks->c.cstate_pstate.pstate_change_ns, prog_wm_value);
552 	} else if (watermarks->c.cstate_pstate.pstate_change_ns
553 			< hubbub1->watermarks.c.cstate_pstate.pstate_change_ns)
554 		wm_pending = true;
555 
556 	/* clock state D */
557 	if (safe_to_lower || watermarks->d.cstate_pstate.pstate_change_ns
558 			> hubbub1->watermarks.d.cstate_pstate.pstate_change_ns) {
559 		hubbub1->watermarks.d.cstate_pstate.pstate_change_ns =
560 				watermarks->d.cstate_pstate.pstate_change_ns;
561 		prog_wm_value = convert_and_clamp(
562 				watermarks->d.cstate_pstate.pstate_change_ns,
563 				refclk_mhz, 0x1fffff);
564 		REG_SET_2(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, 0,
565 				DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, prog_wm_value,
566 				DCHUBBUB_ARB_VM_ROW_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, prog_wm_value);
567 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_D calculated =%d\n"
568 			"HW register value = 0x%x\n\n",
569 			watermarks->d.cstate_pstate.pstate_change_ns, prog_wm_value);
570 	} else if (watermarks->d.cstate_pstate.pstate_change_ns
571 			< hubbub1->watermarks.d.cstate_pstate.pstate_change_ns)
572 		wm_pending = true;
573 
574 	return wm_pending;
575 }
576 
577 bool hubbub21_program_watermarks(
578 		struct hubbub *hubbub,
579 		struct dcn_watermark_set *watermarks,
580 		unsigned int refclk_mhz,
581 		bool safe_to_lower)
582 {
583 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
584 	bool wm_pending = false;
585 
586 	if (hubbub21_program_urgent_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
587 		wm_pending = true;
588 
589 	if (hubbub21_program_stutter_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
590 		wm_pending = true;
591 
592 	if (hubbub21_program_pstate_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
593 		wm_pending = true;
594 
595 	/*
596 	 * The DCHub arbiter has a mechanism to dynamically rate limit the DCHub request stream to the fabric.
597 	 * If the memory controller is fully utilized and the DCHub requestors are
598 	 * well ahead of their amortized schedule, then it is safe to prevent the next winner
599 	 * from being committed and sent to the fabric.
600 	 * The utilization of the memory controller is approximated by ensuring that
601 	 * the number of outstanding requests is greater than a threshold specified
602 	 * by the ARB_MIN_REQ_OUTSTANDING. To determine that the DCHub requestors are well ahead of the amortized schedule,
603 	 * the slack of the next winner is compared with the ARB_SAT_LEVEL in DLG RefClk cycles.
604 	 *
605 	 * TODO: Revisit request limit after figure out right number. request limit for Renoir isn't decided yet, set maximum value (0x1FF)
606 	 * to turn off it for now.
607 	 */
608 	REG_SET(DCHUBBUB_ARB_SAT_LEVEL, 0,
609 			DCHUBBUB_ARB_SAT_LEVEL, 60 * refclk_mhz);
610 	REG_UPDATE_2(DCHUBBUB_ARB_DF_REQ_OUTSTAND,
611 			DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 0x1FF,
612 			DCHUBBUB_ARB_MIN_REQ_OUTSTAND_COMMIT_THRESHOLD, 0xA);
613 	REG_UPDATE(DCHUBBUB_ARB_HOSTVM_CNTL,
614 			DCHUBBUB_ARB_MAX_QOS_COMMIT_THRESHOLD, 0xF);
615 
616 	hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
617 
618 	return wm_pending;
619 }
620 
621 void hubbub21_wm_read_state(struct hubbub *hubbub,
622 		struct dcn_hubbub_wm *wm)
623 {
624 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
625 	struct dcn_hubbub_wm_set *s;
626 
627 	memset(wm, 0, sizeof(struct dcn_hubbub_wm));
628 
629 	s = &wm->sets[0];
630 	s->wm_set = 0;
631 	REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A,
632 			DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, &s->data_urgent);
633 
634 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A,
635 			DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, &s->sr_enter);
636 
637 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A,
638 			DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, &s->sr_exit);
639 
640 	REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A,
641 			 DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, &s->dram_clk_chanage);
642 
643 	s = &wm->sets[1];
644 	s->wm_set = 1;
645 	REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B,
646 			DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, &s->data_urgent);
647 
648 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B,
649 			DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, &s->sr_enter);
650 
651 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B,
652 			DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, &s->sr_exit);
653 
654 	REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B,
655 			DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, &s->dram_clk_chanage);
656 
657 	s = &wm->sets[2];
658 	s->wm_set = 2;
659 	REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C,
660 			DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_C, &s->data_urgent);
661 
662 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C,
663 			DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C, &s->sr_enter);
664 
665 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C,
666 			DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C, &s->sr_exit);
667 
668 	REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C,
669 			DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, &s->dram_clk_chanage);
670 
671 	s = &wm->sets[3];
672 	s->wm_set = 3;
673 	REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D,
674 			DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_D, &s->data_urgent);
675 
676 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D,
677 			DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D, &s->sr_enter);
678 
679 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D,
680 			DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D, &s->sr_exit);
681 
682 	REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D,
683 			DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, &s->dram_clk_chanage);
684 }
685 
686 void hubbub21_apply_DEDCN21_147_wa(struct hubbub *hubbub)
687 {
688 	struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
689 	uint32_t prog_wm_value;
690 
691 	prog_wm_value = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A);
692 	REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, prog_wm_value);
693 }
694 
695 static const struct hubbub_funcs hubbub21_funcs = {
696 	.update_dchub = hubbub2_update_dchub,
697 	.init_dchub_sys_ctx = hubbub21_init_dchub,
698 	.init_vm_ctx = hubbub2_init_vm_ctx,
699 	.dcc_support_swizzle = hubbub2_dcc_support_swizzle,
700 	.dcc_support_pixel_format = hubbub2_dcc_support_pixel_format,
701 	.get_dcc_compression_cap = hubbub2_get_dcc_compression_cap,
702 	.wm_read_state = hubbub21_wm_read_state,
703 	.get_dchub_ref_freq = hubbub2_get_dchub_ref_freq,
704 	.program_watermarks = hubbub21_program_watermarks,
705 	.allow_self_refresh_control = hubbub1_allow_self_refresh_control,
706 	.apply_DEDCN21_147_wa = hubbub21_apply_DEDCN21_147_wa,
707 };
708 
709 void hubbub21_construct(struct dcn20_hubbub *hubbub,
710 	struct dc_context *ctx,
711 	const struct dcn_hubbub_registers *hubbub_regs,
712 	const struct dcn_hubbub_shift *hubbub_shift,
713 	const struct dcn_hubbub_mask *hubbub_mask)
714 {
715 	hubbub->base.ctx = ctx;
716 
717 	hubbub->base.funcs = &hubbub21_funcs;
718 
719 	hubbub->regs = hubbub_regs;
720 	hubbub->shifts = hubbub_shift;
721 	hubbub->masks = hubbub_mask;
722 
723 	hubbub->debug_test_index_pstate = 0xB;
724 	hubbub->detile_buf_size = 164 * 1024; /* 164KB for DCN2.0 */
725 }
726