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 "dcn35/dcn35_dccg.h"
8 #include "dcn42_dccg.h"
9 #include "dcn20/dcn20_dccg.h"
10
11 #define TO_DCN_DCCG(dccg)\
12 container_of(dccg, struct dcn_dccg, base)
13
14 #define REG(reg) \
15 (dccg_dcn->regs->reg)
16
17 #undef FN
18 #define FN(reg_name, field_name) \
19 dccg_dcn->dccg_shift->field_name, dccg_dcn->dccg_mask->field_name
20
21 #define CTX \
22 dccg_dcn->base.ctx
23 #define DC_LOGGER \
24 dccg->ctx->logger
25
dccg42_otg_add_pixel(struct dccg * dccg,uint32_t otg_inst)26 void dccg42_otg_add_pixel(struct dccg *dccg,
27 uint32_t otg_inst)
28 {
29 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
30
31 switch (otg_inst) {
32 case 0:
33 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
34 OTG0_ADD_PIXEL, 1);
35 break;
36 case 1:
37 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
38 OTG1_ADD_PIXEL, 1);
39 break;
40 case 2:
41 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
42 OTG2_ADD_PIXEL, 1);
43 break;
44 case 3:
45 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
46 OTG3_ADD_PIXEL, 1);
47 break;
48 default:
49 ASSERT(0);
50 }
51 }
52
dccg42_otg_drop_pixel(struct dccg * dccg,uint32_t otg_inst)53 void dccg42_otg_drop_pixel(struct dccg *dccg,
54 uint32_t otg_inst)
55 {
56 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
57
58 switch (otg_inst) {
59 case 0:
60 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
61 OTG0_DROP_PIXEL, 1);
62 break;
63 case 1:
64 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
65 OTG1_DROP_PIXEL, 1);
66 break;
67 case 2:
68 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
69 OTG2_DROP_PIXEL, 1);
70 break;
71 case 3:
72 REG_UPDATE(OTG_ADD_DROP_PIXEL_CNTL,
73 OTG3_DROP_PIXEL, 1);
74 break;
75 default:
76 ASSERT(0);
77 }
78 }
79
dccg42_enable_global_fgcg(struct dccg * dccg,bool enable)80 void dccg42_enable_global_fgcg(struct dccg *dccg, bool enable)
81 {
82 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
83
84 /* Temporary workaround for IOMMU mismatch issue.
85 * Fine grain control via bit2 of debug flag.
86 */
87 if (dccg->ctx->dc->debug.disable_clock_gate || (dccg->ctx->dc->debug.iommu_mismatch_temp_wka & 0x4))
88 enable = false;
89
90 REG_UPDATE(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, !enable);
91 }
92
dccg42_get_global_fgcg_status(struct dccg * dccg)93 bool dccg42_get_global_fgcg_status(struct dccg *dccg)
94 {
95 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
96 uint32_t disabled = 0;
97
98 REG_GET(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, &disabled);
99 return disabled & 0x1;
100 }
101
dccg42_set_physymclk(struct dccg * dccg,int phy_inst,enum physymclk_clock_source clk_src,bool force_enable)102 void dccg42_set_physymclk(
103 struct dccg *dccg,
104 int phy_inst,
105 enum physymclk_clock_source clk_src,
106 bool force_enable)
107 {
108 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
109
110 switch (phy_inst) {
111 case 0:
112 if (force_enable) {
113 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
114 PHYASYMCLK_ROOT_GATE_DISABLE, 1);
115 REG_UPDATE_2(PHYASYMCLK_CLOCK_CNTL,
116 PHYASYMCLK_EN, 1,
117 PHYASYMCLK_SRC_SEL, clk_src);
118 } else {
119 REG_UPDATE_2(PHYASYMCLK_CLOCK_CNTL,
120 PHYASYMCLK_EN, 0,
121 PHYASYMCLK_SRC_SEL, 0);
122 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
123 PHYASYMCLK_ROOT_GATE_DISABLE,
124 dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk ? 0 : 1);
125 }
126 break;
127 case 1:
128 if (force_enable) {
129 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
130 PHYBSYMCLK_ROOT_GATE_DISABLE, 1);
131 REG_UPDATE_2(PHYBSYMCLK_CLOCK_CNTL,
132 PHYBSYMCLK_EN, 1,
133 PHYBSYMCLK_SRC_SEL, clk_src);
134 } else {
135 REG_UPDATE_2(PHYBSYMCLK_CLOCK_CNTL,
136 PHYBSYMCLK_EN, 0,
137 PHYBSYMCLK_SRC_SEL, 0);
138 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
139 PHYBSYMCLK_ROOT_GATE_DISABLE,
140 dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk ? 0 : 1);
141 }
142 break;
143 case 2:
144 if (force_enable) {
145 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
146 PHYCSYMCLK_ROOT_GATE_DISABLE, 1);
147 REG_UPDATE_2(PHYCSYMCLK_CLOCK_CNTL,
148 PHYCSYMCLK_EN, 1,
149 PHYCSYMCLK_SRC_SEL, clk_src);
150 } else {
151 REG_UPDATE_2(PHYCSYMCLK_CLOCK_CNTL,
152 PHYCSYMCLK_EN, 0,
153 PHYCSYMCLK_SRC_SEL, 0);
154 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
155 PHYCSYMCLK_ROOT_GATE_DISABLE,
156 dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk ? 0 : 1);
157 }
158 break;
159 case 3:
160 if (force_enable) {
161 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
162 PHYDSYMCLK_ROOT_GATE_DISABLE, 1);
163 REG_UPDATE_2(PHYDSYMCLK_CLOCK_CNTL,
164 PHYDSYMCLK_EN, 1,
165 PHYDSYMCLK_SRC_SEL, clk_src);
166 } else {
167 REG_UPDATE_2(PHYDSYMCLK_CLOCK_CNTL,
168 PHYDSYMCLK_EN, 0,
169 PHYDSYMCLK_SRC_SEL, 0);
170 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
171 PHYDSYMCLK_ROOT_GATE_DISABLE,
172 dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk ? 0 : 1);
173 }
174 break;
175 case 4:
176 if (force_enable) {
177 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
178 PHYESYMCLK_ROOT_GATE_DISABLE, 1);
179 REG_UPDATE_2(PHYESYMCLK_CLOCK_CNTL,
180 PHYESYMCLK_EN, 1,
181 PHYESYMCLK_SRC_SEL, clk_src);
182 } else {
183 REG_UPDATE_2(PHYESYMCLK_CLOCK_CNTL,
184 PHYESYMCLK_EN, 0,
185 PHYESYMCLK_SRC_SEL, 0);
186 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
187 PHYESYMCLK_ROOT_GATE_DISABLE,
188 dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk ? 0 : 1);
189 }
190 break;
191 default:
192 BREAK_TO_DEBUGGER();
193 return;
194 }
195 }
196
dccg42_disable_hdmistreamclk(struct dccg * dccg)197 static void dccg42_disable_hdmistreamclk(struct dccg *dccg)
198 {
199 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
200
201 REG_UPDATE_2(HDMISTREAMCLK_CNTL,
202 HDMISTREAMCLK0_EN, 0,
203 HDMISTREAMCLK0_SRC_SEL, 0);
204
205 REG_UPDATE(DCCG_GATE_DISABLE_CNTL6,
206 HDMISTREAMCLK0_ROOT_GATE_DISABLE,
207 dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream ? 0 : 1);
208 }
209
dccg42_disable_hdmicharclk(struct dccg * dccg,int hpo_inst)210 static void dccg42_disable_hdmicharclk(struct dccg *dccg, int hpo_inst)
211 {
212 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
213
214 ASSERT(hpo_inst >= 0);
215 REG_WRITE(HDMICHARCLK_CLOCK_CNTL[hpo_inst], 0);
216
217 REG_UPDATE(DCCG_GATE_DISABLE_CNTL2,
218 HDMICHARCLK0_GATE_DISABLE, 0);
219 REG_UPDATE(DCCG_GATE_DISABLE_CNTL4,
220 HDMICHARCLK0_ROOT_GATE_DISABLE, dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar ? 0 : 1);
221 }
222
dccg42_set_pixel_rate_div(struct dccg * dccg,uint32_t otg_inst,enum pixel_rate_div tmds_div,enum pixel_rate_div unused)223 void dccg42_set_pixel_rate_div(
224 struct dccg *dccg,
225 uint32_t otg_inst,
226 enum pixel_rate_div tmds_div,
227 enum pixel_rate_div unused)
228 {
229 (void)unused;
230 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
231 uint32_t cur_tmds_div = PIXEL_RATE_DIV_NA;
232 uint32_t dp_dto_int;
233 uint32_t reg_val;
234
235 // only 2 and 4 are valid on dcn401
236 if (tmds_div != PIXEL_RATE_DIV_BY_2 && tmds_div != PIXEL_RATE_DIV_BY_4) {
237 return;
238 }
239
240 dccg401_get_pixel_rate_div(dccg, otg_inst, &cur_tmds_div, &dp_dto_int);
241 if (tmds_div == cur_tmds_div)
242 return;
243
244 // encode enum to register value
245 reg_val = tmds_div == PIXEL_RATE_DIV_BY_4 ? 1 : 0;
246
247 switch (otg_inst) {
248 case 0:
249 REG_UPDATE(OTG_PIXEL_RATE_DIV,
250 OTG0_TMDS_PIXEL_RATE_DIV, reg_val);
251 break;
252 case 1:
253 REG_UPDATE(OTG_PIXEL_RATE_DIV,
254 OTG1_TMDS_PIXEL_RATE_DIV, reg_val);
255 break;
256 case 2:
257 REG_UPDATE(OTG_PIXEL_RATE_DIV,
258 OTG2_TMDS_PIXEL_RATE_DIV, reg_val);
259 break;
260 case 3:
261 REG_UPDATE(OTG_PIXEL_RATE_DIV,
262 OTG3_TMDS_PIXEL_RATE_DIV, reg_val);
263 break;
264 default:
265 BREAK_TO_DEBUGGER();
266 return;
267 }
268 }
269
dccg42_trigger_dio_fifo_resync(struct dccg * dccg)270 void dccg42_trigger_dio_fifo_resync(struct dccg *dccg)
271 {
272 struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
273
274 REG_UPDATE(DISPCLK_FREQ_CHANGE_CNTL, RESYNC_FIFO_LEVEL_ADJUST_EN, 1);
275 REG_UPDATE(DISPCLK_FREQ_CHANGE_CNTL, RESYNC_FIFO_LEVEL_ADJUST_EN, 0);
276 REG_WAIT(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_FREQ_RAMP_DONE, 1, 50, 2000);
277 }
278
dccg42_init(struct dccg * dccg)279 static void dccg42_init(struct dccg *dccg)
280 {
281 unsigned int i;
282 struct resource_pool *res_pool = dccg->ctx->dc->res_pool;
283
284 /* Set HPO stream encoder to use refclk to avoid case where PHY is
285 * disabled and SYMCLK32 for HPO SE is sourced from PHYD32CLK which
286 * will cause DCN to hang.
287 */
288 for (i = 0; i < res_pool->hpo_dp_stream_enc_count; i++)
289 dccg35_disable_symclk32_se(dccg, i);
290
291 if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) {
292 for (i = 0; i < res_pool->hpo_dp_link_enc_count; i++)
293 dccg401_disable_symclk32_le(dccg, i);
294 }
295
296 if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpstream) {
297 for (i = 0; i < res_pool->hpo_dp_stream_enc_count; i++)
298 dccg401_disable_dpstreamclk(dccg, i);
299 }
300
301 dccg42_disable_hdmistreamclk(dccg);
302 if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar)
303 dccg42_disable_hdmicharclk(dccg, 0);
304
305 if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpp) {
306 for (i = 0; i < res_pool->pipe_count; i++) {
307 dccg35_dpp_root_clock_control(dccg, i, true);
308 }
309 }
310 }
311
312
313 static const struct dccg_funcs dccg42_funcs = {
314 .enable_hdmicharclk = dccg401_enable_hdmicharclk,
315 .disable_hdmicharclk = dccg42_disable_hdmicharclk,
316 .set_hdmistreamclk = dccg35_set_hdmistreamclk,
317 .set_hdmistreamclk_root_clock_gating = dccg35_set_hdmistreamclk_root_clock_gating,
318 .update_dpp_dto = dccg35_update_dpp_dto,
319 .dpp_root_clock_control = dccg35_dpp_root_clock_control,
320 .get_dccg_ref_freq = dccg401_get_dccg_ref_freq,
321 .dccg_init = dccg42_init,
322 .set_dpstreamclk = dccg401_set_dpstreamclk,
323 /* Redundant with above^ */
324 /* .set_dpstreamclk_root_clock_gating = dccg35_set_dpstreamclk_root_clock_gating, */
325 .enable_symclk32_se = dccg31_enable_symclk32_se,
326 .disable_symclk32_se = dccg35_disable_symclk32_se,
327 .enable_symclk32_le = dccg401_enable_symclk32_le,
328 .disable_symclk32_le = dccg401_disable_symclk32_le,
329 .set_symclk32_le_root_clock_gating = dccg31_set_symclk32_le_root_clock_gating,
330 .set_physymclk = dccg42_set_physymclk,
331 .set_dtbclk_dto = NULL,
332 .set_dto_dscclk = dccg401_set_dto_dscclk,
333 .set_ref_dscclk = dccg401_set_ref_dscclk,
334 .set_valid_pixel_rate = NULL,
335 .set_fifo_errdet_ovr_en = dccg2_set_fifo_errdet_ovr_en,
336 .set_audio_dtbclk_dto = NULL,
337 .otg_add_pixel = dccg42_otg_add_pixel,
338 .otg_drop_pixel = dccg42_otg_drop_pixel,
339 .disable_dsc = dccg35_disable_dscclk,
340 .enable_dsc = dccg35_enable_dscclk,
341 .set_pixel_rate_div = dccg42_set_pixel_rate_div,
342 .get_pixel_rate_div = dccg401_get_pixel_rate_div,
343 .trigger_dio_fifo_resync = dccg42_trigger_dio_fifo_resync,
344 .set_dp_dto = dccg401_set_dp_dto,
345 .enable_symclk_se = dccg35_enable_symclk_se,
346 .disable_symclk_se = dccg35_disable_symclk_se,
347 .set_dtbclk_p_src = dccg401_set_dtbclk_p_src,
348 .dccg_root_gate_disable_control = dccg35_root_gate_disable_control,
349 .dccg_read_reg_state = dccg31_read_reg_state,
350 .dccg_enable_global_fgcg = dccg42_enable_global_fgcg,
351 .allow_clock_gating = dccg2_allow_clock_gating,
352 .dccg_get_global_fgcg_status = dccg42_get_global_fgcg_status,
353 };
354
dccg42_create(struct dc_context * ctx,const struct dccg_registers * regs,const struct dccg_shift * dccg_shift,const struct dccg_mask * dccg_mask)355 struct dccg *dccg42_create(
356 struct dc_context *ctx,
357 const struct dccg_registers *regs,
358 const struct dccg_shift *dccg_shift,
359 const struct dccg_mask *dccg_mask)
360 {
361 struct dcn_dccg *dccg_dcn = kzalloc_obj(*dccg_dcn);
362 struct dccg *base;
363
364 if (dccg_dcn == NULL) {
365 BREAK_TO_DEBUGGER();
366 return NULL;
367 }
368
369 base = &dccg_dcn->base;
370 base->ctx = ctx;
371 base->funcs = &dccg42_funcs;
372
373 dccg_dcn->regs = regs;
374 dccg_dcn->dccg_shift = dccg_shift;
375 dccg_dcn->dccg_mask = dccg_mask;
376
377 return &dccg_dcn->base;
378 }
379