From 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 22 Oct 2024 10:36:11 +0000 Subject: [PATCH] 修改4g拨号为QMI,需要在系统里后台执行quectel-CM --- kernel/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c | 2254 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 1,658 insertions(+), 596 deletions(-) diff --git a/kernel/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/kernel/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index ead221c..1c669f1 100644 --- a/kernel/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/kernel/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -25,26 +25,33 @@ #include <linux/delay.h> #include "dm_services.h" +#include "basics/dc_common.h" #include "core_types.h" #include "resource.h" #include "custom_float.h" #include "dcn10_hw_sequencer.h" -#include "dce110/dce110_hw_sequencer.h" +#include "dcn10_hw_sequencer_debug.h" #include "dce/dce_hwseq.h" #include "abm.h" #include "dmcu.h" #include "dcn10_optc.h" -#include "dcn10/dcn10_dpp.h" -#include "dcn10/dcn10_mpc.h" +#include "dcn10_dpp.h" +#include "dcn10_mpc.h" #include "timing_generator.h" #include "opp.h" #include "ipp.h" #include "mpc.h" #include "reg_helper.h" -#include "custom_float.h" #include "dcn10_hubp.h" #include "dcn10_hubbub.h" #include "dcn10_cm_common.h" +#include "dc_link_dp.h" +#include "dccg.h" +#include "clk_mgr.h" +#include "link_hwss.h" +#include "dpcd_defs.h" +#include "dsc.h" +#include "dce/dmub_hw_lock_mgr.h" #define DC_LOGGER_INIT(logger) @@ -59,11 +66,15 @@ /*print is 17 wide, first two characters are spaces*/ #define DTN_INFO_MICRO_SEC(ref_cycle) \ - print_microsec(dc_ctx, ref_cycle) + print_microsec(dc_ctx, log_ctx, ref_cycle) -void print_microsec(struct dc_context *dc_ctx, uint32_t ref_cycle) +#define GAMMA_HW_POINTS_NUM 256 + +void print_microsec(struct dc_context *dc_ctx, + struct dc_log_buffer_ctx *log_ctx, + uint32_t ref_cycle) { - const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clock_inKhz / 1000; + const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000; static const unsigned int frac = 1000; uint32_t us_x10 = (ref_cycle * frac) / ref_clk_mhz; @@ -72,8 +83,36 @@ us_x10 % frac); } +void dcn10_lock_all_pipes(struct dc *dc, + struct dc_state *context, + bool lock) +{ + struct pipe_ctx *pipe_ctx; + struct timing_generator *tg; + int i; -static void log_mpc_crc(struct dc *dc) + for (i = 0; i < dc->res_pool->pipe_count; i++) { + pipe_ctx = &context->res_ctx.pipe_ctx[i]; + tg = pipe_ctx->stream_res.tg; + + /* + * Only lock the top pipe's tg to prevent redundant + * (un)locking. Also skip if pipe is disabled. + */ + if (pipe_ctx->top_pipe || + !pipe_ctx->stream || !pipe_ctx->plane_state || + !tg->funcs->is_tg_enabled(tg)) + continue; + + if (lock) + dc->hwss.pipe_control_lock(dc, pipe_ctx, true); + else + dc->hwss.pipe_control_lock(dc, pipe_ctx, false); + } +} + +static void log_mpc_crc(struct dc *dc, + struct dc_log_buffer_ctx *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct dce_hwseq *hws = dc->hwseq; @@ -86,13 +125,14 @@ REG_READ(DPP_TOP0_DPP_CRC_VAL_B_A), REG_READ(DPP_TOP0_DPP_CRC_VAL_R_G)); } -void dcn10_log_hubbub_state(struct dc *dc) +void dcn10_log_hubbub_state(struct dc *dc, struct dc_log_buffer_ctx *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct dcn_hubbub_wm wm; int i; - hubbub1_wm_read_state(dc->res_pool->hubbub, &wm); + memset(&wm, 0, sizeof(struct dcn_hubbub_wm)); + dc->res_pool->hubbub->funcs->wm_read_state(dc->res_pool->hubbub, &wm); DTN_INFO("HUBBUB WM: data_urgent pte_meta_urgent" " sr_enter sr_exit dram_clk_change\n"); @@ -113,15 +153,14 @@ DTN_INFO("\n"); } -static void dcn10_log_hubp_states(struct dc *dc) +static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; int i; - DTN_INFO("HUBP: format addr_hi width height" - " rot mir sw_mode dcc_en blank_en ttu_dis underflow" - " min_ttu_vblank qos_low_wm qos_high_wm\n"); + DTN_INFO( + "HUBP: format addr_hi width height rot mir sw_mode dcc_en blank_en clock_en ttu_dis underflow min_ttu_vblank qos_low_wm qos_high_wm\n"); for (i = 0; i < pool->pipe_count; i++) { struct hubp *hubp = pool->hubps[i]; struct dcn_hubp_state *s = &(TO_DCN10_HUBP(hubp)->state); @@ -129,8 +168,7 @@ hubp->funcs->hubp_read_state(hubp); if (!s->blank_en) { - DTN_INFO("[%2d]: %5xh %6xh %5d %6d %2xh %2xh %6xh" - " %6d %8d %7d %8xh", + DTN_INFO("[%2d]: %5xh %6xh %5d %6d %2xh %2xh %6xh %6d %8d %8d %7d %8xh", hubp->inst, s->pixel_format, s->inuse_addr_hi, @@ -141,6 +179,7 @@ s->sw_mode, s->dcc_en, s->blank_en, + s->clock_en, s->ttu_disable, s->underflow_status); DTN_INFO_MICRO_SEC(s->min_ttu_vblank); @@ -228,7 +267,8 @@ DTN_INFO("\n"); } -void dcn10_log_hw_state(struct dc *dc) +void dcn10_log_hw_state(struct dc *dc, + struct dc_log_buffer_ctx *log_ctx) { struct dc_context *dc_ctx = dc->ctx; struct resource_pool *pool = dc->res_pool; @@ -236,18 +276,21 @@ DTN_INFO_BEGIN(); - dcn10_log_hubbub_state(dc); + dcn10_log_hubbub_state(dc, log_ctx); - dcn10_log_hubp_states(dc); + dcn10_log_hubp_states(dc, log_ctx); DTN_INFO("DPP: IGAM format IGAM mode DGAM mode RGAM mode" " GAMUT mode C11 C12 C13 C14 C21 C22 C23 C24 " "C31 C32 C33 C34\n"); for (i = 0; i < pool->pipe_count; i++) { struct dpp *dpp = pool->dpps[i]; - struct dcn_dpp_state s; + struct dcn_dpp_state s = {0}; dpp->funcs->dpp_read_state(dpp, &s); + + if (!s.is_enabled) + continue; DTN_INFO("[%2d]: %11xh %-11s %-11s %-11s" "%8x %08xh %08xh %08xh %08xh %08xh %08xh", @@ -294,21 +337,31 @@ } DTN_INFO("\n"); - DTN_INFO("OTG: v_bs v_be v_ss v_se vpol vmax vmin vmax_sel vmin_sel" - " h_bs h_be h_ss h_se hpol htot vtot underflow\n"); + DTN_INFO("OTG: v_bs v_be v_ss v_se vpol vmax vmin vmax_sel vmin_sel h_bs h_be h_ss h_se hpol htot vtot underflow blank_en\n"); for (i = 0; i < pool->timing_generator_count; i++) { struct timing_generator *tg = pool->timing_generators[i]; struct dcn_otg_state s = {0}; - + /* Read shared OTG state registers for all DCNx */ optc1_read_otg_state(DCN10TG_FROM_TG(tg), &s); + + /* + * For DCN2 and greater, a register on the OPP is used to + * determine if the CRTC is blanked instead of the OTG. So use + * dpg_is_blanked() if exists, otherwise fallback on otg. + * + * TODO: Implement DCN-specific read_otg_state hooks. + */ + if (pool->opps[i]->funcs->dpg_is_blanked) + s.blank_enabled = pool->opps[i]->funcs->dpg_is_blanked(pool->opps[i]); + else + s.blank_enabled = tg->funcs->is_blanked(tg); //only print if OTG master is enabled if ((s.otg_enabled & 1) == 0) continue; - DTN_INFO("[%d]: %5d %5d %5d %5d %5d %5d %5d %9d %9d %5d %5d %5d" - " %5d %5d %5d %5d %9d\n", + DTN_INFO("[%d]: %5d %5d %5d %5d %5d %5d %5d %9d %9d %5d %5d %5d %5d %5d %5d %5d %9d %8d\n", tg->inst, s.v_blank_start, s.v_blank_end, @@ -326,7 +379,8 @@ s.h_sync_a_pol, s.h_total, s.v_total, - s.underflow_occurred_status); + s.underflow_occurred_status, + s.blank_enabled); // Clear underflow for debug purposes // We want to keep underflow sticky bit on for the longevity tests outside of test environment. @@ -336,29 +390,103 @@ } DTN_INFO("\n"); + // dcn_dsc_state struct field bytes_per_pixel was renamed to bits_per_pixel + // TODO: Update golden log header to reflect this name change + DTN_INFO("DSC: CLOCK_EN SLICE_WIDTH Bytes_pp\n"); + for (i = 0; i < pool->res_cap->num_dsc; i++) { + struct display_stream_compressor *dsc = pool->dscs[i]; + struct dcn_dsc_state s = {0}; + + dsc->funcs->dsc_read_state(dsc, &s); + DTN_INFO("[%d]: %-9d %-12d %-10d\n", + dsc->inst, + s.dsc_clock_en, + s.dsc_slice_width, + s.dsc_bits_per_pixel); + DTN_INFO("\n"); + } + DTN_INFO("\n"); + + DTN_INFO("S_ENC: DSC_MODE SEC_GSP7_LINE_NUM" + " VBID6_LINE_REFERENCE VBID6_LINE_NUM SEC_GSP7_ENABLE SEC_STREAM_ENABLE\n"); + for (i = 0; i < pool->stream_enc_count; i++) { + struct stream_encoder *enc = pool->stream_enc[i]; + struct enc_state s = {0}; + + if (enc->funcs->enc_read_state) { + enc->funcs->enc_read_state(enc, &s); + DTN_INFO("[%-3d]: %-9d %-18d %-21d %-15d %-16d %-17d\n", + enc->id, + s.dsc_mode, + s.sec_gsp_pps_line_num, + s.vbid6_line_reference, + s.vbid6_line_num, + s.sec_gsp_pps_enable, + s.sec_stream_enable); + DTN_INFO("\n"); + } + } + DTN_INFO("\n"); + + DTN_INFO("L_ENC: DPHY_FEC_EN DPHY_FEC_READY_SHADOW DPHY_FEC_ACTIVE_STATUS DP_LINK_TRAINING_COMPLETE\n"); + for (i = 0; i < dc->link_count; i++) { + struct link_encoder *lenc = dc->links[i]->link_enc; + + struct link_enc_state s = {0}; + + if (lenc->funcs->read_state) { + lenc->funcs->read_state(lenc, &s); + DTN_INFO("[%-3d]: %-12d %-22d %-22d %-25d\n", + i, + s.dphy_fec_en, + s.dphy_fec_ready_shadow, + s.dphy_fec_active_status, + s.dp_link_training_complete); + DTN_INFO("\n"); + } + } + DTN_INFO("\n"); + DTN_INFO("\nCALCULATED Clocks: dcfclk_khz:%d dcfclk_deep_sleep_khz:%d dispclk_khz:%d\n" "dppclk_khz:%d max_supported_dppclk_khz:%d fclk_khz:%d socclk_khz:%d\n\n", - dc->current_state->bw.dcn.clk.dcfclk_khz, - dc->current_state->bw.dcn.clk.dcfclk_deep_sleep_khz, - dc->current_state->bw.dcn.clk.dispclk_khz, - dc->current_state->bw.dcn.clk.dppclk_khz, - dc->current_state->bw.dcn.clk.max_supported_dppclk_khz, - dc->current_state->bw.dcn.clk.fclk_khz, - dc->current_state->bw.dcn.clk.socclk_khz); + dc->current_state->bw_ctx.bw.dcn.clk.dcfclk_khz, + dc->current_state->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz, + dc->current_state->bw_ctx.bw.dcn.clk.dispclk_khz, + dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz, + dc->current_state->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz, + dc->current_state->bw_ctx.bw.dcn.clk.fclk_khz, + dc->current_state->bw_ctx.bw.dcn.clk.socclk_khz); - log_mpc_crc(dc); + log_mpc_crc(dc, log_ctx); DTN_INFO_END(); } -static void enable_power_gating_plane( +bool dcn10_did_underflow_occur(struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct hubp *hubp = pipe_ctx->plane_res.hubp; + struct timing_generator *tg = pipe_ctx->stream_res.tg; + + if (tg->funcs->is_optc_underflow_occurred(tg)) { + tg->funcs->clear_optc_underflow(tg); + return true; + } + + if (hubp->funcs->hubp_get_underflow_status(hubp)) { + hubp->funcs->hubp_clear_underflow(hubp); + return true; + } + return false; +} + +void dcn10_enable_power_gating_plane( struct dce_hwseq *hws, bool enable) { - bool force_on = 1; /* disable power gating */ + bool force_on = true; /* disable power gating */ if (enable) - force_on = 0; + force_on = false; /* DCHUBP0/1/2/3 */ REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, force_on); @@ -373,7 +501,7 @@ REG_UPDATE(DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, force_on); } -static void disable_vga( +void dcn10_disable_vga( struct dce_hwseq *hws) { unsigned int in_vga1_mode = 0; @@ -406,7 +534,7 @@ REG_UPDATE(VGA_TEST_CONTROL, VGA_TEST_RENDER_START, 1); } -static void dpp_pg_control( +void dcn10_dpp_pg_control( struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on) @@ -458,7 +586,7 @@ } } -static void hubp_pg_control( +void dcn10_hubp_pg_control( struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on) @@ -518,8 +646,13 @@ if (REG(DC_IP_REQUEST_CNTL)) { REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - dpp_pg_control(hws, plane_id, true); - hubp_pg_control(hws, plane_id, true); + + if (hws->funcs.dpp_pg_control) + hws->funcs.dpp_pg_control(hws, plane_id, true); + + if (hws->funcs.hubp_pg_control) + hws->funcs.hubp_pg_control(hws, plane_id, true); + REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); DC_LOG_DEBUG( @@ -540,7 +673,7 @@ REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - hubp_pg_control(hws, 0, false); + hws->funcs.hubp_pg_control(hws, 0, false); REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); @@ -569,7 +702,7 @@ REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - hubp_pg_control(hws, 0, true); + hws->funcs.hubp_pg_control(hws, 0, true); REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); @@ -577,10 +710,27 @@ hws->wa_state.DEGVIDCN10_253_applied = true; } -static void bios_golden_init(struct dc *dc) +void dcn10_bios_golden_init(struct dc *dc) { + struct dce_hwseq *hws = dc->hwseq; struct dc_bios *bp = dc->ctx->dc_bios; int i; + bool allow_self_fresh_force_enable = true; + + if (hws->funcs.s0i3_golden_init_wa && hws->funcs.s0i3_golden_init_wa(dc)) + return; + + if (dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled) + allow_self_fresh_force_enable = + dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled(dc->res_pool->hubbub); + + + /* WA for making DF sleep when idle after resume from S0i3. + * DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE is set to 1 by + * command table, if DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE = 0 + * before calling command table and it changed to 1 after, + * it should be set back to 0. + */ /* initialize dcn global */ bp->funcs->enable_disp_power_gating(bp, @@ -591,6 +741,13 @@ bp->funcs->enable_disp_power_gating(bp, CONTROLLER_ID_D0 + i, ASIC_PIPE_DISABLE); } + + if (dc->res_pool->hubbub->funcs->allow_self_refresh_control) + if (allow_self_fresh_force_enable == false && + dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled(dc->res_pool->hubbub)) + dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub, + !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter); + } static void false_optc_underflow_wa( @@ -615,13 +772,14 @@ dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, old_pipe_ctx); } - tg->funcs->set_blank_data_double_buffer(tg, true); + if (tg->funcs->set_blank_data_double_buffer) + tg->funcs->set_blank_data_double_buffer(tg, true); if (tg->funcs->is_optc_underflow_occurred(tg) && !underflow) tg->funcs->clear_optc_underflow(tg); } -static enum dc_status dcn10_enable_stream_timing( +enum dc_status dcn10_enable_stream_timing( struct pipe_ctx *pipe_ctx, struct dc_state *context, struct dc *dc) @@ -651,16 +809,15 @@ BREAK_TO_DEBUGGER(); return DC_ERROR_UNEXPECTED; } - pipe_ctx->stream_res.tg->dlg_otg_param.vready_offset = pipe_ctx->pipe_dlg_param.vready_offset; - pipe_ctx->stream_res.tg->dlg_otg_param.vstartup_start = pipe_ctx->pipe_dlg_param.vstartup_start; - pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_offset = pipe_ctx->pipe_dlg_param.vupdate_offset; - pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_width = pipe_ctx->pipe_dlg_param.vupdate_width; - - pipe_ctx->stream_res.tg->dlg_otg_param.signal = pipe_ctx->stream->signal; pipe_ctx->stream_res.tg->funcs->program_timing( pipe_ctx->stream_res.tg, &stream->timing, + pipe_ctx->pipe_dlg_param.vready_offset, + pipe_ctx->pipe_dlg_param.vstartup_start, + pipe_ctx->pipe_dlg_param.vupdate_offset, + pipe_ctx->pipe_dlg_param.vupdate_width, + pipe_ctx->stream->signal, true); #if 0 /* move to after enable_crtc */ @@ -677,6 +834,14 @@ /* program otg blank color */ color_space = stream->output_color_space; color_space_to_black_color(dc, color_space, &black_color); + + /* + * The way 420 is packed, 2 channels carry Y component, 1 channel + * alternate between Cb and Cr, so both channels need the pixel + * value for Y + */ + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + black_color.color_r_cr = black_color.color_g_y; if (pipe_ctx->stream_res.tg->funcs->set_blank_color) pipe_ctx->stream_res.tg->funcs->set_blank_color( @@ -707,12 +872,13 @@ return DC_OK; } -static void reset_back_end_for_pipe( +static void dcn10_reset_back_end_for_pipe( struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context) { int i; + struct dc_link *link; DC_LOGGER_INIT(dc->ctx->logger); if (pipe_ctx->stream_res.stream_enc == NULL) { pipe_ctx->stream = NULL; @@ -720,13 +886,31 @@ } if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { - /* DPMS may already disable */ - if (!pipe_ctx->stream->dpms_off) - core_link_disable_stream(pipe_ctx, FREE_ACQUIRED_RESOURCE); - else if (pipe_ctx->stream_res.audio) { - dc->hwss.disable_audio_stream(pipe_ctx, FREE_ACQUIRED_RESOURCE); - } + link = pipe_ctx->stream->link; + /* 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) + core_link_disable_stream(pipe_ctx); + else if (pipe_ctx->stream_res.audio) + dc->hwss.disable_audio_stream(pipe_ctx); + if (pipe_ctx->stream_res.audio) { + /*disable az_endpoint*/ + pipe_ctx->stream_res.audio->funcs->az_disable(pipe_ctx->stream_res.audio); + + /*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. @@ -734,9 +918,16 @@ * parent pipe. */ if (pipe_ctx->top_pipe == NULL) { + + if (pipe_ctx->stream_res.abm) + dc->hwss.set_abm_immediate_disable(pipe_ctx); + pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); + if (pipe_ctx->stream_res.tg->funcs->set_drr) + pipe_ctx->stream_res.tg->funcs->set_drr( + pipe_ctx->stream_res.tg, NULL); } for (i = 0; i < dc->res_pool->pipe_count; i++) @@ -765,7 +956,7 @@ &dc->current_state->res_ctx.pipe_ctx[i]; if (pipe_ctx != NULL) { hubp = pipe_ctx->plane_res.hubp; - if (hubp != NULL) { + if (hubp != NULL && hubp->funcs->hubp_get_underflow_status) { if (hubp->funcs->hubp_get_underflow_status(hubp) != 0) { /* one pipe underflow, we will reset all the pipes*/ need_recover = true; @@ -791,7 +982,7 @@ if (pipe_ctx != NULL) { hubp = pipe_ctx->plane_res.hubp; /*DCHUBP_CNTL:HUBP_BLANK_EN=1*/ - if (hubp != NULL) + if (hubp != NULL && hubp->funcs->set_hubp_blank_en) hubp->funcs->set_hubp_blank_en(hubp, true); } } @@ -804,7 +995,7 @@ if (pipe_ctx != NULL) { hubp = pipe_ctx->plane_res.hubp; /*DCHUBP_CNTL:HUBP_DISABLE=1*/ - if (hubp != NULL) + if (hubp != NULL && hubp->funcs->hubp_disable_control) hubp->funcs->hubp_disable_control(hubp, true); } } @@ -814,7 +1005,7 @@ if (pipe_ctx != NULL) { hubp = pipe_ctx->plane_res.hubp; /*DCHUBP_CNTL:HUBP_DISABLE=0*/ - if (hubp != NULL) + if (hubp != NULL && hubp->funcs->hubp_disable_control) hubp->funcs->hubp_disable_control(hubp, true); } } @@ -826,7 +1017,7 @@ if (pipe_ctx != NULL) { hubp = pipe_ctx->plane_res.hubp; /*DCHUBP_CNTL:HUBP_BLANK_EN=0*/ - if (hubp != NULL) + if (hubp != NULL && hubp->funcs->set_hubp_blank_en) hubp->funcs->set_hubp_blank_en(hubp, true); } } @@ -841,7 +1032,7 @@ if (!hubbub1_verify_allow_pstate_change_high(dc->res_pool->hubbub)) { if (should_log_hw_state) { - dcn10_log_hw_state(dc); + dcn10_log_hw_state(dc, NULL); } BREAK_TO_DEBUGGER(); if (dcn10_hw_wa_force_recovery(dc)) { @@ -853,8 +1044,9 @@ } /* trigger HW to start disconnect plane from stream on the next vsync */ -void hwss1_plane_atomic_disconnect(struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_plane_atomic_disconnect(struct dc *dc, struct pipe_ctx *pipe_ctx) { + struct dce_hwseq *hws = dc->hwseq; struct hubp *hubp = pipe_ctx->plane_res.hubp; int dpp_id = pipe_ctx->plane_res.dpp->inst; struct mpc *mpc = dc->res_pool->mpc; @@ -879,33 +1071,40 @@ hubp->funcs->hubp_disconnect(hubp); if (dc->debug.sanity_checks) - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); } -static void plane_atomic_power_down(struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_plane_atomic_power_down(struct dc *dc, + struct dpp *dpp, + struct hubp *hubp) { struct dce_hwseq *hws = dc->hwseq; - struct dpp *dpp = pipe_ctx->plane_res.dpp; DC_LOGGER_INIT(dc->ctx->logger); if (REG(DC_IP_REQUEST_CNTL)) { REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - dpp_pg_control(hws, dpp->inst, false); - hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, false); + + if (hws->funcs.dpp_pg_control) + hws->funcs.dpp_pg_control(hws, dpp->inst, false); + + if (hws->funcs.hubp_pg_control) + hws->funcs.hubp_pg_control(hws, hubp->inst, false); + dpp->funcs->dpp_reset(dpp); REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); DC_LOG_DEBUG( - "Power gated front end %d\n", pipe_ctx->pipe_idx); + "Power gated front end %d\n", hubp->inst); } } /* disable HW used by plane. * note: cannot disable until disconnect is complete */ -static void plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_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; int opp_id = hubp->opp_id; @@ -924,7 +1123,9 @@ hubp->power_gated = true; dc->optimized_required = false; /* We're powering off, no need to optimize */ - plane_atomic_power_down(dc, pipe_ctx); + hws->funcs.plane_atomic_power_down(dc, + pipe_ctx->plane_res.dpp, + pipe_ctx->plane_res.hubp); pipe_ctx->stream = NULL; memset(&pipe_ctx->stream_res, 0, sizeof(pipe_ctx->stream_res)); @@ -934,14 +1135,15 @@ pipe_ctx->plane_state = NULL; } -static void dcn10_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx) { + struct dce_hwseq *hws = dc->hwseq; DC_LOGGER_INIT(dc->ctx->logger); if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated) return; - plane_atomic_disable(dc, pipe_ctx); + hws->funcs.plane_atomic_disable(dc, pipe_ctx); apply_DEGVIDCN10_253_wa(dc); @@ -949,16 +1151,133 @@ pipe_ctx->pipe_idx); } -static void dcn10_init_hw(struct dc *dc) +void dcn10_init_pipes(struct dc *dc, struct dc_state *context) { int i; + struct dce_hwseq *hws = dc->hwseq; + bool can_apply_seamless_boot = false; + + for (i = 0; i < context->stream_count; i++) { + if (context->streams[i]->apply_seamless_boot_optimization) { + can_apply_seamless_boot = true; + break; + } + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct timing_generator *tg = dc->res_pool->timing_generators[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /* There is assumption that pipe_ctx is not mapping irregularly + * to non-preferred front end. If pipe_ctx->stream is not NULL, + * we will use the pipe, so don't disable + */ + if (pipe_ctx->stream != NULL && can_apply_seamless_boot) + continue; + + /* Blank controller using driver code instead of + * command table. + */ + if (tg->funcs->is_tg_enabled(tg)) { + if (hws->funcs.init_blank != NULL) { + hws->funcs.init_blank(dc, tg); + tg->funcs->lock(tg); + } else { + tg->funcs->lock(tg); + tg->funcs->set_blank(tg, true); + hwss_wait_for_blank_complete(tg); + } + } + } + + /* num_opp will be equal to number of mpcc */ + for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /* Cannot reset the MPC mux if seamless boot */ + if (pipe_ctx->stream != NULL && can_apply_seamless_boot) + continue; + + dc->res_pool->mpc->funcs->mpc_init_single_inst( + dc->res_pool->mpc, i); + } + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct timing_generator *tg = dc->res_pool->timing_generators[i]; + struct hubp *hubp = dc->res_pool->hubps[i]; + struct dpp *dpp = dc->res_pool->dpps[i]; + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + /* There is assumption that pipe_ctx is not mapping irregularly + * to non-preferred front end. If pipe_ctx->stream is not NULL, + * we will use the pipe, so don't disable + */ + if (can_apply_seamless_boot && + pipe_ctx->stream != NULL && + pipe_ctx->stream_res.tg->funcs->is_tg_enabled( + pipe_ctx->stream_res.tg)) { + // Enable double buffering for OTG_BLANK no matter if + // seamless boot is enabled or not to suppress global sync + // signals when OTG blanked. This is to prevent pipe from + // requesting data while in PSR. + tg->funcs->tg_init(tg); + continue; + } + + /* Disable on the current state so the new one isn't cleared. */ + pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + + dpp->funcs->dpp_reset(dpp); + + pipe_ctx->stream_res.tg = tg; + pipe_ctx->pipe_idx = i; + + pipe_ctx->plane_res.hubp = hubp; + pipe_ctx->plane_res.dpp = dpp; + pipe_ctx->plane_res.mpcc_inst = dpp->inst; + hubp->mpcc_id = dpp->inst; + hubp->opp_id = OPP_ID_INVALID; + hubp->power_gated = false; + + dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst; + dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL; + dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; + pipe_ctx->stream_res.opp = dc->res_pool->opps[i]; + + hws->funcs.plane_atomic_disconnect(dc, pipe_ctx); + + if (tg->funcs->is_tg_enabled(tg)) + tg->funcs->unlock(tg); + + dc->hwss.disable_plane(dc, pipe_ctx); + + pipe_ctx->stream_res.tg = NULL; + pipe_ctx->plane_res.hubp = NULL; + + tg->funcs->tg_init(tg); + } +} + +void dcn10_init_hw(struct dc *dc) +{ + int i, j; struct abm *abm = dc->res_pool->abm; struct dmcu *dmcu = dc->res_pool->dmcu; struct dce_hwseq *hws = dc->hwseq; struct dc_bios *dcb = dc->ctx->dc_bios; - struct dc_state *context = dc->current_state; + struct resource_pool *res_pool = dc->res_pool; + uint32_t backlight = MAX_BACKLIGHT_LEVEL; + bool is_optimized_init_done = false; + + if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks) + dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); + + // Initialize the dccg + if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->dccg_init) + dc->res_pool->dccg->funcs->dccg_init(res_pool->dccg); if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + REG_WRITE(REFCLK_CNTL, 0); REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1); REG_WRITE(DIO_MEM_PWR_CTRL, 0); @@ -972,113 +1291,145 @@ REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); } - enable_power_gating_plane(dc->hwseq, true); - } else { + //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); - if (!dcb->funcs->is_accelerated_mode(dcb)) { - bios_golden_init(dc); - disable_vga(dc->hwseq); + return; + } + + if (!dcb->funcs->is_accelerated_mode(dcb)) + hws->funcs.disable_vga(dc->hwseq); + + hws->funcs.bios_golden_init(dc); + + 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 (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + if (res_pool->dccg && 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); + + (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 (!is_optimized_init_done) + 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; + } + + /* Power gate DSCs */ + if (!is_optimized_init_done) { + for (i = 0; i < res_pool->res_cap->num_dsc; i++) + if (hws->funcs.dsc_pg_control != NULL) + hws->funcs.dsc_pg_control(hws, res_pool->dscs[i]->inst, false); + } + + /* we want to turn off all dp displays before doing detection */ + if (dc->config.power_down_display_on_boot) { + uint8_t dpcd_power_state = '\0'; + enum dc_status status = DC_ERROR_UNEXPECTED; + + for (i = 0; i < dc->link_count; i++) { + if (dc->links[i]->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) + continue; + + /* + * If any of the displays are lit up turn them off. + * The reason is that some MST hubs cannot be turned off + * completely until we tell them to do so. + * If not turned off, then displays connected to MST hub + * won't light up. + */ + status = core_link_read_dpcd(dc->links[i], DP_SET_POWER, + &dpcd_power_state, sizeof(dpcd_power_state)); + if (status == DC_OK && dpcd_power_state == DP_POWER_STATE_D0) { + /* blank dp stream before power off receiver*/ + if (dc->links[i]->link_enc->funcs->get_dig_frontend) { + unsigned int fe = dc->links[i]->link_enc->funcs->get_dig_frontend(dc->links[i]->link_enc); + + for (j = 0; j < dc->res_pool->stream_enc_count; j++) { + if (fe == dc->res_pool->stream_enc[j]->id) { + dc->res_pool->stream_enc[j]->funcs->dp_blank( + dc->res_pool->stream_enc[j]); + break; + } + } + } + dp_receiver_power_ctrl(dc->links[i], false); + } + } + } + + /* 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.power_down_display_on_boot) { + if (!is_optimized_init_done) { + 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); + } + } + + if (!is_optimized_init_done) { + + 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++) { - /* 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->link_enc->connector.id == CONNECTOR_ID_EDP) - dc->hwss.edp_power_control(link, true); - - link->link_enc->funcs->hw_init(link->link_enc); + if (link->panel_cntl) + backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl); } + + if (abm != NULL) + abm->funcs->abm_init(abm, backlight); + + if (dmcu != NULL && !dmcu->auto_load_dmcu) + dmcu->funcs->dmcu_init(dmcu); } - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct timing_generator *tg = dc->res_pool->timing_generators[i]; - - if (tg->funcs->is_tg_enabled(tg)) - tg->funcs->lock(tg); - } - - /* Blank controller using driver code instead of - * command table. - */ - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct timing_generator *tg = dc->res_pool->timing_generators[i]; - - if (tg->funcs->is_tg_enabled(tg)) { - tg->funcs->set_blank(tg, true); - hwss_wait_for_blank_complete(tg); - } - } - - /* Reset all MPCC muxes */ - dc->res_pool->mpc->funcs->mpc_init(dc->res_pool->mpc); - - for (i = 0; i < dc->res_pool->timing_generator_count; i++) { - struct timing_generator *tg = dc->res_pool->timing_generators[i]; - struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; - struct hubp *hubp = dc->res_pool->hubps[i]; - struct dpp *dpp = dc->res_pool->dpps[i]; - - pipe_ctx->stream_res.tg = tg; - pipe_ctx->pipe_idx = i; - - pipe_ctx->plane_res.hubp = hubp; - pipe_ctx->plane_res.dpp = dpp; - pipe_ctx->plane_res.mpcc_inst = dpp->inst; - hubp->mpcc_id = dpp->inst; - hubp->opp_id = 0xf; - hubp->power_gated = false; - - dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst; - dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL; - dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; - pipe_ctx->stream_res.opp = dc->res_pool->opps[i]; - - hwss1_plane_atomic_disconnect(dc, pipe_ctx); - } - - for (i = 0; i < dc->res_pool->pipe_count; i++) { - struct timing_generator *tg = dc->res_pool->timing_generators[i]; - - if (tg->funcs->is_tg_enabled(tg)) - tg->funcs->unlock(tg); - } - - 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]; - - dcn10_disable_plane(dc, pipe_ctx); - - pipe_ctx->stream_res.tg = NULL; - pipe_ctx->plane_res.hubp = NULL; - - tg->funcs->tg_init(tg); - } - - /* end of FPGA. Below if real ASIC */ - if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) - return; - - for (i = 0; i < dc->res_pool->audio_count; i++) { - struct audio *audio = dc->res_pool->audios[i]; - - audio->funcs->hw_init(audio); - } - - if (abm != NULL) { - abm->funcs->init_backlight(abm); - abm->funcs->abm_init(abm); - } - - if (dmcu != NULL) - dmcu->funcs->dmcu_init(dmcu); + if (abm != NULL && dmcu != NULL) + abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu); /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ - REG_WRITE(DIO_MEM_PWR_CTRL, 0); + if (!is_optimized_init_done) + REG_WRITE(DIO_MEM_PWR_CTRL, 0); if (!dc->debug.disable_clock_gate) { /* enable all DCN clock gating */ @@ -1088,17 +1439,71 @@ REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); } + if (hws->funcs.enable_power_gating_plane) + hws->funcs.enable_power_gating_plane(dc->hwseq, true); - enable_power_gating_plane(dc->hwseq, true); + if (dc->clk_mgr->funcs->notify_wm_ranges) + dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr); - memset(&dc->res_pool->dccg->clks, 0, sizeof(dc->res_pool->dccg->clks)); +#ifdef CONFIG_DRM_AMD_DC_DCN3_0 + if (dc->clk_mgr->funcs->set_hard_max_memclk) + dc->clk_mgr->funcs->set_hard_max_memclk(dc->clk_mgr); +#endif + } -static void reset_hw_ctx_wrap( +/* 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 + */ +void dcn10_power_down_on_boot(struct dc *dc) +{ + int i = 0; + struct dc_link *edp_link; + + if (!dc->config.power_down_display_on_boot) + return; + + edp_link = get_edp_link(dc); + 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->hwss.power_down && + dc->hwss.edp_power_control) { + dc->hwseq->funcs.edp_backlight_control(edp_link, false); + dc->hwss.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) && + dc->hwss.power_down) { + dc->hwss.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); +} + +void dcn10_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--) { @@ -1116,12 +1521,13 @@ pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) { struct clock_source *old_clk = pipe_ctx_old->clock_source; - reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state); + dcn10_reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state); + if (hws->funcs.enable_stream_gating) + hws->funcs.enable_stream_gating(dc, pipe_ctx); if (old_clk) old_clk->funcs->cs_power_down(old_clk); } } - } static bool patch_address_for_sbs_tb_stereo( @@ -1150,9 +1556,7 @@ return false; } - - -static void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) { bool addr_patched = false; PHYSICAL_ADDRESS_LOC addr; @@ -1177,8 +1581,8 @@ pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; } -static bool dcn10_set_input_transfer_func(struct pipe_ctx *pipe_ctx, - const struct dc_plane_state *plane_state) +bool dcn10_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, + const struct dc_plane_state *plane_state) { struct dpp *dpp_base = pipe_ctx->plane_res.dpp; const struct dc_transfer_func *tf = NULL; @@ -1210,6 +1614,11 @@ 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; @@ -1227,13 +1636,36 @@ return result; } +#define MAX_NUM_HW_POINTS 0x200 +static void log_tf(struct dc_context *ctx, + struct dc_transfer_func *tf, uint32_t hw_points_num) +{ + // DC_LOG_GAMMA is default logging of all hw points + // DC_LOG_ALL_GAMMA logs all points, not only hw points + // DC_LOG_ALL_TF_POINTS logs all channels of the tf + int i = 0; + DC_LOGGER_INIT(ctx->logger); + DC_LOG_GAMMA("Gamma Correction TF"); + DC_LOG_ALL_GAMMA("Logging all tf points..."); + DC_LOG_ALL_TF_CHANNELS("Logging all channels..."); + for (i = 0; i < hw_points_num; i++) { + DC_LOG_GAMMA("R\t%d\t%llu", i, tf->tf_pts.red[i].value); + DC_LOG_ALL_TF_CHANNELS("G\t%d\t%llu", i, tf->tf_pts.green[i].value); + DC_LOG_ALL_TF_CHANNELS("B\t%d\t%llu", i, tf->tf_pts.blue[i].value); + } -static bool -dcn10_set_output_transfer_func(struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) + for (i = hw_points_num; i < MAX_NUM_HW_POINTS; i++) { + DC_LOG_ALL_GAMMA("R\t%d\t%llu", i, tf->tf_pts.red[i].value); + DC_LOG_ALL_TF_CHANNELS("G\t%d\t%llu", i, tf->tf_pts.green[i].value); + DC_LOG_ALL_TF_CHANNELS("B\t%d\t%llu", i, tf->tf_pts.blue[i].value); + } +} + +bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, + const struct dc_stream_state *stream) { struct dpp *dpp = pipe_ctx->plane_res.dpp; @@ -1259,22 +1691,31 @@ } else dpp->funcs->dpp_program_regamma_pwl(dpp, NULL, OPP_REGAMMA_BYPASS); + if (stream != NULL && stream->ctx != NULL && + stream->out_transfer_func != NULL) { + log_tf(stream->ctx, + stream->out_transfer_func, + dpp->regamma_params.hw_points_num); + } + return true; } -static void dcn10_pipe_control_lock( +void dcn10_pipe_control_lock( struct dc *dc, struct pipe_ctx *pipe, bool lock) { + struct dce_hwseq *hws = dc->hwseq; + /* use TG master update lock to lock everything on the TG * therefore only top pipe need to lock */ - if (pipe->top_pipe) + if (!pipe || pipe->top_pipe) return; if (dc->debug.sanity_checks) - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); if (lock) pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg); @@ -1282,7 +1723,98 @@ pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg); if (dc->debug.sanity_checks) - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); +} + +/** + * delay_cursor_until_vupdate() - Delay cursor update if too close to VUPDATE. + * + * Software keepout workaround to prevent cursor update locking from stalling + * out cursor updates indefinitely or from old values from being retained in + * the case where the viewport changes in the same frame as the cursor. + * + * The idea is to calculate the remaining time from VPOS to VUPDATE. If it's + * too close to VUPDATE, then stall out until VUPDATE finishes. + * + * TODO: Optimize cursor programming to be once per frame before VUPDATE + * to avoid the need for this workaround. + */ +static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct dc_stream_state *stream = pipe_ctx->stream; + struct crtc_position position; + uint32_t vupdate_start, vupdate_end; + unsigned int lines_to_vupdate, us_to_vupdate, vpos; + unsigned int us_per_line, us_vupdate; + + if (!dc->hwss.calc_vupdate_position || !dc->hwss.get_position) + return; + + if (!pipe_ctx->stream_res.stream_enc || !pipe_ctx->stream_res.tg) + return; + + dc->hwss.calc_vupdate_position(dc, pipe_ctx, &vupdate_start, + &vupdate_end); + + dc->hwss.get_position(&pipe_ctx, 1, &position); + vpos = position.vertical_count; + + /* Avoid wraparound calculation issues */ + vupdate_start += stream->timing.v_total; + vupdate_end += stream->timing.v_total; + vpos += stream->timing.v_total; + + if (vpos <= vupdate_start) { + /* VPOS is in VACTIVE or back porch. */ + lines_to_vupdate = vupdate_start - vpos; + } else if (vpos > vupdate_end) { + /* VPOS is in the front porch. */ + return; + } else { + /* VPOS is in VUPDATE. */ + lines_to_vupdate = 0; + } + + /* Calculate time until VUPDATE in microseconds. */ + us_per_line = + stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz; + us_to_vupdate = lines_to_vupdate * us_per_line; + + /* 70 us is a conservative estimate of cursor update time*/ + if (us_to_vupdate > 70) + return; + + /* Stall out until the cursor update completes. */ + if (vupdate_end < vupdate_start) + vupdate_end += stream->timing.v_total; + us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line; + udelay(us_to_vupdate + us_vupdate); +} + +void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock) +{ + /* cursor lock is per MPCC tree, so only need to lock one pipe per stream */ + if (!pipe || pipe->top_pipe) + return; + + /* Prevent cursor lock from stalling out cursor updates. */ + if (lock) + delay_cursor_until_vupdate(dc, pipe); + + if (pipe->stream && should_use_dmub_lock(pipe->stream->link)) { + union dmub_hw_lock_flags hw_locks = { 0 }; + struct dmub_hw_lock_inst_flags inst_flags = { 0 }; + + hw_locks.bits.lock_cursor = 1; + inst_flags.opp_inst = pipe->stream_res.opp->inst; + + dmub_hw_lock_mgr_cmd(dc->ctx->dmub_srv, + lock, + &hw_locks, + &inst_flags); + } else + dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc, + pipe->stream_res.opp->inst, lock); } static bool wait_for_reset_trigger_to_occur( @@ -1322,7 +1854,7 @@ return rc; } -static void dcn10_enable_timing_synchronization( +void dcn10_enable_timing_synchronization( struct dc *dc, int group_index, int group_size, @@ -1352,7 +1884,7 @@ DC_SYNC_INFO("Sync complete\n"); } -static void dcn10_enable_per_frame_crtc_position_reset( +void dcn10_enable_per_frame_crtc_position_reset( struct dc *dc, int group_size, struct pipe_ctx *grouped_pipes[]) @@ -1365,7 +1897,7 @@ if (grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset) grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset( grouped_pipes[i]->stream_res.tg, - grouped_pipes[i]->stream->triggered_crtc_reset.event_source->status.primary_otg_inst, + 0, &grouped_pipes[i]->stream->triggered_crtc_reset); DC_SYNC_INFO("Waiting for trigger\n"); @@ -1377,10 +1909,10 @@ } /*static void print_rq_dlg_ttu( - struct dc *core_dc, + struct dc *dc, struct pipe_ctx *pipe_ctx) { - DC_LOG_BANDWIDTH_CALCS(core_dc->ctx->logger, + DC_LOG_BANDWIDTH_CALCS(dc->ctx->logger, "\n============== DML TTU Output parameters [%d] ==============\n" "qos_level_low_wm: %d, \n" "qos_level_high_wm: %d, \n" @@ -1410,7 +1942,7 @@ pipe_ctx->ttu_regs.refcyc_per_req_delivery_pre_c ); - DC_LOG_BANDWIDTH_CALCS(core_dc->ctx->logger, + DC_LOG_BANDWIDTH_CALCS(dc->ctx->logger, "\n============== DML DLG Output parameters [%d] ==============\n" "refcyc_h_blank_end: %d, \n" "dlg_vblank_end: %d, \n" @@ -1445,7 +1977,7 @@ pipe_ctx->dlg_regs.refcyc_per_pte_group_nom_l ); - DC_LOG_BANDWIDTH_CALCS(core_dc->ctx->logger, + DC_LOG_BANDWIDTH_CALCS(dc->ctx->logger, "\ndst_y_per_meta_row_nom_l: %d, \n" "refcyc_per_meta_chunk_nom_l: %d, \n" "refcyc_per_line_delivery_pre_l: %d, \n" @@ -1475,7 +2007,7 @@ pipe_ctx->dlg_regs.refcyc_per_line_delivery_c ); - DC_LOG_BANDWIDTH_CALCS(core_dc->ctx->logger, + DC_LOG_BANDWIDTH_CALCS(dc->ctx->logger, "\n============== DML RQ Output parameters [%d] ==============\n" "chunk_size: %d \n" "min_chunk_size: %d \n" @@ -1569,7 +2101,7 @@ } -static void dcn10_program_pte_vm(struct dce_hwseq *hws, struct hubp *hubp) +void dcn10_program_pte_vm(struct dce_hwseq *hws, struct hubp *hubp) { struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp); struct vm_system_aperture_param apt = { {{ 0 } } }; @@ -1590,7 +2122,7 @@ struct dce_hwseq *hws = dc->hwseq; if (dc->debug.sanity_checks) { - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); } undo_DEGVIDCN10_253_wa(dc); @@ -1647,11 +2179,11 @@ dcn10_program_pte_vm(hws, pipe_ctx->plane_res.hubp); if (dc->debug.sanity_checks) { - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); } } -static void program_gamut_remap(struct pipe_ctx *pipe_ctx) +void dcn10_program_gamut_remap(struct pipe_ctx *pipe_ctx) { int i = 0; struct dpp_grph_csc_adjustment adjust; @@ -1664,89 +2196,80 @@ for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) adjust.temperature_matrix[i] = pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + } else if (pipe_ctx->plane_state && + pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) { + adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; + for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) + adjust.temperature_matrix[i] = + pipe_ctx->plane_state->gamut_remap_matrix.matrix[i]; } pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp, &adjust); } -static void program_csc_matrix(struct pipe_ctx *pipe_ctx, +static bool dcn10_is_rear_mpo_fix_required(struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace) +{ + if (pipe_ctx->plane_state && pipe_ctx->plane_state->layer_index > 0 && is_rgb_cspace(colorspace)) { + if (pipe_ctx->top_pipe) { + struct pipe_ctx *top = pipe_ctx->top_pipe; + + while (top->top_pipe) + top = top->top_pipe; // Traverse to top pipe_ctx + if (top->plane_state && top->plane_state->layer_index == 0) + return true; // Front MPO plane not hidden + } + } + return false; +} + +static void dcn10_set_csc_adjustment_rgb_mpo_fix(struct pipe_ctx *pipe_ctx, uint16_t *matrix) +{ + // Override rear plane RGB bias to fix MPO brightness + uint16_t rgb_bias = matrix[3]; + + matrix[3] = 0; + matrix[7] = 0; + matrix[11] = 0; + pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment(pipe_ctx->plane_res.dpp, matrix); + matrix[3] = rgb_bias; + matrix[7] = rgb_bias; + matrix[11] = rgb_bias; +} + +void dcn10_program_output_csc(struct dc *dc, + struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace, - uint16_t *matrix) + uint16_t *matrix, + int opp_id) { if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) { - if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment != NULL) + if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment != NULL) { + + /* MPO is broken with RGB colorspaces when OCSC matrix + * brightness offset >= 0 on DCN1 due to OCSC before MPC + * Blending adds offsets from front + rear to rear plane + * + * Fix is to set RGB bias to 0 on rear plane, top plane + * black value pixels add offset instead of rear + front + */ + + int16_t rgb_bias = matrix[3]; + // matrix[3/7/11] are all the same offset value + + if (rgb_bias > 0 && dcn10_is_rear_mpo_fix_required(pipe_ctx, colorspace)) { + dcn10_set_csc_adjustment_rgb_mpo_fix(pipe_ctx, matrix); + } else { pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment(pipe_ctx->plane_res.dpp, matrix); + } + } } else { if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_default != NULL) pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_default(pipe_ctx->plane_res.dpp, colorspace); } } -static void dcn10_program_output_csc(struct dc *dc, - struct pipe_ctx *pipe_ctx, - enum dc_color_space colorspace, - uint16_t *matrix, - int opp_id) -{ - if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment != NULL) - program_csc_matrix(pipe_ctx, - colorspace, - matrix); -} - -static bool is_lower_pipe_tree_visible(struct pipe_ctx *pipe_ctx) -{ - if (pipe_ctx->plane_state->visible) - return true; - if (pipe_ctx->bottom_pipe && is_lower_pipe_tree_visible(pipe_ctx->bottom_pipe)) - return true; - return false; -} - -static bool is_upper_pipe_tree_visible(struct pipe_ctx *pipe_ctx) -{ - if (pipe_ctx->plane_state->visible) - return true; - if (pipe_ctx->top_pipe && is_upper_pipe_tree_visible(pipe_ctx->top_pipe)) - return true; - return false; -} - -static bool is_pipe_tree_visible(struct pipe_ctx *pipe_ctx) -{ - if (pipe_ctx->plane_state->visible) - return true; - if (pipe_ctx->top_pipe && is_upper_pipe_tree_visible(pipe_ctx->top_pipe)) - return true; - if (pipe_ctx->bottom_pipe && is_lower_pipe_tree_visible(pipe_ctx->bottom_pipe)) - return true; - return false; -} - -bool is_rgb_cspace(enum dc_color_space output_color_space) -{ - switch (output_color_space) { - case COLOR_SPACE_SRGB: - case COLOR_SPACE_SRGB_LIMITED: - case COLOR_SPACE_2020_RGB_FULLRANGE: - case COLOR_SPACE_2020_RGB_LIMITEDRANGE: - case COLOR_SPACE_ADOBERGB: - return true; - case COLOR_SPACE_YCBCR601: - case COLOR_SPACE_YCBCR709: - case COLOR_SPACE_YCBCR601_LIMITED: - case COLOR_SPACE_YCBCR709_LIMITED: - case COLOR_SPACE_2020_YCBCR: - return false; - default: - /* Add a case to switch */ - BREAK_TO_DEBUGGER(); - return false; - } -} - -static void dcn10_get_surface_visual_confirm_color( +void dcn10_get_surface_visual_confirm_color( const struct pipe_ctx *pipe_ctx, struct tg_color *color) { @@ -1754,25 +2277,25 @@ switch (pipe_ctx->plane_res.scl_data.format) { case PIXEL_FORMAT_ARGB8888: - /* set boarder color to red */ + /* set border color to red */ color->color_r_cr = color_value; break; case PIXEL_FORMAT_ARGB2101010: - /* set boarder color to blue */ + /* set border color to blue */ color->color_b_cb = color_value; break; case PIXEL_FORMAT_420BPP8: - /* set boarder color to green */ + /* set border color to green */ color->color_g_y = color_value; break; case PIXEL_FORMAT_420BPP10: - /* set boarder color to yellow */ + /* set border color to yellow */ color->color_g_y = color_value; color->color_r_cr = color_value; break; case PIXEL_FORMAT_FP16: - /* set boarder color to white */ + /* set border color to white */ color->color_r_cr = color_value; color->color_b_cb = color_value; color->color_g_y = color_value; @@ -1782,7 +2305,7 @@ } } -static void dcn10_get_hdr_visual_confirm_color( +void dcn10_get_hdr_visual_confirm_color( struct pipe_ctx *pipe_ctx, struct tg_color *color) { @@ -1796,22 +2319,26 @@ switch (top_pipe_ctx->plane_res.scl_data.format) { case PIXEL_FORMAT_ARGB2101010: - if (top_pipe_ctx->stream->out_transfer_func->tf == TRANSFER_FUNCTION_UNITY) { - /* HDR10, ARGB2101010 - set boarder color to red */ + if (top_pipe_ctx->stream->out_transfer_func->tf == TRANSFER_FUNCTION_PQ) { + /* HDR10, ARGB2101010 - set border color to red */ color->color_r_cr = color_value; + } else if (top_pipe_ctx->stream->out_transfer_func->tf == TRANSFER_FUNCTION_GAMMA22) { + /* FreeSync 2 ARGB2101010 - set border color to pink */ + color->color_r_cr = color_value; + color->color_b_cb = color_value; } break; case PIXEL_FORMAT_FP16: if (top_pipe_ctx->stream->out_transfer_func->tf == TRANSFER_FUNCTION_PQ) { - /* HDR10, FP16 - set boarder color to blue */ + /* HDR10, FP16 - set border color to blue */ color->color_b_cb = color_value; } else if (top_pipe_ctx->stream->out_transfer_func->tf == TRANSFER_FUNCTION_GAMMA22) { - /* FreeSync 2 HDR - set boarder color to green */ + /* FreeSync 2 HDR - set border color to green */ color->color_g_y = color_value; } break; default: - /* SDR - set boarder color to Gray */ + /* SDR - set border color to Gray */ color->color_r_cr = color_value/2; color->color_b_cb = color_value/2; color->color_g_y = color_value/2; @@ -1819,70 +2346,7 @@ } } -static uint16_t fixed_point_to_int_frac( - struct fixed31_32 arg, - uint8_t integer_bits, - uint8_t fractional_bits) -{ - int32_t numerator; - int32_t divisor = 1 << fractional_bits; - - uint16_t result; - - uint16_t d = (uint16_t)dc_fixpt_floor( - dc_fixpt_abs( - arg)); - - if (d <= (uint16_t)(1 << integer_bits) - (1 / (uint16_t)divisor)) - numerator = (uint16_t)dc_fixpt_floor( - dc_fixpt_mul_int( - arg, - divisor)); - else { - numerator = dc_fixpt_floor( - dc_fixpt_sub( - dc_fixpt_from_int( - 1LL << integer_bits), - dc_fixpt_recip( - dc_fixpt_from_int( - divisor)))); - } - - if (numerator >= 0) - result = (uint16_t)numerator; - else - result = (uint16_t)( - (1 << (integer_bits + fractional_bits + 1)) + numerator); - - if ((result != 0) && dc_fixpt_lt( - arg, dc_fixpt_zero)) - result |= 1 << (integer_bits + fractional_bits); - - return result; -} - -void build_prescale_params(struct dc_bias_and_scale *bias_and_scale, - const struct dc_plane_state *plane_state) -{ - if (plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN - && plane_state->format != SURFACE_PIXEL_FORMAT_INVALID - && plane_state->input_csc_color_matrix.enable_adjustment - && plane_state->coeff_reduction_factor.value != 0) { - bias_and_scale->scale_blue = fixed_point_to_int_frac( - dc_fixpt_mul(plane_state->coeff_reduction_factor, - dc_fixpt_from_fraction(256, 255)), - 2, - 13); - bias_and_scale->scale_red = bias_and_scale->scale_blue; - bias_and_scale->scale_green = bias_and_scale->scale_blue; - } else { - bias_and_scale->scale_blue = 0x2000; - bias_and_scale->scale_red = 0x2000; - bias_and_scale->scale_green = 0x2000; - } -} - -static void update_dpp(struct dpp *dpp, struct dc_plane_state *plane_state) +static void dcn10_update_dpp(struct dpp *dpp, struct dc_plane_state *plane_state) { struct dc_bias_and_scale bns_params = {0}; @@ -1891,7 +2355,8 @@ plane_state->format, EXPANSION_MODE_ZERO, plane_state->input_csc_color_matrix, - plane_state->color_space); + plane_state->color_space, + NULL); //set scale and bias registers build_prescale_params(&bns_params, plane_state); @@ -1899,25 +2364,22 @@ dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params); } -static void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) +void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) { + struct dce_hwseq *hws = dc->hwseq; struct hubp *hubp = pipe_ctx->plane_res.hubp; - struct mpcc_blnd_cfg blnd_cfg = {0}; + struct mpcc_blnd_cfg blnd_cfg = {{0}}; bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; 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); - - - /* TODO: proper fix once fpga works */ - if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR) { - dcn10_get_hdr_visual_confirm_color( + hws->funcs.get_hdr_visual_confirm_color( pipe_ctx, &blnd_cfg.black_color); } else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) { - dcn10_get_surface_visual_confirm_color( + hws->funcs.get_surface_visual_confirm_color( pipe_ctx, &blnd_cfg.black_color); } else { color_space_to_black_color( @@ -1925,14 +2387,22 @@ &blnd_cfg.black_color); } - if (per_pixel_alpha) - blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; - else - blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; - blnd_cfg.overlap_only = false; - blnd_cfg.global_alpha = 0xff; blnd_cfg.global_gain = 0xff; + + if (per_pixel_alpha && 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 if (per_pixel_alpha) { + blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; + } else { + 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 = 0xff; /* DCN1.0 has output CM before MPC which seems to screw with * pre-multiplied alpha. @@ -1988,8 +2458,6 @@ bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; - /* TODO: proper fix once fpga works */ - pipe_ctx->plane_res.scl_data.lb_params.alpha_en = per_pixel_alpha; pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP; /* scaler configuration */ @@ -1997,32 +2465,72 @@ pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data); } -static void update_dchubp_dpp( +static void dcn10_update_dchubp_dpp( struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context) { + struct dce_hwseq *hws = dc->hwseq; struct hubp *hubp = pipe_ctx->plane_res.hubp; struct dpp *dpp = pipe_ctx->plane_res.dpp; struct dc_plane_state *plane_state = pipe_ctx->plane_state; - union plane_size size = plane_state->plane_size; + struct plane_size size = plane_state->plane_size; + unsigned int compat_level = 0; + bool should_divided_by_2 = false; /* depends on DML calculation, DPP clock value may change dynamically */ /* If request max dpp clk is lower than current dispclk, no need to * divided by 2 */ if (plane_state->update_flags.bits.full_update) { - bool should_divided_by_2 = context->bw.dcn.clk.dppclk_khz <= - dc->res_pool->dccg->clks.dispclk_khz / 2; + + /* new calculated dispclk, dppclk are stored in + * context->bw_ctx.bw.dcn.clk.dispclk_khz / dppclk_khz. current + * dispclk, dppclk are from dc->clk_mgr->clks.dispclk_khz. + * dcn_validate_bandwidth compute new dispclk, dppclk. + * dispclk will put in use after optimize_bandwidth when + * ramp_up_dispclk_with_dpp is called. + * there are two places for dppclk be put in use. One location + * is the same as the location as dispclk. Another is within + * update_dchubp_dpp which happens between pre_bandwidth and + * optimize_bandwidth. + * dppclk updated within update_dchubp_dpp will cause new + * clock values of dispclk and dppclk not be in use at the same + * time. when clocks are decreased, this may cause dppclk is + * lower than previous configuration and let pipe stuck. + * for example, eDP + external dp, change resolution of DP from + * 1920x1080x144hz to 1280x960x60hz. + * before change: dispclk = 337889 dppclk = 337889 + * change mode, dcn_validate_bandwidth calculate + * dispclk = 143122 dppclk = 143122 + * update_dchubp_dpp be executed before dispclk be updated, + * dispclk = 337889, but dppclk use new value dispclk /2 = + * 168944. this will cause pipe pstate warning issue. + * solution: between pre_bandwidth and optimize_bandwidth, while + * dispclk is going to be decreased, keep dppclk = dispclk + **/ + if (context->bw_ctx.bw.dcn.clk.dispclk_khz < + dc->clk_mgr->clks.dispclk_khz) + should_divided_by_2 = false; + else + should_divided_by_2 = + context->bw_ctx.bw.dcn.clk.dppclk_khz <= + dc->clk_mgr->clks.dispclk_khz / 2; dpp->funcs->dpp_dppclk_control( dpp, should_divided_by_2, true); - dc->res_pool->dccg->clks.dppclk_khz = should_divided_by_2 ? - dc->res_pool->dccg->clks.dispclk_khz / 2 : - dc->res_pool->dccg->clks.dispclk_khz; + if (dc->res_pool->dccg) + dc->res_pool->dccg->funcs->update_dpp_dto( + dc->res_pool->dccg, + dpp->inst, + pipe_ctx->plane_res.bw.dppclk_khz); + else + dc->clk_mgr->clks.dppclk_khz = should_divided_by_2 ? + dc->clk_mgr->clks.dispclk_khz / 2 : + dc->clk_mgr->clks.dispclk_khz; } /* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG @@ -2038,20 +2546,26 @@ &pipe_ctx->ttu_regs, &pipe_ctx->rq_regs, &pipe_ctx->pipe_dlg_param); + hubp->funcs->hubp_setup_interdependent( + hubp, + &pipe_ctx->dlg_regs, + &pipe_ctx->ttu_regs); } - size.grph.surface_size = pipe_ctx->plane_res.scl_data.viewport; + size.surface_size = pipe_ctx->plane_res.scl_data.viewport; if (plane_state->update_flags.bits.full_update || plane_state->update_flags.bits.bpp_change) - update_dpp(dpp, plane_state); - - if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.per_pixel_alpha_change) - dc->hwss.update_mpcc(dc, pipe_ctx); + dcn10_update_dpp(dpp, plane_state); if (plane_state->update_flags.bits.full_update || plane_state->update_flags.bits.per_pixel_alpha_change || + plane_state->update_flags.bits.global_alpha_change) + hws->funcs.update_mpcc(dc, pipe_ctx); + + if (plane_state->update_flags.bits.full_update || + plane_state->update_flags.bits.per_pixel_alpha_change || + plane_state->update_flags.bits.global_alpha_change || plane_state->update_flags.bits.scaling_change || plane_state->update_flags.bits.position_change) { update_scaler(pipe_ctx); @@ -2069,17 +2583,20 @@ if (pipe_ctx->stream->cursor_attributes.address.quad_part != 0) { dc->hwss.set_cursor_position(pipe_ctx); dc->hwss.set_cursor_attribute(pipe_ctx); + + if (dc->hwss.set_cursor_sdr_white_level) + dc->hwss.set_cursor_sdr_white_level(pipe_ctx); } if (plane_state->update_flags.bits.full_update) { /*gamut remap*/ - program_gamut_remap(pipe_ctx); + dc->hwss.program_gamut_remap(pipe_ctx); dc->hwss.program_output_csc(dc, pipe_ctx, pipe_ctx->stream->output_color_space, pipe_ctx->stream->csc_color_matrix.matrix, - hubp->opp_id); + pipe_ctx->stream_res.opp->inst); } if (plane_state->update_flags.bits.full_update || @@ -2089,7 +2606,8 @@ plane_state->update_flags.bits.swizzle_change || plane_state->update_flags.bits.dcc_change || plane_state->update_flags.bits.bpp_change || - plane_state->update_flags.bits.scaling_change) { + plane_state->update_flags.bits.scaling_change || + plane_state->update_flags.bits.plane_size_change) { hubp->funcs->hubp_program_surface_config( hubp, plane_state->format, @@ -2097,18 +2615,19 @@ &size, plane_state->rotation, &plane_state->dcc, - plane_state->horizontal_mirror); + plane_state->horizontal_mirror, + compat_level); } hubp->power_gated = false; - dc->hwss.update_plane_addr(dc, pipe_ctx); + hws->funcs.update_plane_addr(dc, pipe_ctx); if (is_pipe_tree_visible(pipe_ctx)) hubp->funcs->set_blank(hubp, false); } -static void dcn10_blank_pixel_data( +void dcn10_blank_pixel_data( struct dc *dc, struct pipe_ctx *pipe_ctx, bool blank) @@ -2139,20 +2658,22 @@ if (!blank) { if (stream_res->tg->funcs->set_blank) stream_res->tg->funcs->set_blank(stream_res->tg, blank); - if (stream_res->abm) + if (stream_res->abm) { + dc->hwss.set_pipe(pipe_ctx); stream_res->abm->funcs->set_abm_level(stream_res->abm, stream->abm_level); + } } else if (blank) { - if (stream_res->abm) - stream_res->abm->funcs->set_abm_immediate_disable(stream_res->abm); - if (stream_res->tg->funcs->set_blank) + dc->hwss.set_abm_immediate_disable(pipe_ctx); + if (stream_res->tg->funcs->set_blank) { + stream_res->tg->funcs->wait_for_state(stream_res->tg, CRTC_STATE_VBLANK); stream_res->tg->funcs->set_blank(stream_res->tg, blank); + } } } -static void set_hdr_multiplier(struct pipe_ctx *pipe_ctx) +void dcn10_set_hdr_multiplier(struct pipe_ctx *pipe_ctx) { - struct fixed31_32 multiplier = dc_fixpt_from_fraction( - pipe_ctx->plane_state->sdr_white_level, 80); + struct fixed31_32 multiplier = pipe_ctx->plane_state->hdr_mult; uint32_t hw_mult = 0x1f000; // 1.0 default multiplier struct custom_float_format fmt; @@ -2160,7 +2681,8 @@ fmt.mantissa_bits = 12; fmt.sign = true; - if (pipe_ctx->plane_state->sdr_white_level > 80) + + if (!dc_fixpt_eq(multiplier, dc_fixpt_from_int(0))) // check != 0 convert_to_custom_float_format(multiplier, &fmt, &hw_mult); pipe_ctx->plane_res.dpp->funcs->dpp_set_hdr_multiplier( @@ -2172,17 +2694,19 @@ struct pipe_ctx *pipe_ctx, struct dc_state *context) { + struct dce_hwseq *hws = dc->hwseq; + if (pipe_ctx->plane_state->update_flags.bits.full_update) dcn10_enable_plane(dc, pipe_ctx, context); - update_dchubp_dpp(dc, pipe_ctx, context); + dcn10_update_dchubp_dpp(dc, pipe_ctx, context); - set_hdr_multiplier(pipe_ctx); + hws->funcs.set_hdr_multiplier(pipe_ctx); if (pipe_ctx->plane_state->update_flags.bits.full_update || pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || pipe_ctx->plane_state->update_flags.bits.gamma_change) - dc->hwss.set_input_transfer_func(pipe_ctx, pipe_ctx->plane_state); + hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); /* dcn10_translate_regamma_to_hw_format takes 750us to finish * only do gamma programming for full update. @@ -2191,80 +2715,43 @@ * doing heavy calculation and programming */ if (pipe_ctx->plane_state->update_flags.bits.full_update) - dc->hwss.set_output_transfer_func(pipe_ctx, pipe_ctx->stream); + hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); } -static void program_all_pipe_in_tree( +static void dcn10_program_all_pipe_in_tree( struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context) { + struct dce_hwseq *hws = dc->hwseq; + if (pipe_ctx->top_pipe == NULL) { bool blank = !is_pipe_tree_visible(pipe_ctx); - pipe_ctx->stream_res.tg->dlg_otg_param.vready_offset = pipe_ctx->pipe_dlg_param.vready_offset; - pipe_ctx->stream_res.tg->dlg_otg_param.vstartup_start = pipe_ctx->pipe_dlg_param.vstartup_start; - pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_offset = pipe_ctx->pipe_dlg_param.vupdate_offset; - pipe_ctx->stream_res.tg->dlg_otg_param.vupdate_width = pipe_ctx->pipe_dlg_param.vupdate_width; - pipe_ctx->stream_res.tg->dlg_otg_param.signal = pipe_ctx->stream->signal; - pipe_ctx->stream_res.tg->funcs->program_global_sync( - pipe_ctx->stream_res.tg); + pipe_ctx->stream_res.tg, + pipe_ctx->pipe_dlg_param.vready_offset, + pipe_ctx->pipe_dlg_param.vstartup_start, + pipe_ctx->pipe_dlg_param.vupdate_offset, + pipe_ctx->pipe_dlg_param.vupdate_width); - dc->hwss.blank_pixel_data(dc, pipe_ctx, blank); + pipe_ctx->stream_res.tg->funcs->set_vtg_params( + pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); + if (hws->funcs.setup_vupdate_interrupt) + hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx); + + hws->funcs.blank_pixel_data(dc, pipe_ctx, blank); } - if (pipe_ctx->plane_state != NULL) { - dcn10_program_pipe(dc, pipe_ctx, context); - } + if (pipe_ctx->plane_state != NULL) + hws->funcs.program_pipe(dc, pipe_ctx, context); - if (pipe_ctx->bottom_pipe != NULL && pipe_ctx->bottom_pipe != pipe_ctx) { - program_all_pipe_in_tree(dc, pipe_ctx->bottom_pipe, context); - } + if (pipe_ctx->bottom_pipe != NULL && pipe_ctx->bottom_pipe != pipe_ctx) + dcn10_program_all_pipe_in_tree(dc, pipe_ctx->bottom_pipe, context); } -static void dcn10_pplib_apply_display_requirements( - struct dc *dc, - struct dc_state *context) -{ - struct dm_pp_display_configuration *pp_display_cfg = &context->pp_display_cfg; - - pp_display_cfg->min_engine_clock_khz = dc->res_pool->dccg->clks.dcfclk_khz; - pp_display_cfg->min_memory_clock_khz = dc->res_pool->dccg->clks.fclk_khz; - pp_display_cfg->min_engine_clock_deep_sleep_khz = dc->res_pool->dccg->clks.dcfclk_deep_sleep_khz; - pp_display_cfg->min_dcfc_deep_sleep_clock_khz = dc->res_pool->dccg->clks.dcfclk_deep_sleep_khz; - pp_display_cfg->min_dcfclock_khz = dc->res_pool->dccg->clks.dcfclk_khz; - pp_display_cfg->disp_clk_khz = dc->res_pool->dccg->clks.dispclk_khz; - dce110_fill_display_configs(context, pp_display_cfg); - - if (memcmp(&dc->prev_display_config, pp_display_cfg, sizeof( - struct dm_pp_display_configuration)) != 0) - dm_pp_apply_display_requirements(dc->ctx, pp_display_cfg); - - dc->prev_display_config = *pp_display_cfg; -} - -static void optimize_shared_resources(struct dc *dc) -{ - if (dc->current_state->stream_count == 0) { - /* S0i2 message */ - dcn10_pplib_apply_display_requirements(dc, dc->current_state); - } - - if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) - dcn_bw_notify_pplib_of_wm_ranges(dc); -} - -static void ready_shared_resources(struct dc *dc, struct dc_state *context) -{ - /* S0i2 message */ - if (dc->current_state->stream_count == 0 && - context->stream_count != 0) - dcn10_pplib_apply_display_requirements(dc, context); -} - -static struct pipe_ctx *find_top_pipe_for_stream( +static struct pipe_ctx *dcn10_find_top_pipe_for_stream( struct dc *dc, struct dc_state *context, const struct dc_stream_state *stream) @@ -2282,35 +2769,203 @@ if (pipe_ctx->stream != stream) continue; - if (!pipe_ctx->top_pipe) + if (!pipe_ctx->top_pipe && !pipe_ctx->prev_odm_pipe) return pipe_ctx; } return NULL; } -static void dcn10_apply_ctx_for_surface( +bool dcn10_disconnect_pipes( + struct dc *dc, + struct dc_state *context) +{ + bool found_pipe = false; + int i, j; + struct dce_hwseq *hws = dc->hwseq; + struct dc_state *old_ctx = dc->current_state; + bool mpcc_disconnected = false; + struct pipe_ctx *old_pipe; + struct pipe_ctx *new_pipe; + DC_LOGGER_INIT(dc->ctx->logger); + + /* Set pipe update flags and lock pipes */ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + new_pipe = &context->res_ctx.pipe_ctx[i]; + new_pipe->update_flags.raw = 0; + + if (!old_pipe->plane_state && !new_pipe->plane_state) + continue; + + if (old_pipe->plane_state && !new_pipe->plane_state) + new_pipe->update_flags.bits.disable = 1; + + /* Check for scl update */ + if (memcmp(&old_pipe->plane_res.scl_data, &new_pipe->plane_res.scl_data, sizeof(struct scaler_data))) + new_pipe->update_flags.bits.scaler = 1; + + /* Check for vp update */ + if (memcmp(&old_pipe->plane_res.scl_data.viewport, &new_pipe->plane_res.scl_data.viewport, sizeof(struct rect)) + || memcmp(&old_pipe->plane_res.scl_data.viewport_c, + &new_pipe->plane_res.scl_data.viewport_c, sizeof(struct rect))) + new_pipe->update_flags.bits.viewport = 1; + + } + + if (!IS_DIAG_DC(dc->ctx->dce_environment)) { + /* Disconnect mpcc here only if losing pipe split*/ + for (i = 0; i < dc->res_pool->pipe_count; i++) { + if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable && + old_ctx->res_ctx.pipe_ctx[i].top_pipe) { + + /* Find the top pipe in the new ctx for the bottom pipe that we + * want to remove by comparing the streams and planes. If both + * pipes are being disabled then do it in the regular pipe + * programming sequence + */ + for (j = 0; j < dc->res_pool->pipe_count; j++) { + if (old_ctx->res_ctx.pipe_ctx[i].top_pipe->stream == context->res_ctx.pipe_ctx[j].stream && + old_ctx->res_ctx.pipe_ctx[i].top_pipe->plane_state == context->res_ctx.pipe_ctx[j].plane_state && + !context->res_ctx.pipe_ctx[j].top_pipe && + !context->res_ctx.pipe_ctx[j].update_flags.bits.disable) { + found_pipe = true; + break; + } + } + + // Disconnect if the top pipe lost it's pipe split + if (found_pipe && !context->res_ctx.pipe_ctx[j].bottom_pipe) { + hws->funcs.plane_atomic_disconnect(dc, &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); + mpcc_disconnected = true; + } + } + found_pipe = false; + } + } + + if (mpcc_disconnected) { + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + struct dc_plane_state *plane_state = pipe_ctx->plane_state; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + + if (!pipe_ctx || !plane_state || !pipe_ctx->stream) + continue; + + // Only update scaler and viewport here if we lose a pipe split. + // This is to prevent half the screen from being black when we + // unlock after disconnecting MPCC. + if (!(old_pipe && !pipe_ctx->top_pipe && + !pipe_ctx->bottom_pipe && old_pipe->bottom_pipe)) + continue; + + if (pipe_ctx->update_flags.raw || pipe_ctx->plane_state->update_flags.raw || pipe_ctx->stream->update_flags.raw) { + if (pipe_ctx->update_flags.bits.scaler || + plane_state->update_flags.bits.scaling_change || + plane_state->update_flags.bits.position_change || + plane_state->update_flags.bits.per_pixel_alpha_change || + pipe_ctx->stream->update_flags.bits.scaling) { + + pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha; + ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_30BPP); + /* scaler configuration */ + pipe_ctx->plane_res.dpp->funcs->dpp_set_scaler( + pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data); + } + + if (pipe_ctx->update_flags.bits.viewport || + (context == dc->current_state && plane_state->update_flags.bits.position_change) || + (context == dc->current_state && plane_state->update_flags.bits.scaling_change) || + (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) { + + hubp->funcs->mem_program_viewport( + hubp, + &pipe_ctx->plane_res.scl_data.viewport, + &pipe_ctx->plane_res.scl_data.viewport_c); + } + } + } + } + return mpcc_disconnected; +} + +void dcn10_wait_for_pending_cleared(struct dc *dc, + struct dc_state *context) +{ + struct pipe_ctx *pipe_ctx; + struct timing_generator *tg; + int i; + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + pipe_ctx = &context->res_ctx.pipe_ctx[i]; + tg = pipe_ctx->stream_res.tg; + + /* + * Only wait for top pipe's tg penindg bit + * Also skip if pipe is disabled. + */ + if (pipe_ctx->top_pipe || + !pipe_ctx->stream || !pipe_ctx->plane_state || + !tg->funcs->is_tg_enabled(tg)) + continue; + + /* + * Wait for VBLANK then VACTIVE to ensure we get VUPDATE. + * For some reason waiting for OTG_UPDATE_PENDING cleared + * seems to not trigger the update right away, and if we + * lock again before VUPDATE then we don't get a separated + * operation. + */ + 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); + } +} + +void dcn10_apply_ctx_for_surface( struct dc *dc, const struct dc_stream_state *stream, int num_planes, struct dc_state *context) { + struct dce_hwseq *hws = dc->hwseq; int i; struct timing_generator *tg; - bool removed_pipe[4] = { false }; + uint32_t underflow_check_delay_us; + bool interdependent_update = false; struct pipe_ctx *top_pipe_to_program = - find_top_pipe_for_stream(dc, context, stream); + dcn10_find_top_pipe_for_stream(dc, context, stream); DC_LOGGER_INIT(dc->ctx->logger); + + // Clear pipe_ctx flag + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + pipe_ctx->update_flags.raw = 0; + } if (!top_pipe_to_program) return; tg = top_pipe_to_program->stream_res.tg; - dcn10_pipe_control_lock(dc, top_pipe_to_program, true); + interdependent_update = top_pipe_to_program->plane_state && + top_pipe_to_program->plane_state->update_flags.bits.full_update; + + underflow_check_delay_us = dc->debug.underflow_assert_delay_us; + + if (underflow_check_delay_us != 0xFFFFFFFF && hws->funcs.did_underflow_occur) + ASSERT(hws->funcs.did_underflow_occur(dc, top_pipe_to_program)); + + if (underflow_check_delay_us != 0xFFFFFFFF) + udelay(underflow_check_delay_us); + + if (underflow_check_delay_us != 0xFFFFFFFF && hws->funcs.did_underflow_occur) + ASSERT(hws->funcs.did_underflow_occur(dc, top_pipe_to_program)); if (num_planes == 0) { /* OTG blank before remove all front end */ - dc->hwss.blank_pixel_data(dc, top_pipe_to_program, true); + hws->funcs.blank_pixel_data(dc, top_pipe_to_program, true); } /* Disconnect unused mpcc */ @@ -2318,32 +2973,14 @@ struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; struct pipe_ctx *old_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; - /* - * Powergate reused pipes that are not powergated - * fairly hacky right now, using opp_id as indicator - * TODO: After move dc_post to dc_update, this will - * be removed. - */ - if (pipe_ctx->plane_state && !old_pipe_ctx->plane_state) { - if (old_pipe_ctx->stream_res.tg == tg && - old_pipe_ctx->plane_res.hubp && - old_pipe_ctx->plane_res.hubp->opp_id != 0xf) { - dcn10_disable_plane(dc, old_pipe_ctx); - /* - * power down fe will unlock when calling reset, need - * to lock it back here. Messy, need rework. - */ - pipe_ctx->stream_res.tg->funcs->lock(pipe_ctx->stream_res.tg); - } - } if ((!pipe_ctx->plane_state || pipe_ctx->stream_res.tg != old_pipe_ctx->stream_res.tg) && old_pipe_ctx->plane_state && old_pipe_ctx->stream_res.tg == tg) { - dc->hwss.plane_atomic_disconnect(dc, old_pipe_ctx); - removed_pipe[i] = true; + hws->funcs.plane_atomic_disconnect(dc, old_pipe_ctx); + pipe_ctx->update_flags.bits.disable = 1; DC_LOG_DC("Reset mpcc for pipe %d\n", old_pipe_ctx->pipe_idx); @@ -2351,68 +2988,175 @@ } if (num_planes > 0) - program_all_pipe_in_tree(dc, top_pipe_to_program, context); + dcn10_program_all_pipe_in_tree(dc, top_pipe_to_program, context); - dcn10_pipe_control_lock(dc, top_pipe_to_program, false); + /* Program secondary blending tree and writeback pipes */ + if ((stream->num_wb_info > 0) && (hws->funcs.program_all_writeback_pipes_in_tree)) + hws->funcs.program_all_writeback_pipes_in_tree(dc, stream, context); + if (interdependent_update) + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + /* Skip inactive pipes and ones already updated */ + if (!pipe_ctx->stream || pipe_ctx->stream == stream || + !pipe_ctx->plane_state || !tg->funcs->is_tg_enabled(tg)) + continue; - if (num_planes == 0) - false_optc_underflow_wa(dc, stream, tg); + pipe_ctx->plane_res.hubp->funcs->hubp_setup_interdependent( + pipe_ctx->plane_res.hubp, + &pipe_ctx->dlg_regs, + &pipe_ctx->ttu_regs); + } +} + +void dcn10_post_unlock_program_front_end( + struct dc *dc, + struct dc_state *context) +{ + int i; + + DC_LOGGER_INIT(dc->ctx->logger); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; + + if (!pipe_ctx->top_pipe && + !pipe_ctx->prev_odm_pipe && + pipe_ctx->stream) { + struct timing_generator *tg = pipe_ctx->stream_res.tg; + + if (context->stream_status[i].plane_count == 0) + false_optc_underflow_wa(dc, pipe_ctx->stream, tg); + } + } for (i = 0; i < dc->res_pool->pipe_count; i++) - if (removed_pipe[i]) - dcn10_disable_plane(dc, &dc->current_state->res_ctx.pipe_ctx[i]); + if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable) + dc->hwss.disable_plane(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.optimize_bandwidth(dc, context); + break; + } if (dc->hwseq->wa.DEGVIDCN10_254) hubbub1_wm_change_req_wa(dc->res_pool->hubbub); } -static void dcn10_set_bandwidth( - struct dc *dc, - struct dc_state *context, - bool safe_to_lower) +static void dcn10_stereo_hw_frame_pack_wa(struct dc *dc, struct dc_state *context) { + uint8_t i; + + for (i = 0; i < context->stream_count; i++) { + if (context->streams[i]->timing.timing_3d_format + == TIMING_3D_FORMAT_HW_FRAME_PACKING) { + /* + * Disable stutter + */ + hubbub1_allow_self_refresh_control(dc->res_pool->hubbub, false); + break; + } + } +} + +void dcn10_prepare_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubbub *hubbub = dc->res_pool->hubbub; + if (dc->debug.sanity_checks) - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { if (context->stream_count == 0) - context->bw.dcn.clk.phyclk_khz = 0; + context->bw_ctx.bw.dcn.clk.phyclk_khz = 0; - dc->res_pool->dccg->funcs->update_clocks( - dc->res_pool->dccg, - &context->bw.dcn.clk, - safe_to_lower); - - dcn10_pplib_apply_display_requirements(dc, context); + dc->clk_mgr->funcs->update_clocks( + dc->clk_mgr, + context, + false); } - hubbub1_program_watermarks(dc->res_pool->hubbub, - &context->bw.dcn.watermarks, - dc->res_pool->ref_clock_inKhz / 1000, + 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, true); + dcn10_stereo_hw_frame_pack_wa(dc, context); + + if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) + dcn_bw_notify_pplib_of_wm_ranges(dc); if (dc->debug.sanity_checks) - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); } -static void set_drr(struct pipe_ctx **pipe_ctx, - int num_pipes, int vmin, int vmax) +void dcn10_optimize_bandwidth( + struct dc *dc, + struct dc_state *context) +{ + struct dce_hwseq *hws = dc->hwseq; + struct hubbub *hubbub = dc->res_pool->hubbub; + + if (dc->debug.sanity_checks) + hws->funcs.verify_allow_pstate_change_high(dc); + + if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { + if (context->stream_count == 0) + context->bw_ctx.bw.dcn.clk.phyclk_khz = 0; + + dc->clk_mgr->funcs->update_clocks( + dc->clk_mgr, + context, + true); + } + + hubbub->funcs->program_watermarks(hubbub, + &context->bw_ctx.bw.dcn.watermarks, + dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, + true); + + dcn10_stereo_hw_frame_pack_wa(dc, context); + + if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) + dcn_bw_notify_pplib_of_wm_ranges(dc); + + if (dc->debug.sanity_checks) + hws->funcs.verify_allow_pstate_change_high(dc); +} + +void dcn10_set_drr(struct pipe_ctx **pipe_ctx, + int num_pipes, unsigned int vmin, unsigned int vmax, + unsigned int vmid, unsigned int vmid_frame_number) { int i = 0; struct drr_params params = {0}; + // DRR set trigger event mapped to OTG_TRIG_A (bit 11) for manual control flow + unsigned int event_triggers = 0x800; + // Note DRR trigger events are generated regardless of whether num frames met. + unsigned int num_frames = 2; params.vertical_total_max = vmax; params.vertical_total_min = vmin; + params.vertical_total_mid = vmid; + params.vertical_total_mid_frame_num = vmid_frame_number; /* TODO: If multiple pipes are to be supported, you need - * some GSL stuff + * some GSL stuff. Static screen triggers may be programmed differently + * as well. */ for (i = 0; i < num_pipes; i++) { - pipe_ctx[i]->stream_res.tg->funcs->set_drr(pipe_ctx[i]->stream_res.tg, ¶ms); + pipe_ctx[i]->stream_res.tg->funcs->set_drr( + pipe_ctx[i]->stream_res.tg, ¶ms); + if (vmax != 0 && vmin != 0) + pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control( + pipe_ctx[i]->stream_res.tg, + event_triggers, num_frames); } } -static void get_position(struct pipe_ctx **pipe_ctx, +void dcn10_get_position(struct pipe_ctx **pipe_ctx, int num_pipes, struct crtc_position *position) { @@ -2424,22 +3168,23 @@ pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position); } -static void set_static_screen_control(struct pipe_ctx **pipe_ctx, - int num_pipes, const struct dc_static_screen_events *events) +void dcn10_set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_params *params) { unsigned int i; - unsigned int value = 0; + unsigned int triggers = 0; - if (events->surface_update) - value |= 0x80; - if (events->cursor_update) - value |= 0x2; - if (events->force_trigger) - value |= 0x1; + if (params->triggers.surface_update) + triggers |= 0x80; + if (params->triggers.cursor_update) + triggers |= 0x2; + if (params->triggers.force_trigger) + triggers |= 0x1; for (i = 0; i < num_pipes; i++) pipe_ctx[i]->stream_res.tg->funcs-> - set_static_screen_control(pipe_ctx[i]->stream_res.tg, value); + set_static_screen_control(pipe_ctx[i]->stream_res.tg, + triggers, params->num_frames); } static void dcn10_config_stereo_parameters( @@ -2464,7 +3209,7 @@ timing_3d_format == TIMING_3D_FORMAT_DP_HDMI_INBAND_FA || timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) { enum display_dongle_type dongle = \ - stream->sink->link->ddc->dongle_type; + stream->link->ddc->dongle_type; if (dongle == DISPLAY_DONGLE_DP_VGA_CONVERTER || dongle == DISPLAY_DONGLE_DP_DVI_CONVERTER || dongle == DISPLAY_DONGLE_DP_HDMI_CONVERTER) @@ -2479,12 +3224,19 @@ return; } -static void dcn10_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) +void dcn10_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) { struct crtc_stereo_flags flags = { 0 }; struct dc_stream_state *stream = pipe_ctx->stream; dcn10_config_stereo_parameters(stream, &flags); + + if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) { + if (!dc_set_generic_gpio_for_stereo(true, dc->ctx->gpio_service)) + dc_set_generic_gpio_for_stereo(false, dc->ctx->gpio_service); + } else { + dc_set_generic_gpio_for_stereo(false, dc->ctx->gpio_service); + } pipe_ctx->stream_res.opp->funcs->opp_program_stereo( pipe_ctx->stream_res.opp, @@ -2511,15 +3263,16 @@ return NULL; } -static void dcn10_wait_for_mpcc_disconnect( +void dcn10_wait_for_mpcc_disconnect( struct dc *dc, struct resource_pool *res_pool, struct pipe_ctx *pipe_ctx) { + struct dce_hwseq *hws = dc->hwseq; int mpcc_inst; if (dc->debug.sanity_checks) { - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); } if (!pipe_ctx->stream_res.opp) @@ -2529,22 +3282,21 @@ if (pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst]) { struct hubp *hubp = get_hubp_by_inst(res_pool, mpcc_inst); - res_pool->mpc->funcs->wait_for_idle(res_pool->mpc, mpcc_inst); + if (pipe_ctx->stream_res.tg && + pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) + res_pool->mpc->funcs->wait_for_idle(res_pool->mpc, mpcc_inst); pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst] = false; hubp->funcs->set_blank(hubp, true); - /*DC_LOG_ERROR(dc->ctx->logger, - "[debug_mpo: wait_for_mpcc finished waiting on mpcc %d]\n", - i);*/ } } if (dc->debug.sanity_checks) { - dcn10_verify_allow_pstate_change_high(dc); + hws->funcs.verify_allow_pstate_change_high(dc); } } -static bool dcn10_dummy_display_power_gating( +bool dcn10_dummy_display_power_gating( struct dc *dc, uint8_t controller_id, struct dc_bios *dcb, @@ -2553,11 +3305,12 @@ return true; } -static void dcn10_update_pending_status(struct pipe_ctx *pipe_ctx) +void dcn10_update_pending_status(struct pipe_ctx *pipe_ctx) { struct dc_plane_state *plane_state = pipe_ctx->plane_state; struct timing_generator *tg = pipe_ctx->stream_res.tg; bool flip_pending; + struct dc *dc = plane_state->ctx->dc; if (plane_state == NULL) return; @@ -2565,7 +3318,7 @@ flip_pending = pipe_ctx->plane_res.hubp->funcs->hubp_is_flip_pending( pipe_ctx->plane_res.hubp); - plane_state->status.is_flip_pending = flip_pending; + plane_state->status.is_flip_pending = plane_state->status.is_flip_pending || flip_pending; if (!flip_pending) plane_state->status.current_address = plane_state->status.requested_address; @@ -2575,57 +3328,210 @@ plane_state->status.is_right_eye = !tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg); } -} -static void dcn10_update_dchub(struct dce_hwseq *hws, struct dchub_init_data *dh_data) -{ - if (hws->ctx->dc->res_pool->hubbub != NULL) { - struct hubp *hubp = hws->ctx->dc->res_pool->hubps[0]; + if (dc->hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied) { + struct dce_hwseq *hwseq = dc->hwseq; + struct timing_generator *tg = dc->res_pool->timing_generators[0]; + unsigned int cur_frame = tg->funcs->get_frame_count(tg); - if (hubp->funcs->hubp_update_dchub) - hubp->funcs->hubp_update_dchub(hubp, dh_data); - else - hubbub1_update_dchub(hws->ctx->dc->res_pool->hubbub, dh_data); + if (cur_frame != hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied_on_frame) { + struct hubbub *hubbub = dc->res_pool->hubbub; + + hubbub->funcs->allow_self_refresh_control(hubbub, !dc->debug.disable_stutter); + hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied = false; + } } } -static void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) +void dcn10_update_dchub(struct dce_hwseq *hws, struct dchub_init_data *dh_data) +{ + struct hubbub *hubbub = hws->ctx->dc->res_pool->hubbub; + + /* In DCN, this programming sequence is owned by the hubbub */ + hubbub->funcs->update_dchub(hubbub, dh_data); +} + +static bool dcn10_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx) +{ + struct pipe_ctx *test_pipe; + const struct rect *r1 = &pipe_ctx->plane_res.scl_data.recout, *r2; + int r1_r = r1->x + r1->width, r1_b = r1->y + r1->height, r2_r, r2_b; + + /** + * Disable the cursor if there's another pipe above this with a + * plane that contains this pipe's viewport to prevent double cursor + * and incorrect scaling artifacts. + */ + for (test_pipe = pipe_ctx->top_pipe; test_pipe; + test_pipe = test_pipe->top_pipe) { + if (!test_pipe->plane_state->visible) + continue; + + r2 = &test_pipe->plane_res.scl_data.recout; + r2_r = r2->x + r2->width; + r2_b = r2->y + r2->height; + + if (r1->x >= r2->x && r1->y >= r2->y && r1_r <= r2_r && r1_b <= r2_b) + return true; + } + + return false; +} + +void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) { struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position; struct hubp *hubp = pipe_ctx->plane_res.hubp; struct dpp *dpp = pipe_ctx->plane_res.dpp; struct dc_cursor_mi_param param = { - .pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_khz, - .ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clock_inKhz, + .pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10, + .ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz, .viewport = pipe_ctx->plane_res.scl_data.viewport, .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz, .v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert, .rotation = pipe_ctx->plane_state->rotation, .mirror = pipe_ctx->plane_state->horizontal_mirror }; + bool pipe_split_on = (pipe_ctx->top_pipe != NULL) || + (pipe_ctx->bottom_pipe != NULL); + + int x_plane = pipe_ctx->plane_state->dst_rect.x; + int y_plane = pipe_ctx->plane_state->dst_rect.y; + int x_pos = pos_cpy.x; + int y_pos = pos_cpy.y; + + /** + * DC cursor is stream space, HW cursor is plane space and drawn + * as part of the framebuffer. + * + * Cursor position can't be negative, but hotspot can be used to + * shift cursor out of the plane bounds. Hotspot must be smaller + * than the cursor size. + */ + + /** + * Translate cursor from stream space to plane space. + * + * If the cursor is scaled then we need to scale the position + * to be in the approximately correct place. We can't do anything + * about the actual size being incorrect, that's a limitation of + * the hardware. + */ + x_pos = (x_pos - x_plane) * pipe_ctx->plane_state->src_rect.width / + pipe_ctx->plane_state->dst_rect.width; + y_pos = (y_pos - y_plane) * pipe_ctx->plane_state->src_rect.height / + pipe_ctx->plane_state->dst_rect.height; + + /** + * If the cursor's source viewport is clipped then we need to + * translate the cursor to appear in the correct position on + * the screen. + * + * This translation isn't affected by scaling so it needs to be + * done *after* we adjust the position for the scale factor. + * + * This is only done by opt-in for now since there are still + * some usecases like tiled display that might enable the + * cursor on both streams while expecting dc to clip it. + */ + if (pos_cpy.translate_by_source) { + x_pos += pipe_ctx->plane_state->src_rect.x; + y_pos += pipe_ctx->plane_state->src_rect.y; + } + + /** + * If the position is negative then we need to add to the hotspot + * to shift the cursor outside the plane. + */ + + if (x_pos < 0) { + pos_cpy.x_hotspot -= x_pos; + x_pos = 0; + } + + if (y_pos < 0) { + pos_cpy.y_hotspot -= y_pos; + y_pos = 0; + } + + pos_cpy.x = (uint32_t)x_pos; + pos_cpy.y = (uint32_t)y_pos; if (pipe_ctx->plane_state->address.type == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE) pos_cpy.enable = false; - if (pipe_ctx->top_pipe && pipe_ctx->plane_state != pipe_ctx->top_pipe->plane_state) + if (pos_cpy.enable && dcn10_can_pipe_disable_cursor(pipe_ctx)) pos_cpy.enable = false; + // Swap axis and mirror horizontally + if (param.rotation == ROTATION_ANGLE_90) { + uint32_t temp_x = pos_cpy.x; + + pos_cpy.x = pipe_ctx->plane_res.scl_data.viewport.width - + (pos_cpy.y - pipe_ctx->plane_res.scl_data.viewport.x) + pipe_ctx->plane_res.scl_data.viewport.x; + pos_cpy.y = temp_x; + } + // Swap axis and mirror vertically + else if (param.rotation == ROTATION_ANGLE_270) { + uint32_t temp_y = pos_cpy.y; + int viewport_height = + pipe_ctx->plane_res.scl_data.viewport.height; + + if (pipe_split_on) { + if (pos_cpy.x > viewport_height) { + pos_cpy.x = pos_cpy.x - viewport_height; + pos_cpy.y = viewport_height - pos_cpy.x; + } else { + pos_cpy.y = 2 * viewport_height - pos_cpy.x; + } + } else + pos_cpy.y = viewport_height - pos_cpy.x; + pos_cpy.x = temp_y; + } + // Mirror horizontally and vertically + else if (param.rotation == ROTATION_ANGLE_180) { + int viewport_width = + pipe_ctx->plane_res.scl_data.viewport.width; + int viewport_x = + pipe_ctx->plane_res.scl_data.viewport.x; + + if (pipe_split_on) { + if (pos_cpy.x >= viewport_width + viewport_x) { + pos_cpy.x = 2 * viewport_width + - pos_cpy.x + 2 * viewport_x; + } else { + uint32_t temp_x = pos_cpy.x; + + pos_cpy.x = 2 * viewport_x - pos_cpy.x; + if (temp_x >= viewport_x + + (int)hubp->curs_attr.width || pos_cpy.x + <= (int)hubp->curs_attr.width + + pipe_ctx->plane_state->src_rect.x) { + pos_cpy.x = temp_x + viewport_width; + } + } + } else { + pos_cpy.x = viewport_width - pos_cpy.x + 2 * viewport_x; + } + pos_cpy.y = pipe_ctx->plane_res.scl_data.viewport.height - pos_cpy.y; + } + hubp->funcs->set_cursor_position(hubp, &pos_cpy, ¶m); - dpp->funcs->set_cursor_position(dpp, &pos_cpy, ¶m, hubp->curs_attr.width); + dpp->funcs->set_cursor_position(dpp, &pos_cpy, ¶m, hubp->curs_attr.width, hubp->curs_attr.height); } -static void dcn10_set_cursor_attribute(struct pipe_ctx *pipe_ctx) +void dcn10_set_cursor_attribute(struct pipe_ctx *pipe_ctx) { struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes; pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes( pipe_ctx->plane_res.hubp, attributes); pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes( - pipe_ctx->plane_res.dpp, attributes->color_format); + pipe_ctx->plane_res.dpp, attributes); } -static void dcn10_set_cursor_sdr_white_level(struct pipe_ctx *pipe_ctx) +void dcn10_set_cursor_sdr_white_level(struct pipe_ctx *pipe_ctx) { uint32_t sdr_white_level = pipe_ctx->stream->cursor_attributes.sdr_white_level; struct fixed31_32 multiplier; @@ -2652,60 +3558,216 @@ pipe_ctx->plane_res.dpp, &opt_attr); } -static const struct hw_sequencer_funcs dcn10_funcs = { - .program_gamut_remap = program_gamut_remap, - .program_csc_matrix = program_csc_matrix, - .init_hw = dcn10_init_hw, - .apply_ctx_to_hw = dce110_apply_ctx_to_hw, - .apply_ctx_for_surface = dcn10_apply_ctx_for_surface, - .update_plane_addr = dcn10_update_plane_addr, - .plane_atomic_disconnect = hwss1_plane_atomic_disconnect, - .update_dchub = dcn10_update_dchub, - .update_mpcc = dcn10_update_mpcc, - .update_pending_status = dcn10_update_pending_status, - .set_input_transfer_func = dcn10_set_input_transfer_func, - .set_output_transfer_func = dcn10_set_output_transfer_func, - .program_output_csc = dcn10_program_output_csc, - .power_down = dce110_power_down, - .enable_accelerated_mode = dce110_enable_accelerated_mode, - .enable_timing_synchronization = dcn10_enable_timing_synchronization, - .enable_per_frame_crtc_position_reset = dcn10_enable_per_frame_crtc_position_reset, - .update_info_frame = dce110_update_info_frame, - .enable_stream = dce110_enable_stream, - .disable_stream = dce110_disable_stream, - .unblank_stream = dce110_unblank_stream, - .blank_stream = dce110_blank_stream, - .enable_audio_stream = dce110_enable_audio_stream, - .disable_audio_stream = dce110_disable_audio_stream, - .enable_display_power_gating = dcn10_dummy_display_power_gating, - .disable_plane = dcn10_disable_plane, - .blank_pixel_data = dcn10_blank_pixel_data, - .pipe_control_lock = dcn10_pipe_control_lock, - .set_bandwidth = dcn10_set_bandwidth, - .reset_hw_ctx_wrap = reset_hw_ctx_wrap, - .enable_stream_timing = dcn10_enable_stream_timing, - .set_drr = set_drr, - .get_position = get_position, - .set_static_screen_control = set_static_screen_control, - .setup_stereo = dcn10_setup_stereo, - .set_avmute = dce110_set_avmute, - .log_hw_state = dcn10_log_hw_state, - .wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect, - .ready_shared_resources = ready_shared_resources, - .optimize_shared_resources = optimize_shared_resources, - .pplib_apply_display_requirements = - dcn10_pplib_apply_display_requirements, - .edp_backlight_control = hwss_edp_backlight_control, - .edp_power_control = hwss_edp_power_control, - .edp_wait_for_hpd_ready = hwss_edp_wait_for_hpd_ready, - .set_cursor_position = dcn10_set_cursor_position, - .set_cursor_attribute = dcn10_set_cursor_attribute, - .set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level -}; - - -void dcn10_hw_sequencer_construct(struct dc *dc) +/* + * apply_front_porch_workaround TODO FPGA still need? + * + * This is a workaround for a bug that has existed since R5xx and has not been + * fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. + */ +static void apply_front_porch_workaround( + struct dc_crtc_timing *timing) { - dc->hwss = dcn10_funcs; + if (timing->flags.INTERLACE == 1) { + if (timing->v_front_porch < 2) + timing->v_front_porch = 2; + } else { + if (timing->v_front_porch < 1) + timing->v_front_porch = 1; + } } +int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx) +{ + const struct dc_crtc_timing *dc_crtc_timing = &pipe_ctx->stream->timing; + struct dc_crtc_timing patched_crtc_timing; + int vesa_sync_start; + int asic_blank_end; + int interlace_factor; + int vertical_line_start; + + patched_crtc_timing = *dc_crtc_timing; + apply_front_porch_workaround(&patched_crtc_timing); + + interlace_factor = patched_crtc_timing.flags.INTERLACE ? 2 : 1; + + vesa_sync_start = patched_crtc_timing.v_addressable + + patched_crtc_timing.v_border_bottom + + patched_crtc_timing.v_front_porch; + + asic_blank_end = (patched_crtc_timing.v_total - + vesa_sync_start - + patched_crtc_timing.v_border_top) + * interlace_factor; + + vertical_line_start = asic_blank_end - + pipe_ctx->pipe_dlg_param.vstartup_start + 1; + + return vertical_line_start; +} + +void dcn10_calc_vupdate_position( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + uint32_t *start_line, + uint32_t *end_line) +{ + const struct dc_crtc_timing *dc_crtc_timing = &pipe_ctx->stream->timing; + int vline_int_offset_from_vupdate = + pipe_ctx->stream->periodic_interrupt.lines_offset; + int vupdate_offset_from_vsync = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx); + int start_position; + + if (vline_int_offset_from_vupdate > 0) + vline_int_offset_from_vupdate--; + else if (vline_int_offset_from_vupdate < 0) + vline_int_offset_from_vupdate++; + + start_position = vline_int_offset_from_vupdate + vupdate_offset_from_vsync; + + if (start_position >= 0) + *start_line = start_position; + else + *start_line = dc_crtc_timing->v_total + start_position - 1; + + *end_line = *start_line + 2; + + if (*end_line >= dc_crtc_timing->v_total) + *end_line = 2; +} + +static void dcn10_cal_vline_position( + struct dc *dc, + struct pipe_ctx *pipe_ctx, + uint32_t *start_line, + uint32_t *end_line) +{ + switch (pipe_ctx->stream->periodic_interrupt.ref_point) { + case START_V_UPDATE: + dcn10_calc_vupdate_position( + dc, + pipe_ctx, + start_line, + end_line); + break; + case START_V_SYNC: + // vsync is line 0 so start_line is just the requested line offset + *start_line = pipe_ctx->stream->periodic_interrupt.lines_offset; + *end_line = *start_line + 2; + break; + default: + ASSERT(0); + break; + } +} + +void dcn10_setup_periodic_interrupt( + struct dc *dc, + struct pipe_ctx *pipe_ctx) +{ + struct timing_generator *tg = pipe_ctx->stream_res.tg; + uint32_t start_line = 0; + uint32_t end_line = 0; + + dcn10_cal_vline_position(dc, pipe_ctx, &start_line, &end_line); + + tg->funcs->setup_vertical_interrupt0(tg, start_line, end_line); +} + +void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + struct timing_generator *tg = pipe_ctx->stream_res.tg; + int start_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx); + + if (start_line < 0) { + ASSERT(0); + start_line = 0; + } + + if (tg->funcs->setup_vertical_interrupt2) + tg->funcs->setup_vertical_interrupt2(tg, start_line); +} + +void dcn10_unblank_stream(struct pipe_ctx *pipe_ctx, + struct dc_link_settings *link_settings) +{ + struct encoder_unblank_param params = { { 0 } }; + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct dce_hwseq *hws = link->dc->hwseq; + + /* only 3 items below are used by unblank */ + params.timing = pipe_ctx->stream->timing; + + params.link_settings.link_rate = link_settings->link_rate; + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) { + if (params.timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + params.timing.pix_clk_100hz /= 2; + pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(pipe_ctx->stream_res.stream_enc, ¶ms); + } + + if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { + hws->funcs.edp_backlight_control(link, true); + } +} + +void dcn10_send_immediate_sdp_message(struct pipe_ctx *pipe_ctx, + const uint8_t *custom_sdp_message, + unsigned int sdp_message_size) +{ + if (dc_is_dp_signal(pipe_ctx->stream->signal)) { + pipe_ctx->stream_res.stream_enc->funcs->send_immediate_sdp_message( + pipe_ctx->stream_res.stream_enc, + custom_sdp_message, + sdp_message_size); + } +} +enum dc_status dcn10_set_clock(struct dc *dc, + enum dc_clock_type clock_type, + uint32_t clk_khz, + uint32_t stepping) +{ + struct dc_state *context = dc->current_state; + struct dc_clock_config clock_cfg = {0}; + struct dc_clocks *current_clocks = &context->bw_ctx.bw.dcn.clk; + + if (!dc->clk_mgr || !dc->clk_mgr->funcs->get_clock) + return DC_FAIL_UNSUPPORTED_1; + + dc->clk_mgr->funcs->get_clock(dc->clk_mgr, + context, clock_type, &clock_cfg); + + if (clk_khz > clock_cfg.max_clock_khz) + return DC_FAIL_CLK_EXCEED_MAX; + + if (clk_khz < clock_cfg.min_clock_khz) + return DC_FAIL_CLK_BELOW_MIN; + + if (clk_khz < clock_cfg.bw_requirequired_clock_khz) + return DC_FAIL_CLK_BELOW_CFG_REQUIRED; + + /*update internal request clock for update clock use*/ + if (clock_type == DC_CLOCK_TYPE_DISPCLK) + current_clocks->dispclk_khz = clk_khz; + else if (clock_type == DC_CLOCK_TYPE_DPPCLK) + current_clocks->dppclk_khz = clk_khz; + else + return DC_ERROR_UNEXPECTED; + + if (dc->clk_mgr->funcs->update_clocks) + dc->clk_mgr->funcs->update_clocks(dc->clk_mgr, + context, true); + return DC_OK; + +} + +void dcn10_get_clock(struct dc *dc, + enum dc_clock_type clock_type, + struct dc_clock_config *clock_cfg) +{ + struct dc_state *context = dc->current_state; + + if (dc->clk_mgr && dc->clk_mgr->funcs->get_clock) + dc->clk_mgr->funcs->get_clock(dc->clk_mgr, context, clock_type, clock_cfg); + +} -- Gitblit v1.6.2