Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Nicholas Kazlauskas and committed by
Alex Deucher
63731e73 7d1ee78f

+81 -1
+68 -1
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
··· 1683 1683 hws->funcs.verify_allow_pstate_change_high(dc); 1684 1684 } 1685 1685 1686 + /** 1687 + * delay_cursor_until_vupdate() - Delay cursor update if too close to VUPDATE. 1688 + * 1689 + * Software keepout workaround to prevent cursor update locking from stalling 1690 + * out cursor updates indefinitely or from old values from being retained in 1691 + * the case where the viewport changes in the same frame as the cursor. 1692 + * 1693 + * The idea is to calculate the remaining time from VPOS to VUPDATE. If it's 1694 + * too close to VUPDATE, then stall out until VUPDATE finishes. 1695 + * 1696 + * TODO: Optimize cursor programming to be once per frame before VUPDATE 1697 + * to avoid the need for this workaround. 1698 + */ 1699 + static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx) 1700 + { 1701 + struct dc_stream_state *stream = pipe_ctx->stream; 1702 + struct crtc_position position; 1703 + uint32_t vupdate_start, vupdate_end; 1704 + unsigned int lines_to_vupdate, us_to_vupdate, vpos; 1705 + unsigned int us_per_line, us_vupdate; 1706 + 1707 + if (!dc->hwss.calc_vupdate_position || !dc->hwss.get_position) 1708 + return; 1709 + 1710 + if (!pipe_ctx->stream_res.stream_enc || !pipe_ctx->stream_res.tg) 1711 + return; 1712 + 1713 + dc->hwss.calc_vupdate_position(dc, pipe_ctx, &vupdate_start, 1714 + &vupdate_end); 1715 + 1716 + dc->hwss.get_position(&pipe_ctx, 1, &position); 1717 + vpos = position.vertical_count; 1718 + 1719 + /* Avoid wraparound calculation issues */ 1720 + vupdate_start += stream->timing.v_total; 1721 + vupdate_end += stream->timing.v_total; 1722 + vpos += stream->timing.v_total; 1723 + 1724 + if (vpos <= vupdate_start) { 1725 + /* VPOS is in VACTIVE or back porch. */ 1726 + lines_to_vupdate = vupdate_start - vpos; 1727 + } else if (vpos > vupdate_end) { 1728 + /* VPOS is in the front porch. */ 1729 + return; 1730 + } else { 1731 + /* VPOS is in VUPDATE. */ 1732 + lines_to_vupdate = 0; 1733 + } 1734 + 1735 + /* Calculate time until VUPDATE in microseconds. */ 1736 + us_per_line = 1737 + stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz; 1738 + us_to_vupdate = lines_to_vupdate * us_per_line; 1739 + 1740 + /* 70 us is a conservative estimate of cursor update time*/ 1741 + if (us_to_vupdate > 70) 1742 + return; 1743 + 1744 + /* Stall out until the cursor update completes. */ 1745 + us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line; 1746 + udelay(us_to_vupdate + us_vupdate); 1747 + } 1748 + 1686 1749 void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock) 1687 1750 { 1688 1751 /* cursor lock is per MPCC tree, so only need to lock one pipe per stream */ 1689 1752 if (!pipe || pipe->top_pipe) 1690 1753 return; 1754 + 1755 + /* Prevent cursor lock from stalling out cursor updates. */ 1756 + if (lock) 1757 + delay_cursor_until_vupdate(dc, pipe); 1691 1758 1692 1759 dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc, 1693 1760 pipe->stream_res.opp->inst, lock); ··· 3369 3302 return vertical_line_start; 3370 3303 } 3371 3304 3372 - static void dcn10_calc_vupdate_position( 3305 + void dcn10_calc_vupdate_position( 3373 3306 struct dc *dc, 3374 3307 struct pipe_ctx *pipe_ctx, 3375 3308 uint32_t *start_line,
+5
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h
··· 34 34 void dcn10_hw_sequencer_construct(struct dc *dc); 35 35 36 36 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); 37 42 void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx); 38 43 enum dc_status dcn10_enable_stream_timing( 39 44 struct pipe_ctx *pipe_ctx,
+1
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_init.c
··· 72 72 .set_clock = dcn10_set_clock, 73 73 .get_clock = dcn10_get_clock, 74 74 .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync, 75 + .calc_vupdate_position = dcn10_calc_vupdate_position, 75 76 .set_backlight_level = dce110_set_backlight_level, 76 77 .set_abm_immediate_disable = dce110_set_abm_immediate_disable, 77 78 };
+1
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.c
··· 83 83 .init_vm_ctx = dcn20_init_vm_ctx, 84 84 .set_flip_control_gsl = dcn20_set_flip_control_gsl, 85 85 .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync, 86 + .calc_vupdate_position = dcn10_calc_vupdate_position, 86 87 .set_backlight_level = dce110_set_backlight_level, 87 88 .set_abm_immediate_disable = dce110_set_abm_immediate_disable, 88 89 };
+1
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_init.c
··· 86 86 .optimize_pwr_state = dcn21_optimize_pwr_state, 87 87 .exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state, 88 88 .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync, 89 + .calc_vupdate_position = dcn10_calc_vupdate_position, 89 90 .power_down = dce110_power_down, 90 91 .set_backlight_level = dce110_set_backlight_level, 91 92 .set_abm_immediate_disable = dce110_set_abm_immediate_disable,
+5
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h
··· 96 96 void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes, 97 97 struct crtc_position *position); 98 98 int (*get_vupdate_offset_from_vsync)(struct pipe_ctx *pipe_ctx); 99 + void (*calc_vupdate_position)( 100 + struct dc *dc, 101 + struct pipe_ctx *pipe_ctx, 102 + uint32_t *start_line, 103 + uint32_t *end_line); 99 104 void (*enable_per_frame_crtc_position_reset)(struct dc *dc, 100 105 int group_size, struct pipe_ctx *grouped_pipes[]); 101 106 void (*enable_timing_synchronization)(struct dc *dc,