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