Skip to content

Commit 31ecebe

Browse files
Nicholas Kazlauskasalexdeucher
authored andcommitted
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 <[email protected]> Reviewed-by: Aric Cyr <[email protected]> Acked-by: Rodrigo Siqueira <[email protected]> Signed-off-by: Alex Deucher <[email protected]>
1 parent 5aa82e3 commit 31ecebe

File tree

6 files changed

+81
-1
lines changed

6 files changed

+81
-1
lines changed

drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1625,12 +1625,79 @@ void dcn10_pipe_control_lock(
16251625
hws->funcs.verify_allow_pstate_change_high(dc);
16261626
}
16271627

1628+
/**
1629+
* delay_cursor_until_vupdate() - Delay cursor update if too close to VUPDATE.
1630+
*
1631+
* Software keepout workaround to prevent cursor update locking from stalling
1632+
* out cursor updates indefinitely or from old values from being retained in
1633+
* the case where the viewport changes in the same frame as the cursor.
1634+
*
1635+
* The idea is to calculate the remaining time from VPOS to VUPDATE. If it's
1636+
* too close to VUPDATE, then stall out until VUPDATE finishes.
1637+
*
1638+
* TODO: Optimize cursor programming to be once per frame before VUPDATE
1639+
* to avoid the need for this workaround.
1640+
*/
1641+
static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx)
1642+
{
1643+
struct dc_stream_state *stream = pipe_ctx->stream;
1644+
struct crtc_position position;
1645+
uint32_t vupdate_start, vupdate_end;
1646+
unsigned int lines_to_vupdate, us_to_vupdate, vpos;
1647+
unsigned int us_per_line, us_vupdate;
1648+
1649+
if (!dc->hwss.calc_vupdate_position || !dc->hwss.get_position)
1650+
return;
1651+
1652+
if (!pipe_ctx->stream_res.stream_enc || !pipe_ctx->stream_res.tg)
1653+
return;
1654+
1655+
dc->hwss.calc_vupdate_position(dc, pipe_ctx, &vupdate_start,
1656+
&vupdate_end);
1657+
1658+
dc->hwss.get_position(&pipe_ctx, 1, &position);
1659+
vpos = position.vertical_count;
1660+
1661+
/* Avoid wraparound calculation issues */
1662+
vupdate_start += stream->timing.v_total;
1663+
vupdate_end += stream->timing.v_total;
1664+
vpos += stream->timing.v_total;
1665+
1666+
if (vpos <= vupdate_start) {
1667+
/* VPOS is in VACTIVE or back porch. */
1668+
lines_to_vupdate = vupdate_start - vpos;
1669+
} else if (vpos > vupdate_end) {
1670+
/* VPOS is in the front porch. */
1671+
return;
1672+
} else {
1673+
/* VPOS is in VUPDATE. */
1674+
lines_to_vupdate = 0;
1675+
}
1676+
1677+
/* Calculate time until VUPDATE in microseconds. */
1678+
us_per_line =
1679+
stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz;
1680+
us_to_vupdate = lines_to_vupdate * us_per_line;
1681+
1682+
/* 70 us is a conservative estimate of cursor update time*/
1683+
if (us_to_vupdate > 70)
1684+
return;
1685+
1686+
/* Stall out until the cursor update completes. */
1687+
us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line;
1688+
udelay(us_to_vupdate + us_vupdate);
1689+
}
1690+
16281691
void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock)
16291692
{
16301693
/* cursor lock is per MPCC tree, so only need to lock one pipe per stream */
16311694
if (!pipe || pipe->top_pipe)
16321695
return;
16331696

1697+
/* Prevent cursor lock from stalling out cursor updates. */
1698+
if (lock)
1699+
delay_cursor_until_vupdate(dc, pipe);
1700+
16341701
dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc,
16351702
pipe->stream_res.opp->inst, lock);
16361703
}
@@ -3236,7 +3303,7 @@ int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx)
32363303
return vertical_line_start;
32373304
}
32383305

3239-
static void dcn10_calc_vupdate_position(
3306+
void dcn10_calc_vupdate_position(
32403307
struct dc *dc,
32413308
struct pipe_ctx *pipe_ctx,
32423309
uint32_t *start_line,

drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ struct dc;
3434
void dcn10_hw_sequencer_construct(struct dc *dc);
3535

3636
int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx);
37+
void dcn10_calc_vupdate_position(
38+
struct dc *dc,
39+
struct pipe_ctx *pipe_ctx,
40+
uint32_t *start_line,
41+
uint32_t *end_line);
3742
void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx);
3843
enum dc_status dcn10_enable_stream_timing(
3944
struct pipe_ctx *pipe_ctx,

drivers/gpu/drm/amd/display/dc/dcn10/dcn10_init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = {
7272
.set_clock = dcn10_set_clock,
7373
.get_clock = dcn10_get_clock,
7474
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
75+
.calc_vupdate_position = dcn10_calc_vupdate_position,
7576
};
7677

7778
static const struct hwseq_private_funcs dcn10_private_funcs = {

drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = {
8383
.init_vm_ctx = dcn20_init_vm_ctx,
8484
.set_flip_control_gsl = dcn20_set_flip_control_gsl,
8585
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
86+
.calc_vupdate_position = dcn10_calc_vupdate_position,
8687
};
8788

8889
static const struct hwseq_private_funcs dcn20_private_funcs = {

drivers/gpu/drm/amd/display/dc/dcn21/dcn21_init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ static const struct hw_sequencer_funcs dcn21_funcs = {
8686
.optimize_pwr_state = dcn21_optimize_pwr_state,
8787
.exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state,
8888
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
89+
.calc_vupdate_position = dcn10_calc_vupdate_position,
8990
.set_cursor_position = dcn10_set_cursor_position,
9091
.set_cursor_attribute = dcn10_set_cursor_attribute,
9192
.set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level,

drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ struct hw_sequencer_funcs {
9292
void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes,
9393
struct crtc_position *position);
9494
int (*get_vupdate_offset_from_vsync)(struct pipe_ctx *pipe_ctx);
95+
void (*calc_vupdate_position)(
96+
struct dc *dc,
97+
struct pipe_ctx *pipe_ctx,
98+
uint32_t *start_line,
99+
uint32_t *end_line);
95100
void (*enable_per_frame_crtc_position_reset)(struct dc *dc,
96101
int group_size, struct pipe_ctx *grouped_pipes[]);
97102
void (*enable_timing_synchronization)(struct dc *dc,

0 commit comments

Comments
 (0)