1 // SPDX-License-Identifier: MIT
2
3 // Copyright 2026 Advanced Micro Devices, Inc.
4
5 #include "reg_helper.h"
6 #include "core_types.h"
7 #include "dcn42_pg_cntl.h"
8 #include "dccg.h"
9
10 #define TO_DCN_PG_CNTL(pg_cntl)\
11 container_of(pg_cntl, struct dcn_pg_cntl, base)
12
13 #define REG(reg) \
14 (pg_cntl_dcn->regs->reg)
15
16 #undef FN
17 #define FN(reg_name, field_name) \
18 pg_cntl_dcn->pg_cntl_shift->field_name, pg_cntl_dcn->pg_cntl_mask->field_name
19
20 #define CTX \
21 pg_cntl_dcn->base.ctx
22 #define DC_LOGGER \
23 pg_cntl->ctx->logger
24
25 /*
26 * ONO PG Workoaround: Saved FGCG repeaters states captured before powering up an ONO
27 * domain so it can be restored once the domain is powered up.
28 */
29 struct dcn42_global_fgcg_rep_state {
30 uint32_t dmu_rep_fgcg;
31 uint32_t dccg_global_ono_rep_fgcg;
32 uint32_t az_rep_fgcg;
33 };
34
35 /* Save and disable FGCG repeaters before powering up the ONO domain. */
pg_cntl42_save_and_disable_global_fgcg_rep(struct pg_cntl * pg_cntl,struct dcn42_global_fgcg_rep_state * state)36 static void pg_cntl42_save_and_disable_global_fgcg_rep(struct pg_cntl *pg_cntl,
37 struct dcn42_global_fgcg_rep_state *state)
38 {
39 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
40
41 REG_GET(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, &state->dmu_rep_fgcg);
42 if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_get_global_fgcg_status)
43 state->dccg_global_ono_rep_fgcg = pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_get_global_fgcg_status(pg_cntl->ctx->dc->res_pool->dccg);
44 REG_GET(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, &state->az_rep_fgcg);
45
46 REG_UPDATE(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, 1);
47 if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
48 pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false);
49 REG_UPDATE(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, 1);
50 }
51
52 /* Restore FGCG repeaters after the ONO domains are powered up. */
pg_cntl42_restore_global_fgcg_rep(struct pg_cntl * pg_cntl,struct dcn42_global_fgcg_rep_state * state)53 static void pg_cntl42_restore_global_fgcg_rep(struct pg_cntl *pg_cntl,
54 struct dcn42_global_fgcg_rep_state *state)
55 {
56 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
57
58 REG_UPDATE(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, state->dmu_rep_fgcg);
59 if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
60 pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, state->dccg_global_ono_rep_fgcg);
61 REG_UPDATE(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, state->az_rep_fgcg);
62 }
63
should_skip_pg_control(bool dc_in_idle_opt,bool power_on,bool block_enabled)64 static bool should_skip_pg_control(bool dc_in_idle_opt, bool power_on, bool block_enabled)
65 {
66 if (dc_in_idle_opt)
67 return true;
68
69 if (power_on && block_enabled)
70 return true;
71
72 if (!power_on && !block_enabled)
73 return true;
74
75 return false;
76 }
77
78
pg_cntl42_dsc_pg_status(struct pg_cntl * pg_cntl,unsigned int dsc_inst)79 static bool pg_cntl42_dsc_pg_status(struct pg_cntl *pg_cntl, unsigned int dsc_inst)
80 {
81 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
82 uint32_t pwr_status = 0;
83
84 switch (dsc_inst) {
85 case 0: /* DSC0 */
86 REG_GET(DOMAIN16_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
87 break;
88 case 1: /* DSC1 */
89 REG_GET(DOMAIN17_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
90 break;
91 case 2: /* DSC2 */
92 REG_GET(DOMAIN18_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
93 break;
94 case 3: /* DSC3 */
95 REG_GET(DOMAIN19_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
96 break;
97 default:
98 BREAK_TO_DEBUGGER();
99 break;
100 }
101
102 return pwr_status == 0;
103 }
104
pg_cntl42_dsc_pg_control(struct pg_cntl * pg_cntl,unsigned int dsc_inst,bool power_on)105 void pg_cntl42_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bool power_on)
106 {
107 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
108 uint32_t power_gate = power_on ? 0 : 1;
109 uint32_t pwr_status = power_on ? 0 : 2;
110 uint32_t org_ip_request_cntl = 0;
111 struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
112 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || pg_cntl->ctx->dc->debug.disable_dsc_power_gate;
113
114 if (block_pg_disabled && !power_on)
115 return;
116
117 bool block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, dsc_inst);
118 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
119 return;
120
121 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
122 if (org_ip_request_cntl == 0)
123 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
124
125 if (power_on)
126 pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
127
128 switch (dsc_inst) {
129 case 0: /* DSC0 */
130 REG_UPDATE(DOMAIN16_PG_CONFIG,
131 DOMAIN_POWER_GATE, power_gate);
132
133 REG_WAIT(DOMAIN16_PG_STATUS,
134 DOMAIN_PGFSM_PWR_STATUS, pwr_status,
135 1, 1000);
136 break;
137 case 1: /* DSC1 */
138 REG_UPDATE(DOMAIN17_PG_CONFIG,
139 DOMAIN_POWER_GATE, power_gate);
140
141 REG_WAIT(DOMAIN17_PG_STATUS,
142 DOMAIN_PGFSM_PWR_STATUS, pwr_status,
143 1, 1000);
144 break;
145 case 2: /* DSC2 */
146 REG_UPDATE(DOMAIN18_PG_CONFIG,
147 DOMAIN_POWER_GATE, power_gate);
148
149 REG_WAIT(DOMAIN18_PG_STATUS,
150 DOMAIN_PGFSM_PWR_STATUS, pwr_status,
151 1, 1000);
152 break;
153 case 3: /* DSC3 */
154 REG_UPDATE(DOMAIN19_PG_CONFIG,
155 DOMAIN_POWER_GATE, power_gate);
156
157 REG_WAIT(DOMAIN19_PG_STATUS,
158 DOMAIN_PGFSM_PWR_STATUS, pwr_status,
159 1, 1000);
160 break;
161 default:
162 BREAK_TO_DEBUGGER();
163 break;
164 }
165
166 if (power_on)
167 pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
168
169 if (dsc_inst < MAX_PIPES)
170 pg_cntl->pg_pipe_res_enable[PG_DSC][dsc_inst] = power_on;
171 }
172
pg_cntl42_hubp_dpp_pg_status(struct pg_cntl * pg_cntl,unsigned int hubp_dpp_inst)173 static bool pg_cntl42_hubp_dpp_pg_status(struct pg_cntl *pg_cntl, unsigned int hubp_dpp_inst)
174 {
175 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
176 uint32_t pwr_status = 0;
177
178 switch (hubp_dpp_inst) {
179 case 0:
180 /* DPP0 & HUBP0 */
181 REG_GET(DOMAIN0_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
182 break;
183 case 1:
184 /* DPP1 & HUBP1 */
185 REG_GET(DOMAIN1_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
186 break;
187 case 2:
188 /* DPP2 & HUBP2 */
189 REG_GET(DOMAIN2_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
190 break;
191 case 3:
192 /* DPP3 & HUBP3 */
193 REG_GET(DOMAIN3_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
194 break;
195 default:
196 BREAK_TO_DEBUGGER();
197 break;
198 }
199
200 return pwr_status == 0;
201 }
202
pg_cntl42_hubp_dpp_pg_control(struct pg_cntl * pg_cntl,unsigned int hubp_dpp_inst,bool power_on)203 void pg_cntl42_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dpp_inst, bool power_on)
204 {
205 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
206 uint32_t power_gate = power_on ? 0 : 1;
207 uint32_t pwr_status = power_on ? 0 : 2;
208 uint32_t org_ip_request_cntl;
209 struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
210 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
211 pg_cntl->ctx->dc->debug.disable_hubp_power_gate ||
212 pg_cntl->ctx->dc->debug.disable_dpp_power_gate;
213
214 if (block_pg_disabled && !power_on)
215 return;
216
217 bool block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, hubp_dpp_inst);
218 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
219 return;
220
221 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
222 if (org_ip_request_cntl == 0)
223 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
224
225 if (power_on)
226 pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
227
228 switch (hubp_dpp_inst) {
229 case 0:
230 /* DPP0 & HUBP0 */
231 REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
232 REG_WAIT(DOMAIN0_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
233 break;
234 case 1:
235 /* DPP1 & HUBP1 */
236 REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
237 REG_WAIT(DOMAIN1_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
238 break;
239 case 2:
240 /* DPP2 & HUBP2 */
241 REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
242 REG_WAIT(DOMAIN2_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
243 break;
244 case 3:
245 /* DPP3 & HUBP3 */
246 REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
247 REG_WAIT(DOMAIN3_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
248 break;
249 default:
250 BREAK_TO_DEBUGGER();
251 break;
252 }
253
254 if (power_on)
255 pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
256
257 DC_LOG_DEBUG("HUBP DPP instance %d, power %s", hubp_dpp_inst,
258 power_on ? "ON" : "OFF");
259
260 if (hubp_dpp_inst < MAX_PIPES) {
261 pg_cntl->pg_pipe_res_enable[PG_HUBP][hubp_dpp_inst] = power_on;
262 pg_cntl->pg_pipe_res_enable[PG_DPP][hubp_dpp_inst] = power_on;
263 }
264 }
265
pg_cntl42_hpo_pg_status(struct pg_cntl * pg_cntl)266 static bool pg_cntl42_hpo_pg_status(struct pg_cntl *pg_cntl)
267 {
268 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
269 uint32_t pwr_status = 0;
270
271 REG_GET(DOMAIN25_PG_STATUS,
272 DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
273
274 return pwr_status == 0;
275 }
276
pg_cntl42_hpo_pg_control(struct pg_cntl * pg_cntl,bool power_on)277 void pg_cntl42_hpo_pg_control(struct pg_cntl *pg_cntl, bool power_on)
278 {
279 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
280 uint32_t power_gate = power_on ? 0 : 1;
281 uint32_t pwr_status = power_on ? 0 : 2;
282 uint32_t org_ip_request_cntl;
283 uint32_t power_forceon;
284 struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
285
286 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
287 pg_cntl->ctx->dc->debug.disable_hpo_power_gate;
288
289 if (block_pg_disabled && !power_on)
290 return;
291
292 bool block_enabled = pg_cntl42_hpo_pg_status(pg_cntl);
293
294 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
295 return;
296
297 REG_GET(DOMAIN25_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon);
298 if (power_forceon)
299 return;
300
301 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
302 if (org_ip_request_cntl == 0)
303 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
304 if (power_on)
305 pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
306
307 REG_UPDATE(DOMAIN25_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
308 REG_WAIT(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
309
310 if (power_on)
311 pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
312
313 pg_cntl->pg_res_enable[PG_HPO] = power_on;
314 }
315
pg_cntl42_io_clk_status(struct pg_cntl * pg_cntl)316 static bool pg_cntl42_io_clk_status(struct pg_cntl *pg_cntl)
317 {
318 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
319 uint32_t pwr_status = 0;
320
321 REG_GET(DOMAIN22_PG_STATUS,
322 DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
323
324 return pwr_status == 0;
325 }
326
pg_cntl42_io_clk_pg_control(struct pg_cntl * pg_cntl,bool power_on)327 void pg_cntl42_io_clk_pg_control(struct pg_cntl *pg_cntl, bool power_on)
328 {
329 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
330 uint32_t power_gate = power_on ? 0 : 1;
331 uint32_t pwr_status = power_on ? 0 : 2;
332 uint32_t org_ip_request_cntl;
333 uint32_t power_forceon;
334
335 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
336 pg_cntl->ctx->dc->debug.disable_io_clk_power_gate;
337
338 if (block_pg_disabled && !power_on)
339 return;
340
341 bool block_enabled = pg_cntl42_io_clk_status(pg_cntl);
342
343 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
344 return;
345
346 REG_GET(DOMAIN22_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon);
347 if (power_forceon)
348 return;
349
350 if (!power_on) {
351 if (!pg_cntl->pg_res_enable[PG_DCCG] || !pg_cntl->pg_res_enable[PG_DCIO] || !pg_cntl->pg_res_enable[PG_DCOH])
352 return;
353 }
354 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
355 if (org_ip_request_cntl == 0)
356 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
357
358 /* DCCG, DCOH, DCIO */
359 REG_UPDATE(DOMAIN22_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
360 REG_WAIT(DOMAIN22_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
361
362 pg_cntl->pg_res_enable[PG_DCCG] = power_on;
363 pg_cntl->pg_res_enable[PG_DCOH] = power_on;
364 pg_cntl->pg_res_enable[PG_DCIO] = power_on;
365 }
366
pg_cntl42_plane_otg_status(struct pg_cntl * pg_cntl)367 static bool pg_cntl42_plane_otg_status(struct pg_cntl *pg_cntl)
368 {
369 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
370 uint32_t pwr_status = 0;
371
372 REG_GET(DOMAIN24_PG_STATUS,
373 DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
374
375 return pwr_status == 0;
376 }
377
pg_cntl42_mpcc_pg_control(struct pg_cntl * pg_cntl,unsigned int mpcc_inst,bool power_on)378 void pg_cntl42_mpcc_pg_control(struct pg_cntl *pg_cntl,
379 unsigned int mpcc_inst, bool power_on)
380 {
381 if (pg_cntl->ctx->dc->idle_optimizations_allowed)
382 return;
383
384 if (mpcc_inst < MAX_PIPES)
385 pg_cntl->pg_pipe_res_enable[PG_MPCC][mpcc_inst] = power_on;
386 }
387
pg_cntl42_opp_pg_control(struct pg_cntl * pg_cntl,unsigned int opp_inst,bool power_on)388 void pg_cntl42_opp_pg_control(struct pg_cntl *pg_cntl,
389 unsigned int opp_inst, bool power_on)
390 {
391 if (pg_cntl->ctx->dc->idle_optimizations_allowed)
392 return;
393
394 if (opp_inst < MAX_PIPES)
395 pg_cntl->pg_pipe_res_enable[PG_OPP][opp_inst] = power_on;
396 }
397
pg_cntl42_optc_pg_control(struct pg_cntl * pg_cntl,unsigned int optc_inst,bool power_on)398 void pg_cntl42_optc_pg_control(struct pg_cntl *pg_cntl,
399 unsigned int optc_inst, bool power_on)
400 {
401 // Defer the ONO domain power up/down to plane_otg_pg_control
402 if (pg_cntl->ctx->dc->idle_optimizations_allowed)
403 return;
404
405 if (optc_inst < MAX_PIPES)
406 pg_cntl->pg_pipe_res_enable[PG_OPTC][optc_inst] = power_on;
407 }
pg_cntl42_mem_status(struct pg_cntl * pg_cntl)408 static bool pg_cntl42_mem_status(struct pg_cntl *pg_cntl)
409 {
410 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
411 uint32_t pwr_status = 0;
412
413 REG_GET(DOMAIN23_PG_STATUS,
414 DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
415
416 return pwr_status == 0;
417 }
418
pg_cntl42_mem_pg_control(struct pg_cntl * pg_cntl,bool power_on)419 void pg_cntl42_mem_pg_control(struct pg_cntl *pg_cntl, bool power_on)
420 {
421 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
422 uint32_t power_gate = power_on ? 0 : 1;
423 uint32_t pwr_status = power_on ? 0 : 2;
424 uint32_t org_ip_request_cntl;
425 uint32_t power_forceon;
426
427 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
428 pg_cntl->ctx->dc->debug.disable_mem_power_gate;
429
430 if (block_pg_disabled && !power_on)
431 return;
432
433 bool block_enabled = pg_cntl42_mem_status(pg_cntl);
434 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
435 return;
436 REG_GET(DOMAIN23_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon);
437 if (power_forceon)
438 return;
439
440 if (!power_on) {
441 if (!pg_cntl->pg_res_enable[PG_DCHUBBUB] || !pg_cntl->pg_res_enable[PG_DCHVM])
442 return;
443 }
444 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
445 if (org_ip_request_cntl == 0)
446 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
447
448 /* DCHUBBUB, DCHUBBUBMEMn, DCHVM */
449 REG_UPDATE(DOMAIN23_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
450 REG_WAIT(DOMAIN23_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
451
452 pg_cntl->pg_res_enable[PG_DCHUBBUB] = power_on;
453 pg_cntl->pg_res_enable[PG_DCHVM] = power_on;
454 }
pg_cntl42_dio_pg_status(struct pg_cntl * pg_cntl)455 static bool pg_cntl42_dio_pg_status(struct pg_cntl *pg_cntl)
456 {
457 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
458 uint32_t pwr_status = 0;
459
460 REG_GET(DOMAIN26_PG_STATUS,
461 DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
462
463 return pwr_status == 0;
464 }
465
pg_cntl42_dio_pg_control(struct pg_cntl * pg_cntl,bool power_on)466 void pg_cntl42_dio_pg_control(struct pg_cntl *pg_cntl, bool power_on)
467 {
468 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
469 uint32_t power_gate = power_on ? 0 : 1;
470 uint32_t pwr_status = power_on ? 0 : 2;
471 uint32_t org_ip_request_cntl;
472 struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
473
474 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
475 pg_cntl->ctx->dc->debug.disable_dio_power_gate;
476
477 if (block_pg_disabled && !power_on)
478 return;
479
480 bool block_enabled = pg_cntl42_dio_pg_status(pg_cntl);
481 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
482 return;
483
484 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
485 if (org_ip_request_cntl == 0)
486 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
487 if (power_on)
488 pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
489
490 /* DIO */
491 REG_UPDATE(DOMAIN26_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
492 REG_WAIT(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
493
494 if (power_on)
495 pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
496
497 pg_cntl->pg_res_enable[PG_DIO] = power_on;
498
499 }
500
pg_cntl42_plane_otg_pg_control(struct pg_cntl * pg_cntl,bool power_on)501 void pg_cntl42_plane_otg_pg_control(struct pg_cntl *pg_cntl, bool power_on)
502 {
503 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
504 uint32_t power_gate = power_on ? 0 : 1;
505 uint32_t pwr_status = power_on ? 0 : 2;
506 uint32_t org_ip_request_cntl;
507 unsigned int i;
508 bool all_mpcc_disabled = true, all_opp_disabled = true;
509 bool all_optc_disabled = true, all_stream_disabled = true;
510
511 bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
512 pg_cntl->ctx->dc->debug.disable_optc_power_gate;
513
514 if (block_pg_disabled && !power_on)
515 return;
516
517 bool block_enabled = pg_cntl42_plane_otg_status(pg_cntl);
518
519 if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
520 return;
521
522 for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) {
523 struct pipe_ctx *pipe_ctx = &pg_cntl->ctx->dc->current_state->res_ctx.pipe_ctx[i];
524
525 if (pipe_ctx) {
526 if (pipe_ctx->stream)
527 all_stream_disabled = false;
528 }
529
530 if (pg_cntl->pg_pipe_res_enable[PG_MPCC][i])
531 all_mpcc_disabled = false;
532
533 if (pg_cntl->pg_pipe_res_enable[PG_OPP][i])
534 all_opp_disabled = false;
535
536 if (pg_cntl->pg_pipe_res_enable[PG_OPTC][i])
537 all_optc_disabled = false;
538 }
539
540 if (!power_on) {
541 if (!all_mpcc_disabled || !all_opp_disabled || !all_optc_disabled
542 || !all_stream_disabled)
543 return;
544 }
545
546 REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
547 if (org_ip_request_cntl == 0)
548 REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
549
550 /* MPC, OPP, OPTC, DWB */
551 REG_UPDATE(DOMAIN24_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
552 REG_WAIT(DOMAIN24_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
553
554 for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) {
555 pg_cntl->pg_pipe_res_enable[PG_MPCC][i] = power_on;
556 pg_cntl->pg_pipe_res_enable[PG_OPP][i] = power_on;
557 pg_cntl->pg_pipe_res_enable[PG_OPTC][i] = power_on;
558 }
559 }
560
561
pg_cntl42_init_pg_status(struct pg_cntl * pg_cntl)562 void pg_cntl42_init_pg_status(struct pg_cntl *pg_cntl)
563 {
564 unsigned int i = 0;
565 bool block_enabled;
566
567 pg_cntl->pg_res_enable[PG_HPO] = pg_cntl42_hpo_pg_status(pg_cntl);
568
569 block_enabled = pg_cntl42_io_clk_status(pg_cntl);
570 pg_cntl->pg_res_enable[PG_DCCG] = block_enabled;
571 pg_cntl->pg_res_enable[PG_DCIO] = block_enabled;
572 pg_cntl->pg_res_enable[PG_DCOH] = block_enabled;
573
574 block_enabled = pg_cntl42_dio_pg_status(pg_cntl);
575 pg_cntl->pg_res_enable[PG_DIO] = block_enabled;
576
577 block_enabled = pg_cntl42_mem_status(pg_cntl);
578 pg_cntl->pg_res_enable[PG_DCHUBBUB] = block_enabled;
579 pg_cntl->pg_res_enable[PG_DCHVM] = block_enabled;
580
581 for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) {
582 block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, i);
583 pg_cntl->pg_pipe_res_enable[PG_HUBP][i] = block_enabled;
584 pg_cntl->pg_pipe_res_enable[PG_DPP][i] = block_enabled;
585
586 block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, i);
587 pg_cntl->pg_pipe_res_enable[PG_DSC][i] = block_enabled;
588 }
589
590 block_enabled = pg_cntl42_plane_otg_status(pg_cntl);
591 for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) {
592 pg_cntl->pg_pipe_res_enable[PG_MPCC][i] = block_enabled;
593 pg_cntl->pg_pipe_res_enable[PG_OPP][i] = block_enabled;
594 pg_cntl->pg_pipe_res_enable[PG_OPTC][i] = block_enabled;
595 }
596 }
597
598 static const struct pg_cntl_funcs pg_cntl42_funcs = {
599 .init_pg_status = pg_cntl42_init_pg_status,
600 .dsc_pg_control = pg_cntl42_dsc_pg_control,
601 .hubp_dpp_pg_control = pg_cntl42_hubp_dpp_pg_control,
602 .hpo_pg_control = pg_cntl42_hpo_pg_control,
603 .io_clk_pg_control = pg_cntl42_io_clk_pg_control,
604 .plane_otg_pg_control = pg_cntl42_plane_otg_pg_control,
605 .mpcc_pg_control = pg_cntl42_mpcc_pg_control,
606 .opp_pg_control = pg_cntl42_opp_pg_control,
607 .optc_pg_control = pg_cntl42_optc_pg_control,
608 .mem_pg_control = pg_cntl42_mem_pg_control,
609 .dio_pg_control = pg_cntl42_dio_pg_control
610 };
611
pg_cntl42_create(struct dc_context * ctx,const struct pg_cntl_registers * regs,const struct pg_cntl_shift * pg_cntl_shift,const struct pg_cntl_mask * pg_cntl_mask)612 struct pg_cntl *pg_cntl42_create(
613 struct dc_context *ctx,
614 const struct pg_cntl_registers *regs,
615 const struct pg_cntl_shift *pg_cntl_shift,
616 const struct pg_cntl_mask *pg_cntl_mask)
617 {
618 struct dcn_pg_cntl *pg_cntl_dcn = kzalloc_obj(*pg_cntl_dcn);
619 struct pg_cntl *base;
620
621 if (pg_cntl_dcn == NULL) {
622 BREAK_TO_DEBUGGER();
623 return NULL;
624 }
625
626 base = &pg_cntl_dcn->base;
627 base->ctx = ctx;
628 base->funcs = &pg_cntl42_funcs;
629
630 pg_cntl_dcn->regs = regs;
631 pg_cntl_dcn->pg_cntl_shift = pg_cntl_shift;
632 pg_cntl_dcn->pg_cntl_mask = pg_cntl_mask;
633
634 memset(base->pg_pipe_res_enable, 0, PG_HW_PIPE_RESOURCES_NUM_ELEMENT * MAX_PIPES * sizeof(bool));
635 memset(base->pg_res_enable, 0, PG_HW_RESOURCES_NUM_ELEMENT * sizeof(bool));
636
637 return &pg_cntl_dcn->base;
638 }
639
dcn42_pg_cntl_destroy(struct pg_cntl ** pg_cntl)640 void dcn42_pg_cntl_destroy(struct pg_cntl **pg_cntl)
641 {
642 struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(*pg_cntl);
643
644 kfree(pg_cntl_dcn);
645 *pg_cntl = NULL;
646 }
647