| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Roman Li | 6349 | 98.96% | 3 | 30.00% |
| Srinivasan S | 28 | 0.44% | 2 | 20.00% |
| Mario Limonciello | 14 | 0.22% | 1 | 10.00% |
| Gaghik Khachatrian | 10 | 0.16% | 1 | 10.00% |
| Nicholas Kazlauskas | 7 | 0.11% | 1 | 10.00% |
| Charlene Liu | 6 | 0.09% | 1 | 10.00% |
| Clay King | 2 | 0.03% | 1 | 10.00% |
| Total | 6416 | 10 |
// SPDX-License-Identifier: MIT // // Copyright 2026 Advanced Micro Devices, Inc. #include "dm_services.h" #include "dm_helpers.h" #include "core_types.h" #include "resource.h" #include "dccg.h" #include "dce/dce_hwseq.h" #include "dcn30/dcn30_cm_common.h" #include "reg_helper.h" #include "abm.h" #include "hubp.h" #include "dchubbub.h" #include "timing_generator.h" #include "opp.h" #include "ipp.h" #include "mpc.h" #include "mcif_wb.h" #include "dc_dmub_srv.h" #include "link_hwss.h" #include "dpcd_defs.h" #include "dcn401/dcn401_hwseq.h" #include "dcn42_hwseq.h" #include "clk_mgr.h" #include "dsc.h" #include "dcn20/dcn20_optc.h" #include "dce/dmub_hw_lock_mgr.h" #include "dcn42/dcn42_resource.h" #include "link_service.h" #include "../dcn10/dcn10_hwseq.h" #include "../dcn20/dcn20_hwseq.h" #include "dc_state_priv.h" #include "dc_stream_priv.h" #include "dcn35/dcn35_hwseq.h" #include "dcn42/dcn42_hwseq.h" #include "dce/dmub_hw_lock_mgr.h" #include "dio/dcn10/dcn10_dio.h" #define DC_LOGGER \ ctx->logger #define CTX \ hws->ctx #define REG(reg)\ hws->regs->reg #undef FN #define FN(reg_name, field_name) \ hws->shifts->field_name, hws->masks->field_name static void print_pg_status(struct dc *dc, const char *debug_func, const char *debug_log) { if (dc->debug.enable_pg_cntl_debug_logs && dc->res_pool->pg_cntl) { if (dc->res_pool->pg_cntl->funcs->print_pg_status) dc->res_pool->pg_cntl->funcs->print_pg_status(dc->res_pool->pg_cntl, debug_func, debug_log); } } void dcn42_init_hw(struct dc *dc) { struct abm **abms = dc->res_pool->multiple_abms; struct dce_hwseq *hws = dc->hwseq; struct dc_bios *dcb = dc->ctx->dc_bios; struct resource_pool *res_pool = dc->res_pool; int i; unsigned int edp_num; uint32_t backlight = MAX_BACKLIGHT_LEVEL; uint32_t user_level = MAX_BACKLIGHT_LEVEL; bool dchub_ref_freq_changed; int current_dchub_ref_freq = 0; if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); // mark dcmode limits present if any clock has distinct AC and DC values from SMU dc->caps.dcmode_power_limits_present = dc->clk_mgr->funcs->is_dc_mode_present && dc->clk_mgr->funcs->is_dc_mode_present(dc->clk_mgr); } // Initialize the dccg if (res_pool->dccg->funcs->dccg_init) res_pool->dccg->funcs->dccg_init(res_pool->dccg); // Disable DMUB Initialization until IPS state programming is finalized //if (!dcb->funcs->is_accelerated_mode(dcb)) { // hws->funcs.bios_golden_init(dc); //} // Set default OPTC memory power states if (dc->debug.enable_mem_low_power.bits.optc) { // Shutdown when unassigned and light sleep in VBLANK REG_SET_2(ODM_MEM_PWR_CTRL3, 0, ODM_MEM_UNASSIGNED_PWR_MODE, 3, ODM_MEM_VBLANK_PWR_MODE, 1); } if (dc->debug.enable_mem_low_power.bits.vga) { // Power down VGA memory REG_UPDATE(MMHUBBUB_MEM_PWR_CNTL, VGA_MEM_PWR_FORCE, 1); } if (dc->ctx->dc_bios->fw_info_valid) { res_pool->ref_clocks.xtalin_clock_inKhz = dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; if (res_pool->hubbub) { (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg, dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency, &res_pool->ref_clocks.dccg_ref_clock_inKhz); current_dchub_ref_freq = res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub, res_pool->ref_clocks.dccg_ref_clock_inKhz, &res_pool->ref_clocks.dchub_ref_clock_inKhz); } else { // Not all ASICs have DCCG sw component res_pool->ref_clocks.dccg_ref_clock_inKhz = res_pool->ref_clocks.xtalin_clock_inKhz; res_pool->ref_clocks.dchub_ref_clock_inKhz = res_pool->ref_clocks.xtalin_clock_inKhz; } } else ASSERT_CRITICAL(false); for (i = 0; i < dc->link_count; i++) { /* Power up AND update implementation according to the * required signal (which may be different from the * default signal on connector). */ struct dc_link *link = dc->links[i]; if (link->ep_type != DISPLAY_ENDPOINT_PHY) continue; link->link_enc->funcs->hw_init(link->link_enc); /* Check for enabled DIG to identify enabled display */ if (link->link_enc->funcs->is_dig_enabled && link->link_enc->funcs->is_dig_enabled(link->link_enc)) { link->link_status.link_active = true; link->phy_state.symclk_state = SYMCLK_ON_TX_ON; if (link->link_enc->funcs->fec_is_active && link->link_enc->funcs->fec_is_active(link->link_enc)) link->fec_state = dc_link_fec_enabled; } } /* enable_power_gating_plane before dsc_pg_control because * FORCEON = 1 with hw default value on bootup, resume from s3 */ if (hws->funcs.enable_power_gating_plane) hws->funcs.enable_power_gating_plane(dc->hwseq, true); /* we want to turn off all dp displays before doing detection */ dc->link_srv->blank_all_dp_displays(dc); /* If taking control over from VBIOS, we may want to optimize our first * mode set, so we need to skip powering down pipes until we know which * pipes we want to use. * Otherwise, if taking control is not possible, we need to power * everything down. */ if (dcb->funcs->is_accelerated_mode(dcb) || !dc->config.seamless_boot_edp_requested) { /* Disable boot optimizations means power down everything including PHY, DIG, * and OTG (i.e. the boot is not optimized because we do a full power down). */ if (dc->hwss.enable_accelerated_mode && dc->debug.disable_boot_optimizations) dc->hwss.enable_accelerated_mode(dc, dc->current_state); else hws->funcs.init_pipes(dc, dc->current_state); if (dc->res_pool->hubbub->funcs->allow_self_refresh_control) dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); } /* In headless boot cases, DIG may be turned * on which causes HW/SW discrepancies. * To avoid this, power down hardware on boot * if DIG is turned on and seamless boot not enabled */ if (!dc->config.seamless_boot_edp_requested) { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link; dc_get_edp_links(dc, edp_links, &edp_num); if (edp_num) { for (i = 0; i < edp_num; i++) { edp_link = edp_links[i]; if (edp_link->link_enc->funcs->is_dig_enabled && edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) && dc->hwss.edp_backlight_control && hws->funcs.power_down && dc->hwss.edp_power_control) { dc->hwss.edp_backlight_control(edp_link, false); hws->funcs.power_down(dc); dc->hwss.edp_power_control(edp_link, false); } } } else { for (i = 0; i < dc->link_count; i++) { struct dc_link *link = dc->links[i]; if (link && link->link_enc && link->link_enc->funcs->is_dig_enabled && link->link_enc->funcs->is_dig_enabled(link->link_enc) && hws->funcs.power_down) { hws->funcs.power_down(dc); break; } } } } for (i = 0; i < res_pool->audio_count; i++) { struct audio *audio = res_pool->audios[i]; audio->funcs->hw_init(audio); } for (i = 0; i < dc->link_count; i++) { struct dc_link *link = dc->links[i]; if (link->panel_cntl) { backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl); user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL; } } for (i = 0; i < dc->res_pool->pipe_count; i++) { if (abms[i] != NULL && abms[i]->funcs != NULL) abms[i]->funcs->abm_init(abms[i], backlight, user_level); } /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ if (dc->res_pool->dio && dc->res_pool->dio->funcs->mem_pwr_ctrl) dc->res_pool->dio->funcs->mem_pwr_ctrl(dc->res_pool->dio, false); if (!dc->debug.disable_clock_gate) { /* enable all DCN clock gating */ REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); } dcn401_setup_hpo_hw_control(hws, true); if (!dcb->funcs->is_accelerated_mode(dcb) && dc->res_pool->hubbub->funcs->init_watermarks) dc->res_pool->hubbub->funcs->init_watermarks(dc->res_pool->hubbub); if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_wm_ranges) dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); if (dc->res_pool->hubbub->funcs->force_pstate_change_control) dc->res_pool->hubbub->funcs->force_pstate_change_control( dc->res_pool->hubbub, false, false); if (dc->res_pool->hubbub->funcs->init_crb) dc->res_pool->hubbub->funcs->init_crb(dc->res_pool->hubbub); if (dc->res_pool->hubbub->funcs->set_request_limit && dc->clk_mgr && dc->clk_mgr->bw_params && dc->config.sdpif_request_limit_words_per_umc > 0) dc->res_pool->hubbub->funcs->set_request_limit(dc->res_pool->hubbub, dc->clk_mgr->bw_params->num_channels, dc->config.sdpif_request_limit_words_per_umc); // Get DMCUB capabilities if (dc->ctx->dmub_srv) { dc_dmub_srv_query_caps_cmd(dc->ctx->dmub_srv); dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr; dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0; dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver; /* sw and fw FAMS versions must match for support */ dc->debug.fams2_config.bits.enable &= dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; dchub_ref_freq_changed = res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq; if ((!dc->debug.fams2_config.bits.enable || dchub_ref_freq_changed) && dc->res_pool->funcs->update_bw_bounding_box && dc->clk_mgr && dc->clk_mgr->bw_params) { /* update bounding box if FAMS2 disabled, or if dchub clk has changed */ dc->res_pool->funcs->update_bw_bounding_box(dc, dc->clk_mgr->bw_params); } } if (dc->res_pool->pg_cntl) { if (dc->res_pool->pg_cntl->funcs->init_pg_status) dc->res_pool->pg_cntl->funcs->init_pg_status(dc->res_pool->pg_cntl); } print_pg_status(dc, __func__, ": after init_pg_status"); } void dcn42_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) { struct hubp *hubp = pipe_ctx->plane_res.hubp; struct mpcc_blnd_cfg blnd_cfg = {0}; bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha; int mpcc_id; struct mpcc *new_mpcc; struct mpc *mpc = dc->res_pool->mpc; struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params); blnd_cfg.overlap_only = false; blnd_cfg.global_gain = 0xfff; if (per_pixel_alpha) { blnd_cfg.pre_multiplied_alpha = pipe_ctx->plane_state->pre_multiplied_alpha; if (pipe_ctx->plane_state->global_alpha) { blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN; blnd_cfg.global_gain = pipe_ctx->plane_state->global_alpha_value; } else { blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; } } else { blnd_cfg.pre_multiplied_alpha = false; blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; } if (pipe_ctx->plane_state->global_alpha) blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value; else blnd_cfg.global_alpha = 0xfff; blnd_cfg.background_color_bpc = 4; blnd_cfg.bottom_gain_mode = 0; blnd_cfg.top_gain = 0x1f000; blnd_cfg.bottom_inside_gain = 0x1f000; blnd_cfg.bottom_outside_gain = 0x1f000; if (pipe_ctx->plane_state->format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA) blnd_cfg.pre_multiplied_alpha = false; /* * TODO: remove hack * Note: currently there is a bug in init_hw such that * on resume from hibernate, BIOS sets up MPCC0, and * we do mpcc_remove but the mpcc cannot go to idle * after remove. This cause us to pick mpcc1 here, * which causes a pstate hang for yet unknown reason. */ mpcc_id = hubp->inst; /* If there is no full update, don't need to touch MPC tree*/ if (!pipe_ctx->plane_state->update_flags.bits.full_update && !pipe_ctx->update_flags.bits.mpcc) { mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); return; } /* check if this MPCC is already being used */ new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id); /* remove MPCC if being used */ if (new_mpcc != NULL) mpc->funcs->remove_mpcc(mpc, mpc_tree_params, new_mpcc); else if (dc->debug.sanity_checks) mpc->funcs->assert_mpcc_idle_before_connect( dc->res_pool->mpc, mpcc_id); /* Call MPC to insert new plane */ new_mpcc = mpc->funcs->insert_plane(dc->res_pool->mpc, mpc_tree_params, &blnd_cfg, NULL, NULL, hubp->inst, mpcc_id); dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); ASSERT(new_mpcc != NULL); hubp->opp_id = pipe_ctx->stream_res.opp->inst; hubp->mpcc_id = mpcc_id; } void dcn42_program_cm_hist( struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) { (void)dc; struct dpp *dpp = pipe_ctx->plane_res.dpp; if (dpp && dpp->funcs->dpp_cm_hist_control) dpp->funcs->dpp_cm_hist_control(dpp, plane_state->cm_hist_control, plane_state->color_space); } static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc) { if (mpc->funcs->rmcm.power_on_shaper_3dlut && mpc->funcs->rmcm.fl_3dlut_configure && hubp->funcs->hubp_program_3dlut_fl_config) return true; return false; } bool dcn42_program_rmcm_luts( struct hubp *hubp, struct pipe_ctx *pipe_ctx, const struct dc_plane_cm *cm, struct mpc *mpc, int mpcc_id) { struct dpp *dpp_base = pipe_ctx->plane_res.dpp; union mcm_lut_params m_lut_params = {0}; struct dc *dc = hubp->ctx->dc; struct mpc_fl_3dlut_config mpc_fl_config; struct dc_stream_state *stream = pipe_ctx->stream; bool bypass_rmcm_shaper = false; // true->false when it can be allocated at DI time struct dc_rmcm_3dlut *rmcm_3dlut = dc_stream_get_3dlut_for_stream(dc, stream, false); bool lut_bank_a = true; // TODO get from HW //check to see current pipe is part of a stream with allocated rmcm 3dlut if (!rmcm_3dlut) return false; /* Shaper */ if (cm->flags.bits.shaper_enable) { memset(&m_lut_params, 0, sizeof(m_lut_params)); if (cm->shaper_func.type == TF_TYPE_HWPWL) { m_lut_params.pwl = &cm->shaper_func.pwl; } else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { ASSERT(false); cm_helper_translate_curve_to_hw_format( dc->ctx, &cm->shaper_func, &dpp_base->shaper_params, true); m_lut_params.pwl = &dpp_base->shaper_params; } if (m_lut_params.pwl) { if (mpc->funcs->rmcm.populate_lut) mpc->funcs->rmcm.populate_lut(mpc, m_lut_params, lut_bank_a, mpcc_id); if (mpc->funcs->rmcm.program_lut_mode) mpc->funcs->rmcm.program_lut_mode(mpc, !bypass_rmcm_shaper, lut_bank_a, mpcc_id); } else { //RMCM 3dlut won't work without its shaper return false; } } /* 3DLUT */ if (!cm->flags.bits.lut3d_dma_enable) { memset(&m_lut_params, 0, sizeof(m_lut_params)); // Don't know what to do in this case. } else { if (!dc_is_rmcm_3dlut_supported(hubp, mpc)) return false; //seems to be only for the MCM mpc_fl_config.enabled = cm->flags.bits.lut3d_enable; mpc_fl_config.size = cm->lut3d_dma.size; mpc_fl_config.select_lut_bank_a = lut_bank_a; mpc_fl_config.bit_depth = 0; mpc_fl_config.hubp_index = hubp->inst; mpc_fl_config.bias = cm->lut3d_dma.bias; mpc_fl_config.scale = cm->lut3d_dma.scale; //1. power down the block mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, false); //2. program RMCM - 3dlut reg programming mpc->funcs->rmcm.fl_3dlut_configure(mpc, &mpc_fl_config, mpcc_id); /* HUBP */ if (hubp->funcs->hubp_program_3dlut_fl_config) hubp->funcs->hubp_program_3dlut_fl_config(hubp, &cm->lut3d_dma); if (hubp->funcs->hubp_program_3dlut_fl_addr) hubp->funcs->hubp_program_3dlut_fl_addr(hubp, &cm->lut3d_dma.addr); //3. power on the block mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, true); } return true; } bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state) { struct dc *dc = pipe_ctx->plane_res.hubp->ctx->dc; struct hubp *hubp = pipe_ctx->plane_res.hubp; const struct dc_plane_cm *cm = &plane_state->cm; struct mpc *mpc = dc->res_pool->mpc; int mpcc_id = hubp->inst; bool result; /* MCM */ result = dcn401_set_mcm_luts(pipe_ctx, plane_state); /* RMCM */ if (cm->flags.bits.rmcm_enable && cm->flags.bits.lut3d_dma_enable) { /* TODO - move RMCM to its own block */ dcn42_program_rmcm_luts( hubp, pipe_ctx, cm, mpc, mpcc_id); } return result; } void dcn42_hardware_release(struct dc *dc) { dcn35_hardware_release(dc); } static int count_active_streams(const struct dc *dc) { int i, count = 0; for (i = 0; i < dc->current_state->stream_count; ++i) { struct dc_stream_state *stream = dc->current_state->streams[i]; if (stream && (!stream->dpms_off || dc->config.disable_ips_in_dpms_off)) count += 1; } return count; } void dcn42_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, struct pg_block_update *update_state) { bool hpo_frl_stream_enc_acquired = false; bool hpo_dp_stream_enc_acquired = false; int i = 0, j = 0; memset(update_state, 0, sizeof(struct pg_block_update)); update_state->pg_res_update[PG_DIO] = true; for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) { if (context->res_ctx.is_hpo_dp_stream_enc_acquired[i] && dc->res_pool->hpo_dp_stream_enc[i]) { hpo_dp_stream_enc_acquired = true; break; } } if (!hpo_frl_stream_enc_acquired && !hpo_dp_stream_enc_acquired) update_state->pg_res_update[PG_HPO] = true; for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; for (j = 0; j < PG_HW_PIPE_RESOURCES_NUM_ELEMENT; j++) update_state->pg_pipe_res_update[j][i] = true; if (!pipe_ctx) continue; if (pipe_ctx->plane_res.hubp) update_state->pg_pipe_res_update[PG_HUBP][pipe_ctx->plane_res.hubp->inst] = false; if (pipe_ctx->plane_res.dpp && pipe_ctx->plane_res.hubp) update_state->pg_pipe_res_update[PG_DPP][pipe_ctx->plane_res.hubp->inst] = false; if (pipe_ctx->plane_res.dpp || pipe_ctx->stream_res.opp) update_state->pg_pipe_res_update[PG_MPCC][pipe_ctx->plane_res.mpcc_inst] = false; if (pipe_ctx->stream_res.dsc) { update_state->pg_pipe_res_update[PG_DSC][pipe_ctx->stream_res.dsc->inst] = false; if (dc->caps.sequential_ono) { update_state->pg_pipe_res_update[PG_HUBP][pipe_ctx->stream_res.dsc->inst] = false; update_state->pg_pipe_res_update[PG_DPP][pipe_ctx->stream_res.dsc->inst] = false; } } if (pipe_ctx->stream_res.opp) update_state->pg_pipe_res_update[PG_OPP][pipe_ctx->stream_res.opp->inst] = false; if (pipe_ctx->stream_res.hpo_dp_stream_enc) update_state->pg_pipe_res_update[PG_DPSTREAM][pipe_ctx->stream_res.hpo_dp_stream_enc->inst] = false; if (pipe_ctx->link_res.dio_link_enc) { update_state->pg_res_update[PG_DIO] = false; } if (pipe_ctx->link_res.hpo_dp_link_enc) { update_state->pg_res_update[PG_HPO] = false; } } for (i = 0; i < dc->link_count; i++) { update_state->pg_pipe_res_update[PG_PHYSYMCLK][dc->links[i]->link_enc_hw_inst] = true; if (dc->links[i]->type != dc_connection_none) update_state->pg_pipe_res_update[PG_PHYSYMCLK][dc->links[i]->link_enc_hw_inst] = false; } /*domain24 controls all the otg, mpc, opp, as long as one otg is still up, avoid enabling OTG PG*/ for (i = 0; i < dc->res_pool->timing_generator_count; i++) { struct timing_generator *tg = dc->res_pool->timing_generators[i]; if (tg && tg->funcs->is_tg_enabled(tg)) { update_state->pg_pipe_res_update[PG_OPTC][i] = false; } else { // Update pg_pipe_res_enable state if (dc->res_pool->pg_cntl->funcs->optc_pg_control) dc->res_pool->pg_cntl->funcs->optc_pg_control(dc->res_pool->pg_cntl, i, false); } } } void dcn42_prepare_bandwidth( struct dc *dc, struct dc_state *context) { struct pg_block_update pg_update_state; if (dc->hwss.calc_blocks_to_ungate) { dc->hwss.calc_blocks_to_ungate(dc, context, &pg_update_state); if (dc->hwss.root_clock_control) dc->hwss.root_clock_control(dc, &pg_update_state, true); /*power up required HW block*/ if (dc->hwss.hw_block_power_up) dc->hwss.hw_block_power_up(dc, &pg_update_state); } dcn401_prepare_bandwidth(dc, context); } void dcn42_optimize_bandwidth(struct dc *dc, struct dc_state *context) { struct pg_block_update pg_update_state; print_pg_status(dc, __func__, ": before rcg and power up"); dcn401_optimize_bandwidth(dc, context); if (dc->hwss.calc_blocks_to_gate) { dc->hwss.calc_blocks_to_gate(dc, context, &pg_update_state); /*try to power down unused block*/ if (dc->hwss.hw_block_power_down) dc->hwss.hw_block_power_down(dc, &pg_update_state); if (dc->hwss.root_clock_control) dc->hwss.root_clock_control(dc, &pg_update_state, false); } print_pg_status(dc, __func__, ": after rcg and power up"); } void dcn42_calc_blocks_to_ungate(struct dc *dc, struct dc_state *context, struct pg_block_update *update_state) { bool hpo_dp_stream_enc_acquired = false; int i = 0, j = 0; memset(update_state, 0, sizeof(struct pg_block_update)); for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *cur_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; struct pipe_ctx *new_pipe = &context->res_ctx.pipe_ctx[i]; if (cur_pipe == NULL || new_pipe == NULL) continue; if ((!cur_pipe->plane_state && new_pipe->plane_state) || (!cur_pipe->stream && new_pipe->stream) || (cur_pipe->stream != new_pipe->stream && new_pipe->stream)) { // New pipe addition for (j = 0; j < PG_HW_PIPE_RESOURCES_NUM_ELEMENT; j++) { if (j == PG_HUBP && new_pipe->plane_res.hubp) update_state->pg_pipe_res_update[j][new_pipe->plane_res.hubp->inst] = true; if (j == PG_DPP && new_pipe->plane_res.dpp) update_state->pg_pipe_res_update[j][new_pipe->plane_res.dpp->inst] = true; if (j == PG_MPCC && new_pipe->plane_res.dpp) update_state->pg_pipe_res_update[j][new_pipe->plane_res.mpcc_inst] = true; if (j == PG_DSC && new_pipe->stream_res.dsc) update_state->pg_pipe_res_update[j][new_pipe->stream_res.dsc->inst] = true; if (j == PG_OPP && new_pipe->stream_res.opp) update_state->pg_pipe_res_update[j][new_pipe->stream_res.opp->inst] = true; if (j == PG_OPTC && new_pipe->stream_res.tg) update_state->pg_pipe_res_update[j][new_pipe->stream_res.tg->inst] = true; if (j == PG_DPSTREAM && new_pipe->stream_res.hpo_dp_stream_enc) update_state->pg_pipe_res_update[j][new_pipe->stream_res.hpo_dp_stream_enc->inst] = true; } } else if (cur_pipe->plane_state == new_pipe->plane_state || cur_pipe == new_pipe) { //unchanged pipes for (j = 0; j < PG_HW_PIPE_RESOURCES_NUM_ELEMENT; j++) { if (j == PG_HUBP && cur_pipe->plane_res.hubp != new_pipe->plane_res.hubp && new_pipe->plane_res.hubp) update_state->pg_pipe_res_update[j][new_pipe->plane_res.hubp->inst] = true; if (j == PG_DPP && cur_pipe->plane_res.dpp != new_pipe->plane_res.dpp && new_pipe->plane_res.dpp) update_state->pg_pipe_res_update[j][new_pipe->plane_res.dpp->inst] = true; if (j == PG_OPP && cur_pipe->stream_res.opp != new_pipe->stream_res.opp && new_pipe->stream_res.opp) update_state->pg_pipe_res_update[j][new_pipe->stream_res.opp->inst] = true; if (j == PG_DSC && cur_pipe->stream_res.dsc != new_pipe->stream_res.dsc && new_pipe->stream_res.dsc) update_state->pg_pipe_res_update[j][new_pipe->stream_res.dsc->inst] = true; if (j == PG_OPTC && cur_pipe->stream_res.tg != new_pipe->stream_res.tg && new_pipe->stream_res.tg) update_state->pg_pipe_res_update[j][new_pipe->stream_res.tg->inst] = true; if (j == PG_DPSTREAM && cur_pipe->stream_res.hpo_dp_stream_enc != new_pipe->stream_res.hpo_dp_stream_enc && new_pipe->stream_res.hpo_dp_stream_enc) update_state->pg_pipe_res_update[j][new_pipe->stream_res.hpo_dp_stream_enc->inst] = true; } } } for (i = 0; i < dc->link_count; i++) if (dc->links[i]->type != dc_connection_none) update_state->pg_pipe_res_update[PG_PHYSYMCLK][dc->links[i]->link_enc_hw_inst] = true; for (i = 0; i < dc->res_pool->stream_enc_count; i++) { if (dc->current_state->res_ctx.is_stream_enc_acquired[i]) { update_state->pg_res_update[PG_DIO] = true; break; } } for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) { if (context->res_ctx.is_hpo_dp_stream_enc_acquired[i] && dc->res_pool->hpo_dp_stream_enc[i]) { hpo_dp_stream_enc_acquired = true; break; } } if (hpo_dp_stream_enc_acquired) update_state->pg_res_update[PG_HPO] = true; if (count_active_streams(dc) > 0) { update_state->pg_res_update[PG_DCCG] = true; update_state->pg_res_update[PG_DCIO] = true; update_state->pg_res_update[PG_DCHUBBUB] = true; update_state->pg_res_update[PG_DCHVM] = true; update_state->pg_res_update[PG_DCOH] = true; } } /** * dcn42_hw_block_power_down() - power down sequence * * The following sequence describes the ON-OFF (ONO) for power down: * * ONO Region 4, DCPG 25: hpo * ONO Region 11, DCPG 3: dchubp3, dpp3 * ONO Region 9, DCPG 2: dchubp2, dpp2 * ONO Region 7, DCPG 1: dchubp1, dpp1 * ONO Region 5, DCPG 0: dchubp0, dpp0 * ONO Region 2, DCPG 23: dchubbub, dchububmem, dchvm * ONO Region 12, DCPG 19: dsc3 * ONO Region 10, DCPG 18: dsc2 * ONO Region 8, DCPG 17: dsc1 * ONO Region 6, DCPG 16: dsc0 * ONO Region 3, DCPG 24: mpc, opp, optc, dwb * ONO Region 1, DCPG 26: dio * ONO Region 0, DCPG 22: dccg dcio dcoh - SKIPPED * * No seuential ONO power up/down order for DCN42 * Driver PG should only be limited to DCHUBP/DPP, DSC, HPO and DIO, so further optimization can be done * * @dc: Current DC state * @update_state: update PG sequence states for HW block */ void dcn42_hw_block_power_down(struct dc *dc, struct pg_block_update *update_state) { int i = 0; struct pg_cntl *pg_cntl = dc->res_pool->pg_cntl; bool block_disabled = true; if (!pg_cntl) return; if (dc->debug.ignore_pg) return; if (update_state->pg_res_update[PG_HPO]) { if (pg_cntl->funcs->hpo_pg_control) pg_cntl->funcs->hpo_pg_control(pg_cntl, false); } for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) { if (update_state->pg_pipe_res_update[PG_HUBP][i] && update_state->pg_pipe_res_update[PG_DPP][i]) { if (pg_cntl->funcs->hubp_dpp_pg_control) pg_cntl->funcs->hubp_dpp_pg_control(pg_cntl, i, false); } } if (update_state->pg_res_update[PG_DCHUBBUB]) { if (pg_cntl->funcs->mem_pg_control) pg_cntl->funcs->mem_pg_control(pg_cntl, false); } for (i = dc->res_pool->res_cap->num_dsc-1; i >= 0; i--) { if (update_state->pg_pipe_res_update[PG_DSC][i]) { if (pg_cntl->funcs->dsc_pg_control) pg_cntl->funcs->dsc_pg_control(pg_cntl, i, false); } } for (i = 0; i < dc->res_pool->pipe_count; i++) { if (!update_state->pg_pipe_res_update[PG_MPCC][i] || !update_state->pg_pipe_res_update[PG_OPP][i] || !update_state->pg_pipe_res_update[PG_OPTC][i]) { block_disabled = false; break; } } if (block_disabled) { if (pg_cntl->funcs->plane_otg_pg_control) pg_cntl->funcs->plane_otg_pg_control(pg_cntl, false); } if (update_state->pg_res_update[PG_DIO]) { if (pg_cntl->funcs->dio_pg_control) pg_cntl->funcs->dio_pg_control(pg_cntl, false); } if (update_state->pg_res_update[PG_DCCG] && update_state->pg_res_update[PG_DCIO] && update_state->pg_res_update[PG_DCOH]) { // Driver PG should not power down DCCG, DCIO, DCOH. This is handled by IPS. if (pg_cntl->funcs->io_clk_pg_control) pg_cntl->funcs->io_clk_pg_control(pg_cntl, false); } } /** * dcn42_hw_block_power_up() - power up sequence * * The following sequence describes the ON-OFF (ONO) for power up: * * ONO Region 0, DCPG 22: dccg dcio dcoh * ONO Region 1, DCPG 26: dio * ONO Region 3, DCPG 24: mpc, opp, optc, dwb * ONO Region 6, DCPG 16: dsc0 * ONO Region 8, DCPG 17: dsc1 * ONO Region 10, DCPG 18: dsc2 * ONO Region 12, DCPG 19: dsc3 * ONO Region 2, DCPG 23: dchubbub, dchububmem, dchvm * ONO Region 5, DCPG 0: dchubp0, dpp0 * ONO Region 7, DCPG 1: dchubp1, dpp1 * ONO Region 9, DCPG 2: dchubp2, dpp2 * ONO Region 11, DCPG 3: dchubp3, dpp3 * ONO Region 4, DCPG 25: hpo * * No sequential power up/down ordering for DCN42 * * @dc: Current DC state * @update_state: update PG sequence states for HW block */ void dcn42_hw_block_power_up(struct dc *dc, struct pg_block_update *update_state) { int i = 0; struct pg_cntl *pg_cntl = dc->res_pool->pg_cntl; bool block_enabled = false; if (!pg_cntl) return; if (dc->debug.ignore_pg) return; if (update_state->pg_res_update[PG_DCCG] || update_state->pg_res_update[PG_DCIO] || update_state->pg_res_update[PG_DCOH]) { if (pg_cntl->funcs->io_clk_pg_control) pg_cntl->funcs->io_clk_pg_control(pg_cntl, true); } if (update_state->pg_res_update[PG_DIO]) { if (pg_cntl->funcs->dio_pg_control) pg_cntl->funcs->dio_pg_control(pg_cntl, true); } for (i = 0; i < dc->res_pool->pipe_count; i++) { if (update_state->pg_pipe_res_update[PG_MPCC][i] || update_state->pg_pipe_res_update[PG_OPP][i] || update_state->pg_pipe_res_update[PG_OPTC][i]) { block_enabled = true; break; } } if (block_enabled) { if (pg_cntl->funcs->plane_otg_pg_control) pg_cntl->funcs->plane_otg_pg_control(pg_cntl, true); } for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) { if (update_state->pg_pipe_res_update[PG_DSC][i]) { if (pg_cntl->funcs->dsc_pg_control) pg_cntl->funcs->dsc_pg_control(pg_cntl, i, true); } } if (update_state->pg_res_update[PG_DCHUBBUB]) { if (pg_cntl->funcs->mem_pg_control) pg_cntl->funcs->mem_pg_control(pg_cntl, true); } for (i = 0; i < dc->res_pool->pipe_count; i++) { if (update_state->pg_pipe_res_update[PG_HUBP][i] && update_state->pg_pipe_res_update[PG_DPP][i]) { if (pg_cntl->funcs->hubp_dpp_pg_control) pg_cntl->funcs->hubp_dpp_pg_control(pg_cntl, i, true); } } if (update_state->pg_res_update[PG_HPO]) { if (pg_cntl->funcs->hpo_pg_control) pg_cntl->funcs->hpo_pg_control(pg_cntl, true); } } void dcn42_root_clock_control(struct dc *dc, struct pg_block_update *update_state, bool power_on) { int i = 0; struct pg_cntl *pg_cntl = dc->res_pool->pg_cntl; if (!pg_cntl) return; /*enable root clock first when power up*/ if (power_on) { for (i = 0; i < dc->res_pool->pipe_count; i++) { if (update_state->pg_pipe_res_update[PG_HUBP][i] && update_state->pg_pipe_res_update[PG_DPP][i]) { if (dc->hwseq->funcs.dpp_root_clock_control) dc->hwseq->funcs.dpp_root_clock_control(dc->hwseq, i, power_on); } if (update_state->pg_pipe_res_update[PG_DPSTREAM][i]) if (dc->hwseq->funcs.dpstream_root_clock_control) dc->hwseq->funcs.dpstream_root_clock_control(dc->hwseq, i, power_on); } for (i = 0; i < dc->res_pool->dig_link_enc_count; i++) if (update_state->pg_pipe_res_update[PG_PHYSYMCLK][i]) if (dc->hwseq->funcs.physymclk_root_clock_control) dc->hwseq->funcs.physymclk_root_clock_control(dc->hwseq, i, power_on); } for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) { if (update_state->pg_pipe_res_update[PG_DSC][i]) { if (power_on) { if (dc->res_pool->dccg->funcs->enable_dsc) dc->res_pool->dccg->funcs->enable_dsc(dc->res_pool->dccg, i); } else { if (dc->res_pool->dccg->funcs->disable_dsc) dc->res_pool->dccg->funcs->disable_dsc(dc->res_pool->dccg, i); } } } /*disable root clock first when power down*/ if (!power_on) { for (i = 0; i < dc->res_pool->pipe_count; i++) { if (update_state->pg_pipe_res_update[PG_HUBP][i] && update_state->pg_pipe_res_update[PG_DPP][i]) { if (dc->hwseq->funcs.dpp_root_clock_control) dc->hwseq->funcs.dpp_root_clock_control(dc->hwseq, i, power_on); } if (update_state->pg_pipe_res_update[PG_DPSTREAM][i]) if (dc->hwseq->funcs.dpstream_root_clock_control) dc->hwseq->funcs.dpstream_root_clock_control(dc->hwseq, i, power_on); } for (i = 0; i < dc->res_pool->dig_link_enc_count; i++) if (update_state->pg_pipe_res_update[PG_PHYSYMCLK][i]) if (dc->hwseq->funcs.physymclk_root_clock_control) dc->hwseq->funcs.physymclk_root_clock_control(dc->hwseq, i, power_on); } } void dcn42_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) { (void)dc; struct crtc_stereo_flags flags = { 0 }; struct dc_stream_state *stream = pipe_ctx->stream; dcn10_config_stereo_parameters(stream, &flags); pipe_ctx->stream_res.opp->funcs->opp_program_stereo( pipe_ctx->stream_res.opp, flags.PROGRAM_STEREO == 1, &stream->timing); pipe_ctx->stream_res.tg->funcs->program_stereo( pipe_ctx->stream_res.tg, &stream->timing, &flags); return; } void dcn42_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) { union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; if (!dc->ctx || !dc->ctx->dmub_srv) return; /* Use helper to check PSR/Replay for all streams in context */ if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc) && !dmub_hw_lock_mgr_does_context_require_lock(dc, context)) return; hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; hw_lock_cmd.bits.lock = lock; hw_lock_cmd.bits.should_release = !lock; dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); } void dcn42_dmub_hw_control_lock_fast(union block_sequence_params *params) { struct dc *dc = params->dmub_hw_control_lock_fast_params.dc; bool lock = params->dmub_hw_control_lock_fast_params.lock; /* Use helper to check PSR/Replay for the given stream in fast path */ if (params->dmub_hw_control_lock_fast_params.is_required) { union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 }; hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK; hw_lock_cmd.bits.hw_lock_client = HW_LOCK_CLIENT_DRIVER; hw_lock_cmd.bits.lock = lock; hw_lock_cmd.bits.should_release = !lock; dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd); } } /* In headless boot cases, DIG may be turned * on which causes HW/SW discrepancies. * To avoid this, power down hardware on boot * if DIG is turned on */ void dcn42_power_down_on_boot(struct dc *dc) { struct dc_link *edp_links[MAX_NUM_EDP]; struct dc_link *edp_link = NULL; unsigned int edp_num; int i = 0; dc_get_edp_links(dc, edp_links, &edp_num); if (edp_num) edp_link = edp_links[0]; if (edp_link && edp_link->link_enc->funcs->is_dig_enabled && edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) && dc->hwseq->funcs.edp_backlight_control && dc->hwseq->funcs.power_down && dc->hwss.edp_power_control) { dc->hwseq->funcs.edp_backlight_control(edp_link, false); dc->hwseq->funcs.power_down(dc); dc->hwss.edp_power_control(edp_link, false); } else { for (i = 0; i < dc->link_count; i++) { struct dc_link *link = dc->links[i]; if (link->link_enc && link->link_enc->funcs->is_dig_enabled && link->link_enc->funcs->is_dig_enabled(link->link_enc) && dc->hwseq->funcs.power_down) { dc->hwseq->funcs.power_down(dc); break; } } } /* * Call update_clocks with empty context * to send DISPLAY_OFF * Otherwise DISPLAY_OFF may not be asserted */ if (dc->clk_mgr->funcs->set_low_power_state) dc->clk_mgr->funcs->set_low_power_state(dc->clk_mgr); }
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