/* * Copyright 2020 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD *
*/
bool dcn30_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, conststruct dc_stream_state *stream)
{ int mpcc_id = pipe_ctx->plane_res.hubp->inst; struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; conststruct pwl_params *params = NULL; bool ret = false;
/* program OGAM or 3DLUT only for the top pipe*/ if (pipe_ctx->top_pipe == NULL) { /*program rmu shaper and 3dlut in MPC*/
ret = dcn30_set_mpc_shaper_3dlut(pipe_ctx, stream); if (ret == false && mpc->funcs->set_output_gamma) { if (stream->out_transfer_func.type == TF_TYPE_HWPWL)
params = &stream->out_transfer_func.pwl; elseif (pipe_ctx->stream->out_transfer_func.type ==
TF_TYPE_DISTRIBUTED_POINTS &&
cm3_helper_translate_curve_to_hw_format(
&stream->out_transfer_func,
&mpc->blender_params, false))
params = &mpc->blender_params; /* there are no ROM LUTs in OUTGAM */ if (stream->out_transfer_func.type == TF_TYPE_PREDEFINED)
BREAK_TO_DEBUGGER();
}
}
if (mpc->funcs->set_output_gamma)
mpc->funcs->set_output_gamma(mpc, mpcc_id, params); else
DC_LOG_ERROR("%s: set_output_gamma function pointer is NULL.\n", __func__);
bool dcn30_mmhubbub_warmup( struct dc *dc, unsignedint num_dwb, struct dc_writeback_info *wb_info)
{ struct dwbc *dwb; struct mcif_wb *mcif_wb; struct mcif_warmup_params warmup_params = {0}; unsignedint i, i_buf; /* make sure there is no active DWB enabled */ for (i = 0; i < num_dwb; i++) {
dwb = dc->res_pool->dwbc[wb_info[i].dwb_pipe_inst]; if (dwb->dwb_is_efc_transition || dwb->dwb_is_drc) { /*can not do warmup while any dwb enabled*/ returnfalse;
}
}
if (wb_info->mcif_warmup_params.p_vmid == 0) returnfalse;
/*check whether this is new interface: warmup big buffer once*/ if (wb_info->mcif_warmup_params.start_address.quad_part != 0 &&
wb_info->mcif_warmup_params.region_size != 0) { /*mmhubbub is shared, so it does not matter which MCIF*/
mcif_wb = dc->res_pool->mcif_wb[0]; /*warmup a big chunk of VM buffer at once*/
warmup_params.start_address.quad_part = wb_info->mcif_warmup_params.start_address.quad_part;
warmup_params.address_increment = wb_info->mcif_warmup_params.region_size;
warmup_params.region_size = wb_info->mcif_warmup_params.region_size;
warmup_params.p_vmid = wb_info->mcif_warmup_params.p_vmid;
if (warmup_params.address_increment == 0)
warmup_params.address_increment = dc->dml.soc.vmm_page_size_bytes;
mcif_wb->funcs->warmup_mcif(mcif_wb, &warmup_params); returntrue;
} /*following is the original: warmup each DWB's mcif buffer*/ for (i = 0; i < num_dwb; i++) {
mcif_wb = dc->res_pool->mcif_wb[wb_info[i].dwb_pipe_inst]; /*warmup is for VM mode only*/ if (wb_info[i].mcif_buf_params.p_vmid == 0) returnfalse;
if (wb_info.mpcc_inst == -1) { /* Disable writeback pipe and disconnect from MPCC * if source plane has been removed
*/
dc->hwss.disable_writeback(dc, wb_info.dwb_pipe_inst); continue;
}
ASSERT(wb_info.dwb_pipe_inst < dc->res_pool->res_cap->num_dwb);
dwb = dc->res_pool->dwbc[wb_info.dwb_pipe_inst]; if (dwb->funcs->is_enabled(dwb)) { /* writeback pipe already enabled, only need to update */
dc->hwss.update_writeback(dc, &wb_info, context);
} else { /* Enable writeback pipe and connect to MPCC */
dc->hwss.enable_writeback(dc, &wb_info, context);
}
} else { /* Disable writeback pipe and disconnect from MPCC */
dc->hwss.disable_writeback(dc, wb_info.dwb_pipe_inst);
}
}
}
if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks)
dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);
// Initialize the dccg if (res_pool->dccg->funcs->dccg_init)
res_pool->dccg->funcs->dccg_init(res_pool->dccg);
if (!dcb->funcs->is_accelerated_mode(dcb)) {
hws->funcs.bios_golden_init(dc);
hws->funcs.disable_vga(dc->hwseq);
}
if (dc->debug.enable_mem_low_power.bits.dmcu) { // Force ERAM to shutdown if DMCU is not enabled if (dc->debug.disable_dmcu || dc->config.disable_dmcu) {
REG_UPDATE(DMU_MEM_PWR_CNTL, DMCU_ERAM_MEM_PWR_FORCE, 3);
}
}
// 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;
(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];
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; 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;
}
}
/* we want to turn off all dp displays before doing detection */
dc->link_srv->blank_all_dp_displays(dc);
if (hws->funcs.enable_power_gating_plane)
hws->funcs.enable_power_gating_plane(dc->hwseq, true);
/* 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) {
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 = NULL;
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->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_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];
for (i = 0; i < dc->res_pool->pipe_count; i++) { if (abms[i] != NULL)
abms[i]->funcs->abm_init(abms[i], backlight, user_level);
}
/* power AFMT HDMI memory TODO: may move to dis/en output save power*/
REG_WRITE(DIO_MEM_PWR_CTRL, 0);
if (!dc->debug.disable_clock_gate) { /* enable all DCN clock gating */
REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
}
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 softmax is enabled then hardmax will be set by a different call if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->set_hard_max_memclk &&
!dc->clk_mgr->dc_mode_softmax_enabled)
dc->clk_mgr->funcs->set_hard_max_memclk(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_is_hdmi_signal(pipe_ctx->stream->signal) && pipe_ctx->stream_res.stream_enc != NULL) {
pipe_ctx->stream_res.stream_enc->funcs->set_avmute(
pipe_ctx->stream_res.stream_enc,
enable);
/* Wait for two frame to make sure AV mute is sent out */ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
}
}
}
/* if using dynamic meta, don't set up generic infopackets */ if (pipe_ctx->stream->dmdata_address.quad_part != 0) {
pipe_ctx->stream_res.encoder_info_frame.hdrsmd.valid = false;
enable = true;
}
if (!hubp) return;
if (!stream_enc || !stream_enc->funcs->set_dynamic_metadata) return;
/* First, check no-memory-requests case */ for (i = 0; i < dc->current_state->stream_count; i++) { if (dc->current_state->stream_status[i].plane_count) /* Fail eligibility on a visible stream */ break;
}
if (i == dc->current_state->stream_count) { /* Enable no-memory-requests case */
memset(&cmd, 0, sizeof(cmd));
cmd.mall.header.type = DMUB_CMD__MALL;
cmd.mall.header.sub_type = DMUB_CMD__MALL_ACTION_NO_DF_REQ;
cmd.mall.header.payload_bytes = sizeof(cmd.mall) - sizeof(cmd.mall.header);
/* * Second, check MALL eligibility * * single display only, single surface only, 8 and 16 bit formats only, no VM, * do not use MALL for displays that support PSR as they use D0i3.2 in DMCUB FW * * TODO: When we implement multi-display, PSR displays will be allowed if there is * a non-PSR display present, since in that case we can't do D0i3.2
*/ if (dc->current_state->stream_count == 1 &&
stream->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED &&
dc->current_state->stream_status[0].plane_count == 1 &&
plane->format <= SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F &&
plane->format >= SURFACE_PIXEL_FORMAT_GRPH_ARGB8888 &&
plane->address.page_table_base.quad_part == 0 &&
dc->hwss.does_plane_fit_in_mall &&
dc->hwss.does_plane_fit_in_mall(dc, plane->plane_size.surface_pitch,
plane->plane_size.surface_size.height, plane->format,
cursor_cache_enable ? &cursor_attr : NULL)) { unsignedint v_total = stream->adjust.v_total_max ?
stream->adjust.v_total_max : stream->timing.v_total; unsignedint refresh_hz = div_u64((unsignedlonglong) stream->timing.pix_clk_100hz *
100LL, (v_total * stream->timing.h_total));
/* * one frame time in microsec: * Delay_Us = 1000000 / refresh * dynamic_delay_us = 1000000 / refresh + 2 * stutter_period * * one frame time modified by 'additional timer percent' (p): * Delay_Us_modified = dynamic_delay_us + dynamic_delay_us * p / 100 * = dynamic_delay_us * (1 + p / 100) * = (1000000 / refresh + 2 * stutter_period) * (100 + p) / 100 * = (1000000 + 2 * stutter_period * refresh) * (100 + p) / (100 * refresh) * * formula for timer duration based on parameters, from regspec: * dynamic_delay_us = 65.28 * (64 + MallFrameCacheTmrDly) * 2^MallFrameCacheTmrScale * * dynamic_delay_us / 65.28 = (64 + MallFrameCacheTmrDly) * 2^MallFrameCacheTmrScale * (dynamic_delay_us / 65.28) / 2^MallFrameCacheTmrScale = 64 + MallFrameCacheTmrDly * MallFrameCacheTmrDly = ((dynamic_delay_us / 65.28) / 2^MallFrameCacheTmrScale) - 64 * = (1000000 + 2 * stutter_period * refresh) * (100 + p) / (100 * refresh) / 65.28 / 2^MallFrameCacheTmrScale - 64 * = (1000000 + 2 * stutter_period * refresh) * (100 + p) / (refresh * 6528 * 2^MallFrameCacheTmrScale) - 64 * * need to round up the result of the division before the subtraction
*/ unsignedint denom = refresh_hz * 6528; unsignedint stutter_period = dc->current_state->perf_params.stutter_period_us;
/* In some cases the stutter period is really big (tiny modes) in these * cases MALL cant be enabled, So skip these cases to avoid a ASSERT() * * We can check if stutter_period is more than 1/10th the frame time to * consider if we can actually meet the range of hysteresis timer
*/ if (stutter_period > 100000/refresh_hz) returnfalse;
/* scale should be increased until it fits into 6 bits */ while (tmr_delay & ~0x3F) {
tmr_scale++;
if (tmr_scale > 3) { /* Delay exceeds range of hysteresis timer */
ASSERT(false); returnfalse;
}
switch (cursor_attr.color_format) { case CURSOR_MODE_MONO:
cmd.mall.cursor_bpp = 2; break; case CURSOR_MODE_COLOR_1BIT_AND: case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
cmd.mall.cursor_bpp = 32; break;
case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED: case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED:
cmd.mall.cursor_bpp = 64; break;
}
/* Use copied cursor, and it's okay to not switch back */
cursor_attr.address.quad_part = cmd.mall.cursor_copy_dst.quad_part;
dc_stream_program_cursor_attributes(stream, &cursor_attr);
}
if (dc->debug.mall_size_override)
mall_size = 1024 * 1024 * dc->debug.mall_size_override;
if (cursor_attr) {
cursor_size = dc->caps.max_cursor_size * dc->caps.max_cursor_size;
switch (cursor_attr->color_format) { case CURSOR_MODE_MONO:
cursor_size /= 2; break; case CURSOR_MODE_COLOR_1BIT_AND: case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA: case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
cursor_size *= 4; break;
case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED: case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED:
cursor_size *= 8; break;
}
}
/* SubVP treated the same way as FPO. If driver disable and * we are using a SubVP config, disable and force on DCN side * to prevent P-State hang on driver enable.
*/ for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];
if (!pipe->stream) continue;
if (dc_state_get_pipe_subvp_type(dc->current_state, pipe) == SUBVP_MAIN) {
subvp_in_use = true; break;
}
} /* If pstate unsupported, or still supported * by firmware, force it supported by dcn
*/ if (dc->current_state) if ((!dc->clk_mgr->clks.p_state_change_support || subvp_in_use ||
dc->current_state->bw_ctx.bw.dcn.clk.fw_based_mclk_switching) &&
dc->res_pool->hubbub->funcs->force_pstate_change_control)
dc->res_pool->hubbub->funcs->force_pstate_change_control(
dc->res_pool->hubbub, true, true);
}
void dcn30_set_disp_pattern_generator(conststruct dc *dc, struct pipe_ctx *pipe_ctx, enum controller_dp_test_pattern test_pattern, enum controller_dp_color_space color_space, enum dc_color_depth color_depth, conststruct tg_color *solid_color, int width, int height, int offset)
{
pipe_ctx->stream_res.opp->funcs->opp_set_disp_pattern_generator(pipe_ctx->stream_res.opp, test_pattern,
color_space, color_depth, solid_color, width, height, offset);
}
void dcn30_prepare_bandwidth(struct dc *dc, struct dc_state *context)
{ if (context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching && !dc->clk_mgr->clks.fw_based_mclk_switching) {
dc->optimized_required = true;
context->bw_ctx.bw.dcn.clk.p_state_change_support = false;
}
if (dc->clk_mgr->dc_mode_softmax_enabled) if (dc->clk_mgr->clks.dramclk_khz <= dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
context->bw_ctx.bw.dcn.clk.dramclk_khz > dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz);
dcn20_prepare_bandwidth(dc, context);
if (!dc->clk_mgr->clks.fw_based_mclk_switching)
dc_dmub_srv_p_state_delegate(dc, false, context);
}
if (tg && tg->funcs->is_tg_enabled(tg)) { // Poll for 100ms maximum for (i = 0; i < 100000; i++) {
pending_updates = false; if (tg->funcs->get_optc_double_buffer_pending)
pending_updates |= tg->funcs->get_optc_double_buffer_pending(tg);
if (tg->funcs->get_otg_double_buffer_pending)
pending_updates |= tg->funcs->get_otg_double_buffer_pending(tg);
if (tg->funcs->get_pipe_update_pending && pipe_ctx->plane_state)
pending_updates |= tg->funcs->get_pipe_update_pending(tg);
if (!pending_updates) break;
udelay(1);
}
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.