drm/amd/display: Use cursor locking to prevent flip delays
[Why] Current locking scheme for cursor can result in a flip missing its vsync, deferring it for one or more vsyncs. Result is a potential for stuttering when cursor is moved. [How] Use cursor update lock so that flips are not blocked while cursor is being programmed. Signed-off-by: Aric Cyr <aric.cyr@amd.com> Reviewed-by: Nicholas Kazlauskas <Nicholas.Kazlauskas@amd.com> Acked-by: Aurabindo Pillai <aurabindo.pillai@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
668a6741f8
commit
b2a7b0ce07
drivers/gpu/drm/amd/display/dc
@ -231,34 +231,6 @@ struct dc_stream_status *dc_stream_get_status(
|
||||
return dc_stream_get_status_from_state(dc->current_state, stream);
|
||||
}
|
||||
|
||||
static void delay_cursor_until_vupdate(struct pipe_ctx *pipe_ctx, struct dc *dc)
|
||||
{
|
||||
#if defined(CONFIG_DRM_AMD_DC_DCN)
|
||||
unsigned int vupdate_line;
|
||||
unsigned int lines_to_vupdate, us_to_vupdate, vpos, nvpos;
|
||||
struct dc_stream_state *stream = pipe_ctx->stream;
|
||||
unsigned int us_per_line;
|
||||
|
||||
if (!dc->hwss.get_vupdate_offset_from_vsync)
|
||||
return;
|
||||
|
||||
vupdate_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
|
||||
if (!dc_stream_get_crtc_position(dc, &stream, 1, &vpos, &nvpos))
|
||||
return;
|
||||
|
||||
if (vpos >= vupdate_line)
|
||||
return;
|
||||
|
||||
us_per_line =
|
||||
stream->timing.h_total * 10000 / stream->timing.pix_clk_100hz;
|
||||
lines_to_vupdate = vupdate_line - vpos;
|
||||
us_to_vupdate = lines_to_vupdate * us_per_line;
|
||||
|
||||
/* 70 us is a conservative estimate of cursor update time*/
|
||||
if (us_to_vupdate < 70)
|
||||
udelay(us_to_vupdate);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address
|
||||
@ -298,9 +270,7 @@ bool dc_stream_set_cursor_attributes(
|
||||
|
||||
if (!pipe_to_program) {
|
||||
pipe_to_program = pipe_ctx;
|
||||
|
||||
delay_cursor_until_vupdate(pipe_ctx, dc);
|
||||
dc->hwss.pipe_control_lock(dc, pipe_to_program, true);
|
||||
dc->hwss.cursor_lock(dc, pipe_to_program, true);
|
||||
}
|
||||
|
||||
dc->hwss.set_cursor_attribute(pipe_ctx);
|
||||
@ -309,7 +279,7 @@ bool dc_stream_set_cursor_attributes(
|
||||
}
|
||||
|
||||
if (pipe_to_program)
|
||||
dc->hwss.pipe_control_lock(dc, pipe_to_program, false);
|
||||
dc->hwss.cursor_lock(dc, pipe_to_program, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -349,16 +319,14 @@ bool dc_stream_set_cursor_position(
|
||||
|
||||
if (!pipe_to_program) {
|
||||
pipe_to_program = pipe_ctx;
|
||||
|
||||
delay_cursor_until_vupdate(pipe_ctx, dc);
|
||||
dc->hwss.pipe_control_lock(dc, pipe_to_program, true);
|
||||
dc->hwss.cursor_lock(dc, pipe_to_program, true);
|
||||
}
|
||||
|
||||
dc->hwss.set_cursor_position(pipe_ctx);
|
||||
}
|
||||
|
||||
if (pipe_to_program)
|
||||
dc->hwss.pipe_control_lock(dc, pipe_to_program, false);
|
||||
dc->hwss.cursor_lock(dc, pipe_to_program, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2757,6 +2757,7 @@ static const struct hw_sequencer_funcs dce110_funcs = {
|
||||
.disable_plane = dce110_power_down_fe,
|
||||
.pipe_control_lock = dce_pipe_control_lock,
|
||||
.interdependent_update_lock = NULL,
|
||||
.cursor_lock = dce_pipe_control_lock,
|
||||
.prepare_bandwidth = dce110_prepare_bandwidth,
|
||||
.optimize_bandwidth = dce110_optimize_bandwidth,
|
||||
.set_drr = set_drr,
|
||||
|
@ -1625,6 +1625,16 @@ void dcn10_pipe_control_lock(
|
||||
hws->funcs.verify_allow_pstate_change_high(dc);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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(
|
||||
struct dc_context *dc_ctx,
|
||||
struct timing_generator *tg)
|
||||
|
@ -49,6 +49,7 @@ void dcn10_pipe_control_lock(
|
||||
struct dc *dc,
|
||||
struct pipe_ctx *pipe,
|
||||
bool lock);
|
||||
void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock);
|
||||
void dcn10_blank_pixel_data(
|
||||
struct dc *dc,
|
||||
struct pipe_ctx *pipe_ctx,
|
||||
|
@ -50,6 +50,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = {
|
||||
.disable_audio_stream = dce110_disable_audio_stream,
|
||||
.disable_plane = dcn10_disable_plane,
|
||||
.pipe_control_lock = dcn10_pipe_control_lock,
|
||||
.cursor_lock = dcn10_cursor_lock,
|
||||
.interdependent_update_lock = dcn10_lock_all_pipes,
|
||||
.prepare_bandwidth = dcn10_prepare_bandwidth,
|
||||
.optimize_bandwidth = dcn10_optimize_bandwidth,
|
||||
|
@ -223,6 +223,9 @@ struct mpcc *mpc1_insert_plane(
|
||||
REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, dpp_id);
|
||||
REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, tree->opp_id);
|
||||
|
||||
/* Configure VUPDATE lock set for this MPCC to map to the OPP */
|
||||
REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, tree->opp_id);
|
||||
|
||||
/* update mpc tree mux setting */
|
||||
if (tree->opp_list == insert_above_mpcc) {
|
||||
/* insert the toppest mpcc */
|
||||
@ -318,6 +321,7 @@ void mpc1_remove_mpcc(
|
||||
REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
|
||||
REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
|
||||
REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, 0xf);
|
||||
REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
|
||||
|
||||
/* mark this mpcc as not in use */
|
||||
mpc10->mpcc_in_use_mask &= ~(1 << mpcc_id);
|
||||
@ -328,6 +332,7 @@ void mpc1_remove_mpcc(
|
||||
REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
|
||||
REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
|
||||
REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, 0xf);
|
||||
REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,6 +366,7 @@ void mpc1_mpc_init(struct mpc *mpc)
|
||||
REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
|
||||
REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
|
||||
REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, 0xf);
|
||||
REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
|
||||
|
||||
mpc1_init_mpcc(&(mpc->mpcc_array[mpcc_id]), mpcc_id);
|
||||
}
|
||||
@ -381,6 +387,7 @@ void mpc1_mpc_init_single_inst(struct mpc *mpc, unsigned int mpcc_id)
|
||||
REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
|
||||
REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
|
||||
REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, 0xf);
|
||||
REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
|
||||
|
||||
mpc1_init_mpcc(&(mpc->mpcc_array[mpcc_id]), mpcc_id);
|
||||
|
||||
@ -453,6 +460,13 @@ void mpc1_read_mpcc_state(
|
||||
MPCC_BUSY, &s->busy);
|
||||
}
|
||||
|
||||
void mpc1_cursor_lock(struct mpc *mpc, int opp_id, bool lock)
|
||||
{
|
||||
struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
|
||||
|
||||
REG_SET(CUR[opp_id], 0, CUR_VUPDATE_LOCK_SET, lock ? 1 : 0);
|
||||
}
|
||||
|
||||
static const struct mpc_funcs dcn10_mpc_funcs = {
|
||||
.read_mpcc_state = mpc1_read_mpcc_state,
|
||||
.insert_plane = mpc1_insert_plane,
|
||||
@ -464,6 +478,7 @@ static const struct mpc_funcs dcn10_mpc_funcs = {
|
||||
.assert_mpcc_idle_before_connect = mpc1_assert_mpcc_idle_before_connect,
|
||||
.init_mpcc_list_from_hw = mpc1_init_mpcc_list_from_hw,
|
||||
.update_blending = mpc1_update_blending,
|
||||
.cursor_lock = mpc1_cursor_lock,
|
||||
.set_denorm = NULL,
|
||||
.set_denorm_clamp = NULL,
|
||||
.set_output_csc = NULL,
|
||||
|
@ -39,11 +39,12 @@
|
||||
SRII(MPCC_BG_G_Y, MPCC, inst),\
|
||||
SRII(MPCC_BG_R_CR, MPCC, inst),\
|
||||
SRII(MPCC_BG_B_CB, MPCC, inst),\
|
||||
SRII(MPCC_BG_B_CB, MPCC, inst),\
|
||||
SRII(MPCC_SM_CONTROL, MPCC, inst)
|
||||
SRII(MPCC_SM_CONTROL, MPCC, inst),\
|
||||
SRII(MPCC_UPDATE_LOCK_SEL, MPCC, inst)
|
||||
|
||||
#define MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(inst) \
|
||||
SRII(MUX, MPC_OUT, inst)
|
||||
SRII(MUX, MPC_OUT, inst),\
|
||||
VUPDATE_SRII(CUR, VUPDATE_LOCK_SET, inst)
|
||||
|
||||
#define MPC_COMMON_REG_VARIABLE_LIST \
|
||||
uint32_t MPCC_TOP_SEL[MAX_MPCC]; \
|
||||
@ -55,7 +56,9 @@
|
||||
uint32_t MPCC_BG_R_CR[MAX_MPCC]; \
|
||||
uint32_t MPCC_BG_B_CB[MAX_MPCC]; \
|
||||
uint32_t MPCC_SM_CONTROL[MAX_MPCC]; \
|
||||
uint32_t MUX[MAX_OPP];
|
||||
uint32_t MUX[MAX_OPP]; \
|
||||
uint32_t MPCC_UPDATE_LOCK_SEL[MAX_MPCC]; \
|
||||
uint32_t CUR[MAX_OPP];
|
||||
|
||||
#define MPC_COMMON_MASK_SH_LIST_DCN1_0(mask_sh)\
|
||||
SF(MPCC0_MPCC_TOP_SEL, MPCC_TOP_SEL, mask_sh),\
|
||||
@ -78,7 +81,8 @@
|
||||
SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FIELD_ALT, mask_sh),\
|
||||
SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FORCE_NEXT_FRAME_POL, mask_sh),\
|
||||
SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FORCE_NEXT_TOP_POL, mask_sh),\
|
||||
SF(MPC_OUT0_MUX, MPC_OUT_MUX, mask_sh)
|
||||
SF(MPC_OUT0_MUX, MPC_OUT_MUX, mask_sh),\
|
||||
SF(MPCC0_MPCC_UPDATE_LOCK_SEL, MPCC_UPDATE_LOCK_SEL, mask_sh)
|
||||
|
||||
#define MPC_REG_FIELD_LIST(type) \
|
||||
type MPCC_TOP_SEL;\
|
||||
@ -101,7 +105,9 @@
|
||||
type MPCC_SM_FIELD_ALT;\
|
||||
type MPCC_SM_FORCE_NEXT_FRAME_POL;\
|
||||
type MPCC_SM_FORCE_NEXT_TOP_POL;\
|
||||
type MPC_OUT_MUX;
|
||||
type MPC_OUT_MUX;\
|
||||
type MPCC_UPDATE_LOCK_SEL;\
|
||||
type CUR_VUPDATE_LOCK_SET;
|
||||
|
||||
struct dcn_mpc_registers {
|
||||
MPC_COMMON_REG_VARIABLE_LIST
|
||||
@ -192,4 +198,6 @@ void mpc1_read_mpcc_state(
|
||||
int mpcc_inst,
|
||||
struct mpcc_state *s);
|
||||
|
||||
void mpc1_cursor_lock(struct mpc *mpc, int opp_id, bool lock);
|
||||
|
||||
#endif
|
||||
|
@ -181,6 +181,14 @@ enum dcn10_clk_src_array_id {
|
||||
.reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
|
||||
mm ## block ## id ## _ ## reg_name
|
||||
|
||||
#define VUPDATE_SRII(reg_name, block, id)\
|
||||
.reg_name[id] = BASE(mm ## reg_name ## 0 ## _ ## block ## id ## _BASE_IDX) + \
|
||||
mm ## reg_name ## 0 ## _ ## block ## id
|
||||
|
||||
/* set field/register/bitfield name */
|
||||
#define SFRB(field_name, reg_name, bitfield, post_fix)\
|
||||
.field_name = reg_name ## __ ## bitfield ## post_fix
|
||||
|
||||
/* NBIO */
|
||||
#define NBIO_BASE_INNER(seg) \
|
||||
NBIF_BASE__INST0_SEG ## seg
|
||||
@ -419,11 +427,13 @@ static const struct dcn_mpc_registers mpc_regs = {
|
||||
};
|
||||
|
||||
static const struct dcn_mpc_shift mpc_shift = {
|
||||
MPC_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT)
|
||||
MPC_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT),\
|
||||
SFRB(CUR_VUPDATE_LOCK_SET, CUR0_VUPDATE_LOCK_SET0, CUR0_VUPDATE_LOCK_SET, __SHIFT)
|
||||
};
|
||||
|
||||
static const struct dcn_mpc_mask mpc_mask = {
|
||||
MPC_COMMON_MASK_SH_LIST_DCN1_0(_MASK),
|
||||
MPC_COMMON_MASK_SH_LIST_DCN1_0(_MASK),\
|
||||
SFRB(CUR_VUPDATE_LOCK_SET, CUR0_VUPDATE_LOCK_SET0, CUR0_VUPDATE_LOCK_SET, _MASK)
|
||||
};
|
||||
|
||||
#define tg_regs(id)\
|
||||
|
@ -52,6 +52,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = {
|
||||
.disable_plane = dcn20_disable_plane,
|
||||
.pipe_control_lock = dcn20_pipe_control_lock,
|
||||
.interdependent_update_lock = dcn10_lock_all_pipes,
|
||||
.cursor_lock = dcn10_cursor_lock,
|
||||
.prepare_bandwidth = dcn20_prepare_bandwidth,
|
||||
.optimize_bandwidth = dcn20_optimize_bandwidth,
|
||||
.update_bandwidth = dcn20_update_bandwidth,
|
||||
|
@ -545,6 +545,7 @@ const struct mpc_funcs dcn20_mpc_funcs = {
|
||||
.mpc_init = mpc1_mpc_init,
|
||||
.mpc_init_single_inst = mpc1_mpc_init_single_inst,
|
||||
.update_blending = mpc2_update_blending,
|
||||
.cursor_lock = mpc1_cursor_lock,
|
||||
.get_mpcc_for_dpp = mpc2_get_mpcc_for_dpp,
|
||||
.wait_for_idle = mpc2_assert_idle_mpcc,
|
||||
.assert_mpcc_idle_before_connect = mpc2_assert_mpcc_idle_before_connect,
|
||||
|
@ -179,7 +179,8 @@
|
||||
SF(MPC_OUT0_DENORM_CLAMP_G_Y, MPC_OUT_DENORM_CLAMP_MAX_G_Y, mask_sh),\
|
||||
SF(MPC_OUT0_DENORM_CLAMP_G_Y, MPC_OUT_DENORM_CLAMP_MIN_G_Y, mask_sh),\
|
||||
SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MAX_B_CB, mask_sh),\
|
||||
SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MIN_B_CB, mask_sh)
|
||||
SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MIN_B_CB, mask_sh),\
|
||||
SF(CUR_VUPDATE_LOCK_SET0, CUR_VUPDATE_LOCK_SET, mask_sh)
|
||||
|
||||
/*
|
||||
* DCN2 MPC_OCSC debug status register:
|
||||
|
@ -508,6 +508,10 @@ enum dcn20_clk_src_array_id {
|
||||
.block ## _ ## reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
|
||||
mm ## block ## id ## _ ## reg_name
|
||||
|
||||
#define VUPDATE_SRII(reg_name, block, id)\
|
||||
.reg_name[id] = BASE(mm ## reg_name ## _ ## block ## id ## _BASE_IDX) + \
|
||||
mm ## reg_name ## _ ## block ## id
|
||||
|
||||
/* NBIO */
|
||||
#define NBIO_BASE_INNER(seg) \
|
||||
NBIO_BASE__INST0_SEG ## seg
|
||||
|
@ -53,6 +53,7 @@ static const struct hw_sequencer_funcs dcn21_funcs = {
|
||||
.disable_plane = dcn20_disable_plane,
|
||||
.pipe_control_lock = dcn20_pipe_control_lock,
|
||||
.interdependent_update_lock = dcn10_lock_all_pipes,
|
||||
.cursor_lock = dcn10_cursor_lock,
|
||||
.prepare_bandwidth = dcn20_prepare_bandwidth,
|
||||
.optimize_bandwidth = dcn20_optimize_bandwidth,
|
||||
.update_bandwidth = dcn20_update_bandwidth,
|
||||
|
@ -340,6 +340,10 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_1_soc = {
|
||||
.block ## _ ## reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
|
||||
mm ## block ## id ## _ ## reg_name
|
||||
|
||||
#define VUPDATE_SRII(reg_name, block, id)\
|
||||
.reg_name[id] = BASE(mm ## reg_name ## _ ## block ## id ## _BASE_IDX) + \
|
||||
mm ## reg_name ## _ ## block ## id
|
||||
|
||||
/* NBIO */
|
||||
#define NBIO_BASE_INNER(seg) \
|
||||
NBIF0_BASE__INST0_SEG ## seg
|
||||
|
@ -210,6 +210,22 @@ struct mpc_funcs {
|
||||
struct mpcc_blnd_cfg *blnd_cfg,
|
||||
int mpcc_id);
|
||||
|
||||
/*
|
||||
* Lock cursor updates for the specified OPP.
|
||||
* OPP defines the set of MPCC that are locked together for cursor.
|
||||
*
|
||||
* Parameters:
|
||||
* [in] mpc - MPC context.
|
||||
* [in] opp_id - The OPP to lock cursor updates on
|
||||
* [in] lock - lock/unlock the OPP
|
||||
*
|
||||
* Return: void
|
||||
*/
|
||||
void (*cursor_lock)(
|
||||
struct mpc *mpc,
|
||||
int opp_id,
|
||||
bool lock);
|
||||
|
||||
struct mpcc* (*get_mpcc_for_dpp)(
|
||||
struct mpc_tree *tree,
|
||||
int dpp_id);
|
||||
|
@ -86,6 +86,7 @@ struct hw_sequencer_funcs {
|
||||
struct dc_state *context, bool lock);
|
||||
void (*set_flip_control_gsl)(struct pipe_ctx *pipe_ctx,
|
||||
bool flip_immediate);
|
||||
void (*cursor_lock)(struct dc *dc, struct pipe_ctx *pipe, bool lock);
|
||||
|
||||
/* Timing Related */
|
||||
void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes,
|
||||
|
Loading…
x
Reference in New Issue
Block a user