mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 17:59:32 +00:00
drm/amd/display: Defer cursor lock until after VUPDATE
[Why] We dropped the delay after changed the cursor functions locking the entire pipe to locking just the CURSOR registers to fix page flip stuttering - this introduced cursor stuttering instead, and an underflow issue. The cursor update can be delayed indefinitely if the cursor update repeatedly happens right around VUPDATE. The underflow issue can happen if we do a viewport update on a pipe on the same frame where a cursor update happens around VUPDATE - the old cursor registers are retained which can be in an invalid position. This can cause a pipe hang and indefinite underflow. [How] The complex, ideal solution to the problem would be a software triple buffering mechanism from the DM layer to program only one cursor update per frame just before VUPDATE. The simple workaround until we have that infrastructure in place is this change - bring back the delay until VUPDATE before locking, but with some corrections to the calculations. This didn't work for all timings before because the calculation for VUPDATE was wrong - it was using the offset from VSTARTUP instead and didn't correctly handle the case where VUPDATE could be in the back porch. Add a new hardware sequencer function to use the existing helper to calculate the real VUPDATE start and VUPDATE end - VUPDATE can last multiple lines after all. Change the udelay to incorporate the width of VUPDATE as well. Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> Reviewed-by: Aric Cyr <Aric.Cyr@amd.com> Acked-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
5aa82e35ca
commit
31ecebee9c
6 changed files with 81 additions and 1 deletions
|
@ -1625,12 +1625,79 @@ void dcn10_pipe_control_lock(
|
||||||
hws->funcs.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. */
|
||||||
|
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)
|
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 */
|
/* cursor lock is per MPCC tree, so only need to lock one pipe per stream */
|
||||||
if (!pipe || pipe->top_pipe)
|
if (!pipe || pipe->top_pipe)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Prevent cursor lock from stalling out cursor updates. */
|
||||||
|
if (lock)
|
||||||
|
delay_cursor_until_vupdate(dc, pipe);
|
||||||
|
|
||||||
dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc,
|
dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc,
|
||||||
pipe->stream_res.opp->inst, lock);
|
pipe->stream_res.opp->inst, lock);
|
||||||
}
|
}
|
||||||
|
@ -3236,7 +3303,7 @@ int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx)
|
||||||
return vertical_line_start;
|
return vertical_line_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dcn10_calc_vupdate_position(
|
void dcn10_calc_vupdate_position(
|
||||||
struct dc *dc,
|
struct dc *dc,
|
||||||
struct pipe_ctx *pipe_ctx,
|
struct pipe_ctx *pipe_ctx,
|
||||||
uint32_t *start_line,
|
uint32_t *start_line,
|
||||||
|
|
|
@ -34,6 +34,11 @@ struct dc;
|
||||||
void dcn10_hw_sequencer_construct(struct dc *dc);
|
void dcn10_hw_sequencer_construct(struct dc *dc);
|
||||||
|
|
||||||
int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx);
|
int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx);
|
||||||
|
void dcn10_calc_vupdate_position(
|
||||||
|
struct dc *dc,
|
||||||
|
struct pipe_ctx *pipe_ctx,
|
||||||
|
uint32_t *start_line,
|
||||||
|
uint32_t *end_line);
|
||||||
void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx);
|
void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx);
|
||||||
enum dc_status dcn10_enable_stream_timing(
|
enum dc_status dcn10_enable_stream_timing(
|
||||||
struct pipe_ctx *pipe_ctx,
|
struct pipe_ctx *pipe_ctx,
|
||||||
|
|
|
@ -72,6 +72,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = {
|
||||||
.set_clock = dcn10_set_clock,
|
.set_clock = dcn10_set_clock,
|
||||||
.get_clock = dcn10_get_clock,
|
.get_clock = dcn10_get_clock,
|
||||||
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
|
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
|
||||||
|
.calc_vupdate_position = dcn10_calc_vupdate_position,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct hwseq_private_funcs dcn10_private_funcs = {
|
static const struct hwseq_private_funcs dcn10_private_funcs = {
|
||||||
|
|
|
@ -83,6 +83,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = {
|
||||||
.init_vm_ctx = dcn20_init_vm_ctx,
|
.init_vm_ctx = dcn20_init_vm_ctx,
|
||||||
.set_flip_control_gsl = dcn20_set_flip_control_gsl,
|
.set_flip_control_gsl = dcn20_set_flip_control_gsl,
|
||||||
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
|
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
|
||||||
|
.calc_vupdate_position = dcn10_calc_vupdate_position,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct hwseq_private_funcs dcn20_private_funcs = {
|
static const struct hwseq_private_funcs dcn20_private_funcs = {
|
||||||
|
|
|
@ -86,6 +86,7 @@ static const struct hw_sequencer_funcs dcn21_funcs = {
|
||||||
.optimize_pwr_state = dcn21_optimize_pwr_state,
|
.optimize_pwr_state = dcn21_optimize_pwr_state,
|
||||||
.exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state,
|
.exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state,
|
||||||
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
|
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
|
||||||
|
.calc_vupdate_position = dcn10_calc_vupdate_position,
|
||||||
.set_cursor_position = dcn10_set_cursor_position,
|
.set_cursor_position = dcn10_set_cursor_position,
|
||||||
.set_cursor_attribute = dcn10_set_cursor_attribute,
|
.set_cursor_attribute = dcn10_set_cursor_attribute,
|
||||||
.set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level,
|
.set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level,
|
||||||
|
|
|
@ -92,6 +92,11 @@ struct hw_sequencer_funcs {
|
||||||
void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes,
|
void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes,
|
||||||
struct crtc_position *position);
|
struct crtc_position *position);
|
||||||
int (*get_vupdate_offset_from_vsync)(struct pipe_ctx *pipe_ctx);
|
int (*get_vupdate_offset_from_vsync)(struct pipe_ctx *pipe_ctx);
|
||||||
|
void (*calc_vupdate_position)(
|
||||||
|
struct dc *dc,
|
||||||
|
struct pipe_ctx *pipe_ctx,
|
||||||
|
uint32_t *start_line,
|
||||||
|
uint32_t *end_line);
|
||||||
void (*enable_per_frame_crtc_position_reset)(struct dc *dc,
|
void (*enable_per_frame_crtc_position_reset)(struct dc *dc,
|
||||||
int group_size, struct pipe_ctx *grouped_pipes[]);
|
int group_size, struct pipe_ctx *grouped_pipes[]);
|
||||||
void (*enable_timing_synchronization)(struct dc *dc,
|
void (*enable_timing_synchronization)(struct dc *dc,
|
||||||
|
|
Loading…
Reference in a new issue