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