diff options
author | Tony Cheng <tony.cheng@amd.com> | 2017-07-14 21:07:16 +0300 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-09-27 01:15:24 +0300 |
commit | 2b13d7d380d50811fd4fc022d135c3c5bb70a418 (patch) | |
tree | 7403a3b3479f8c6e68abba231c98b9ab23b269e0 /drivers | |
parent | 6d244be884d20b1e9c912291a242dccedd6d9040 (diff) | |
download | linux-2b13d7d380d50811fd4fc022d135c3c5bb70a418.tar.xz |
drm/amd/display: mpo debug sanity checks
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Reviewed-by: Eric Yang <eric.yang2@amd.com>
Acked-by: Harry Wentland <Harry.Wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dc.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c | 152 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c | 1 |
5 files changed, 179 insertions, 2 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 7b8e11c28c62..617270829936 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -160,6 +160,7 @@ struct dc_config { struct dc_debug { bool surface_visual_confirm; + bool sanity_checks; bool max_disp_clk; bool surface_trace; bool timing_trace; diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h index 94d12b5fb7c6..0479554bc231 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_hwseq.h @@ -147,6 +147,9 @@ SRII(OPP_PIPE_CONTROL, OPP_PIPE, 3), \ SR(REFCLK_CNTL), \ SR(DCHUBBUB_GLOBAL_TIMER_CNTL), \ + SR(DCHUBBUB_ARB_DRAM_STATE_CNTL), \ + SR(DCHUBBUB_TEST_DEBUG_INDEX), \ + SR(DCHUBBUB_TEST_DEBUG_DATA), \ SR(DC_IP_REQUEST_CNTL), \ SR(DOMAIN0_PG_CONFIG), \ SR(DOMAIN1_PG_CONFIG), \ @@ -195,6 +198,9 @@ struct dce_hwseq_registers { uint32_t OPP_PIPE_CONTROL[4]; uint32_t REFCLK_CNTL; uint32_t DCHUBBUB_GLOBAL_TIMER_CNTL; + uint32_t DCHUBBUB_ARB_DRAM_STATE_CNTL; + uint32_t DCHUBBUB_TEST_DEBUG_INDEX; + uint32_t DCHUBBUB_TEST_DEBUG_DATA; uint32_t DC_IP_REQUEST_CNTL; uint32_t DOMAIN0_PG_CONFIG; uint32_t DOMAIN1_PG_CONFIG; diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index c0aa32b71ad1..b5a925362c8c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -52,6 +52,75 @@ #define FN(reg_name, field_name) \ hws->shifts->field_name, hws->masks->field_name + +static void verify_allow_pstate_change_high( + struct dce_hwseq *hws) +{ + /* pstate latency is ~20us so if we wait over 40us and pstate allow + * still not asserted, we are probably stuck and going to hang + */ + static unsigned int pstate_wait_timeout_us = 40; + static unsigned int max_sampled_pstate_wait_us; /* data collection */ + static bool forced_pstate_allow; /* help with revert wa */ + + unsigned int debug_index = 0x7; + unsigned int debug_data; + unsigned int force_allow_pstate = 0x30; + unsigned int i; + + if (forced_pstate_allow) { + /* we hacked to force pstate allow to prevent hang last time + * we verify_allow_pstate_change_high. so disable force + * here so we can check status + */ + REG_WRITE(DCHUBBUB_ARB_DRAM_STATE_CNTL, 0); + forced_pstate_allow = false; + } + + /* description "3-0: Pipe0 cursor0 QOS + * 7-4: Pipe1 cursor0 QOS + * 11-8: Pipe2 cursor0 QOS + * 15-12: Pipe3 cursor0 QOS + * 16: Pipe0 Plane0 Allow Pstate Change + * 17: Pipe1 Plane0 Allow Pstate Change + * 18: Pipe2 Plane0 Allow Pstate Change + * 19: Pipe3 Plane0 Allow Pstate Change + * 20: Pipe0 Plane1 Allow Pstate Change + * 21: Pipe1 Plane1 Allow Pstate Change + * 22: Pipe2 Plane1 Allow Pstate Change + * 23: Pipe3 Plane1 Allow Pstate Change + * 24: Pipe0 cursor0 Allow Pstate Change + * 25: Pipe1 cursor0 Allow Pstate Change + * 26: Pipe2 cursor0 Allow Pstate Change + * 27: Pipe3 cursor0 Allow Pstate Change + * 28: WB0 Allow Pstate Change + * 29: WB1 Allow Pstate Change + * 30: Arbiter's allow_pstate_change + * 31: SOC pstate change request + */ + + REG_WRITE(DCHUBBUB_TEST_DEBUG_INDEX, debug_index); + + for (i = 0; i < pstate_wait_timeout_us; i++) { + debug_data = REG_READ(DCHUBBUB_TEST_DEBUG_DATA); + + if (debug_data & (1 << 30)) + return; + + if (max_sampled_pstate_wait_us < i) + max_sampled_pstate_wait_us = i; + + udelay(1); + } + + /* force pstate allow to prevent system hang + * and break to debugger to investigate + */ + REG_WRITE(DCHUBBUB_ARB_DRAM_STATE_CNTL, force_allow_pstate); + forced_pstate_allow = true; + BREAK_TO_DEBUGGER(); +} + static void enable_dppclk( struct dce_hwseq *hws, uint8_t plane_id, @@ -477,12 +546,19 @@ static void reset_front_end( REG_UPDATE(OTG_GLOBAL_SYNC_STATUS[tg->inst], VUPDATE_NO_LOCK_EVENT_CLEAR, 1); tg->funcs->unlock(tg); + + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + REG_WAIT(OTG_GLOBAL_SYNC_STATUS[tg->inst], VUPDATE_NO_LOCK_EVENT_OCCURRED, 1, 20000, 200000); mpcc->funcs->wait_for_idle(mpcc); mi->funcs->set_blank(mi, true); + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + REG_UPDATE(HUBP_CLK_CNTL[fe_idx], HUBP_CLOCK_ENABLE, 0); REG_UPDATE(DPP_CONTROL[fe_idx], @@ -495,6 +571,9 @@ static void reset_front_end( dm_logger_write(dc->ctx->logger, LOG_DC, "Reset front end %d\n", fe_idx); + + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); } static void dcn10_power_down_fe(struct core_dc *dc, int fe_idx) @@ -511,6 +590,9 @@ static void dcn10_power_down_fe(struct core_dc *dc, int fe_idx) IP_REQUEST_EN, 0); dm_logger_write(dc->ctx->logger, LOG_DC, "Power gated front end %d\n", fe_idx); + + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); } static void reset_hw_ctx_wrap( @@ -995,10 +1077,16 @@ static void dcn10_pipe_control_lock( if (pipe->top_pipe) return; + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + if (lock) pipe->tg->funcs->lock(pipe->tg); else pipe->tg->funcs->unlock(pipe->tg); + + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); } static bool wait_for_reset_trigger_to_occur( @@ -1516,6 +1604,12 @@ static void program_all_pipe_in_tree( /* watermark is for all pipes */ pipe_ctx->mi->funcs->program_watermarks( pipe_ctx->mi, &context->bw.dcn.watermarks, ref_clk_mhz); + + if (dc->public.debug.sanity_checks) { + /* pstate stuck check after watermark update */ + verify_allow_pstate_change_high(dc->hwseq); + } + pipe_ctx->tg->funcs->lock(pipe_ctx->tg); pipe_ctx->tg->dlg_otg_param.vready_offset = pipe_ctx->pipe_dlg_param.vready_offset; @@ -1534,6 +1628,11 @@ static void program_all_pipe_in_tree( update_dchubp_dpp(dc, pipe_ctx, context); } + if (dc->public.debug.sanity_checks) { + /* pstate stuck check after each pipe is programmed */ + verify_allow_pstate_change_high(dc->hwseq); + } + if (pipe_ctx->bottom_pipe != NULL) program_all_pipe_in_tree(dc, pipe_ctx->bottom_pipe, context); } @@ -1572,6 +1671,9 @@ static void dcn10_apply_ctx_for_surface( { int i; + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + /* reset unused mpcc */ for (i = 0; i < dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; @@ -1602,6 +1704,10 @@ static void dcn10_apply_ctx_for_surface( mpcc_cfg.bot_mpcc_id = 0xf; mpcc_cfg.top_of_tree = !old_pipe_ctx->top_pipe; old_pipe_ctx->mpcc->funcs->set(old_pipe_ctx->mpcc, &mpcc_cfg); + + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); + /* * the mpcc is the only thing that keeps track of the mpcc * mapping for reset front end right now. Might need some @@ -1679,6 +1785,9 @@ static void dcn10_apply_ctx_for_surface( context->bw.dcn.watermarks.d.cstate_pstate.pstate_change_ns, context->bw.dcn.watermarks.d.pte_meta_urgent_ns ); + + if (dc->public.debug.sanity_checks) + verify_allow_pstate_change_high(dc->hwseq); } static void dcn10_set_bandwidth( @@ -1738,6 +1847,8 @@ static void dcn10_set_bandwidth( context->bw.dcn.calc_clk.min_active_dram_ccm_us; } dcn10_pplib_apply_display_requirements(dc, context); + + /* need to fix this function. not doing the right thing here */ } static void set_drr(struct pipe_ctx **pipe_ctx, @@ -1866,7 +1977,44 @@ static bool dcn10_dummy_display_power_gating( struct core_dc *dc, uint8_t controller_id, struct dc_bios *dcb, - enum pipe_gating_control power_gating) {return true; } + enum pipe_gating_control power_gating) +{ + return true; +} + +void dcn10_update_pending_status(struct pipe_ctx *pipe_ctx) +{ + struct core_surface *surface = pipe_ctx->surface; + struct timing_generator *tg = pipe_ctx->tg; + + if (surface->ctx->dc->debug.sanity_checks) { + struct core_dc *dc = DC_TO_CORE(surface->ctx->dc); + + verify_allow_pstate_change_high(dc->hwseq); + } + + if (surface == NULL) + return; + + surface->status.is_flip_pending = + pipe_ctx->mi->funcs->mem_input_is_flip_pending( + pipe_ctx->mi); + + /* DCN we read INUSE address in MI, do we still need this wa? */ + if (surface->status.is_flip_pending && + !surface->public.visible) { + pipe_ctx->mi->current_address = + pipe_ctx->mi->request_address; + BREAK_TO_DEBUGGER(); + } + + surface->status.current_address = pipe_ctx->mi->current_address; + if (pipe_ctx->mi->current_address.type == PLN_ADDR_TYPE_GRPH_STEREO && + tg->funcs->is_stereo_left_eye) { + surface->status.is_right_eye = + !tg->funcs->is_stereo_left_eye(pipe_ctx->tg); + } +} static const struct hw_sequencer_funcs dcn10_funcs = { .program_gamut_remap = program_gamut_remap, @@ -1876,7 +2024,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = { .apply_ctx_for_surface = dcn10_apply_ctx_for_surface, .set_plane_config = set_plane_config, .update_plane_addr = update_plane_addr, - .update_pending_status = dce110_update_pending_status, + .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, .power_down = dce110_power_down, diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c index de3341d572be..9af288167e2e 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c @@ -77,6 +77,24 @@ static void reset_output_mux(struct dcn10_mpcc *mpcc10) mpcc10->base.opp_id = 0xf; } +static void assert_mpcc_idle_before_connect(struct dcn10_mpcc *mpcc10) +{ + unsigned int top_sel; + unsigned int mpcc_busy, mpcc_idle, mpcc_status; + + REG_GET(MPCC_TOP_SEL, + MPCC_TOP_SEL, &top_sel); + + if (top_sel == 0xf) { + mpcc_status = REG_GET_2(MPCC_STATUS, + MPCC_BUSY, &mpcc_busy, + MPCC_IDLE, &mpcc_idle); + + ASSERT(mpcc_busy == 0); + ASSERT(mpcc_idle == 1); + } +} + static void dcn10_mpcc_set(struct mpcc *mpcc, struct mpcc_cfg *cfg) { struct dcn10_mpcc *mpcc10 = TO_DCN10_MPCC(mpcc); @@ -87,6 +105,9 @@ static void dcn10_mpcc_set(struct mpcc *mpcc, struct mpcc_cfg *cfg) bool blend_active_only = cfg->top_of_tree && !mpcc->ctx->dc->debug.surface_visual_confirm; + if (mpcc->ctx->dc->debug.sanity_checks) + assert_mpcc_idle_before_connect(mpcc10); + REG_SET(MPCC_OPP_ID, 0, MPCC_OPP_ID, cfg->opp_id); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c index b5f0f547ee83..eec0d5b22a9f 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c @@ -424,6 +424,7 @@ static const struct resource_caps res_cap = { static const struct dc_debug debug_defaults_drv = { .disable_dcc = false, + .sanity_checks = true, .disable_dmcu = true, .force_abm_enable = false, .timing_trace = false, |