Commit | Line | Data |
---|---|---|
7ed4e635 HW |
1 | /* |
2 | * Copyright 2016 Advanced Micro Devices, Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: AMD | |
23 | * | |
24 | */ | |
c602b36f | 25 | #include <linux/delay.h> |
7ed4e635 HW |
26 | |
27 | #include "dm_services.h" | |
28 | #include "dm_helpers.h" | |
29 | #include "core_types.h" | |
30 | #include "resource.h" | |
31 | #include "dcn20/dcn20_resource.h" | |
32 | #include "dce110/dce110_hw_sequencer.h" | |
33 | #include "dcn10/dcn10_hw_sequencer.h" | |
34 | #include "dcn20_hwseq.h" | |
35 | #include "dce/dce_hwseq.h" | |
97bda032 HW |
36 | #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT |
37 | #include "dcn20/dcn20_dsc.h" | |
38 | #endif | |
7ed4e635 HW |
39 | #include "abm.h" |
40 | #include "clk_mgr.h" | |
41 | #include "dmcu.h" | |
42 | #include "hubp.h" | |
43 | #include "timing_generator.h" | |
44 | #include "opp.h" | |
45 | #include "ipp.h" | |
46 | #include "mpc.h" | |
47 | #include "mcif_wb.h" | |
48 | #include "reg_helper.h" | |
49 | #include "dcn10/dcn10_cm_common.h" | |
50 | #include "dcn10/dcn10_hubbub.h" | |
51 | #include "dcn10/dcn10_optc.h" | |
52 | #include "dc_link_dp.h" | |
53 | #include "vm_helper.h" | |
54 | #include "dccg.h" | |
55 | ||
56 | #define DC_LOGGER_INIT(logger) | |
57 | ||
58 | #define CTX \ | |
59 | hws->ctx | |
60 | #define REG(reg)\ | |
61 | hws->regs->reg | |
62 | ||
63 | #undef FN | |
64 | #define FN(reg_name, field_name) \ | |
65 | hws->shifts->field_name, hws->masks->field_name | |
66 | ||
67 | static void bios_golden_init(struct dc *dc) | |
68 | { | |
69 | struct dc_bios *bp = dc->ctx->dc_bios; | |
70 | int i; | |
71 | ||
72 | /* initialize dcn global */ | |
73 | bp->funcs->enable_disp_power_gating(bp, | |
74 | CONTROLLER_ID_D0, ASIC_PIPE_INIT); | |
75 | ||
76 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
77 | /* initialize dcn per pipe */ | |
78 | bp->funcs->enable_disp_power_gating(bp, | |
79 | CONTROLLER_ID_D0 + i, ASIC_PIPE_DISABLE); | |
80 | } | |
81 | } | |
82 | ||
83 | static void enable_power_gating_plane( | |
84 | struct dce_hwseq *hws, | |
85 | bool enable) | |
86 | { | |
87 | bool force_on = 1; /* disable power gating */ | |
88 | ||
89 | if (enable) | |
90 | force_on = 0; | |
91 | ||
92 | /* DCHUBP0/1/2/3/4/5 */ | |
93 | REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, force_on); | |
94 | REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, force_on); | |
95 | REG_UPDATE(DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, force_on); | |
96 | REG_UPDATE(DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, force_on); | |
46825fcf TC |
97 | if (REG(DOMAIN8_PG_CONFIG)) |
98 | REG_UPDATE(DOMAIN8_PG_CONFIG, DOMAIN8_POWER_FORCEON, force_on); | |
99 | if (REG(DOMAIN10_PG_CONFIG)) | |
100 | REG_UPDATE(DOMAIN10_PG_CONFIG, DOMAIN8_POWER_FORCEON, force_on); | |
7ed4e635 HW |
101 | |
102 | /* DPP0/1/2/3/4/5 */ | |
103 | REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, force_on); | |
104 | REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, force_on); | |
105 | REG_UPDATE(DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, force_on); | |
106 | REG_UPDATE(DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, force_on); | |
46825fcf TC |
107 | if (REG(DOMAIN9_PG_CONFIG)) |
108 | REG_UPDATE(DOMAIN9_PG_CONFIG, DOMAIN9_POWER_FORCEON, force_on); | |
109 | if (REG(DOMAIN11_PG_CONFIG)) | |
110 | REG_UPDATE(DOMAIN11_PG_CONFIG, DOMAIN9_POWER_FORCEON, force_on); | |
7ed4e635 | 111 | |
46825fcf | 112 | /* DCS0/1/2/3/4/5 */ |
7ed4e635 HW |
113 | REG_UPDATE(DOMAIN16_PG_CONFIG, DOMAIN16_POWER_FORCEON, force_on); |
114 | REG_UPDATE(DOMAIN17_PG_CONFIG, DOMAIN17_POWER_FORCEON, force_on); | |
115 | REG_UPDATE(DOMAIN18_PG_CONFIG, DOMAIN18_POWER_FORCEON, force_on); | |
46825fcf TC |
116 | if (REG(DOMAIN19_PG_CONFIG)) |
117 | REG_UPDATE(DOMAIN19_PG_CONFIG, DOMAIN19_POWER_FORCEON, force_on); | |
118 | if (REG(DOMAIN20_PG_CONFIG)) | |
119 | REG_UPDATE(DOMAIN20_PG_CONFIG, DOMAIN20_POWER_FORCEON, force_on); | |
120 | if (REG(DOMAIN21_PG_CONFIG)) | |
121 | REG_UPDATE(DOMAIN21_PG_CONFIG, DOMAIN21_POWER_FORCEON, force_on); | |
7ed4e635 HW |
122 | } |
123 | ||
c70b4016 | 124 | void dcn20_dccg_init(struct dce_hwseq *hws) |
7ed4e635 HW |
125 | { |
126 | /* | |
127 | * set MICROSECOND_TIME_BASE_DIV | |
128 | * 100Mhz refclk -> 0x120264 | |
129 | * 27Mhz refclk -> 0x12021b | |
130 | * 48Mhz refclk -> 0x120230 | |
131 | * | |
132 | */ | |
133 | REG_WRITE(MICROSECOND_TIME_BASE_DIV, 0x120264); | |
134 | ||
135 | /* | |
136 | * set MILLISECOND_TIME_BASE_DIV | |
137 | * 100Mhz refclk -> 0x1186a0 | |
138 | * 27Mhz refclk -> 0x106978 | |
139 | * 48Mhz refclk -> 0x10bb80 | |
140 | * | |
141 | */ | |
142 | REG_WRITE(MILLISECOND_TIME_BASE_DIV, 0x1186a0); | |
143 | ||
144 | /* This value is dependent on the hardware pipeline delay so set once per SOC */ | |
145 | REG_WRITE(DISPCLK_FREQ_CHANGE_CNTL, 0x801003c); | |
146 | } | |
c70b4016 CL |
147 | void dcn20_display_init(struct dc *dc) |
148 | { | |
149 | struct dce_hwseq *hws = dc->hwseq; | |
150 | ||
151 | /* RBBMIF | |
152 | * disable RBBMIF timeout detection for all clients | |
153 | * Ensure RBBMIF does not drop register accesses due to the per-client timeout | |
154 | */ | |
155 | REG_WRITE(RBBMIF_TIMEOUT_DIS, 0xFFFFFFFF); | |
156 | REG_WRITE(RBBMIF_TIMEOUT_DIS_2, 0xFFFFFFFF); | |
157 | ||
158 | /* DCCG */ | |
159 | dcn20_dccg_init(hws); | |
160 | ||
161 | /* Disable all memory low power mode. All memories are enabled. */ | |
162 | REG_UPDATE(DC_MEM_GLOBAL_PWR_REQ_CNTL, DC_MEM_GLOBAL_PWR_REQ_DIS, 1); | |
163 | ||
164 | /* DCHUB/MMHUBBUB | |
165 | * set global timer refclk divider | |
166 | * 100Mhz refclk -> 2 | |
167 | * 27Mhz refclk -> 1 | |
168 | * 48Mhz refclk -> 1 | |
169 | */ | |
170 | REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, 2); | |
171 | REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1); | |
172 | REG_WRITE(REFCLK_CNTL, 0); | |
173 | ||
174 | /* OPTC | |
175 | * OTG_CONTROL.OTG_DISABLE_POINT_CNTL = 0x3; will be set during optc2_enable_crtc | |
176 | */ | |
177 | ||
178 | /* AZ | |
179 | * default value is 0x64 for 100Mhz ref clock, if the ref clock is 100Mhz, no need to program this regiser, | |
180 | * if not, it should be programmed according to the ref clock | |
181 | */ | |
182 | REG_UPDATE(AZALIA_AUDIO_DTO, AZALIA_AUDIO_DTO_MODULE, 0x64); | |
183 | /* Enable controller clock gating */ | |
184 | REG_WRITE(AZALIA_CONTROLLER_CLOCK_GATING, 0x1); | |
185 | } | |
7ed4e635 HW |
186 | |
187 | static void disable_vga( | |
188 | struct dce_hwseq *hws) | |
189 | { | |
190 | REG_WRITE(D1VGA_CONTROL, 0); | |
191 | REG_WRITE(D2VGA_CONTROL, 0); | |
192 | REG_WRITE(D3VGA_CONTROL, 0); | |
193 | REG_WRITE(D4VGA_CONTROL, 0); | |
194 | REG_WRITE(D5VGA_CONTROL, 0); | |
195 | REG_WRITE(D6VGA_CONTROL, 0); | |
196 | } | |
197 | ||
198 | void dcn20_program_tripleBuffer( | |
199 | const struct dc *dc, | |
200 | struct pipe_ctx *pipe_ctx, | |
201 | bool enableTripleBuffer) | |
202 | { | |
203 | if (pipe_ctx->plane_res.hubp && pipe_ctx->plane_res.hubp->funcs) { | |
204 | pipe_ctx->plane_res.hubp->funcs->hubp_enable_tripleBuffer( | |
205 | pipe_ctx->plane_res.hubp, | |
206 | enableTripleBuffer); | |
207 | } | |
208 | } | |
209 | ||
210 | /* Blank pixel data during initialization */ | |
c70b4016 | 211 | void dcn20_init_blank( |
7ed4e635 HW |
212 | struct dc *dc, |
213 | struct timing_generator *tg) | |
214 | { | |
215 | enum dc_color_space color_space; | |
216 | struct tg_color black_color = {0}; | |
217 | struct output_pixel_processor *opp = NULL; | |
218 | struct output_pixel_processor *bottom_opp = NULL; | |
219 | uint32_t num_opps, opp_id_src0, opp_id_src1; | |
220 | uint32_t otg_active_width, otg_active_height; | |
221 | ||
222 | /* program opp dpg blank color */ | |
223 | color_space = COLOR_SPACE_SRGB; | |
224 | color_space_to_black_color(dc, color_space, &black_color); | |
225 | ||
226 | /* get the OTG active size */ | |
227 | tg->funcs->get_otg_active_size(tg, | |
228 | &otg_active_width, | |
229 | &otg_active_height); | |
230 | ||
231 | /* get the OPTC source */ | |
232 | tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1); | |
233 | ASSERT(opp_id_src0 < dc->res_pool->res_cap->num_opp); | |
234 | opp = dc->res_pool->opps[opp_id_src0]; | |
235 | ||
236 | if (num_opps == 2) { | |
237 | otg_active_width = otg_active_width / 2; | |
238 | ASSERT(opp_id_src1 < dc->res_pool->res_cap->num_opp); | |
239 | bottom_opp = dc->res_pool->opps[opp_id_src1]; | |
240 | } | |
241 | ||
242 | opp->funcs->opp_set_disp_pattern_generator( | |
243 | opp, | |
244 | CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR, | |
245 | COLOR_DEPTH_UNDEFINED, | |
246 | &black_color, | |
247 | otg_active_width, | |
248 | otg_active_height); | |
249 | ||
250 | if (num_opps == 2) { | |
251 | bottom_opp->funcs->opp_set_disp_pattern_generator( | |
252 | bottom_opp, | |
253 | CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR, | |
254 | COLOR_DEPTH_UNDEFINED, | |
255 | &black_color, | |
256 | otg_active_width, | |
257 | otg_active_height); | |
258 | } | |
259 | ||
260 | dcn20_hwss_wait_for_blank_complete(opp); | |
261 | } | |
262 | ||
97bda032 HW |
263 | #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT |
264 | static void dcn20_dsc_pg_control( | |
265 | struct dce_hwseq *hws, | |
266 | unsigned int dsc_inst, | |
267 | bool power_on) | |
268 | { | |
269 | uint32_t power_gate = power_on ? 0 : 1; | |
270 | uint32_t pwr_status = power_on ? 0 : 2; | |
98ce8cc1 | 271 | uint32_t org_ip_request_cntl = 0; |
97bda032 HW |
272 | |
273 | if (hws->ctx->dc->debug.disable_dsc_power_gate) | |
274 | return; | |
275 | ||
276 | if (REG(DOMAIN16_PG_CONFIG) == 0) | |
277 | return; | |
278 | ||
98ce8cc1 NC |
279 | REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); |
280 | if (org_ip_request_cntl == 0) | |
281 | REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); | |
282 | ||
97bda032 HW |
283 | switch (dsc_inst) { |
284 | case 0: /* DSC0 */ | |
285 | REG_UPDATE(DOMAIN16_PG_CONFIG, | |
286 | DOMAIN16_POWER_GATE, power_gate); | |
287 | ||
288 | REG_WAIT(DOMAIN16_PG_STATUS, | |
289 | DOMAIN16_PGFSM_PWR_STATUS, pwr_status, | |
290 | 1, 1000); | |
291 | break; | |
292 | case 1: /* DSC1 */ | |
293 | REG_UPDATE(DOMAIN17_PG_CONFIG, | |
294 | DOMAIN17_POWER_GATE, power_gate); | |
295 | ||
296 | REG_WAIT(DOMAIN17_PG_STATUS, | |
297 | DOMAIN17_PGFSM_PWR_STATUS, pwr_status, | |
298 | 1, 1000); | |
299 | break; | |
300 | case 2: /* DSC2 */ | |
301 | REG_UPDATE(DOMAIN18_PG_CONFIG, | |
302 | DOMAIN18_POWER_GATE, power_gate); | |
303 | ||
304 | REG_WAIT(DOMAIN18_PG_STATUS, | |
305 | DOMAIN18_PGFSM_PWR_STATUS, pwr_status, | |
306 | 1, 1000); | |
307 | break; | |
308 | case 3: /* DSC3 */ | |
309 | REG_UPDATE(DOMAIN19_PG_CONFIG, | |
310 | DOMAIN19_POWER_GATE, power_gate); | |
311 | ||
312 | REG_WAIT(DOMAIN19_PG_STATUS, | |
313 | DOMAIN19_PGFSM_PWR_STATUS, pwr_status, | |
314 | 1, 1000); | |
315 | break; | |
316 | case 4: /* DSC4 */ | |
317 | REG_UPDATE(DOMAIN20_PG_CONFIG, | |
318 | DOMAIN20_POWER_GATE, power_gate); | |
319 | ||
320 | REG_WAIT(DOMAIN20_PG_STATUS, | |
321 | DOMAIN20_PGFSM_PWR_STATUS, pwr_status, | |
322 | 1, 1000); | |
323 | break; | |
324 | case 5: /* DSC5 */ | |
325 | REG_UPDATE(DOMAIN21_PG_CONFIG, | |
326 | DOMAIN21_POWER_GATE, power_gate); | |
327 | ||
328 | REG_WAIT(DOMAIN21_PG_STATUS, | |
329 | DOMAIN21_PGFSM_PWR_STATUS, pwr_status, | |
330 | 1, 1000); | |
331 | break; | |
332 | default: | |
333 | BREAK_TO_DEBUGGER(); | |
334 | break; | |
335 | } | |
98ce8cc1 NC |
336 | |
337 | if (org_ip_request_cntl == 0) | |
338 | REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); | |
97bda032 HW |
339 | } |
340 | #endif | |
7ed4e635 HW |
341 | |
342 | static void dcn20_dpp_pg_control( | |
343 | struct dce_hwseq *hws, | |
344 | unsigned int dpp_inst, | |
345 | bool power_on) | |
346 | { | |
347 | uint32_t power_gate = power_on ? 0 : 1; | |
348 | uint32_t pwr_status = power_on ? 0 : 2; | |
349 | ||
350 | if (hws->ctx->dc->debug.disable_dpp_power_gate) | |
351 | return; | |
352 | if (REG(DOMAIN1_PG_CONFIG) == 0) | |
353 | return; | |
354 | ||
355 | switch (dpp_inst) { | |
356 | case 0: /* DPP0 */ | |
357 | REG_UPDATE(DOMAIN1_PG_CONFIG, | |
358 | DOMAIN1_POWER_GATE, power_gate); | |
359 | ||
360 | REG_WAIT(DOMAIN1_PG_STATUS, | |
361 | DOMAIN1_PGFSM_PWR_STATUS, pwr_status, | |
362 | 1, 1000); | |
363 | break; | |
364 | case 1: /* DPP1 */ | |
365 | REG_UPDATE(DOMAIN3_PG_CONFIG, | |
366 | DOMAIN3_POWER_GATE, power_gate); | |
367 | ||
368 | REG_WAIT(DOMAIN3_PG_STATUS, | |
369 | DOMAIN3_PGFSM_PWR_STATUS, pwr_status, | |
370 | 1, 1000); | |
371 | break; | |
372 | case 2: /* DPP2 */ | |
373 | REG_UPDATE(DOMAIN5_PG_CONFIG, | |
374 | DOMAIN5_POWER_GATE, power_gate); | |
375 | ||
376 | REG_WAIT(DOMAIN5_PG_STATUS, | |
377 | DOMAIN5_PGFSM_PWR_STATUS, pwr_status, | |
378 | 1, 1000); | |
379 | break; | |
380 | case 3: /* DPP3 */ | |
381 | REG_UPDATE(DOMAIN7_PG_CONFIG, | |
382 | DOMAIN7_POWER_GATE, power_gate); | |
383 | ||
384 | REG_WAIT(DOMAIN7_PG_STATUS, | |
385 | DOMAIN7_PGFSM_PWR_STATUS, pwr_status, | |
386 | 1, 1000); | |
387 | break; | |
388 | case 4: /* DPP4 */ | |
389 | REG_UPDATE(DOMAIN9_PG_CONFIG, | |
390 | DOMAIN9_POWER_GATE, power_gate); | |
391 | ||
392 | REG_WAIT(DOMAIN9_PG_STATUS, | |
393 | DOMAIN9_PGFSM_PWR_STATUS, pwr_status, | |
394 | 1, 1000); | |
395 | break; | |
396 | case 5: /* DPP5 */ | |
397 | /* | |
398 | * Do not power gate DPP5, should be left at HW default, power on permanently. | |
399 | * PG on Pipe5 is De-featured, attempting to put it to PG state may result in hard | |
400 | * reset. | |
401 | * REG_UPDATE(DOMAIN11_PG_CONFIG, | |
402 | * DOMAIN11_POWER_GATE, power_gate); | |
403 | * | |
404 | * REG_WAIT(DOMAIN11_PG_STATUS, | |
405 | * DOMAIN11_PGFSM_PWR_STATUS, pwr_status, | |
406 | * 1, 1000); | |
407 | */ | |
408 | break; | |
409 | default: | |
410 | BREAK_TO_DEBUGGER(); | |
411 | break; | |
412 | } | |
413 | } | |
414 | ||
415 | ||
416 | static void dcn20_hubp_pg_control( | |
417 | struct dce_hwseq *hws, | |
418 | unsigned int hubp_inst, | |
419 | bool power_on) | |
420 | { | |
421 | uint32_t power_gate = power_on ? 0 : 1; | |
422 | uint32_t pwr_status = power_on ? 0 : 2; | |
423 | ||
424 | if (hws->ctx->dc->debug.disable_hubp_power_gate) | |
425 | return; | |
426 | if (REG(DOMAIN0_PG_CONFIG) == 0) | |
427 | return; | |
428 | ||
429 | switch (hubp_inst) { | |
430 | case 0: /* DCHUBP0 */ | |
431 | REG_UPDATE(DOMAIN0_PG_CONFIG, | |
432 | DOMAIN0_POWER_GATE, power_gate); | |
433 | ||
434 | REG_WAIT(DOMAIN0_PG_STATUS, | |
435 | DOMAIN0_PGFSM_PWR_STATUS, pwr_status, | |
436 | 1, 1000); | |
437 | break; | |
438 | case 1: /* DCHUBP1 */ | |
439 | REG_UPDATE(DOMAIN2_PG_CONFIG, | |
440 | DOMAIN2_POWER_GATE, power_gate); | |
441 | ||
442 | REG_WAIT(DOMAIN2_PG_STATUS, | |
443 | DOMAIN2_PGFSM_PWR_STATUS, pwr_status, | |
444 | 1, 1000); | |
445 | break; | |
446 | case 2: /* DCHUBP2 */ | |
447 | REG_UPDATE(DOMAIN4_PG_CONFIG, | |
448 | DOMAIN4_POWER_GATE, power_gate); | |
449 | ||
450 | REG_WAIT(DOMAIN4_PG_STATUS, | |
451 | DOMAIN4_PGFSM_PWR_STATUS, pwr_status, | |
452 | 1, 1000); | |
453 | break; | |
454 | case 3: /* DCHUBP3 */ | |
455 | REG_UPDATE(DOMAIN6_PG_CONFIG, | |
456 | DOMAIN6_POWER_GATE, power_gate); | |
457 | ||
458 | REG_WAIT(DOMAIN6_PG_STATUS, | |
459 | DOMAIN6_PGFSM_PWR_STATUS, pwr_status, | |
460 | 1, 1000); | |
461 | break; | |
462 | case 4: /* DCHUBP4 */ | |
463 | REG_UPDATE(DOMAIN8_PG_CONFIG, | |
464 | DOMAIN8_POWER_GATE, power_gate); | |
465 | ||
466 | REG_WAIT(DOMAIN8_PG_STATUS, | |
467 | DOMAIN8_PGFSM_PWR_STATUS, pwr_status, | |
468 | 1, 1000); | |
469 | break; | |
470 | case 5: /* DCHUBP5 */ | |
471 | /* | |
472 | * Do not power gate DCHUB5, should be left at HW default, power on permanently. | |
473 | * PG on Pipe5 is De-featured, attempting to put it to PG state may result in hard | |
474 | * reset. | |
475 | * REG_UPDATE(DOMAIN10_PG_CONFIG, | |
476 | * DOMAIN10_POWER_GATE, power_gate); | |
477 | * | |
478 | * REG_WAIT(DOMAIN10_PG_STATUS, | |
479 | * DOMAIN10_PGFSM_PWR_STATUS, pwr_status, | |
480 | * 1, 1000); | |
481 | */ | |
482 | break; | |
483 | default: | |
484 | BREAK_TO_DEBUGGER(); | |
485 | break; | |
486 | } | |
487 | } | |
488 | ||
489 | ||
490 | ||
491 | static void dcn20_plane_atomic_power_down(struct dc *dc, struct pipe_ctx *pipe_ctx) | |
492 | { | |
493 | struct dce_hwseq *hws = dc->hwseq; | |
494 | struct dpp *dpp = pipe_ctx->plane_res.dpp; | |
495 | ||
496 | DC_LOGGER_INIT(dc->ctx->logger); | |
497 | ||
498 | if (REG(DC_IP_REQUEST_CNTL)) { | |
499 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
500 | IP_REQUEST_EN, 1); | |
501 | dcn20_dpp_pg_control(hws, dpp->inst, false); | |
502 | dcn20_hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, false); | |
503 | dpp->funcs->dpp_reset(dpp); | |
504 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
505 | IP_REQUEST_EN, 0); | |
506 | DC_LOG_DEBUG( | |
507 | "Power gated front end %d\n", pipe_ctx->pipe_idx); | |
508 | } | |
509 | } | |
510 | ||
511 | ||
512 | ||
513 | /* disable HW used by plane. | |
514 | * note: cannot disable until disconnect is complete | |
515 | */ | |
516 | static void dcn20_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx) | |
517 | { | |
518 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
519 | struct dpp *dpp = pipe_ctx->plane_res.dpp; | |
7ed4e635 HW |
520 | |
521 | dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx); | |
522 | ||
6bd8d7d3 AC |
523 | /* In flip immediate with pipe splitting case GSL is used for |
524 | * synchronization so we must disable it when the plane is disabled. | |
525 | */ | |
526 | if (pipe_ctx->stream_res.gsl_group != 0) | |
527 | dcn20_setup_gsl_group_as_lock(dc, pipe_ctx, false); | |
528 | ||
529 | dc->hwss.set_flip_control_gsl(pipe_ctx, false); | |
530 | ||
7ed4e635 HW |
531 | hubp->funcs->hubp_clk_cntl(hubp, false); |
532 | ||
533 | dpp->funcs->dpp_dppclk_control(dpp, false, false); | |
534 | ||
7ed4e635 HW |
535 | hubp->power_gated = true; |
536 | dc->optimized_required = false; /* We're powering off, no need to optimize */ | |
537 | ||
538 | dcn20_plane_atomic_power_down(dc, pipe_ctx); | |
539 | ||
540 | pipe_ctx->stream = NULL; | |
541 | memset(&pipe_ctx->stream_res, 0, sizeof(pipe_ctx->stream_res)); | |
542 | memset(&pipe_ctx->plane_res, 0, sizeof(pipe_ctx->plane_res)); | |
543 | pipe_ctx->top_pipe = NULL; | |
544 | pipe_ctx->bottom_pipe = NULL; | |
545 | pipe_ctx->plane_state = NULL; | |
546 | } | |
547 | ||
548 | ||
ad141db9 | 549 | void dcn20_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx) |
7ed4e635 HW |
550 | { |
551 | DC_LOGGER_INIT(dc->ctx->logger); | |
552 | ||
553 | if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated) | |
554 | return; | |
555 | ||
556 | dcn20_plane_atomic_disable(dc, pipe_ctx); | |
557 | ||
7ed4e635 HW |
558 | DC_LOG_DC("Power down front end %d\n", |
559 | pipe_ctx->pipe_idx); | |
560 | } | |
561 | ||
562 | static void dcn20_init_hw(struct dc *dc) | |
563 | { | |
564 | int i, j; | |
565 | struct abm *abm = dc->res_pool->abm; | |
566 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
567 | struct dce_hwseq *hws = dc->hwseq; | |
568 | struct dc_bios *dcb = dc->ctx->dc_bios; | |
569 | struct resource_pool *res_pool = dc->res_pool; | |
570 | struct dc_state *context = dc->current_state; | |
41a5a2a8 | 571 | struct dc_firmware_info fw_info = { { 0 } }; |
7ed4e635 HW |
572 | |
573 | if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks) | |
574 | dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); | |
575 | ||
576 | // Initialize the dccg | |
577 | if (res_pool->dccg->funcs->dccg_init) | |
578 | res_pool->dccg->funcs->dccg_init(res_pool->dccg); | |
579 | ||
580 | //Enable ability to power gate / don't force power on permanently | |
581 | enable_power_gating_plane(dc->hwseq, true); | |
582 | ||
583 | if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
584 | REG_WRITE(RBBMIF_TIMEOUT_DIS, 0xFFFFFFFF); | |
585 | REG_WRITE(RBBMIF_TIMEOUT_DIS_2, 0xFFFFFFFF); | |
586 | ||
587 | dcn20_dccg_init(hws); | |
588 | ||
589 | REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, 2); | |
590 | REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1); | |
591 | REG_WRITE(REFCLK_CNTL, 0); | |
592 | } else { | |
593 | if (!dcb->funcs->is_accelerated_mode(dcb)) { | |
594 | bios_golden_init(dc); | |
41a5a2a8 | 595 | if (dc->ctx->dc_bios->funcs->get_firmware_info( |
596 | dc->ctx->dc_bios, &fw_info) == BP_RESULT_OK) { | |
597 | res_pool->ref_clocks.xtalin_clock_inKhz = fw_info.pll_info.crystal_frequency; | |
598 | ||
599 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
600 | if (res_pool->dccg && res_pool->hubbub) { | |
601 | ||
602 | (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg, | |
603 | fw_info.pll_info.crystal_frequency, | |
604 | &res_pool->ref_clocks.dccg_ref_clock_inKhz); | |
605 | ||
606 | (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub, | |
607 | res_pool->ref_clocks.dccg_ref_clock_inKhz, | |
608 | &res_pool->ref_clocks.dchub_ref_clock_inKhz); | |
609 | } else { | |
610 | // Not all ASICs have DCCG sw component | |
611 | res_pool->ref_clocks.dccg_ref_clock_inKhz = | |
612 | res_pool->ref_clocks.xtalin_clock_inKhz; | |
613 | res_pool->ref_clocks.dchub_ref_clock_inKhz = | |
614 | res_pool->ref_clocks.xtalin_clock_inKhz; | |
615 | } | |
616 | } | |
617 | } else | |
618 | ASSERT_CRITICAL(false); | |
7ed4e635 HW |
619 | disable_vga(dc->hwseq); |
620 | } | |
621 | ||
622 | for (i = 0; i < dc->link_count; i++) { | |
623 | /* Power up AND update implementation according to the | |
624 | * required signal (which may be different from the | |
625 | * default signal on connector). | |
626 | */ | |
627 | struct dc_link *link = dc->links[i]; | |
628 | ||
7ed4e635 HW |
629 | link->link_enc->funcs->hw_init(link->link_enc); |
630 | } | |
631 | } | |
632 | ||
75c35000 NC |
633 | /* Power gate DSCs */ |
634 | for (i = 0; i < res_pool->res_cap->num_dsc; i++) | |
635 | dcn20_dsc_pg_control(hws, res_pool->dscs[i]->inst, false); | |
636 | ||
7ed4e635 HW |
637 | /* Blank pixel data with OPP DPG */ |
638 | for (i = 0; i < dc->res_pool->timing_generator_count; i++) { | |
639 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; | |
640 | ||
641 | if (tg->funcs->is_tg_enabled(tg)) { | |
642 | dcn20_init_blank(dc, tg); | |
643 | } | |
644 | } | |
645 | ||
646 | for (i = 0; i < res_pool->timing_generator_count; i++) { | |
647 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; | |
648 | ||
649 | if (tg->funcs->is_tg_enabled(tg)) | |
650 | tg->funcs->lock(tg); | |
651 | } | |
652 | ||
653 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
654 | struct dpp *dpp = res_pool->dpps[i]; | |
655 | ||
656 | dpp->funcs->dpp_reset(dpp); | |
657 | } | |
658 | ||
659 | /* Reset all MPCC muxes */ | |
660 | res_pool->mpc->funcs->mpc_init(res_pool->mpc); | |
661 | ||
662 | /* initialize OPP mpc_tree parameter */ | |
663 | for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) { | |
664 | res_pool->opps[i]->mpc_tree_params.opp_id = res_pool->opps[i]->inst; | |
665 | res_pool->opps[i]->mpc_tree_params.opp_list = NULL; | |
666 | for (j = 0; j < MAX_PIPES; j++) | |
667 | res_pool->opps[i]->mpcc_disconnect_pending[j] = false; | |
668 | } | |
669 | ||
670 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
671 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; | |
672 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
673 | struct hubp *hubp = dc->res_pool->hubps[i]; | |
674 | struct dpp *dpp = dc->res_pool->dpps[i]; | |
675 | ||
676 | pipe_ctx->stream_res.tg = tg; | |
677 | pipe_ctx->pipe_idx = i; | |
678 | ||
679 | pipe_ctx->plane_res.hubp = hubp; | |
680 | pipe_ctx->plane_res.dpp = dpp; | |
681 | pipe_ctx->plane_res.mpcc_inst = dpp->inst; | |
682 | hubp->mpcc_id = dpp->inst; | |
043f5bb6 | 683 | hubp->opp_id = OPP_ID_INVALID; |
7ed4e635 HW |
684 | hubp->power_gated = false; |
685 | pipe_ctx->stream_res.opp = NULL; | |
686 | ||
4850ce69 CL |
687 | hubp->funcs->hubp_init(hubp); |
688 | ||
7ed4e635 HW |
689 | //dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst; |
690 | //dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL; | |
691 | dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; | |
692 | pipe_ctx->stream_res.opp = dc->res_pool->opps[i]; | |
693 | /*to do*/ | |
694 | hwss1_plane_atomic_disconnect(dc, pipe_ctx); | |
695 | } | |
696 | ||
697 | /* initialize DWB pointer to MCIF_WB */ | |
698 | for (i = 0; i < res_pool->res_cap->num_dwb; i++) | |
699 | res_pool->dwbc[i]->mcif = res_pool->mcif_wb[i]; | |
700 | ||
701 | for (i = 0; i < dc->res_pool->timing_generator_count; i++) { | |
702 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; | |
703 | ||
704 | if (tg->funcs->is_tg_enabled(tg)) | |
705 | tg->funcs->unlock(tg); | |
706 | } | |
707 | ||
708 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
709 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
710 | ||
711 | dc->hwss.disable_plane(dc, pipe_ctx); | |
712 | ||
713 | pipe_ctx->stream_res.tg = NULL; | |
714 | pipe_ctx->plane_res.hubp = NULL; | |
715 | } | |
716 | ||
717 | for (i = 0; i < dc->res_pool->timing_generator_count; i++) { | |
718 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; | |
719 | ||
720 | tg->funcs->tg_init(tg); | |
721 | } | |
722 | ||
723 | /* end of FPGA. Below if real ASIC */ | |
724 | if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) | |
725 | return; | |
726 | ||
727 | ||
728 | for (i = 0; i < res_pool->audio_count; i++) { | |
729 | struct audio *audio = res_pool->audios[i]; | |
730 | ||
731 | audio->funcs->hw_init(audio); | |
732 | } | |
733 | ||
734 | if (abm != NULL) { | |
735 | abm->funcs->init_backlight(abm); | |
736 | abm->funcs->abm_init(abm); | |
737 | } | |
738 | ||
739 | if (dmcu != NULL) | |
740 | dmcu->funcs->dmcu_init(dmcu); | |
741 | ||
98b5b65e PH |
742 | if (abm != NULL && dmcu != NULL) |
743 | abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu); | |
744 | ||
7ed4e635 HW |
745 | /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ |
746 | REG_WRITE(DIO_MEM_PWR_CTRL, 0); | |
747 | ||
748 | if (!dc->debug.disable_clock_gate) { | |
749 | /* enable all DCN clock gating */ | |
750 | REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); | |
751 | ||
752 | REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0); | |
753 | ||
754 | REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); | |
755 | } | |
756 | ||
757 | } | |
758 | ||
759 | enum dc_status dcn20_enable_stream_timing( | |
760 | struct pipe_ctx *pipe_ctx, | |
761 | struct dc_state *context, | |
762 | struct dc *dc) | |
763 | { | |
764 | struct dc_stream_state *stream = pipe_ctx->stream; | |
7ed4e635 HW |
765 | struct drr_params params = {0}; |
766 | unsigned int event_triggers = 0; | |
7ed4e635 HW |
767 | |
768 | ||
769 | #if defined(CONFIG_DRM_AMD_DC_DCN2_0) | |
770 | struct pipe_ctx *odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); | |
771 | #endif | |
772 | ||
773 | /* by upper caller loop, pipe0 is parent pipe and be called first. | |
774 | * back end is set up by for pipe0. Other children pipe share back end | |
775 | * with pipe 0. No program is needed. | |
776 | */ | |
777 | if (pipe_ctx->top_pipe != NULL) | |
778 | return DC_OK; | |
779 | ||
780 | /* TODO check if timing_changed, disable stream if timing changed */ | |
781 | ||
782 | if (odm_pipe) | |
783 | pipe_ctx->stream_res.tg->funcs->set_odm_combine( | |
784 | pipe_ctx->stream_res.tg, | |
785 | odm_pipe->stream_res.opp->inst, | |
fbc9ca67 IB |
786 | pipe_ctx->stream->timing.h_addressable/2, |
787 | pipe_ctx->stream->timing.pixel_encoding); | |
7ed4e635 HW |
788 | /* HW program guide assume display already disable |
789 | * by unplug sequence. OTG assume stop. | |
790 | */ | |
791 | pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, true); | |
792 | ||
793 | if (false == pipe_ctx->clock_source->funcs->program_pix_clk( | |
794 | pipe_ctx->clock_source, | |
795 | &pipe_ctx->stream_res.pix_clk_params, | |
796 | &pipe_ctx->pll_settings)) { | |
797 | BREAK_TO_DEBUGGER(); | |
798 | return DC_ERROR_UNEXPECTED; | |
799 | } | |
800 | ||
801 | pipe_ctx->stream_res.tg->funcs->program_timing( | |
802 | pipe_ctx->stream_res.tg, | |
803 | &stream->timing, | |
804 | pipe_ctx->pipe_dlg_param.vready_offset, | |
805 | pipe_ctx->pipe_dlg_param.vstartup_start, | |
806 | pipe_ctx->pipe_dlg_param.vupdate_offset, | |
807 | pipe_ctx->pipe_dlg_param.vupdate_width, | |
808 | pipe_ctx->stream->signal, | |
809 | true); | |
810 | ||
6c5be4ac WL |
811 | if (pipe_ctx->stream_res.tg->funcs->setup_global_lock) |
812 | pipe_ctx->stream_res.tg->funcs->setup_global_lock( | |
813 | pipe_ctx->stream_res.tg); | |
814 | ||
324707fd | 815 | if (odm_pipe) |
7ed4e635 HW |
816 | odm_pipe->stream_res.opp->funcs->opp_pipe_clock_control( |
817 | odm_pipe->stream_res.opp, | |
818 | true); | |
819 | ||
7ed4e635 HW |
820 | pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control( |
821 | pipe_ctx->stream_res.opp, | |
822 | true); | |
823 | ||
324707fd | 824 | dc->hwss.blank_pixel_data(dc, pipe_ctx, true); |
7ed4e635 HW |
825 | |
826 | /* VTG is within DCHUB command block. DCFCLK is always on */ | |
827 | if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) { | |
828 | BREAK_TO_DEBUGGER(); | |
829 | return DC_ERROR_UNEXPECTED; | |
830 | } | |
831 | ||
832 | dcn20_hwss_wait_for_blank_complete(pipe_ctx->stream_res.opp); | |
833 | ||
834 | params.vertical_total_min = stream->adjust.v_total_min; | |
835 | params.vertical_total_max = stream->adjust.v_total_max; | |
836 | if (pipe_ctx->stream_res.tg->funcs->set_drr) | |
837 | pipe_ctx->stream_res.tg->funcs->set_drr( | |
838 | pipe_ctx->stream_res.tg, ¶ms); | |
839 | ||
840 | // DRR should set trigger event to monitor surface update event | |
841 | if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0) | |
842 | event_triggers = 0x80; | |
843 | if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control) | |
844 | pipe_ctx->stream_res.tg->funcs->set_static_screen_control( | |
845 | pipe_ctx->stream_res.tg, event_triggers); | |
846 | ||
847 | /* TODO program crtc source select for non-virtual signal*/ | |
848 | /* TODO program FMT */ | |
849 | /* TODO setup link_enc */ | |
850 | /* TODO set stream attributes */ | |
851 | /* TODO program audio */ | |
852 | /* TODO enable stream if timing changed */ | |
853 | /* TODO unblank stream if DP */ | |
854 | ||
855 | return DC_OK; | |
856 | } | |
857 | ||
858 | void dcn20_program_output_csc(struct dc *dc, | |
859 | struct pipe_ctx *pipe_ctx, | |
860 | enum dc_color_space colorspace, | |
861 | uint16_t *matrix, | |
862 | int opp_id) | |
863 | { | |
864 | struct mpc *mpc = dc->res_pool->mpc; | |
865 | enum mpc_output_csc_mode ocsc_mode = MPC_OUTPUT_CSC_COEF_A; | |
866 | ||
867 | if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) { | |
868 | if (mpc->funcs->set_output_csc != NULL) | |
869 | mpc->funcs->set_output_csc(mpc, | |
870 | opp_id, | |
871 | matrix, | |
872 | ocsc_mode); | |
873 | } else { | |
874 | if (mpc->funcs->set_ocsc_default != NULL) | |
875 | mpc->funcs->set_ocsc_default(mpc, | |
876 | opp_id, | |
877 | colorspace, | |
878 | ocsc_mode); | |
879 | } | |
880 | } | |
881 | ||
882 | bool dcn20_set_output_transfer_func(struct pipe_ctx *pipe_ctx, | |
883 | const struct dc_stream_state *stream) | |
884 | { | |
885 | int mpcc_id = pipe_ctx->plane_res.hubp->inst; | |
886 | struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; | |
887 | struct pwl_params *params = NULL; | |
888 | /* | |
889 | * program OGAM only for the top pipe | |
890 | * if there is a pipe split then fix diagnostic is required: | |
891 | * how to pass OGAM parameter for stream. | |
892 | * if programming for all pipes is required then remove condition | |
893 | * pipe_ctx->top_pipe == NULL ,but then fix the diagnostic. | |
894 | */ | |
895 | if ((pipe_ctx->top_pipe == NULL || dc_res_is_odm_head_pipe(pipe_ctx)) | |
896 | && mpc->funcs->set_output_gamma && stream->out_transfer_func) { | |
897 | if (stream->out_transfer_func->type == TF_TYPE_HWPWL) | |
898 | params = &stream->out_transfer_func->pwl; | |
899 | else if (pipe_ctx->stream->out_transfer_func->type == | |
900 | TF_TYPE_DISTRIBUTED_POINTS && | |
901 | cm_helper_translate_curve_to_hw_format( | |
902 | stream->out_transfer_func, | |
903 | &mpc->blender_params, false)) | |
904 | params = &mpc->blender_params; | |
905 | /* | |
906 | * there is no ROM | |
907 | */ | |
908 | if (stream->out_transfer_func->type == TF_TYPE_PREDEFINED) | |
909 | BREAK_TO_DEBUGGER(); | |
910 | } | |
911 | /* | |
912 | * if above if is not executed then 'params' equal to 0 and set in bypass | |
913 | */ | |
914 | mpc->funcs->set_output_gamma(mpc, mpcc_id, params); | |
915 | ||
916 | return true; | |
917 | } | |
918 | ||
919 | static bool dcn20_set_blend_lut( | |
920 | struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) | |
921 | { | |
922 | struct dpp *dpp_base = pipe_ctx->plane_res.dpp; | |
923 | bool result = true; | |
924 | struct pwl_params *blend_lut = NULL; | |
925 | ||
926 | if (plane_state->blend_tf) { | |
927 | if (plane_state->blend_tf->type == TF_TYPE_HWPWL) | |
928 | blend_lut = &plane_state->blend_tf->pwl; | |
929 | else if (plane_state->blend_tf->type == TF_TYPE_DISTRIBUTED_POINTS) { | |
930 | cm_helper_translate_curve_to_hw_format( | |
931 | plane_state->blend_tf, | |
932 | &dpp_base->regamma_params, false); | |
933 | blend_lut = &dpp_base->regamma_params; | |
934 | } | |
935 | } | |
936 | result = dpp_base->funcs->dpp_program_blnd_lut(dpp_base, blend_lut); | |
937 | ||
938 | return result; | |
939 | } | |
940 | ||
941 | static bool dcn20_set_shaper_3dlut( | |
942 | struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) | |
943 | { | |
944 | struct dpp *dpp_base = pipe_ctx->plane_res.dpp; | |
945 | bool result = true; | |
946 | struct pwl_params *shaper_lut = NULL; | |
947 | ||
948 | if (plane_state->in_shaper_func) { | |
949 | if (plane_state->in_shaper_func->type == TF_TYPE_HWPWL) | |
950 | shaper_lut = &plane_state->in_shaper_func->pwl; | |
951 | else if (plane_state->in_shaper_func->type == TF_TYPE_DISTRIBUTED_POINTS) { | |
952 | cm_helper_translate_curve_to_hw_format( | |
953 | plane_state->in_shaper_func, | |
954 | &dpp_base->shaper_params, true); | |
955 | shaper_lut = &dpp_base->shaper_params; | |
956 | } | |
957 | } | |
958 | ||
959 | result = dpp_base->funcs->dpp_program_shaper_lut(dpp_base, shaper_lut); | |
960 | if (plane_state->lut3d_func && | |
a2080098 | 961 | plane_state->lut3d_func->state.bits.initialized == 1) |
7ed4e635 HW |
962 | result = dpp_base->funcs->dpp_program_3dlut(dpp_base, |
963 | &plane_state->lut3d_func->lut_3d); | |
964 | else | |
965 | result = dpp_base->funcs->dpp_program_3dlut(dpp_base, NULL); | |
966 | ||
967 | if (plane_state->lut3d_func && | |
a2080098 | 968 | plane_state->lut3d_func->state.bits.initialized == 1 && |
7ed4e635 HW |
969 | plane_state->lut3d_func->hdr_multiplier != 0) |
970 | dpp_base->funcs->dpp_set_hdr_multiplier(dpp_base, | |
971 | plane_state->lut3d_func->hdr_multiplier); | |
972 | else | |
973 | dpp_base->funcs->dpp_set_hdr_multiplier(dpp_base, 0x1f000); | |
974 | ||
975 | return result; | |
976 | } | |
977 | ||
978 | bool dcn20_set_input_transfer_func(struct pipe_ctx *pipe_ctx, | |
979 | const struct dc_plane_state *plane_state) | |
980 | { | |
981 | struct dpp *dpp_base = pipe_ctx->plane_res.dpp; | |
982 | const struct dc_transfer_func *tf = NULL; | |
983 | bool result = true; | |
984 | bool use_degamma_ram = false; | |
985 | ||
986 | if (dpp_base == NULL || plane_state == NULL) | |
987 | return false; | |
988 | ||
989 | dcn20_set_shaper_3dlut(pipe_ctx, plane_state); | |
990 | dcn20_set_blend_lut(pipe_ctx, plane_state); | |
991 | ||
992 | if (plane_state->in_transfer_func) | |
993 | tf = plane_state->in_transfer_func; | |
994 | ||
995 | ||
996 | if (tf == NULL) { | |
997 | dpp_base->funcs->dpp_set_degamma(dpp_base, | |
998 | IPP_DEGAMMA_MODE_BYPASS); | |
999 | return true; | |
1000 | } | |
1001 | ||
1002 | if (tf->type == TF_TYPE_HWPWL || tf->type == TF_TYPE_DISTRIBUTED_POINTS) | |
1003 | use_degamma_ram = true; | |
1004 | ||
1005 | if (use_degamma_ram == true) { | |
1006 | if (tf->type == TF_TYPE_HWPWL) | |
1007 | dpp_base->funcs->dpp_program_degamma_pwl(dpp_base, | |
1008 | &tf->pwl); | |
1009 | else if (tf->type == TF_TYPE_DISTRIBUTED_POINTS) { | |
1010 | cm_helper_translate_curve_to_degamma_hw_format(tf, | |
1011 | &dpp_base->degamma_params); | |
1012 | dpp_base->funcs->dpp_program_degamma_pwl(dpp_base, | |
1013 | &dpp_base->degamma_params); | |
1014 | } | |
1015 | return true; | |
1016 | } | |
1017 | /* handle here the optimized cases when de-gamma ROM could be used. | |
1018 | * | |
1019 | */ | |
1020 | if (tf->type == TF_TYPE_PREDEFINED) { | |
1021 | switch (tf->tf) { | |
1022 | case TRANSFER_FUNCTION_SRGB: | |
1023 | dpp_base->funcs->dpp_set_degamma(dpp_base, | |
1024 | IPP_DEGAMMA_MODE_HW_sRGB); | |
1025 | break; | |
1026 | case TRANSFER_FUNCTION_BT709: | |
1027 | dpp_base->funcs->dpp_set_degamma(dpp_base, | |
1028 | IPP_DEGAMMA_MODE_HW_xvYCC); | |
1029 | break; | |
1030 | case TRANSFER_FUNCTION_LINEAR: | |
1031 | dpp_base->funcs->dpp_set_degamma(dpp_base, | |
1032 | IPP_DEGAMMA_MODE_BYPASS); | |
1033 | break; | |
1034 | case TRANSFER_FUNCTION_PQ: | |
1035 | default: | |
1036 | result = false; | |
1037 | break; | |
1038 | } | |
1039 | } else if (tf->type == TF_TYPE_BYPASS) | |
1040 | dpp_base->funcs->dpp_set_degamma(dpp_base, | |
1041 | IPP_DEGAMMA_MODE_BYPASS); | |
1042 | else { | |
1043 | /* | |
1044 | * if we are here, we did not handle correctly. | |
1045 | * fix is required for this use case | |
1046 | */ | |
1047 | BREAK_TO_DEBUGGER(); | |
1048 | dpp_base->funcs->dpp_set_degamma(dpp_base, | |
1049 | IPP_DEGAMMA_MODE_BYPASS); | |
1050 | } | |
1051 | ||
1052 | return result; | |
1053 | } | |
1054 | ||
1055 | static void dcn20_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx) | |
1056 | { | |
1057 | struct pipe_ctx *combine_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); | |
1058 | ||
1059 | if (combine_pipe) | |
1060 | pipe_ctx->stream_res.tg->funcs->set_odm_combine( | |
1061 | pipe_ctx->stream_res.tg, | |
1062 | combine_pipe->stream_res.opp->inst, | |
fbc9ca67 IB |
1063 | pipe_ctx->plane_res.scl_data.h_active, |
1064 | pipe_ctx->stream->timing.pixel_encoding); | |
7ed4e635 HW |
1065 | else |
1066 | pipe_ctx->stream_res.tg->funcs->set_odm_bypass( | |
1067 | pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); | |
1068 | } | |
1069 | ||
1070 | void dcn20_blank_pixel_data( | |
1071 | struct dc *dc, | |
1072 | struct pipe_ctx *pipe_ctx, | |
1073 | bool blank) | |
1074 | { | |
7ed4e635 HW |
1075 | struct tg_color black_color = {0}; |
1076 | struct stream_resource *stream_res = &pipe_ctx->stream_res; | |
1077 | struct dc_stream_state *stream = pipe_ctx->stream; | |
324707fd | 1078 | enum dc_color_space color_space = stream->output_color_space; |
7ed4e635 HW |
1079 | enum controller_dp_test_pattern test_pattern = CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR; |
1080 | struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); | |
1081 | ||
7ed4e635 HW |
1082 | int width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right; |
1083 | int height = stream->timing.v_addressable + stream->timing.v_border_bottom + stream->timing.v_border_top; | |
1084 | ||
324707fd | 1085 | /* get opp dpg blank color */ |
7ed4e635 HW |
1086 | color_space_to_black_color(dc, color_space, &black_color); |
1087 | ||
1088 | if (bot_odm_pipe) | |
1089 | width = width / 2; | |
1090 | ||
1091 | if (blank) { | |
1092 | if (stream_res->abm) | |
1093 | stream_res->abm->funcs->set_abm_immediate_disable(stream_res->abm); | |
7ed4e635 | 1094 | |
324707fd JA |
1095 | if (dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE) |
1096 | test_pattern = CONTROLLER_DP_TEST_PATTERN_COLORSQUARES; | |
1097 | } else { | |
1098 | test_pattern = CONTROLLER_DP_TEST_PATTERN_VIDEOMODE; | |
1099 | } | |
7ed4e635 HW |
1100 | |
1101 | stream_res->opp->funcs->opp_set_disp_pattern_generator( | |
1102 | stream_res->opp, | |
1103 | test_pattern, | |
1104 | stream->timing.display_color_depth, | |
1105 | &black_color, | |
1106 | width, | |
1107 | height); | |
1108 | ||
1109 | if (bot_odm_pipe) { | |
1110 | bot_odm_pipe->stream_res.opp->funcs->opp_set_disp_pattern_generator( | |
1111 | bot_odm_pipe->stream_res.opp, | |
324707fd JA |
1112 | dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE ? |
1113 | CONTROLLER_DP_TEST_PATTERN_COLORRAMP : test_pattern, | |
7ed4e635 HW |
1114 | stream->timing.display_color_depth, |
1115 | &black_color, | |
1116 | width, | |
1117 | height); | |
1118 | } | |
1119 | ||
1120 | if (!blank) | |
1121 | if (stream_res->abm) { | |
1122 | stream_res->abm->funcs->set_pipe(stream_res->abm, stream_res->tg->inst + 1); | |
1123 | stream_res->abm->funcs->set_abm_level(stream_res->abm, stream->abm_level); | |
1124 | } | |
1125 | } | |
1126 | ||
1127 | ||
1128 | static void dcn20_power_on_plane( | |
1129 | struct dce_hwseq *hws, | |
1130 | struct pipe_ctx *pipe_ctx) | |
1131 | { | |
1132 | DC_LOGGER_INIT(hws->ctx->logger); | |
1133 | if (REG(DC_IP_REQUEST_CNTL)) { | |
1134 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
1135 | IP_REQUEST_EN, 1); | |
1136 | dcn20_dpp_pg_control(hws, pipe_ctx->plane_res.dpp->inst, true); | |
1137 | dcn20_hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, true); | |
1138 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
1139 | IP_REQUEST_EN, 0); | |
1140 | DC_LOG_DEBUG( | |
1141 | "Un-gated front end for pipe %d\n", pipe_ctx->plane_res.hubp->inst); | |
1142 | } | |
1143 | } | |
1144 | ||
ad141db9 | 1145 | void dcn20_enable_plane( |
7ed4e635 HW |
1146 | struct dc *dc, |
1147 | struct pipe_ctx *pipe_ctx, | |
1148 | struct dc_state *context) | |
1149 | { | |
1150 | //if (dc->debug.sanity_checks) { | |
1151 | // dcn10_verify_allow_pstate_change_high(dc); | |
1152 | //} | |
1153 | dcn20_power_on_plane(dc->hwseq, pipe_ctx); | |
1154 | ||
1155 | /* enable DCFCLK current DCHUB */ | |
1156 | pipe_ctx->plane_res.hubp->funcs->hubp_clk_cntl(pipe_ctx->plane_res.hubp, true); | |
1157 | ||
1158 | /* make sure OPP_PIPE_CLOCK_EN = 1 */ | |
1159 | pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control( | |
1160 | pipe_ctx->stream_res.opp, | |
1161 | true); | |
1162 | ||
1163 | /* TODO: enable/disable in dm as per update type. | |
1164 | if (plane_state) { | |
1165 | DC_LOG_DC(dc->ctx->logger, | |
1166 | "Pipe:%d 0x%x: addr hi:0x%x, " | |
1167 | "addr low:0x%x, " | |
1168 | "src: %d, %d, %d," | |
1169 | " %d; dst: %d, %d, %d, %d;\n", | |
1170 | pipe_ctx->pipe_idx, | |
1171 | plane_state, | |
1172 | plane_state->address.grph.addr.high_part, | |
1173 | plane_state->address.grph.addr.low_part, | |
1174 | plane_state->src_rect.x, | |
1175 | plane_state->src_rect.y, | |
1176 | plane_state->src_rect.width, | |
1177 | plane_state->src_rect.height, | |
1178 | plane_state->dst_rect.x, | |
1179 | plane_state->dst_rect.y, | |
1180 | plane_state->dst_rect.width, | |
1181 | plane_state->dst_rect.height); | |
1182 | ||
1183 | DC_LOG_DC(dc->ctx->logger, | |
1184 | "Pipe %d: width, height, x, y format:%d\n" | |
1185 | "viewport:%d, %d, %d, %d\n" | |
1186 | "recout: %d, %d, %d, %d\n", | |
1187 | pipe_ctx->pipe_idx, | |
1188 | plane_state->format, | |
1189 | pipe_ctx->plane_res.scl_data.viewport.width, | |
1190 | pipe_ctx->plane_res.scl_data.viewport.height, | |
1191 | pipe_ctx->plane_res.scl_data.viewport.x, | |
1192 | pipe_ctx->plane_res.scl_data.viewport.y, | |
1193 | pipe_ctx->plane_res.scl_data.recout.width, | |
1194 | pipe_ctx->plane_res.scl_data.recout.height, | |
1195 | pipe_ctx->plane_res.scl_data.recout.x, | |
1196 | pipe_ctx->plane_res.scl_data.recout.y); | |
1197 | print_rq_dlg_ttu(dc, pipe_ctx); | |
1198 | } | |
1199 | */ | |
bda9afda | 1200 | if (dc->vm_pa_config.valid) { |
7ed4e635 HW |
1201 | struct vm_system_aperture_param apt; |
1202 | ||
1203 | apt.sys_default.quad_part = 0; | |
7ed4e635 | 1204 | |
6d988a55 JL |
1205 | apt.sys_low.quad_part = dc->vm_pa_config.system_aperture.start_addr; |
1206 | apt.sys_high.quad_part = dc->vm_pa_config.system_aperture.end_addr; | |
7ed4e635 HW |
1207 | |
1208 | // Program system aperture settings | |
1209 | pipe_ctx->plane_res.hubp->funcs->hubp_set_vm_system_aperture_settings(pipe_ctx->plane_res.hubp, &apt); | |
1210 | } | |
1211 | ||
1212 | // if (dc->debug.sanity_checks) { | |
1213 | // dcn10_verify_allow_pstate_change_high(dc); | |
1214 | // } | |
1215 | } | |
1216 | ||
1217 | ||
ad141db9 | 1218 | static void dcn20_program_pipe( |
7ed4e635 HW |
1219 | struct dc *dc, |
1220 | struct pipe_ctx *pipe_ctx, | |
1221 | struct dc_state *context) | |
1222 | { | |
1223 | pipe_ctx->plane_state->update_flags.bits.full_update = | |
1224 | context->commit_hints.full_update_needed ? 1 : pipe_ctx->plane_state->update_flags.bits.full_update; | |
1225 | ||
1226 | if (pipe_ctx->plane_state->update_flags.bits.full_update) | |
1227 | dcn20_enable_plane(dc, pipe_ctx, context); | |
1228 | ||
1229 | update_dchubp_dpp(dc, pipe_ctx, context); | |
1230 | ||
1231 | set_hdr_multiplier(pipe_ctx); | |
1232 | ||
1233 | if (pipe_ctx->plane_state->update_flags.bits.full_update || | |
1234 | pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || | |
1235 | pipe_ctx->plane_state->update_flags.bits.gamma_change) | |
1236 | dc->hwss.set_input_transfer_func(pipe_ctx, pipe_ctx->plane_state); | |
1237 | ||
1238 | /* dcn10_translate_regamma_to_hw_format takes 750us to finish | |
1239 | * only do gamma programming for full update. | |
1240 | * TODO: This can be further optimized/cleaned up | |
1241 | * Always call this for now since it does memcmp inside before | |
1242 | * doing heavy calculation and programming | |
1243 | */ | |
1244 | if (pipe_ctx->plane_state->update_flags.bits.full_update) | |
1245 | dc->hwss.set_output_transfer_func(pipe_ctx, pipe_ctx->stream); | |
1246 | } | |
1247 | ||
1248 | static void dcn20_program_all_pipe_in_tree( | |
1249 | struct dc *dc, | |
1250 | struct pipe_ctx *pipe_ctx, | |
1251 | struct dc_state *context) | |
1252 | { | |
1253 | if (pipe_ctx->top_pipe == NULL) { | |
1254 | bool blank = !is_pipe_tree_visible(pipe_ctx); | |
1255 | ||
1256 | pipe_ctx->stream_res.tg->funcs->program_global_sync( | |
1257 | pipe_ctx->stream_res.tg, | |
1258 | pipe_ctx->pipe_dlg_param.vready_offset, | |
1259 | pipe_ctx->pipe_dlg_param.vstartup_start, | |
1260 | pipe_ctx->pipe_dlg_param.vupdate_offset, | |
1261 | pipe_ctx->pipe_dlg_param.vupdate_width); | |
1262 | ||
3972c350 JA |
1263 | pipe_ctx->stream_res.tg->funcs->set_vtg_params( |
1264 | pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); | |
1265 | ||
7ed4e635 HW |
1266 | dc->hwss.blank_pixel_data(dc, pipe_ctx, blank); |
1267 | ||
1268 | if (dc->hwss.update_odm) | |
1269 | dc->hwss.update_odm(dc, context, pipe_ctx); | |
1270 | } | |
1271 | ||
1272 | if (pipe_ctx->plane_state != NULL) | |
1273 | dcn20_program_pipe(dc, pipe_ctx, context); | |
1274 | ||
1275 | if (pipe_ctx->bottom_pipe != NULL && pipe_ctx->bottom_pipe != pipe_ctx) | |
1276 | dcn20_program_all_pipe_in_tree(dc, pipe_ctx->bottom_pipe, context); | |
1277 | } | |
1278 | ||
4850ce69 | 1279 | void dcn20_pipe_control_lock_global( |
7ed4e635 HW |
1280 | struct dc *dc, |
1281 | struct pipe_ctx *pipe, | |
1282 | bool lock) | |
1283 | { | |
db5378c1 WL |
1284 | if (lock) { |
1285 | pipe->stream_res.tg->funcs->lock_doublebuffer_enable( | |
1286 | pipe->stream_res.tg); | |
1287 | pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg); | |
1288 | } else { | |
7ed4e635 | 1289 | pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg); |
db5378c1 WL |
1290 | pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, |
1291 | CRTC_STATE_VACTIVE); | |
1292 | pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, | |
1293 | CRTC_STATE_VBLANK); | |
29344d15 WL |
1294 | pipe->stream_res.tg->funcs->wait_for_state(pipe->stream_res.tg, |
1295 | CRTC_STATE_VACTIVE); | |
db5378c1 WL |
1296 | pipe->stream_res.tg->funcs->lock_doublebuffer_disable( |
1297 | pipe->stream_res.tg); | |
1298 | } | |
7ed4e635 HW |
1299 | } |
1300 | ||
4850ce69 | 1301 | void dcn20_pipe_control_lock( |
7ed4e635 HW |
1302 | struct dc *dc, |
1303 | struct pipe_ctx *pipe, | |
1304 | bool lock) | |
1305 | { | |
1306 | bool flip_immediate = false; | |
1307 | ||
1308 | /* use TG master update lock to lock everything on the TG | |
1309 | * therefore only top pipe need to lock | |
1310 | */ | |
1311 | if (pipe->top_pipe) | |
1312 | return; | |
1313 | ||
1314 | if (pipe->plane_state != NULL) | |
1315 | flip_immediate = pipe->plane_state->flip_immediate; | |
1316 | ||
4c6a9618 AL |
1317 | if (flip_immediate && lock) { |
1318 | while (pipe->plane_res.hubp->funcs->hubp_is_flip_pending(pipe->plane_res.hubp)) { | |
1319 | udelay(1); | |
1320 | } | |
1321 | ||
1322 | if (pipe->bottom_pipe != NULL) | |
1323 | while (pipe->bottom_pipe->plane_res.hubp->funcs->hubp_is_flip_pending(pipe->bottom_pipe->plane_res.hubp)) { | |
1324 | udelay(1); | |
1325 | } | |
1326 | } | |
1327 | ||
7ed4e635 HW |
1328 | /* In flip immediate and pipe splitting case, we need to use GSL |
1329 | * for synchronization. Only do setup on locking and on flip type change. | |
1330 | */ | |
1331 | if (lock && pipe->bottom_pipe != NULL) | |
1332 | if ((flip_immediate && pipe->stream_res.gsl_group == 0) || | |
1333 | (!flip_immediate && pipe->stream_res.gsl_group > 0)) | |
2e2e73fc | 1334 | dcn20_setup_gsl_group_as_lock(dc, pipe, flip_immediate); |
7ed4e635 HW |
1335 | |
1336 | if (pipe->plane_state != NULL && pipe->plane_state->triplebuffer_flips) { | |
1337 | if (lock) | |
1338 | pipe->stream_res.tg->funcs->triplebuffer_lock(pipe->stream_res.tg); | |
1339 | else | |
1340 | pipe->stream_res.tg->funcs->triplebuffer_unlock(pipe->stream_res.tg); | |
1341 | } else { | |
1342 | if (lock) | |
1343 | pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg); | |
1344 | else | |
1345 | pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg); | |
1346 | } | |
1347 | } | |
1348 | ||
7ed4e635 HW |
1349 | static void dcn20_apply_ctx_for_surface( |
1350 | struct dc *dc, | |
1351 | const struct dc_stream_state *stream, | |
1352 | int num_planes, | |
1353 | struct dc_state *context) | |
1354 | { | |
1355 | ||
1356 | int i; | |
1357 | struct timing_generator *tg; | |
1358 | bool removed_pipe[6] = { false }; | |
4e0cbbbf | 1359 | bool interdependent_update = false; |
7ed4e635 HW |
1360 | struct pipe_ctx *top_pipe_to_program = |
1361 | find_top_pipe_for_stream(dc, context, stream); | |
1362 | DC_LOGGER_INIT(dc->ctx->logger); | |
1363 | ||
1364 | if (!top_pipe_to_program) | |
1365 | return; | |
1366 | ||
1367 | tg = top_pipe_to_program->stream_res.tg; | |
1368 | ||
4e0cbbbf LL |
1369 | interdependent_update = top_pipe_to_program->plane_state && |
1370 | top_pipe_to_program->plane_state->update_flags.bits.full_update; | |
1371 | ||
1372 | if (interdependent_update) | |
1373 | lock_all_pipes(dc, context, true); | |
1374 | else | |
1375 | dcn20_pipe_control_lock(dc, top_pipe_to_program, true); | |
7ed4e635 HW |
1376 | |
1377 | if (num_planes == 0) { | |
1378 | /* OTG blank before remove all front end */ | |
1379 | dc->hwss.blank_pixel_data(dc, top_pipe_to_program, true); | |
1380 | } | |
1381 | ||
1382 | /* Disconnect unused mpcc */ | |
1383 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1384 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
1385 | struct pipe_ctx *old_pipe_ctx = | |
1386 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
1387 | /* | |
1388 | * Powergate reused pipes that are not powergated | |
1389 | * fairly hacky right now, using opp_id as indicator | |
1390 | * TODO: After move dc_post to dc_update, this will | |
1391 | * be removed. | |
1392 | */ | |
1393 | if (pipe_ctx->plane_state && !old_pipe_ctx->plane_state) { | |
1394 | if (old_pipe_ctx->stream_res.tg == tg && | |
4e0cbbbf | 1395 | old_pipe_ctx->plane_res.hubp && |
043f5bb6 | 1396 | old_pipe_ctx->plane_res.hubp->opp_id != OPP_ID_INVALID) |
7ed4e635 | 1397 | dcn20_disable_plane(dc, old_pipe_ctx); |
7ed4e635 HW |
1398 | } |
1399 | ||
1b394e92 LL |
1400 | if ((!pipe_ctx->plane_state || |
1401 | pipe_ctx->stream_res.tg != old_pipe_ctx->stream_res.tg) && | |
1402 | old_pipe_ctx->plane_state && | |
1403 | old_pipe_ctx->stream_res.tg == tg) { | |
7ed4e635 HW |
1404 | |
1405 | dc->hwss.plane_atomic_disconnect(dc, old_pipe_ctx); | |
1406 | removed_pipe[i] = true; | |
1407 | ||
1408 | DC_LOG_DC("Reset mpcc for pipe %d\n", | |
1409 | old_pipe_ctx->pipe_idx); | |
1410 | } | |
1411 | } | |
1412 | ||
1413 | if (num_planes > 0) | |
1414 | dcn20_program_all_pipe_in_tree(dc, top_pipe_to_program, context); | |
1415 | ||
1416 | /* Program secondary blending tree and writeback pipes */ | |
1417 | if ((stream->num_wb_info > 0) && (dc->hwss.program_all_writeback_pipes_in_tree)) | |
1418 | dc->hwss.program_all_writeback_pipes_in_tree(dc, stream, context); | |
1419 | ||
4e0cbbbf | 1420 | if (interdependent_update) |
7ed4e635 HW |
1421 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
1422 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
1423 | ||
1424 | /* Skip inactive pipes and ones already updated */ | |
4e0cbbbf LL |
1425 | if (!pipe_ctx->stream || pipe_ctx->stream == stream || |
1426 | !pipe_ctx->plane_state || !tg->funcs->is_tg_enabled(tg)) | |
7ed4e635 HW |
1427 | continue; |
1428 | ||
7ed4e635 HW |
1429 | pipe_ctx->plane_res.hubp->funcs->hubp_setup_interdependent( |
1430 | pipe_ctx->plane_res.hubp, | |
1431 | &pipe_ctx->dlg_regs, | |
1432 | &pipe_ctx->ttu_regs); | |
1433 | } | |
1434 | ||
4e0cbbbf LL |
1435 | if (interdependent_update) |
1436 | lock_all_pipes(dc, context, false); | |
1437 | else | |
1438 | dcn20_pipe_control_lock(dc, top_pipe_to_program, false); | |
7ed4e635 HW |
1439 | |
1440 | for (i = 0; i < dc->res_pool->pipe_count; i++) | |
1441 | if (removed_pipe[i]) | |
1442 | dcn20_disable_plane(dc, &dc->current_state->res_ctx.pipe_ctx[i]); | |
1443 | } | |
1444 | ||
1445 | ||
1446 | void dcn20_prepare_bandwidth( | |
1447 | struct dc *dc, | |
1448 | struct dc_state *context) | |
1449 | { | |
1450 | struct hubbub *hubbub = dc->res_pool->hubbub; | |
1451 | ||
057fc695 JL |
1452 | dc->clk_mgr->funcs->update_clocks( |
1453 | dc->clk_mgr, | |
1454 | context, | |
1455 | false); | |
1456 | ||
7ed4e635 HW |
1457 | /* program dchubbub watermarks */ |
1458 | hubbub->funcs->program_watermarks(hubbub, | |
1459 | &context->bw_ctx.bw.dcn.watermarks, | |
1460 | dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, | |
1461 | false); | |
7ed4e635 HW |
1462 | } |
1463 | ||
1464 | void dcn20_optimize_bandwidth( | |
1465 | struct dc *dc, | |
1466 | struct dc_state *context) | |
1467 | { | |
1468 | struct hubbub *hubbub = dc->res_pool->hubbub; | |
1469 | ||
1470 | /* program dchubbub watermarks */ | |
1471 | hubbub->funcs->program_watermarks(hubbub, | |
1472 | &context->bw_ctx.bw.dcn.watermarks, | |
1473 | dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, | |
1474 | true); | |
1475 | ||
1476 | dc->clk_mgr->funcs->update_clocks( | |
1477 | dc->clk_mgr, | |
1478 | context, | |
1479 | true); | |
1480 | } | |
1481 | ||
1482 | bool dcn20_update_bandwidth( | |
1483 | struct dc *dc, | |
1484 | struct dc_state *context) | |
1485 | { | |
1486 | int i; | |
1487 | ||
1488 | /* recalculate DML parameters */ | |
254eb07c | 1489 | if (!dc->res_pool->funcs->validate_bandwidth(dc, context, false)) |
7ed4e635 | 1490 | return false; |
7ed4e635 HW |
1491 | |
1492 | /* apply updated bandwidth parameters */ | |
1493 | dc->hwss.prepare_bandwidth(dc, context); | |
1494 | ||
1495 | /* update hubp configs for all pipes */ | |
1496 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1497 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
1498 | ||
1499 | if (pipe_ctx->plane_state == NULL) | |
1500 | continue; | |
1501 | ||
1502 | if (pipe_ctx->top_pipe == NULL) { | |
1503 | bool blank = !is_pipe_tree_visible(pipe_ctx); | |
1504 | ||
1505 | pipe_ctx->stream_res.tg->funcs->program_global_sync( | |
1506 | pipe_ctx->stream_res.tg, | |
1507 | pipe_ctx->pipe_dlg_param.vready_offset, | |
1508 | pipe_ctx->pipe_dlg_param.vstartup_start, | |
1509 | pipe_ctx->pipe_dlg_param.vupdate_offset, | |
1510 | pipe_ctx->pipe_dlg_param.vupdate_width); | |
1511 | ||
3972c350 JA |
1512 | pipe_ctx->stream_res.tg->funcs->set_vtg_params( |
1513 | pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); | |
1514 | ||
7ed4e635 HW |
1515 | dc->hwss.blank_pixel_data(dc, pipe_ctx, blank); |
1516 | } | |
1517 | ||
1518 | pipe_ctx->plane_res.hubp->funcs->hubp_setup( | |
1519 | pipe_ctx->plane_res.hubp, | |
1520 | &pipe_ctx->dlg_regs, | |
1521 | &pipe_ctx->ttu_regs, | |
1522 | &pipe_ctx->rq_regs, | |
1523 | &pipe_ctx->pipe_dlg_param); | |
1524 | } | |
1525 | ||
1526 | return true; | |
1527 | } | |
1528 | ||
1529 | static void dcn20_enable_writeback( | |
1530 | struct dc *dc, | |
1531 | const struct dc_stream_status *stream_status, | |
1532 | struct dc_writeback_info *wb_info) | |
1533 | { | |
1534 | struct dwbc *dwb; | |
1535 | struct mcif_wb *mcif_wb; | |
1536 | struct timing_generator *optc; | |
1537 | ||
1538 | ASSERT(wb_info->dwb_pipe_inst < MAX_DWB_PIPES); | |
1539 | ASSERT(wb_info->wb_enabled); | |
1540 | dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst]; | |
1541 | mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst]; | |
1542 | ||
1543 | /* set the OPTC source mux */ | |
1544 | ASSERT(stream_status->primary_otg_inst < MAX_PIPES); | |
1545 | optc = dc->res_pool->timing_generators[stream_status->primary_otg_inst]; | |
1546 | optc->funcs->set_dwb_source(optc, wb_info->dwb_pipe_inst); | |
1547 | /* set MCIF_WB buffer and arbitration configuration */ | |
1548 | mcif_wb->funcs->config_mcif_buf(mcif_wb, &wb_info->mcif_buf_params, wb_info->dwb_params.dest_height); | |
1549 | mcif_wb->funcs->config_mcif_arb(mcif_wb, &dc->current_state->bw_ctx.bw.dcn.bw_writeback.mcif_wb_arb[wb_info->dwb_pipe_inst]); | |
1550 | /* Enable MCIF_WB */ | |
1551 | mcif_wb->funcs->enable_mcif(mcif_wb); | |
1552 | /* Enable DWB */ | |
1553 | dwb->funcs->enable(dwb, &wb_info->dwb_params); | |
1554 | /* TODO: add sequence to enable/disable warmup */ | |
1555 | } | |
1556 | ||
1557 | void dcn20_disable_writeback( | |
1558 | struct dc *dc, | |
1559 | unsigned int dwb_pipe_inst) | |
1560 | { | |
1561 | struct dwbc *dwb; | |
1562 | struct mcif_wb *mcif_wb; | |
1563 | ||
1564 | ASSERT(dwb_pipe_inst < MAX_DWB_PIPES); | |
1565 | dwb = dc->res_pool->dwbc[dwb_pipe_inst]; | |
1566 | mcif_wb = dc->res_pool->mcif_wb[dwb_pipe_inst]; | |
1567 | ||
1568 | dwb->funcs->disable(dwb); | |
1569 | mcif_wb->funcs->disable_mcif(mcif_wb); | |
1570 | } | |
1571 | ||
1572 | bool dcn20_hwss_wait_for_blank_complete( | |
1573 | struct output_pixel_processor *opp) | |
1574 | { | |
1575 | int counter; | |
1576 | ||
1577 | for (counter = 0; counter < 1000; counter++) { | |
1578 | if (opp->funcs->dpg_is_blanked(opp)) | |
1579 | break; | |
1580 | ||
1581 | udelay(100); | |
1582 | } | |
1583 | ||
1584 | if (counter == 1000) { | |
1585 | dm_error("DC: failed to blank crtc!\n"); | |
1586 | return false; | |
1587 | } | |
1588 | ||
1589 | return true; | |
1590 | } | |
1591 | ||
1592 | bool dcn20_dmdata_status_done(struct pipe_ctx *pipe_ctx) | |
1593 | { | |
1594 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
1595 | ||
1596 | if (!hubp) | |
1597 | return false; | |
1598 | return hubp->funcs->dmdata_status_done(hubp); | |
1599 | } | |
1600 | ||
1601 | static void dcn20_disable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx) | |
1602 | { | |
97bda032 HW |
1603 | #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT |
1604 | struct dce_hwseq *hws = dc->hwseq; | |
1605 | struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); | |
1606 | ||
1607 | if (pipe_ctx->stream_res.dsc) { | |
1608 | dcn20_dsc_pg_control(hws, pipe_ctx->stream_res.dsc->inst, true); | |
1609 | if (bot_odm_pipe) | |
1610 | dcn20_dsc_pg_control(hws, bot_odm_pipe->stream_res.dsc->inst, true); | |
1611 | } | |
1612 | #endif | |
7ed4e635 HW |
1613 | } |
1614 | ||
1615 | static void dcn20_enable_stream_gating(struct dc *dc, struct pipe_ctx *pipe_ctx) | |
1616 | { | |
97bda032 HW |
1617 | #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT |
1618 | struct dce_hwseq *hws = dc->hwseq; | |
1619 | struct pipe_ctx *bot_odm_pipe = dc_res_get_odm_bottom_pipe(pipe_ctx); | |
1620 | ||
1621 | if (pipe_ctx->stream_res.dsc) { | |
1622 | dcn20_dsc_pg_control(hws, pipe_ctx->stream_res.dsc->inst, false); | |
1623 | if (bot_odm_pipe) | |
1624 | dcn20_dsc_pg_control(hws, bot_odm_pipe->stream_res.dsc->inst, false); | |
1625 | } | |
1626 | #endif | |
7ed4e635 HW |
1627 | } |
1628 | ||
1629 | void dcn20_set_dmdata_attributes(struct pipe_ctx *pipe_ctx) | |
1630 | { | |
1631 | struct dc_dmdata_attributes attr = { 0 }; | |
1632 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
1633 | ||
1634 | attr.dmdata_mode = DMDATA_HW_MODE; | |
1635 | attr.dmdata_size = | |
1636 | dc_is_hdmi_signal(pipe_ctx->stream->signal) ? 32 : 36; | |
1637 | attr.address.quad_part = | |
1638 | pipe_ctx->stream->dmdata_address.quad_part; | |
1639 | attr.dmdata_dl_delta = 0; | |
1640 | attr.dmdata_qos_mode = 0; | |
1641 | attr.dmdata_qos_level = 0; | |
1642 | attr.dmdata_repeat = 1; /* always repeat */ | |
1643 | attr.dmdata_updated = 1; | |
1644 | attr.dmdata_sw_data = NULL; | |
1645 | ||
1646 | hubp->funcs->dmdata_set_attributes(hubp, &attr); | |
1647 | } | |
1648 | ||
1649 | void dcn20_disable_stream(struct pipe_ctx *pipe_ctx, int option) | |
1650 | { | |
1651 | dce110_disable_stream(pipe_ctx, option); | |
1652 | } | |
1653 | ||
bda9afda DL |
1654 | static void dcn20_init_vm_ctx( |
1655 | struct dce_hwseq *hws, | |
1656 | struct dc *dc, | |
1657 | struct dc_virtual_addr_space_config *va_config, | |
1658 | int vmid) | |
7ed4e635 | 1659 | { |
bda9afda DL |
1660 | struct dcn_hubbub_virt_addr_config config; |
1661 | ||
1662 | if (vmid == 0) { | |
1663 | ASSERT(0); /* VMID cannot be 0 for vm context */ | |
1664 | return; | |
1665 | } | |
1666 | ||
1667 | config.page_table_start_addr = va_config->page_table_start_addr; | |
1668 | config.page_table_end_addr = va_config->page_table_end_addr; | |
1669 | config.page_table_block_size = va_config->page_table_block_size_in_bytes; | |
1670 | config.page_table_depth = va_config->page_table_depth; | |
1671 | config.page_table_base_addr = va_config->page_table_base_addr; | |
1672 | ||
1673 | dc->res_pool->hubbub->funcs->init_vm_ctx(dc->res_pool->hubbub, &config, vmid); | |
1674 | } | |
1675 | ||
1676 | static int dcn20_init_sys_ctx(struct dce_hwseq *hws, struct dc *dc, struct dc_phy_addr_space_config *pa_config) | |
1677 | { | |
1678 | struct dcn_hubbub_phys_addr_config config; | |
1679 | ||
1680 | config.system_aperture.fb_top = pa_config->system_aperture.fb_top; | |
1681 | config.system_aperture.fb_offset = pa_config->system_aperture.fb_offset; | |
1682 | config.system_aperture.fb_base = pa_config->system_aperture.fb_base; | |
1683 | config.system_aperture.agp_top = pa_config->system_aperture.agp_top; | |
1684 | config.system_aperture.agp_bot = pa_config->system_aperture.agp_bot; | |
1685 | config.system_aperture.agp_base = pa_config->system_aperture.agp_base; | |
1686 | config.gart_config.page_table_start_addr = pa_config->gart_config.page_table_start_addr; | |
1687 | config.gart_config.page_table_end_addr = pa_config->gart_config.page_table_end_addr; | |
1688 | config.gart_config.page_table_base_addr = pa_config->gart_config.page_table_base_addr; | |
1689 | ||
1690 | return dc->res_pool->hubbub->funcs->init_dchub_sys_ctx(dc->res_pool->hubbub, &config); | |
7ed4e635 HW |
1691 | } |
1692 | ||
1693 | static bool patch_address_for_sbs_tb_stereo( | |
1694 | struct pipe_ctx *pipe_ctx, PHYSICAL_ADDRESS_LOC *addr) | |
1695 | { | |
1696 | struct dc_plane_state *plane_state = pipe_ctx->plane_state; | |
1697 | bool sec_split = pipe_ctx->top_pipe && | |
1698 | pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state; | |
1699 | if (sec_split && plane_state->address.type == PLN_ADDR_TYPE_GRPH_STEREO && | |
1700 | (pipe_ctx->stream->timing.timing_3d_format == | |
1701 | TIMING_3D_FORMAT_SIDE_BY_SIDE || | |
1702 | pipe_ctx->stream->timing.timing_3d_format == | |
1703 | TIMING_3D_FORMAT_TOP_AND_BOTTOM)) { | |
1704 | *addr = plane_state->address.grph_stereo.left_addr; | |
1705 | plane_state->address.grph_stereo.left_addr = | |
1706 | plane_state->address.grph_stereo.right_addr; | |
1707 | return true; | |
1708 | } | |
1709 | ||
1710 | if (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_NONE && | |
1711 | plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO) { | |
1712 | plane_state->address.type = PLN_ADDR_TYPE_GRPH_STEREO; | |
1713 | plane_state->address.grph_stereo.right_addr = | |
1714 | plane_state->address.grph_stereo.left_addr; | |
1715 | } | |
1716 | return false; | |
1717 | } | |
1718 | ||
1719 | ||
1720 | static void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) | |
1721 | { | |
1722 | bool addr_patched = false; | |
1723 | PHYSICAL_ADDRESS_LOC addr; | |
1724 | struct dc_plane_state *plane_state = pipe_ctx->plane_state; | |
7ed4e635 HW |
1725 | |
1726 | if (plane_state == NULL) | |
1727 | return; | |
1728 | ||
1729 | addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr); | |
1730 | ||
bda9afda DL |
1731 | // Call Helper to track VMID use |
1732 | vm_helper_mark_vmid_used(dc->vm_helper, plane_state->address.vmid, pipe_ctx->plane_res.hubp->inst); | |
7ed4e635 HW |
1733 | |
1734 | pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( | |
1735 | pipe_ctx->plane_res.hubp, | |
1736 | &plane_state->address, | |
bda9afda | 1737 | plane_state->flip_immediate); |
7ed4e635 HW |
1738 | |
1739 | plane_state->status.requested_address = plane_state->address; | |
1740 | ||
1741 | if (plane_state->flip_immediate) | |
1742 | plane_state->status.current_address = plane_state->address; | |
1743 | ||
1744 | if (addr_patched) | |
1745 | pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; | |
1746 | } | |
1747 | ||
1748 | void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx, | |
1749 | struct dc_link_settings *link_settings) | |
1750 | { | |
1751 | struct encoder_unblank_param params = { { 0 } }; | |
1752 | struct dc_stream_state *stream = pipe_ctx->stream; | |
1753 | struct dc_link *link = stream->link; | |
1754 | params.odm = dc_res_get_odm_bottom_pipe(pipe_ctx); | |
1755 | ||
1756 | /* only 3 items below are used by unblank */ | |
1757 | params.timing = pipe_ctx->stream->timing; | |
1758 | ||
1759 | params.link_settings.link_rate = link_settings->link_rate; | |
1760 | ||
1761 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) { | |
1762 | if (optc1_is_two_pixels_per_containter(&stream->timing) || params.odm) | |
1763 | params.timing.pix_clk_100hz /= 2; | |
1764 | pipe_ctx->stream_res.stream_enc->funcs->dp_set_odm_combine( | |
1765 | pipe_ctx->stream_res.stream_enc, params.odm); | |
1766 | pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(pipe_ctx->stream_res.stream_enc, ¶ms); | |
1767 | } | |
1768 | ||
1769 | if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { | |
1770 | link->dc->hwss.edp_backlight_control(link, true); | |
1771 | } | |
1772 | } | |
1773 | ||
1774 | void dcn20_setup_vupdate_interrupt(struct pipe_ctx *pipe_ctx) | |
1775 | { | |
1776 | struct timing_generator *tg = pipe_ctx->stream_res.tg; | |
7fad39ca | 1777 | int start_line = get_vupdate_offset_from_vsync(pipe_ctx); |
7ed4e635 | 1778 | |
7fad39ca EB |
1779 | if (start_line < 0) |
1780 | start_line = 0; | |
7ed4e635 HW |
1781 | |
1782 | if (tg->funcs->setup_vertical_interrupt2) | |
1783 | tg->funcs->setup_vertical_interrupt2(tg, start_line); | |
1784 | } | |
1785 | ||
1786 | static void dcn20_reset_back_end_for_pipe( | |
1787 | struct dc *dc, | |
1788 | struct pipe_ctx *pipe_ctx, | |
1789 | struct dc_state *context) | |
1790 | { | |
1791 | int i; | |
1792 | DC_LOGGER_INIT(dc->ctx->logger); | |
1793 | if (pipe_ctx->stream_res.stream_enc == NULL) { | |
1794 | pipe_ctx->stream = NULL; | |
1795 | return; | |
1796 | } | |
1797 | ||
1798 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
1799 | /* DPMS may already disable */ | |
1800 | if (!pipe_ctx->stream->dpms_off) | |
1801 | core_link_disable_stream(pipe_ctx, FREE_ACQUIRED_RESOURCE); | |
1802 | else if (pipe_ctx->stream_res.audio) { | |
1803 | dc->hwss.disable_audio_stream(pipe_ctx, FREE_ACQUIRED_RESOURCE); | |
1804 | } | |
7ed4e635 | 1805 | } |
ec16ac6b | 1806 | #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT |
606b3551 | 1807 | else if (pipe_ctx->stream_res.dsc) { |
ec16ac6b | 1808 | dp_set_dsc_enable(pipe_ctx, false); |
606b3551 | 1809 | } |
ec16ac6b | 1810 | #endif |
7ed4e635 HW |
1811 | |
1812 | /* by upper caller loop, parent pipe: pipe0, will be reset last. | |
1813 | * back end share by all pipes and will be disable only when disable | |
1814 | * parent pipe. | |
1815 | */ | |
1816 | if (pipe_ctx->top_pipe == NULL) { | |
1817 | pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); | |
1818 | ||
1819 | pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); | |
1820 | if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass) | |
1821 | pipe_ctx->stream_res.tg->funcs->set_odm_bypass( | |
1822 | pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); | |
1823 | } | |
1824 | ||
1825 | for (i = 0; i < dc->res_pool->pipe_count; i++) | |
1826 | if (&dc->current_state->res_ctx.pipe_ctx[i] == pipe_ctx) | |
1827 | break; | |
1828 | ||
1829 | if (i == dc->res_pool->pipe_count) | |
1830 | return; | |
1831 | ||
1832 | pipe_ctx->stream = NULL; | |
1833 | DC_LOG_DEBUG("Reset back end for pipe %d, tg:%d\n", | |
1834 | pipe_ctx->pipe_idx, pipe_ctx->stream_res.tg->inst); | |
1835 | } | |
1836 | ||
1837 | static void dcn20_reset_hw_ctx_wrap( | |
1838 | struct dc *dc, | |
1839 | struct dc_state *context) | |
1840 | { | |
1841 | int i; | |
1842 | ||
1843 | /* Reset Back End*/ | |
1844 | for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { | |
1845 | struct pipe_ctx *pipe_ctx_old = | |
1846 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
1847 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
1848 | ||
1849 | if (!pipe_ctx_old->stream) | |
1850 | continue; | |
1851 | ||
1852 | if (pipe_ctx_old->top_pipe) | |
1853 | continue; | |
1854 | ||
1855 | if (!pipe_ctx->stream || | |
1856 | pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { | |
1857 | struct clock_source *old_clk = pipe_ctx_old->clock_source; | |
1858 | ||
1859 | dcn20_reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state); | |
1860 | if (dc->hwss.enable_stream_gating) | |
1861 | dc->hwss.enable_stream_gating(dc, pipe_ctx); | |
1862 | if (old_clk) | |
1863 | old_clk->funcs->cs_power_down(old_clk); | |
1864 | } | |
1865 | } | |
1866 | } | |
1867 | ||
1868 | static void dcn20_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) | |
1869 | { | |
1870 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
1871 | struct mpcc_blnd_cfg blnd_cfg = { {0} }; | |
473e0ecb | 1872 | bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha; |
7ed4e635 HW |
1873 | int mpcc_id; |
1874 | struct mpcc *new_mpcc; | |
1875 | struct mpc *mpc = dc->res_pool->mpc; | |
1876 | struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params); | |
1877 | ||
1878 | // input to MPCC is always RGB, by default leave black_color at 0 | |
1879 | if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR) { | |
1880 | dcn10_get_hdr_visual_confirm_color( | |
1881 | pipe_ctx, &blnd_cfg.black_color); | |
1882 | } else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) { | |
1883 | dcn10_get_surface_visual_confirm_color( | |
1884 | pipe_ctx, &blnd_cfg.black_color); | |
1885 | } | |
1886 | ||
1887 | if (per_pixel_alpha) | |
1888 | blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; | |
1889 | else | |
1890 | blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; | |
1891 | ||
1892 | blnd_cfg.overlap_only = false; | |
1893 | blnd_cfg.global_gain = 0xff; | |
1894 | ||
1895 | if (pipe_ctx->plane_state->global_alpha) | |
1896 | blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value; | |
1897 | else | |
1898 | blnd_cfg.global_alpha = 0xff; | |
1899 | ||
1900 | blnd_cfg.background_color_bpc = 4; | |
1901 | blnd_cfg.bottom_gain_mode = 0; | |
1902 | blnd_cfg.top_gain = 0x1f000; | |
1903 | blnd_cfg.bottom_inside_gain = 0x1f000; | |
1904 | blnd_cfg.bottom_outside_gain = 0x1f000; | |
1905 | blnd_cfg.pre_multiplied_alpha = per_pixel_alpha; | |
1906 | ||
1907 | /* | |
1908 | * TODO: remove hack | |
1909 | * Note: currently there is a bug in init_hw such that | |
1910 | * on resume from hibernate, BIOS sets up MPCC0, and | |
1911 | * we do mpcc_remove but the mpcc cannot go to idle | |
1912 | * after remove. This cause us to pick mpcc1 here, | |
1913 | * which causes a pstate hang for yet unknown reason. | |
1914 | */ | |
1915 | mpcc_id = hubp->inst; | |
1916 | ||
1917 | /* If there is no full update, don't need to touch MPC tree*/ | |
1918 | if (!pipe_ctx->plane_state->update_flags.bits.full_update) { | |
1919 | mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); | |
1920 | return; | |
1921 | } | |
1922 | ||
1923 | /* check if this MPCC is already being used */ | |
1924 | new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id); | |
1925 | /* remove MPCC if being used */ | |
1926 | if (new_mpcc != NULL) | |
1927 | mpc->funcs->remove_mpcc(mpc, mpc_tree_params, new_mpcc); | |
1928 | else | |
1929 | if (dc->debug.sanity_checks) | |
1930 | mpc->funcs->assert_mpcc_idle_before_connect( | |
1931 | dc->res_pool->mpc, mpcc_id); | |
1932 | ||
1933 | /* Call MPC to insert new plane */ | |
1934 | new_mpcc = mpc->funcs->insert_plane(dc->res_pool->mpc, | |
1935 | mpc_tree_params, | |
1936 | &blnd_cfg, | |
1937 | NULL, | |
1938 | NULL, | |
1939 | hubp->inst, | |
1940 | mpcc_id); | |
1941 | ||
1942 | ASSERT(new_mpcc != NULL); | |
1943 | hubp->opp_id = pipe_ctx->stream_res.opp->inst; | |
1944 | hubp->mpcc_id = mpcc_id; | |
1945 | } | |
1946 | ||
1947 | static int find_free_gsl_group(const struct dc *dc) | |
1948 | { | |
1949 | if (dc->res_pool->gsl_groups.gsl_0 == 0) | |
1950 | return 1; | |
1951 | if (dc->res_pool->gsl_groups.gsl_1 == 0) | |
1952 | return 2; | |
1953 | if (dc->res_pool->gsl_groups.gsl_2 == 0) | |
1954 | return 3; | |
1955 | ||
1956 | return 0; | |
1957 | } | |
1958 | ||
1959 | /* NOTE: This is not a generic setup_gsl function (hence the suffix as_lock) | |
1960 | * This is only used to lock pipes in pipe splitting case with immediate flip | |
1961 | * Ordinary MPC/OTG locks suppress VUPDATE which doesn't help with immediate, | |
1962 | * so we get tearing with freesync since we cannot flip multiple pipes | |
1963 | * atomically. | |
1964 | * We use GSL for this: | |
1965 | * - immediate flip: find first available GSL group if not already assigned | |
1966 | * program gsl with that group, set current OTG as master | |
1967 | * and always us 0x4 = AND of flip_ready from all pipes | |
1968 | * - vsync flip: disable GSL if used | |
1969 | * | |
1970 | * Groups in stream_res are stored as +1 from HW registers, i.e. | |
1971 | * gsl_0 <=> pipe_ctx->stream_res.gsl_group == 1 | |
1972 | * Using a magic value like -1 would require tracking all inits/resets | |
1973 | */ | |
1974 | void dcn20_setup_gsl_group_as_lock( | |
1975 | const struct dc *dc, | |
2e2e73fc | 1976 | struct pipe_ctx *pipe_ctx, |
6bd8d7d3 | 1977 | bool enable) |
7ed4e635 HW |
1978 | { |
1979 | struct gsl_params gsl; | |
1980 | int group_idx; | |
1981 | ||
1982 | memset(&gsl, 0, sizeof(struct gsl_params)); | |
1983 | ||
6bd8d7d3 | 1984 | if (enable) { |
7ed4e635 HW |
1985 | /* return if group already assigned since GSL was set up |
1986 | * for vsync flip, we would unassign so it can't be "left over" | |
1987 | */ | |
1988 | if (pipe_ctx->stream_res.gsl_group > 0) | |
1989 | return; | |
1990 | ||
1991 | group_idx = find_free_gsl_group(dc); | |
1992 | ASSERT(group_idx != 0); | |
1993 | pipe_ctx->stream_res.gsl_group = group_idx; | |
1994 | ||
1995 | /* set gsl group reg field and mark resource used */ | |
1996 | switch (group_idx) { | |
1997 | case 1: | |
1998 | gsl.gsl0_en = 1; | |
1999 | dc->res_pool->gsl_groups.gsl_0 = 1; | |
2000 | break; | |
2001 | case 2: | |
2002 | gsl.gsl1_en = 1; | |
2003 | dc->res_pool->gsl_groups.gsl_1 = 1; | |
2004 | break; | |
2005 | case 3: | |
2006 | gsl.gsl2_en = 1; | |
2007 | dc->res_pool->gsl_groups.gsl_2 = 1; | |
2008 | break; | |
2009 | default: | |
2010 | BREAK_TO_DEBUGGER(); | |
2011 | return; // invalid case | |
2012 | } | |
2013 | gsl.gsl_master_en = 1; | |
2014 | } else { | |
2015 | group_idx = pipe_ctx->stream_res.gsl_group; | |
2016 | if (group_idx == 0) | |
2017 | return; // if not in use, just return | |
2018 | ||
2019 | pipe_ctx->stream_res.gsl_group = 0; | |
2020 | ||
2021 | /* unset gsl group reg field and mark resource free */ | |
2022 | switch (group_idx) { | |
2023 | case 1: | |
2024 | gsl.gsl0_en = 0; | |
2025 | dc->res_pool->gsl_groups.gsl_0 = 0; | |
2026 | break; | |
2027 | case 2: | |
2028 | gsl.gsl1_en = 0; | |
2029 | dc->res_pool->gsl_groups.gsl_1 = 0; | |
2030 | break; | |
2031 | case 3: | |
2032 | gsl.gsl2_en = 0; | |
2033 | dc->res_pool->gsl_groups.gsl_2 = 0; | |
2034 | break; | |
2035 | default: | |
2036 | BREAK_TO_DEBUGGER(); | |
2037 | return; | |
2038 | } | |
2039 | gsl.gsl_master_en = 0; | |
2040 | } | |
2041 | ||
2042 | /* at this point we want to program whether it's to enable or disable */ | |
2043 | if (pipe_ctx->stream_res.tg->funcs->set_gsl != NULL && | |
2044 | pipe_ctx->stream_res.tg->funcs->set_gsl_source_select != NULL) { | |
2045 | pipe_ctx->stream_res.tg->funcs->set_gsl( | |
2046 | pipe_ctx->stream_res.tg, | |
2047 | &gsl); | |
2048 | ||
2049 | pipe_ctx->stream_res.tg->funcs->set_gsl_source_select( | |
6bd8d7d3 | 2050 | pipe_ctx->stream_res.tg, group_idx, enable ? 4 : 0); |
7ed4e635 HW |
2051 | } else |
2052 | BREAK_TO_DEBUGGER(); | |
2053 | } | |
2054 | ||
2055 | static void dcn20_set_flip_control_gsl( | |
2056 | struct pipe_ctx *pipe_ctx, | |
2057 | bool flip_immediate) | |
2058 | { | |
2059 | if (pipe_ctx && pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_control_surface_gsl) | |
2060 | pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_control_surface_gsl( | |
2061 | pipe_ctx->plane_res.hubp, flip_immediate); | |
2062 | ||
2063 | } | |
2064 | ||
f591344e JP |
2065 | static void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) |
2066 | { | |
2067 | enum dc_lane_count lane_count = | |
2068 | pipe_ctx->stream->link->cur_link_settings.lane_count; | |
2069 | ||
2070 | struct dc_crtc_timing *timing = &pipe_ctx->stream->timing; | |
2071 | struct dc_link *link = pipe_ctx->stream->link; | |
2072 | ||
2073 | uint32_t active_total_with_borders; | |
2074 | uint32_t early_control = 0; | |
2075 | struct timing_generator *tg = pipe_ctx->stream_res.tg; | |
2076 | ||
2077 | /* For MST, there are multiply stream go to only one link. | |
2078 | * connect DIG back_end to front_end while enable_stream and | |
2079 | * disconnect them during disable_stream | |
2080 | * BY this, it is logic clean to separate stream and link | |
2081 | */ | |
2082 | link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc, | |
2083 | pipe_ctx->stream_res.stream_enc->id, true); | |
2084 | ||
2085 | if (link->dc->hwss.program_dmdata_engine) | |
2086 | link->dc->hwss.program_dmdata_engine(pipe_ctx); | |
2087 | ||
2088 | link->dc->hwss.update_info_frame(pipe_ctx); | |
2089 | ||
2090 | /* enable early control to avoid corruption on DP monitor*/ | |
2091 | active_total_with_borders = | |
2092 | timing->h_addressable | |
2093 | + timing->h_border_left | |
2094 | + timing->h_border_right; | |
2095 | ||
2096 | if (lane_count != 0) | |
2097 | early_control = active_total_with_borders % lane_count; | |
2098 | ||
2099 | if (early_control == 0) | |
2100 | early_control = lane_count; | |
2101 | ||
2102 | tg->funcs->set_early_control(tg, early_control); | |
2103 | ||
2104 | /* enable audio only within mode set */ | |
2105 | if (pipe_ctx->stream_res.audio != NULL) { | |
2106 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) | |
2107 | pipe_ctx->stream_res.stream_enc->funcs->dp_audio_enable(pipe_ctx->stream_res.stream_enc); | |
2108 | } | |
2109 | } | |
2110 | ||
2111 | static void dcn20_program_dmdata_engine(struct pipe_ctx *pipe_ctx) | |
2112 | { | |
2113 | struct dc_stream_state *stream = pipe_ctx->stream; | |
2114 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
2115 | bool enable = false; | |
2116 | struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc; | |
2117 | enum dynamic_metadata_mode mode = dc_is_dp_signal(stream->signal) | |
2118 | ? dmdata_dp | |
2119 | : dmdata_hdmi; | |
2120 | ||
2121 | /* if using dynamic meta, don't set up generic infopackets */ | |
2122 | if (pipe_ctx->stream->dmdata_address.quad_part != 0) { | |
2123 | pipe_ctx->stream_res.encoder_info_frame.hdrsmd.valid = false; | |
2124 | enable = true; | |
2125 | } | |
2126 | ||
2127 | if (!hubp) | |
2128 | return; | |
2129 | ||
2130 | if (!stream_enc || !stream_enc->funcs->set_dynamic_metadata) | |
2131 | return; | |
2132 | ||
2133 | stream_enc->funcs->set_dynamic_metadata(stream_enc, enable, | |
2134 | hubp->inst, mode); | |
2135 | } | |
2136 | ||
7ed4e635 HW |
2137 | void dcn20_hw_sequencer_construct(struct dc *dc) |
2138 | { | |
2139 | dcn10_hw_sequencer_construct(dc); | |
2140 | dc->hwss.init_hw = dcn20_init_hw; | |
2141 | dc->hwss.init_pipes = NULL; | |
2142 | dc->hwss.unblank_stream = dcn20_unblank_stream; | |
2143 | dc->hwss.update_plane_addr = dcn20_update_plane_addr; | |
2144 | dc->hwss.disable_plane = dcn20_disable_plane, | |
2145 | dc->hwss.enable_stream_timing = dcn20_enable_stream_timing; | |
2146 | dc->hwss.program_triplebuffer = dcn20_program_tripleBuffer; | |
2147 | dc->hwss.set_input_transfer_func = dcn20_set_input_transfer_func; | |
2148 | dc->hwss.set_output_transfer_func = dcn20_set_output_transfer_func; | |
2149 | dc->hwss.apply_ctx_for_surface = dcn20_apply_ctx_for_surface; | |
2150 | dc->hwss.pipe_control_lock = dcn20_pipe_control_lock; | |
2151 | dc->hwss.pipe_control_lock_global = dcn20_pipe_control_lock_global; | |
2152 | dc->hwss.optimize_bandwidth = dcn20_optimize_bandwidth; | |
2153 | dc->hwss.prepare_bandwidth = dcn20_prepare_bandwidth; | |
2154 | dc->hwss.update_bandwidth = dcn20_update_bandwidth; | |
2155 | dc->hwss.enable_writeback = dcn20_enable_writeback; | |
2156 | dc->hwss.disable_writeback = dcn20_disable_writeback; | |
2157 | dc->hwss.program_output_csc = dcn20_program_output_csc; | |
2158 | dc->hwss.update_odm = dcn20_update_odm; | |
2159 | dc->hwss.blank_pixel_data = dcn20_blank_pixel_data; | |
2160 | dc->hwss.dmdata_status_done = dcn20_dmdata_status_done; | |
f591344e JP |
2161 | dc->hwss.program_dmdata_engine = dcn20_program_dmdata_engine; |
2162 | dc->hwss.enable_stream = dcn20_enable_stream; | |
7ed4e635 | 2163 | dc->hwss.disable_stream = dcn20_disable_stream; |
bda9afda DL |
2164 | dc->hwss.init_sys_ctx = dcn20_init_sys_ctx; |
2165 | dc->hwss.init_vm_ctx = dcn20_init_vm_ctx; | |
7ed4e635 HW |
2166 | dc->hwss.disable_stream_gating = dcn20_disable_stream_gating; |
2167 | dc->hwss.enable_stream_gating = dcn20_enable_stream_gating; | |
2168 | dc->hwss.setup_vupdate_interrupt = dcn20_setup_vupdate_interrupt; | |
2169 | dc->hwss.reset_hw_ctx_wrap = dcn20_reset_hw_ctx_wrap; | |
2170 | dc->hwss.update_mpcc = dcn20_update_mpcc; | |
2171 | dc->hwss.set_flip_control_gsl = dcn20_set_flip_control_gsl; | |
1a7d296d | 2172 | dc->hwss.did_underflow_occur = dcn10_did_underflow_occur; |
7ed4e635 | 2173 | } |