/* * Copyright 2016 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 *
*/ #include <linux/delay.h>
staticint find_free_gsl_group(conststruct dc *dc)
{ if (dc->res_pool->gsl_groups.gsl_0 == 0) return1; if (dc->res_pool->gsl_groups.gsl_1 == 0) return2; if (dc->res_pool->gsl_groups.gsl_2 == 0) return3;
return0;
}
/* NOTE: This is not a generic setup_gsl function (hence the suffix as_lock) * This is only used to lock pipes in pipe splitting case with immediate flip * Ordinary MPC/OTG locks suppress VUPDATE which doesn't help with immediate, * so we get tearing with freesync since we cannot flip multiple pipes * atomically. * We use GSL for this: * - immediate flip: find first available GSL group if not already assigned * program gsl with that group, set current OTG as master * and always us 0x4 = AND of flip_ready from all pipes * - vsync flip: disable GSL if used * * Groups in stream_res are stored as +1 from HW registers, i.e. * gsl_0 <=> pipe_ctx->stream_res.gsl_group == 1 * Using a magic value like -1 would require tracking all inits/resets
*/ void dcn20_setup_gsl_group_as_lock( conststruct dc *dc, struct pipe_ctx *pipe_ctx, bool enable)
{ struct gsl_params gsl; int group_idx;
memset(&gsl, 0, sizeof(struct gsl_params));
if (enable) { /* return if group already assigned since GSL was set up * for vsync flip, we would unassign so it can't be "left over"
*/ if (pipe_ctx->stream_res.gsl_group > 0) return;
/* at this point we want to program whether it's to enable or disable */ if (pipe_ctx->stream_res.tg->funcs->set_gsl != NULL) {
pipe_ctx->stream_res.tg->funcs->set_gsl(
pipe_ctx->stream_res.tg,
&gsl); if (pipe_ctx->stream_res.tg->funcs->set_gsl_source_select != NULL)
pipe_ctx->stream_res.tg->funcs->set_gsl_source_select(
pipe_ctx->stream_res.tg, group_idx, enable ? 4 : 0);
} else
BREAK_TO_DEBUGGER();
}
/* program opp dpg blank color */
color_space = COLOR_SPACE_SRGB;
color_space_to_black_color(dc, color_space, &black_color);
/* get the OTG active size */
tg->funcs->get_otg_active_size(tg,
&otg_active_width,
&otg_active_height);
/* get the OPTC source */
tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1);
if (opp_id_src0 >= dc->res_pool->res_cap->num_opp) {
ASSERT(false); return;
}
opp = dc->res_pool->opps[opp_id_src0];
/* don't override the blank pattern if already enabled with the correct one. */ if (opp->funcs->dpg_is_blanked && opp->funcs->dpg_is_blanked(opp)) return;
if (num_opps == 2) {
otg_active_width = otg_active_width / 2;
REG_WAIT(DOMAIN9_PG_STATUS,
DOMAIN9_PGFSM_PWR_STATUS, pwr_status, 1, 1000); break; case5: /* DPP5 */ /* * Do not power gate DPP5, should be left at HW default, power on permanently. * PG on Pipe5 is De-featured, attempting to put it to PG state may result in hard * reset. * REG_UPDATE(DOMAIN11_PG_CONFIG, * DOMAIN11_POWER_GATE, power_gate); * * REG_WAIT(DOMAIN11_PG_STATUS, * DOMAIN11_PGFSM_PWR_STATUS, pwr_status, * 1, 1000);
*/ break; default:
BREAK_TO_DEBUGGER(); break;
}
}
REG_WAIT(DOMAIN8_PG_STATUS,
DOMAIN8_PGFSM_PWR_STATUS, pwr_status, 1, 1000); break; case5: /* DCHUBP5 */ /* * Do not power gate DCHUB5, should be left at HW default, power on permanently. * PG on Pipe5 is De-featured, attempting to put it to PG state may result in hard * reset. * REG_UPDATE(DOMAIN10_PG_CONFIG, * DOMAIN10_POWER_GATE, power_gate); * * REG_WAIT(DOMAIN10_PG_STATUS, * DOMAIN10_PGFSM_PWR_STATUS, pwr_status, * 1, 1000);
*/ break; default:
BREAK_TO_DEBUGGER(); break;
}
}
/* disable HW used by plane. * note: cannot disable until disconnect is complete
*/ void dcn20_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx)
{ struct dce_hwseq *hws = dc->hwseq; struct hubp *hubp = pipe_ctx->plane_res.hubp; struct dpp *dpp = pipe_ctx->plane_res.dpp;
/* In flip immediate with pipe splitting case GSL is used for * synchronization so we must disable it when the plane is disabled.
*/ if (pipe_ctx->stream_res.gsl_group != 0)
dcn20_setup_gsl_group_as_lock(dc, pipe_ctx, false);
if (hubp->funcs->hubp_update_mall_sel)
hubp->funcs->hubp_update_mall_sel(hubp, 0, false);
if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated) return;
dcn20_plane_atomic_disable(dc, pipe_ctx);
/* Turn back off the phantom OTG after the phantom plane is fully disabled
*/ if (is_phantom) if (tg && tg->funcs->disable_phantom_crtc)
tg->funcs->disable_phantom_crtc(tg);
DC_LOG_DC("Power down front end %d\n",
pipe_ctx->pipe_idx);
}
if (dc->res_pool->dccg->funcs->set_pixel_rate_div)
dc->res_pool->dccg->funcs->set_pixel_rate_div(
dc->res_pool->dccg,
pipe_ctx->stream_res.tg->inst,
pipe_ctx->pixel_rate_divider.div_factor1,
pipe_ctx->pixel_rate_divider.div_factor2);
/* by upper caller loop, pipe0 is parent pipe and be called first. * back end is set up by for pipe0. Other children pipe share back end * with pipe 0. No program is needed.
*/ if (pipe_ctx->top_pipe != NULL) return DC_OK;
/* TODO check if timing_changed, disable stream if timing changed */
opp_cnt = resource_get_opp_heads_for_otg_master(pipe_ctx, &context->res_ctx, opp_heads); for (i = 0; i < opp_cnt; i++)
opp_inst[i] = opp_heads[i]->stream_res.opp->inst;
rate_control_2x_pclk = rate_control_2x_pclk || opp_cnt > 1;
flow_control.flow_ctrl_mode = 0;
flow_control.flow_ctrl_cnt0 = 0x80;
flow_control.flow_ctrl_cnt1 = calc_mpc_flow_ctrl_cnt(stream, opp_cnt,
is_two_pixels_per_container); if (mpc->funcs->set_out_rate_control) { for (i = 0; i < opp_cnt; ++i) {
mpc->funcs->set_out_rate_control(
mpc, opp_inst[i], true,
rate_control_2x_pclk,
&flow_control);
}
}
for (i = 0; i < opp_cnt; i++) {
opp_heads[i]->stream_res.opp->funcs->opp_pipe_clock_control(
opp_heads[i]->stream_res.opp, true);
opp_heads[i]->stream_res.opp->funcs->opp_program_left_edge_extra_pixel(
opp_heads[i]->stream_res.opp,
stream->timing.pixel_encoding,
resource_is_pipe_type(opp_heads[i], OTG_MASTER));
}
hws->funcs.blank_pixel_data(dc, pipe_ctx, true);
/* VTG is within DCHUB command block. DCFCLK is always on */ if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) {
BREAK_TO_DEBUGGER(); return DC_ERROR_UNEXPECTED;
}
// DRR should set trigger event to monitor surface update event if (stream->adjust.v_total_min != 0 && stream->adjust.v_total_max != 0)
event_triggers = 0x80; /* Event triggers and num frames initialized for DRR, but can be * later updated for PSR use. Note DRR trigger events are generated * regardless of whether num frames met.
*/ if (pipe_ctx->stream_res.tg->funcs->set_static_screen_control)
pipe_ctx->stream_res.tg->funcs->set_static_screen_control(
pipe_ctx->stream_res.tg, event_triggers, 2);
/* TODO program crtc source select for non-virtual signal*/ /* TODO program FMT */ /* TODO setup link_enc */ /* TODO set stream attributes */ /* TODO program audio */ /* TODO enable stream if timing changed */ /* TODO unblank stream if DP */
if (dc_state_get_pipe_subvp_type(context, pipe_ctx) == SUBVP_PHANTOM) { if (pipe_ctx->stream_res.tg->funcs->phantom_crtc_post_enable)
pipe_ctx->stream_res.tg->funcs->phantom_crtc_post_enable(pipe_ctx->stream_res.tg);
}
return DC_OK;
}
void dcn20_program_output_csc(struct dc *dc, struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace,
uint16_t *matrix, int opp_id)
{ struct mpc *mpc = dc->res_pool->mpc; enum mpc_output_csc_mode ocsc_mode = MPC_OUTPUT_CSC_COEF_A; int mpcc_id = pipe_ctx->plane_res.hubp->inst;
if (mpc->funcs->power_on_mpc_mem_pwr)
mpc->funcs->power_on_mpc_mem_pwr(mpc, mpcc_id, true);
if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) { if (mpc->funcs->set_output_csc != NULL)
mpc->funcs->set_output_csc(mpc,
opp_id,
matrix,
ocsc_mode);
} else { if (mpc->funcs->set_ocsc_default != NULL)
mpc->funcs->set_ocsc_default(mpc,
opp_id,
colorspace,
ocsc_mode);
}
}
bool dcn20_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; /* * program OGAM only for the top pipe * if there is a pipe split then fix diagnostic is required: * how to pass OGAM parameter for stream. * if programming for all pipes is required then remove condition * pipe_ctx->top_pipe == NULL ,but then fix the diagnostic.
*/ if (mpc->funcs->power_on_mpc_mem_pwr)
mpc->funcs->power_on_mpc_mem_pwr(mpc, mpcc_id, true); if (pipe_ctx->top_pipe == NULL
&& 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 &&
cm_helper_translate_curve_to_hw_format(dc->ctx,
&stream->out_transfer_func,
&mpc->blender_params, false))
params = &mpc->blender_params; /* * there is no ROM
*/ if (stream->out_transfer_func.type == TF_TYPE_PREDEFINED)
BREAK_TO_DEBUGGER();
} /* * if above if is not executed then 'params' equal to 0 and set in bypass
*/ if (mpc->funcs->set_output_gamma)
mpc->funcs->set_output_gamma(mpc, mpcc_id, params);
result = dpp_base->funcs->dpp_program_shaper_lut(dpp_base, shaper_lut); if (plane_state->lut3d_func.state.bits.initialized == 1)
result = dpp_base->funcs->dpp_program_3dlut(dpp_base,
&plane_state->lut3d_func.lut_3d); else
result = dpp_base->funcs->dpp_program_3dlut(dpp_base, NULL);
if (use_degamma_ram == true) { if (tf->type == TF_TYPE_HWPWL)
dpp_base->funcs->dpp_program_degamma_pwl(dpp_base,
&tf->pwl); elseif (tf->type == TF_TYPE_DISTRIBUTED_POINTS) {
cm_helper_translate_curve_to_degamma_hw_format(tf,
&dpp_base->degamma_params);
dpp_base->funcs->dpp_program_degamma_pwl(dpp_base,
&dpp_base->degamma_params);
} returntrue;
} /* handle here the optimized cases when de-gamma ROM could be used. *
*/ if (tf->type == TF_TYPE_PREDEFINED) { switch (tf->tf) { case TRANSFER_FUNCTION_SRGB:
dpp_base->funcs->dpp_set_degamma(dpp_base,
IPP_DEGAMMA_MODE_HW_sRGB); break; case TRANSFER_FUNCTION_BT709:
dpp_base->funcs->dpp_set_degamma(dpp_base,
IPP_DEGAMMA_MODE_HW_xvYCC); break; case TRANSFER_FUNCTION_LINEAR:
dpp_base->funcs->dpp_set_degamma(dpp_base,
IPP_DEGAMMA_MODE_BYPASS); break; case TRANSFER_FUNCTION_PQ:
dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_USER_PWL);
cm_helper_translate_curve_to_degamma_hw_format(tf, &dpp_base->degamma_params);
dpp_base->funcs->dpp_program_degamma_pwl(dpp_base, &dpp_base->degamma_params);
result = true; break; default:
result = false; break;
}
} elseif (tf->type == TF_TYPE_BYPASS)
dpp_base->funcs->dpp_set_degamma(dpp_base,
IPP_DEGAMMA_MODE_BYPASS); else { /* * if we are here, we did not handle correctly. * fix is required for this use case
*/
BREAK_TO_DEBUGGER();
dpp_base->funcs->dpp_set_degamma(dpp_base,
IPP_DEGAMMA_MODE_BYPASS);
}
return result;
}
void dcn20_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx)
{ struct pipe_ctx *odm_pipe; int opp_cnt = 1; int opp_inst[MAX_PIPES] = { pipe_ctx->stream_res.opp->inst }; int odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, false); int last_odm_slice_width = resource_get_odm_slice_dst_width(pipe_ctx, true);
/* use TG master update lock to lock everything on the TG * therefore only top pipe need to lock
*/ if (!pipe || pipe->top_pipe) return;
if (pipe->plane_state != NULL)
flip_immediate = pipe->plane_state->flip_immediate;
if (pipe->stream_res.gsl_group > 0) {
temp_pipe = pipe->bottom_pipe; while (!flip_immediate && temp_pipe) { if (temp_pipe->plane_state != NULL)
flip_immediate = temp_pipe->plane_state->flip_immediate;
temp_pipe = temp_pipe->bottom_pipe;
}
}
if (flip_immediate && lock) { constint TIMEOUT_FOR_FLIP_PENDING_US = 100000; unsignedint polling_interval_us = 1; int i;
temp_pipe = pipe; while (temp_pipe) { if (temp_pipe->plane_state && temp_pipe->plane_state->flip_immediate) { for (i = 0; i < TIMEOUT_FOR_FLIP_PENDING_US / polling_interval_us; ++i) { if (!temp_pipe->plane_res.hubp->funcs->hubp_is_flip_pending(temp_pipe->plane_res.hubp)) break;
udelay(polling_interval_us);
}
/* no reason it should take this long for immediate flips */
ASSERT(i != TIMEOUT_FOR_FLIP_PENDING_US);
}
temp_pipe = temp_pipe->bottom_pipe;
}
}
/* In flip immediate and pipe splitting case, we need to use GSL * for synchronization. Only do setup on locking and on flip type change.
*/ if (lock && (pipe->bottom_pipe != NULL || !flip_immediate)) if ((flip_immediate && pipe->stream_res.gsl_group == 0) ||
(!flip_immediate && pipe->stream_res.gsl_group > 0))
dcn20_setup_gsl_group_as_lock(dc, pipe, flip_immediate);
if (pipe->plane_state != NULL)
flip_immediate = pipe->plane_state->flip_immediate;
temp_pipe = pipe->bottom_pipe; while (flip_immediate && temp_pipe) { if (temp_pipe->plane_state != NULL)
flip_immediate = temp_pipe->plane_state->flip_immediate;
temp_pipe = temp_pipe->bottom_pipe;
}
/* If non-phantom pipe is being transitioned to a phantom pipe, * set disable and return immediately. This is because the pipe * that was previously in use must be fully disabled before we * can "enable" it as a phantom pipe (since the OTG will certainly * be different). The post_unlock sequence will set the correct * update flags to enable the phantom pipe.
*/ if (old_pipe->plane_state && !old_is_phantom &&
new_pipe->plane_state && new_is_phantom) {
new_pipe->update_flags.bits.disable = 1; return;
}
/* For SubVP we need to unconditionally enable because any phantom pipes are * always removed then newly added for every full updates whenever SubVP is in use. * The remove-add sequence of the phantom pipe always results in the pipe * being blanked in enable_stream_timing (DPG).
*/ if (new_pipe->stream && dc_state_get_pipe_subvp_type(new_state, new_pipe) == SUBVP_PHANTOM)
new_pipe->update_flags.bits.enable = 1;
/* Phantom pipes are effectively disabled, if the pipe was previously phantom * we have to enable
*/ if (old_pipe->plane_state && old_is_phantom &&
new_pipe->plane_state && !new_is_phantom)
new_pipe->update_flags.bits.enable = 1;
if (old_pipe->plane_state && !new_pipe->plane_state) {
new_pipe->update_flags.bits.disable = 1; return;
}
/* Detect top pipe only changes */ if (resource_is_pipe_type(new_pipe, OTG_MASTER)) { /* Detect global sync changes */ if (old_pipe->pipe_dlg_param.vready_offset != new_pipe->pipe_dlg_param.vready_offset
|| old_pipe->pipe_dlg_param.vstartup_start != new_pipe->pipe_dlg_param.vstartup_start
|| old_pipe->pipe_dlg_param.vupdate_offset != new_pipe->pipe_dlg_param.vupdate_offset
|| old_pipe->pipe_dlg_param.vupdate_width != new_pipe->pipe_dlg_param.vupdate_width)
new_pipe->update_flags.bits.global_sync = 1;
}
if (old_pipe->det_buffer_size_kb != new_pipe->det_buffer_size_kb)
new_pipe->update_flags.bits.det_size = 1;
/* * Detect opp / tg change, only set on change, not on enable * Assume mpcc inst = pipe index, if not this code needs to be updated * since mpcc is what is affected by these. In fact all of our sequence * makes this assumption at the moment with how hubp reset is matched to * same index mpcc reset.
*/ if (old_pipe->stream_res.opp != new_pipe->stream_res.opp)
new_pipe->update_flags.bits.opp_changed = 1; if (old_pipe->stream_res.tg != new_pipe->stream_res.tg)
new_pipe->update_flags.bits.tg_changed = 1;
/* * Detect mpcc blending changes, only dpp inst and opp matter here, * mpccs getting removed/inserted update connected ones during their own * programming
*/ if (old_pipe->plane_res.dpp != new_pipe->plane_res.dpp
|| old_pipe->stream_res.opp != new_pipe->stream_res.opp)
new_pipe->update_flags.bits.mpcc = 1;
if (pipe_ctx->update_flags.bits.dppclk)
dpp->funcs->dpp_dppclk_control(dpp, false, true);
if (pipe_ctx->update_flags.bits.enable)
dccg->funcs->update_dpp_dto(dccg, dpp->inst, pipe_ctx->plane_res.bw.dppclk_khz);
/* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG * VTG is within DCHUBBUB which is commond block share by each pipe HUBP. * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG
*/
if (pipe_ctx->update_flags.bits.hubp_rq_dlg_ttu) {
hubp->funcs->hubp_vtg_sel(hubp, pipe_ctx->stream_res.tg->inst);
// program the input csc
dpp->funcs->dpp_setup(dpp,
plane_state->format,
EXPANSION_MODE_ZERO,
plane_state->input_csc_color_matrix,
plane_state->color_space,
NULL);
if (dpp->funcs->set_cursor_matrix) {
dpp->funcs->set_cursor_matrix(dpp,
plane_state->color_space,
plane_state->cursor_csc_color_matrix);
} if (dpp->funcs->dpp_program_bias_and_scale) { //TODO :for CNVC set scale and bias registers if necessary
dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params);
}
}
if (pipe_ctx->update_flags.bits.mpcc
|| pipe_ctx->update_flags.bits.plane_changed
|| plane_state->update_flags.bits.global_alpha_change
|| plane_state->update_flags.bits.per_pixel_alpha_change) { // MPCC inst is equal to pipe index in practice
hws->funcs.update_mpcc(dc, pipe_ctx);
}
if (hubp->funcs->hubp_program_mcache_id_and_split_coordinate)
hubp->funcs->hubp_program_mcache_id_and_split_coordinate(hubp, &pipe_ctx->mcache_regs);
/* Any updates are handled in dc interface, just need to apply existing for plane enable */ if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
pipe_ctx->update_flags.bits.scaler || viewport_changed == true) &&
pipe_ctx->stream->cursor_attributes.address.quad_part != 0) {
dc->hwss.set_cursor_attribute(pipe_ctx);
dc->hwss.set_cursor_position(pipe_ctx);
if (dc->hwss.set_cursor_sdr_white_level)
dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
}
/* Any updates are handled in dc interface, just need
* to apply existing for plane enable / opp change */ if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed
|| pipe_ctx->update_flags.bits.plane_changed
|| pipe_ctx->stream->update_flags.bits.gamut_remap
|| plane_state->update_flags.bits.gamut_remap_change
|| pipe_ctx->stream->update_flags.bits.out_csc) { /* dpp/cm gamut remap*/
dc->hwss.program_gamut_remap(pipe_ctx);
/*call the dcn2 method which uses mpc csc*/
dc->hwss.program_output_csc(dc,
pipe_ctx,
pipe_ctx->stream->output_color_space,
pipe_ctx->stream->csc_color_matrix.matrix,
hubp->opp_id);
}
if (pipe_ctx->update_flags.bits.enable)
hubp->funcs->set_blank(hubp, false); /* If the stream paired with this plane is phantom, the plane is also phantom */ if (pipe_mall_type == SUBVP_PHANTOM && hubp->funcs->phantom_hubp_post_enable)
hubp->funcs->phantom_hubp_post_enable(hubp);
}
/* Only need to unblank on top pipe */ if (resource_is_pipe_type(pipe_ctx, OTG_MASTER)) { if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.odm ||
pipe_ctx->stream->update_flags.bits.abm_level)
hws->funcs.blank_pixel_data(dc, pipe_ctx,
!pipe_ctx->plane_state ||
!pipe_ctx->plane_state->visible);
}
/* Only update TG on top pipe */ if (pipe_ctx->update_flags.bits.global_sync && !pipe_ctx->top_pipe
&& !pipe_ctx->prev_odm_pipe)
dcn20_program_tg(dc, pipe_ctx, context, hws);
if (pipe_ctx->update_flags.bits.odm)
hws->funcs.update_odm(dc, context, pipe_ctx);
if (pipe_ctx->update_flags.bits.enable) { if (hws->funcs.enable_plane)
hws->funcs.enable_plane(dc, pipe_ctx, context); else
dcn20_enable_plane(dc, pipe_ctx, context);
if (dc->res_pool->hubbub->funcs->force_wm_propagate_to_pipes)
dc->res_pool->hubbub->funcs->force_wm_propagate_to_pipes(dc->res_pool->hubbub);
}
if (pipe_ctx->update_flags.bits.det_size) { if (dc->res_pool->hubbub->funcs->program_det_size)
dc->res_pool->hubbub->funcs->program_det_size(
dc->res_pool->hubbub, pipe_ctx->plane_res.hubp->inst, pipe_ctx->det_buffer_size_kb);
if (dc->res_pool->hubbub->funcs->program_det_segments)
dc->res_pool->hubbub->funcs->program_det_segments(
dc->res_pool->hubbub, pipe_ctx->plane_res.hubp->inst, pipe_ctx->hubp_regs.det_size);
}
/* dcn10_translate_regamma_to_hw_format takes 750us to finish * only do gamma programming for powering on, internal memcmp to avoid * updating on slave planes
*/ if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
pipe_ctx->stream->update_flags.bits.out_tf)
hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
/* If the pipe has been enabled or has a different opp, we * should reprogram the fmt. This deals with cases where * interation between mpc and odm combine on different streams * causes a different pipe to be chosen to odm combine with.
*/ if (pipe_ctx->update_flags.bits.enable
|| pipe_ctx->update_flags.bits.opp_changed) {
/* Set ABM pipe after other pipe configurations done */ if ((pipe_ctx->plane_state && pipe_ctx->plane_state->visible)) { if (pipe_ctx->stream_res.abm) {
dc->hwss.set_pipe(pipe_ctx);
pipe_ctx->stream_res.abm->funcs->set_abm_level(pipe_ctx->stream_res.abm,
pipe_ctx->stream->abm_level);
}
}
if (resource_is_pipe_topology_changed(dc->current_state, context))
resource_log_pipe_topology_update(dc, context);
if (dc->hwss.program_triplebuffer != NULL && dc->debug.enable_tri_buf) { for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state) {
ASSERT(!pipe->plane_state->triplebuffer_flips); /*turn off triple buffer for full update*/
dc->hwss.program_triplebuffer(
dc, pipe, pipe->plane_state->triplebuffer_flips);
}
}
}
for (i = 0; i < dc->res_pool->pipe_count; i++) { if (dc->current_state->res_ctx.pipe_ctx[i].plane_state)
prev_hubp_count++; if (context->res_ctx.pipe_ctx[i].plane_state)
hubp_count++;
}
if (prev_hubp_count == 0 && hubp_count > 0) { if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
dc->res_pool->hubbub->funcs->force_pstate_change_control(
dc->res_pool->hubbub, true, false);
udelay(500);
}
/* Set pipe update flags and lock pipes */ for (i = 0; i < dc->res_pool->pipe_count; i++)
dcn20_detect_pipe_changes(dc->current_state, context, &dc->current_state->res_ctx.pipe_ctx[i],
&context->res_ctx.pipe_ctx[i]);
/* When disabling phantom pipes, turn on phantom OTG first (so we can get double * buffer updates properly)
*/ for (i = 0; i < dc->res_pool->pipe_count; i++) { struct dc_stream_state *stream = dc->current_state->res_ctx.pipe_ctx[i].stream;
if (tg->funcs->enable_crtc) { if (dc->hwseq->funcs.blank_pixel_data)
dc->hwseq->funcs.blank_pixel_data(dc, pipe, true);
tg->funcs->enable_crtc(tg);
}
}
} /* OTG blank before disabling all front ends */ for (i = 0; i < dc->res_pool->pipe_count; i++) if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
&& !context->res_ctx.pipe_ctx[i].top_pipe
&& !context->res_ctx.pipe_ctx[i].prev_odm_pipe
&& context->res_ctx.pipe_ctx[i].stream)
hws->funcs.blank_pixel_data(dc, &context->res_ctx.pipe_ctx[i], true);
/* Disconnect mpcc */ for (i = 0; i < dc->res_pool->pipe_count; i++) if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
|| context->res_ctx.pipe_ctx[i].update_flags.bits.opp_changed) { struct hubbub *hubbub = dc->res_pool->hubbub;
/* Phantom pipe DET should be 0, but if a pipe in use is being transitioned to phantom * then we want to do the programming here (effectively it's being disabled). If we do * the programming later the DET won't be updated until the OTG for the phantom pipe is * turned on (i.e. in an MCLK switch) which can come in too late and cause issues with * DET allocation.
*/ if ((context->res_ctx.pipe_ctx[i].update_flags.bits.disable ||
(context->res_ctx.pipe_ctx[i].plane_state &&
dc_state_get_pipe_subvp_type(context, &context->res_ctx.pipe_ctx[i])
== SUBVP_PHANTOM))) { if (hubbub->funcs->program_det_size)
hubbub->funcs->program_det_size(hubbub,
dc->current_state->res_ctx.pipe_ctx[i].plane_res.hubp->inst, 0); if (dc->res_pool->hubbub->funcs->program_det_segments)
dc->res_pool->hubbub->funcs->program_det_segments(
hubbub, dc->current_state->res_ctx.pipe_ctx[i].plane_res.hubp->inst, 0);
}
hws->funcs.plane_atomic_disconnect(dc, dc->current_state,
&dc->current_state->res_ctx.pipe_ctx[i]);
DC_LOG_DC("Reset mpcc for pipe %d\n", dc->current_state->res_ctx.pipe_ctx[i].pipe_idx);
}
/* update ODM for blanked OTG master pipes */ for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i]; if (resource_is_pipe_type(pipe, OTG_MASTER) &&
!resource_is_pipe_type(pipe, DPP_PIPE) &&
pipe->update_flags.bits.odm &&
hws->funcs.update_odm)
hws->funcs.update_odm(dc, context, pipe);
}
/* * Program all updated pipes, order matters for mpcc setup. Start with * top pipe and program all pipes that follow in order
*/ for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state && !pipe->top_pipe) { while (pipe) { if (hws->funcs.program_pipe)
hws->funcs.program_pipe(dc, pipe, context); else { /* Don't program phantom pipes in the regular front end programming sequence. * There is an MPO transition case where a pipe being used by a video plane is * transitioned directly to be a phantom pipe when closing the MPO video. * However the phantom pipe will program a new HUBP_VTG_SEL (update takes place * right away) but the MPO still exists until the double buffered update of the * main pipe so we will get a frame of underflow if the phantom pipe is * programmed here.
*/ if (pipe->stream &&
dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM)
dcn20_program_pipe(dc, pipe, context);
}
pipe = pipe->bottom_pipe;
}
}
/* Program secondary blending tree and writeback pipes */
pipe = &context->res_ctx.pipe_ctx[i]; if (!pipe->top_pipe && !pipe->prev_odm_pipe
&& pipe->stream && pipe->stream->num_wb_info > 0
&& (pipe->update_flags.raw || (pipe->plane_state && pipe->plane_state->update_flags.raw)
|| pipe->stream->update_flags.raw)
&& hws->funcs.program_all_writeback_pipes_in_tree)
hws->funcs.program_all_writeback_pipes_in_tree(dc, pipe->stream, context);
/* Avoid underflow by check of pipe line read when adding 2nd plane. */ if (hws->wa.wait_hubpret_read_start_during_mpo_transition &&
!pipe->top_pipe &&
pipe->stream &&
pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start &&
dc->current_state->stream_status[0].plane_count == 1 &&
context->stream_status[0].plane_count > 1) {
pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start(pipe->plane_res.hubp);
}
}
}
/* post_unlock_reset_opp - the function wait for corresponding double * buffered pending status clear and reset opp head pipe's none double buffered * registers to their initial state.
*/ void dcn20_post_unlock_reset_opp(struct dc *dc, struct pipe_ctx *opp_head)
{ struct display_stream_compressor *dsc = opp_head->stream_res.dsc; struct dccg *dccg = dc->res_pool->dccg;
/* * wait for all DPP pipes in current mpc blending tree completes double * buffered disconnection before resetting OPP
*/
dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, opp_head);
if (dsc) { bool is_dsc_ungated = false;
if (dc->hwseq->funcs.dsc_pg_status)
is_dsc_ungated = dc->hwseq->funcs.dsc_pg_status(dc->hwseq, dsc->inst);
if (is_dsc_ungated) { /* * seamless update specific where we will postpone non * double buffered DSCCLK disable logic in post unlock * sequence after DSC is disconnected from OPP but not * yet power gated.
*/
dsc->funcs->dsc_wait_disconnect_pending_clear(dsc);
dsc->funcs->dsc_disable(dsc); if (dccg->funcs->set_ref_dscclk)
dccg->funcs->set_ref_dscclk(dccg, dsc->inst);
}
}
}
void dcn20_post_unlock_program_front_end( struct dc *dc, struct dc_state *context)
{ // Timeout for pipe enable unsignedint timeout_us = 100000; unsignedint polling_interval_us = 1; struct dce_hwseq *hwseq = dc->hwseq; int i;
for (i = 0; i < dc->res_pool->pipe_count; i++) if (resource_is_pipe_type(&dc->current_state->res_ctx.pipe_ctx[i], OPP_HEAD) &&
!resource_is_pipe_type(&context->res_ctx.pipe_ctx[i], OPP_HEAD))
dcn20_post_unlock_reset_opp(dc,
&dc->current_state->res_ctx.pipe_ctx[i]);
for (i = 0; i < dc->res_pool->pipe_count; i++) if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable)
dc->hwss.disable_plane(dc, dc->current_state, &dc->current_state->res_ctx.pipe_ctx[i]);
/* * If we are enabling a pipe, we need to wait for pending clear as this is a critical * part of the enable operation otherwise, DM may request an immediate flip which * will cause HW to perform an "immediate enable" (as opposed to "vsync enable") which * is unsupported on DCN.
*/ for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; // Don't check flip pending on phantom pipes if (pipe->plane_state && !pipe->top_pipe && pipe->update_flags.bits.enable &&
dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) { struct hubp *hubp = pipe->plane_res.hubp; int j = 0;
for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i]; struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
/* When going from a smaller ODM slice count to larger, we must ensure double * buffer update completes before we return to ensure we don't reduce DISPCLK * before we've transitioned to 2:1 or 4:1
*/ if (resource_is_pipe_type(old_pipe, OTG_MASTER) && resource_is_pipe_type(pipe, OTG_MASTER) &&
resource_get_odm_slice_count(old_pipe) < resource_get_odm_slice_count(pipe) &&
dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) { int j = 0; struct timing_generator *tg = pipe->stream_res.tg;
if (tg->funcs->get_optc_double_buffer_pending) { for (j = 0; j < timeout_us / polling_interval_us
&& tg->funcs->get_optc_double_buffer_pending(tg); j++)
udelay(polling_interval_us);
}
}
}
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);
for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state && !pipe->top_pipe) { /* Program phantom pipe here to prevent a frame of underflow in the MPO transition * case (if a pipe being used for a video plane transitions to a phantom pipe, it * can underflow due to HUBP_VTG_SEL programming if done in the regular front end * programming sequence).
*/ while (pipe) { if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) { /* When turning on the phantom pipe we want to run through the * entire enable sequence, so apply all the "enable" flags.
*/ if (dc->hwss.apply_update_flags_for_phantom)
dc->hwss.apply_update_flags_for_phantom(pipe); if (dc->hwss.update_phantom_vp_position)
dc->hwss.update_phantom_vp_position(dc, context, pipe);
dcn20_program_pipe(dc, pipe, context);
}
pipe = pipe->bottom_pipe;
}
}
}
if (!hwseq) return;
/* P-State support transitions: * Natural -> FPO: P-State disabled in prepare, force disallow anytime is safe * FPO -> Natural: Unforce anytime after FW disable is safe (P-State will assert naturally) * Unsupported -> FPO: P-State enabled in optimize, force disallow anytime is safe * FPO -> Unsupported: P-State disabled in prepare, unforce disallow anytime is safe * FPO <-> SubVP: Force disallow is maintained on the FPO / SubVP pipes
*/ if (hwseq->funcs.update_force_pstate)
dc->hwseq->funcs.update_force_pstate(dc, context);
/* Only program the MALL registers after all the main and phantom pipes * are done programming.
*/ if (hwseq->funcs.program_mall_pipe_config)
hwseq->funcs.program_mall_pipe_config(dc, context);
/* WA to apply WM setting*/ if (hwseq->wa.DEGVIDCN21)
dc->res_pool->hubbub->funcs->apply_DEDCN21_147_wa(dc->res_pool->hubbub);
/* WA for stutter underflow during MPO transitions when adding 2nd plane */ if (hwseq->wa.disallow_self_refresh_during_multi_plane_transition) {
if (dc->current_state->stream_status[0].plane_count == 1 &&
context->stream_status[0].plane_count > 1) {
for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
// At optimize don't restore the original watermark value if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_NONE) {
context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = 4U * 1000U * 1000U * 1000U; break;
}
}
/* program dchubbub watermarks: * For assigning wm_optimized_required, use |= operator since we don't want * to clear the value if the optimize has not happened yet
*/
dc->wm_optimized_required |= hubbub->funcs->program_watermarks(hubbub,
&context->bw_ctx.bw.dcn.watermarks,
dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, false);
// Restore the real watermark so we can commit the value to DMCUB // DMCUB uses the "original" watermark value in SubVP MCLK switch
context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = cache_wm_a;
void dcn20_optimize_bandwidth( struct dc *dc, struct dc_state *context)
{ struct hubbub *hubbub = dc->res_pool->hubbub; int i;
for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
// At optimize don't need to restore the original watermark value if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_NONE) {
context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns = 4U * 1000U * 1000U * 1000U; break;
}
}
for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
params.opp_cnt++;
} /* only 3 items below are used by unblank */
params.timing = pipe_ctx->stream->timing;
/* DPMS may already disable or */ /* dpms_off status is incorrect due to fastboot * feature. When system resume from S4 with second * screen only, the dpms_off would be true but * VBIOS lit up eDP, so check link status too.
*/ if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
dc->link_srv->set_dpms_off(pipe_ctx); elseif (pipe_ctx->stream_res.audio)
dc->hwss.disable_audio_stream(pipe_ctx);
/*free audio*/ if (dc->caps.dynamic_audio == true) { /*we have to dynamic arbitrate the audio endpoints*/ /*we free the resource, need reset is_audio_acquired*/
update_audio_usage(&dc->current_state->res_ctx, dc->res_pool,
pipe_ctx->stream_res.audio, false);
pipe_ctx->stream_res.audio = NULL;
}
}
/* by upper caller loop, parent pipe: pipe0, will be reset last. * back end share by all pipes and will be disable only when disable * parent pipe.
*/ if (pipe_ctx->top_pipe == NULL) {
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass)
pipe_ctx->stream_res.tg->funcs->set_odm_bypass(
pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);
set_drr_and_clear_adjust_pending(pipe_ctx, pipe_ctx->stream, NULL); /* TODO - convert symclk_ref_cnts for otg to a bit map to solve * the case where the same symclk is shared across multiple otg * instances
*/ if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal))
link->phy_state.symclk_ref_cnts.otg = 0; if (link->phy_state.symclk_state == SYMCLK_ON_TX_OFF) {
link_hwss->disable_link_output(link,
&pipe_ctx->link_res, pipe_ctx->stream->signal);
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
} if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx) && dccg
&& dc->ctx->dce_version >= DCN_VERSION_3_5) {
dto_params.otg_inst = pipe_ctx->stream_res.tg->inst;
dto_params.timing = &pipe_ctx->stream->timing; if (dccg && dccg->funcs->set_dtbclk_dto)
dccg->funcs->set_dtbclk_dto(dccg, &dto_params);
}
}
/* * In case of a dangling plane, setting this to NULL unconditionally * causes failures during reset hw ctx where, if stream is NULL, * it is expected that the pipe_ctx pointers to pipes and plane are NULL.
*/
pipe_ctx->stream = NULL;
pipe_ctx->top_pipe = NULL;
pipe_ctx->bottom_pipe = NULL;
pipe_ctx->next_odm_pipe = NULL;
pipe_ctx->prev_odm_pipe = NULL;
DC_LOG_DEBUG("Reset back end for pipe %d, tg:%d\n",
pipe_ctx->pipe_idx, pipe_ctx->stream_res.tg->inst);
}
void dcn20_reset_hw_ctx_wrap( struct dc *dc, struct dc_state *context)
{ int i; struct dce_hwseq *hws = dc->hwseq;
/* Reset Back End*/ for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) { struct pipe_ctx *pipe_ctx_old =
&dc->current_state->res_ctx.pipe_ctx[i]; struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
if (!pipe_ctx_old->stream) continue;
if (pipe_ctx_old->top_pipe || pipe_ctx_old->prev_odm_pipe) continue;
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);
if (dc->res_pool->dccg->funcs->set_pixel_rate_div)
dc->res_pool->dccg->funcs->set_pixel_rate_div(
dc->res_pool->dccg,
pipe_ctx->stream_res.tg->inst,
pipe_ctx->pixel_rate_divider.div_factor1,
pipe_ctx->pixel_rate_divider.div_factor2);
link_hwss->setup_stream_encoder(pipe_ctx);
if (pipe_ctx->plane_state && pipe_ctx->plane_state->flip_immediate != 1) { if (dc->hwss.program_dmdata_engine)
dc->hwss.program_dmdata_engine(pipe_ctx);
}
dc->hwss.update_info_frame(pipe_ctx);
if (dc_is_dp_signal(pipe_ctx->stream->signal))
dc->link_srv->dp_trace_source_sequence(link, DPCD_SOURCE_SEQ_AFTER_UPDATE_INFO_FRAME);
/* enable early control to avoid corruption on DP monitor*/
active_total_with_borders =
timing->h_addressable
+ timing->h_border_left
+ timing->h_border_right;
if (lane_count != 0)
early_control = active_total_with_borders % lane_count;
if (early_control == 0)
early_control = lane_count;
/* 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;
void dcn20_fpga_init_hw(struct dc *dc)
{ int i, j; struct dce_hwseq *hws = dc->hwseq; struct resource_pool *res_pool = dc->res_pool; struct dc_state *context = dc->current_state;
if (dc->clk_mgr && 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);
//Enable ability to power gate / don't force power on permanently if (hws->funcs.enable_power_gating_plane)
hws->funcs.enable_power_gating_plane(hws, true);
// Specific to FPGA dccg and registers
REG_WRITE(RBBMIF_TIMEOUT_DIS, 0xFFFFFFFF);
REG_WRITE(RBBMIF_TIMEOUT_DIS_2, 0xFFFFFFFF);
/* Blank pixel data with OPP DPG */ for (i = 0; i < dc->res_pool->timing_generator_count; i++) { struct timing_generator *tg = dc->res_pool->timing_generators[i];
if (tg->funcs->is_tg_enabled(tg))
dcn20_init_blank(dc, tg);
}
for (i = 0; i < res_pool->timing_generator_count; i++) { struct timing_generator *tg = dc->res_pool->timing_generators[i];
if (tg->funcs->is_tg_enabled(tg))
tg->funcs->lock(tg);
}
for (i = 0; i < dc->res_pool->pipe_count; i++) { struct dpp *dpp = res_pool->dpps[i];
dpp->funcs->dpp_reset(dpp);
}
/* Reset all MPCC muxes */
res_pool->mpc->funcs->mpc_init(res_pool->mpc);
/* initialize OPP mpc_tree parameter */ for (i = 0; i < dc->res_pool->pipe_count; i++) {
res_pool->opps[i]->mpc_tree_params.opp_id = res_pool->opps[i]->inst;
res_pool->opps[i]->mpc_tree_params.opp_list = NULL; for (j = 0; j < MAX_PIPES; j++)
res_pool->opps[i]->mpcc_disconnect_pending[j] = false;
}
for (i = 0; i < dc->res_pool->pipe_count; i++) { struct timing_generator *tg = dc->res_pool->timing_generators[i]; struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; struct hubp *hubp = dc->res_pool->hubps[i]; struct dpp *dpp = dc->res_pool->dpps[i];
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.