xref: /linux/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: MIT
2 //
3 // Copyright 2025 Advanced Micro Devices, Inc.
4 
5 #include "dcn60_hubbub.h"
6 #include "dm_services.h"
7 #include "reg_helper.h"
8 #include "fixed31_32.h"
9 
10 #define CTX \
11 	hubbub2->base.ctx
12 #define DC_LOGGER \
13 	hubbub2->base.ctx->logger
14 #define REG(reg)\
15 	hubbub2->regs->reg
16 
17 #undef FN
18 #define FN(reg_name, field_name) \
19 	hubbub2->shifts->field_name, hubbub2->masks->field_name
20 
21 static void dcn60_init_crb(struct hubbub *hubbub)
22 {
23 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
24 
25 	REG_GET(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT,
26 		&hubbub2->det0_size);
27 
28 	REG_GET(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT,
29 		&hubbub2->det1_size);
30 
31 	REG_GET(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT,
32 		&hubbub2->det2_size);
33 
34 	REG_GET(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT,
35 		&hubbub2->det3_size);
36 
37 	REG_GET(DCHUBBUB_COMPBUF_CTRL, COMPBUF_SIZE_CURRENT,
38 		&hubbub2->compbuf_size_segments);
39 
40 	REG_SET(COMPBUF_RESERVED_SPACE, 0,
41 			COMPBUF_RESERVED_SPACE_64B, hubbub2->pixel_chunk_size / 32); // 256 64Bytes
42 }
43 
44 static void dcn60_program_det_segments(struct hubbub *hubbub, int hubp_inst, unsigned int det_buffer_size_seg)
45 {
46 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
47 
48 	switch (hubp_inst) {
49 	case 0:
50 		REG_UPDATE(DCHUBBUB_DET0_CTRL,
51 			DET0_SIZE, det_buffer_size_seg);
52 		hubbub2->det0_size = det_buffer_size_seg;
53 		break;
54 	case 1:
55 		REG_UPDATE(DCHUBBUB_DET1_CTRL,
56 			DET1_SIZE, det_buffer_size_seg);
57 		hubbub2->det1_size = det_buffer_size_seg;
58 		break;
59 	case 2:
60 		REG_UPDATE(DCHUBBUB_DET2_CTRL,
61 			DET2_SIZE, det_buffer_size_seg);
62 		hubbub2->det2_size = det_buffer_size_seg;
63 		break;
64 	case 3:
65 		REG_UPDATE(DCHUBBUB_DET3_CTRL,
66 			DET3_SIZE, det_buffer_size_seg);
67 		hubbub2->det3_size = det_buffer_size_seg;
68 		break;
69 	default:
70 		break;
71 	}
72 	if (hubbub2->det0_size + hubbub2->det1_size + hubbub2->det2_size
73 		+ hubbub2->det3_size + hubbub2->compbuf_size_segments > hubbub2->crb_size_segs) {
74 		/* This may happen during seamless transition from ODM 2:1 to ODM4:1 */
75 		DC_LOG_WARNING("CRB Config Warning: DET size (%d,%d,%d,%d) + Compbuf size (%d) >  CRB segments (%d)\n",
76 			hubbub2->det0_size, hubbub2->det1_size, hubbub2->det2_size, hubbub2->det3_size,
77 			hubbub2->compbuf_size_segments, hubbub2->crb_size_segs);
78 	}
79 }
80 
81 static void dcn60_program_compbuf_segments(struct hubbub *hubbub, unsigned int compbuf_size_seg, bool safe_to_increase)
82 {
83 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
84 
85 	if (safe_to_increase || compbuf_size_seg <= hubbub2->compbuf_size_segments) {
86 		if (compbuf_size_seg > hubbub2->compbuf_size_segments) {
87 			REG_WAIT(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, hubbub2->det0_size, 1, 100);
88 			REG_WAIT(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, hubbub2->det1_size, 1, 100);
89 			REG_WAIT(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, hubbub2->det2_size, 1, 100);
90 			REG_WAIT(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, hubbub2->det3_size, 1, 100);
91 		}
92 		/* Should never be hit, if it is we have an erroneous hw config*/
93 		ASSERT(hubbub2->det0_size + hubbub2->det1_size + hubbub2->det2_size
94 			+ hubbub2->det3_size + compbuf_size_seg <= hubbub2->crb_size_segs);
95 		REG_UPDATE(DCHUBBUB_COMPBUF_CTRL, COMPBUF_SIZE, compbuf_size_seg);
96 		hubbub2->compbuf_size_segments = compbuf_size_seg;
97 	}
98 }
99 
100 static void dcn60_wait_for_det_update(struct hubbub *hubbub, int hubp_inst)
101 {
102 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
103 
104 	switch (hubp_inst) {
105 	case 0:
106 		REG_WAIT(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, hubbub2->det0_size, 1, 100000); /* 1 vupdate at 10hz */
107 		break;
108 	case 1:
109 		REG_WAIT(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, hubbub2->det1_size, 1, 100000);
110 		break;
111 	case 2:
112 		REG_WAIT(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, hubbub2->det2_size, 1, 100000);
113 		break;
114 	case 3:
115 		REG_WAIT(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, hubbub2->det3_size, 1, 100000);
116 		break;
117 	default:
118 		break;
119 	}
120 }
121 
122 static bool hubbub60_program_urgent_watermarks(
123 		struct hubbub *hubbub,
124 		union dcn_watermark_set *watermarks,
125 		unsigned int refclk_mhz,
126 		bool safe_to_lower)
127 {
128 	(void)refclk_mhz;
129 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
130 	bool wm_pending = false;
131 
132 	/* Repeat for water mark set A and B */
133 	/* clock state A */
134 	if (safe_to_lower || watermarks->dcn4x.a.urgent > hubbub2->watermarks.dcn4x.a.urgent) {
135 		hubbub2->watermarks.dcn4x.a.urgent = watermarks->dcn4x.a.urgent;
136 		REG_SET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, 0,
137 				DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, watermarks->dcn4x.a.urgent);
138 		DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_A calculated =%d\n"
139 			"HW register value = 0x%x\n",
140 			watermarks->dcn4x.a.urgent, watermarks->dcn4x.a.urgent);
141 	} else if (watermarks->dcn4x.a.urgent < hubbub2->watermarks.dcn4x.a.urgent)
142 		wm_pending = true;
143 
144 	/* determine the transfer time for a quantity of data for a particular requestor.*/
145 	if (safe_to_lower || watermarks->dcn4x.a.frac_urg_bw_flip
146 			> hubbub2->watermarks.dcn4x.a.frac_urg_bw_flip) {
147 		hubbub2->watermarks.dcn4x.a.frac_urg_bw_flip = watermarks->dcn4x.a.frac_urg_bw_flip;
148 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, 0,
149 				DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A, watermarks->dcn4x.a.frac_urg_bw_flip);
150 	} else if (watermarks->dcn4x.a.frac_urg_bw_flip
151 			< hubbub2->watermarks.dcn4x.a.frac_urg_bw_flip)
152 		wm_pending = true;
153 
154 	if (safe_to_lower || watermarks->dcn4x.a.frac_urg_bw_nom
155 			> hubbub2->watermarks.dcn4x.a.frac_urg_bw_nom) {
156 		hubbub2->watermarks.dcn4x.a.frac_urg_bw_nom = watermarks->dcn4x.a.frac_urg_bw_nom;
157 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, 0,
158 				DCHUBBUB_ARB_FRAC_URG_BW_NOM_A, watermarks->dcn4x.a.frac_urg_bw_nom);
159 	} else if (watermarks->dcn4x.a.frac_urg_bw_nom
160 			< hubbub2->watermarks.dcn4x.a.frac_urg_bw_nom)
161 		wm_pending = true;
162 
163 	if (safe_to_lower ||
164 		watermarks->dcn4x.a.refcyc_per_trip_to_mem > hubbub2->watermarks.dcn4x.a.refcyc_per_trip_to_mem) {
165 		hubbub2->watermarks.dcn4x.a.refcyc_per_trip_to_mem = watermarks->dcn4x.a.refcyc_per_trip_to_mem;
166 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, 0,
167 				DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A, watermarks->dcn4x.a.refcyc_per_trip_to_mem);
168 	} else if (watermarks->dcn4x.a.refcyc_per_trip_to_mem < hubbub2->watermarks.dcn4x.a.refcyc_per_trip_to_mem)
169 		wm_pending = true;
170 
171 	if (safe_to_lower ||
172 		watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem > hubbub2->watermarks.dcn4x.a.refcyc_per_meta_trip_to_mem) {
173 		hubbub2->watermarks.dcn4x.a.refcyc_per_meta_trip_to_mem = watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem;
174 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A, 0,
175 				DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A, watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem);
176 	} else if (watermarks->dcn4x.a.refcyc_per_meta_trip_to_mem <
177 								hubbub2->watermarks.dcn4x.a.refcyc_per_meta_trip_to_mem)
178 		wm_pending = true;
179 
180 	if (safe_to_lower || watermarks->dcn4x.a.buffer_fullness > hubbub2->watermarks.dcn4x.a.buffer_fullness) {
181 		hubbub2->watermarks.dcn4x.a.buffer_fullness = watermarks->dcn4x.a.buffer_fullness;
182 		REG_SET(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A, 0,
183 			DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A, watermarks->dcn4x.a.buffer_fullness);
184 	} else if (watermarks->dcn4x.a.buffer_fullness < hubbub2->watermarks.dcn4x.a.buffer_fullness)
185 		wm_pending = true;
186 
187 	/* clock state B */
188 	if (safe_to_lower || watermarks->dcn4x.b.urgent > hubbub2->watermarks.dcn4x.b.urgent) {
189 		hubbub2->watermarks.dcn4x.b.urgent = watermarks->dcn4x.b.urgent;
190 		REG_SET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, 0,
191 				DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, watermarks->dcn4x.b.urgent);
192 		DC_LOG_BANDWIDTH_CALCS("URGENCY_WATERMARK_B calculated =%d\n"
193 			"HW register value = 0x%x\n",
194 			watermarks->dcn4x.b.urgent, watermarks->dcn4x.b.urgent);
195 	} else if (watermarks->dcn4x.b.urgent < hubbub2->watermarks.dcn4x.b.urgent)
196 		wm_pending = true;
197 
198 	/* determine the transfer time for a quantity of data for a particular requestor.*/
199 	if (safe_to_lower || watermarks->dcn4x.b.frac_urg_bw_flip
200 			> hubbub2->watermarks.dcn4x.b.frac_urg_bw_flip) {
201 		hubbub2->watermarks.dcn4x.b.frac_urg_bw_flip = watermarks->dcn4x.b.frac_urg_bw_flip;
202 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, 0,
203 				DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, watermarks->dcn4x.b.frac_urg_bw_flip);
204 	} else if (watermarks->dcn4x.b.frac_urg_bw_flip
205 			< hubbub2->watermarks.dcn4x.b.frac_urg_bw_flip)
206 		wm_pending = true;
207 
208 	if (safe_to_lower || watermarks->dcn4x.b.frac_urg_bw_nom
209 			> hubbub2->watermarks.dcn4x.b.frac_urg_bw_nom) {
210 		hubbub2->watermarks.dcn4x.b.frac_urg_bw_nom = watermarks->dcn4x.b.frac_urg_bw_nom;
211 		REG_SET(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, 0,
212 				DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, watermarks->dcn4x.b.frac_urg_bw_nom);
213 	} else if (watermarks->dcn4x.b.frac_urg_bw_nom
214 			< hubbub2->watermarks.dcn4x.b.frac_urg_bw_nom)
215 		wm_pending = true;
216 
217 	if (safe_to_lower ||
218 		watermarks->dcn4x.b.refcyc_per_trip_to_mem > hubbub2->watermarks.dcn4x.b.refcyc_per_trip_to_mem) {
219 		hubbub2->watermarks.dcn4x.b.refcyc_per_trip_to_mem = watermarks->dcn4x.b.refcyc_per_trip_to_mem;
220 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, 0,
221 				DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, watermarks->dcn4x.b.refcyc_per_trip_to_mem);
222 	} else if (watermarks->dcn4x.b.refcyc_per_trip_to_mem < hubbub2->watermarks.dcn4x.b.refcyc_per_trip_to_mem)
223 		wm_pending = true;
224 
225 	if (safe_to_lower ||
226 		watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem > hubbub2->watermarks.dcn4x.b.refcyc_per_meta_trip_to_mem) {
227 		hubbub2->watermarks.dcn4x.b.refcyc_per_meta_trip_to_mem = watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem;
228 		REG_SET(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, 0,
229 				DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem);
230 	} else if (watermarks->dcn4x.b.refcyc_per_meta_trip_to_mem <
231 								hubbub2->watermarks.dcn4x.b.refcyc_per_meta_trip_to_mem)
232 		wm_pending = true;
233 
234 	if (safe_to_lower || watermarks->dcn4x.b.buffer_fullness > hubbub2->watermarks.dcn4x.b.buffer_fullness) {
235 		hubbub2->watermarks.dcn4x.b.buffer_fullness = watermarks->dcn4x.b.buffer_fullness;
236 		REG_SET(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, 0,
237 			DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, watermarks->dcn4x.b.buffer_fullness);
238 	} else if (watermarks->dcn4x.b.buffer_fullness < hubbub2->watermarks.dcn4x.b.buffer_fullness)
239 		wm_pending = true;
240 
241 	return wm_pending;
242 }
243 
244 static bool hubbub60_program_pstate_watermarks(
245 		struct hubbub *hubbub,
246 		union dcn_watermark_set *watermarks,
247 		unsigned int refclk_mhz,
248 		bool safe_to_lower)
249 {
250 	(void)refclk_mhz;
251 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
252 	bool wm_pending = false;
253 
254 	/* Section for UCLK_PSTATE_CHANGE_WATERMARKS, used for UCLK change (UCLK/FCLK PState) */
255 	/* clock state A */
256 	if (safe_to_lower || watermarks->dcn4x.a.uclk_pstate
257 			> hubbub2->watermarks.dcn4x.a.uclk_pstate) {
258 		hubbub2->watermarks.dcn4x.a.uclk_pstate =
259 				watermarks->dcn4x.a.uclk_pstate;
260 		REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, 0,
261 				DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, watermarks->dcn4x.a.uclk_pstate);
262 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_A calculated =%d\n"
263 			"HW register value = 0x%x\n\n",
264 			watermarks->dcn4x.a.uclk_pstate, watermarks->dcn4x.a.uclk_pstate);
265 	} else if (watermarks->dcn4x.a.uclk_pstate
266 			< hubbub2->watermarks.dcn4x.a.uclk_pstate)
267 		wm_pending = true;
268 
269 	/* clock state B */
270 	if (safe_to_lower || watermarks->dcn4x.b.uclk_pstate
271 			> hubbub2->watermarks.dcn4x.b.uclk_pstate) {
272 		hubbub2->watermarks.dcn4x.b.uclk_pstate =
273 				watermarks->dcn4x.b.uclk_pstate;
274 		REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, 0,
275 				DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, watermarks->dcn4x.b.uclk_pstate);
276 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK_B calculated =%d\n"
277 			"HW register value = 0x%x\n\n",
278 			watermarks->dcn4x.b.uclk_pstate, watermarks->dcn4x.b.uclk_pstate);
279 	} else if (watermarks->dcn4x.b.uclk_pstate
280 			< hubbub2->watermarks.dcn4x.b.uclk_pstate)
281 		wm_pending = true;
282 
283 	/* Section for UCLK_PSTATE_CHANGE_WATERMARKS1 (Reserved set, can be used for FCLK Pstate only) */
284 	if (safe_to_lower || watermarks->dcn4x.a.fclk_pstate
285 			> hubbub2->watermarks.dcn4x.a.fclk_pstate) {
286 		hubbub2->watermarks.dcn4x.a.fclk_pstate =
287 				watermarks->dcn4x.a.fclk_pstate;
288 		REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, 0,
289 				DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, watermarks->dcn4x.a.fclk_pstate);
290 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK1_A calculated =%d\n"
291 			"HW register value = 0x%x\n\n",
292 			watermarks->dcn4x.a.fclk_pstate, watermarks->dcn4x.a.fclk_pstate);
293 	} else if (watermarks->dcn4x.a.fclk_pstate
294 			< hubbub2->watermarks.dcn4x.a.fclk_pstate)
295 		wm_pending = true;
296 
297 	/* clock state B */
298 	if (safe_to_lower || watermarks->dcn4x.b.fclk_pstate
299 			> hubbub2->watermarks.dcn4x.b.fclk_pstate) {
300 		hubbub2->watermarks.dcn4x.b.fclk_pstate =
301 				watermarks->dcn4x.b.fclk_pstate;
302 		REG_SET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, 0,
303 				DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, watermarks->dcn4x.b.fclk_pstate);
304 		DC_LOG_BANDWIDTH_CALCS("DRAM_CLK_CHANGE_WATERMARK1_B calculated =%d\n"
305 			"HW register value = 0x%x\n\n",
306 			watermarks->dcn4x.b.fclk_pstate, watermarks->dcn4x.b.fclk_pstate);
307 	} else if (watermarks->dcn4x.b.fclk_pstate
308 			< hubbub2->watermarks.dcn4x.b.fclk_pstate)
309 		wm_pending = true;
310 
311 	/* Section for FCLK_PSTATE_CHANGE_WATERMARKS, instance 0 used for G7 PPT */
312 	/* clock state A */
313 	if (safe_to_lower || watermarks->dcn4x.a.ppt
314 			> hubbub2->watermarks.dcn4x.a.ppt) {
315 		hubbub2->watermarks.dcn4x.a.ppt =
316 				watermarks->dcn4x.a.ppt;
317 		REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A, 0,
318 				DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A, watermarks->dcn4x.a.ppt);
319 		DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK_A calculated =%d\n"
320 			"HW register value = 0x%x\n\n",
321 			watermarks->dcn4x.a.ppt, watermarks->dcn4x.a.ppt);
322 	} else if (watermarks->dcn4x.a.ppt
323 			< hubbub2->watermarks.dcn4x.a.ppt)
324 		wm_pending = true;
325 
326 	/* clock state B */
327 	if (safe_to_lower || watermarks->dcn4x.b.ppt
328 			> hubbub2->watermarks.dcn4x.b.ppt) {
329 		hubbub2->watermarks.dcn4x.b.ppt =
330 				watermarks->dcn4x.b.ppt;
331 		REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, 0,
332 				DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, watermarks->dcn4x.b.ppt);
333 		DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK_B calculated =%d\n"
334 			"HW register value = 0x%x\n\n",
335 			watermarks->dcn4x.b.ppt, watermarks->dcn4x.b.ppt);
336 	} else if (watermarks->dcn4x.b.ppt
337 			< hubbub2->watermarks.dcn4x.b.ppt)
338 		wm_pending = true;
339 
340 	/* Section for FCLK_CHANGE_WATERMARKS1, instance 1 used for G7 Temp Read */
341 	if (safe_to_lower || watermarks->dcn4x.a.temp_read
342 			> hubbub2->watermarks.dcn4x.a.temp_read) {
343 		hubbub2->watermarks.dcn4x.a.temp_read =
344 				watermarks->dcn4x.a.temp_read;
345 		REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A, 0,
346 				DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A, watermarks->dcn4x.a.temp_read);
347 		DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK1_A calculated =%d\n"
348 			"HW register value = 0x%x\n\n",
349 			watermarks->dcn4x.a.temp_read, watermarks->dcn4x.a.temp_read);
350 	} else if (watermarks->dcn4x.a.temp_read
351 			< hubbub2->watermarks.dcn4x.a.temp_read)
352 		wm_pending = true;
353 
354 	/* clock state B */
355 	if (safe_to_lower || watermarks->dcn4x.b.temp_read
356 			> hubbub2->watermarks.dcn4x.b.temp_read) {
357 		hubbub2->watermarks.dcn4x.b.temp_read =
358 				watermarks->dcn4x.b.temp_read;
359 		REG_SET(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, 0,
360 				DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, watermarks->dcn4x.b.temp_read);
361 		DC_LOG_BANDWIDTH_CALCS("FCLK_CHANGE_WATERMARK1_B calculated =%d\n"
362 			"HW register value = 0x%x\n\n",
363 			watermarks->dcn4x.b.temp_read, watermarks->dcn4x.b.temp_read);
364 	} else if (watermarks->dcn4x.b.temp_read
365 			< hubbub2->watermarks.dcn4x.b.temp_read)
366 		wm_pending = true;
367 
368 	return wm_pending;
369 }
370 
371 static bool hubbub60_program_stutter_watermarks(
372 		struct hubbub *hubbub,
373 		union dcn_watermark_set *watermarks,
374 		unsigned int refclk_mhz,
375 		bool safe_to_lower)
376 {
377 	(void)refclk_mhz;
378 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
379 	bool wm_pending = false;
380 
381 	/* clock state A */
382 	if (safe_to_lower || watermarks->dcn4x.a.sr_enter
383 			> hubbub2->watermarks.dcn4x.a.sr_enter) {
384 		hubbub2->watermarks.dcn4x.a.sr_enter =
385 				watermarks->dcn4x.a.sr_enter;
386 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, 0,
387 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, watermarks->dcn4x.a.sr_enter);
388 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_A calculated =%d\n"
389 			"HW register value = 0x%x\n",
390 			watermarks->dcn4x.a.sr_enter, watermarks->dcn4x.a.sr_enter);
391 		// On dGPU Z states are N/A, so program unused Stutter Enter wm A with the same value
392 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, 0,
393 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, watermarks->dcn4x.a.sr_enter);
394 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, 0,
395 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, watermarks->dcn4x.a.sr_enter);
396 
397 	}
398 	/* possible 2nd stutter watermark. PMFW to choose which one to use */
399 	if (safe_to_lower || watermarks->dcn4x.a.sr_enter_low_power
400 			> hubbub2->watermarks.dcn4x.a.sr_enter_low_power) {
401 		hubbub2->watermarks.dcn4x.a.sr_enter_low_power =
402 				watermarks->dcn4x.a.sr_enter_low_power;
403 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, 0,
404 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, watermarks->dcn4x.a.sr_enter_low_power);
405 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK1_A calculated =%d\n"
406 			"HW register value = 0x%x\n",
407 			watermarks->dcn4x.a.sr_enter_low_power, watermarks->dcn4x.a.sr_enter_low_power);
408 	}
409 
410 	if (watermarks->dcn4x.a.sr_enter
411 			< hubbub2->watermarks.dcn4x.a.sr_enter ||
412 			watermarks->dcn4x.a.sr_enter_low_power
413 			< hubbub2->watermarks.dcn4x.a.sr_enter_low_power)
414 		wm_pending = true;
415 
416 	if (safe_to_lower || watermarks->dcn4x.a.sr_exit
417 			> hubbub2->watermarks.dcn4x.a.sr_exit) {
418 		hubbub2->watermarks.dcn4x.a.sr_exit =
419 				watermarks->dcn4x.a.sr_exit;
420 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, 0,
421 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, watermarks->dcn4x.a.sr_exit);
422 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_A calculated =%d\n"
423 			"HW register value = 0x%x\n",
424 			watermarks->dcn4x.a.sr_exit, watermarks->dcn4x.a.sr_exit);
425 		// On dGPU Z states are N/A, so program unused Stutter Exit wm A with the same value
426 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, 0,
427 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, watermarks->dcn4x.a.sr_exit);
428 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, 0,
429 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, watermarks->dcn4x.a.sr_exit);
430 	}
431 	/* possible 2nd stutter exit watermark. PMFW to choose which one to use */
432 	if (safe_to_lower || watermarks->dcn4x.a.sr_exit_low_power
433 			> hubbub2->watermarks.dcn4x.a.sr_exit_low_power) {
434 		hubbub2->watermarks.dcn4x.a.sr_exit_low_power =
435 				watermarks->dcn4x.a.sr_exit_low_power;
436 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, 0,
437 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, watermarks->dcn4x.a.sr_exit_low_power);
438 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK1_A calculated =%d\n"
439 			"HW register value = 0x%x\n",
440 			watermarks->dcn4x.a.sr_exit_low_power, watermarks->dcn4x.a.sr_exit_low_power);
441 	}
442 
443 	if (watermarks->dcn4x.a.sr_exit
444 			< hubbub2->watermarks.dcn4x.a.sr_exit ||
445 			watermarks->dcn4x.a.sr_exit_low_power
446 			< hubbub2->watermarks.dcn4x.a.sr_exit_low_power)
447 		wm_pending = true;
448 
449 	/* clock state B */
450 	if (safe_to_lower || watermarks->dcn4x.b.sr_enter
451 			> hubbub2->watermarks.dcn4x.b.sr_enter) {
452 		hubbub2->watermarks.dcn4x.b.sr_enter =
453 				watermarks->dcn4x.b.sr_enter;
454 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, 0,
455 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, watermarks->dcn4x.b.sr_enter);
456 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK_B calculated =%d\n"
457 			"HW register value = 0x%x\n",
458 			watermarks->dcn4x.b.sr_enter, watermarks->dcn4x.b.sr_enter);
459 		// On dGPU Z states are N/A, so program unused Stutter Enter wm B with the same value
460 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, 0,
461 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, watermarks->dcn4x.b.sr_enter);
462 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, 0,
463 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, watermarks->dcn4x.b.sr_enter);
464 	}
465 	/* possible 2nd stutter enter watermark. PMFW to choose which one to use */
466 	if (safe_to_lower || watermarks->dcn4x.b.sr_enter_low_power
467 			> hubbub2->watermarks.dcn4x.b.sr_enter_low_power) {
468 		hubbub2->watermarks.dcn4x.b.sr_enter_low_power =
469 				watermarks->dcn4x.b.sr_enter_low_power;
470 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, 0,
471 				DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, watermarks->dcn4x.b.sr_enter_low_power);
472 		DC_LOG_BANDWIDTH_CALCS("SR_ENTER_EXIT_WATERMARK1_B calculated =%d\n"
473 			"HW register value = 0x%x\n",
474 			watermarks->dcn4x.b.sr_enter_low_power, watermarks->dcn4x.b.sr_enter_low_power);
475 	}
476 
477 	if (watermarks->dcn4x.b.sr_enter
478 			< hubbub2->watermarks.dcn4x.b.sr_enter ||
479 			watermarks->dcn4x.b.sr_enter_low_power
480 			< hubbub2->watermarks.dcn4x.b.sr_enter_low_power)
481 		wm_pending = true;
482 
483 	if (safe_to_lower || watermarks->dcn4x.b.sr_exit
484 			> hubbub2->watermarks.dcn4x.b.sr_exit) {
485 		hubbub2->watermarks.dcn4x.b.sr_exit =
486 				watermarks->dcn4x.b.sr_exit;
487 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, 0,
488 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, watermarks->dcn4x.b.sr_exit);
489 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK_B calculated =%d\n"
490 			"HW register value = 0x%x\n",
491 			watermarks->dcn4x.b.sr_exit, watermarks->dcn4x.b.sr_exit);
492 		// On dGPU Z states are N/A, so program unused Stutter Exit wm B with the same value
493 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, 0,
494 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, watermarks->dcn4x.b.sr_exit);
495 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, 0,
496 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, watermarks->dcn4x.b.sr_exit);
497 	}
498 	/* possible 2nd stutter exit watermark. PMFW to choose which one to use */
499 	if (safe_to_lower || watermarks->dcn4x.b.sr_exit_low_power
500 			> hubbub2->watermarks.dcn4x.b.sr_exit_low_power) {
501 		hubbub2->watermarks.dcn4x.b.sr_exit_low_power =
502 				watermarks->dcn4x.b.sr_exit_low_power;
503 		REG_SET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, 0,
504 				DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, watermarks->dcn4x.b.sr_exit_low_power);
505 		DC_LOG_BANDWIDTH_CALCS("SR_EXIT_WATERMARK1_B calculated =%d\n"
506 			"HW register value = 0x%x\n",
507 			watermarks->dcn4x.b.sr_exit_low_power, watermarks->dcn4x.b.sr_exit_low_power);
508 	}
509 
510 	if (watermarks->dcn4x.b.sr_exit
511 			< hubbub2->watermarks.dcn4x.b.sr_exit ||
512 			watermarks->dcn4x.b.sr_exit_low_power
513 			< hubbub2->watermarks.dcn4x.b.sr_exit_low_power)
514 		wm_pending = true;
515 
516 	return wm_pending;
517 }
518 
519 static bool hubbub60_program_watermarks(
520 		struct hubbub *hubbub,
521 		union dcn_watermark_set *watermarks,
522 		unsigned int refclk_mhz,
523 		bool safe_to_lower)
524 {
525 	bool wm_pending = false;
526 
527 	if (hubbub60_program_urgent_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
528 		wm_pending = true;
529 
530 	if (hubbub60_program_stutter_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
531 		wm_pending = true;
532 
533 	if (hubbub60_program_pstate_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
534 		wm_pending = true;
535 
536 	hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
537 
538 	return wm_pending;
539 }
540 
541 /* Copy values from WM set A to all other sets */
542 static void hubbub60_init_watermarks(struct hubbub *hubbub)
543 {
544 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
545 	uint32_t reg;
546 
547 	reg = REG_READ(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A);
548 	REG_WRITE(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, reg);
549 
550 	reg = REG_READ(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_A);
551 	REG_WRITE(DCHUBBUB_ARB_FRAC_URG_BW_FLIP_B, reg);
552 
553 	reg = REG_READ(DCHUBBUB_ARB_FRAC_URG_BW_NOM_A);
554 	REG_WRITE(DCHUBBUB_ARB_FRAC_URG_BW_NOM_B, reg);
555 
556 	reg = REG_READ(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_A);
557 	REG_WRITE(DCHUBBUB_ARB_REFCYC_PER_TRIP_TO_MEMORY_B, reg);
558 
559 	reg = REG_READ(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_A);
560 	REG_WRITE(DCHUBBUB_ARB_REFCYC_PER_META_TRIP_B, reg);
561 
562 	reg = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A);
563 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, reg);
564 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_A, reg);
565 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK1_B, reg);
566 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_A, reg);
567 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK2_B, reg);
568 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_A, reg);
569 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK3_B, reg);
570 
571 	reg = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A);
572 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, reg);
573 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_A, reg);
574 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK1_B, reg);
575 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_A, reg);
576 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK2_B, reg);
577 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_A, reg);
578 	REG_WRITE(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK3_B, reg);
579 
580 	reg = REG_READ(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A);
581 	REG_WRITE(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, reg);
582 	reg = REG_READ(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A);
583 	REG_WRITE(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, reg);
584 
585 	reg = REG_READ(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_A);
586 	REG_WRITE(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK_B, reg);
587 	reg = REG_READ(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_A);
588 	REG_WRITE(DCHUBBUB_ARB_FCLK_PSTATE_CHANGE_WATERMARK1_B, reg);
589 
590 	reg = REG_READ(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_A);
591 	REG_WRITE(DCHUBBUB_ARB_BUFFER_FULLNESS_WATERMARK_B, reg);
592 }
593 
594 static void hubbub60_force_wm_propagate_to_pipes(struct hubbub *hubbub)
595 {
596 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
597 
598 	REG_SET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, 0,
599 		DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, hubbub2->watermarks.dcn4x.a.urgent);
600 
601 }
602 
603 static void hubbub60_wm_read_state(struct hubbub *hubbub,
604 	struct dcn_hubbub_wm *wm)
605 {
606 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
607 	struct dcn_hubbub_wm_set *s;
608 
609 	memset(wm, 0, sizeof(struct dcn_hubbub_wm));
610 
611 	s = &wm->sets[0];
612 	s->wm_set = 0;
613 	REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A,
614 			DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_A, &s->data_urgent);
615 
616 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A,
617 			DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A, &s->sr_enter);
618 
619 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A,
620 			DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, &s->sr_exit);
621 
622 	REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A,
623 			 DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, &s->dram_clk_change);
624 
625 	REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A,
626 		DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_A, &s->fclk_pstate_change);
627 
628 	s = &wm->sets[1];
629 	s->wm_set = 1;
630 	REG_GET(DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B,
631 			DCHUBBUB_ARB_DATA_URGENCY_WATERMARK_B, &s->data_urgent);
632 
633 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B,
634 			DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B, &s->sr_enter);
635 
636 	REG_GET(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B,
637 			DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, &s->sr_exit);
638 
639 	REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B,
640 			DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, &s->dram_clk_change);
641 
642 	REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B,
643 		DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK1_B, &s->fclk_pstate_change);
644 }
645 
646 /**
647  * @brief Resets the performance monitor for the DCN 6.0 hubbub.
648  *
649  * This function resets the performance monitoring counters and related
650  * state for the specified hubbub instance. It is typically called to
651  * clear performance statistics before starting a new measurement period.
652  *
653  * @param hubbub Pointer to the hubbub structure to reset performance monitoring
654  * for.
655  */
656 static void hubbub60_perfmon_reset(struct hubbub *hubbub)
657 {
658 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
659 
660 	REG_WRITE(DC_PERFMON5_PERFMON_CNTL, 0);
661 	REG_WRITE(DC_PERFMON5_PERFMON_CNTL2, 0);
662 	REG_WRITE(DC_PERFMON5_PERFCOUNTER_STATE, 0);
663 	REG_WRITE(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, 0xFF00);
664 	REG_WRITE(DC_PERFMON5_PERFMON_CVALUE_LOW, 0);
665 	REG_WRITE(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0);
666 	REG_WRITE(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0);
667 }
668 
669 /**
670  * Starts measuring memory latencies (maximum, minimum, and average) in
671  * nanoseconds using performance counters.
672  *
673  * This function configures and enables performance monitoring counters 4, 5,
674  * 6, and 7 to track memory latency within the hubbub hardware block. The
675  * function sets up:
676  *
677  * Counter 4: Counts the number of memory latency samples
678  * (PERFCOUNTER_INC_MODE = 0x3 for positive edge counting)
679  * Counter 5: Accumulates total latency cycles for average calculation
680  * (PERFCOUNTER_INC_MODE = 0x2 for LSB level counting)
681  * Counter 6: Tracks maximum latency values (PERFCOUNTER_COUNTED_VALUE_TYPE =
682  * 0x1, event 74 for frame_window_refclk)
683  * Counter 7: Tracks minimum latency values (PERFCOUNTER_COUNTED_VALUE_TYPE =
684  * 0x2, event 74 for frame_window_refclk)
685  *
686  * Configuration details:
687  * - Uses Data Fabric latency source (LATENCY_SOURCE_SEL = 0x2)
688  * - Monitors all request types (UTM_FILTER_SEL = 0)
689  * - Event 79 for memory latency monitoring (counters 4, 5)
690  * - Event 74 for frame_window_refclk timing (counters 6, 7)
691  * - Counters 6 and 7 use independent state mode with restart enabled
692  * - All counters run on refclk cycles for consistent timing measurement
693  *
694  * @param hubbub Pointer to the hubbub structure representing the hardware
695  * instance.
696  */
697 static void hubbub60_perfmon_start_measuring_memory_latencies(
698 		struct hubbub *hubbub)
699 {
700 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
701 
702 	/* configure measurement control */
703 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0,
704 			DCHUBBUB_LATENCY_CNT_EN, 0x1,
705 			DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1);
706 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0,
707 			LATENCY_SOURCE_SEL, 0x2,
708 			UTM_FILTER_SEL, 0);
709 
710 	/* program counter 4 to count until duration  */
711 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
712 			PERFCOUNTER_CNTL_SEL, 0x4,
713 			PERFCOUNTER_EVENT_SEL, 79,
714 			PERFCOUNTER_CVALUE_SEL, 0x0,
715 			PERFCOUNTER_INC_MODE, 0x3,
716 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
717 			PERFCOUNTER_RUNEN_MODE, 0x0,
718 			PERFCOUNTER_RESTART_EN, 0x0,
719 			PERFCOUNTER_ACTIVE, 0x1);
720 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
721 			PERFCOUNTER_CNTL2_SEL, 0x4,
722 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0,
723 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
724 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
725 
726 	/* program counter 5 to measure accumulated latency */
727 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
728 			PERFCOUNTER_CNTL_SEL, 0x5,
729 			PERFCOUNTER_EVENT_SEL, 79,
730 			PERFCOUNTER_CVALUE_SEL, 0x0,
731 			PERFCOUNTER_INC_MODE, 0x2,
732 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
733 			PERFCOUNTER_RUNEN_MODE, 0x0,
734 			PERFCOUNTER_RESTART_EN, 0x0,
735 			PERFCOUNTER_ACTIVE, 1);
736 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
737 			PERFCOUNTER_CNTL2_SEL, 0x5,
738 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0,
739 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
740 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
741 
742 	/* program counter 6 to measure max latency */
743 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
744 			PERFCOUNTER_CNTL_SEL, 0x6,
745 			PERFCOUNTER_EVENT_SEL, 74,
746 			PERFCOUNTER_CVALUE_SEL, 0x0,
747 			PERFCOUNTER_INC_MODE, 0x2,
748 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
749 			PERFCOUNTER_RUNEN_MODE, 0x0,
750 			PERFCOUNTER_RESTART_EN, 0x1,
751 			PERFCOUNTER_ACTIVE, 1);
752 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
753 			PERFCOUNTER_CNTL2_SEL, 0x6,
754 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x1,
755 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
756 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
757 
758 	/* program counter 7 to measure min latency */
759 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
760 			PERFCOUNTER_CNTL_SEL, 0x7,
761 			PERFCOUNTER_EVENT_SEL, 74,
762 			PERFCOUNTER_CVALUE_SEL, 0x0,
763 			PERFCOUNTER_INC_MODE, 0x2,
764 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
765 			PERFCOUNTER_RUNEN_MODE, 0x0,
766 			PERFCOUNTER_RESTART_EN, 0x1,
767 			PERFCOUNTER_ACTIVE, 1);
768 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
769 			PERFCOUNTER_CNTL2_SEL, 0x7,
770 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x2,
771 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
772 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
773 
774 	/* Program perfcounter states */
775 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_STATE, 0,
776 			PERFCOUNTER_STATE_SEL6, 0x1,
777 			PERFCOUNTER_CNT6_STATE, 0x3,
778 			PERFCOUNTER_STATE_SEL7, 0x1,
779 			PERFCOUNTER_CNT7_STATE, 0x3);
780 
781 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0,
782 			PERFMON_RUN_ENABLE_START_SEL, 0x0,
783 			PERFMON_RUN_ENABLE_STOP_SEL, 0);
784 
785 	/* start the counters */
786 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0,
787 			PERFMON_STATE, 0x1,
788 			PERFMON_RPT_COUNT, 0xFFFFF);
789 }
790 
791 /**
792  * @brief Reads the current performance monitor results and calculates
793  * memory latencies in nanoseconds.
794  *
795  * This function reads the values from the DCN 6.0 hubbub performance monitor
796  * counters, then calculates the minimum, maximum, and average observed
797  * memory latencies in nanoseconds.
798  *
799  * @param hubbub Pointer to the hubbub structure representing the hardware
800  * instance.
801  * @param refclk_mhz Reference clock frequency in MHz, used for time
802  * conversion.
803  * @param min_latency_ns Optional pointer to store the minimum latency in
804  * nanoseconds.
805  * @param max_latency_ns Optional pointer to store the maximum latency in
806  * nanoseconds.
807  * @param avg_latency_ns Optional pointer to store the average latency in
808  * nanoseconds.
809  *
810  * @return The number of memory latency samples measured as a 32-bit
811  * unsigned integer.
812  */
813 static uint32_t hubbub60_perfmon_get_memory_latencies_ns(
814 		struct hubbub *hubbub, uint32_t refclk_mhz,
815 		uint32_t *min_latency_ns, uint32_t *max_latency_ns,
816 		uint32_t *avg_latency_ns)
817 {
818 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
819 	uint32_t count4 = 0, count5 = 0, count6 = 0, count7 = 0;
820 	struct fixed31_32 temp;
821 
822 	ASSERT(refclk_mhz != 0);
823 	if (refclk_mhz == 0)
824 		return 0;
825 
826 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4);
827 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4);
828 
829 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x5);
830 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count5);
831 
832 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x6);
833 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count6);
834 
835 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x7);
836 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count7);
837 
838 	if (avg_latency_ns && count4) {
839 		temp = dc_fixpt_from_fraction(count5, count4);
840 		temp = dc_fixpt_div_int(temp, refclk_mhz);
841 		*avg_latency_ns = dc_fixpt_ceil(dc_fixpt_mul_int(temp, 1000));
842 	}
843 
844 	if (max_latency_ns) {
845 		temp = dc_fixpt_from_fraction(count6, refclk_mhz);
846 		*max_latency_ns = dc_fixpt_ceil(dc_fixpt_mul_int(temp, 1000));
847 	}
848 
849 	if (min_latency_ns) {
850 		temp = dc_fixpt_from_fraction(count7, refclk_mhz);
851 		*min_latency_ns = dc_fixpt_ceil(dc_fixpt_mul_int(temp, 1000));
852 	}
853 
854 	return count4;
855 }
856 
857 /**
858  * Starts measuring urgent assertion and deassertion counts using
859  * performance counters.
860  *
861  * This function configures and enables performance monitoring counters 0,
862  * 1, and 4 to track urgent assertion and deassertion events within the
863  * hubbub hardware block. The function sets up:
864  *
865  * Counter 0: Counts urgent assertion events (PERFCOUNTER_INC_MODE = 0x3 for
866  * positive edge counting)
867  * Counter 1: Counts urgent deassertion events (PERFCOUNTER_INC_MODE = 0x4 for
868  * negative edge counting)
869  * Counter 4: Gets the current timestamp in reference clock cycles
870  *
871  * Configuration details:
872  * - Uses UTM urgent latency source (LATENCY_SOURCE_SEL = 0x4)
873  * - Monitors all request types (UTM_FILTER_SEL = 0)
874  * - Event 65 for urgent assertion/deassertion monitoring (counters 0, 1)
875  * - Event 19 for current timestamp (counter 4)
876  *
877  * @param hubbub Pointer to the hubbub structure representing the hardware
878  * instance.
879  */
880 static void hubbub60_perfmon_start_measuring_urgent_assertion_count(
881 		struct hubbub *hubbub)
882 {
883 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
884 
885 	/* configure measurement control */
886 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0,
887 			DCHUBBUB_LATENCY_CNT_EN, 0x1,
888 			DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1);
889 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0,
890 			LATENCY_SOURCE_SEL, 0x4,
891 			UTM_FILTER_SEL, 0);
892 
893 	/* program counter 0 to urgent assertions */
894 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
895 			PERFCOUNTER_CNTL_SEL, 0x0,
896 			PERFCOUNTER_EVENT_SEL, 65,
897 			PERFCOUNTER_CVALUE_SEL, 0x0,
898 			PERFCOUNTER_INC_MODE, 0x3,
899 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
900 			PERFCOUNTER_RUNEN_MODE, 0x1,
901 			PERFCOUNTER_RESTART_EN, 0x0,
902 			PERFCOUNTER_ACTIVE, 1);
903 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
904 			PERFCOUNTER_CNTL2_SEL, 0x0,
905 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0,
906 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
907 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
908 
909 	/* program counter 1 to urgent assertions */
910 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
911 			PERFCOUNTER_CNTL_SEL, 0x1,
912 			PERFCOUNTER_EVENT_SEL, 65,
913 			PERFCOUNTER_CVALUE_SEL, 0x0,
914 			PERFCOUNTER_INC_MODE, 0x4,
915 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
916 			PERFCOUNTER_RUNEN_MODE, 0x1,
917 			PERFCOUNTER_RESTART_EN, 0x0,
918 			PERFCOUNTER_ACTIVE, 1);
919 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
920 			PERFCOUNTER_CNTL2_SEL, 0x1,
921 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0,
922 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
923 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
924 
925 	/* program counter 4 to get current timestamp in refclk cycles */
926 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
927 			PERFCOUNTER_CNTL_SEL, 0x4,
928 			PERFCOUNTER_EVENT_SEL, 19,
929 			PERFCOUNTER_CVALUE_SEL, 0x0,
930 			PERFCOUNTER_INC_MODE, 0x2,
931 			PERFCOUNTER_HW_CNTL_SEL, 0x0,
932 			PERFCOUNTER_RUNEN_MODE, 0x0,
933 			PERFCOUNTER_RESTART_EN, 0x0,
934 			PERFCOUNTER_ACTIVE, 1);
935 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
936 			PERFCOUNTER_CNTL2_SEL, 0x4,
937 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0,
938 			PERFCOUNTER_HW_STOP1_SEL, 0x0,
939 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
940 
941 	REG_WRITE(DC_PERFMON5_PERFCOUNTER_STATE, 0);
942 
943 	/* start the counters */
944 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0,
945 			PERFMON_STATE, 0x1,
946 			PERFMON_RPT_COUNT, 0xFFFFF);
947 }
948 
949 /**
950  * @brief Reads the urgent assertion and deassertion counts from the
951  * performance monitor.
952  *
953  * This function reads the values from the DCN 6.0 hubbub performance monitor
954  * counters for urgent assertion and deassertion events. It also retrieves
955  * a timestamp in microseconds based on the reference clock frequency.
956  *
957  * @param hubbub Pointer to the hubbub structure representing the hardware
958  * instance.
959  * @param refclk_mhz Reference clock frequency in MHz, used for time
960  * conversion.
961  * @param assertion_count Optional pointer to store the urgent assertion count.
962  * @param deassertion_count Optional pointer to store the urgent deassertion
963  * count.
964  * @param timestamp_us Optional pointer to store the timestamp in microseconds.
965  *
966  * @return A boolean indicating whether the assertion or deassertion counts
967  * have been updated since the last read.
968  */
969 static bool hubbub60_perfmon_get_urgent_assertion_count(
970 		struct hubbub *hubbub, uint32_t refclk_mhz,
971 		uint32_t *assertion_count, uint32_t *deassertion_count,
972 		uint32_t *timestamp_us)
973 {
974 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
975 	uint32_t count0 = 0, count1 = 0, count4 = 0;
976 	bool updated = false;
977 
978 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0);
979 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0);
980 
981 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x1);
982 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count1);
983 
984 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4);
985 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4);
986 
987 	if (refclk_mhz != 0 && timestamp_us)
988 		*timestamp_us = count4 / refclk_mhz;
989 
990 	if (assertion_count && (*assertion_count != count0)) {
991 		*assertion_count = count0;
992 		updated = true;
993 	}
994 
995 	if (deassertion_count && (*deassertion_count != count1)) {
996 		*deassertion_count = count1;
997 		updated = true;
998 	}
999 
1000 	return updated;
1001 }
1002 
1003 /**
1004  * @brief Configures the performance monitor to measure the urgent ramp
1005  * latency.
1006  *
1007  * This function configures the performance monitoring counters to measure
1008  * the urgent ramp latency. It uses hardware counters 0, 1, 2, 4, 5, 6, 7
1009  * for this purpose:
1010  *
1011  * Counter 4:
1012  *   - Purpose: Begins counting on the first urgent data return, marking
1013  *     the start of the first rolling window (t_win).
1014  *   - Behavior: Signals a stop event at the end of every t_win period.
1015  *
1016  * Counter 5:
1017  *   - Purpose: Starts counting at the beginning of t_win.
1018  *   - Behavior: Signals a stop event at 1/3 of t_win.
1019  *
1020  * Counter 6:
1021  *   - Purpose: Starts counting at the beginning of t_win.
1022  *   - Behavior: Signals a stop event at 2/3 of t_win.
1023  *
1024  * After the first t_win period, counters 4, 5, and 6 will generate stop
1025  * events every 1/3 t_win. These stop events trigger counters 0, 1, and 2
1026  * respectively:
1027  *
1028  * Counter 0, 1, 2:
1029  *   - Purpose: Track the total accumulated data received during each 1/3
1030  *     t_win interval.
1031  *   - Behavior: Each counter starts on its respective stop event and stops
1032  *     after 1/3 t_win.
1033  *   - Threshold: If the total data size exceeds a precalculated threshold,
1034  *     it indicates bandwidth higher than the target for that 1/3 t_win
1035  *     period.
1036  *   - When the counter reaches the threshold cvalue, it signals a stop
1037  *     interrupt for counter 7.
1038  *
1039  * Counter 7:
1040  *   - Purpose: Measures the total time in reference clock (refclk) cycles.
1041  *   - Behavior: Starts from the first data return and stops when any of
1042  *     counters 0, 1, or 2 signal a stop interrupt.
1043  *   - Usage: The total time counted in refclk cycles is converted into
1044  *     urgent ramp latency.
1045  *
1046  * @param hubbub Pointer to the hubbub structure representing the hardware
1047  *        instance.
1048  * @param params Pointer to the urgent latency measurement parameters.
1049  */
1050 static void hubbub60_perfmon_start_measuring_urgent_ramp_latency(
1051 		struct hubbub *hubbub,
1052 		const struct hubbub_urgent_latency_params *params)
1053 {
1054 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1055 
1056 	/* configure measurement control */
1057 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0,
1058 			DCHUBBUB_LATENCY_CNT_EN, 0x1,
1059 			DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1);
1060 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0,
1061 			LATENCY_SOURCE_SEL, 0x4, // df_urgent
1062 			UTM_FILTER_SEL, 0x1);    // urgent requests
1063 
1064 	/* program counter 0 as the first bw counter */
1065 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1066 			PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0
1067 			PERFCOUNTER_EVENT_SEL, 257, // ROB output valid event
1068 			PERFCOUNTER_CVALUE_SEL, 0x7, // use cvalue bits 47-36
1069 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // individual mode
1070 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1071 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1072 			PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it is active
1073 			PERFCOUNTER_INT_EN, 0x1, // signal when the measurement is complete
1074 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1075 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1076 			PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0
1077 			PERFCOUNTER_CNTOFF_SEL, 0x4, // start when counter 4 stops
1078 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1079 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1080 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue
1081 	REG_SET_2(DC_PERFMON5_PERFCOUNTER_STATE, 0,
1082 			PERFCOUNTER_STATE_SEL0, 0x1, // independent state mode
1083 			PERFCOUNTER_CNT0_STATE, 0x3); // hw mode
1084 
1085 	/* program counter 1 as the second bw counter */
1086 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1087 			PERFCOUNTER_CNTL_SEL, 0x1, // select counter 1
1088 			PERFCOUNTER_EVENT_SEL, 257, // ROB output valid event
1089 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1090 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached.
1091 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1092 			PERFCOUNTER_CVALUE_SEL, 0x7, // use cvalue bits 47-36
1093 			PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it is active
1094 			PERFCOUNTER_INT_EN, 0x1, // signal when the measurement is complete
1095 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1096 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1097 			PERFCOUNTER_CNTL2_SEL, 0x1, // select counter 1
1098 			PERFCOUNTER_CNTOFF_SEL, 0x5, // start when counter 5 stops
1099 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1100 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1101 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue
1102 	REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE,
1103 			PERFCOUNTER_STATE_SEL1, 0x1, // independent state mode
1104 			PERFCOUNTER_CNT1_STATE, 0x3); // hw mode
1105 
1106 	/* program counter 2 as the third bw counter */
1107 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1108 			PERFCOUNTER_CNTL_SEL, 0x2, // select counter 2
1109 			PERFCOUNTER_EVENT_SEL, 257, // ROB output valid event
1110 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1111 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached.
1112 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1113 			PERFCOUNTER_CVALUE_SEL, 0x7, // use cvalue bits 47-36
1114 			PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it is active
1115 			PERFCOUNTER_INT_EN, 0x1, // signal when the measurement is complete
1116 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1117 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1118 			PERFCOUNTER_CNTL2_SEL, 0x2, // select counter 2
1119 			PERFCOUNTER_CNTOFF_SEL, 0x6, // start when counter 6 stops
1120 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1121 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1122 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue
1123 	REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE,
1124 			PERFCOUNTER_STATE_SEL2, 0x1, // independent state mode
1125 			PERFCOUNTER_CNT2_STATE, 0x3); // hw mode
1126 
1127 	/* program counter 4 as the first time window */
1128 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1129 			PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4
1130 			PERFCOUNTER_EVENT_SEL, 19, // Always 1 event
1131 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1132 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached.
1133 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1134 			PERFCOUNTER_CVALUE_SEL, 0x4, // use cvalue bits 11-0
1135 			PERFCOUNTER_RESTART_EN, 0x0, // do not restart the counter while active.
1136 			PERFCOUNTER_INT_EN, 0x0, // used for timing only, do not enable interrupt
1137 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1138 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1139 			PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4
1140 			PERFCOUNTER_CNTOFF_SEL, 0x8, // start on custom signal: latency start | counter interrupt
1141 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1142 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1143 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue
1144 	REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE,
1145 			PERFCOUNTER_STATE_SEL4, 0x1, // independent state mode
1146 			PERFCOUNTER_CNT4_STATE, 0x3); // hw mode
1147 
1148 	/* program counter 5 as the second time window */
1149 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1150 			PERFCOUNTER_CNTL_SEL, 0x5, // select counter 5
1151 			PERFCOUNTER_EVENT_SEL, 19, // Always 1 event
1152 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1153 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached.
1154 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1155 			PERFCOUNTER_CVALUE_SEL, 0x5, // use cvalue bits 23-12
1156 			PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it's active
1157 			PERFCOUNTER_OFF_MASK, 0x1, // Don't allow this counter to trigger counter 4 restart
1158 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1159 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1160 			PERFCOUNTER_CNTL2_SEL, 0x5, // select counter 5
1161 			PERFCOUNTER_CNTOFF_SEL, 0x4, // start when counter 4 stops
1162 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1163 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1164 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue
1165 	REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE,
1166 			PERFCOUNTER_STATE_SEL5, 0x1, // independent state mode
1167 			PERFCOUNTER_CNT5_STATE, 0x3); // hw mode
1168 
1169 	/* program counter 6 as the third time window */
1170 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1171 			PERFCOUNTER_CNTL_SEL, 0x6, // select counter 6
1172 			PERFCOUNTER_EVENT_SEL, 19, // Always 1 event
1173 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1174 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached.
1175 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1176 			PERFCOUNTER_CVALUE_SEL, 0x6, // use cvalue bits 35-24
1177 			PERFCOUNTER_RESTART_EN, 0x1, // restart the counter while it's active
1178 			PERFCOUNTER_OFF_MASK, 0x1, // Don't allow this counter to trigger counter 4 restart
1179 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1180 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1181 			PERFCOUNTER_CNTL2_SEL, 0x6, // select counter 6
1182 			PERFCOUNTER_CNTOFF_SEL, 0x4, // start when counter 4 stops
1183 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1184 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1185 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // stop when count reaches cvalue
1186 	REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE,
1187 			PERFCOUNTER_STATE_SEL6, 0x1, // independent state mode
1188 			PERFCOUNTER_CNT6_STATE, 0x3); // hw mode
1189 
1190 	/* program counter 7 to count urgent ramp latency from urg asserted until bw reaches threshold */
1191 	REG_SET_10(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1192 			PERFCOUNTER_CNTL_SEL, 0x7, // select counter 7
1193 			PERFCOUNTER_EVENT_SEL, 19, // Always 1 event
1194 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1195 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // hw indepedennt mode: start event on perfmon off and stop counting on cvalue reached.
1196 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1197 			PERFCOUNTER_CVALUE_SEL, 0x0, // default/not used
1198 			PERFCOUNTER_RESTART_EN, 0x0, // do not restart counter
1199 			PERFCOUNTER_INT_EN, 0x0, // Final measurement; doesn't need to interrupt
1200 			PERFCOUNTER_OFF_MASK, 0x0, // default
1201 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1202 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1203 			PERFCOUNTER_CNTL2_SEL, 0x7, // select counter 7
1204 			PERFCOUNTER_CNTOFF_SEL, 0x8, // start on custom signal: latency start | counter interrupt
1205 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1206 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored for hardware independent mode
1207 			PERFCOUNTER_HW_STOP2_SEL, 0x1); // stop on external event
1208 	REG_UPDATE_2(DC_PERFMON5_PERFCOUNTER_STATE,
1209 			PERFCOUNTER_STATE_SEL7, 0x1, // independent state mode
1210 			PERFCOUNTER_CNT7_STATE, 0x3); // hw mode
1211 
1212 	REG_SET_3(DC_PERFMON5_PERFMON_CNTL2, 0,
1213 			PERFMON_RUN_ENABLE_START_SEL, 0x0, // start on first urgent request from lat mon
1214 			PERFMON_RUN_ENABLE_STOP_SEL, 11, // stop on counter interrupt
1215 			PERFMON_CNTOFF_INT_TYPE, 0x0);
1216 
1217 	// Program Perfmon cvalue registers (example values, should be
1218 	// calculated as per doc) refclk_hz = refclk_mhz * 1,000,000 t_win_s =
1219 	// t_win_ns / 1,000,000,000 CVALUE[11:0] = refclk_hz * t_win_s =
1220 	// (refclk_mhz * t_win_ns) / 1000
1221 	struct fixed31_32 slice_size =
1222 			dc_fixpt_from_fraction((uint64_t)params->refclk_mhz * params->t_win_ns, 3000);
1223 	struct fixed31_32 threshold_bytes = dc_fixpt_div_int(
1224 			dc_fixpt_from_int(params->bandwidth_mbps * params->t_win_ns),
1225 			1000);
1226 
1227 	uint32_t cvalue_0 = dc_fixpt_floor(dc_fixpt_mul_int(slice_size, 3));
1228 	uint32_t cvalue_1 = dc_fixpt_floor(slice_size);
1229 	uint32_t cvalue_2 = dc_fixpt_floor(dc_fixpt_mul_int(slice_size, 2));
1230 	uint32_t cvalue_3 = dc_fixpt_floor(dc_fixpt_mul(
1231 			dc_fixpt_div_int(threshold_bytes, 64),
1232 			dc_fixpt_from_fraction(params->bw_factor_x1000, 1000)));
1233 
1234 	// Pack into 48 bits: [47:36][35:24][23:12][11:0]
1235 	uint64_t cvalue = ((uint64_t)cvalue_3 << 36) |
1236 			((uint64_t)cvalue_2 << 24) |
1237 			((uint64_t)cvalue_1 << 12) |
1238 			((uint64_t)cvalue_0);
1239 
1240 	REG_SET(DC_PERFMON5_PERFMON_CVALUE_INT_MISC, 0, PERFMON_CVALUE_HI, (uint32_t) (cvalue >> 32));
1241 	REG_SET(DC_PERFMON5_PERFMON_CVALUE_LOW, 0, PERFMON_CVALUE_LOW, (uint32_t) (cvalue & 0xFFFFFFFF));
1242 
1243 	/* start the counters */
1244 	REG_SET_3(DC_PERFMON5_PERFMON_CNTL, 0,
1245 			PERFMON_STATE, 0x3,
1246 			PERFMON_RPT_COUNT, 1,
1247 			PERFMON_CNTOFF_INT_EN, 0x1);
1248 }
1249 
1250 /**
1251  * @brief Reads the current performance monitor result and calculates
1252  * the urgent ramp latency in nanoseconds.
1253  *
1254  * This function reads the values from the DCN 6.0 hubbub performance
1255  * monitor counters, then calculates the urgent ramp latency in
1256  * nanoseconds.
1257  *
1258  * @param hubbub Pointer to the hubbub structure representing the
1259  * hardware instance.
1260  * @param refclk_mhz Reference clock frequency in MHz, used for time
1261  * conversion.
1262  *
1263  * @return The urgent ramp latency in nanoseconds as a 32-bit unsigned
1264  * integer.
1265  */
1266 static uint32_t hubbub60_perfmon_get_urgent_ramp_latency_ns(
1267 		struct hubbub *hubbub, uint32_t refclk_mhz)
1268 {
1269 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1270 	uint32_t count7 = 0, latency_ns = 0;
1271 	struct fixed31_32 temp;
1272 
1273 	if (refclk_mhz == 0)
1274 		return 0;
1275 
1276 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x7);
1277 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count7);
1278 
1279 	temp = dc_fixpt_from_fraction(count7, refclk_mhz);
1280 	temp = dc_fixpt_mul_int(temp, 1000);
1281 	latency_ns = dc_fixpt_ceil(temp);
1282 
1283 	return latency_ns;
1284 }
1285 
1286 /**
1287  * hubbub60_perfmon_arm_measuring_out_of_order_bandwidth - Configure the out-of-order BW counter.
1288  * @hubbub: pointer to the hubbub hardware instance
1289  *
1290  * Configures the performance monitoring counters to measure out-of-order
1291  * (peak prefetch) bandwidth using hardware counters 0, 1, and 4:
1292  *
1293  * Counter 0: count-off counter — counts response-valid events and gates the
1294  *            measurement window once it reaches its target value.
1295  * Counter 1: data counter — tracks total data received during the measurement
1296  *            period; generates an interrupt when measurement completes.
1297  * Counter 4: duration timer — measures elapsed time in refclk cycles.
1298  *
1299  * UTM_FILTER_SEL is set to 0 so that isolation comes from OTG-vblank gating
1300  * rather than the silicon-broken HW filter.  The first 200 prefetch requests
1301  * are skipped (ramp-up), and the subsequent 200 are the measurement window.
1302  *
1303  * This function configures counters only; call
1304  * hubbub60_perfmon_start_measuring_out_of_order_bandwidth() to enable them.
1305  */
1306 static void hubbub60_perfmon_arm_measuring_out_of_order_bandwidth(
1307 		struct hubbub *hubbub)
1308 {
1309 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1310 
1311 	/* configure measurement control */
1312 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0,
1313 			DCHUBBUB_LATENCY_CNT_EN, 0x1,
1314 			DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1);
1315 	/*
1316 	 * UTM_FILTER_SEL = 0: time-domain gating (OTG vblank) replaces the
1317 	 * silicon-broken HW filter that can no longer isolate prefetch traffic.
1318 	 */
1319 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0,
1320 			LATENCY_SOURCE_SEL, 0x2,
1321 			UTM_FILTER_SEL, 0x0);
1322 
1323 	/* Program counter 0 as the count off counter */
1324 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1325 			PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0
1326 			PERFCOUNTER_EVENT_SEL, 259, // response vld
1327 			PERFCOUNTER_CVALUE_SEL, 0x1, // use cvalue bits 15-0
1328 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1329 			PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode
1330 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1331 			PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done
1332 			PERFCOUNTER_ACTIVE, 1);
1333 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1334 			PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0
1335 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1336 			PERFCOUNTER_HW_STOP1_SEL, 0x1, // the stop trigger is that perfcounter meet the target CVALUE
1337 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // ignored
1338 
1339 	/* Program counter 1 to count total data received */
1340 	REG_SET_9(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1341 			PERFCOUNTER_CNTL_SEL, 0x1, // select counter 1
1342 			PERFCOUNTER_EVENT_SEL, 259, // response vld
1343 			PERFCOUNTER_CVALUE_SEL, 0x2, // use cvalue bits 31-16
1344 			PERFCOUNTER_INC_MODE, 0x2, // count LSB level
1345 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode
1346 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1347 			PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done
1348 			PERFCOUNTER_INT_EN, 1, // signal when the measurement is complete
1349 			PERFCOUNTER_ACTIVE, 0x1);
1350 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1351 			PERFCOUNTER_CNTL2_SEL, 0x1,
1352 			PERFCOUNTER_CNTOFF_SEL, 0, // start when count0 stops
1353 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1354 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored
1355 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // the stop trigger is that perfcounter meet the target CVALUE
1356 
1357 	/* Program counter 4 to count the measuring time */
1358 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1359 			PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4
1360 			PERFCOUNTER_EVENT_SEL, 19, // always 1 event
1361 			PERFCOUNTER_CVALUE_SEL, 0x0, // ignored
1362 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1363 			PERFCOUNTER_HW_CNTL_SEL, 0x1, // independent mode
1364 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1365 			PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done
1366 			PERFCOUNTER_ACTIVE, 1);
1367 	REG_SET_5(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1368 			PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4
1369 			PERFCOUNTER_CNTOFF_SEL, 0, // start when count0 stops
1370 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1371 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // ignored
1372 			PERFCOUNTER_HW_STOP2_SEL, 0x1); // the stop trigger is from the external 64 pairs of start/stop events
1373 
1374 	/* Program perfcounter states */
1375 	REG_SET_6(DC_PERFMON5_PERFCOUNTER_STATE, 0,
1376 			PERFCOUNTER_STATE_SEL0, 0x1, // independent state mode
1377 			PERFCOUNTER_CNT0_STATE, 0x3, // hw mode
1378 			PERFCOUNTER_STATE_SEL1, 0x1, // independent state mode
1379 			PERFCOUNTER_CNT1_STATE, 0x3, // hw mode
1380 			PERFCOUNTER_STATE_SEL4, 0x1, // independent state mode
1381 			PERFCOUNTER_CNT4_STATE, 0x3); // hw mode
1382 
1383 	/*
1384 	 * The cvalue is derived based on experimental results at the lowest
1385 	 * clock state. Out-of-order bandwidth requires ~200 prefetch requests
1386 	 * to ramp up to full speed; the subsequent 200 requests form the
1387 	 * measurement window.
1388 	 */
1389 	REG_SET(DC_PERFMON5_PERFMON_CVALUE_LOW, 0, PERFMON_CVALUE_LOW, 200 | 200 << 16);
1390 
1391 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0,
1392 			PERFMON_RUN_ENABLE_START_SEL, 0x0,
1393 			PERFMON_RUN_ENABLE_STOP_SEL, 11); // perfmon counter off event
1394 }
1395 
1396 /**
1397  * hubbub60_perfmon_start_measuring_out_of_order_bandwidth - Enable the out-of-order BW counter.
1398  * @hubbub: pointer to the hubbub hardware instance
1399  *
1400  * Enables the performance monitor counters previously configured by
1401  * hubbub60_perfmon_arm_measuring_out_of_order_bandwidth().  The counter
1402  * self-stops once the count-off counter reaches its target; there is no
1403  * explicit stop step for the peak-BW path.
1404  */
1405 static void hubbub60_perfmon_start_measuring_out_of_order_bandwidth(
1406 		struct hubbub *hubbub)
1407 {
1408 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1409 
1410 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0,
1411 			PERFMON_STATE, 0x3,
1412 			PERFMON_RPT_COUNT, 1);
1413 }
1414 
1415 /**
1416  * hubbub60_perfmon_get_out_of_order_bandwidth_mbps - Read out-of-order BW counter result.
1417  * @hubbub: pointer to the hubbub hardware instance
1418  * @refclk_mhz: reference clock frequency in MHz, used for duration conversion
1419  * @duration_ns: output parameter; receives the measured duration in nanoseconds
1420  *
1421  * Reads hardware counters 1 (data) and 4 (duration) and converts the raw
1422  * refclk-cycle count into bandwidth in Mbps.
1423  *
1424  * Return: out-of-order bandwidth in Mbps
1425  */
1426 static uint32_t hubbub60_perfmon_get_out_of_order_bandwidth_mbps(
1427 		struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *duration_ns)
1428 {
1429 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1430 	uint32_t count0 = 0, count1 = 0, count4 = 0,
1431 			out_of_order_bandwidth_mbps = 0, measuring_duration_ns = 0;
1432 	struct fixed31_32 temp;
1433 
1434 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0);
1435 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0);
1436 
1437 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x1);
1438 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count1);
1439 
1440 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4);
1441 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4);
1442 
1443 	if (refclk_mhz == 0)
1444 		return 0;
1445 
1446 	temp = dc_fixpt_from_fraction(count4, refclk_mhz);
1447 	temp = dc_fixpt_mul_int(temp, 1000);
1448 	measuring_duration_ns = dc_fixpt_ceil(temp);
1449 
1450 	if (duration_ns)
1451 		*duration_ns = measuring_duration_ns;
1452 	if (measuring_duration_ns == 0)
1453 		return 0;
1454 
1455 	temp = dc_fixpt_from_fraction(count1, measuring_duration_ns);
1456 	temp = dc_fixpt_mul_int(temp, 64 * 1000);
1457 	out_of_order_bandwidth_mbps = dc_fixpt_floor(temp);
1458 
1459 	return out_of_order_bandwidth_mbps;
1460 }
1461 
1462 /**
1463  * @brief Configures the performance monitor to measure in-order
1464  * bandwidth in megabits per second (Mbps).
1465  *
1466  * This function sets up performance monitoring counters to measure
1467  * in-order bandwidth. It uses hardware counters 0 and 4 for this
1468  * purpose:
1469  *
1470  * Counter 0:
1471  *   - Purpose: Tracks the total data received during the measurement
1472  *     period.
1473  *   - Behavior: Starts counting when the measurement starts and stops
1474  *     when the measurement is stopped manually.
1475  *
1476  * Counter 4:
1477  *   - Purpose: Measures the total time taken during the measurement
1478  *     period in reference clock (refclk) cycles.
1479  *   - Behavior: Starts counting when the measurement starts and stops
1480  *     when the measurement is stopped manually.
1481  *
1482  * @param hubbub Pointer to the hubbub structure representing the hardware
1483  *        instance.
1484  */
1485 static void hubbub60_perfmon_start_measuring_in_order_bandwidth(
1486 		struct hubbub *hubbub)
1487 {
1488 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1489 
1490 	/* Program Latency Monitor Registers */
1491 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0,
1492 			DCHUBBUB_LATENCY_CNT_EN, 0x1,
1493 			DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1);
1494 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0,
1495 			LATENCY_SOURCE_SEL, 0x8, // ROB latency
1496 			UTM_FILTER_SEL, 0x0);
1497 
1498 	/* Program counter 0 to count total data received */
1499 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1500 			PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0
1501 			PERFCOUNTER_EVENT_SEL, 257, // in order data
1502 			PERFCOUNTER_CVALUE_SEL, 0x0, // ignored
1503 			PERFCOUNTER_INC_MODE, 0x2, // count LSB level
1504 			PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode
1505 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1506 			PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done
1507 			PERFCOUNTER_ACTIVE, 0x1);
1508 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1509 			PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0
1510 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1511 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // stop when other counters are done
1512 			PERFCOUNTER_HW_STOP2_SEL, 0x0);
1513 
1514 	/* Program counter 4 to count the measuring time */
1515 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1516 			PERFCOUNTER_CNTL_SEL, 0x4, // select counter 4
1517 			PERFCOUNTER_EVENT_SEL, 19, // always 1 event
1518 			PERFCOUNTER_CVALUE_SEL, 0x0, // all cvalue range
1519 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1520 			PERFCOUNTER_HW_CNTL_SEL, 0x0, // simutaneous mode
1521 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1522 			PERFCOUNTER_RESTART_EN, 0x0, // stops after counting is done
1523 			PERFCOUNTER_ACTIVE, 0x1); // enable the counter
1524 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1525 			PERFCOUNTER_CNTL2_SEL, 0x4, // select counter 4
1526 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1527 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // stop when count reaches cvalue
1528 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // ignored
1529 
1530 	/* Program perfcounter states */
1531 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_STATE, 0,
1532 			PERFCOUNTER_STATE_SEL0, 0x0, // global state mode
1533 			PERFCOUNTER_CNT0_STATE, 0x0, // ignored
1534 			PERFCOUNTER_STATE_SEL4, 0x0, // global state mode
1535 			PERFCOUNTER_CNT4_STATE, 0x0); // ignored
1536 
1537 	/* start the counters */
1538 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0,
1539 			PERFMON_STATE, 1,
1540 			PERFMON_RPT_COUNT, 0xFFFFF);
1541 }
1542 
1543 /**
1544  * @brief Reads the current performance monitor result and calculates
1545  * the in-order bandwidth in Mbps.
1546  *
1547  * This function reads the values from the DCN 6.0 hubbub performance
1548  * monitor counters, then calculates the in-order bandwidth in Mbps.
1549  *
1550  * @param hubbub Pointer to the hubbub structure representing the
1551  * hardware instance.
1552  * @param refclk_mhz Reference clock frequency in MHz, used for time
1553  * conversion.
1554  * @param min_duration_ns Minimum duration in nanoseconds required for
1555  * a valid measurement.
1556  * @param duration_ns Measured duration in nanoseconds.
1557  *
1558  * @return The in-order bandwidth in Mbps as a 32-bit unsigned integer.
1559  */
1560 static uint32_t hubbub60_perfmon_get_in_order_bandwidth_mbps(
1561 		struct hubbub *hubbub, uint32_t refclk_mhz,
1562 		uint32_t min_duration_ns, uint32_t *duration_ns)
1563 {
1564 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1565 	uint32_t count0 = 0, count4 = 0, in_order_bandwidth_mbps = 0,
1566 			measuring_duration_ns = 0;
1567 	struct fixed31_32 temp;
1568 
1569 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4);
1570 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4);
1571 
1572 	if (refclk_mhz == 0)
1573 		return 0;
1574 
1575 	measuring_duration_ns = count4 * 1000 / refclk_mhz;
1576 	*duration_ns = measuring_duration_ns;
1577 	if (min_duration_ns > measuring_duration_ns)
1578 		return 0;
1579 
1580 	/* stop the counters */
1581 	REG_SET(DC_PERFMON5_PERFMON_CNTL, 0, PERFMON_STATE, 2);
1582 
1583 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0);
1584 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0);
1585 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x4);
1586 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count4);
1587 
1588 	ASSERT(count4);
1589 	temp = dc_fixpt_from_fraction(count4, refclk_mhz);
1590 	temp = dc_fixpt_mul_int(temp, 1000);
1591 	measuring_duration_ns = dc_fixpt_ceil(temp);
1592 	if (duration_ns)
1593 		*duration_ns = measuring_duration_ns;
1594 
1595 	temp = dc_fixpt_from_fraction(count0, measuring_duration_ns);
1596 	temp = dc_fixpt_mul_int(temp, 64 * 1000);
1597 	in_order_bandwidth_mbps = dc_fixpt_floor(temp);
1598 
1599 	return in_order_bandwidth_mbps;
1600 }
1601 
1602 /**
1603  * @brief Configures the performance monitor to measure prefetch data size.
1604  *
1605  * This function sets up performance monitoring counter 0 to measure
1606  * the total prefetch data size in bytes. It configures the necessary
1607  * registers to count the number of valid ROB output events, which
1608  * correspond to prefetch data.
1609  *
1610  * @param hubbub Pointer to the hubbub structure representing the hardware
1611  *        instance.
1612  */
1613 static void hubbub60_perfmon_start_measuring_prefetch_data_size(
1614 		struct hubbub *hubbub)
1615 {
1616 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1617 
1618 	/* configure measurement control */
1619 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL, 0,
1620 			DCHUBBUB_LATENCY_CNT_EN, 0x1,
1621 			DCHUBBUB_DF_REQ_CMD_LATENCY_SEL, 0x1);
1622 	REG_SET_2(DCHUBBUB_PERFORMANCE_MEASUREMENT_CNTL2, 0,
1623 			LATENCY_SOURCE_SEL, 0x2,
1624 			UTM_FILTER_SEL, 0x2); // prefetch only
1625 
1626 	/* program counter 0 to count prefetch data size */
1627 	REG_SET_8(DC_PERFMON5_PERFCOUNTER_CNTL, 0,
1628 			PERFCOUNTER_CNTL_SEL, 0x0, // select counter 0
1629 			PERFCOUNTER_EVENT_SEL, 259, // ROB output valid event
1630 			PERFCOUNTER_CVALUE_SEL, 0x0, // ignored
1631 			PERFCOUNTER_INC_MODE, 0x2, // Count LSB level
1632 			PERFCOUNTER_HW_CNTL_SEL, 0x0, // simultaneous mode
1633 			PERFCOUNTER_RUNEN_MODE, 0x0, // counter runs as long as run_enable is high
1634 			PERFCOUNTER_RESTART_EN, 0x0, // stop after counting is done
1635 			PERFCOUNTER_ACTIVE, 1);
1636 	REG_SET_4(DC_PERFMON5_PERFCOUNTER_CNTL2, 0,
1637 			PERFCOUNTER_CNTL2_SEL, 0x0, // select counter 0
1638 			PERFCOUNTER_COUNTED_VALUE_TYPE, 0x0, // count the accumulated value
1639 			PERFCOUNTER_HW_STOP1_SEL, 0x0, // stop when counting is done
1640 			PERFCOUNTER_HW_STOP2_SEL, 0x0); // ignored
1641 
1642 	REG_SET_2(DC_PERFMON5_PERFCOUNTER_STATE, 0,
1643 			PERFCOUNTER_STATE_SEL0, 0x1, // simultaneous state mode
1644 			PERFCOUNTER_CNT0_STATE, 0x3); // hw mode
1645 
1646 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL2, 0,
1647 			PERFMON_RUN_ENABLE_START_SEL, 0x0,
1648 			PERFMON_RUN_ENABLE_STOP_SEL, 0);
1649 
1650 	/* start the counters */
1651 	REG_SET_2(DC_PERFMON5_PERFMON_CNTL, 0,
1652 			PERFMON_STATE, 0x3,
1653 			PERFMON_RPT_COUNT, 0x1);
1654 }
1655 
1656 /**
1657  * @brief Reads the current performance monitor result and calculates
1658  * the prefetch data size in bytes.
1659  *
1660  * This function reads the value from the DCN 6.0 hubbub performance
1661  * monitor counter 0, then calculates the prefetch data size in bytes.
1662  *
1663  * @param hubbub Pointer to the hubbub structure representing the
1664  * hardware instance.
1665  *
1666  * @return The prefetch data size in bytes as a 32-bit unsigned integer.
1667  */
1668 static uint32_t hubbub60_perfmon_get_prefetch_data_size(
1669 		struct hubbub *hubbub)
1670 {
1671 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1672 	uint32_t count0 = 0;
1673 	uint32_t prefetch_data_size_bytes = 0;
1674 
1675 	REG_SET(DC_PERFMON5_PERFMON_HI, 0, PERFMON_READ_SEL, 0x0);
1676 	REG_GET(DC_PERFMON5_PERFMON_LOW, PERFMON_LOW, &count0);
1677 
1678 	prefetch_data_size_bytes = count0 * 64;
1679 	return prefetch_data_size_bytes;
1680 }
1681 
1682 /**
1683  * @brief Forces the display to use the nominal QoS profile.
1684  *
1685  * This function configures the hubbub to force the display to use
1686  * the nominal QoS profile by updating the relevant registers.
1687  *
1688  * @param hubbub Pointer to the hubbub structure representing the
1689  * hardware instance.
1690  */
1691 static void hubbub60_force_display_nominal_profile(struct hubbub *hubbub)
1692 {
1693 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1694 
1695 	REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, 0);
1696 	REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, 1);
1697 }
1698 
1699 /**
1700  * @brief Forces the display to use the urgent QoS profile.
1701  *
1702  * This function configures the hubbub to force the display to use
1703  * the urgent QoS profile by updating the relevant registers.
1704  *
1705  * @param hubbub Pointer to the hubbub structure representing the
1706  * hardware instance.
1707  */
1708 static void hubbub60_force_display_urgent_profile(struct hubbub *hubbub)
1709 {
1710 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1711 
1712 	REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, 1);
1713 	REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, 1);
1714 }
1715 
1716 /**
1717  * @brief Resets the display QoS profile to default.
1718  *
1719  * This function resets the display QoS profile by disabling any
1720  * forced QoS settings in the hubbub.
1721  *
1722  * @param hubbub Pointer to the hubbub structure representing the
1723  * hardware instance.
1724  */
1725 static void hubbub60_reset_display_qos_profile(struct hubbub *hubbub)
1726 {
1727 	struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
1728 
1729 	REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_ENABLE, 0);
1730 	REG_UPDATE(DCHUBBUB_ARB_QOS_FORCE, DCHUBBUB_ARB_UTM_FORCE_URGENT, 0);
1731 }
1732 
1733 static const struct hubbub_funcs hubbub60_funcs = {
1734 	.update_dchub = hubbub2_update_dchub,
1735 	.init_dchub_sys_ctx = hubbub3_init_dchub_sys_ctx,
1736 	.init_vm_ctx = hubbub2_init_vm_ctx,
1737 	.dcc_support_swizzle_addr3 = hubbub401_dcc_support_swizzle,
1738 	.dcc_support_pixel_format_plane0_plane1 =
1739 		hubbub401_dcc_support_pixel_format,
1740 	.get_dcc_compression_cap = hubbub401_get_dcc_compression_cap,
1741 	.wm_read_state = hubbub60_wm_read_state,
1742 	.get_dchub_ref_freq = hubbub2_get_dchub_ref_freq,
1743 	.program_watermarks = hubbub60_program_watermarks,
1744 	.allow_self_refresh_control = hubbub1_allow_self_refresh_control,
1745 	.is_allow_self_refresh_enabled = hubbub1_is_allow_self_refresh_enabled,
1746 	.verify_allow_pstate_change_high = NULL,
1747 	.force_wm_propagate_to_pipes = hubbub60_force_wm_propagate_to_pipes,
1748 	.force_pstate_change_control = hubbub3_force_pstate_change_control,
1749 	.init_watermarks = hubbub60_init_watermarks,
1750 	.init_crb = dcn60_init_crb,
1751 	.hubbub_read_state = hubbub2_read_state,
1752 	.force_usr_retraining_allow = NULL,
1753 	.set_request_limit = hubbub32_set_request_limit,
1754 	.program_det_segments = dcn60_program_det_segments,
1755 	.program_compbuf_segments = dcn60_program_compbuf_segments,
1756 	.wait_for_det_update = dcn60_wait_for_det_update,
1757 	.program_arbiter = dcn401_program_arbiter,
1758 	.hubbub_read_reg_state = hubbub3_read_reg_state,
1759 	.perfmon = {
1760 		.reset = hubbub60_perfmon_reset,
1761 		.start_measuring_memory_latencies =
1762 			hubbub60_perfmon_start_measuring_memory_latencies,
1763 		.get_memory_latencies_ns =
1764 			hubbub60_perfmon_get_memory_latencies_ns,
1765 		.start_measuring_urgent_assertion_count =
1766 			hubbub60_perfmon_start_measuring_urgent_assertion_count,
1767 		.get_urgent_assertion_count =
1768 			hubbub60_perfmon_get_urgent_assertion_count,
1769 		.start_measuring_urgent_ramp_latency =
1770 			hubbub60_perfmon_start_measuring_urgent_ramp_latency,
1771 		.get_urgent_ramp_latency_ns =
1772 			hubbub60_perfmon_get_urgent_ramp_latency_ns,
1773 		.arm_measuring_out_of_order_bandwidth =
1774 			hubbub60_perfmon_arm_measuring_out_of_order_bandwidth,
1775 		.start_measuring_out_of_order_bandwidth =
1776 			hubbub60_perfmon_start_measuring_out_of_order_bandwidth,
1777 		.get_out_of_order_bandwidth_mbps =
1778 			hubbub60_perfmon_get_out_of_order_bandwidth_mbps,
1779 		.start_measuring_in_order_bandwidth =
1780 			hubbub60_perfmon_start_measuring_in_order_bandwidth,
1781 		.get_in_order_bandwidth_mbps =
1782 			hubbub60_perfmon_get_in_order_bandwidth_mbps,
1783 		.start_measuring_prefetch_data_size =
1784 			hubbub60_perfmon_start_measuring_prefetch_data_size,
1785 		.get_prefetch_data_size =
1786 			hubbub60_perfmon_get_prefetch_data_size,
1787 	},
1788 	.qos = {
1789 		.force_display_nominal_profile =
1790 			hubbub60_force_display_nominal_profile,
1791 		.force_display_urgent_profile =
1792 			hubbub60_force_display_urgent_profile,
1793 		.reset_display_qos_profile =
1794 			hubbub60_reset_display_qos_profile,
1795 	},
1796 };
1797 
1798 void hubbub60_construct(struct dcn20_hubbub *hubbub2,
1799 	struct dc_context *ctx,
1800 	const struct dcn_hubbub_registers *hubbub_regs,
1801 	const struct dcn_hubbub_shift *hubbub_shift,
1802 	const struct dcn_hubbub_mask *hubbub_mask,
1803 	int det_size_kb,
1804 	int pixel_chunk_size_kb,
1805 	int config_return_buffer_size_kb)
1806 {
1807 	hubbub2->base.ctx = ctx;
1808 	hubbub2->base.funcs = &hubbub60_funcs;
1809 	hubbub2->regs = hubbub_regs;
1810 	hubbub2->shifts = hubbub_shift;
1811 	hubbub2->masks = hubbub_mask;
1812 
1813 	hubbub2->detile_buf_size = det_size_kb * 1024;
1814 	hubbub2->pixel_chunk_size = pixel_chunk_size_kb * 1024;
1815 	hubbub2->crb_size_segs = config_return_buffer_size_kb / DCN6_0_CRB_SEGMENT_SIZE_KB;
1816 }
1817