Commit eed4edda authored by Robin Chen's avatar Robin Chen Committed by Alex Deucher

drm/amd/display: Support long vblank feature

[WHY]
We want to support low hz case, but the original
vtotal/vtotal_min/vtotal_max can't support more
than 0x7FFF.

[HOW]
We use the 2 HW reg to contorl long vblank case.
1. OTG_V_COUNT_STOP_CONTROL -> vcount_stop
2. OTG_V_COUNT_STOP_CONTROL2 -> vcount_stop_timer

vcount_stop define from which line we stop using vcount
and start using vcount2.
vcount_stop_timer define how long we use vcount2.

Ex:
Vtotal = 7
OTG_V_COUNT_STOP_CONTROL = 4
OTG_V_COUNT_STOP_CONTROL2 = 5

time    : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
vcount  : 0, 1, 2, 3, -  -  -  -  -  4,  5,  6
vcount2 :             0, 1, 2, 3, 4,
Reviewed-by: default avatarJun Lei <jun.lei@amd.com>
Acked-by: default avatarAlex Hung <alex.hung@amd.com>
Signed-off-by: default avatarChunTao Tso <chuntao.tso@amd.com>
Signed-off-by: Robin Chen<robin.chen@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent c90835b0
......@@ -386,6 +386,30 @@ static void dc_perf_trace_destroy(struct dc_perf_trace **perf_trace)
*perf_trace = NULL;
}
static bool set_long_vtotal(struct dc *dc, struct dc_stream_state *stream, struct dc_crtc_timing_adjust *adjust)
{
if (!dc || !stream || !adjust)
return false;
if (!dc->current_state)
return false;
int i;
for (i = 0; i < MAX_PIPES; i++) {
struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];
if (pipe->stream == stream && pipe->stream_res.tg) {
if (dc->hwss.set_long_vtotal)
dc->hwss.set_long_vtotal(&pipe, 1, adjust->v_total_min, adjust->v_total_max);
return true;
}
}
return false;
}
/**
* dc_stream_adjust_vmin_vmax - look up pipe context & update parts of DRR
* @dc: dc reference
......@@ -420,6 +444,15 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc,
stream->adjust.v_total_mid = adjust->v_total_mid;
stream->adjust.v_total_mid_frame_num = adjust->v_total_mid_frame_num;
stream->adjust.v_total_min = adjust->v_total_min;
stream->adjust.allow_otg_v_count_halt = adjust->allow_otg_v_count_halt;
if (dc->caps.max_v_total != 0 &&
(adjust->v_total_max > dc->caps.max_v_total || adjust->v_total_min > dc->caps.max_v_total)) {
if (adjust->allow_otg_v_count_halt)
return set_long_vtotal(dc, stream, adjust);
else
return false;
}
for (i = 0; i < MAX_PIPES; i++) {
struct pipe_ctx *pipe = &dc->current_state->res_ctx.pipe_ctx[i];
......
......@@ -974,6 +974,7 @@ struct dc_crtc_timing_adjust {
uint32_t v_total_max;
uint32_t v_total_mid;
uint32_t v_total_mid_frame_num;
uint32_t allow_otg_v_count_halt;
};
......
......@@ -1394,3 +1394,31 @@ void dcn35_set_static_screen_control(struct pipe_ctx **pipe_ctx,
set_static_screen_control(pipe_ctx[i]->stream_res.tg,
triggers, params->num_frames);
}
void dcn35_set_long_vblank(struct pipe_ctx **pipe_ctx,
int num_pipes, uint32_t v_total_min, uint32_t v_total_max)
{
int i = 0;
struct long_vtotal_params params = {0};
params.vertical_total_max = v_total_max;
params.vertical_total_min = v_total_min;
for (i = 0; i < num_pipes; i++) {
if (!pipe_ctx[i])
continue;
if (pipe_ctx[i]->stream) {
struct dc_crtc_timing *timing = &pipe_ctx[i]->stream->timing;
if (timing)
params.vertical_blank_start = timing->v_total - timing->v_front_porch;
else
params.vertical_blank_start = 0;
if ((pipe_ctx[i]->stream_res.tg != NULL) && pipe_ctx[i]->stream_res.tg->funcs &&
pipe_ctx[i]->stream_res.tg->funcs->set_long_vtotal)
pipe_ctx[i]->stream_res.tg->funcs->set_long_vtotal(pipe_ctx[i]->stream_res.tg, &params);
}
}
}
......@@ -93,4 +93,7 @@ void dcn35_set_drr(struct pipe_ctx **pipe_ctx,
void dcn35_set_static_screen_control(struct pipe_ctx **pipe_ctx,
int num_pipes, const struct dc_static_screen_params *params);
void dcn35_set_long_vblank(struct pipe_ctx **pipe_ctx,
int num_pipes, uint32_t v_total_min, uint32_t v_total_max);
#endif /* __DC_HWSS_DCN35_H__ */
......@@ -122,7 +122,8 @@ static const struct hw_sequencer_funcs dcn35_funcs = {
.hw_block_power_down = dcn35_hw_block_power_down,
.root_clock_control = dcn35_root_clock_control,
.set_idle_state = dcn35_set_idle_state,
.get_idle_state = dcn35_get_idle_state
.get_idle_state = dcn35_get_idle_state,
.set_long_vtotal = dcn35_set_long_vblank,
};
static const struct hwseq_private_funcs dcn35_private_funcs = {
......
......@@ -429,6 +429,7 @@ struct hw_sequencer_funcs {
bool (*is_pipe_topology_transition_seamless)(struct dc *dc,
const struct dc_state *cur_ctx,
const struct dc_state *new_ctx);
void (*set_long_vtotal)(struct pipe_ctx **pipe_ctx, int num_pipes, uint32_t v_total_min, uint32_t v_total_max);
};
void color_space_to_black_color(
......
......@@ -64,6 +64,12 @@ struct drr_params {
bool immediate_flip;
};
struct long_vtotal_params {
uint32_t vertical_total_min;
uint32_t vertical_total_max;
uint32_t vertical_blank_start;
};
#define LEFT_EYE_3D_PRIMARY_SURFACE 1
#define RIGHT_EYE_3D_PRIMARY_SURFACE 0
......@@ -331,6 +337,7 @@ struct timing_generator_funcs {
void (*init_odm)(struct timing_generator *tg);
void (*wait_drr_doublebuffer_pending_clear)(struct timing_generator *tg);
void (*set_long_vtotal)(struct timing_generator *optc, const struct long_vtotal_params *params);
void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg);
};
......
......@@ -129,6 +129,8 @@ struct dcn_optc_registers {
uint32_t OTG_V_TOTAL_MID;
uint32_t OTG_V_TOTAL_MIN;
uint32_t OTG_V_TOTAL_CONTROL;
uint32_t OTG_V_COUNT_STOP_CONTROL;
uint32_t OTG_V_COUNT_STOP_CONTROL2;
uint32_t OTG_TRIGA_CNTL;
uint32_t OTG_TRIGA_MANUAL_TRIG;
uint32_t OTG_MANUAL_FLOW_CONTROL;
......@@ -581,7 +583,9 @@ struct dcn_optc_registers {
type OTG_CRC1_WINDOWB_X_END_READBACK;\
type OTG_CRC1_WINDOWB_Y_START_READBACK;\
type OTG_CRC1_WINDOWB_Y_END_READBACK;\
type OPTC_FGCG_REP_DIS;
type OPTC_FGCG_REP_DIS;\
type OTG_V_COUNT_STOP;\
type OTG_V_COUNT_STOP_TIMER;
struct dcn_optc_shift {
TG_REG_FIELD_LIST(uint8_t)
......
......@@ -32,6 +32,7 @@
#include "reg_helper.h"
#include "dc.h"
#include "dcn_calc_math.h"
#include "dc_dmub_srv.h"
#define REG(reg)\
optc1->tg_regs->reg
......@@ -213,6 +214,167 @@ static bool optc35_configure_crc(struct timing_generator *optc,
return true;
}
static void optc35_setup_manual_trigger(struct timing_generator *optc)
{
if (!optc || !optc->ctx)
return;
struct optc *optc1 = DCN10TG_FROM_TG(optc);
struct dc *dc = optc->ctx->dc;
if (dc->caps.dmub_caps.mclk_sw && !dc->debug.disable_fams)
dc_dmub_srv_set_drr_manual_trigger_cmd(dc, optc->inst);
else {
/*
* MIN_MASK_EN is gone and MASK is now always enabled.
*
* To get it to it work with manual trigger we need to make sure
* we program the correct bit.
*/
REG_UPDATE_4(OTG_V_TOTAL_CONTROL,
OTG_V_TOTAL_MIN_SEL, 1,
OTG_V_TOTAL_MAX_SEL, 1,
OTG_FORCE_LOCK_ON_EVENT, 0,
OTG_SET_V_TOTAL_MIN_MASK, (1 << 1)); /* TRIGA */
// Setup manual flow control for EOF via TRIG_A
if (optc->funcs && optc->funcs->setup_manual_trigger)
optc->funcs->setup_manual_trigger(optc);
}
}
void optc35_set_drr(
struct timing_generator *optc,
const struct drr_params *params)
{
if (!optc || !params)
return;
struct optc *optc1 = DCN10TG_FROM_TG(optc);
uint32_t max_otg_v_total = optc1->max_v_total - 1;
if (params != NULL &&
params->vertical_total_max > 0 &&
params->vertical_total_min > 0) {
if (params->vertical_total_mid != 0) {
REG_SET(OTG_V_TOTAL_MID, 0,
OTG_V_TOTAL_MID, params->vertical_total_mid - 1);
REG_UPDATE_2(OTG_V_TOTAL_CONTROL,
OTG_VTOTAL_MID_REPLACING_MAX_EN, 1,
OTG_VTOTAL_MID_FRAME_NUM,
(uint8_t)params->vertical_total_mid_frame_num);
}
if (optc->funcs && optc->funcs->set_vtotal_min_max)
optc->funcs->set_vtotal_min_max(optc,
params->vertical_total_min - 1, params->vertical_total_max - 1);
optc35_setup_manual_trigger(optc);
} else {
REG_UPDATE_4(OTG_V_TOTAL_CONTROL,
OTG_SET_V_TOTAL_MIN_MASK, 0,
OTG_V_TOTAL_MIN_SEL, 0,
OTG_V_TOTAL_MAX_SEL, 0,
OTG_FORCE_LOCK_ON_EVENT, 0);
if (optc->funcs && optc->funcs->set_vtotal_min_max)
optc->funcs->set_vtotal_min_max(optc, 0, 0);
}
REG_WRITE(OTG_V_COUNT_STOP_CONTROL, max_otg_v_total);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL2, 0);
}
static void optc35_set_long_vtotal(
struct timing_generator *optc,
const struct long_vtotal_params *params)
{
if (!optc || !params)
return;
struct optc *optc1 = DCN10TG_FROM_TG(optc);
uint32_t vcount_stop_timer = 0, vcount_stop = 0;
uint32_t max_otg_v_total = optc1->max_v_total - 1;
if (params->vertical_total_min <= max_otg_v_total && params->vertical_total_max <= max_otg_v_total)
return;
if (params->vertical_total_max == 0 || params->vertical_total_min == 0) {
REG_UPDATE_4(OTG_V_TOTAL_CONTROL,
OTG_SET_V_TOTAL_MIN_MASK, 0,
OTG_V_TOTAL_MIN_SEL, 0,
OTG_V_TOTAL_MAX_SEL, 0,
OTG_FORCE_LOCK_ON_EVENT, 0);
if (optc->funcs && optc->funcs->set_vtotal_min_max)
optc->funcs->set_vtotal_min_max(optc, 0, 0);
} else if (params->vertical_total_max == params->vertical_total_min) {
vcount_stop = params->vertical_blank_start;
vcount_stop_timer = params->vertical_total_max - max_otg_v_total;
REG_UPDATE_4(OTG_V_TOTAL_CONTROL,
OTG_V_TOTAL_MIN_SEL, 1,
OTG_V_TOTAL_MAX_SEL, 1,
OTG_FORCE_LOCK_ON_EVENT, 0,
OTG_SET_V_TOTAL_MIN_MASK, 0);
if (optc->funcs && optc->funcs->set_vtotal_min_max)
optc->funcs->set_vtotal_min_max(optc, max_otg_v_total, max_otg_v_total);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL, vcount_stop);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL2, vcount_stop_timer);
} else {
// Variable rate, keep DRR trigger mask
if (params->vertical_total_min > max_otg_v_total) {
// cannot be supported
// If MAX_OTG_V_COUNT < DRR trigger < v_total_min < v_total_max,
// DRR trigger will drop the vtotal counting directly to a new frame.
// But it should trigger between v_total_min and v_total_max.
ASSERT(0);
REG_UPDATE_4(OTG_V_TOTAL_CONTROL,
OTG_SET_V_TOTAL_MIN_MASK, 0,
OTG_V_TOTAL_MIN_SEL, 0,
OTG_V_TOTAL_MAX_SEL, 0,
OTG_FORCE_LOCK_ON_EVENT, 0);
if (optc->funcs && optc->funcs->set_vtotal_min_max)
optc->funcs->set_vtotal_min_max(optc, 0, 0);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL, max_otg_v_total);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL2, 0);
} else {
// For total_min <= MAX_OTG_V_COUNT and total_max > MAX_OTG_V_COUNT
vcount_stop = params->vertical_total_min;
vcount_stop_timer = params->vertical_total_max - max_otg_v_total;
// Example:
// params->vertical_total_min 1000
// params->vertical_total_max 2000
// MAX_OTG_V_COUNT_STOP = 1500
//
// If DRR event not happened,
// time 0,1,2,3,4,...1000,1001,........,1500,1501,1502, ...1999
// vcount 0,1,2,3,4....1000...................,1001,1002,1003,...1399
// vcount2 0,1,2,3,4,..499,
// else (DRR event happened, ex : at line 1004)
// time 0,1,2,3,4,...1000,1001.....1004, 0
// vcount 0,1,2,3,4....1000,.............. 0 (new frame)
// vcount2 0,1,2, 3, -
if (optc->funcs && optc->funcs->set_vtotal_min_max)
optc->funcs->set_vtotal_min_max(optc,
params->vertical_total_min - 1, max_otg_v_total);
optc35_setup_manual_trigger(optc);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL, vcount_stop);
REG_WRITE(OTG_V_COUNT_STOP_CONTROL2, vcount_stop_timer);
}
}
}
static struct timing_generator_funcs dcn35_tg_funcs = {
.validate_timing = optc1_validate_timing,
.program_timing = optc1_program_timing,
......@@ -245,7 +407,7 @@ static struct timing_generator_funcs dcn35_tg_funcs = {
.lock_doublebuffer_enable = optc3_lock_doublebuffer_enable,
.lock_doublebuffer_disable = optc3_lock_doublebuffer_disable,
.enable_optc_clock = optc1_enable_optc_clock,
.set_drr = optc31_set_drr,
.set_drr = optc35_set_drr,
.get_last_used_drr_vtotal = optc2_get_last_used_drr_vtotal,
.set_vtotal_min_max = optc1_set_vtotal_min_max,
.set_static_screen_control = optc1_set_static_screen_control,
......@@ -275,6 +437,7 @@ static struct timing_generator_funcs dcn35_tg_funcs = {
.setup_manual_trigger = optc2_setup_manual_trigger,
.get_hw_timing = optc1_get_hw_timing,
.init_odm = optc3_init_odm,
.set_long_vtotal = optc35_set_long_vtotal,
};
void dcn35_timing_generator_init(struct optc *optc1)
......
......@@ -65,10 +65,14 @@
SF(OTG0_OTG_CRC1_WINDOWB_X_CONTROL_READBACK, OTG_CRC1_WINDOWB_X_END_READBACK, mask_sh),\
SF(OTG0_OTG_CRC1_WINDOWB_Y_CONTROL_READBACK, OTG_CRC1_WINDOWB_Y_START_READBACK, mask_sh),\
SF(OTG0_OTG_CRC1_WINDOWB_Y_CONTROL_READBACK, OTG_CRC1_WINDOWB_Y_END_READBACK, mask_sh),\
SF(OPTC_CLOCK_CONTROL, OPTC_FGCG_REP_DIS, mask_sh)
SF(OPTC_CLOCK_CONTROL, OPTC_FGCG_REP_DIS, mask_sh),\
SF(OTG0_OTG_V_COUNT_STOP_CONTROL, OTG_V_COUNT_STOP, mask_sh),\
SF(OTG0_OTG_V_COUNT_STOP_CONTROL2, OTG_V_COUNT_STOP_TIMER, mask_sh)
void dcn35_timing_generator_init(struct optc *optc1);
void dcn35_timing_generator_set_fgcg(struct optc *optc1, bool enable);
void optc35_set_drr(struct timing_generator *optc, const struct drr_params *params);
#endif /* __DC_OPTC_DCN35_H__ */
......@@ -240,6 +240,8 @@ struct resource_pool *dcn35_create_resource_pool(
SRI_ARR(OTG_V_TOTAL_MAX, OTG, inst),\
SRI_ARR(OTG_V_TOTAL_MIN, OTG, inst),\
SRI_ARR(OTG_V_TOTAL_CONTROL, OTG, inst),\
SRI_ARR(OTG_V_COUNT_STOP_CONTROL, OTG, inst),\
SRI_ARR(OTG_V_COUNT_STOP_CONTROL2, OTG, inst),\
SRI_ARR(OTG_TRIGA_CNTL, OTG, inst),\
SRI_ARR(OTG_FORCE_COUNT_NOW_CNTL, OTG, inst),\
SRI_ARR(OTG_STATIC_SCREEN_CONTROL, OTG, inst),\
......
......@@ -1126,6 +1126,8 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync,
in_out_vrr->adjust.v_total_min = stream->timing.v_total;
in_out_vrr->adjust.v_total_max = stream->timing.v_total;
}
in_out_vrr->adjust.allow_otg_v_count_halt = (in_config->state == VRR_STATE_ACTIVE_FIXED) ? true : false;
}
void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment