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