| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Matthew Stewart | 1277 | 100.00% | 1 | 100.00% |
| Total | 1277 | 1 |
/* * SPDX-License-Identifier: MIT * * Copyright 2026 Advanced Micro Devices, Inc. */ #include "dm_services.h" #include "core_types.h" #include "resource.h" #include "dce/dce_hwseq.h" #include "dcn10/dcn10_hwseq.h" #include "reg_helper.h" #include "hubp.h" #include "dchubbub.h" #include "timing_generator.h" #include "opp.h" #include "mpc.h" #include "dcn42b_hwseq.h" #define CTX \ hws->ctx #define REG(reg)\ hws->regs->reg #define DC_LOGGER \ hws->ctx->logger #undef FN #define FN(reg_name, field_name) \ hws->shifts->field_name, hws->masks->field_name /* * dcn42b_init_pipes - Initialize pipes for dcn42b * * This function is modeled after dcn10_init_pipes but handles the case * where num_timing_generator != num_pipes (e.g., 3 TGs but 4 pipes). * * For dcn42b: * - num_timing_generator = 3 * - num_pipes (num_dpp) = 4 * * The key difference is that we iterate over timing generators separately * from pipes to avoid accessing timing_generators[i] when i >= num_timing_generator. */ void dcn42b_init_pipes(struct dc *dc, struct dc_state *context) { uint8_t i; struct dce_hwseq *hws = dc->hwseq; struct hubbub *hubbub = dc->res_pool->hubbub; bool can_apply_seamless_boot = false; bool tg_enabled[MAX_PIPES] = {false}; for (i = 0; i < context->stream_count; i++) { if (context->streams[i]->apply_seamless_boot_optimization) { can_apply_seamless_boot = true; break; } } for (i = 0; i < dc->res_pool->timing_generator_count; i++) { struct timing_generator *tg = dc->res_pool->timing_generators[i]; struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; /* There is assumption that pipe_ctx is not mapping irregularly * to non-preferred front end. If pipe_ctx->stream is not NULL, * we will use the pipe, so don't disable */ if (pipe_ctx->stream != NULL && can_apply_seamless_boot) continue; /* Blank controller using driver code instead of * command table. */ if (tg->funcs->is_tg_enabled(tg)) { if (hws->funcs.init_blank != NULL) { hws->funcs.init_blank(dc, tg); tg->funcs->lock(tg); } else { tg->funcs->lock(tg); tg->funcs->set_blank(tg, true); hwss_wait_for_blank_complete(tg); } } } /* Reset det size */ for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; struct hubp *hubp = dc->res_pool->hubps[i]; /* Do not need to reset for seamless boot */ if (pipe_ctx->stream != NULL && can_apply_seamless_boot) continue; if (hubbub && hubp) { if (hubbub->funcs->program_det_size) hubbub->funcs->program_det_size(hubbub, hubp->inst, 0); if (hubbub->funcs->program_det_segments) hubbub->funcs->program_det_segments(hubbub, hubp->inst, 0); } } /* num_opp will be equal to number of mpcc */ for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; /* Cannot reset the MPC mux if seamless boot */ if (pipe_ctx->stream != NULL && can_apply_seamless_boot) continue; dc->res_pool->mpc->funcs->mpc_init_single_inst( dc->res_pool->mpc, i); } /* initialize DWB pointer to MCIF_WB */ for (i = 0; i < dc->res_pool->res_cap->num_dwb; i++) dc->res_pool->dwbc[i]->mcif = dc->res_pool->mcif_wb[i]; for (i = 0; i < dc->res_pool->timing_generator_count; i++) { struct timing_generator *tg = dc->res_pool->timing_generators[i]; struct hubp *hubp = dc->res_pool->hubps[i]; struct dpp *dpp = dc->res_pool->dpps[i]; struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; /* There is assumption that pipe_ctx is not mapping irregularly * to non-preferred front end. If pipe_ctx->stream is not NULL, * we will use the pipe, so don't disable */ if (can_apply_seamless_boot && pipe_ctx->stream != NULL && pipe_ctx->stream_res.tg->funcs->is_tg_enabled( pipe_ctx->stream_res.tg)) { // Enable double buffering for OTG_BLANK no matter if // seamless boot is enabled or not to suppress global sync // signals when OTG blanked. This is to prevent pipe from // requesting data while in PSR. tg->funcs->tg_init(tg); hubp->power_gated = true; tg_enabled[i] = true; continue; } /* Disable on the current state so the new one isn't cleared. */ pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; hubp->funcs->hubp_reset(hubp); dpp->funcs->dpp_reset(dpp); pipe_ctx->stream_res.tg = tg; pipe_ctx->pipe_idx = i; pipe_ctx->plane_res.hubp = hubp; pipe_ctx->plane_res.dpp = dpp; pipe_ctx->plane_res.mpcc_inst = (uint8_t)dpp->inst; hubp->mpcc_id = dpp->inst; hubp->opp_id = OPP_ID_INVALID; hubp->power_gated = false; dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst; dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL; dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; pipe_ctx->stream_res.opp = dc->res_pool->opps[i]; hws->funcs.plane_atomic_disconnect(dc, context, pipe_ctx); if (tg->funcs->is_tg_enabled(tg)) tg->funcs->unlock(tg); dc->hwss.disable_plane(dc, context, pipe_ctx); pipe_ctx->stream_res.tg = NULL; pipe_ctx->plane_res.hubp = NULL; if (tg->funcs->is_tg_enabled(tg)) { if (tg->funcs->init_odm) tg->funcs->init_odm(tg); } tg->funcs->tg_init(tg); } /* Clean up MPC tree */ for (i = 0; i < dc->res_pool->pipe_count; i++) { if (tg_enabled[i]) { if (dc->res_pool->opps[i]->mpc_tree_params.opp_list) { if (dc->res_pool->opps[i]->mpc_tree_params.opp_list->mpcc_bot) { int bot_id = dc->res_pool->opps[i]->mpc_tree_params.opp_list->mpcc_bot->mpcc_id; if ((bot_id < MAX_MPCC) && (bot_id < MAX_PIPES) && (!tg_enabled[bot_id])) dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL; } } } } /* Power gate DSCs */ if (hws->funcs.dsc_pg_control != NULL) { uint32_t num_opps = 0; uint32_t opp_id_src0 = OPP_ID_INVALID; uint32_t opp_id_src1 = OPP_ID_INVALID; // Step 1: To find out which OPTC is running & OPTC DSC is ON // We can't use res_pool->res_cap->num_timing_generator to check // Because it records display pipes default setting built in driver, // not display pipes of the current chip. // Some ASICs would be fused display pipes less than the default setting. // In dcnxx_resource_construct function, driver would obatin real information. for (i = 0; i < dc->res_pool->timing_generator_count; i++) { uint32_t optc_dsc_state = 0; struct timing_generator *tg = dc->res_pool->timing_generators[i]; if (tg->funcs->is_tg_enabled(tg)) { if (tg->funcs->get_dsc_status) tg->funcs->get_dsc_status(tg, &optc_dsc_state); // Only one OPTC with DSC is ON, so if we got one result, we would exit this block. // non-zero value is DSC enabled if (optc_dsc_state != 0) { tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1); break; } } } // Step 2: To power down DSC but skip DSC of running OPTC for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) { struct dcn_dsc_state s = {0}; dc->res_pool->dscs[i]->funcs->dsc_read_state(dc->res_pool->dscs[i], &s); if ((s.dsc_opp_source == opp_id_src0 || s.dsc_opp_source == opp_id_src1) && s.dsc_clock_en && s.dsc_fw_en) continue; hws->funcs.dsc_pg_control(hws, dc->res_pool->dscs[i]->inst, false); } } }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1