]>
Commit | Line | Data |
---|---|---|
70ccab60 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 | */ | |
25 | ||
88099f53 | 26 | #include <linux/delay.h> |
70ccab60 | 27 | #include "dm_services.h" |
78c77382 | 28 | #include "basics/dc_common.h" |
70ccab60 | 29 | #include "core_types.h" |
70ccab60 | 30 | #include "resource.h" |
b02c3b05 | 31 | #include "custom_float.h" |
70ccab60 | 32 | #include "dcn10_hw_sequencer.h" |
78c77382 | 33 | #include "dcn10_hw_sequencer_debug.h" |
5aff86c1 | 34 | #include "dce/dce_hwseq.h" |
70ccab60 | 35 | #include "abm.h" |
4952d4c5 | 36 | #include "dmcu.h" |
b51adc77 | 37 | #include "dcn10_optc.h" |
78c77382 AK |
38 | #include "dcn10_dpp.h" |
39 | #include "dcn10_mpc.h" | |
70ccab60 HW |
40 | #include "timing_generator.h" |
41 | #include "opp.h" | |
42 | #include "ipp.h" | |
b02c3b05 | 43 | #include "mpc.h" |
184debdb | 44 | #include "reg_helper.h" |
86be9a04 | 45 | #include "dcn10_hubp.h" |
62d591a8 | 46 | #include "dcn10_hubbub.h" |
b6295960 | 47 | #include "dcn10_cm_common.h" |
aa9c4abe | 48 | #include "dc_link_dp.h" |
ea2e8d92 | 49 | #include "dccg.h" |
dc88b4a6 | 50 | #include "clk_mgr.h" |
896dace8 JG |
51 | #include "link_hwss.h" |
52 | #include "dpcd_defs.h" | |
97bda032 | 53 | #include "dsc.h" |
dc6e2448 | 54 | #include "dce/dmub_hw_lock_mgr.h" |
8b198f6e | 55 | #include "dc_trace.h" |
4f8e37db | 56 | #include "dce/dmub_outbox.h" |
c5bc8c1b | 57 | #include "inc/dc_link_dp.h" |
30adeee5 | 58 | #include "inc/link_dpcd.h" |
7ed4e635 | 59 | |
5d4b05dd BL |
60 | #define DC_LOGGER_INIT(logger) |
61 | ||
184debdb DL |
62 | #define CTX \ |
63 | hws->ctx | |
64 | #define REG(reg)\ | |
65 | hws->regs->reg | |
70ccab60 | 66 | |
184debdb DL |
67 | #undef FN |
68 | #define FN(reg_name, field_name) \ | |
69 | hws->shifts->field_name, hws->masks->field_name | |
70ccab60 | 70 | |
a052a516 | 71 | /*print is 17 wide, first two characters are spaces*/ |
62d591a8 | 72 | #define DTN_INFO_MICRO_SEC(ref_cycle) \ |
46659a83 | 73 | print_microsec(dc_ctx, log_ctx, ref_cycle) |
71395011 | 74 | |
78c77382 AK |
75 | #define GAMMA_HW_POINTS_NUM 256 |
76 | ||
23344703 RS |
77 | #define PGFSM_POWER_ON 0 |
78 | #define PGFSM_POWER_OFF 2 | |
79 | ||
46659a83 NK |
80 | void print_microsec(struct dc_context *dc_ctx, |
81 | struct dc_log_buffer_ctx *log_ctx, | |
82 | uint32_t ref_cycle) | |
eb4e33b7 | 83 | { |
33d7598d | 84 | const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; |
a052a516 | 85 | static const unsigned int frac = 1000; |
eb4e33b7 TC |
86 | uint32_t us_x10 = (ref_cycle * frac) / ref_clk_mhz; |
87 | ||
a052a516 | 88 | DTN_INFO(" %11d.%03d", |
eb4e33b7 TC |
89 | us_x10 / frac, |
90 | us_x10 % frac); | |
91 | } | |
92 | ||
009114f6 | 93 | void dcn10_lock_all_pipes(struct dc *dc, |
78c77382 AK |
94 | struct dc_state *context, |
95 | bool lock) | |
96 | { | |
97 | struct pipe_ctx *pipe_ctx; | |
98 | struct timing_generator *tg; | |
99 | int i; | |
100 | ||
101 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
102 | pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
103 | tg = pipe_ctx->stream_res.tg; | |
009114f6 | 104 | |
78c77382 AK |
105 | /* |
106 | * Only lock the top pipe's tg to prevent redundant | |
107 | * (un)locking. Also skip if pipe is disabled. | |
108 | */ | |
109 | if (pipe_ctx->top_pipe || | |
110 | !pipe_ctx->stream || !pipe_ctx->plane_state || | |
111 | !tg->funcs->is_tg_enabled(tg)) | |
112 | continue; | |
113 | ||
114 | if (lock) | |
009114f6 | 115 | dc->hwss.pipe_control_lock(dc, pipe_ctx, true); |
78c77382 | 116 | else |
009114f6 | 117 | dc->hwss.pipe_control_lock(dc, pipe_ctx, false); |
78c77382 AK |
118 | } |
119 | } | |
120 | ||
46659a83 NK |
121 | static void log_mpc_crc(struct dc *dc, |
122 | struct dc_log_buffer_ctx *log_ctx) | |
233dcd20 | 123 | { |
62d591a8 YHL |
124 | struct dc_context *dc_ctx = dc->ctx; |
125 | struct dce_hwseq *hws = dc->hwseq; | |
126 | ||
127 | if (REG(MPC_CRC_RESULT_GB)) | |
128 | DTN_INFO("MPC_CRC_RESULT_GB:%d MPC_CRC_RESULT_C:%d MPC_CRC_RESULT_AR:%d\n", | |
129 | REG_READ(MPC_CRC_RESULT_GB), REG_READ(MPC_CRC_RESULT_C), REG_READ(MPC_CRC_RESULT_AR)); | |
130 | if (REG(DPP_TOP0_DPP_CRC_VAL_B_A)) | |
131 | DTN_INFO("DPP_TOP0_DPP_CRC_VAL_B_A:%d DPP_TOP0_DPP_CRC_VAL_R_G:%d\n", | |
132 | REG_READ(DPP_TOP0_DPP_CRC_VAL_B_A), REG_READ(DPP_TOP0_DPP_CRC_VAL_R_G)); | |
233dcd20 TC |
133 | } |
134 | ||
46659a83 | 135 | void dcn10_log_hubbub_state(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) |
233dcd20 TC |
136 | { |
137 | struct dc_context *dc_ctx = dc->ctx; | |
785fd44c | 138 | struct dcn_hubbub_wm wm; |
233dcd20 TC |
139 | int i; |
140 | ||
785fd44c | 141 | memset(&wm, 0, sizeof(struct dcn_hubbub_wm)); |
da1043cf | 142 | dc->res_pool->hubbub->funcs->wm_read_state(dc->res_pool->hubbub, &wm); |
233dcd20 | 143 | |
a052a516 DL |
144 | DTN_INFO("HUBBUB WM: data_urgent pte_meta_urgent" |
145 | " sr_enter sr_exit dram_clk_change\n"); | |
233dcd20 TC |
146 | |
147 | for (i = 0; i < 4; i++) { | |
148 | struct dcn_hubbub_wm_set *s; | |
149 | ||
150 | s = &wm.sets[i]; | |
a052a516 | 151 | DTN_INFO("WM_Set[%d]:", s->wm_set); |
233dcd20 TC |
152 | DTN_INFO_MICRO_SEC(s->data_urgent); |
153 | DTN_INFO_MICRO_SEC(s->pte_meta_urgent); | |
154 | DTN_INFO_MICRO_SEC(s->sr_enter); | |
155 | DTN_INFO_MICRO_SEC(s->sr_exit); | |
156 | DTN_INFO_MICRO_SEC(s->dram_clk_chanage); | |
157 | DTN_INFO("\n"); | |
158 | } | |
159 | ||
160 | DTN_INFO("\n"); | |
161 | } | |
162 | ||
46659a83 | 163 | static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx) |
71395011 TC |
164 | { |
165 | struct dc_context *dc_ctx = dc->ctx; | |
166 | struct resource_pool *pool = dc->res_pool; | |
167 | int i; | |
168 | ||
ae8cf977 LHM |
169 | DTN_INFO( |
170 | "HUBP: format addr_hi width height rot mir sw_mode dcc_en blank_en clock_en ttu_dis underflow min_ttu_vblank qos_low_wm qos_high_wm\n"); | |
71395011 | 171 | for (i = 0; i < pool->pipe_count; i++) { |
8feabd03 | 172 | struct hubp *hubp = pool->hubps[i]; |
34cb6b38 | 173 | struct dcn_hubp_state *s = &(TO_DCN10_HUBP(hubp)->state); |
71395011 | 174 | |
34cb6b38 | 175 | hubp->funcs->hubp_read_state(hubp); |
71395011 | 176 | |
a3cb1c1c | 177 | if (!s->blank_en) { |
ae8cf977 | 178 | DTN_INFO("[%2d]: %5xh %6xh %5d %6d %2xh %2xh %6xh %6d %8d %8d %7d %8xh", |
a3cb1c1c NC |
179 | hubp->inst, |
180 | s->pixel_format, | |
181 | s->inuse_addr_hi, | |
182 | s->viewport_width, | |
183 | s->viewport_height, | |
184 | s->rotation_angle, | |
185 | s->h_mirror_en, | |
186 | s->sw_mode, | |
187 | s->dcc_en, | |
188 | s->blank_en, | |
ae8cf977 | 189 | s->clock_en, |
a3cb1c1c NC |
190 | s->ttu_disable, |
191 | s->underflow_status); | |
192 | DTN_INFO_MICRO_SEC(s->min_ttu_vblank); | |
193 | DTN_INFO_MICRO_SEC(s->qos_level_low_wm); | |
194 | DTN_INFO_MICRO_SEC(s->qos_level_high_wm); | |
195 | DTN_INFO("\n"); | |
196 | } | |
71395011 | 197 | } |
34cb6b38 DL |
198 | |
199 | DTN_INFO("\n=========RQ========\n"); | |
200 | DTN_INFO("HUBP: drq_exp_m prq_exp_m mrq_exp_m crq_exp_m plane1_ba L:chunk_s min_chu_s meta_ch_s" | |
b9c1c67a | 201 | " min_m_c_s dpte_gr_s mpte_gr_s swath_hei pte_row_h C:chunk_s min_chu_s meta_ch_s" |
34cb6b38 DL |
202 | " min_m_c_s dpte_gr_s mpte_gr_s swath_hei pte_row_h\n"); |
203 | for (i = 0; i < pool->pipe_count; i++) { | |
204 | struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); | |
205 | struct _vcs_dpi_display_rq_regs_st *rq_regs = &s->rq_regs; | |
206 | ||
a3cb1c1c | 207 | if (!s->blank_en) |
b9c1c67a | 208 | DTN_INFO("[%2d]: %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh\n", |
a3cb1c1c NC |
209 | pool->hubps[i]->inst, rq_regs->drq_expansion_mode, rq_regs->prq_expansion_mode, rq_regs->mrq_expansion_mode, |
210 | rq_regs->crq_expansion_mode, rq_regs->plane1_base_address, rq_regs->rq_regs_l.chunk_size, | |
211 | rq_regs->rq_regs_l.min_chunk_size, rq_regs->rq_regs_l.meta_chunk_size, | |
212 | rq_regs->rq_regs_l.min_meta_chunk_size, rq_regs->rq_regs_l.dpte_group_size, | |
213 | rq_regs->rq_regs_l.mpte_group_size, rq_regs->rq_regs_l.swath_height, | |
b9c1c67a DL |
214 | rq_regs->rq_regs_l.pte_row_height_linear, rq_regs->rq_regs_c.chunk_size, rq_regs->rq_regs_c.min_chunk_size, |
215 | rq_regs->rq_regs_c.meta_chunk_size, rq_regs->rq_regs_c.min_meta_chunk_size, | |
216 | rq_regs->rq_regs_c.dpte_group_size, rq_regs->rq_regs_c.mpte_group_size, | |
217 | rq_regs->rq_regs_c.swath_height, rq_regs->rq_regs_c.pte_row_height_linear); | |
34cb6b38 DL |
218 | } |
219 | ||
220 | DTN_INFO("========DLG========\n"); | |
221 | DTN_INFO("HUBP: rc_hbe dlg_vbe min_d_y_n rc_per_ht rc_x_a_s " | |
222 | " dst_y_a_s dst_y_pf dst_y_vvb dst_y_rvb dst_y_vfl dst_y_rfl rf_pix_fq" | |
223 | " vratio_pf vrat_pf_c rc_pg_vbl rc_pg_vbc rc_mc_vbl rc_mc_vbc rc_pg_fll" | |
224 | " rc_pg_flc rc_mc_fll rc_mc_flc pr_nom_l pr_nom_c rc_pg_nl rc_pg_nc " | |
225 | " mr_nom_l mr_nom_c rc_mc_nl rc_mc_nc rc_ld_pl rc_ld_pc rc_ld_l " | |
226 | " rc_ld_c cha_cur0 ofst_cur1 cha_cur1 vr_af_vc0 ddrq_limt x_rt_dlay" | |
227 | " x_rp_dlay x_rr_sfl\n"); | |
228 | for (i = 0; i < pool->pipe_count; i++) { | |
229 | struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); | |
230 | struct _vcs_dpi_display_dlg_regs_st *dlg_regs = &s->dlg_attr; | |
231 | ||
a3cb1c1c NC |
232 | if (!s->blank_en) |
233 | DTN_INFO("[%2d]: %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh" | |
234 | "% 8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh" | |
235 | " %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh\n", | |
236 | pool->hubps[i]->inst, dlg_regs->refcyc_h_blank_end, dlg_regs->dlg_vblank_end, dlg_regs->min_dst_y_next_start, | |
237 | dlg_regs->refcyc_per_htotal, dlg_regs->refcyc_x_after_scaler, dlg_regs->dst_y_after_scaler, | |
238 | dlg_regs->dst_y_prefetch, dlg_regs->dst_y_per_vm_vblank, dlg_regs->dst_y_per_row_vblank, | |
239 | dlg_regs->dst_y_per_vm_flip, dlg_regs->dst_y_per_row_flip, dlg_regs->ref_freq_to_pix_freq, | |
240 | dlg_regs->vratio_prefetch, dlg_regs->vratio_prefetch_c, dlg_regs->refcyc_per_pte_group_vblank_l, | |
241 | dlg_regs->refcyc_per_pte_group_vblank_c, dlg_regs->refcyc_per_meta_chunk_vblank_l, | |
242 | dlg_regs->refcyc_per_meta_chunk_vblank_c, dlg_regs->refcyc_per_pte_group_flip_l, | |
243 | dlg_regs->refcyc_per_pte_group_flip_c, dlg_regs->refcyc_per_meta_chunk_flip_l, | |
244 | dlg_regs->refcyc_per_meta_chunk_flip_c, dlg_regs->dst_y_per_pte_row_nom_l, | |
245 | dlg_regs->dst_y_per_pte_row_nom_c, dlg_regs->refcyc_per_pte_group_nom_l, | |
246 | dlg_regs->refcyc_per_pte_group_nom_c, dlg_regs->dst_y_per_meta_row_nom_l, | |
247 | dlg_regs->dst_y_per_meta_row_nom_c, dlg_regs->refcyc_per_meta_chunk_nom_l, | |
248 | dlg_regs->refcyc_per_meta_chunk_nom_c, dlg_regs->refcyc_per_line_delivery_pre_l, | |
249 | dlg_regs->refcyc_per_line_delivery_pre_c, dlg_regs->refcyc_per_line_delivery_l, | |
250 | dlg_regs->refcyc_per_line_delivery_c, dlg_regs->chunk_hdl_adjust_cur0, dlg_regs->dst_y_offset_cur1, | |
251 | dlg_regs->chunk_hdl_adjust_cur1, dlg_regs->vready_after_vcount0, dlg_regs->dst_y_delta_drq_limit, | |
252 | dlg_regs->xfc_reg_transfer_delay, dlg_regs->xfc_reg_precharge_delay, | |
253 | dlg_regs->xfc_reg_remote_surface_flip_latency); | |
34cb6b38 DL |
254 | } |
255 | ||
256 | DTN_INFO("========TTU========\n"); | |
257 | DTN_INFO("HUBP: qos_ll_wm qos_lh_wm mn_ttu_vb qos_l_flp rc_rd_p_l rc_rd_l rc_rd_p_c" | |
258 | " rc_rd_c rc_rd_c0 rc_rd_pc0 rc_rd_c1 rc_rd_pc1 qos_lf_l qos_rds_l" | |
259 | " qos_lf_c qos_rds_c qos_lf_c0 qos_rds_c0 qos_lf_c1 qos_rds_c1\n"); | |
260 | for (i = 0; i < pool->pipe_count; i++) { | |
261 | struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state); | |
262 | struct _vcs_dpi_display_ttu_regs_st *ttu_regs = &s->ttu_attr; | |
263 | ||
a3cb1c1c NC |
264 | if (!s->blank_en) |
265 | DTN_INFO("[%2d]: %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh %8xh\n", | |
266 | pool->hubps[i]->inst, ttu_regs->qos_level_low_wm, ttu_regs->qos_level_high_wm, ttu_regs->min_ttu_vblank, | |
267 | ttu_regs->qos_level_flip, ttu_regs->refcyc_per_req_delivery_pre_l, ttu_regs->refcyc_per_req_delivery_l, | |
268 | ttu_regs->refcyc_per_req_delivery_pre_c, ttu_regs->refcyc_per_req_delivery_c, ttu_regs->refcyc_per_req_delivery_cur0, | |
269 | ttu_regs->refcyc_per_req_delivery_pre_cur0, ttu_regs->refcyc_per_req_delivery_cur1, | |
270 | ttu_regs->refcyc_per_req_delivery_pre_cur1, ttu_regs->qos_level_fixed_l, ttu_regs->qos_ramp_disable_l, | |
271 | ttu_regs->qos_level_fixed_c, ttu_regs->qos_ramp_disable_c, ttu_regs->qos_level_fixed_cur0, | |
272 | ttu_regs->qos_ramp_disable_cur0, ttu_regs->qos_level_fixed_cur1, ttu_regs->qos_ramp_disable_cur1); | |
34cb6b38 | 273 | } |
71395011 | 274 | DTN_INFO("\n"); |
34cb6b38 DL |
275 | } |
276 | ||
46659a83 NK |
277 | void dcn10_log_hw_state(struct dc *dc, |
278 | struct dc_log_buffer_ctx *log_ctx) | |
34cb6b38 DL |
279 | { |
280 | struct dc_context *dc_ctx = dc->ctx; | |
281 | struct resource_pool *pool = dc->res_pool; | |
282 | int i; | |
283 | ||
284 | DTN_INFO_BEGIN(); | |
285 | ||
46659a83 | 286 | dcn10_log_hubbub_state(dc, log_ctx); |
34cb6b38 | 287 | |
46659a83 | 288 | dcn10_log_hubp_states(dc, log_ctx); |
a052a516 | 289 | |
7c91bd43 AK |
290 | DTN_INFO("DPP: IGAM format IGAM mode DGAM mode RGAM mode" |
291 | " GAMUT mode C11 C12 C13 C14 C21 C22 C23 C24 " | |
292 | "C31 C32 C33 C34\n"); | |
293 | for (i = 0; i < pool->pipe_count; i++) { | |
294 | struct dpp *dpp = pool->dpps[i]; | |
d02e0794 | 295 | struct dcn_dpp_state s = {0}; |
7c91bd43 AK |
296 | |
297 | dpp->funcs->dpp_read_state(dpp, &s); | |
298 | ||
d02e0794 NC |
299 | if (!s.is_enabled) |
300 | continue; | |
301 | ||
7c91bd43 | 302 | DTN_INFO("[%2d]: %11xh %-11s %-11s %-11s" |
7b265fd9 | 303 | "%8x %08xh %08xh %08xh %08xh %08xh %08xh", |
7c91bd43 AK |
304 | dpp->inst, |
305 | s.igam_input_format, | |
306 | (s.igam_lut_mode == 0) ? "BypassFixed" : | |
307 | ((s.igam_lut_mode == 1) ? "BypassFloat" : | |
308 | ((s.igam_lut_mode == 2) ? "RAM" : | |
309 | ((s.igam_lut_mode == 3) ? "RAM" : | |
310 | "Unknown"))), | |
311 | (s.dgam_lut_mode == 0) ? "Bypass" : | |
312 | ((s.dgam_lut_mode == 1) ? "sRGB" : | |
313 | ((s.dgam_lut_mode == 2) ? "Ycc" : | |
314 | ((s.dgam_lut_mode == 3) ? "RAM" : | |
315 | ((s.dgam_lut_mode == 4) ? "RAM" : | |
316 | "Unknown")))), | |
317 | (s.rgam_lut_mode == 0) ? "Bypass" : | |
318 | ((s.rgam_lut_mode == 1) ? "sRGB" : | |
319 | ((s.rgam_lut_mode == 2) ? "Ycc" : | |
320 | ((s.rgam_lut_mode == 3) ? "RAM" : | |
321 | ((s.rgam_lut_mode == 4) ? "RAM" : | |
322 | "Unknown")))), | |
323 | s.gamut_remap_mode, | |
324 | s.gamut_remap_c11_c12, | |
325 | s.gamut_remap_c13_c14, | |
326 | s.gamut_remap_c21_c22, | |
327 | s.gamut_remap_c23_c24, | |
328 | s.gamut_remap_c31_c32, | |
329 | s.gamut_remap_c33_c34); | |
330 | DTN_INFO("\n"); | |
331 | } | |
332 | DTN_INFO("\n"); | |
333 | ||
a052a516 | 334 | DTN_INFO("MPCC: OPP DPP MPCCBOT MODE ALPHA_MODE PREMULT OVERLAP_ONLY IDLE\n"); |
dfd01f29 | 335 | for (i = 0; i < pool->pipe_count; i++) { |
dfd01f29 DL |
336 | struct mpcc_state s = {0}; |
337 | ||
a052a516 | 338 | pool->mpc->funcs->read_mpcc_state(pool->mpc, i, &s); |
1ba2faf2 DL |
339 | if (s.opp_id != 0xf) |
340 | DTN_INFO("[%2d]: %2xh %2xh %6xh %4d %10d %7d %12d %4d\n", | |
341 | i, s.opp_id, s.dpp_id, s.bot_mpcc_id, | |
342 | s.mode, s.alpha_mode, s.pre_multiplied_alpha, s.overlap_only, | |
343 | s.idle); | |
dfd01f29 DL |
344 | } |
345 | DTN_INFO("\n"); | |
71395011 | 346 | |
ae8cf977 | 347 | DTN_INFO("OTG: v_bs v_be v_ss v_se vpol vmax vmin vmax_sel vmin_sel h_bs h_be h_ss h_se hpol htot vtot underflow blank_en\n"); |
214435ff | 348 | |
3be1406a | 349 | for (i = 0; i < pool->timing_generator_count; i++) { |
214435ff | 350 | struct timing_generator *tg = pool->timing_generators[i]; |
6f54d0b1 | 351 | struct dcn_otg_state s = {0}; |
ae8cf977 | 352 | /* Read shared OTG state registers for all DCNx */ |
40e045a9 | 353 | optc1_read_otg_state(DCN10TG_FROM_TG(tg), &s); |
214435ff | 354 | |
ae8cf977 LHM |
355 | /* |
356 | * For DCN2 and greater, a register on the OPP is used to | |
357 | * determine if the CRTC is blanked instead of the OTG. So use | |
358 | * dpg_is_blanked() if exists, otherwise fallback on otg. | |
359 | * | |
360 | * TODO: Implement DCN-specific read_otg_state hooks. | |
361 | */ | |
362 | if (pool->opps[i]->funcs->dpg_is_blanked) | |
363 | s.blank_enabled = pool->opps[i]->funcs->dpg_is_blanked(pool->opps[i]); | |
364 | else | |
365 | s.blank_enabled = tg->funcs->is_blanked(tg); | |
ae8cf977 | 366 | |
6f54d0b1 LT |
367 | //only print if OTG master is enabled |
368 | if ((s.otg_enabled & 1) == 0) | |
369 | continue; | |
370 | ||
ae8cf977 | 371 | DTN_INFO("[%d]: %5d %5d %5d %5d %5d %5d %5d %9d %9d %5d %5d %5d %5d %5d %5d %5d %9d %8d\n", |
3be1406a | 372 | tg->inst, |
214435ff CM |
373 | s.v_blank_start, |
374 | s.v_blank_end, | |
375 | s.v_sync_a_start, | |
376 | s.v_sync_a_end, | |
377 | s.v_sync_a_pol, | |
378 | s.v_total_max, | |
379 | s.v_total_min, | |
01fe3e48 AK |
380 | s.v_total_max_sel, |
381 | s.v_total_min_sel, | |
214435ff CM |
382 | s.h_blank_start, |
383 | s.h_blank_end, | |
384 | s.h_sync_a_start, | |
385 | s.h_sync_a_end, | |
386 | s.h_sync_a_pol, | |
387 | s.h_total, | |
388 | s.v_total, | |
ae8cf977 LHM |
389 | s.underflow_occurred_status, |
390 | s.blank_enabled); | |
a944744b NC |
391 | |
392 | // Clear underflow for debug purposes | |
393 | // We want to keep underflow sticky bit on for the longevity tests outside of test environment. | |
394 | // This function is called only from Windows or Diags test environment, hence it's safe to clear | |
395 | // it from here without affecting the original intent. | |
396 | tg->funcs->clear_optc_underflow(tg); | |
214435ff CM |
397 | } |
398 | DTN_INFO("\n"); | |
399 | ||
91b2e45b EB |
400 | // dcn_dsc_state struct field bytes_per_pixel was renamed to bits_per_pixel |
401 | // TODO: Update golden log header to reflect this name change | |
97bda032 HW |
402 | DTN_INFO("DSC: CLOCK_EN SLICE_WIDTH Bytes_pp\n"); |
403 | for (i = 0; i < pool->res_cap->num_dsc; i++) { | |
404 | struct display_stream_compressor *dsc = pool->dscs[i]; | |
405 | struct dcn_dsc_state s = {0}; | |
406 | ||
407 | dsc->funcs->dsc_read_state(dsc, &s); | |
408 | DTN_INFO("[%d]: %-9d %-12d %-10d\n", | |
409 | dsc->inst, | |
410 | s.dsc_clock_en, | |
411 | s.dsc_slice_width, | |
91b2e45b | 412 | s.dsc_bits_per_pixel); |
97bda032 HW |
413 | DTN_INFO("\n"); |
414 | } | |
415 | DTN_INFO("\n"); | |
416 | ||
417 | DTN_INFO("S_ENC: DSC_MODE SEC_GSP7_LINE_NUM" | |
418 | " VBID6_LINE_REFERENCE VBID6_LINE_NUM SEC_GSP7_ENABLE SEC_STREAM_ENABLE\n"); | |
419 | for (i = 0; i < pool->stream_enc_count; i++) { | |
420 | struct stream_encoder *enc = pool->stream_enc[i]; | |
421 | struct enc_state s = {0}; | |
422 | ||
423 | if (enc->funcs->enc_read_state) { | |
424 | enc->funcs->enc_read_state(enc, &s); | |
425 | DTN_INFO("[%-3d]: %-9d %-18d %-21d %-15d %-16d %-17d\n", | |
426 | enc->id, | |
427 | s.dsc_mode, | |
bcba830c | 428 | s.sec_gsp_pps_line_num, |
97bda032 HW |
429 | s.vbid6_line_reference, |
430 | s.vbid6_line_num, | |
bcba830c | 431 | s.sec_gsp_pps_enable, |
97bda032 HW |
432 | s.sec_stream_enable); |
433 | DTN_INFO("\n"); | |
434 | } | |
435 | } | |
436 | DTN_INFO("\n"); | |
437 | ||
ae8cf977 | 438 | DTN_INFO("L_ENC: DPHY_FEC_EN DPHY_FEC_READY_SHADOW DPHY_FEC_ACTIVE_STATUS DP_LINK_TRAINING_COMPLETE\n"); |
97bda032 HW |
439 | for (i = 0; i < dc->link_count; i++) { |
440 | struct link_encoder *lenc = dc->links[i]->link_enc; | |
441 | ||
442 | struct link_enc_state s = {0}; | |
443 | ||
444 | if (lenc->funcs->read_state) { | |
445 | lenc->funcs->read_state(lenc, &s); | |
ae8cf977 | 446 | DTN_INFO("[%-3d]: %-12d %-22d %-22d %-25d\n", |
97bda032 HW |
447 | i, |
448 | s.dphy_fec_en, | |
449 | s.dphy_fec_ready_shadow, | |
ae8cf977 LHM |
450 | s.dphy_fec_active_status, |
451 | s.dp_link_training_complete); | |
97bda032 HW |
452 | DTN_INFO("\n"); |
453 | } | |
454 | } | |
455 | DTN_INFO("\n"); | |
7ed4e635 | 456 | |
a4765463 DL |
457 | DTN_INFO("\nCALCULATED Clocks: dcfclk_khz:%d dcfclk_deep_sleep_khz:%d dispclk_khz:%d\n" |
458 | "dppclk_khz:%d max_supported_dppclk_khz:%d fclk_khz:%d socclk_khz:%d\n\n", | |
813d20dc AW |
459 | dc->current_state->bw_ctx.bw.dcn.clk.dcfclk_khz, |
460 | dc->current_state->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz, | |
461 | dc->current_state->bw_ctx.bw.dcn.clk.dispclk_khz, | |
462 | dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz, | |
463 | dc->current_state->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz, | |
464 | dc->current_state->bw_ctx.bw.dcn.clk.fclk_khz, | |
465 | dc->current_state->bw_ctx.bw.dcn.clk.socclk_khz); | |
0a93dc7f | 466 | |
46659a83 | 467 | log_mpc_crc(dc, log_ctx); |
71395011 TC |
468 | |
469 | DTN_INFO_END(); | |
470 | } | |
2b13d7d3 | 471 | |
1a7d296d TL |
472 | bool dcn10_did_underflow_occur(struct dc *dc, struct pipe_ctx *pipe_ctx) |
473 | { | |
474 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
475 | struct timing_generator *tg = pipe_ctx->stream_res.tg; | |
476 | ||
477 | if (tg->funcs->is_optc_underflow_occurred(tg)) { | |
478 | tg->funcs->clear_optc_underflow(tg); | |
479 | return true; | |
480 | } | |
481 | ||
482 | if (hubp->funcs->hubp_get_underflow_status(hubp)) { | |
483 | hubp->funcs->hubp_clear_underflow(hubp); | |
484 | return true; | |
485 | } | |
486 | return false; | |
487 | } | |
488 | ||
78c77382 | 489 | void dcn10_enable_power_gating_plane( |
184debdb | 490 | struct dce_hwseq *hws, |
70ccab60 HW |
491 | bool enable) |
492 | { | |
0ee600a7 | 493 | bool force_on = true; /* disable power gating */ |
70ccab60 HW |
494 | |
495 | if (enable) | |
0ee600a7 | 496 | force_on = false; |
70ccab60 HW |
497 | |
498 | /* DCHUBP0/1/2/3 */ | |
184debdb DL |
499 | REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, force_on); |
500 | REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, force_on); | |
501 | REG_UPDATE(DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, force_on); | |
502 | REG_UPDATE(DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, force_on); | |
70ccab60 HW |
503 | |
504 | /* DPP0/1/2/3 */ | |
184debdb DL |
505 | REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, force_on); |
506 | REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, force_on); | |
507 | REG_UPDATE(DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, force_on); | |
508 | REG_UPDATE(DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, force_on); | |
70ccab60 HW |
509 | } |
510 | ||
78c77382 | 511 | void dcn10_disable_vga( |
0a87425a TC |
512 | struct dce_hwseq *hws) |
513 | { | |
5e9a4f08 CZ |
514 | unsigned int in_vga1_mode = 0; |
515 | unsigned int in_vga2_mode = 0; | |
516 | unsigned int in_vga3_mode = 0; | |
517 | unsigned int in_vga4_mode = 0; | |
518 | ||
519 | REG_GET(D1VGA_CONTROL, D1VGA_MODE_ENABLE, &in_vga1_mode); | |
520 | REG_GET(D2VGA_CONTROL, D2VGA_MODE_ENABLE, &in_vga2_mode); | |
521 | REG_GET(D3VGA_CONTROL, D3VGA_MODE_ENABLE, &in_vga3_mode); | |
522 | REG_GET(D4VGA_CONTROL, D4VGA_MODE_ENABLE, &in_vga4_mode); | |
523 | ||
524 | if (in_vga1_mode == 0 && in_vga2_mode == 0 && | |
525 | in_vga3_mode == 0 && in_vga4_mode == 0) | |
e18d3086 EY |
526 | return; |
527 | ||
0a87425a | 528 | REG_WRITE(D1VGA_CONTROL, 0); |
5e9a4f08 CZ |
529 | REG_WRITE(D2VGA_CONTROL, 0); |
530 | REG_WRITE(D3VGA_CONTROL, 0); | |
531 | REG_WRITE(D4VGA_CONTROL, 0); | |
90e3103e BL |
532 | |
533 | /* HW Engineer's Notes: | |
534 | * During switch from vga->extended, if we set the VGA_TEST_ENABLE and | |
535 | * then hit the VGA_TEST_RENDER_START, then the DCHUBP timing gets updated correctly. | |
536 | * | |
537 | * Then vBIOS will have it poll for the VGA_TEST_RENDER_DONE and unset | |
538 | * VGA_TEST_ENABLE, to leave it in the same state as before. | |
539 | */ | |
540 | REG_UPDATE(VGA_TEST_CONTROL, VGA_TEST_ENABLE, 1); | |
541 | REG_UPDATE(VGA_TEST_CONTROL, VGA_TEST_RENDER_START, 1); | |
0a87425a TC |
542 | } |
543 | ||
23344703 RS |
544 | /** |
545 | * dcn10_dpp_pg_control - DPP power gate control. | |
546 | * | |
547 | * @hws: dce_hwseq reference. | |
548 | * @dpp_inst: DPP instance reference. | |
549 | * @power_on: true if we want to enable power gate, false otherwise. | |
550 | * | |
551 | * Enable or disable power gate in the specific DPP instance. | |
552 | */ | |
78c77382 | 553 | void dcn10_dpp_pg_control( |
184debdb | 554 | struct dce_hwseq *hws, |
70ccab60 HW |
555 | unsigned int dpp_inst, |
556 | bool power_on) | |
557 | { | |
70ccab60 | 558 | uint32_t power_gate = power_on ? 0 : 1; |
23344703 | 559 | uint32_t pwr_status = power_on ? PGFSM_POWER_ON : PGFSM_POWER_OFF; |
70ccab60 | 560 | |
184debdb | 561 | if (hws->ctx->dc->debug.disable_dpp_power_gate) |
70ccab60 | 562 | return; |
7f93c1de CL |
563 | if (REG(DOMAIN1_PG_CONFIG) == 0) |
564 | return; | |
70ccab60 HW |
565 | |
566 | switch (dpp_inst) { | |
567 | case 0: /* DPP0 */ | |
184debdb | 568 | REG_UPDATE(DOMAIN1_PG_CONFIG, |
70ccab60 HW |
569 | DOMAIN1_POWER_GATE, power_gate); |
570 | ||
184debdb | 571 | REG_WAIT(DOMAIN1_PG_STATUS, |
8a5d8245 TC |
572 | DOMAIN1_PGFSM_PWR_STATUS, pwr_status, |
573 | 1, 1000); | |
70ccab60 HW |
574 | break; |
575 | case 1: /* DPP1 */ | |
184debdb | 576 | REG_UPDATE(DOMAIN3_PG_CONFIG, |
70ccab60 HW |
577 | DOMAIN3_POWER_GATE, power_gate); |
578 | ||
184debdb | 579 | REG_WAIT(DOMAIN3_PG_STATUS, |
8a5d8245 TC |
580 | DOMAIN3_PGFSM_PWR_STATUS, pwr_status, |
581 | 1, 1000); | |
70ccab60 HW |
582 | break; |
583 | case 2: /* DPP2 */ | |
184debdb | 584 | REG_UPDATE(DOMAIN5_PG_CONFIG, |
70ccab60 HW |
585 | DOMAIN5_POWER_GATE, power_gate); |
586 | ||
184debdb | 587 | REG_WAIT(DOMAIN5_PG_STATUS, |
8a5d8245 TC |
588 | DOMAIN5_PGFSM_PWR_STATUS, pwr_status, |
589 | 1, 1000); | |
70ccab60 HW |
590 | break; |
591 | case 3: /* DPP3 */ | |
184debdb | 592 | REG_UPDATE(DOMAIN7_PG_CONFIG, |
70ccab60 HW |
593 | DOMAIN7_POWER_GATE, power_gate); |
594 | ||
184debdb | 595 | REG_WAIT(DOMAIN7_PG_STATUS, |
8a5d8245 TC |
596 | DOMAIN7_PGFSM_PWR_STATUS, pwr_status, |
597 | 1, 1000); | |
70ccab60 HW |
598 | break; |
599 | default: | |
600 | BREAK_TO_DEBUGGER(); | |
601 | break; | |
602 | } | |
603 | } | |
604 | ||
23344703 RS |
605 | /** |
606 | * dcn10_hubp_pg_control - HUBP power gate control. | |
607 | * | |
608 | * @hws: dce_hwseq reference. | |
609 | * @hubp_inst: DPP instance reference. | |
610 | * @power_on: true if we want to enable power gate, false otherwise. | |
611 | * | |
612 | * Enable or disable power gate in the specific HUBP instance. | |
613 | */ | |
78c77382 | 614 | void dcn10_hubp_pg_control( |
184debdb | 615 | struct dce_hwseq *hws, |
70ccab60 HW |
616 | unsigned int hubp_inst, |
617 | bool power_on) | |
618 | { | |
70ccab60 | 619 | uint32_t power_gate = power_on ? 0 : 1; |
23344703 | 620 | uint32_t pwr_status = power_on ? PGFSM_POWER_ON : PGFSM_POWER_OFF; |
70ccab60 | 621 | |
184debdb | 622 | if (hws->ctx->dc->debug.disable_hubp_power_gate) |
70ccab60 | 623 | return; |
7f93c1de CL |
624 | if (REG(DOMAIN0_PG_CONFIG) == 0) |
625 | return; | |
70ccab60 HW |
626 | |
627 | switch (hubp_inst) { | |
628 | case 0: /* DCHUBP0 */ | |
184debdb | 629 | REG_UPDATE(DOMAIN0_PG_CONFIG, |
70ccab60 HW |
630 | DOMAIN0_POWER_GATE, power_gate); |
631 | ||
184debdb | 632 | REG_WAIT(DOMAIN0_PG_STATUS, |
8a5d8245 TC |
633 | DOMAIN0_PGFSM_PWR_STATUS, pwr_status, |
634 | 1, 1000); | |
70ccab60 HW |
635 | break; |
636 | case 1: /* DCHUBP1 */ | |
184debdb | 637 | REG_UPDATE(DOMAIN2_PG_CONFIG, |
70ccab60 HW |
638 | DOMAIN2_POWER_GATE, power_gate); |
639 | ||
184debdb | 640 | REG_WAIT(DOMAIN2_PG_STATUS, |
8a5d8245 TC |
641 | DOMAIN2_PGFSM_PWR_STATUS, pwr_status, |
642 | 1, 1000); | |
70ccab60 HW |
643 | break; |
644 | case 2: /* DCHUBP2 */ | |
184debdb | 645 | REG_UPDATE(DOMAIN4_PG_CONFIG, |
70ccab60 HW |
646 | DOMAIN4_POWER_GATE, power_gate); |
647 | ||
184debdb | 648 | REG_WAIT(DOMAIN4_PG_STATUS, |
8a5d8245 TC |
649 | DOMAIN4_PGFSM_PWR_STATUS, pwr_status, |
650 | 1, 1000); | |
70ccab60 HW |
651 | break; |
652 | case 3: /* DCHUBP3 */ | |
184debdb | 653 | REG_UPDATE(DOMAIN6_PG_CONFIG, |
70ccab60 HW |
654 | DOMAIN6_POWER_GATE, power_gate); |
655 | ||
184debdb | 656 | REG_WAIT(DOMAIN6_PG_STATUS, |
8a5d8245 TC |
657 | DOMAIN6_PGFSM_PWR_STATUS, pwr_status, |
658 | 1, 1000); | |
70ccab60 HW |
659 | break; |
660 | default: | |
661 | BREAK_TO_DEBUGGER(); | |
662 | break; | |
663 | } | |
664 | } | |
665 | ||
666 | static void power_on_plane( | |
184debdb | 667 | struct dce_hwseq *hws, |
cfe4645e | 668 | int plane_id) |
70ccab60 | 669 | { |
5d4b05dd | 670 | DC_LOGGER_INIT(hws->ctx->logger); |
5df921d4 KC |
671 | if (REG(DC_IP_REQUEST_CNTL)) { |
672 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
673 | IP_REQUEST_EN, 1); | |
d2138be3 NK |
674 | |
675 | if (hws->funcs.dpp_pg_control) | |
676 | hws->funcs.dpp_pg_control(hws, plane_id, true); | |
677 | ||
678 | if (hws->funcs.hubp_pg_control) | |
679 | hws->funcs.hubp_pg_control(hws, plane_id, true); | |
680 | ||
5df921d4 KC |
681 | REG_SET(DC_IP_REQUEST_CNTL, 0, |
682 | IP_REQUEST_EN, 0); | |
1296423b | 683 | DC_LOG_DEBUG( |
5df921d4 KC |
684 | "Un-gated front end for pipe %d\n", plane_id); |
685 | } | |
70ccab60 HW |
686 | } |
687 | ||
41f97c07 HW |
688 | static void undo_DEGVIDCN10_253_wa(struct dc *dc) |
689 | { | |
690 | struct dce_hwseq *hws = dc->hwseq; | |
8feabd03 | 691 | struct hubp *hubp = dc->res_pool->hubps[0]; |
c9bb686b | 692 | |
7f914a62 | 693 | if (!hws->wa_state.DEGVIDCN10_253_applied) |
c9bb686b | 694 | return; |
41f97c07 | 695 | |
8feabd03 | 696 | hubp->funcs->set_blank(hubp, true); |
41f97c07 HW |
697 | |
698 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
699 | IP_REQUEST_EN, 1); | |
700 | ||
f42ea55b | 701 | hws->funcs.hubp_pg_control(hws, 0, false); |
41f97c07 HW |
702 | REG_SET(DC_IP_REQUEST_CNTL, 0, |
703 | IP_REQUEST_EN, 0); | |
7f914a62 YS |
704 | |
705 | hws->wa_state.DEGVIDCN10_253_applied = false; | |
41f97c07 HW |
706 | } |
707 | ||
41f97c07 HW |
708 | static void apply_DEGVIDCN10_253_wa(struct dc *dc) |
709 | { | |
710 | struct dce_hwseq *hws = dc->hwseq; | |
8feabd03 | 711 | struct hubp *hubp = dc->res_pool->hubps[0]; |
7f914a62 | 712 | int i; |
41f97c07 | 713 | |
6bf52028 HW |
714 | if (dc->debug.disable_stutter) |
715 | return; | |
716 | ||
7f914a62 YS |
717 | if (!hws->wa.DEGVIDCN10_253) |
718 | return; | |
719 | ||
720 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
721 | if (!dc->res_pool->hubps[i]->power_gated) | |
722 | return; | |
723 | } | |
724 | ||
725 | /* all pipe power gated, apply work around to enable stutter. */ | |
726 | ||
41f97c07 HW |
727 | REG_SET(DC_IP_REQUEST_CNTL, 0, |
728 | IP_REQUEST_EN, 1); | |
729 | ||
f42ea55b | 730 | hws->funcs.hubp_pg_control(hws, 0, true); |
41f97c07 HW |
731 | REG_SET(DC_IP_REQUEST_CNTL, 0, |
732 | IP_REQUEST_EN, 0); | |
733 | ||
8feabd03 | 734 | hubp->funcs->set_hubp_blank_en(hubp, false); |
7f914a62 | 735 | hws->wa_state.DEGVIDCN10_253_applied = true; |
41f97c07 HW |
736 | } |
737 | ||
78c77382 | 738 | void dcn10_bios_golden_init(struct dc *dc) |
70ccab60 | 739 | { |
f42ea55b | 740 | struct dce_hwseq *hws = dc->hwseq; |
70ccab60 HW |
741 | struct dc_bios *bp = dc->ctx->dc_bios; |
742 | int i; | |
8a31820b ML |
743 | bool allow_self_fresh_force_enable = true; |
744 | ||
f42ea55b | 745 | if (hws->funcs.s0i3_golden_init_wa && hws->funcs.s0i3_golden_init_wa(dc)) |
c0fb59a4 | 746 | return; |
f42ea55b | 747 | |
8a31820b ML |
748 | if (dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled) |
749 | allow_self_fresh_force_enable = | |
750 | dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled(dc->res_pool->hubbub); | |
751 | ||
752 | ||
753 | /* WA for making DF sleep when idle after resume from S0i3. | |
754 | * DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE is set to 1 by | |
755 | * command table, if DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE = 0 | |
756 | * before calling command table and it changed to 1 after, | |
757 | * it should be set back to 0. | |
758 | */ | |
70ccab60 HW |
759 | |
760 | /* initialize dcn global */ | |
761 | bp->funcs->enable_disp_power_gating(bp, | |
762 | CONTROLLER_ID_D0, ASIC_PIPE_INIT); | |
763 | ||
764 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
765 | /* initialize dcn per pipe */ | |
766 | bp->funcs->enable_disp_power_gating(bp, | |
767 | CONTROLLER_ID_D0 + i, ASIC_PIPE_DISABLE); | |
768 | } | |
8a31820b ML |
769 | |
770 | if (dc->res_pool->hubbub->funcs->allow_self_refresh_control) | |
771 | if (allow_self_fresh_force_enable == false && | |
772 | dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled(dc->res_pool->hubbub)) | |
087a1ff8 HW |
773 | dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, |
774 | !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); | |
8a31820b | 775 | |
70ccab60 HW |
776 | } |
777 | ||
5cc2687c YS |
778 | static void false_optc_underflow_wa( |
779 | struct dc *dc, | |
780 | const struct dc_stream_state *stream, | |
781 | struct timing_generator *tg) | |
782 | { | |
783 | int i; | |
784 | bool underflow; | |
785 | ||
786 | if (!dc->hwseq->wa.false_optc_underflow) | |
787 | return; | |
788 | ||
789 | underflow = tg->funcs->is_optc_underflow_occurred(tg); | |
790 | ||
791 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
792 | struct pipe_ctx *old_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; | |
793 | ||
794 | if (old_pipe_ctx->stream != stream) | |
795 | continue; | |
796 | ||
797 | dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, old_pipe_ctx); | |
798 | } | |
799 | ||
90830e84 VP |
800 | if (tg->funcs->set_blank_data_double_buffer) |
801 | tg->funcs->set_blank_data_double_buffer(tg, true); | |
5cc2687c YS |
802 | |
803 | if (tg->funcs->is_optc_underflow_occurred(tg) && !underflow) | |
804 | tg->funcs->clear_optc_underflow(tg); | |
805 | } | |
806 | ||
78c77382 | 807 | enum dc_status dcn10_enable_stream_timing( |
70ccab60 | 808 | struct pipe_ctx *pipe_ctx, |
608ac7bb | 809 | struct dc_state *context, |
fb3466a4 | 810 | struct dc *dc) |
70ccab60 | 811 | { |
0971c40e | 812 | struct dc_stream_state *stream = pipe_ctx->stream; |
70ccab60 HW |
813 | enum dc_color_space color_space; |
814 | struct tg_color black_color = {0}; | |
70ccab60 | 815 | |
70ccab60 HW |
816 | /* by upper caller loop, pipe0 is parent pipe and be called first. |
817 | * back end is set up by for pipe0. Other children pipe share back end | |
818 | * with pipe 0. No program is needed. | |
819 | */ | |
820 | if (pipe_ctx->top_pipe != NULL) | |
821 | return DC_OK; | |
822 | ||
823 | /* TODO check if timing_changed, disable stream if timing changed */ | |
824 | ||
825 | /* HW program guide assume display already disable | |
826 | * by unplug sequence. OTG assume stop. | |
827 | */ | |
6b670fa9 | 828 | pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, true); |
70ccab60 HW |
829 | |
830 | if (false == pipe_ctx->clock_source->funcs->program_pix_clk( | |
831 | pipe_ctx->clock_source, | |
10688217 | 832 | &pipe_ctx->stream_res.pix_clk_params, |
70ccab60 HW |
833 | &pipe_ctx->pll_settings)) { |
834 | BREAK_TO_DEBUGGER(); | |
835 | return DC_ERROR_UNEXPECTED; | |
836 | } | |
70ccab60 | 837 | |
6b670fa9 HW |
838 | pipe_ctx->stream_res.tg->funcs->program_timing( |
839 | pipe_ctx->stream_res.tg, | |
4fa086b9 | 840 | &stream->timing, |
e7e10c46 DL |
841 | pipe_ctx->pipe_dlg_param.vready_offset, |
842 | pipe_ctx->pipe_dlg_param.vstartup_start, | |
843 | pipe_ctx->pipe_dlg_param.vupdate_offset, | |
844 | pipe_ctx->pipe_dlg_param.vupdate_width, | |
845 | pipe_ctx->stream->signal, | |
70ccab60 HW |
846 | true); |
847 | ||
70ccab60 HW |
848 | #if 0 /* move to after enable_crtc */ |
849 | /* TODO: OPP FMT, ABM. etc. should be done here. */ | |
850 | /* or FPGA now. instance 0 only. TODO: move to opp.c */ | |
851 | ||
6b670fa9 | 852 | inst_offset = reg_offsets[pipe_ctx->stream_res.tg->inst].fmt; |
70ccab60 | 853 | |
a6a6cb34 HW |
854 | pipe_ctx->stream_res.opp->funcs->opp_program_fmt( |
855 | pipe_ctx->stream_res.opp, | |
70ccab60 HW |
856 | &stream->bit_depth_params, |
857 | &stream->clamping); | |
858 | #endif | |
859 | /* program otg blank color */ | |
4fa086b9 | 860 | color_space = stream->output_color_space; |
70ccab60 | 861 | color_space_to_black_color(dc, color_space, &black_color); |
70ccab60 | 862 | |
e0a3794d | 863 | /* |
864 | * The way 420 is packed, 2 channels carry Y component, 1 channel | |
865 | * alternate between Cb and Cr, so both channels need the pixel | |
866 | * value for Y | |
867 | */ | |
868 | if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
869 | black_color.color_r_cr = black_color.color_g_y; | |
870 | ||
b51adc77 YHL |
871 | if (pipe_ctx->stream_res.tg->funcs->set_blank_color) |
872 | pipe_ctx->stream_res.tg->funcs->set_blank_color( | |
873 | pipe_ctx->stream_res.tg, | |
874 | &black_color); | |
875 | ||
876 | if (pipe_ctx->stream_res.tg->funcs->is_blanked && | |
877 | !pipe_ctx->stream_res.tg->funcs->is_blanked(pipe_ctx->stream_res.tg)) { | |
5cc2687c YS |
878 | pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true); |
879 | hwss_wait_for_blank_complete(pipe_ctx->stream_res.tg); | |
880 | false_optc_underflow_wa(dc, pipe_ctx->stream, pipe_ctx->stream_res.tg); | |
881 | } | |
70ccab60 HW |
882 | |
883 | /* VTG is within DCHUB command block. DCFCLK is always on */ | |
6b670fa9 | 884 | if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) { |
70ccab60 HW |
885 | BREAK_TO_DEBUGGER(); |
886 | return DC_ERROR_UNEXPECTED; | |
887 | } | |
888 | ||
889 | /* TODO program crtc source select for non-virtual signal*/ | |
890 | /* TODO program FMT */ | |
891 | /* TODO setup link_enc */ | |
892 | /* TODO set stream attributes */ | |
893 | /* TODO program audio */ | |
894 | /* TODO enable stream if timing changed */ | |
895 | /* TODO unblank stream if DP */ | |
896 | ||
897 | return DC_OK; | |
898 | } | |
899 | ||
4a797d24 | 900 | static void dcn10_reset_back_end_for_pipe( |
fb3466a4 | 901 | struct dc *dc, |
70ccab60 | 902 | struct pipe_ctx *pipe_ctx, |
608ac7bb | 903 | struct dc_state *context) |
70ccab60 HW |
904 | { |
905 | int i; | |
efca0905 | 906 | struct dc_link *link; |
5d4b05dd | 907 | DC_LOGGER_INIT(dc->ctx->logger); |
8e9c4c8c | 908 | if (pipe_ctx->stream_res.stream_enc == NULL) { |
70ccab60 HW |
909 | pipe_ctx->stream = NULL; |
910 | return; | |
911 | } | |
912 | ||
d050f8ed | 913 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { |
efca0905 PH |
914 | link = pipe_ctx->stream->link; |
915 | /* DPMS may already disable or */ | |
916 | /* dpms_off status is incorrect due to fastboot | |
917 | * feature. When system resume from S4 with second | |
918 | * screen only, the dpms_off would be true but | |
919 | * VBIOS lit up eDP, so check link status too. | |
920 | */ | |
921 | if (!pipe_ctx->stream->dpms_off || link->link_status.link_active) | |
57430404 SSC |
922 | core_link_disable_stream(pipe_ctx); |
923 | else if (pipe_ctx->stream_res.audio) | |
924 | dc->hwss.disable_audio_stream(pipe_ctx); | |
925 | ||
926 | if (pipe_ctx->stream_res.audio) { | |
927 | /*disable az_endpoint*/ | |
928 | pipe_ctx->stream_res.audio->funcs->az_disable(pipe_ctx->stream_res.audio); | |
929 | ||
930 | /*free audio*/ | |
931 | if (dc->caps.dynamic_audio == true) { | |
932 | /*we have to dynamic arbitrate the audio endpoints*/ | |
933 | /*we free the resource, need reset is_audio_acquired*/ | |
934 | update_audio_usage(&dc->current_state->res_ctx, dc->res_pool, | |
935 | pipe_ctx->stream_res.audio, false); | |
936 | pipe_ctx->stream_res.audio = NULL; | |
937 | } | |
ea7ea2a8 | 938 | } |
d050f8ed | 939 | } |
70ccab60 HW |
940 | |
941 | /* by upper caller loop, parent pipe: pipe0, will be reset last. | |
942 | * back end share by all pipes and will be disable only when disable | |
943 | * parent pipe. | |
944 | */ | |
945 | if (pipe_ctx->top_pipe == NULL) { | |
9edf202d JZ |
946 | |
947 | if (pipe_ctx->stream_res.abm) | |
3ba01817 | 948 | dc->hwss.set_abm_immediate_disable(pipe_ctx); |
9edf202d | 949 | |
6b670fa9 | 950 | pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); |
70ccab60 | 951 | |
6b670fa9 | 952 | pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); |
38df0701 WL |
953 | if (pipe_ctx->stream_res.tg->funcs->set_drr) |
954 | pipe_ctx->stream_res.tg->funcs->set_drr( | |
955 | pipe_ctx->stream_res.tg, NULL); | |
70ccab60 HW |
956 | } |
957 | ||
70ccab60 | 958 | for (i = 0; i < dc->res_pool->pipe_count; i++) |
608ac7bb | 959 | if (&dc->current_state->res_ctx.pipe_ctx[i] == pipe_ctx) |
70ccab60 HW |
960 | break; |
961 | ||
962 | if (i == dc->res_pool->pipe_count) | |
963 | return; | |
964 | ||
965 | pipe_ctx->stream = NULL; | |
1296423b | 966 | DC_LOG_DEBUG("Reset back end for pipe %d, tg:%d\n", |
6b670fa9 | 967 | pipe_ctx->pipe_idx, pipe_ctx->stream_res.tg->inst); |
70ccab60 HW |
968 | } |
969 | ||
3ba43a59 CL |
970 | static bool dcn10_hw_wa_force_recovery(struct dc *dc) |
971 | { | |
972 | struct hubp *hubp ; | |
973 | unsigned int i; | |
974 | bool need_recover = true; | |
975 | ||
976 | if (!dc->debug.recovery_enabled) | |
977 | return false; | |
978 | ||
979 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
980 | struct pipe_ctx *pipe_ctx = | |
981 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
982 | if (pipe_ctx != NULL) { | |
983 | hubp = pipe_ctx->plane_res.hubp; | |
95022795 | 984 | if (hubp != NULL && hubp->funcs->hubp_get_underflow_status) { |
3ba43a59 CL |
985 | if (hubp->funcs->hubp_get_underflow_status(hubp) != 0) { |
986 | /* one pipe underflow, we will reset all the pipes*/ | |
987 | need_recover = true; | |
988 | } | |
989 | } | |
990 | } | |
991 | } | |
992 | if (!need_recover) | |
993 | return false; | |
994 | /* | |
995 | DCHUBP_CNTL:HUBP_BLANK_EN=1 | |
996 | DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=1 | |
997 | DCHUBP_CNTL:HUBP_DISABLE=1 | |
998 | DCHUBP_CNTL:HUBP_DISABLE=0 | |
999 | DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=0 | |
1000 | DCSURF_PRIMARY_SURFACE_ADDRESS | |
1001 | DCHUBP_CNTL:HUBP_BLANK_EN=0 | |
1002 | */ | |
1003 | ||
1004 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1005 | struct pipe_ctx *pipe_ctx = | |
1006 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
1007 | if (pipe_ctx != NULL) { | |
1008 | hubp = pipe_ctx->plane_res.hubp; | |
1009 | /*DCHUBP_CNTL:HUBP_BLANK_EN=1*/ | |
95022795 | 1010 | if (hubp != NULL && hubp->funcs->set_hubp_blank_en) |
3ba43a59 CL |
1011 | hubp->funcs->set_hubp_blank_en(hubp, true); |
1012 | } | |
1013 | } | |
1014 | /*DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=1*/ | |
1015 | hubbub1_soft_reset(dc->res_pool->hubbub, true); | |
1016 | ||
1017 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1018 | struct pipe_ctx *pipe_ctx = | |
1019 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
1020 | if (pipe_ctx != NULL) { | |
1021 | hubp = pipe_ctx->plane_res.hubp; | |
1022 | /*DCHUBP_CNTL:HUBP_DISABLE=1*/ | |
95022795 | 1023 | if (hubp != NULL && hubp->funcs->hubp_disable_control) |
3ba43a59 CL |
1024 | hubp->funcs->hubp_disable_control(hubp, true); |
1025 | } | |
1026 | } | |
1027 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1028 | struct pipe_ctx *pipe_ctx = | |
1029 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
1030 | if (pipe_ctx != NULL) { | |
1031 | hubp = pipe_ctx->plane_res.hubp; | |
1032 | /*DCHUBP_CNTL:HUBP_DISABLE=0*/ | |
95022795 | 1033 | if (hubp != NULL && hubp->funcs->hubp_disable_control) |
3ba43a59 CL |
1034 | hubp->funcs->hubp_disable_control(hubp, true); |
1035 | } | |
1036 | } | |
1037 | /*DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=0*/ | |
1038 | hubbub1_soft_reset(dc->res_pool->hubbub, false); | |
1039 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
1040 | struct pipe_ctx *pipe_ctx = | |
1041 | &dc->current_state->res_ctx.pipe_ctx[i]; | |
1042 | if (pipe_ctx != NULL) { | |
1043 | hubp = pipe_ctx->plane_res.hubp; | |
1044 | /*DCHUBP_CNTL:HUBP_BLANK_EN=0*/ | |
95022795 | 1045 | if (hubp != NULL && hubp->funcs->set_hubp_blank_en) |
3ba43a59 CL |
1046 | hubp->funcs->set_hubp_blank_en(hubp, true); |
1047 | } | |
1048 | } | |
1049 | return true; | |
1050 | ||
1051 | } | |
1052 | ||
c2437b1f | 1053 | void dcn10_verify_allow_pstate_change_high(struct dc *dc) |
ea00f297 | 1054 | { |
f042c330 | 1055 | struct hubbub *hubbub = dc->res_pool->hubbub; |
ea00f297 YHL |
1056 | static bool should_log_hw_state; /* prevent hw state log by default */ |
1057 | ||
f042c330 NK |
1058 | if (!hubbub->funcs->verify_allow_pstate_change_high) |
1059 | return; | |
1060 | ||
1061 | if (!hubbub->funcs->verify_allow_pstate_change_high(hubbub)) { | |
8b198f6e RS |
1062 | int i = 0; |
1063 | ||
1064 | if (should_log_hw_state) | |
46659a83 | 1065 | dcn10_log_hw_state(dc, NULL); |
8b198f6e RS |
1066 | |
1067 | TRACE_DC_PIPE_STATE(pipe_ctx, i, MAX_PIPES); | |
ea00f297 | 1068 | BREAK_TO_DEBUGGER(); |
3ba43a59 | 1069 | if (dcn10_hw_wa_force_recovery(dc)) { |
f042c330 NK |
1070 | /*check again*/ |
1071 | if (!hubbub->funcs->verify_allow_pstate_change_high(hubbub)) | |
3ba43a59 CL |
1072 | BREAK_TO_DEBUGGER(); |
1073 | } | |
ea00f297 YHL |
1074 | } |
1075 | } | |
1076 | ||
a74b2734 | 1077 | /* trigger HW to start disconnect plane from stream on the next vsync */ |
78c77382 | 1078 | void dcn10_plane_atomic_disconnect(struct dc *dc, struct pipe_ctx *pipe_ctx) |
70ccab60 | 1079 | { |
f42ea55b | 1080 | struct dce_hwseq *hws = dc->hwseq; |
e07f541f YS |
1081 | struct hubp *hubp = pipe_ctx->plane_res.hubp; |
1082 | int dpp_id = pipe_ctx->plane_res.dpp->inst; | |
cc408d72 | 1083 | struct mpc *mpc = dc->res_pool->mpc; |
feb4a3cd EB |
1084 | struct mpc_tree *mpc_tree_params; |
1085 | struct mpcc *mpcc_to_remove = NULL; | |
365acbaf | 1086 | struct output_pixel_processor *opp = pipe_ctx->stream_res.opp; |
cc408d72 | 1087 | |
365acbaf YS |
1088 | mpc_tree_params = &(opp->mpc_tree_params); |
1089 | mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id); | |
feb4a3cd | 1090 | |
a74b2734 | 1091 | /*Already reset*/ |
365acbaf | 1092 | if (mpcc_to_remove == NULL) |
a74b2734 | 1093 | return; |
70ccab60 | 1094 | |
feb4a3cd | 1095 | mpc->funcs->remove_mpcc(mpc, mpc_tree_params, mpcc_to_remove); |
7f93c1de CL |
1096 | if (opp != NULL) |
1097 | opp->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; | |
e6c258cb | 1098 | |
0aa63a33 | 1099 | dc->optimized_required = true; |
82c026d1 | 1100 | |
e6c258cb YS |
1101 | if (hubp->funcs->hubp_disconnect) |
1102 | hubp->funcs->hubp_disconnect(hubp); | |
1103 | ||
fb3466a4 | 1104 | if (dc->debug.sanity_checks) |
f42ea55b | 1105 | hws->funcs.verify_allow_pstate_change_high(dc); |
d65359d5 TC |
1106 | } |
1107 | ||
23344703 RS |
1108 | /** |
1109 | * dcn10_plane_atomic_power_down - Power down plane components. | |
1110 | * | |
1111 | * @dc: dc struct reference. used for grab hwseq. | |
1112 | * @dpp: dpp struct reference. | |
1113 | * @hubp: hubp struct reference. | |
1114 | * | |
1115 | * Keep in mind that this operation requires a power gate configuration; | |
1116 | * however, requests for switch power gate are precisely controlled to avoid | |
1117 | * problems. For this reason, power gate request is usually disabled. This | |
1118 | * function first needs to enable the power gate request before disabling DPP | |
1119 | * and HUBP. Finally, it disables the power gate request again. | |
1120 | */ | |
78c77382 | 1121 | void dcn10_plane_atomic_power_down(struct dc *dc, |
522f82f3 AK |
1122 | struct dpp *dpp, |
1123 | struct hubp *hubp) | |
a74b2734 TC |
1124 | { |
1125 | struct dce_hwseq *hws = dc->hwseq; | |
5d4b05dd | 1126 | DC_LOGGER_INIT(dc->ctx->logger); |
a74b2734 | 1127 | |
e6c258cb YS |
1128 | if (REG(DC_IP_REQUEST_CNTL)) { |
1129 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
1130 | IP_REQUEST_EN, 1); | |
d2138be3 NK |
1131 | |
1132 | if (hws->funcs.dpp_pg_control) | |
1133 | hws->funcs.dpp_pg_control(hws, dpp->inst, false); | |
1134 | ||
1135 | if (hws->funcs.hubp_pg_control) | |
1136 | hws->funcs.hubp_pg_control(hws, hubp->inst, false); | |
1137 | ||
e6c258cb YS |
1138 | dpp->funcs->dpp_reset(dpp); |
1139 | REG_SET(DC_IP_REQUEST_CNTL, 0, | |
1140 | IP_REQUEST_EN, 0); | |
1296423b | 1141 | DC_LOG_DEBUG( |
522f82f3 | 1142 | "Power gated front end %d\n", hubp->inst); |
e6c258cb YS |
1143 | } |
1144 | } | |
a74b2734 | 1145 | |
7f914a62 YS |
1146 | /* disable HW used by plane. |
1147 | * note: cannot disable until disconnect is complete | |
1148 | */ | |
78c77382 | 1149 | void dcn10_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx) |
e6c258cb | 1150 | { |
f42ea55b | 1151 | struct dce_hwseq *hws = dc->hwseq; |
e07f541f | 1152 | struct hubp *hubp = pipe_ctx->plane_res.hubp; |
f8e413bf | 1153 | struct dpp *dpp = pipe_ctx->plane_res.dpp; |
7f914a62 | 1154 | int opp_id = hubp->opp_id; |
a74b2734 | 1155 | |
1ccda80f | 1156 | dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx); |
a74b2734 | 1157 | |
c8242b98 YS |
1158 | hubp->funcs->hubp_clk_cntl(hubp, false); |
1159 | ||
f8e413bf | 1160 | dpp->funcs->dpp_dppclk_control(dpp, false, false); |
a74b2734 | 1161 | |
2e9d6a57 | 1162 | if (opp_id != 0xf && pipe_ctx->stream_res.opp->mpc_tree_params.opp_list == NULL) |
be2f449a YS |
1163 | pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control( |
1164 | pipe_ctx->stream_res.opp, | |
1165 | false); | |
e6c258cb | 1166 | |
7f914a62 | 1167 | hubp->power_gated = true; |
0aa63a33 | 1168 | dc->optimized_required = false; /* We're powering off, no need to optimize */ |
a74b2734 | 1169 | |
f42ea55b | 1170 | hws->funcs.plane_atomic_power_down(dc, |
522f82f3 AK |
1171 | pipe_ctx->plane_res.dpp, |
1172 | pipe_ctx->plane_res.hubp); | |
1ccda80f YS |
1173 | |
1174 | pipe_ctx->stream = NULL; | |
1175 | memset(&pipe_ctx->stream_res, 0, sizeof(pipe_ctx->stream_res)); | |
1176 | memset(&pipe_ctx->plane_res, 0, sizeof(pipe_ctx->plane_res)); | |
1177 | pipe_ctx->top_pipe = NULL; | |
1178 | pipe_ctx->bottom_pipe = NULL; | |
1179 | pipe_ctx->plane_state = NULL; | |
7f914a62 YS |
1180 | } |
1181 | ||
78c77382 | 1182 | void dcn10_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx) |
7f914a62 | 1183 | { |
f42ea55b | 1184 | struct dce_hwseq *hws = dc->hwseq; |
5d4b05dd | 1185 | DC_LOGGER_INIT(dc->ctx->logger); |
1296423b | 1186 | |
24a30505 | 1187 | if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated) |
7f914a62 YS |
1188 | return; |
1189 | ||
f42ea55b | 1190 | hws->funcs.plane_atomic_disable(dc, pipe_ctx); |
7f914a62 YS |
1191 | |
1192 | apply_DEGVIDCN10_253_wa(dc); | |
a74b2734 | 1193 | |
1296423b | 1194 | DC_LOG_DC("Power down front end %d\n", |
7f914a62 | 1195 | pipe_ctx->pipe_idx); |
a74b2734 TC |
1196 | } |
1197 | ||
78c77382 | 1198 | void dcn10_init_pipes(struct dc *dc, struct dc_state *context) |
7a5086a7 | 1199 | { |
4e1c1875 | 1200 | int i; |
f42ea55b | 1201 | struct dce_hwseq *hws = dc->hwseq; |
ce72741b AK |
1202 | bool can_apply_seamless_boot = false; |
1203 | ||
1204 | for (i = 0; i < context->stream_count; i++) { | |
1205 | if (context->streams[i]->apply_seamless_boot_optimization) { | |
1206 | can_apply_seamless_boot = true; | |
1207 | break; | |
1208 | } | |
1209 | } | |
7a5086a7 YS |
1210 | |
1211 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
7a5086a7 | 1212 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; |
ce72741b AK |
1213 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
1214 | ||
1215 | /* There is assumption that pipe_ctx is not mapping irregularly | |
1216 | * to non-preferred front end. If pipe_ctx->stream is not NULL, | |
1217 | * we will use the pipe, so don't disable | |
1218 | */ | |
d5ca5fdb | 1219 | if (pipe_ctx->stream != NULL && can_apply_seamless_boot) |
ce72741b | 1220 | continue; |
7a5086a7 | 1221 | |
fb55546e AK |
1222 | /* Blank controller using driver code instead of |
1223 | * command table. | |
1224 | */ | |
0af4096d | 1225 | if (tg->funcs->is_tg_enabled(tg)) { |
f42ea55b AK |
1226 | if (hws->funcs.init_blank != NULL) { |
1227 | hws->funcs.init_blank(dc, tg); | |
8a31820b ML |
1228 | tg->funcs->lock(tg); |
1229 | } else { | |
1230 | tg->funcs->lock(tg); | |
1231 | tg->funcs->set_blank(tg, true); | |
1232 | hwss_wait_for_blank_complete(tg); | |
1233 | } | |
0af4096d YS |
1234 | } |
1235 | } | |
7a5086a7 | 1236 | |
1380c1bf NA |
1237 | /* num_opp will be equal to number of mpcc */ |
1238 | for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) { | |
5ec43eda ML |
1239 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
1240 | ||
1241 | /* Cannot reset the MPC mux if seamless boot */ | |
1242 | if (pipe_ctx->stream != NULL && can_apply_seamless_boot) | |
1243 | continue; | |
1244 | ||
1245 | dc->res_pool->mpc->funcs->mpc_init_single_inst( | |
1246 | dc->res_pool->mpc, i); | |
1247 | } | |
feb4a3cd | 1248 | |
fb55546e | 1249 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
0af4096d | 1250 | struct timing_generator *tg = dc->res_pool->timing_generators[i]; |
621fd3e3 | 1251 | struct hubp *hubp = dc->res_pool->hubps[i]; |
e07f541f | 1252 | struct dpp *dpp = dc->res_pool->dpps[i]; |
fb55546e AK |
1253 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
1254 | ||
ce72741b AK |
1255 | /* There is assumption that pipe_ctx is not mapping irregularly |
1256 | * to non-preferred front end. If pipe_ctx->stream is not NULL, | |
1257 | * we will use the pipe, so don't disable | |
1258 | */ | |
46570f09 AK |
1259 | if (can_apply_seamless_boot && |
1260 | pipe_ctx->stream != NULL && | |
1261 | pipe_ctx->stream_res.tg->funcs->is_tg_enabled( | |
9f21f379 AL |
1262 | pipe_ctx->stream_res.tg)) { |
1263 | // Enable double buffering for OTG_BLANK no matter if | |
1264 | // seamless boot is enabled or not to suppress global sync | |
1265 | // signals when OTG blanked. This is to prevent pipe from | |
1266 | // requesting data while in PSR. | |
1267 | tg->funcs->tg_init(tg); | |
accff74e | 1268 | hubp->power_gated = true; |
ce72741b | 1269 | continue; |
9f21f379 | 1270 | } |
ce72741b | 1271 | |
4bc46da4 NK |
1272 | /* Disable on the current state so the new one isn't cleared. */ |
1273 | pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; | |
1274 | ||
fb55546e | 1275 | dpp->funcs->dpp_reset(dpp); |
621fd3e3 | 1276 | |
0af4096d YS |
1277 | pipe_ctx->stream_res.tg = tg; |
1278 | pipe_ctx->pipe_idx = i; | |
621fd3e3 AJ |
1279 | |
1280 | pipe_ctx->plane_res.hubp = hubp; | |
e07f541f YS |
1281 | pipe_ctx->plane_res.dpp = dpp; |
1282 | pipe_ctx->plane_res.mpcc_inst = dpp->inst; | |
1283 | hubp->mpcc_id = dpp->inst; | |
043f5bb6 | 1284 | hubp->opp_id = OPP_ID_INVALID; |
621fd3e3 | 1285 | hubp->power_gated = false; |
0af4096d | 1286 | |
f23d5584 EB |
1287 | dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst; |
1288 | dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL; | |
e07f541f | 1289 | dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; |
cc55b1f5 | 1290 | pipe_ctx->stream_res.opp = dc->res_pool->opps[i]; |
1ccda80f | 1291 | |
f42ea55b | 1292 | hws->funcs.plane_atomic_disconnect(dc, pipe_ctx); |
0af4096d YS |
1293 | |
1294 | if (tg->funcs->is_tg_enabled(tg)) | |
1295 | tg->funcs->unlock(tg); | |
0af4096d | 1296 | |
8a31820b | 1297 | dc->hwss.disable_plane(dc, pipe_ctx); |
073a45e8 | 1298 | |
0af4096d YS |
1299 | pipe_ctx->stream_res.tg = NULL; |
1300 | pipe_ctx->plane_res.hubp = NULL; | |
1301 | ||
073a45e8 | 1302 | tg->funcs->tg_init(tg); |
7a5086a7 | 1303 | } |
fb55546e AK |
1304 | } |
1305 | ||
78c77382 | 1306 | void dcn10_init_hw(struct dc *dc) |
fb55546e | 1307 | { |
8ceb47e8 | 1308 | int i; |
fb55546e AK |
1309 | struct abm *abm = dc->res_pool->abm; |
1310 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
1311 | struct dce_hwseq *hws = dc->hwseq; | |
1312 | struct dc_bios *dcb = dc->ctx->dc_bios; | |
8a31820b | 1313 | struct resource_pool *res_pool = dc->res_pool; |
3ba01817 | 1314 | uint32_t backlight = MAX_BACKLIGHT_LEVEL; |
cd9a180a | 1315 | bool is_optimized_init_done = false; |
8a31820b ML |
1316 | |
1317 | if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks) | |
1318 | dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); | |
1319 | ||
1320 | // Initialize the dccg | |
1321 | if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->dccg_init) | |
1322 | dc->res_pool->dccg->funcs->dccg_init(res_pool->dccg); | |
fb55546e AK |
1323 | |
1324 | if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
8a31820b | 1325 | |
fb55546e AK |
1326 | REG_WRITE(REFCLK_CNTL, 0); |
1327 | REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1); | |
1328 | REG_WRITE(DIO_MEM_PWR_CTRL, 0); | |
7a5086a7 | 1329 | |
fb55546e AK |
1330 | if (!dc->debug.disable_clock_gate) { |
1331 | /* enable all DCN clock gating */ | |
1332 | REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); | |
1333 | ||
1334 | REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0); | |
1335 | ||
1336 | REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); | |
1337 | } | |
1338 | ||
8a31820b | 1339 | //Enable ability to power gate / don't force power on permanently |
3c9de4da AL |
1340 | if (hws->funcs.enable_power_gating_plane) |
1341 | hws->funcs.enable_power_gating_plane(hws, true); | |
fb55546e | 1342 | |
529c690b | 1343 | return; |
fb55546e AK |
1344 | } |
1345 | ||
f49cfa27 | 1346 | if (!dcb->funcs->is_accelerated_mode(dcb)) |
f42ea55b | 1347 | hws->funcs.disable_vga(dc->hwseq); |
f49cfa27 | 1348 | |
64c51ea5 | 1349 | hws->funcs.bios_golden_init(dc); |
8f95ff28 | 1350 | |
f49cfa27 | 1351 | if (dc->ctx->dc_bios->fw_info_valid) { |
1352 | res_pool->ref_clocks.xtalin_clock_inKhz = | |
1353 | dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; | |
1354 | ||
1355 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
1356 | if (res_pool->dccg && res_pool->hubbub) { | |
1357 | ||
1358 | (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg, | |
1359 | dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency, | |
1360 | &res_pool->ref_clocks.dccg_ref_clock_inKhz); | |
1361 | ||
1362 | (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub, | |
1363 | res_pool->ref_clocks.dccg_ref_clock_inKhz, | |
1364 | &res_pool->ref_clocks.dchub_ref_clock_inKhz); | |
1365 | } else { | |
1366 | // Not all ASICs have DCCG sw component | |
1367 | res_pool->ref_clocks.dccg_ref_clock_inKhz = | |
1368 | res_pool->ref_clocks.xtalin_clock_inKhz; | |
1369 | res_pool->ref_clocks.dchub_ref_clock_inKhz = | |
1370 | res_pool->ref_clocks.xtalin_clock_inKhz; | |
1371 | } | |
1372 | } | |
1373 | } else | |
1374 | ASSERT_CRITICAL(false); | |
fb55546e AK |
1375 | |
1376 | for (i = 0; i < dc->link_count; i++) { | |
1377 | /* Power up AND update implementation according to the | |
1378 | * required signal (which may be different from the | |
1379 | * default signal on connector). | |
1380 | */ | |
1381 | struct dc_link *link = dc->links[i]; | |
1382 | ||
cd9a180a IK |
1383 | if (!is_optimized_init_done) |
1384 | link->link_enc->funcs->hw_init(link->link_enc); | |
fb55546e AK |
1385 | |
1386 | /* Check for enabled DIG to identify enabled display */ | |
1387 | if (link->link_enc->funcs->is_dig_enabled && | |
1388 | link->link_enc->funcs->is_dig_enabled(link->link_enc)) | |
1389 | link->link_status.link_active = true; | |
1390 | } | |
529c690b | 1391 | |
8a31820b | 1392 | /* Power gate DSCs */ |
cd9a180a IK |
1393 | if (!is_optimized_init_done) { |
1394 | for (i = 0; i < res_pool->res_cap->num_dsc; i++) | |
1395 | if (hws->funcs.dsc_pg_control != NULL) | |
1396 | hws->funcs.dsc_pg_control(hws, res_pool->dscs[i]->inst, false); | |
1397 | } | |
8a31820b | 1398 | |
4f8e37db MS |
1399 | /* Enable outbox notification feature of dmub */ |
1400 | if (dc->debug.enable_dmub_aux_for_legacy_ddc) | |
1401 | dmub_enable_outbox_notification(dc); | |
1402 | ||
896dace8 | 1403 | /* we want to turn off all dp displays before doing detection */ |
8ceb47e8 LHM |
1404 | if (dc->config.power_down_display_on_boot) |
1405 | blank_all_dp_displays(dc, true); | |
896dace8 | 1406 | |
235fb600 RL |
1407 | if (hws->funcs.enable_power_gating_plane) |
1408 | hws->funcs.enable_power_gating_plane(dc->hwseq, true); | |
1409 | ||
522f82f3 AK |
1410 | /* If taking control over from VBIOS, we may want to optimize our first |
1411 | * mode set, so we need to skip powering down pipes until we know which | |
1412 | * pipes we want to use. | |
1413 | * Otherwise, if taking control is not possible, we need to power | |
1414 | * everything down. | |
1415 | */ | |
27eaa492 | 1416 | if (dcb->funcs->is_accelerated_mode(dcb) || dc->config.power_down_display_on_boot) { |
cd9a180a IK |
1417 | if (!is_optimized_init_done) { |
1418 | hws->funcs.init_pipes(dc, dc->current_state); | |
1419 | if (dc->res_pool->hubbub->funcs->allow_self_refresh_control) | |
1420 | dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, | |
1421 | !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); | |
1422 | } | |
522f82f3 AK |
1423 | } |
1424 | ||
cd9a180a | 1425 | if (!is_optimized_init_done) { |
7a5086a7 | 1426 | |
cd9a180a IK |
1427 | for (i = 0; i < res_pool->audio_count; i++) { |
1428 | struct audio *audio = res_pool->audios[i]; | |
7a5086a7 | 1429 | |
cd9a180a IK |
1430 | audio->funcs->hw_init(audio); |
1431 | } | |
3ba01817 | 1432 | |
cd9a180a IK |
1433 | for (i = 0; i < dc->link_count; i++) { |
1434 | struct dc_link *link = dc->links[i]; | |
7a5086a7 | 1435 | |
cd9a180a IK |
1436 | if (link->panel_cntl) |
1437 | backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl); | |
1438 | } | |
3ba01817 | 1439 | |
cd9a180a IK |
1440 | if (abm != NULL) |
1441 | abm->funcs->abm_init(abm, backlight); | |
1442 | ||
1443 | if (dmcu != NULL && !dmcu->auto_load_dmcu) | |
1444 | dmcu->funcs->dmcu_init(dmcu); | |
1445 | } | |
7a5086a7 | 1446 | |
70d9e8cb PH |
1447 | if (abm != NULL && dmcu != NULL) |
1448 | abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu); | |
1449 | ||
7a5086a7 | 1450 | /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ |
cd9a180a IK |
1451 | if (!is_optimized_init_done) |
1452 | REG_WRITE(DIO_MEM_PWR_CTRL, 0); | |
7a5086a7 YS |
1453 | |
1454 | if (!dc->debug.disable_clock_gate) { | |
1455 | /* enable all DCN clock gating */ | |
1456 | REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); | |
1457 | ||
1458 | REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0); | |
1459 | ||
1460 | REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); | |
1461 | } | |
622a88c8 EY |
1462 | |
1463 | if (dc->clk_mgr->funcs->notify_wm_ranges) | |
1464 | dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); | |
7a5086a7 YS |
1465 | } |
1466 | ||
ca751df2 SL |
1467 | /* In headless boot cases, DIG may be turned |
1468 | * on which causes HW/SW discrepancies. | |
1469 | * To avoid this, power down hardware on boot | |
25331a18 | 1470 | * if DIG is turned on |
ca751df2 SL |
1471 | */ |
1472 | void dcn10_power_down_on_boot(struct dc *dc) | |
1473 | { | |
45a1261b | 1474 | struct dc_link *edp_links[MAX_NUM_EDP]; |
3addbde2 | 1475 | struct dc_link *edp_link = NULL; |
45a1261b JW |
1476 | int edp_num; |
1477 | int i = 0; | |
ca751df2 | 1478 | |
45a1261b | 1479 | get_edp_links(dc, edp_links, &edp_num); |
3addbde2 JW |
1480 | if (edp_num) |
1481 | edp_link = edp_links[0]; | |
1482 | ||
1483 | if (edp_link && edp_link->link_enc->funcs->is_dig_enabled && | |
1484 | edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) && | |
1485 | dc->hwseq->funcs.edp_backlight_control && | |
1486 | dc->hwss.power_down && | |
1487 | dc->hwss.edp_power_control) { | |
1488 | dc->hwseq->funcs.edp_backlight_control(edp_link, false); | |
1489 | dc->hwss.power_down(dc); | |
1490 | dc->hwss.edp_power_control(edp_link, false); | |
387f3a30 SL |
1491 | } else { |
1492 | for (i = 0; i < dc->link_count; i++) { | |
1493 | struct dc_link *link = dc->links[i]; | |
ca751df2 | 1494 | |
74a3e2f9 | 1495 | if (link->link_enc && link->link_enc->funcs->is_dig_enabled && |
387f3a30 SL |
1496 | link->link_enc->funcs->is_dig_enabled(link->link_enc) && |
1497 | dc->hwss.power_down) { | |
1498 | dc->hwss.power_down(dc); | |
1499 | break; | |
ca751df2 | 1500 | } |
387f3a30 | 1501 | |
ca751df2 SL |
1502 | } |
1503 | } | |
387f3a30 SL |
1504 | |
1505 | /* | |
1506 | * Call update_clocks with empty context | |
1507 | * to send DISPLAY_OFF | |
1508 | * Otherwise DISPLAY_OFF may not be asserted | |
1509 | */ | |
1510 | if (dc->clk_mgr->funcs->set_low_power_state) | |
1511 | dc->clk_mgr->funcs->set_low_power_state(dc->clk_mgr); | |
ca751df2 SL |
1512 | } |
1513 | ||
78c77382 | 1514 | void dcn10_reset_hw_ctx_wrap( |
fb3466a4 | 1515 | struct dc *dc, |
608ac7bb | 1516 | struct dc_state *context) |
70ccab60 HW |
1517 | { |
1518 | int i; | |
f42ea55b | 1519 | struct dce_hwseq *hws = dc->hwseq; |
70ccab60 | 1520 | |
cfe4645e | 1521 | /* Reset Back End*/ |
70ccab60 HW |
1522 | for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { |
1523 | struct pipe_ctx *pipe_ctx_old = | |
608ac7bb | 1524 | &dc->current_state->res_ctx.pipe_ctx[i]; |
70ccab60 HW |
1525 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
1526 | ||
1527 | if (!pipe_ctx_old->stream) | |
1528 | continue; | |
1529 | ||
56e6ed45 YS |
1530 | if (pipe_ctx_old->top_pipe) |
1531 | continue; | |
1532 | ||
70ccab60 | 1533 | if (!pipe_ctx->stream || |
21e67d4d HW |
1534 | pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { |
1535 | struct clock_source *old_clk = pipe_ctx_old->clock_source; | |
1536 | ||
4a797d24 | 1537 | dcn10_reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state); |
f42ea55b AK |
1538 | if (hws->funcs.enable_stream_gating) |
1539 | hws->funcs.enable_stream_gating(dc, pipe_ctx); | |
21e67d4d HW |
1540 | if (old_clk) |
1541 | old_clk->funcs->cs_power_down(old_clk); | |
1542 | } | |
70ccab60 HW |
1543 | } |
1544 | } | |
1545 | ||
cfe4645e DL |
1546 | static bool patch_address_for_sbs_tb_stereo( |
1547 | struct pipe_ctx *pipe_ctx, PHYSICAL_ADDRESS_LOC *addr) | |
70ccab60 | 1548 | { |
3be5262e | 1549 | struct dc_plane_state *plane_state = pipe_ctx->plane_state; |
70ccab60 | 1550 | bool sec_split = pipe_ctx->top_pipe && |
3be5262e HW |
1551 | pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state; |
1552 | if (sec_split && plane_state->address.type == PLN_ADDR_TYPE_GRPH_STEREO && | |
4fa086b9 | 1553 | (pipe_ctx->stream->timing.timing_3d_format == |
70ccab60 | 1554 | TIMING_3D_FORMAT_SIDE_BY_SIDE || |
4fa086b9 | 1555 | pipe_ctx->stream->timing.timing_3d_format == |
70ccab60 | 1556 | TIMING_3D_FORMAT_TOP_AND_BOTTOM)) { |
3be5262e HW |
1557 | *addr = plane_state->address.grph_stereo.left_addr; |
1558 | plane_state->address.grph_stereo.left_addr = | |
1559 | plane_state->address.grph_stereo.right_addr; | |
70ccab60 | 1560 | return true; |
cdc5e048 | 1561 | } else { |
4fa086b9 | 1562 | if (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_NONE && |
3be5262e HW |
1563 | plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO) { |
1564 | plane_state->address.type = PLN_ADDR_TYPE_GRPH_STEREO; | |
1565 | plane_state->address.grph_stereo.right_addr = | |
1566 | plane_state->address.grph_stereo.left_addr; | |
480c5b8f AL |
1567 | plane_state->address.grph_stereo.right_meta_addr = |
1568 | plane_state->address.grph_stereo.left_meta_addr; | |
cdc5e048 | 1569 | } |
70ccab60 HW |
1570 | } |
1571 | return false; | |
1572 | } | |
1573 | ||
78c77382 | 1574 | void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) |
70ccab60 HW |
1575 | { |
1576 | bool addr_patched = false; | |
1577 | PHYSICAL_ADDRESS_LOC addr; | |
3be5262e | 1578 | struct dc_plane_state *plane_state = pipe_ctx->plane_state; |
70ccab60 | 1579 | |
3be5262e | 1580 | if (plane_state == NULL) |
70ccab60 | 1581 | return; |
68199bd1 | 1582 | |
70ccab60 | 1583 | addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr); |
68199bd1 | 1584 | |
8feabd03 YHL |
1585 | pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( |
1586 | pipe_ctx->plane_res.hubp, | |
3be5262e | 1587 | &plane_state->address, |
bda9afda | 1588 | plane_state->flip_immediate); |
68199bd1 | 1589 | |
3be5262e | 1590 | plane_state->status.requested_address = plane_state->address; |
68199bd1 TC |
1591 | |
1592 | if (plane_state->flip_immediate) | |
1593 | plane_state->status.current_address = plane_state->address; | |
1594 | ||
70ccab60 | 1595 | if (addr_patched) |
3be5262e | 1596 | pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; |
70ccab60 HW |
1597 | } |
1598 | ||
78c77382 AK |
1599 | bool dcn10_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, |
1600 | const struct dc_plane_state *plane_state) | |
70ccab60 | 1601 | { |
d94585a0 | 1602 | struct dpp *dpp_base = pipe_ctx->plane_res.dpp; |
7b0c470f | 1603 | const struct dc_transfer_func *tf = NULL; |
70ccab60 HW |
1604 | bool result = true; |
1605 | ||
d94585a0 | 1606 | if (dpp_base == NULL) |
70ccab60 HW |
1607 | return false; |
1608 | ||
3be5262e HW |
1609 | if (plane_state->in_transfer_func) |
1610 | tf = plane_state->in_transfer_func; | |
70ccab60 | 1611 | |
f9549850 | 1612 | if (plane_state->gamma_correction && |
8ce504b9 ML |
1613 | !dpp_base->ctx->dc->debug.always_use_regamma |
1614 | && !plane_state->gamma_correction->is_identity | |
e43a432c | 1615 | && dce_use_lut(plane_state->format)) |
a6114e85 | 1616 | dpp_base->funcs->dpp_program_input_lut(dpp_base, plane_state->gamma_correction); |
5aff86c1 | 1617 | |
70ccab60 | 1618 | if (tf == NULL) |
5fa2ec4f | 1619 | dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS); |
7b0c470f LSL |
1620 | else if (tf->type == TF_TYPE_PREDEFINED) { |
1621 | switch (tf->tf) { | |
70ccab60 | 1622 | case TRANSFER_FUNCTION_SRGB: |
a6114e85 | 1623 | dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_HW_sRGB); |
70ccab60 HW |
1624 | break; |
1625 | case TRANSFER_FUNCTION_BT709: | |
a6114e85 | 1626 | dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_HW_xvYCC); |
70ccab60 HW |
1627 | break; |
1628 | case TRANSFER_FUNCTION_LINEAR: | |
a6114e85 | 1629 | dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS); |
70ccab60 HW |
1630 | break; |
1631 | case TRANSFER_FUNCTION_PQ: | |
2853ecc6 RA |
1632 | dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_USER_PWL); |
1633 | cm_helper_translate_curve_to_degamma_hw_format(tf, &dpp_base->degamma_params); | |
1634 | dpp_base->funcs->dpp_program_degamma_pwl(dpp_base, &dpp_base->degamma_params); | |
1635 | result = true; | |
1636 | break; | |
70ccab60 HW |
1637 | default: |
1638 | result = false; | |
1639 | break; | |
1640 | } | |
7b0c470f | 1641 | } else if (tf->type == TF_TYPE_BYPASS) { |
5fa2ec4f | 1642 | dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS); |
70ccab60 | 1643 | } else { |
78e4405c DF |
1644 | cm_helper_translate_curve_to_degamma_hw_format(tf, |
1645 | &dpp_base->degamma_params); | |
1646 | dpp_base->funcs->dpp_program_degamma_pwl(dpp_base, | |
1647 | &dpp_base->degamma_params); | |
1648 | result = true; | |
70ccab60 HW |
1649 | } |
1650 | ||
1651 | return result; | |
1652 | } | |
70ccab60 | 1653 | |
8221b6ea WW |
1654 | #define MAX_NUM_HW_POINTS 0x200 |
1655 | ||
1656 | static void log_tf(struct dc_context *ctx, | |
1657 | struct dc_transfer_func *tf, uint32_t hw_points_num) | |
1658 | { | |
1659 | // DC_LOG_GAMMA is default logging of all hw points | |
1660 | // DC_LOG_ALL_GAMMA logs all points, not only hw points | |
1661 | // DC_LOG_ALL_TF_POINTS logs all channels of the tf | |
1662 | int i = 0; | |
1663 | ||
1664 | DC_LOGGER_INIT(ctx->logger); | |
1665 | DC_LOG_GAMMA("Gamma Correction TF"); | |
1666 | DC_LOG_ALL_GAMMA("Logging all tf points..."); | |
1667 | DC_LOG_ALL_TF_CHANNELS("Logging all channels..."); | |
1668 | ||
1669 | for (i = 0; i < hw_points_num; i++) { | |
cbc697b2 WW |
1670 | DC_LOG_GAMMA("R\t%d\t%llu", i, tf->tf_pts.red[i].value); |
1671 | DC_LOG_ALL_TF_CHANNELS("G\t%d\t%llu", i, tf->tf_pts.green[i].value); | |
1672 | DC_LOG_ALL_TF_CHANNELS("B\t%d\t%llu", i, tf->tf_pts.blue[i].value); | |
8221b6ea WW |
1673 | } |
1674 | ||
1675 | for (i = hw_points_num; i < MAX_NUM_HW_POINTS; i++) { | |
cbc697b2 WW |
1676 | DC_LOG_ALL_GAMMA("R\t%d\t%llu", i, tf->tf_pts.red[i].value); |
1677 | DC_LOG_ALL_TF_CHANNELS("G\t%d\t%llu", i, tf->tf_pts.green[i].value); | |
1678 | DC_LOG_ALL_TF_CHANNELS("B\t%d\t%llu", i, tf->tf_pts.blue[i].value); | |
8221b6ea WW |
1679 | } |
1680 | } | |
1681 | ||
78c77382 AK |
1682 | bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, |
1683 | const struct dc_stream_state *stream) | |
70ccab60 | 1684 | { |
d94585a0 | 1685 | struct dpp *dpp = pipe_ctx->plane_res.dpp; |
70ccab60 | 1686 | |
d94585a0 | 1687 | if (dpp == NULL) |
f46661dd AZ |
1688 | return false; |
1689 | ||
d94585a0 | 1690 | dpp->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM; |
70ccab60 | 1691 | |
4fa086b9 | 1692 | if (stream->out_transfer_func && |
a6114e85 HW |
1693 | stream->out_transfer_func->type == TF_TYPE_PREDEFINED && |
1694 | stream->out_transfer_func->tf == TRANSFER_FUNCTION_SRGB) | |
5fa2ec4f | 1695 | dpp->funcs->dpp_program_regamma_pwl(dpp, NULL, OPP_REGAMMA_SRGB); |
675634c5 YS |
1696 | |
1697 | /* dcn10_translate_regamma_to_hw_format takes 750us, only do it when full | |
1698 | * update. | |
1699 | */ | |
b6295960 | 1700 | else if (cm_helper_translate_curve_to_hw_format( |
675634c5 | 1701 | stream->out_transfer_func, |
b6295960 | 1702 | &dpp->regamma_params, false)) { |
675634c5 YS |
1703 | dpp->funcs->dpp_program_regamma_pwl( |
1704 | dpp, | |
1705 | &dpp->regamma_params, OPP_REGAMMA_USER); | |
1706 | } else | |
5fa2ec4f | 1707 | dpp->funcs->dpp_program_regamma_pwl(dpp, NULL, OPP_REGAMMA_BYPASS); |
70ccab60 | 1708 | |
ddef02de WW |
1709 | if (stream != NULL && stream->ctx != NULL && |
1710 | stream->out_transfer_func != NULL) { | |
1711 | log_tf(stream->ctx, | |
1712 | stream->out_transfer_func, | |
1713 | dpp->regamma_params.hw_points_num); | |
1714 | } | |
8221b6ea | 1715 | |
70ccab60 HW |
1716 | return true; |
1717 | } | |
1718 | ||
78c77382 | 1719 | void dcn10_pipe_control_lock( |
fb3466a4 | 1720 | struct dc *dc, |
70ccab60 HW |
1721 | struct pipe_ctx *pipe, |
1722 | bool lock) | |
1723 | { | |
f42ea55b AK |
1724 | struct dce_hwseq *hws = dc->hwseq; |
1725 | ||
70ccab60 HW |
1726 | /* use TG master update lock to lock everything on the TG |
1727 | * therefore only top pipe need to lock | |
1728 | */ | |
009114f6 | 1729 | if (!pipe || pipe->top_pipe) |
70ccab60 HW |
1730 | return; |
1731 | ||
fb3466a4 | 1732 | if (dc->debug.sanity_checks) |
f42ea55b | 1733 | hws->funcs.verify_allow_pstate_change_high(dc); |
2b13d7d3 | 1734 | |
70ccab60 | 1735 | if (lock) |
6b670fa9 | 1736 | pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg); |
70ccab60 | 1737 | else |
6b670fa9 | 1738 | pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg); |
2b13d7d3 | 1739 | |
fb3466a4 | 1740 | if (dc->debug.sanity_checks) |
f42ea55b | 1741 | hws->funcs.verify_allow_pstate_change_high(dc); |
70ccab60 HW |
1742 | } |
1743 | ||
63731e73 NK |
1744 | /** |
1745 | * delay_cursor_until_vupdate() - Delay cursor update if too close to VUPDATE. | |
1746 | * | |
1747 | * Software keepout workaround to prevent cursor update locking from stalling | |
1748 | * out cursor updates indefinitely or from old values from being retained in | |
1749 | * the case where the viewport changes in the same frame as the cursor. | |
1750 | * | |
1751 | * The idea is to calculate the remaining time from VPOS to VUPDATE. If it's | |
1752 | * too close to VUPDATE, then stall out until VUPDATE finishes. | |
1753 | * | |
1754 | * TODO: Optimize cursor programming to be once per frame before VUPDATE | |
1755 | * to avoid the need for this workaround. | |
1756 | */ | |
1757 | static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx) | |
1758 | { | |
1759 | struct dc_stream_state *stream = pipe_ctx->stream; | |
1760 | struct crtc_position position; | |
1761 | uint32_t vupdate_start, vupdate_end; | |
1762 | unsigned int lines_to_vupdate, us_to_vupdate, vpos; | |
1763 | unsigned int us_per_line, us_vupdate; | |
1764 | ||
1765 | if (!dc->hwss.calc_vupdate_position || !dc->hwss.get_position) | |
1766 | return; | |
1767 | ||
1768 | if (!pipe_ctx->stream_res.stream_enc || !pipe_ctx->stream_res.tg) | |
1769 | return; | |
1770 | ||
1771 | dc->hwss.calc_vupdate_position(dc, pipe_ctx, &vupdate_start, | |
1772 | &vupdate_end); | |
1773 | ||
1774 | dc->hwss.get_position(&pipe_ctx, 1, &position); | |
1775 | vpos = position.vertical_count; | |
1776 | ||
1777 | /* Avoid wraparound calculation issues */ | |
1778 | vupdate_start += stream->timing.v_total; | |
1779 | vupdate_end += stream->timing.v_total; | |
1780 | vpos += stream->timing.v_total; | |
1781 | ||
1782 | if (vpos <= vupdate_start) { | |
1783 | /* VPOS is in VACTIVE or back porch. */ | |
1784 | lines_to_vupdate = vupdate_start - vpos; | |
1785 | } else if (vpos > vupdate_end) { | |
1786 | /* VPOS is in the front porch. */ | |
1787 | return; | |
1788 | } else { | |
1789 | /* VPOS is in VUPDATE. */ | |
1790 | lines_to_vupdate = 0; | |
1791 | } | |
1792 | ||
1793 | /* Calculate time until VUPDATE in microseconds. */ | |
1794 | us_per_line = | |
1795 | stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz; | |
1796 | us_to_vupdate = lines_to_vupdate * us_per_line; | |
1797 | ||
1798 | /* 70 us is a conservative estimate of cursor update time*/ | |
1799 | if (us_to_vupdate > 70) | |
1800 | return; | |
1801 | ||
1802 | /* Stall out until the cursor update completes. */ | |
185082b6 AC |
1803 | if (vupdate_end < vupdate_start) |
1804 | vupdate_end += stream->timing.v_total; | |
63731e73 NK |
1805 | us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line; |
1806 | udelay(us_to_vupdate + us_vupdate); | |
1807 | } | |
1808 | ||
1e461c37 AC |
1809 | void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock) |
1810 | { | |
1811 | /* cursor lock is per MPCC tree, so only need to lock one pipe per stream */ | |
1812 | if (!pipe || pipe->top_pipe) | |
1813 | return; | |
1814 | ||
63731e73 NK |
1815 | /* Prevent cursor lock from stalling out cursor updates. */ |
1816 | if (lock) | |
1817 | delay_cursor_until_vupdate(dc, pipe); | |
1818 | ||
dc6e2448 WW |
1819 | if (pipe->stream && should_use_dmub_lock(pipe->stream->link)) { |
1820 | union dmub_hw_lock_flags hw_locks = { 0 }; | |
1821 | struct dmub_hw_lock_inst_flags inst_flags = { 0 }; | |
1822 | ||
1823 | hw_locks.bits.lock_cursor = 1; | |
1824 | inst_flags.opp_inst = pipe->stream_res.opp->inst; | |
1825 | ||
1826 | dmub_hw_lock_mgr_cmd(dc->ctx->dmub_srv, | |
1827 | lock, | |
1828 | &hw_locks, | |
1829 | &inst_flags); | |
1830 | } else | |
1831 | dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc, | |
1832 | pipe->stream_res.opp->inst, lock); | |
1e461c37 AC |
1833 | } |
1834 | ||
70ccab60 HW |
1835 | static bool wait_for_reset_trigger_to_occur( |
1836 | struct dc_context *dc_ctx, | |
1837 | struct timing_generator *tg) | |
1838 | { | |
1839 | bool rc = false; | |
1840 | ||
1841 | /* To avoid endless loop we wait at most | |
1842 | * frames_to_wait_on_triggered_reset frames for the reset to occur. */ | |
1843 | const uint32_t frames_to_wait_on_triggered_reset = 10; | |
1844 | int i; | |
1845 | ||
1846 | for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { | |
1847 | ||
1848 | if (!tg->funcs->is_counter_moving(tg)) { | |
1849 | DC_ERROR("TG counter is not moving!\n"); | |
1850 | break; | |
1851 | } | |
1852 | ||
1853 | if (tg->funcs->did_triggered_reset_occur(tg)) { | |
1854 | rc = true; | |
1855 | /* usually occurs at i=1 */ | |
1856 | DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", | |
1857 | i); | |
1858 | break; | |
1859 | } | |
1860 | ||
1861 | /* Wait for one frame. */ | |
1862 | tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE); | |
1863 | tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); | |
1864 | } | |
1865 | ||
1866 | if (false == rc) | |
1867 | DC_ERROR("GSL: Timeout on reset trigger!\n"); | |
1868 | ||
1869 | return rc; | |
1870 | } | |
1871 | ||
77a2b726 VS |
1872 | uint64_t reduceSizeAndFraction( |
1873 | uint64_t *numerator, | |
1874 | uint64_t *denominator, | |
1875 | bool checkUint32Bounary) | |
1876 | { | |
1877 | int i; | |
1878 | bool ret = checkUint32Bounary == false; | |
1879 | uint64_t max_int32 = 0xffffffff; | |
1880 | uint64_t num, denom; | |
1881 | static const uint16_t prime_numbers[] = { | |
1882 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, | |
1883 | 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, | |
1884 | 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, | |
1885 | 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, | |
1886 | 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, | |
1887 | 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, | |
1888 | 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, | |
1889 | 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, | |
1890 | 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, | |
1891 | 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, | |
1892 | 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, | |
1893 | 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, | |
1894 | 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, | |
1895 | 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, | |
1896 | 941, 947, 953, 967, 971, 977, 983, 991, 997}; | |
1897 | int count = ARRAY_SIZE(prime_numbers); | |
1898 | ||
1899 | num = *numerator; | |
1900 | denom = *denominator; | |
1901 | for (i = 0; i < count; i++) { | |
553ef24d | 1902 | uint32_t num_remainder, denom_remainder; |
a8a2e134 | 1903 | uint64_t num_result, denom_result; |
77a2b726 VS |
1904 | if (checkUint32Bounary && |
1905 | num <= max_int32 && denom <= max_int32) { | |
1906 | ret = true; | |
1907 | break; | |
1908 | } | |
a8a2e134 | 1909 | do { |
553ef24d VS |
1910 | num_result = div_u64_rem(num, prime_numbers[i], &num_remainder); |
1911 | denom_result = div_u64_rem(denom, prime_numbers[i], &denom_remainder); | |
1912 | if (num_remainder == 0 && denom_remainder == 0) { | |
a8a2e134 VS |
1913 | num = num_result; |
1914 | denom = denom_result; | |
1915 | } | |
553ef24d | 1916 | } while (num_remainder == 0 && denom_remainder == 0); |
77a2b726 VS |
1917 | } |
1918 | *numerator = num; | |
1919 | *denominator = denom; | |
1920 | return ret; | |
1921 | } | |
1922 | ||
1923 | bool is_low_refresh_rate(struct pipe_ctx *pipe) | |
1924 | { | |
1925 | uint32_t master_pipe_refresh_rate = | |
1926 | pipe->stream->timing.pix_clk_100hz * 100 / | |
1927 | pipe->stream->timing.h_total / | |
1928 | pipe->stream->timing.v_total; | |
1929 | return master_pipe_refresh_rate <= 30; | |
1930 | } | |
1931 | ||
1932 | uint8_t get_clock_divider(struct pipe_ctx *pipe, bool account_low_refresh_rate) | |
1933 | { | |
1934 | uint32_t clock_divider = 1; | |
1935 | uint32_t numpipes = 1; | |
1936 | ||
1937 | if (account_low_refresh_rate && is_low_refresh_rate(pipe)) | |
1938 | clock_divider *= 2; | |
1939 | ||
1940 | if (pipe->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
1941 | clock_divider *= 2; | |
1942 | ||
1943 | while (pipe->next_odm_pipe) { | |
1944 | pipe = pipe->next_odm_pipe; | |
1945 | numpipes++; | |
1946 | } | |
1947 | clock_divider *= numpipes; | |
1948 | ||
1949 | return clock_divider; | |
1950 | } | |
1951 | ||
1952 | int dcn10_align_pixel_clocks( | |
1953 | struct dc *dc, | |
1954 | int group_size, | |
1955 | struct pipe_ctx *grouped_pipes[]) | |
1956 | { | |
1957 | struct dc_context *dc_ctx = dc->ctx; | |
1958 | int i, master = -1, embedded = -1; | |
1959 | struct dc_crtc_timing hw_crtc_timing[MAX_PIPES] = {0}; | |
1960 | uint64_t phase[MAX_PIPES]; | |
1961 | uint64_t modulo[MAX_PIPES]; | |
1962 | unsigned int pclk; | |
1963 | ||
1964 | uint32_t embedded_pix_clk_100hz; | |
1965 | uint16_t embedded_h_total; | |
1966 | uint16_t embedded_v_total; | |
1967 | bool clamshell_closed = false; | |
1968 | uint32_t dp_ref_clk_100hz = | |
1969 | dc->res_pool->dp_clock_source->ctx->dc->clk_mgr->dprefclk_khz*10; | |
1970 | ||
1971 | if (dc->config.vblank_alignment_dto_params && | |
1972 | dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk) { | |
1973 | clamshell_closed = | |
1974 | (dc->config.vblank_alignment_dto_params >> 63); | |
1975 | embedded_h_total = | |
1976 | (dc->config.vblank_alignment_dto_params >> 32) & 0x7FFF; | |
1977 | embedded_v_total = | |
1978 | (dc->config.vblank_alignment_dto_params >> 48) & 0x7FFF; | |
1979 | embedded_pix_clk_100hz = | |
1980 | dc->config.vblank_alignment_dto_params & 0xFFFFFFFF; | |
1981 | ||
1982 | for (i = 0; i < group_size; i++) { | |
1983 | grouped_pipes[i]->stream_res.tg->funcs->get_hw_timing( | |
1984 | grouped_pipes[i]->stream_res.tg, | |
1985 | &hw_crtc_timing[i]); | |
1986 | dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( | |
1987 | dc->res_pool->dp_clock_source, | |
1988 | grouped_pipes[i]->stream_res.tg->inst, | |
1989 | &pclk); | |
1990 | hw_crtc_timing[i].pix_clk_100hz = pclk; | |
1991 | if (dc_is_embedded_signal( | |
1992 | grouped_pipes[i]->stream->signal)) { | |
1993 | embedded = i; | |
1994 | master = i; | |
1995 | phase[i] = embedded_pix_clk_100hz*100; | |
1996 | modulo[i] = dp_ref_clk_100hz*100; | |
1997 | } else { | |
1998 | ||
1999 | phase[i] = (uint64_t)embedded_pix_clk_100hz* | |
2000 | hw_crtc_timing[i].h_total* | |
783bf403 VS |
2001 | hw_crtc_timing[i].v_total; |
2002 | phase[i] = div_u64(phase[i], get_clock_divider(grouped_pipes[i], true)); | |
77a2b726 VS |
2003 | modulo[i] = (uint64_t)dp_ref_clk_100hz* |
2004 | embedded_h_total* | |
2005 | embedded_v_total; | |
2006 | ||
2007 | if (reduceSizeAndFraction(&phase[i], | |
2008 | &modulo[i], true) == false) { | |
2009 | /* | |
2010 | * this will help to stop reporting | |
2011 | * this timing synchronizable | |
2012 | */ | |
2013 | DC_SYNC_INFO("Failed to reduce DTO parameters\n"); | |
2014 | grouped_pipes[i]->stream->has_non_synchronizable_pclk = true; | |
2015 | } | |
2016 | } | |
2017 | } | |
2018 | ||
2019 | for (i = 0; i < group_size; i++) { | |
2020 | if (i != embedded && !grouped_pipes[i]->stream->has_non_synchronizable_pclk) { | |
2021 | dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk( | |
2022 | dc->res_pool->dp_clock_source, | |
2023 | grouped_pipes[i]->stream_res.tg->inst, | |
2024 | phase[i], modulo[i]); | |
2025 | dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz( | |
2026 | dc->res_pool->dp_clock_source, | |
2027 | grouped_pipes[i]->stream_res.tg->inst, &pclk); | |
2028 | grouped_pipes[i]->stream->timing.pix_clk_100hz = | |
2029 | pclk*get_clock_divider(grouped_pipes[i], false); | |
2030 | if (master == -1) | |
2031 | master = i; | |
2032 | } | |
2033 | } | |
2034 | ||
2035 | } | |
2036 | return master; | |
2037 | } | |
2038 | ||
2039 | void dcn10_enable_vblanks_synchronization( | |
2040 | struct dc *dc, | |
2041 | int group_index, | |
2042 | int group_size, | |
2043 | struct pipe_ctx *grouped_pipes[]) | |
2044 | { | |
2045 | struct dc_context *dc_ctx = dc->ctx; | |
2046 | struct output_pixel_processor *opp; | |
2047 | struct timing_generator *tg; | |
2048 | int i, width, height, master; | |
2049 | ||
2050 | for (i = 1; i < group_size; i++) { | |
2051 | opp = grouped_pipes[i]->stream_res.opp; | |
2052 | tg = grouped_pipes[i]->stream_res.tg; | |
2053 | tg->funcs->get_otg_active_size(tg, &width, &height); | |
2054 | if (opp->funcs->opp_program_dpg_dimensions) | |
2055 | opp->funcs->opp_program_dpg_dimensions(opp, width, 2*(height) + 1); | |
2056 | } | |
2057 | ||
2058 | for (i = 0; i < group_size; i++) { | |
2059 | if (grouped_pipes[i]->stream == NULL) | |
2060 | continue; | |
2061 | grouped_pipes[i]->stream->vblank_synchronized = false; | |
2062 | grouped_pipes[i]->stream->has_non_synchronizable_pclk = false; | |
2063 | } | |
2064 | ||
2065 | DC_SYNC_INFO("Aligning DP DTOs\n"); | |
2066 | ||
2067 | master = dcn10_align_pixel_clocks(dc, group_size, grouped_pipes); | |
2068 | ||
2069 | DC_SYNC_INFO("Synchronizing VBlanks\n"); | |
2070 | ||
2071 | if (master >= 0) { | |
2072 | for (i = 0; i < group_size; i++) { | |
2073 | if (i != master && !grouped_pipes[i]->stream->has_non_synchronizable_pclk) | |
2074 | grouped_pipes[i]->stream_res.tg->funcs->align_vblanks( | |
2075 | grouped_pipes[master]->stream_res.tg, | |
2076 | grouped_pipes[i]->stream_res.tg, | |
2077 | grouped_pipes[master]->stream->timing.pix_clk_100hz, | |
2078 | grouped_pipes[i]->stream->timing.pix_clk_100hz, | |
2079 | get_clock_divider(grouped_pipes[master], false), | |
2080 | get_clock_divider(grouped_pipes[i], false)); | |
2081 | grouped_pipes[i]->stream->vblank_synchronized = true; | |
2082 | } | |
2083 | grouped_pipes[master]->stream->vblank_synchronized = true; | |
2084 | DC_SYNC_INFO("Sync complete\n"); | |
2085 | } | |
2086 | ||
2087 | for (i = 1; i < group_size; i++) { | |
2088 | opp = grouped_pipes[i]->stream_res.opp; | |
2089 | tg = grouped_pipes[i]->stream_res.tg; | |
2090 | tg->funcs->get_otg_active_size(tg, &width, &height); | |
2091 | if (opp->funcs->opp_program_dpg_dimensions) | |
2092 | opp->funcs->opp_program_dpg_dimensions(opp, width, height); | |
2093 | } | |
2094 | } | |
2095 | ||
78c77382 | 2096 | void dcn10_enable_timing_synchronization( |
fb3466a4 | 2097 | struct dc *dc, |
70ccab60 HW |
2098 | int group_index, |
2099 | int group_size, | |
2100 | struct pipe_ctx *grouped_pipes[]) | |
2101 | { | |
2102 | struct dc_context *dc_ctx = dc->ctx; | |
a47cc3ab TH |
2103 | struct output_pixel_processor *opp; |
2104 | struct timing_generator *tg; | |
2105 | int i, width, height; | |
70ccab60 HW |
2106 | |
2107 | DC_SYNC_INFO("Setting up OTG reset trigger\n"); | |
2108 | ||
a47cc3ab TH |
2109 | for (i = 1; i < group_size; i++) { |
2110 | opp = grouped_pipes[i]->stream_res.opp; | |
2111 | tg = grouped_pipes[i]->stream_res.tg; | |
2112 | tg->funcs->get_otg_active_size(tg, &width, &height); | |
2113 | if (opp->funcs->opp_program_dpg_dimensions) | |
2114 | opp->funcs->opp_program_dpg_dimensions(opp, width, 2*(height) + 1); | |
2115 | } | |
2116 | ||
77a2b726 VS |
2117 | for (i = 0; i < group_size; i++) { |
2118 | if (grouped_pipes[i]->stream == NULL) | |
2119 | continue; | |
2120 | grouped_pipes[i]->stream->vblank_synchronized = false; | |
2121 | } | |
2122 | ||
70ccab60 | 2123 | for (i = 1; i < group_size; i++) |
6b670fa9 | 2124 | grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger( |
fa2123db ML |
2125 | grouped_pipes[i]->stream_res.tg, |
2126 | grouped_pipes[0]->stream_res.tg->inst); | |
70ccab60 HW |
2127 | |
2128 | DC_SYNC_INFO("Waiting for trigger\n"); | |
2129 | ||
2130 | /* Need to get only check 1 pipe for having reset as all the others are | |
2131 | * synchronized. Look at last pipe programmed to reset. | |
2132 | */ | |
fa2123db | 2133 | |
6b670fa9 | 2134 | wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[1]->stream_res.tg); |
70ccab60 | 2135 | for (i = 1; i < group_size; i++) |
6b670fa9 HW |
2136 | grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger( |
2137 | grouped_pipes[i]->stream_res.tg); | |
70ccab60 | 2138 | |
a47cc3ab TH |
2139 | for (i = 1; i < group_size; i++) { |
2140 | opp = grouped_pipes[i]->stream_res.opp; | |
2141 | tg = grouped_pipes[i]->stream_res.tg; | |
2142 | tg->funcs->get_otg_active_size(tg, &width, &height); | |
2143 | if (opp->funcs->opp_program_dpg_dimensions) | |
2144 | opp->funcs->opp_program_dpg_dimensions(opp, width, height); | |
2145 | } | |
2146 | ||
70ccab60 HW |
2147 | DC_SYNC_INFO("Sync complete\n"); |
2148 | } | |
2149 | ||
78c77382 | 2150 | void dcn10_enable_per_frame_crtc_position_reset( |
fa2123db ML |
2151 | struct dc *dc, |
2152 | int group_size, | |
2153 | struct pipe_ctx *grouped_pipes[]) | |
2154 | { | |
2155 | struct dc_context *dc_ctx = dc->ctx; | |
2156 | int i; | |
2157 | ||
2158 | DC_SYNC_INFO("Setting up\n"); | |
2159 | for (i = 0; i < group_size; i++) | |
7f93c1de CL |
2160 | if (grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset) |
2161 | grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset( | |
2162 | grouped_pipes[i]->stream_res.tg, | |
37cd85ce | 2163 | 0, |
7f93c1de | 2164 | &grouped_pipes[i]->stream->triggered_crtc_reset); |
fa2123db ML |
2165 | |
2166 | DC_SYNC_INFO("Waiting for trigger\n"); | |
2167 | ||
03736f4c | 2168 | for (i = 0; i < group_size; i++) |
fa2123db ML |
2169 | wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg); |
2170 | ||
2171 | DC_SYNC_INFO("Multi-display sync is complete\n"); | |
2172 | } | |
2173 | ||
a4e6d14e YS |
2174 | static void mmhub_read_vm_system_aperture_settings(struct dcn10_hubp *hubp1, |
2175 | struct vm_system_aperture_param *apt, | |
2176 | struct dce_hwseq *hws) | |
2177 | { | |
2178 | PHYSICAL_ADDRESS_LOC physical_page_number; | |
2179 | uint32_t logical_addr_low; | |
2180 | uint32_t logical_addr_high; | |
2181 | ||
2182 | REG_GET(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB, | |
2183 | PHYSICAL_PAGE_NUMBER_MSB, &physical_page_number.high_part); | |
2184 | REG_GET(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB, | |
2185 | PHYSICAL_PAGE_NUMBER_LSB, &physical_page_number.low_part); | |
2186 | ||
2187 | REG_GET(MC_VM_SYSTEM_APERTURE_LOW_ADDR, | |
2188 | LOGICAL_ADDR, &logical_addr_low); | |
2189 | ||
2190 | REG_GET(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, | |
2191 | LOGICAL_ADDR, &logical_addr_high); | |
2192 | ||
2193 | apt->sys_default.quad_part = physical_page_number.quad_part << 12; | |
2194 | apt->sys_low.quad_part = (int64_t)logical_addr_low << 18; | |
2195 | apt->sys_high.quad_part = (int64_t)logical_addr_high << 18; | |
2196 | } | |
2197 | ||
2198 | /* Temporary read settings, future will get values from kmd directly */ | |
2199 | static void mmhub_read_vm_context0_settings(struct dcn10_hubp *hubp1, | |
2200 | struct vm_context0_param *vm0, | |
2201 | struct dce_hwseq *hws) | |
2202 | { | |
2203 | PHYSICAL_ADDRESS_LOC fb_base; | |
2204 | PHYSICAL_ADDRESS_LOC fb_offset; | |
2205 | uint32_t fb_base_value; | |
2206 | uint32_t fb_offset_value; | |
2207 | ||
2208 | REG_GET(DCHUBBUB_SDPIF_FB_BASE, SDPIF_FB_BASE, &fb_base_value); | |
2209 | REG_GET(DCHUBBUB_SDPIF_FB_OFFSET, SDPIF_FB_OFFSET, &fb_offset_value); | |
2210 | ||
2211 | REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32, | |
2212 | PAGE_DIRECTORY_ENTRY_HI32, &vm0->pte_base.high_part); | |
2213 | REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32, | |
2214 | PAGE_DIRECTORY_ENTRY_LO32, &vm0->pte_base.low_part); | |
2215 | ||
2216 | REG_GET(VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32, | |
2217 | LOGICAL_PAGE_NUMBER_HI4, &vm0->pte_start.high_part); | |
2218 | REG_GET(VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32, | |
2219 | LOGICAL_PAGE_NUMBER_LO32, &vm0->pte_start.low_part); | |
2220 | ||
2221 | REG_GET(VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32, | |
2222 | LOGICAL_PAGE_NUMBER_HI4, &vm0->pte_end.high_part); | |
2223 | REG_GET(VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32, | |
2224 | LOGICAL_PAGE_NUMBER_LO32, &vm0->pte_end.low_part); | |
2225 | ||
2226 | REG_GET(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32, | |
2227 | PHYSICAL_PAGE_ADDR_HI4, &vm0->fault_default.high_part); | |
2228 | REG_GET(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32, | |
2229 | PHYSICAL_PAGE_ADDR_LO32, &vm0->fault_default.low_part); | |
2230 | ||
2231 | /* | |
2232 | * The values in VM_CONTEXT0_PAGE_TABLE_BASE_ADDR is in UMA space. | |
2233 | * Therefore we need to do | |
2234 | * DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR | |
2235 | * - DCHUBBUB_SDPIF_FB_OFFSET + DCHUBBUB_SDPIF_FB_BASE | |
2236 | */ | |
2237 | fb_base.quad_part = (uint64_t)fb_base_value << 24; | |
2238 | fb_offset.quad_part = (uint64_t)fb_offset_value << 24; | |
2239 | vm0->pte_base.quad_part += fb_base.quad_part; | |
2240 | vm0->pte_base.quad_part -= fb_offset.quad_part; | |
2241 | } | |
2242 | ||
2243 | ||
560a77f5 | 2244 | void dcn10_program_pte_vm(struct dce_hwseq *hws, struct hubp *hubp) |
a4e6d14e YS |
2245 | { |
2246 | struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); | |
2247 | struct vm_system_aperture_param apt = { {{ 0 } } }; | |
2248 | struct vm_context0_param vm0 = { { { 0 } } }; | |
2249 | ||
2250 | mmhub_read_vm_system_aperture_settings(hubp1, &apt, hws); | |
2251 | mmhub_read_vm_context0_settings(hubp1, &vm0, hws); | |
2252 | ||
2253 | hubp->funcs->hubp_set_vm_system_aperture_settings(hubp, &apt); | |
2254 | hubp->funcs->hubp_set_vm_context0_settings(hubp, &vm0); | |
2255 | } | |
2256 | ||
7f914a62 | 2257 | static void dcn10_enable_plane( |
fb3466a4 | 2258 | struct dc *dc, |
70ccab60 | 2259 | struct pipe_ctx *pipe_ctx, |
608ac7bb | 2260 | struct dc_state *context) |
70ccab60 | 2261 | { |
184debdb | 2262 | struct dce_hwseq *hws = dc->hwseq; |
70ccab60 | 2263 | |
fb3466a4 | 2264 | if (dc->debug.sanity_checks) { |
f42ea55b | 2265 | hws->funcs.verify_allow_pstate_change_high(dc); |
665da60f CM |
2266 | } |
2267 | ||
7f914a62 YS |
2268 | undo_DEGVIDCN10_253_wa(dc); |
2269 | ||
184debdb | 2270 | power_on_plane(dc->hwseq, |
e07f541f | 2271 | pipe_ctx->plane_res.hubp->inst); |
70ccab60 | 2272 | |
c9742685 | 2273 | /* enable DCFCLK current DCHUB */ |
c8242b98 | 2274 | pipe_ctx->plane_res.hubp->funcs->hubp_clk_cntl(pipe_ctx->plane_res.hubp, true); |
d21becbe TC |
2275 | |
2276 | /* make sure OPP_PIPE_CLOCK_EN = 1 */ | |
be2f449a YS |
2277 | pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control( |
2278 | pipe_ctx->stream_res.opp, | |
2279 | true); | |
c9742685 | 2280 | |
a4e6d14e YS |
2281 | if (dc->config.gpu_vm_support) |
2282 | dcn10_program_pte_vm(hws, pipe_ctx->plane_res.hubp); | |
665da60f | 2283 | |
fb3466a4 | 2284 | if (dc->debug.sanity_checks) { |
f42ea55b | 2285 | hws->funcs.verify_allow_pstate_change_high(dc); |
665da60f | 2286 | } |
0c66824b QZ |
2287 | |
2288 | if (!pipe_ctx->top_pipe | |
2289 | && pipe_ctx->plane_state | |
2290 | && pipe_ctx->plane_state->flip_int_enabled | |
2291 | && pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_int) | |
2292 | pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_int(pipe_ctx->plane_res.hubp); | |
2293 | ||
70ccab60 HW |
2294 | } |
2295 | ||
78c77382 | 2296 | void dcn10_program_gamut_remap(struct pipe_ctx *pipe_ctx) |
70ccab60 | 2297 | { |
146a9f63 | 2298 | int i = 0; |
d94585a0 | 2299 | struct dpp_grph_csc_adjustment adjust; |
70ccab60 HW |
2300 | memset(&adjust, 0, sizeof(adjust)); |
2301 | adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; | |
2302 | ||
2303 | ||
4fa086b9 | 2304 | if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { |
70ccab60 | 2305 | adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; |
146a9f63 KK |
2306 | for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) |
2307 | adjust.temperature_matrix[i] = | |
2308 | pipe_ctx->stream->gamut_remap_matrix.matrix[i]; | |
5c41c023 SW |
2309 | } else if (pipe_ctx->plane_state && |
2310 | pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) { | |
2311 | adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; | |
2312 | for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) | |
2313 | adjust.temperature_matrix[i] = | |
2314 | pipe_ctx->plane_state->gamut_remap_matrix.matrix[i]; | |
70ccab60 HW |
2315 | } |
2316 | ||
d94585a0 | 2317 | pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp, &adjust); |
70ccab60 HW |
2318 | } |
2319 | ||
6d83a32d MS |
2320 | |
2321 | static bool dcn10_is_rear_mpo_fix_required(struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace) | |
2322 | { | |
2323 | if (pipe_ctx->plane_state && pipe_ctx->plane_state->layer_index > 0 && is_rgb_cspace(colorspace)) { | |
2324 | if (pipe_ctx->top_pipe) { | |
2325 | struct pipe_ctx *top = pipe_ctx->top_pipe; | |
2326 | ||
2327 | while (top->top_pipe) | |
2328 | top = top->top_pipe; // Traverse to top pipe_ctx | |
2329 | if (top->plane_state && top->plane_state->layer_index == 0) | |
2330 | return true; // Front MPO plane not hidden | |
2331 | } | |
2332 | } | |
2333 | return false; | |
2334 | } | |
2335 | ||
2336 | static void dcn10_set_csc_adjustment_rgb_mpo_fix(struct pipe_ctx *pipe_ctx, uint16_t *matrix) | |
2337 | { | |
2338 | // Override rear plane RGB bias to fix MPO brightness | |
2339 | uint16_t rgb_bias = matrix[3]; | |
2340 | ||
2341 | matrix[3] = 0; | |
2342 | matrix[7] = 0; | |
2343 | matrix[11] = 0; | |
2344 | pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment(pipe_ctx->plane_res.dpp, matrix); | |
2345 | matrix[3] = rgb_bias; | |
2346 | matrix[7] = rgb_bias; | |
2347 | matrix[11] = rgb_bias; | |
2348 | } | |
2349 | ||
78c77382 | 2350 | void dcn10_program_output_csc(struct dc *dc, |
3917a470 | 2351 | struct pipe_ctx *pipe_ctx, |
abe07e80 | 2352 | enum dc_color_space colorspace, |
3917a470 KK |
2353 | uint16_t *matrix, |
2354 | int opp_id) | |
abe07e80 | 2355 | { |
56ef6ed9 | 2356 | if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) { |
6d83a32d MS |
2357 | if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment != NULL) { |
2358 | ||
2359 | /* MPO is broken with RGB colorspaces when OCSC matrix | |
2360 | * brightness offset >= 0 on DCN1 due to OCSC before MPC | |
2361 | * Blending adds offsets from front + rear to rear plane | |
2362 | * | |
2363 | * Fix is to set RGB bias to 0 on rear plane, top plane | |
2364 | * black value pixels add offset instead of rear + front | |
2365 | */ | |
2366 | ||
2367 | int16_t rgb_bias = matrix[3]; | |
2368 | // matrix[3/7/11] are all the same offset value | |
2369 | ||
2370 | if (rgb_bias > 0 && dcn10_is_rear_mpo_fix_required(pipe_ctx, colorspace)) { | |
2371 | dcn10_set_csc_adjustment_rgb_mpo_fix(pipe_ctx, matrix); | |
2372 | } else { | |
2373 | pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment(pipe_ctx->plane_res.dpp, matrix); | |
2374 | } | |
2375 | } | |
ea826d64 | 2376 | } else { |
afbeb263 YHL |
2377 | if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_default != NULL) |
2378 | pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_default(pipe_ctx->plane_res.dpp, colorspace); | |
abe07e80 YHL |
2379 | } |
2380 | } | |
de4a2967 | 2381 | |
78c77382 | 2382 | static void dcn10_update_dpp(struct dpp *dpp, struct dc_plane_state *plane_state) |
70ccab60 | 2383 | { |
de4a2967 | 2384 | struct dc_bias_and_scale bns_params = {0}; |
cbfd33fd | 2385 | |
de4a2967 | 2386 | // program the input csc |
5fa2ec4f | 2387 | dpp->funcs->dpp_setup(dpp, |
3be5262e | 2388 | plane_state->format, |
de4a2967 S |
2389 | EXPANSION_MODE_ZERO, |
2390 | plane_state->input_csc_color_matrix, | |
7ed4e635 HW |
2391 | plane_state->color_space, |
2392 | NULL); | |
de4a2967 S |
2393 | |
2394 | //set scale and bias registers | |
78c77382 | 2395 | build_prescale_params(&bns_params, plane_state); |
5fa2ec4f YHL |
2396 | if (dpp->funcs->dpp_program_bias_and_scale) |
2397 | dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params); | |
480bd0cf YS |
2398 | } |
2399 | ||
60df8441 WW |
2400 | void dcn10_update_visual_confirm_color(struct dc *dc, struct pipe_ctx *pipe_ctx, struct tg_color *color, int mpcc_id) |
2401 | { | |
2402 | struct mpc *mpc = dc->res_pool->mpc; | |
2403 | ||
2404 | if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR) | |
2405 | get_hdr_visual_confirm_color(pipe_ctx, color); | |
2406 | else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) | |
2407 | get_surface_visual_confirm_color(pipe_ctx, color); | |
793c82ee PTC |
2408 | else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SWIZZLE) |
2409 | get_surface_tile_visual_confirm_color(pipe_ctx, color); | |
60df8441 WW |
2410 | else |
2411 | color_space_to_black_color( | |
2412 | dc, pipe_ctx->stream->output_color_space, color); | |
2413 | ||
2414 | if (mpc->funcs->set_bg_color) | |
2415 | mpc->funcs->set_bg_color(mpc, color, mpcc_id); | |
2416 | } | |
2417 | ||
3ca40237 WW |
2418 | void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) |
2419 | { | |
480bd0cf | 2420 | struct hubp *hubp = pipe_ctx->plane_res.hubp; |
35fb7220 | 2421 | struct mpcc_blnd_cfg blnd_cfg = {{0}}; |
feb4a3cd EB |
2422 | bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; |
2423 | int mpcc_id; | |
2424 | struct mpcc *new_mpcc; | |
2425 | struct mpc *mpc = dc->res_pool->mpc; | |
2426 | struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params); | |
480bd0cf | 2427 | |
feb4a3cd | 2428 | blnd_cfg.overlap_only = false; |
feb4a3cd EB |
2429 | blnd_cfg.global_gain = 0xff; |
2430 | ||
978f203d MW |
2431 | if (per_pixel_alpha && pipe_ctx->plane_state->global_alpha) { |
2432 | blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN; | |
2433 | blnd_cfg.global_gain = pipe_ctx->plane_state->global_alpha_value; | |
2434 | } else if (per_pixel_alpha) { | |
2435 | blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; | |
2436 | } else { | |
2437 | blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; | |
2438 | } | |
2439 | ||
94a4ffd1 GL |
2440 | if (pipe_ctx->plane_state->global_alpha) |
2441 | blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value; | |
2442 | else | |
2443 | blnd_cfg.global_alpha = 0xff; | |
2444 | ||
ad327346 DL |
2445 | /* DCN1.0 has output CM before MPC which seems to screw with |
2446 | * pre-multiplied alpha. | |
2447 | */ | |
feb4a3cd | 2448 | blnd_cfg.pre_multiplied_alpha = is_rgb_cspace( |
4fa086b9 | 2449 | pipe_ctx->stream->output_color_space) |
ad327346 | 2450 | && per_pixel_alpha; |
feb4a3cd | 2451 | |
4173c0bd | 2452 | |
feb4a3cd EB |
2453 | /* |
2454 | * TODO: remove hack | |
2455 | * Note: currently there is a bug in init_hw such that | |
2456 | * on resume from hibernate, BIOS sets up MPCC0, and | |
2457 | * we do mpcc_remove but the mpcc cannot go to idle | |
2458 | * after remove. This cause us to pick mpcc1 here, | |
2459 | * which causes a pstate hang for yet unknown reason. | |
2460 | */ | |
2461 | mpcc_id = hubp->inst; | |
2462 | ||
4173c0bd EY |
2463 | /* If there is no full update, don't need to touch MPC tree*/ |
2464 | if (!pipe_ctx->plane_state->update_flags.bits.full_update) { | |
2465 | mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); | |
60df8441 | 2466 | dc->hwss.update_visual_confirm_color(dc, pipe_ctx, &blnd_cfg.black_color, mpcc_id); |
4173c0bd EY |
2467 | return; |
2468 | } | |
2469 | ||
feb4a3cd EB |
2470 | /* check if this MPCC is already being used */ |
2471 | new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id); | |
2472 | /* remove MPCC if being used */ | |
2473 | if (new_mpcc != NULL) | |
2474 | mpc->funcs->remove_mpcc(mpc, mpc_tree_params, new_mpcc); | |
49fac0b4 EB |
2475 | else |
2476 | if (dc->debug.sanity_checks) | |
2477 | mpc->funcs->assert_mpcc_idle_before_connect( | |
2478 | dc->res_pool->mpc, mpcc_id); | |
feb4a3cd EB |
2479 | |
2480 | /* Call MPC to insert new plane */ | |
2481 | new_mpcc = mpc->funcs->insert_plane(dc->res_pool->mpc, | |
2482 | mpc_tree_params, | |
2483 | &blnd_cfg, | |
2484 | NULL, | |
2485 | NULL, | |
2486 | hubp->inst, | |
2487 | mpcc_id); | |
60df8441 | 2488 | dc->hwss.update_visual_confirm_color(dc, pipe_ctx, &blnd_cfg.black_color, mpcc_id); |
feb4a3cd EB |
2489 | |
2490 | ASSERT(new_mpcc != NULL); | |
2491 | ||
2492 | hubp->opp_id = pipe_ctx->stream_res.opp->inst; | |
2493 | hubp->mpcc_id = mpcc_id; | |
480bd0cf YS |
2494 | } |
2495 | ||
2496 | static void update_scaler(struct pipe_ctx *pipe_ctx) | |
2497 | { | |
2498 | bool per_pixel_alpha = | |
2499 | pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; | |
2500 | ||
6702a9ac | 2501 | pipe_ctx->plane_res.scl_data.lb_params.alpha_en = per_pixel_alpha; |
a316db72 | 2502 | pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_36BPP; |
70ccab60 | 2503 | /* scaler configuration */ |
d94585a0 YHL |
2504 | pipe_ctx->plane_res.dpp->funcs->dpp_set_scaler( |
2505 | pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data); | |
480bd0cf YS |
2506 | } |
2507 | ||
78c77382 | 2508 | static void dcn10_update_dchubp_dpp( |
480bd0cf YS |
2509 | struct dc *dc, |
2510 | struct pipe_ctx *pipe_ctx, | |
2511 | struct dc_state *context) | |
2512 | { | |
f42ea55b | 2513 | struct dce_hwseq *hws = dc->hwseq; |
480bd0cf YS |
2514 | struct hubp *hubp = pipe_ctx->plane_res.hubp; |
2515 | struct dpp *dpp = pipe_ctx->plane_res.dpp; | |
2516 | struct dc_plane_state *plane_state = pipe_ctx->plane_state; | |
12e2b2d4 | 2517 | struct plane_size size = plane_state->plane_size; |
a465feae | 2518 | unsigned int compat_level = 0; |
1f9ce3cf | 2519 | bool should_divided_by_2 = false; |
480bd0cf YS |
2520 | |
2521 | /* depends on DML calculation, DPP clock value may change dynamically */ | |
45bb8dd6 YS |
2522 | /* If request max dpp clk is lower than current dispclk, no need to |
2523 | * divided by 2 | |
2524 | */ | |
16a29dd3 | 2525 | if (plane_state->update_flags.bits.full_update) { |
1f9ce3cf | 2526 | |
2527 | /* new calculated dispclk, dppclk are stored in | |
2528 | * context->bw_ctx.bw.dcn.clk.dispclk_khz / dppclk_khz. current | |
2529 | * dispclk, dppclk are from dc->clk_mgr->clks.dispclk_khz. | |
2530 | * dcn_validate_bandwidth compute new dispclk, dppclk. | |
2531 | * dispclk will put in use after optimize_bandwidth when | |
2532 | * ramp_up_dispclk_with_dpp is called. | |
2533 | * there are two places for dppclk be put in use. One location | |
2534 | * is the same as the location as dispclk. Another is within | |
2535 | * update_dchubp_dpp which happens between pre_bandwidth and | |
2536 | * optimize_bandwidth. | |
2537 | * dppclk updated within update_dchubp_dpp will cause new | |
2538 | * clock values of dispclk and dppclk not be in use at the same | |
2539 | * time. when clocks are decreased, this may cause dppclk is | |
2540 | * lower than previous configuration and let pipe stuck. | |
2541 | * for example, eDP + external dp, change resolution of DP from | |
2542 | * 1920x1080x144hz to 1280x960x60hz. | |
2543 | * before change: dispclk = 337889 dppclk = 337889 | |
2544 | * change mode, dcn_validate_bandwidth calculate | |
2545 | * dispclk = 143122 dppclk = 143122 | |
2546 | * update_dchubp_dpp be executed before dispclk be updated, | |
2547 | * dispclk = 337889, but dppclk use new value dispclk /2 = | |
2548 | * 168944. this will cause pipe pstate warning issue. | |
2549 | * solution: between pre_bandwidth and optimize_bandwidth, while | |
2550 | * dispclk is going to be decreased, keep dppclk = dispclk | |
2551 | **/ | |
2552 | if (context->bw_ctx.bw.dcn.clk.dispclk_khz < | |
2553 | dc->clk_mgr->clks.dispclk_khz) | |
2554 | should_divided_by_2 = false; | |
2555 | else | |
2556 | should_divided_by_2 = | |
2557 | context->bw_ctx.bw.dcn.clk.dppclk_khz <= | |
2558 | dc->clk_mgr->clks.dispclk_khz / 2; | |
45bb8dd6 | 2559 | |
f8e413bf YS |
2560 | dpp->funcs->dpp_dppclk_control( |
2561 | dpp, | |
45bb8dd6 | 2562 | should_divided_by_2, |
f8e413bf YS |
2563 | true); |
2564 | ||
ea2e8d92 DL |
2565 | if (dc->res_pool->dccg) |
2566 | dc->res_pool->dccg->funcs->update_dpp_dto( | |
2567 | dc->res_pool->dccg, | |
2568 | dpp->inst, | |
799c5b9c | 2569 | pipe_ctx->plane_res.bw.dppclk_khz); |
ea2e8d92 | 2570 | else |
dc88b4a6 EY |
2571 | dc->clk_mgr->clks.dppclk_khz = should_divided_by_2 ? |
2572 | dc->clk_mgr->clks.dispclk_khz / 2 : | |
2573 | dc->clk_mgr->clks.dispclk_khz; | |
480bd0cf YS |
2574 | } |
2575 | ||
2576 | /* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG | |
2577 | * VTG is within DCHUBBUB which is commond block share by each pipe HUBP. | |
2578 | * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG | |
2579 | */ | |
2580 | if (plane_state->update_flags.bits.full_update) { | |
c8242b98 | 2581 | hubp->funcs->hubp_vtg_sel(hubp, pipe_ctx->stream_res.tg->inst); |
d94585a0 | 2582 | |
480bd0cf YS |
2583 | hubp->funcs->hubp_setup( |
2584 | hubp, | |
2585 | &pipe_ctx->dlg_regs, | |
2586 | &pipe_ctx->ttu_regs, | |
2587 | &pipe_ctx->rq_regs, | |
2588 | &pipe_ctx->pipe_dlg_param); | |
1a1adf17 DL |
2589 | hubp->funcs->hubp_setup_interdependent( |
2590 | hubp, | |
2591 | &pipe_ctx->dlg_regs, | |
2592 | &pipe_ctx->ttu_regs); | |
480bd0cf | 2593 | } |
70ccab60 | 2594 | |
12e2b2d4 | 2595 | size.surface_size = pipe_ctx->plane_res.scl_data.viewport; |
70ccab60 | 2596 | |
480bd0cf YS |
2597 | if (plane_state->update_flags.bits.full_update || |
2598 | plane_state->update_flags.bits.bpp_change) | |
78c77382 | 2599 | dcn10_update_dpp(dpp, plane_state); |
480bd0cf YS |
2600 | |
2601 | if (plane_state->update_flags.bits.full_update || | |
94a4ffd1 GL |
2602 | plane_state->update_flags.bits.per_pixel_alpha_change || |
2603 | plane_state->update_flags.bits.global_alpha_change) | |
f42ea55b | 2604 | hws->funcs.update_mpcc(dc, pipe_ctx); |
480bd0cf YS |
2605 | |
2606 | if (plane_state->update_flags.bits.full_update || | |
2607 | plane_state->update_flags.bits.per_pixel_alpha_change || | |
94a4ffd1 | 2608 | plane_state->update_flags.bits.global_alpha_change || |
480bd0cf YS |
2609 | plane_state->update_flags.bits.scaling_change || |
2610 | plane_state->update_flags.bits.position_change) { | |
2611 | update_scaler(pipe_ctx); | |
2612 | } | |
2613 | ||
2614 | if (plane_state->update_flags.bits.full_update || | |
16a29dd3 AJ |
2615 | plane_state->update_flags.bits.scaling_change || |
2616 | plane_state->update_flags.bits.position_change) { | |
480bd0cf YS |
2617 | hubp->funcs->mem_program_viewport( |
2618 | hubp, | |
2619 | &pipe_ctx->plane_res.scl_data.viewport, | |
cf27a6d1 | 2620 | &pipe_ctx->plane_res.scl_data.viewport_c); |
480bd0cf YS |
2621 | } |
2622 | ||
33fd17d9 EY |
2623 | if (pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { |
2624 | dc->hwss.set_cursor_position(pipe_ctx); | |
2625 | dc->hwss.set_cursor_attribute(pipe_ctx); | |
86eb426a S |
2626 | |
2627 | if (dc->hwss.set_cursor_sdr_white_level) | |
2628 | dc->hwss.set_cursor_sdr_white_level(pipe_ctx); | |
33fd17d9 EY |
2629 | } |
2630 | ||
480bd0cf YS |
2631 | if (plane_state->update_flags.bits.full_update) { |
2632 | /*gamut remap*/ | |
8a31820b | 2633 | dc->hwss.program_gamut_remap(pipe_ctx); |
480bd0cf | 2634 | |
8e357610 | 2635 | dc->hwss.program_output_csc(dc, |
480bd0cf YS |
2636 | pipe_ctx, |
2637 | pipe_ctx->stream->output_color_space, | |
2638 | pipe_ctx->stream->csc_color_matrix.matrix, | |
c0826487 | 2639 | pipe_ctx->stream_res.opp->inst); |
480bd0cf YS |
2640 | } |
2641 | ||
2642 | if (plane_state->update_flags.bits.full_update || | |
3aa0cadd | 2643 | plane_state->update_flags.bits.pixel_format_change || |
480bd0cf YS |
2644 | plane_state->update_flags.bits.horizontal_mirror_change || |
2645 | plane_state->update_flags.bits.rotation_change || | |
2646 | plane_state->update_flags.bits.swizzle_change || | |
e9dd9223 | 2647 | plane_state->update_flags.bits.dcc_change || |
f8668c09 | 2648 | plane_state->update_flags.bits.bpp_change || |
ebd084cd LH |
2649 | plane_state->update_flags.bits.scaling_change || |
2650 | plane_state->update_flags.bits.plane_size_change) { | |
480bd0cf YS |
2651 | hubp->funcs->hubp_program_surface_config( |
2652 | hubp, | |
2653 | plane_state->format, | |
2654 | &plane_state->tiling_info, | |
2655 | &size, | |
2656 | plane_state->rotation, | |
2657 | &plane_state->dcc, | |
a465feae CL |
2658 | plane_state->horizontal_mirror, |
2659 | compat_level); | |
480bd0cf | 2660 | } |
70ccab60 | 2661 | |
7f914a62 YS |
2662 | hubp->power_gated = false; |
2663 | ||
f42ea55b | 2664 | hws->funcs.update_plane_addr(dc, pipe_ctx); |
50d4cfdc | 2665 | |
8eb5def9 | 2666 | if (is_pipe_tree_visible(pipe_ctx)) |
0b7421f0 | 2667 | hubp->funcs->set_blank(hubp, false); |
70ccab60 HW |
2668 | } |
2669 | ||
78c77382 | 2670 | void dcn10_blank_pixel_data( |
9aef1a31 | 2671 | struct dc *dc, |
ea4a2020 | 2672 | struct pipe_ctx *pipe_ctx, |
aa5a5777 S |
2673 | bool blank) |
2674 | { | |
9aef1a31 S |
2675 | enum dc_color_space color_space; |
2676 | struct tg_color black_color = {0}; | |
ea4a2020 EB |
2677 | struct stream_resource *stream_res = &pipe_ctx->stream_res; |
2678 | struct dc_stream_state *stream = pipe_ctx->stream; | |
9aef1a31 S |
2679 | |
2680 | /* program otg blank color */ | |
2681 | color_space = stream->output_color_space; | |
2682 | color_space_to_black_color(dc, color_space, &black_color); | |
2683 | ||
12750d16 EY |
2684 | /* |
2685 | * The way 420 is packed, 2 channels carry Y component, 1 channel | |
2686 | * alternate between Cb and Cr, so both channels need the pixel | |
2687 | * value for Y | |
2688 | */ | |
2689 | if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
2690 | black_color.color_r_cr = black_color.color_g_y; | |
2691 | ||
2692 | ||
3158223e EB |
2693 | if (stream_res->tg->funcs->set_blank_color) |
2694 | stream_res->tg->funcs->set_blank_color( | |
2695 | stream_res->tg, | |
9aef1a31 | 2696 | &black_color); |
aa5a5777 S |
2697 | |
2698 | if (!blank) { | |
3158223e EB |
2699 | if (stream_res->tg->funcs->set_blank) |
2700 | stream_res->tg->funcs->set_blank(stream_res->tg, blank); | |
4dfdd0ee | 2701 | if (stream_res->abm) { |
474ac4a8 | 2702 | dc->hwss.set_pipe(pipe_ctx); |
3158223e | 2703 | stream_res->abm->funcs->set_abm_level(stream_res->abm, stream->abm_level); |
4dfdd0ee | 2704 | } |
aa5a5777 | 2705 | } else if (blank) { |
3ba01817 | 2706 | dc->hwss.set_abm_immediate_disable(pipe_ctx); |
0976ef31 PH |
2707 | if (stream_res->tg->funcs->set_blank) { |
2708 | stream_res->tg->funcs->wait_for_state(stream_res->tg, CRTC_STATE_VBLANK); | |
3158223e | 2709 | stream_res->tg->funcs->set_blank(stream_res->tg, blank); |
0976ef31 | 2710 | } |
aa5a5777 S |
2711 | } |
2712 | } | |
9d6f264b | 2713 | |
78c77382 | 2714 | void dcn10_set_hdr_multiplier(struct pipe_ctx *pipe_ctx) |
a4056c2a | 2715 | { |
46250a0c | 2716 | struct fixed31_32 multiplier = pipe_ctx->plane_state->hdr_mult; |
a4056c2a KK |
2717 | uint32_t hw_mult = 0x1f000; // 1.0 default multiplier |
2718 | struct custom_float_format fmt; | |
2719 | ||
2720 | fmt.exponenta_bits = 6; | |
2721 | fmt.mantissa_bits = 12; | |
2722 | fmt.sign = true; | |
2723 | ||
46250a0c | 2724 | |
186a1fb7 | 2725 | if (!dc_fixpt_eq(multiplier, dc_fixpt_from_int(0))) // check != 0 |
a4056c2a KK |
2726 | convert_to_custom_float_format(multiplier, &fmt, &hw_mult); |
2727 | ||
2728 | pipe_ctx->plane_res.dpp->funcs->dpp_set_hdr_multiplier( | |
2729 | pipe_ctx->plane_res.dpp, hw_mult); | |
2730 | } | |
2731 | ||
c2437b1f EB |
2732 | void dcn10_program_pipe( |
2733 | struct dc *dc, | |
2734 | struct pipe_ctx *pipe_ctx, | |
2735 | struct dc_state *context) | |
2736 | { | |
f42ea55b AK |
2737 | struct dce_hwseq *hws = dc->hwseq; |
2738 | ||
1a2c82a2 | 2739 | if (pipe_ctx->top_pipe == NULL) { |
aa5a5777 | 2740 | bool blank = !is_pipe_tree_visible(pipe_ctx); |
70ccab60 | 2741 | |
6b670fa9 | 2742 | pipe_ctx->stream_res.tg->funcs->program_global_sync( |
e7e10c46 DL |
2743 | pipe_ctx->stream_res.tg, |
2744 | pipe_ctx->pipe_dlg_param.vready_offset, | |
2745 | pipe_ctx->pipe_dlg_param.vstartup_start, | |
2746 | pipe_ctx->pipe_dlg_param.vupdate_offset, | |
2747 | pipe_ctx->pipe_dlg_param.vupdate_width); | |
b51adc77 | 2748 | |
6476a7c8 | 2749 | pipe_ctx->stream_res.tg->funcs->set_vtg_params( |
5200c401 | 2750 | pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, true); |
6476a7c8 | 2751 | |
f42ea55b AK |
2752 | if (hws->funcs.setup_vupdate_interrupt) |
2753 | hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx); | |
bf53769d | 2754 | |
f42ea55b | 2755 | hws->funcs.blank_pixel_data(dc, pipe_ctx, blank); |
1a2c82a2 | 2756 | } |
cbfd33fd | 2757 | |
7f63d8a1 PH |
2758 | if (pipe_ctx->plane_state->update_flags.bits.full_update) |
2759 | dcn10_enable_plane(dc, pipe_ctx, context); | |
e6c258cb | 2760 | |
7f63d8a1 | 2761 | dcn10_update_dchubp_dpp(dc, pipe_ctx, context); |
e6c258cb | 2762 | |
7f63d8a1 | 2763 | hws->funcs.set_hdr_multiplier(pipe_ctx); |
e6c258cb | 2764 | |
7f63d8a1 PH |
2765 | if (pipe_ctx->plane_state->update_flags.bits.full_update || |
2766 | pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || | |
2767 | pipe_ctx->plane_state->update_flags.bits.gamma_change) | |
2768 | hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); | |
e6c258cb | 2769 | |
7f63d8a1 PH |
2770 | /* dcn10_translate_regamma_to_hw_format takes 750us to finish |
2771 | * only do gamma programming for full update. | |
2772 | * TODO: This can be further optimized/cleaned up | |
2773 | * Always call this for now since it does memcmp inside before | |
2774 | * doing heavy calculation and programming | |
2775 | */ | |
2776 | if (pipe_ctx->plane_state->update_flags.bits.full_update) | |
2777 | hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); | |
e6c258cb YS |
2778 | } |
2779 | ||
81b437f5 AL |
2780 | void dcn10_wait_for_pending_cleared(struct dc *dc, |
2781 | struct dc_state *context) | |
2782 | { | |
2783 | struct pipe_ctx *pipe_ctx; | |
2784 | struct timing_generator *tg; | |
2785 | int i; | |
2786 | ||
2787 | for (i = 0; i < dc->res_pool->pipe_count; i++) { | |
2788 | pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
2789 | tg = pipe_ctx->stream_res.tg; | |
2790 | ||
2791 | /* | |
2792 | * Only wait for top pipe's tg penindg bit | |
2793 | * Also skip if pipe is disabled. | |
2794 | */ | |
2795 | if (pipe_ctx->top_pipe || | |
2796 | !pipe_ctx->stream || !pipe_ctx->plane_state || | |
2797 | !tg->funcs->is_tg_enabled(tg)) | |
2798 | continue; | |
2799 | ||
2800 | /* | |
2801 | * Wait for VBLANK then VACTIVE to ensure we get VUPDATE. | |
2802 | * For some reason waiting for OTG_UPDATE_PENDING cleared | |
2803 | * seems to not trigger the update right away, and if we | |
2804 | * lock again before VUPDATE then we don't get a separated | |
2805 | * operation. | |
2806 | */ | |
2807 | pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); | |
2808 | pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); | |
2809 | } | |
2810 | } | |
2811 | ||
bbf5f6c3 AK |
2812 | void dcn10_post_unlock_program_front_end( |
2813 | struct dc *dc, | |
2814 | struct dc_state *context) | |
2815 | { | |
db70e2c1 | 2816 | int i; |
bbf5f6c3 AK |
2817 | |
2818 | DC_LOGGER_INIT(dc->ctx->logger); | |
c7e557ab | 2819 | |
bbf5f6c3 AK |
2820 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
2821 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; | |
2822 | ||
2823 | if (!pipe_ctx->top_pipe && | |
2824 | !pipe_ctx->prev_odm_pipe && | |
2825 | pipe_ctx->stream) { | |
bbf5f6c3 AK |
2826 | struct timing_generator *tg = pipe_ctx->stream_res.tg; |
2827 | ||
bbf5f6c3 AK |
2828 | if (context->stream_status[i].plane_count == 0) |
2829 | false_optc_underflow_wa(dc, pipe_ctx->stream, tg); | |
2830 | } | |
2831 | } | |
5cc2687c | 2832 | |
e21db6db | 2833 | for (i = 0; i < dc->res_pool->pipe_count; i++) |
bbf5f6c3 | 2834 | if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable) |
8a31820b | 2835 | dc->hwss.disable_plane(dc, &dc->current_state->res_ctx.pipe_ctx[i]); |
7144d3cf | 2836 | |
08d238e6 | 2837 | for (i = 0; i < dc->res_pool->pipe_count; i++) |
bbf5f6c3 | 2838 | if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable) { |
08d238e6 ML |
2839 | dc->hwss.optimize_bandwidth(dc, context); |
2840 | break; | |
2841 | } | |
2842 | ||
e21db6db DL |
2843 | if (dc->hwseq->wa.DEGVIDCN10_254) |
2844 | hubbub1_wm_change_req_wa(dc->res_pool->hubbub); | |
70ccab60 HW |
2845 | } |
2846 | ||
b9d4b330 WC |
2847 | static void dcn10_stereo_hw_frame_pack_wa(struct dc *dc, struct dc_state *context) |
2848 | { | |
2849 | uint8_t i; | |
2850 | ||
2851 | for (i = 0; i < context->stream_count; i++) { | |
2852 | if (context->streams[i]->timing.timing_3d_format | |
2853 | == TIMING_3D_FORMAT_HW_FRAME_PACKING) { | |
2854 | /* | |
2855 | * Disable stutter | |
2856 | */ | |
2857 | hubbub1_allow_self_refresh_control(dc->res_pool->hubbub, false); | |
2858 | break; | |
2859 | } | |
2860 | } | |
2861 | } | |
2862 | ||
78c77382 | 2863 | void dcn10_prepare_bandwidth( |
fb3466a4 | 2864 | struct dc *dc, |
9566b675 | 2865 | struct dc_state *context) |
70ccab60 | 2866 | { |
f42ea55b | 2867 | struct dce_hwseq *hws = dc->hwseq; |
91f28756 YS |
2868 | struct hubbub *hubbub = dc->res_pool->hubbub; |
2869 | ||
c910a717 | 2870 | if (dc->debug.sanity_checks) |
f42ea55b | 2871 | hws->funcs.verify_allow_pstate_change_high(dc); |
665da60f | 2872 | |
e21db6db DL |
2873 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { |
2874 | if (context->stream_count == 0) | |
813d20dc | 2875 | context->bw_ctx.bw.dcn.clk.phyclk_khz = 0; |
70ccab60 | 2876 | |
dc88b4a6 EY |
2877 | dc->clk_mgr->funcs->update_clocks( |
2878 | dc->clk_mgr, | |
24f7dd7e | 2879 | context, |
9566b675 DL |
2880 | false); |
2881 | } | |
2882 | ||
89e94bc5 | 2883 | dc->wm_optimized_required = hubbub->funcs->program_watermarks(hubbub, |
813d20dc | 2884 | &context->bw_ctx.bw.dcn.watermarks, |
33d7598d | 2885 | dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, |
9566b675 | 2886 | true); |
b9d4b330 | 2887 | dcn10_stereo_hw_frame_pack_wa(dc, context); |
9566b675 DL |
2888 | |
2889 | if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) | |
2890 | dcn_bw_notify_pplib_of_wm_ranges(dc); | |
2891 | ||
2892 | if (dc->debug.sanity_checks) | |
f42ea55b | 2893 | hws->funcs.verify_allow_pstate_change_high(dc); |
9566b675 DL |
2894 | } |
2895 | ||
78c77382 | 2896 | void dcn10_optimize_bandwidth( |
9566b675 DL |
2897 | struct dc *dc, |
2898 | struct dc_state *context) | |
2899 | { | |
f42ea55b | 2900 | struct dce_hwseq *hws = dc->hwseq; |
91f28756 YS |
2901 | struct hubbub *hubbub = dc->res_pool->hubbub; |
2902 | ||
9566b675 | 2903 | if (dc->debug.sanity_checks) |
f42ea55b | 2904 | hws->funcs.verify_allow_pstate_change_high(dc); |
9566b675 DL |
2905 | |
2906 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { | |
4c631826 | 2907 | if (context->stream_count == 0) |
813d20dc | 2908 | context->bw_ctx.bw.dcn.clk.phyclk_khz = 0; |
9566b675 | 2909 | |
4c631826 YS |
2910 | dc->clk_mgr->funcs->update_clocks( |
2911 | dc->clk_mgr, | |
2912 | context, | |
9566b675 | 2913 | true); |
e21db6db | 2914 | } |
fab55d61 | 2915 | |
4c631826 YS |
2916 | hubbub->funcs->program_watermarks(hubbub, |
2917 | &context->bw_ctx.bw.dcn.watermarks, | |
2918 | dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, | |
2919 | true); | |
2920 | ||
b9d4b330 | 2921 | dcn10_stereo_hw_frame_pack_wa(dc, context); |
2b13d7d3 | 2922 | |
24f7dd7e DL |
2923 | if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) |
2924 | dcn_bw_notify_pplib_of_wm_ranges(dc); | |
2925 | ||
c910a717 | 2926 | if (dc->debug.sanity_checks) |
f42ea55b | 2927 | hws->funcs.verify_allow_pstate_change_high(dc); |
70ccab60 HW |
2928 | } |
2929 | ||
78c77382 | 2930 | void dcn10_set_drr(struct pipe_ctx **pipe_ctx, |
49c70ece | 2931 | int num_pipes, struct dc_crtc_timing_adjust adjust) |
70ccab60 HW |
2932 | { |
2933 | int i = 0; | |
2934 | struct drr_params params = {0}; | |
e63e2491 EB |
2935 | // DRR set trigger event mapped to OTG_TRIG_A (bit 11) for manual control flow |
2936 | unsigned int event_triggers = 0x800; | |
5b5abe95 AK |
2937 | // Note DRR trigger events are generated regardless of whether num frames met. |
2938 | unsigned int num_frames = 2; | |
70ccab60 | 2939 | |
49c70ece AL |
2940 | params.vertical_total_max = adjust.v_total_max; |
2941 | params.vertical_total_min = adjust.v_total_min; | |
2942 | params.vertical_total_mid = adjust.v_total_mid; | |
2943 | params.vertical_total_mid_frame_num = adjust.v_total_mid_frame_num; | |
70ccab60 | 2944 | /* TODO: If multiple pipes are to be supported, you need |
98e6436d AK |
2945 | * some GSL stuff. Static screen triggers may be programmed differently |
2946 | * as well. | |
70ccab60 HW |
2947 | */ |
2948 | for (i = 0; i < num_pipes; i++) { | |
98e6436d AK |
2949 | pipe_ctx[i]->stream_res.tg->funcs->set_drr( |
2950 | pipe_ctx[i]->stream_res.tg, ¶ms); | |
49c70ece | 2951 | if (adjust.v_total_max != 0 && adjust.v_total_min != 0) |
98e6436d AK |
2952 | pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control( |
2953 | pipe_ctx[i]->stream_res.tg, | |
5b5abe95 | 2954 | event_triggers, num_frames); |
70ccab60 HW |
2955 | } |
2956 | } | |
2957 | ||
78c77382 | 2958 | void dcn10_get_position(struct pipe_ctx **pipe_ctx, |
70ccab60 HW |
2959 | int num_pipes, |
2960 | struct crtc_position *position) | |
2961 | { | |
2962 | int i = 0; | |
2963 | ||
2964 | /* TODO: handle pipes > 1 | |
2965 | */ | |
2966 | for (i = 0; i < num_pipes; i++) | |
6b670fa9 | 2967 | pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position); |
70ccab60 HW |
2968 | } |
2969 | ||
78c77382 | 2970 | void dcn10_set_static_screen_control(struct pipe_ctx **pipe_ctx, |
5b5abe95 | 2971 | int num_pipes, const struct dc_static_screen_params *params) |
70ccab60 HW |
2972 | { |
2973 | unsigned int i; | |
5b5abe95 | 2974 | unsigned int triggers = 0; |
70ccab60 | 2975 | |
5b5abe95 AK |
2976 | if (params->triggers.surface_update) |
2977 | triggers |= 0x80; | |
2978 | if (params->triggers.cursor_update) | |
2979 | triggers |= 0x2; | |
2980 | if (params->triggers.force_trigger) | |
2981 | triggers |= 0x1; | |
70ccab60 HW |
2982 | |
2983 | for (i = 0; i < num_pipes; i++) | |
6b670fa9 | 2984 | pipe_ctx[i]->stream_res.tg->funcs-> |
5b5abe95 AK |
2985 | set_static_screen_control(pipe_ctx[i]->stream_res.tg, |
2986 | triggers, params->num_frames); | |
70ccab60 HW |
2987 | } |
2988 | ||
4fac6da2 | 2989 | static void dcn10_config_stereo_parameters( |
0971c40e | 2990 | struct dc_stream_state *stream, struct crtc_stereo_flags *flags) |
7f5c22d1 | 2991 | { |
4fa086b9 | 2992 | enum view_3d_format view_format = stream->view_format; |
7f5c22d1 | 2993 | enum dc_timing_3d_format timing_3d_format =\ |
4fa086b9 | 2994 | stream->timing.timing_3d_format; |
7f5c22d1 VP |
2995 | bool non_stereo_timing = false; |
2996 | ||
2997 | if (timing_3d_format == TIMING_3D_FORMAT_NONE || | |
2998 | timing_3d_format == TIMING_3D_FORMAT_SIDE_BY_SIDE || | |
2999 | timing_3d_format == TIMING_3D_FORMAT_TOP_AND_BOTTOM) | |
3000 | non_stereo_timing = true; | |
3001 | ||
3002 | if (non_stereo_timing == false && | |
3003 | view_format == VIEW_3D_FORMAT_FRAME_SEQUENTIAL) { | |
3004 | ||
3005 | flags->PROGRAM_STEREO = 1; | |
3006 | flags->PROGRAM_POLARITY = 1; | |
3007 | if (timing_3d_format == TIMING_3D_FORMAT_INBAND_FA || | |
3008 | timing_3d_format == TIMING_3D_FORMAT_DP_HDMI_INBAND_FA || | |
3009 | timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) { | |
3010 | enum display_dongle_type dongle = \ | |
ceb3dbb4 | 3011 | stream->link->ddc->dongle_type; |
7f5c22d1 VP |
3012 | if (dongle == DISPLAY_DONGLE_DP_VGA_CONVERTER || |
3013 | dongle == DISPLAY_DONGLE_DP_DVI_CONVERTER || | |
3014 | dongle == DISPLAY_DONGLE_DP_HDMI_CONVERTER) | |
3015 | flags->DISABLE_STEREO_DP_SYNC = 1; | |
3016 | } | |
3017 | flags->RIGHT_EYE_POLARITY =\ | |
4fa086b9 | 3018 | stream->timing.flags.RIGHT_EYE_3D_POLARITY; |
7f5c22d1 VP |
3019 | if (timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) |
3020 | flags->FRAME_PACKED = 1; | |
3021 | } | |
3022 | ||
3023 | return; | |
3024 | } | |
3025 | ||
78c77382 | 3026 | void dcn10_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) |
7f5c22d1 VP |
3027 | { |
3028 | struct crtc_stereo_flags flags = { 0 }; | |
0971c40e | 3029 | struct dc_stream_state *stream = pipe_ctx->stream; |
7f5c22d1 VP |
3030 | |
3031 | dcn10_config_stereo_parameters(stream, &flags); | |
3032 | ||
c2cd9d04 ML |
3033 | if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) { |
3034 | if (!dc_set_generic_gpio_for_stereo(true, dc->ctx->gpio_service)) | |
3035 | dc_set_generic_gpio_for_stereo(false, dc->ctx->gpio_service); | |
3036 | } else { | |
3037 | dc_set_generic_gpio_for_stereo(false, dc->ctx->gpio_service); | |
3038 | } | |
3039 | ||
72d520d4 | 3040 | pipe_ctx->stream_res.opp->funcs->opp_program_stereo( |
a6a6cb34 | 3041 | pipe_ctx->stream_res.opp, |
20bdcc99 | 3042 | flags.PROGRAM_STEREO == 1, |
72d520d4 | 3043 | &stream->timing); |
7f5c22d1 | 3044 | |
6b670fa9 HW |
3045 | pipe_ctx->stream_res.tg->funcs->program_stereo( |
3046 | pipe_ctx->stream_res.tg, | |
4fa086b9 | 3047 | &stream->timing, |
7f5c22d1 VP |
3048 | &flags); |
3049 | ||
7f5c22d1 VP |
3050 | return; |
3051 | } | |
3052 | ||
0b7421f0 | 3053 | static struct hubp *get_hubp_by_inst(struct resource_pool *res_pool, int mpcc_inst) |
e07f541f YS |
3054 | { |
3055 | int i; | |
3056 | ||
0b7421f0 AP |
3057 | for (i = 0; i < res_pool->pipe_count; i++) { |
3058 | if (res_pool->hubps[i]->inst == mpcc_inst) | |
3059 | return res_pool->hubps[i]; | |
e07f541f YS |
3060 | } |
3061 | ASSERT(false); | |
3062 | return NULL; | |
3063 | } | |
3064 | ||
78c77382 | 3065 | void dcn10_wait_for_mpcc_disconnect( |
fb3466a4 | 3066 | struct dc *dc, |
6be425f3 EY |
3067 | struct resource_pool *res_pool, |
3068 | struct pipe_ctx *pipe_ctx) | |
87480687 | 3069 | { |
f42ea55b | 3070 | struct dce_hwseq *hws = dc->hwseq; |
e07f541f | 3071 | int mpcc_inst; |
87480687 | 3072 | |
fb3466a4 | 3073 | if (dc->debug.sanity_checks) { |
f42ea55b | 3074 | hws->funcs.verify_allow_pstate_change_high(dc); |
665da60f CM |
3075 | } |
3076 | ||
a6a6cb34 | 3077 | if (!pipe_ctx->stream_res.opp) |
6be425f3 EY |
3078 | return; |
3079 | ||
e07f541f YS |
3080 | for (mpcc_inst = 0; mpcc_inst < MAX_PIPES; mpcc_inst++) { |
3081 | if (pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst]) { | |
0b7421f0 | 3082 | struct hubp *hubp = get_hubp_by_inst(res_pool, mpcc_inst); |
e07f541f YS |
3083 | |
3084 | res_pool->mpc->funcs->wait_for_idle(res_pool->mpc, mpcc_inst); | |
3085 | pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst] = false; | |
0b7421f0 | 3086 | hubp->funcs->set_blank(hubp, true); |
87480687 EY |
3087 | } |
3088 | } | |
6be425f3 | 3089 | |
fb3466a4 | 3090 | if (dc->debug.sanity_checks) { |
f42ea55b | 3091 | hws->funcs.verify_allow_pstate_change_high(dc); |
665da60f CM |
3092 | } |
3093 | ||
87480687 EY |
3094 | } |
3095 | ||
78c77382 | 3096 | bool dcn10_dummy_display_power_gating( |
fb3466a4 | 3097 | struct dc *dc, |
4fac6da2 DL |
3098 | uint8_t controller_id, |
3099 | struct dc_bios *dcb, | |
2b13d7d3 TC |
3100 | enum pipe_gating_control power_gating) |
3101 | { | |
3102 | return true; | |
3103 | } | |
3104 | ||
78c77382 | 3105 | void dcn10_update_pending_status(struct pipe_ctx *pipe_ctx) |
2b13d7d3 | 3106 | { |
3be5262e | 3107 | struct dc_plane_state *plane_state = pipe_ctx->plane_state; |
6b670fa9 | 3108 | struct timing_generator *tg = pipe_ctx->stream_res.tg; |
68199bd1 | 3109 | bool flip_pending; |
d9758768 | 3110 | struct dc *dc = plane_state->ctx->dc; |
2b13d7d3 | 3111 | |
3be5262e | 3112 | if (plane_state == NULL) |
2b13d7d3 TC |
3113 | return; |
3114 | ||
68199bd1 | 3115 | flip_pending = pipe_ctx->plane_res.hubp->funcs->hubp_is_flip_pending( |
8feabd03 | 3116 | pipe_ctx->plane_res.hubp); |
2b13d7d3 | 3117 | |
128c075a | 3118 | plane_state->status.is_flip_pending = plane_state->status.is_flip_pending || flip_pending; |
68199bd1 TC |
3119 | |
3120 | if (!flip_pending) | |
3121 | plane_state->status.current_address = plane_state->status.requested_address; | |
3122 | ||
3123 | if (plane_state->status.current_address.type == PLN_ADDR_TYPE_GRPH_STEREO && | |
2b13d7d3 | 3124 | tg->funcs->is_stereo_left_eye) { |
3be5262e | 3125 | plane_state->status.is_right_eye = |
6b670fa9 | 3126 | !tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg); |
2b13d7d3 | 3127 | } |
d9758768 GS |
3128 | |
3129 | if (dc->hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied) { | |
3130 | struct dce_hwseq *hwseq = dc->hwseq; | |
3131 | struct timing_generator *tg = dc->res_pool->timing_generators[0]; | |
3132 | unsigned int cur_frame = tg->funcs->get_frame_count(tg); | |
3133 | ||
3134 | if (cur_frame != hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied_on_frame) { | |
3135 | struct hubbub *hubbub = dc->res_pool->hubbub; | |
3136 | ||
3137 | hubbub->funcs->allow_self_refresh_control(hubbub, !dc->debug.disable_stutter); | |
3138 | hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied = false; | |
3139 | } | |
3140 | } | |
2b13d7d3 | 3141 | } |
4fac6da2 | 3142 | |
78c77382 | 3143 | void dcn10_update_dchub(struct dce_hwseq *hws, struct dchub_init_data *dh_data) |
afa9104b | 3144 | { |
5fc43055 | 3145 | struct hubbub *hubbub = hws->ctx->dc->res_pool->hubbub; |
7f93c1de | 3146 | |
5fc43055 JP |
3147 | /* In DCN, this programming sequence is owned by the hubbub */ |
3148 | hubbub->funcs->update_dchub(hubbub, dh_data); | |
afa9104b | 3149 | } |
0cb8a881 | 3150 | |
c6a228be NK |
3151 | static bool dcn10_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx) |
3152 | { | |
3153 | struct pipe_ctx *test_pipe; | |
5bb0d5cf KK |
3154 | const struct scaler_data *scl_data = &pipe_ctx->plane_res.scl_data; |
3155 | const struct rect *r1 = &scl_data->recout, *r2; | |
3156 | int r1_r = r1->x + r1->width, r1_b = r1->y + r1->height, r2_r, r2_b; | |
71e433ee | 3157 | int cur_layer = pipe_ctx->plane_state->layer_index; |
5bb0d5cf KK |
3158 | bool upper_pipe_exists = false; |
3159 | struct fixed31_32 one = dc_fixpt_from_int(1); | |
c6a228be NK |
3160 | |
3161 | /** | |
5bb0d5cf KK |
3162 | * Disable the cursor if there's another pipe above this with a |
3163 | * plane that contains this pipe's viewport to prevent double cursor | |
3164 | * and incorrect scaling artifacts. | |
c6a228be NK |
3165 | */ |
3166 | for (test_pipe = pipe_ctx->top_pipe; test_pipe; | |
3167 | test_pipe = test_pipe->top_pipe) { | |
3168 | if (!test_pipe->plane_state->visible) | |
3169 | continue; | |
3170 | ||
5bb0d5cf KK |
3171 | r2 = &test_pipe->plane_res.scl_data.recout; |
3172 | r2_r = r2->x + r2->width; | |
3173 | r2_b = r2->y + r2->height; | |
3174 | ||
3175 | if (r1->x >= r2->x && r1->y >= r2->y && r1_r <= r2_r && r1_b <= r2_b) | |
c6a228be | 3176 | return true; |
5bb0d5cf KK |
3177 | |
3178 | if (test_pipe->plane_state->layer_index < cur_layer) | |
3179 | upper_pipe_exists = true; | |
c6a228be NK |
3180 | } |
3181 | ||
5bb0d5cf KK |
3182 | // if plane scaled, assume an upper plane can handle cursor if it exists. |
3183 | if (upper_pipe_exists && | |
3184 | (scl_data->ratios.horz.value != one.value || | |
3185 | scl_data->ratios.vert.value != one.value)) | |
3186 | return true; | |
3187 | ||
c6a228be NK |
3188 | return false; |
3189 | } | |
3190 | ||
78c77382 | 3191 | void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) |
33fd17d9 EY |
3192 | { |
3193 | struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position; | |
3194 | struct hubp *hubp = pipe_ctx->plane_res.hubp; | |
3195 | struct dpp *dpp = pipe_ctx->plane_res.dpp; | |
3196 | struct dc_cursor_mi_param param = { | |
380604e2 | 3197 | .pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10, |
33d7598d | 3198 | .ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz, |
39a9f4d8 DL |
3199 | .viewport = pipe_ctx->plane_res.scl_data.viewport, |
3200 | .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz, | |
3201 | .v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert, | |
08ed681c DL |
3202 | .rotation = pipe_ctx->plane_state->rotation, |
3203 | .mirror = pipe_ctx->plane_state->horizontal_mirror | |
33fd17d9 | 3204 | }; |
64267454 ST |
3205 | bool pipe_split_on = (pipe_ctx->top_pipe != NULL) || |
3206 | (pipe_ctx->bottom_pipe != NULL); | |
aceeeea3 ST |
3207 | bool odm_combine_on = (pipe_ctx->next_odm_pipe != NULL) || |
3208 | (pipe_ctx->prev_odm_pipe != NULL); | |
4ed79864 AC |
3209 | |
3210 | int x_plane = pipe_ctx->plane_state->dst_rect.x; | |
3211 | int y_plane = pipe_ctx->plane_state->dst_rect.y; | |
3212 | int x_pos = pos_cpy.x; | |
3213 | int y_pos = pos_cpy.y; | |
3214 | ||
12aec9ef NK |
3215 | /** |
3216 | * DC cursor is stream space, HW cursor is plane space and drawn | |
3217 | * as part of the framebuffer. | |
3218 | * | |
3219 | * Cursor position can't be negative, but hotspot can be used to | |
3220 | * shift cursor out of the plane bounds. Hotspot must be smaller | |
3221 | * than the cursor size. | |
3222 | */ | |
3223 | ||
3224 | /** | |
3225 | * Translate cursor from stream space to plane space. | |
3226 | * | |
3227 | * If the cursor is scaled then we need to scale the position | |
3228 | * to be in the approximately correct place. We can't do anything | |
3229 | * about the actual size being incorrect, that's a limitation of | |
3230 | * the hardware. | |
3231 | */ | |
d9b20b45 AC |
3232 | if (param.rotation == ROTATION_ANGLE_90 || param.rotation == ROTATION_ANGLE_270) { |
3233 | x_pos = (x_pos - x_plane) * pipe_ctx->plane_state->src_rect.height / | |
3234 | pipe_ctx->plane_state->dst_rect.width; | |
3235 | y_pos = (y_pos - y_plane) * pipe_ctx->plane_state->src_rect.width / | |
3236 | pipe_ctx->plane_state->dst_rect.height; | |
3237 | } else { | |
3238 | x_pos = (x_pos - x_plane) * pipe_ctx->plane_state->src_rect.width / | |
3239 | pipe_ctx->plane_state->dst_rect.width; | |
3240 | y_pos = (y_pos - y_plane) * pipe_ctx->plane_state->src_rect.height / | |
3241 | pipe_ctx->plane_state->dst_rect.height; | |
3242 | } | |
4ed79864 | 3243 | |
12aec9ef NK |
3244 | /** |
3245 | * If the cursor's source viewport is clipped then we need to | |
3246 | * translate the cursor to appear in the correct position on | |
3247 | * the screen. | |
3248 | * | |
3249 | * This translation isn't affected by scaling so it needs to be | |
3250 | * done *after* we adjust the position for the scale factor. | |
033baeee NK |
3251 | * |
3252 | * This is only done by opt-in for now since there are still | |
3253 | * some usecases like tiled display that might enable the | |
3254 | * cursor on both streams while expecting dc to clip it. | |
12aec9ef | 3255 | */ |
033baeee NK |
3256 | if (pos_cpy.translate_by_source) { |
3257 | x_pos += pipe_ctx->plane_state->src_rect.x; | |
3258 | y_pos += pipe_ctx->plane_state->src_rect.y; | |
3259 | } | |
12aec9ef NK |
3260 | |
3261 | /** | |
3262 | * If the position is negative then we need to add to the hotspot | |
3263 | * to shift the cursor outside the plane. | |
3264 | */ | |
3265 | ||
4ed79864 AC |
3266 | if (x_pos < 0) { |
3267 | pos_cpy.x_hotspot -= x_pos; | |
3268 | x_pos = 0; | |
3269 | } | |
3270 | ||
3271 | if (y_pos < 0) { | |
3272 | pos_cpy.y_hotspot -= y_pos; | |
3273 | y_pos = 0; | |
3274 | } | |
3275 | ||
3276 | pos_cpy.x = (uint32_t)x_pos; | |
3277 | pos_cpy.y = (uint32_t)y_pos; | |
94a4ffd1 | 3278 | |
33fd17d9 EY |
3279 | if (pipe_ctx->plane_state->address.type |
3280 | == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) | |
3281 | pos_cpy.enable = false; | |
c6a228be NK |
3282 | |
3283 | if (pos_cpy.enable && dcn10_can_pipe_disable_cursor(pipe_ctx)) | |
3284 | pos_cpy.enable = false; | |
33fd17d9 | 3285 | |
c0057622 JC |
3286 | // Swap axis and mirror horizontally |
3287 | if (param.rotation == ROTATION_ANGLE_90) { | |
3288 | uint32_t temp_x = pos_cpy.x; | |
64267454 | 3289 | |
c0057622 JC |
3290 | pos_cpy.x = pipe_ctx->plane_res.scl_data.viewport.width - |
3291 | (pos_cpy.y - pipe_ctx->plane_res.scl_data.viewport.x) + pipe_ctx->plane_res.scl_data.viewport.x; | |
3292 | pos_cpy.y = temp_x; | |
3293 | } | |
3294 | // Swap axis and mirror vertically | |
3295 | else if (param.rotation == ROTATION_ANGLE_270) { | |
3296 | uint32_t temp_y = pos_cpy.y; | |
64267454 ST |
3297 | int viewport_height = |
3298 | pipe_ctx->plane_res.scl_data.viewport.height; | |
aceeeea3 ST |
3299 | int viewport_y = |
3300 | pipe_ctx->plane_res.scl_data.viewport.y; | |
3301 | ||
3302 | /** | |
3303 | * Display groups that are 1xnY, have pos_cpy.x > 2 * viewport.height | |
3304 | * For pipe split cases: | |
3305 | * - apply offset of viewport.y to normalize pos_cpy.x | |
3306 | * - calculate the pos_cpy.y as before | |
3307 | * - shift pos_cpy.y back by same offset to get final value | |
3308 | * - since we iterate through both pipes, use the lower | |
3309 | * viewport.y for offset | |
3310 | * For non pipe split cases, use the same calculation for | |
3311 | * pos_cpy.y as the 180 degree rotation case below, | |
3312 | * but use pos_cpy.x as our input because we are rotating | |
3313 | * 270 degrees | |
3314 | */ | |
3315 | if (pipe_split_on || odm_combine_on) { | |
3316 | int pos_cpy_x_offset; | |
3317 | int other_pipe_viewport_y; | |
3318 | ||
3319 | if (pipe_split_on) { | |
3320 | if (pipe_ctx->bottom_pipe) { | |
3321 | other_pipe_viewport_y = | |
3322 | pipe_ctx->bottom_pipe->plane_res.scl_data.viewport.y; | |
3323 | } else { | |
3324 | other_pipe_viewport_y = | |
3325 | pipe_ctx->top_pipe->plane_res.scl_data.viewport.y; | |
3326 | } | |
3327 | } else { | |
3328 | if (pipe_ctx->next_odm_pipe) { | |
3329 | other_pipe_viewport_y = | |
3330 | pipe_ctx->next_odm_pipe->plane_res.scl_data.viewport.y; | |
3331 | } else { | |
3332 | other_pipe_viewport_y = | |
3333 | pipe_ctx->prev_odm_pipe->plane_res.scl_data.viewport.y; | |
3334 | } | |
3335 | } | |
3336 | pos_cpy_x_offset = (viewport_y > other_pipe_viewport_y) ? | |
3337 | other_pipe_viewport_y : viewport_y; | |
3338 | pos_cpy.x -= pos_cpy_x_offset; | |
64267454 ST |
3339 | if (pos_cpy.x > viewport_height) { |
3340 | pos_cpy.x = pos_cpy.x - viewport_height; | |
3341 | pos_cpy.y = viewport_height - pos_cpy.x; | |
3342 | } else { | |
3343 | pos_cpy.y = 2 * viewport_height - pos_cpy.x; | |
3344 | } | |
aceeeea3 ST |
3345 | pos_cpy.y += pos_cpy_x_offset; |
3346 | } else { | |
3347 | pos_cpy.y = (2 * viewport_y) + viewport_height - pos_cpy.x; | |
3348 | } | |
c0057622 JC |
3349 | pos_cpy.x = temp_y; |
3350 | } | |
3351 | // Mirror horizontally and vertically | |
3352 | else if (param.rotation == ROTATION_ANGLE_180) { | |
64267454 ST |
3353 | int viewport_width = |
3354 | pipe_ctx->plane_res.scl_data.viewport.width; | |
3355 | int viewport_x = | |
3356 | pipe_ctx->plane_res.scl_data.viewport.x; | |
3357 | ||
aceeeea3 | 3358 | if (pipe_split_on || odm_combine_on) { |
64267454 ST |
3359 | if (pos_cpy.x >= viewport_width + viewport_x) { |
3360 | pos_cpy.x = 2 * viewport_width | |
3361 | - pos_cpy.x + 2 * viewport_x; | |
3362 | } else { | |
3363 | uint32_t temp_x = pos_cpy.x; | |
3364 | ||
3365 | pos_cpy.x = 2 * viewport_x - pos_cpy.x; | |
3366 | if (temp_x >= viewport_x + | |
3367 | (int)hubp->curs_attr.width || pos_cpy.x | |
3368 | <= (int)hubp->curs_attr.width + | |
3369 | pipe_ctx->plane_state->src_rect.x) { | |
3370 | pos_cpy.x = temp_x + viewport_width; | |
3371 | } | |
c0057622 | 3372 | } |
64267454 ST |
3373 | } else { |
3374 | pos_cpy.x = viewport_width - pos_cpy.x + 2 * viewport_x; | |
c0057622 | 3375 | } |
aceeeea3 ST |
3376 | |
3377 | /** | |
3378 | * Display groups that are 1xnY, have pos_cpy.y > viewport.height | |
3379 | * Calculation: | |
3380 | * delta_from_bottom = viewport.y + viewport.height - pos_cpy.y | |
3381 | * pos_cpy.y_new = viewport.y + delta_from_bottom | |
3382 | * Simplify it as: | |
3383 | * pos_cpy.y = viewport.y * 2 + viewport.height - pos_cpy.y | |
3384 | */ | |
3385 | pos_cpy.y = (2 * pipe_ctx->plane_res.scl_data.viewport.y) + | |
3386 | pipe_ctx->plane_res.scl_data.viewport.height - pos_cpy.y; | |
c0057622 JC |
3387 | } |
3388 | ||
33fd17d9 | 3389 | hubp->funcs->set_cursor_position(hubp, &pos_cpy, ¶m); |
94a4ffd1 | 3390 | dpp->funcs->set_cursor_position(dpp, &pos_cpy, ¶m, hubp->curs_attr.width, hubp->curs_attr.height); |
33fd17d9 EY |
3391 | } |
3392 | ||
78c77382 | 3393 | void dcn10_set_cursor_attribute(struct pipe_ctx *pipe_ctx) |
33fd17d9 EY |
3394 | { |
3395 | struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes; | |
3396 | ||
3397 | pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes( | |
3398 | pipe_ctx->plane_res.hubp, attributes); | |
3399 | pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes( | |
5e1613e2 | 3400 | pipe_ctx->plane_res.dpp, attributes); |
33fd17d9 EY |
3401 | } |
3402 | ||
78c77382 | 3403 | void dcn10_set_cursor_sdr_white_level(struct pipe_ctx *pipe_ctx) |
6d92b5c2 KK |
3404 | { |
3405 | uint32_t sdr_white_level = pipe_ctx->stream->cursor_attributes.sdr_white_level; | |
3406 | struct fixed31_32 multiplier; | |
3407 | struct dpp_cursor_attributes opt_attr = { 0 }; | |
3408 | uint32_t hw_scale = 0x3c00; // 1.0 default multiplier | |
3409 | struct custom_float_format fmt; | |
3410 | ||
3411 | if (!pipe_ctx->plane_res.dpp->funcs->set_optional_cursor_attributes) | |
3412 | return; | |
3413 | ||
3414 | fmt.exponenta_bits = 5; | |
3415 | fmt.mantissa_bits = 10; | |
3416 | fmt.sign = true; | |
3417 | ||
3418 | if (sdr_white_level > 80) { | |
3419 | multiplier = dc_fixpt_from_fraction(sdr_white_level, 80); | |
3420 | convert_to_custom_float_format(multiplier, &fmt, &hw_scale); | |
3421 | } | |
3422 | ||
3423 | opt_attr.scale = hw_scale; | |
3424 | opt_attr.bias = 0; | |
3425 | ||
3426 | pipe_ctx->plane_res.dpp->funcs->set_optional_cursor_attributes( | |
3427 | pipe_ctx->plane_res.dpp, &opt_attr); | |
3428 | } | |
3429 | ||
78c77382 AK |
3430 | /* |
3431 | * apply_front_porch_workaround TODO FPGA still need? | |
3432 | * | |
3433 | * This is a workaround for a bug that has existed since R5xx and has not been | |
3434 | * fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. | |
3435 | */ | |
d6001aed YS |
3436 | static void apply_front_porch_workaround( |
3437 | struct dc_crtc_timing *timing) | |
3438 | { | |
3439 | if (timing->flags.INTERLACE == 1) { | |
3440 | if (timing->v_front_porch < 2) | |
3441 | timing->v_front_porch = 2; | |
3442 | } else { | |
3443 | if (timing->v_front_porch < 1) | |
3444 | timing->v_front_porch = 1; | |
3445 | } | |
3446 | } | |
3447 | ||
78c77382 | 3448 | int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx) |
d6001aed | 3449 | { |
d6001aed YS |
3450 | const struct dc_crtc_timing *dc_crtc_timing = &pipe_ctx->stream->timing; |
3451 | struct dc_crtc_timing patched_crtc_timing; | |
3452 | int vesa_sync_start; | |
3453 | int asic_blank_end; | |
3454 | int interlace_factor; | |
3455 | int vertical_line_start; | |
3456 | ||
3457 | patched_crtc_timing = *dc_crtc_timing; | |
3458 | apply_front_porch_workaround(&patched_crtc_timing); | |
3459 | ||
3460 | interlace_factor = patched_crtc_timing.flags.INTERLACE ? 2 : 1; | |
3461 | ||
3462 | vesa_sync_start = patched_crtc_timing.v_addressable + | |
3463 | patched_crtc_timing.v_border_bottom + | |
3464 | patched_crtc_timing.v_front_porch; | |
3465 | ||
3466 | asic_blank_end = (patched_crtc_timing.v_total - | |
3467 | vesa_sync_start - | |
3468 | patched_crtc_timing.v_border_top) | |
3469 | * interlace_factor; | |
3470 | ||
3471 | vertical_line_start = asic_blank_end - | |
e7e10c46 | 3472 | pipe_ctx->pipe_dlg_param.vstartup_start + 1; |
d6001aed YS |
3473 | |
3474 | return vertical_line_start; | |
3475 | } | |
3476 | ||
63731e73 | 3477 | void dcn10_calc_vupdate_position( |
78c77382 | 3478 | struct dc *dc, |
d6001aed YS |
3479 | struct pipe_ctx *pipe_ctx, |
3480 | uint32_t *start_line, | |
3481 | uint32_t *end_line) | |
3482 | { | |
3483 | const struct dc_crtc_timing *dc_crtc_timing = &pipe_ctx->stream->timing; | |
3484 | int vline_int_offset_from_vupdate = | |
3485 | pipe_ctx->stream->periodic_interrupt0.lines_offset; | |
78c77382 | 3486 | int vupdate_offset_from_vsync = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx); |
d6001aed YS |
3487 | int start_position; |
3488 | ||
3489 | if (vline_int_offset_from_vupdate > 0) | |
3490 | vline_int_offset_from_vupdate--; | |
3491 | else if (vline_int_offset_from_vupdate < 0) | |
3492 | vline_int_offset_from_vupdate++; | |
3493 | ||
3494 | start_position = vline_int_offset_from_vupdate + vupdate_offset_from_vsync; | |
3495 | ||
3496 | if (start_position >= 0) | |
3497 | *start_line = start_position; | |
3498 | else | |
3499 | *start_line = dc_crtc_timing->v_total + start_position - 1; | |
3500 | ||
3501 | *end_line = *start_line + 2; | |
3502 | ||
3503 | if (*end_line >= dc_crtc_timing->v_total) | |
3504 | *end_line = 2; | |
3505 | } | |
3506 | ||
78c77382 AK |
3507 | static void dcn10_cal_vline_position( |
3508 | struct dc *dc, | |
d6001aed YS |
3509 | struct pipe_ctx *pipe_ctx, |
3510 | enum vline_select vline, | |
3511 | uint32_t *start_line, | |
3512 | uint32_t *end_line) | |
3513 | { | |
3514 | enum vertical_interrupt_ref_point ref_point = INVALID_POINT; | |
3515 | ||
3516 | if (vline == VLINE0) | |
3517 | ref_point = pipe_ctx->stream->periodic_interrupt0.ref_point; | |
3518 | else if (vline == VLINE1) | |
3519 | ref_point = pipe_ctx->stream->periodic_interrupt1.ref_point; | |
3520 | ||
3521 | switch (ref_point) { | |
3522 | case START_V_UPDATE: | |
78c77382 AK |
3523 | dcn10_calc_vupdate_position( |
3524 | dc, | |
d6001aed YS |
3525 | pipe_ctx, |
3526 | start_line, | |
3527 | end_line); | |
3528 | break; | |
3529 | case START_V_SYNC: | |
3530 | // Suppose to do nothing because vsync is 0; | |
3531 | break; | |
3532 | default: | |
3533 | ASSERT(0); | |
3534 | break; | |
3535 | } | |
3536 | } | |
3537 | ||
78c77382 AK |
3538 | void dcn10_setup_periodic_interrupt( |
3539 | struct dc *dc, | |
d6001aed YS |
3540 | struct pipe_ctx *pipe_ctx, |
3541 | enum vline_select vline) | |
3542 | { | |
3543 | struct timing_generator *tg = pipe_ctx->stream_res.tg; | |
3544 | ||
3545 | if (vline == VLINE0) { | |
3546 | uint32_t start_line = 0; | |
3547 | uint32_t end_line = 0; | |
3548 | ||
78c77382 | 3549 | dcn10_cal_vline_position(dc, pipe_ctx, vline, &start_line, &end_line); |
d6001aed YS |
3550 | |
3551 | tg->funcs->setup_vertical_interrupt0(tg, start_line, end_line); | |
3552 | ||
3553 | } else if (vline == VLINE1) { | |
3554 | pipe_ctx->stream_res.tg->funcs->setup_vertical_interrupt1( | |
3555 | tg, | |
3556 | pipe_ctx->stream->periodic_interrupt1.lines_offset); | |
3557 | } | |
3558 | } | |
3559 | ||
78c77382 | 3560 | void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx) |
d6001aed YS |
3561 | { |
3562 | struct timing_generator *tg = pipe_ctx->stream_res.tg; | |
78c77382 | 3563 | int start_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx); |
d6001aed YS |
3564 | |
3565 | if (start_line < 0) { | |
3566 | ASSERT(0); | |
3567 | start_line = 0; | |
3568 | } | |
3569 | ||
3570 | if (tg->funcs->setup_vertical_interrupt2) | |
3571 | tg->funcs->setup_vertical_interrupt2(tg, start_line); | |
3572 | } | |
3573 | ||
78c77382 | 3574 | void dcn10_unblank_stream(struct pipe_ctx *pipe_ctx, |
7fe538a4 CL |
3575 | struct dc_link_settings *link_settings) |
3576 | { | |
3577 | struct encoder_unblank_param params = { { 0 } }; | |
3578 | struct dc_stream_state *stream = pipe_ctx->stream; | |
3579 | struct dc_link *link = stream->link; | |
f42ea55b | 3580 | struct dce_hwseq *hws = link->dc->hwseq; |
7fe538a4 CL |
3581 | |
3582 | /* only 3 items below are used by unblank */ | |
3583 | params.timing = pipe_ctx->stream->timing; | |
3584 | ||
3585 | params.link_settings.link_rate = link_settings->link_rate; | |
3586 | ||
3587 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) { | |
3588 | if (params.timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
3589 | params.timing.pix_clk_100hz /= 2; | |
bc749a88 | 3590 | pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms); |
7fe538a4 CL |
3591 | } |
3592 | ||
3593 | if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { | |
f42ea55b | 3594 | hws->funcs.edp_backlight_control(link, true); |
7fe538a4 CL |
3595 | } |
3596 | } | |
3597 | ||
78c77382 | 3598 | void dcn10_send_immediate_sdp_message(struct pipe_ctx *pipe_ctx, |
88ccdf1d LHM |
3599 | const uint8_t *custom_sdp_message, |
3600 | unsigned int sdp_message_size) | |
3601 | { | |
3602 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) { | |
3603 | pipe_ctx->stream_res.stream_enc->funcs->send_immediate_sdp_message( | |
3604 | pipe_ctx->stream_res.stream_enc, | |
3605 | custom_sdp_message, | |
3606 | sdp_message_size); | |
3607 | } | |
3608 | } | |
78c77382 | 3609 | enum dc_status dcn10_set_clock(struct dc *dc, |
925f566c CL |
3610 | enum dc_clock_type clock_type, |
3611 | uint32_t clk_khz, | |
3612 | uint32_t stepping) | |
3613 | { | |
3614 | struct dc_state *context = dc->current_state; | |
3615 | struct dc_clock_config clock_cfg = {0}; | |
3616 | struct dc_clocks *current_clocks = &context->bw_ctx.bw.dcn.clk; | |
3617 | ||
55459456 | 3618 | if (!dc->clk_mgr || !dc->clk_mgr->funcs->get_clock) |
925f566c CL |
3619 | return DC_FAIL_UNSUPPORTED_1; |
3620 | ||
55459456 TL |
3621 | dc->clk_mgr->funcs->get_clock(dc->clk_mgr, |
3622 | context, clock_type, &clock_cfg); | |
3623 | ||
925f566c CL |
3624 | if (clk_khz > clock_cfg.max_clock_khz) |
3625 | return DC_FAIL_CLK_EXCEED_MAX; | |
3626 | ||
3627 | if (clk_khz < clock_cfg.min_clock_khz) | |
3628 | return DC_FAIL_CLK_BELOW_MIN; | |
3629 | ||
3630 | if (clk_khz < clock_cfg.bw_requirequired_clock_khz) | |
3631 | return DC_FAIL_CLK_BELOW_CFG_REQUIRED; | |
3632 | ||
3633 | /*update internal request clock for update clock use*/ | |
3634 | if (clock_type == DC_CLOCK_TYPE_DISPCLK) | |
3635 | current_clocks->dispclk_khz = clk_khz; | |
3636 | else if (clock_type == DC_CLOCK_TYPE_DPPCLK) | |
3637 | current_clocks->dppclk_khz = clk_khz; | |
3638 | else | |
3639 | return DC_ERROR_UNEXPECTED; | |
3640 | ||
55459456 | 3641 | if (dc->clk_mgr->funcs->update_clocks) |
925f566c CL |
3642 | dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, |
3643 | context, true); | |
3644 | return DC_OK; | |
3645 | ||
3646 | } | |
3647 | ||
78c77382 | 3648 | void dcn10_get_clock(struct dc *dc, |
925f566c CL |
3649 | enum dc_clock_type clock_type, |
3650 | struct dc_clock_config *clock_cfg) | |
3651 | { | |
3652 | struct dc_state *context = dc->current_state; | |
3653 | ||
3654 | if (dc->clk_mgr && dc->clk_mgr->funcs->get_clock) | |
3655 | dc->clk_mgr->funcs->get_clock(dc->clk_mgr, context, clock_type, clock_cfg); | |
3656 | ||
3657 | } | |
712343cd VL |
3658 | |
3659 | void dcn10_get_dcc_en_bits(struct dc *dc, int *dcc_en_bits) | |
3660 | { | |
3661 | struct resource_pool *pool = dc->res_pool; | |
3662 | int i; | |
3663 | ||
3664 | for (i = 0; i < pool->pipe_count; i++) { | |
3665 | struct hubp *hubp = pool->hubps[i]; | |
3666 | struct dcn_hubp_state *s = &(TO_DCN10_HUBP(hubp)->state); | |
3667 | ||
3668 | hubp->funcs->hubp_read_state(hubp); | |
3669 | ||
3670 | if (!s->blank_en) | |
3671 | dcc_en_bits[i] = s->dcc_en ? 1 : 0; | |
3672 | } | |
3673 | } |