xref: /linux/drivers/gpu/drm/amd/display/dc/hwss/dcn42b/dcn42b_hwseq.c (revision b2128290c29902315e632ea59e0504d6bc9e9b42)
1 /*
2  * SPDX-License-Identifier: MIT
3  *
4  * Copyright 2026 Advanced Micro Devices, Inc.
5  */
6 
7 #include "dm_services.h"
8 #include "core_types.h"
9 #include "resource.h"
10 #include "dce/dce_hwseq.h"
11 #include "dcn10/dcn10_hwseq.h"
12 #include "reg_helper.h"
13 #include "hubp.h"
14 #include "dchubbub.h"
15 #include "timing_generator.h"
16 #include "opp.h"
17 #include "mpc.h"
18 #include "dcn42b_hwseq.h"
19 
20 #define CTX \
21 	hws->ctx
22 
23 #define REG(reg)\
24 	hws->regs->reg
25 
26 #define DC_LOGGER \
27 	hws->ctx->logger
28 
29 #undef FN
30 #define FN(reg_name, field_name) \
31 	hws->shifts->field_name, hws->masks->field_name
32 
33 /*
34  * dcn42b_init_pipes - Initialize pipes for dcn42b
35  *
36  * This function is modeled after dcn10_init_pipes but handles the case
37  * where num_timing_generator != num_pipes (e.g., 3 TGs but 4 pipes).
38  *
39  * For dcn42b:
40  * - num_timing_generator = 3
41  * - num_pipes (num_dpp) = 4
42  *
43  * The key difference is that we iterate over timing generators separately
44  * from pipes to avoid accessing timing_generators[i] when i >= num_timing_generator.
45  */
46 void dcn42b_init_pipes(struct dc *dc, struct dc_state *context)
47 {
48 	uint8_t i;
49 	struct dce_hwseq *hws = dc->hwseq;
50 	struct hubbub *hubbub = dc->res_pool->hubbub;
51 	bool can_apply_seamless_boot = false;
52 	bool tg_enabled[MAX_PIPES] = {false};
53 
54 	for (i = 0; i < context->stream_count; i++) {
55 		if (context->streams[i]->apply_seamless_boot_optimization) {
56 			can_apply_seamless_boot = true;
57 			break;
58 		}
59 	}
60 
61 	for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
62 		struct timing_generator *tg = dc->res_pool->timing_generators[i];
63 		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
64 
65 		/* There is assumption that pipe_ctx is not mapping irregularly
66 		 * to non-preferred front end. If pipe_ctx->stream is not NULL,
67 		 * we will use the pipe, so don't disable
68 		 */
69 		if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
70 			continue;
71 
72 		/* Blank controller using driver code instead of
73 		 * command table.
74 		 */
75 		if (tg->funcs->is_tg_enabled(tg)) {
76 			if (hws->funcs.init_blank != NULL) {
77 				hws->funcs.init_blank(dc, tg);
78 				tg->funcs->lock(tg);
79 			} else {
80 				tg->funcs->lock(tg);
81 				tg->funcs->set_blank(tg, true);
82 				hwss_wait_for_blank_complete(tg);
83 			}
84 		}
85 	}
86 
87 	/* Reset det size */
88 	for (i = 0; i < dc->res_pool->pipe_count; i++) {
89 		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
90 		struct hubp *hubp = dc->res_pool->hubps[i];
91 
92 		/* Do not need to reset for seamless boot */
93 		if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
94 			continue;
95 
96 		if (hubbub && hubp) {
97 			if (hubbub->funcs->program_det_size)
98 				hubbub->funcs->program_det_size(hubbub, hubp->inst, 0);
99 			if (hubbub->funcs->program_det_segments)
100 				hubbub->funcs->program_det_segments(hubbub, hubp->inst, 0);
101 		}
102 	}
103 
104 	/* num_opp will be equal to number of mpcc */
105 	for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) {
106 		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
107 
108 		/* Cannot reset the MPC mux if seamless boot */
109 		if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
110 			continue;
111 
112 		dc->res_pool->mpc->funcs->mpc_init_single_inst(
113 				dc->res_pool->mpc, i);
114 	}
115 
116 	/* initialize DWB pointer to MCIF_WB */
117 	for (i = 0; i < dc->res_pool->res_cap->num_dwb; i++)
118 		dc->res_pool->dwbc[i]->mcif = dc->res_pool->mcif_wb[i];
119 
120 	for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
121 		struct timing_generator *tg = dc->res_pool->timing_generators[i];
122 		struct hubp *hubp = dc->res_pool->hubps[i];
123 		struct dpp *dpp = dc->res_pool->dpps[i];
124 		struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
125 
126 		/* There is assumption that pipe_ctx is not mapping irregularly
127 		 * to non-preferred front end. If pipe_ctx->stream is not NULL,
128 		 * we will use the pipe, so don't disable
129 		 */
130 		if (can_apply_seamless_boot &&
131 			pipe_ctx->stream != NULL &&
132 			pipe_ctx->stream_res.tg->funcs->is_tg_enabled(
133 				pipe_ctx->stream_res.tg)) {
134 			// Enable double buffering for OTG_BLANK no matter if
135 			// seamless boot is enabled or not to suppress global sync
136 			// signals when OTG blanked. This is to prevent pipe from
137 			// requesting data while in PSR.
138 			tg->funcs->tg_init(tg);
139 			hubp->power_gated = true;
140 			tg_enabled[i] = true;
141 			continue;
142 		}
143 
144 		/* Disable on the current state so the new one isn't cleared. */
145 		pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
146 
147 		hubp->funcs->hubp_reset(hubp);
148 		dpp->funcs->dpp_reset(dpp);
149 
150 		pipe_ctx->stream_res.tg = tg;
151 		pipe_ctx->pipe_idx = i;
152 
153 		pipe_ctx->plane_res.hubp = hubp;
154 		pipe_ctx->plane_res.dpp = dpp;
155 		pipe_ctx->plane_res.mpcc_inst = (uint8_t)dpp->inst;
156 		hubp->mpcc_id = dpp->inst;
157 		hubp->opp_id = OPP_ID_INVALID;
158 		hubp->power_gated = false;
159 
160 		dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst;
161 		dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL;
162 		dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true;
163 		pipe_ctx->stream_res.opp = dc->res_pool->opps[i];
164 
165 		hws->funcs.plane_atomic_disconnect(dc, context, pipe_ctx);
166 
167 		if (tg->funcs->is_tg_enabled(tg))
168 			tg->funcs->unlock(tg);
169 
170 		dc->hwss.disable_plane(dc, context, pipe_ctx);
171 
172 		pipe_ctx->stream_res.tg = NULL;
173 		pipe_ctx->plane_res.hubp = NULL;
174 
175 		if (tg->funcs->is_tg_enabled(tg)) {
176 			if (tg->funcs->init_odm)
177 				tg->funcs->init_odm(tg);
178 		}
179 
180 		tg->funcs->tg_init(tg);
181 	}
182 
183 	/* Clean up MPC tree */
184 	for (i = 0; i < dc->res_pool->pipe_count; i++) {
185 		if (tg_enabled[i]) {
186 			if (dc->res_pool->opps[i]->mpc_tree_params.opp_list) {
187 				if (dc->res_pool->opps[i]->mpc_tree_params.opp_list->mpcc_bot) {
188 					int bot_id = dc->res_pool->opps[i]->mpc_tree_params.opp_list->mpcc_bot->mpcc_id;
189 
190 					if ((bot_id < MAX_MPCC) && (bot_id < MAX_PIPES) && (!tg_enabled[bot_id]))
191 						dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL;
192 				}
193 			}
194 		}
195 	}
196 
197 	/* Power gate DSCs */
198 	if (hws->funcs.dsc_pg_control != NULL) {
199 		uint32_t num_opps = 0;
200 		uint32_t opp_id_src0 = OPP_ID_INVALID;
201 		uint32_t opp_id_src1 = OPP_ID_INVALID;
202 
203 		// Step 1: To find out which OPTC is running & OPTC DSC is ON
204 		// We can't use res_pool->res_cap->num_timing_generator to check
205 		// Because it records display pipes default setting built in driver,
206 		// not display pipes of the current chip.
207 		// Some ASICs would be fused display pipes less than the default setting.
208 		// In dcnxx_resource_construct function, driver would obatin real information.
209 		for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
210 			uint32_t optc_dsc_state = 0;
211 			struct timing_generator *tg = dc->res_pool->timing_generators[i];
212 
213 			if (tg->funcs->is_tg_enabled(tg)) {
214 				if (tg->funcs->get_dsc_status)
215 					tg->funcs->get_dsc_status(tg, &optc_dsc_state);
216 				// Only one OPTC with DSC is ON, so if we got one result, we would exit this block.
217 				// non-zero value is DSC enabled
218 				if (optc_dsc_state != 0) {
219 					tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1);
220 					break;
221 				}
222 			}
223 		}
224 
225 		// Step 2: To power down DSC but skip DSC  of running OPTC
226 		for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) {
227 			struct dcn_dsc_state s  = {0};
228 
229 			dc->res_pool->dscs[i]->funcs->dsc_read_state(dc->res_pool->dscs[i], &s);
230 
231 			if ((s.dsc_opp_source == opp_id_src0 || s.dsc_opp_source == opp_id_src1) &&
232 				s.dsc_clock_en && s.dsc_fw_en)
233 				continue;
234 
235 			hws->funcs.dsc_pg_control(hws, dc->res_pool->dscs[i]->inst, false);
236 		}
237 	}
238 }
239