drm/amd/display: Synchronize displays with different timings
[why] Vendor based fan noise improvement [how] Report timing synchronizable when DP streams time frame difference is less than 0.05 percent. Adjust DP DTOs and sync displays using MASTER_UPDATE_LOCK_DB_X_Y Signed-off-by: Vladimir Stempen <vladimir.stempen@amd.com> Acked-by: Bindu Ramamurthy <bindu.r@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
860b0cf52a
commit
77a2b7265f
@ -1102,6 +1102,7 @@ static void program_timing_sync(
|
||||
|
||||
for (i = 0; i < pipe_count; i++) {
|
||||
int group_size = 1;
|
||||
enum timing_synchronization_type sync_type = NOT_SYNCHRONIZABLE;
|
||||
struct pipe_ctx *pipe_set[MAX_PIPES];
|
||||
|
||||
if (!unsynced_pipes[i])
|
||||
@ -1116,10 +1117,22 @@ static void program_timing_sync(
|
||||
for (j = i + 1; j < pipe_count; j++) {
|
||||
if (!unsynced_pipes[j])
|
||||
continue;
|
||||
|
||||
if (resource_are_streams_timing_synchronizable(
|
||||
if (sync_type != TIMING_SYNCHRONIZABLE &&
|
||||
dc->hwss.enable_vblanks_synchronization &&
|
||||
unsynced_pipes[j]->stream_res.tg->funcs->align_vblanks &&
|
||||
resource_are_vblanks_synchronizable(
|
||||
unsynced_pipes[j]->stream,
|
||||
pipe_set[0]->stream)) {
|
||||
sync_type = VBLANK_SYNCHRONIZABLE;
|
||||
pipe_set[group_size] = unsynced_pipes[j];
|
||||
unsynced_pipes[j] = NULL;
|
||||
group_size++;
|
||||
} else
|
||||
if (sync_type != VBLANK_SYNCHRONIZABLE &&
|
||||
resource_are_streams_timing_synchronizable(
|
||||
unsynced_pipes[j]->stream,
|
||||
pipe_set[0]->stream)) {
|
||||
sync_type = TIMING_SYNCHRONIZABLE;
|
||||
pipe_set[group_size] = unsynced_pipes[j];
|
||||
unsynced_pipes[j] = NULL;
|
||||
group_size++;
|
||||
@ -1145,7 +1158,6 @@ static void program_timing_sync(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (k = 0; k < group_size; k++) {
|
||||
struct dc_stream_status *status = dc_stream_get_status_from_state(ctx, pipe_set[k]->stream);
|
||||
|
||||
@ -1175,8 +1187,14 @@ static void program_timing_sync(
|
||||
}
|
||||
|
||||
if (group_size > 1) {
|
||||
dc->hwss.enable_timing_synchronization(
|
||||
dc, group_index, group_size, pipe_set);
|
||||
if (sync_type == TIMING_SYNCHRONIZABLE) {
|
||||
dc->hwss.enable_timing_synchronization(
|
||||
dc, group_index, group_size, pipe_set);
|
||||
} else
|
||||
if (sync_type == VBLANK_SYNCHRONIZABLE) {
|
||||
dc->hwss.enable_vblanks_synchronization(
|
||||
dc, group_index, group_size, pipe_set);
|
||||
}
|
||||
group_index++;
|
||||
}
|
||||
num_group++;
|
||||
|
@ -417,6 +417,49 @@ int resource_get_clock_source_reference(
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool resource_are_vblanks_synchronizable(
|
||||
struct dc_stream_state *stream1,
|
||||
struct dc_stream_state *stream2)
|
||||
{
|
||||
uint32_t base60_refresh_rates[] = {10, 20, 5};
|
||||
uint8_t i;
|
||||
uint8_t rr_count = sizeof(base60_refresh_rates)/sizeof(base60_refresh_rates[0]);
|
||||
int64_t frame_time_diff;
|
||||
|
||||
if (stream1->ctx->dc->config.vblank_alignment_dto_params &&
|
||||
stream1->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0 &&
|
||||
dc_is_dp_signal(stream1->signal) &&
|
||||
dc_is_dp_signal(stream2->signal) &&
|
||||
false == stream1->has_non_synchronizable_pclk &&
|
||||
false == stream2->has_non_synchronizable_pclk &&
|
||||
stream1->timing.flags.VBLANK_SYNCHRONIZABLE &&
|
||||
stream2->timing.flags.VBLANK_SYNCHRONIZABLE) {
|
||||
/* disable refresh rates higher than 60Hz for now */
|
||||
if (stream1->timing.pix_clk_100hz*100/stream1->timing.h_total/
|
||||
stream1->timing.v_total > 60)
|
||||
return false;
|
||||
if (stream2->timing.pix_clk_100hz*100/stream2->timing.h_total/
|
||||
stream2->timing.v_total > 60)
|
||||
return false;
|
||||
frame_time_diff = (int64_t)10000 *
|
||||
stream1->timing.h_total *
|
||||
stream1->timing.v_total *
|
||||
stream2->timing.pix_clk_100hz /
|
||||
stream1->timing.pix_clk_100hz /
|
||||
stream2->timing.h_total /
|
||||
stream2->timing.v_total;
|
||||
for (i = 0; i < rr_count; i++) {
|
||||
int64_t diff = (frame_time_diff * base60_refresh_rates[i]) / 10 - 10000;
|
||||
|
||||
if (diff < 0)
|
||||
diff = -diff;
|
||||
if (diff < stream1->ctx->dc->config.vblank_alignment_max_frame_time_diff)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool resource_are_streams_timing_synchronizable(
|
||||
struct dc_stream_state *stream1,
|
||||
struct dc_stream_state *stream2)
|
||||
|
@ -301,6 +301,8 @@ struct dc_config {
|
||||
#if defined(CONFIG_DRM_AMD_DC_DCN)
|
||||
bool clamp_min_dcfclk;
|
||||
#endif
|
||||
uint64_t vblank_alignment_dto_params;
|
||||
uint8_t vblank_alignment_max_frame_time_diff;
|
||||
};
|
||||
|
||||
enum visual_confirm {
|
||||
@ -528,6 +530,7 @@ struct dc_debug_options {
|
||||
bool disable_dsc;
|
||||
bool enable_dram_clock_change_one_display_vactive;
|
||||
union mem_low_power_enable_options enable_mem_low_power;
|
||||
bool force_vblank_alignment;
|
||||
};
|
||||
|
||||
struct dc_debug_data {
|
||||
|
@ -705,6 +705,7 @@ struct dc_crtc_timing_flags {
|
||||
#ifndef TRIM_FSFT
|
||||
uint32_t FAST_TRANSPORT: 1;
|
||||
#endif
|
||||
uint32_t VBLANK_SYNCHRONIZABLE: 1;
|
||||
};
|
||||
|
||||
enum dc_timing_3d_format {
|
||||
|
@ -228,6 +228,9 @@ struct dc_stream_state {
|
||||
uint32_t stream_id;
|
||||
bool is_dsc_enabled;
|
||||
union stream_update_flags update_flags;
|
||||
|
||||
bool has_non_synchronizable_pclk;
|
||||
bool vblank_synchronized;
|
||||
};
|
||||
|
||||
#define ABM_LEVEL_IMMEDIATE_DISABLE 255
|
||||
|
@ -1002,15 +1002,27 @@ static bool get_pixel_clk_frequency_100hz(
|
||||
{
|
||||
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source);
|
||||
unsigned int clock_hz = 0;
|
||||
unsigned int modulo_hz = 0;
|
||||
|
||||
if (clock_source->id == CLOCK_SOURCE_ID_DP_DTO) {
|
||||
clock_hz = REG_READ(PHASE[inst]);
|
||||
|
||||
/* NOTE: There is agreement with VBIOS here that MODULO is
|
||||
* programmed equal to DPREFCLK, in which case PHASE will be
|
||||
* equivalent to pixel clock.
|
||||
*/
|
||||
*pixel_clk_khz = clock_hz / 100;
|
||||
if (clock_source->ctx->dc->hwss.enable_vblanks_synchronization &&
|
||||
clock_source->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0) {
|
||||
/* NOTE: In case VBLANK syncronization is enabled, MODULO may
|
||||
* not be programmed equal to DPREFCLK
|
||||
*/
|
||||
modulo_hz = REG_READ(MODULO[inst]);
|
||||
*pixel_clk_khz = ((uint64_t)clock_hz*
|
||||
clock_source->ctx->dc->clk_mgr->dprefclk_khz*10)/
|
||||
modulo_hz;
|
||||
} else {
|
||||
/* NOTE: There is agreement with VBIOS here that MODULO is
|
||||
* programmed equal to DPREFCLK, in which case PHASE will be
|
||||
* equivalent to pixel clock.
|
||||
*/
|
||||
*pixel_clk_khz = clock_hz / 100;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1074,8 +1086,35 @@ static bool dcn20_program_pix_clk(
|
||||
struct pixel_clk_params *pix_clk_params,
|
||||
struct pll_settings *pll_settings)
|
||||
{
|
||||
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source);
|
||||
unsigned int inst = pix_clk_params->controller_id - CONTROLLER_ID_D0;
|
||||
|
||||
dce112_program_pix_clk(clock_source, pix_clk_params, pll_settings);
|
||||
|
||||
if (clock_source->ctx->dc->hwss.enable_vblanks_synchronization &&
|
||||
clock_source->ctx->dc->config.vblank_alignment_max_frame_time_diff > 0) {
|
||||
/* NOTE: In case VBLANK syncronization is enabled,
|
||||
* we need to set modulo to default DPREFCLK first
|
||||
* dce112_program_pix_clk does not set default DPREFCLK
|
||||
*/
|
||||
REG_WRITE(MODULO[inst],
|
||||
clock_source->ctx->dc->clk_mgr->dprefclk_khz*1000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dcn20_override_dp_pix_clk(
|
||||
struct clock_source *clock_source,
|
||||
unsigned int inst,
|
||||
unsigned int pixel_clk,
|
||||
unsigned int ref_clk)
|
||||
{
|
||||
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(clock_source);
|
||||
|
||||
REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 0);
|
||||
REG_WRITE(PHASE[inst], pixel_clk);
|
||||
REG_WRITE(MODULO[inst], ref_clk);
|
||||
REG_UPDATE(PIXEL_RATE_CNTL[inst], DP_DTO0_ENABLE, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1083,7 +1122,8 @@ static const struct clock_source_funcs dcn20_clk_src_funcs = {
|
||||
.cs_power_down = dce110_clock_source_power_down,
|
||||
.program_pix_clk = dcn20_program_pix_clk,
|
||||
.get_pix_clk_dividers = dce112_get_pix_clk_dividers,
|
||||
.get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz
|
||||
.get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz,
|
||||
.override_dp_pix_clk = dcn20_override_dp_pix_clk
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DRM_AMD_DC_DCN)
|
||||
|
@ -1851,6 +1851,225 @@ static bool wait_for_reset_trigger_to_occur(
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint64_t reduceSizeAndFraction(
|
||||
uint64_t *numerator,
|
||||
uint64_t *denominator,
|
||||
bool checkUint32Bounary)
|
||||
{
|
||||
int i;
|
||||
bool ret = checkUint32Bounary == false;
|
||||
uint64_t max_int32 = 0xffffffff;
|
||||
uint64_t num, denom;
|
||||
static const uint16_t prime_numbers[] = {
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
|
||||
47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
|
||||
107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
|
||||
167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
|
||||
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
|
||||
283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353,
|
||||
359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,
|
||||
431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487,
|
||||
491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
|
||||
571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631,
|
||||
641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
|
||||
709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773,
|
||||
787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857,
|
||||
859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937,
|
||||
941, 947, 953, 967, 971, 977, 983, 991, 997};
|
||||
int count = ARRAY_SIZE(prime_numbers);
|
||||
|
||||
num = *numerator;
|
||||
denom = *denominator;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (checkUint32Bounary &&
|
||||
num <= max_int32 && denom <= max_int32) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
while (num % prime_numbers[i] == 0 &&
|
||||
denom % prime_numbers[i] == 0) {
|
||||
num /= prime_numbers[i];
|
||||
denom /= prime_numbers[i];
|
||||
}
|
||||
}
|
||||
*numerator = num;
|
||||
*denominator = denom;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool is_low_refresh_rate(struct pipe_ctx *pipe)
|
||||
{
|
||||
uint32_t master_pipe_refresh_rate =
|
||||
pipe->stream->timing.pix_clk_100hz * 100 /
|
||||
pipe->stream->timing.h_total /
|
||||
pipe->stream->timing.v_total;
|
||||
return master_pipe_refresh_rate <= 30;
|
||||
}
|
||||
|
||||
uint8_t get_clock_divider(struct pipe_ctx *pipe, bool account_low_refresh_rate)
|
||||
{
|
||||
uint32_t clock_divider = 1;
|
||||
uint32_t numpipes = 1;
|
||||
|
||||
if (account_low_refresh_rate && is_low_refresh_rate(pipe))
|
||||
clock_divider *= 2;
|
||||
|
||||
if (pipe->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420)
|
||||
clock_divider *= 2;
|
||||
|
||||
while (pipe->next_odm_pipe) {
|
||||
pipe = pipe->next_odm_pipe;
|
||||
numpipes++;
|
||||
}
|
||||
clock_divider *= numpipes;
|
||||
|
||||
return clock_divider;
|
||||
}
|
||||
|
||||
int dcn10_align_pixel_clocks(
|
||||
struct dc *dc,
|
||||
int group_size,
|
||||
struct pipe_ctx *grouped_pipes[])
|
||||
{
|
||||
struct dc_context *dc_ctx = dc->ctx;
|
||||
int i, master = -1, embedded = -1;
|
||||
struct dc_crtc_timing hw_crtc_timing[MAX_PIPES] = {0};
|
||||
uint64_t phase[MAX_PIPES];
|
||||
uint64_t modulo[MAX_PIPES];
|
||||
unsigned int pclk;
|
||||
|
||||
uint32_t embedded_pix_clk_100hz;
|
||||
uint16_t embedded_h_total;
|
||||
uint16_t embedded_v_total;
|
||||
bool clamshell_closed = false;
|
||||
uint32_t dp_ref_clk_100hz =
|
||||
dc->res_pool->dp_clock_source->ctx->dc->clk_mgr->dprefclk_khz*10;
|
||||
|
||||
if (dc->config.vblank_alignment_dto_params &&
|
||||
dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk) {
|
||||
clamshell_closed =
|
||||
(dc->config.vblank_alignment_dto_params >> 63);
|
||||
embedded_h_total =
|
||||
(dc->config.vblank_alignment_dto_params >> 32) & 0x7FFF;
|
||||
embedded_v_total =
|
||||
(dc->config.vblank_alignment_dto_params >> 48) & 0x7FFF;
|
||||
embedded_pix_clk_100hz =
|
||||
dc->config.vblank_alignment_dto_params & 0xFFFFFFFF;
|
||||
|
||||
for (i = 0; i < group_size; i++) {
|
||||
grouped_pipes[i]->stream_res.tg->funcs->get_hw_timing(
|
||||
grouped_pipes[i]->stream_res.tg,
|
||||
&hw_crtc_timing[i]);
|
||||
dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
|
||||
dc->res_pool->dp_clock_source,
|
||||
grouped_pipes[i]->stream_res.tg->inst,
|
||||
&pclk);
|
||||
hw_crtc_timing[i].pix_clk_100hz = pclk;
|
||||
if (dc_is_embedded_signal(
|
||||
grouped_pipes[i]->stream->signal)) {
|
||||
embedded = i;
|
||||
master = i;
|
||||
phase[i] = embedded_pix_clk_100hz*100;
|
||||
modulo[i] = dp_ref_clk_100hz*100;
|
||||
} else {
|
||||
|
||||
phase[i] = (uint64_t)embedded_pix_clk_100hz*
|
||||
hw_crtc_timing[i].h_total*
|
||||
hw_crtc_timing[i].v_total/
|
||||
get_clock_divider(grouped_pipes[i], true);
|
||||
modulo[i] = (uint64_t)dp_ref_clk_100hz*
|
||||
embedded_h_total*
|
||||
embedded_v_total;
|
||||
|
||||
if (reduceSizeAndFraction(&phase[i],
|
||||
&modulo[i], true) == false) {
|
||||
/*
|
||||
* this will help to stop reporting
|
||||
* this timing synchronizable
|
||||
*/
|
||||
DC_SYNC_INFO("Failed to reduce DTO parameters\n");
|
||||
grouped_pipes[i]->stream->has_non_synchronizable_pclk = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < group_size; i++) {
|
||||
if (i != embedded && !grouped_pipes[i]->stream->has_non_synchronizable_pclk) {
|
||||
dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk(
|
||||
dc->res_pool->dp_clock_source,
|
||||
grouped_pipes[i]->stream_res.tg->inst,
|
||||
phase[i], modulo[i]);
|
||||
dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
|
||||
dc->res_pool->dp_clock_source,
|
||||
grouped_pipes[i]->stream_res.tg->inst, &pclk);
|
||||
grouped_pipes[i]->stream->timing.pix_clk_100hz =
|
||||
pclk*get_clock_divider(grouped_pipes[i], false);
|
||||
if (master == -1)
|
||||
master = i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return master;
|
||||
}
|
||||
|
||||
void dcn10_enable_vblanks_synchronization(
|
||||
struct dc *dc,
|
||||
int group_index,
|
||||
int group_size,
|
||||
struct pipe_ctx *grouped_pipes[])
|
||||
{
|
||||
struct dc_context *dc_ctx = dc->ctx;
|
||||
struct output_pixel_processor *opp;
|
||||
struct timing_generator *tg;
|
||||
int i, width, height, master;
|
||||
|
||||
for (i = 1; i < group_size; i++) {
|
||||
opp = grouped_pipes[i]->stream_res.opp;
|
||||
tg = grouped_pipes[i]->stream_res.tg;
|
||||
tg->funcs->get_otg_active_size(tg, &width, &height);
|
||||
if (opp->funcs->opp_program_dpg_dimensions)
|
||||
opp->funcs->opp_program_dpg_dimensions(opp, width, 2*(height) + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < group_size; i++) {
|
||||
if (grouped_pipes[i]->stream == NULL)
|
||||
continue;
|
||||
grouped_pipes[i]->stream->vblank_synchronized = false;
|
||||
grouped_pipes[i]->stream->has_non_synchronizable_pclk = false;
|
||||
}
|
||||
|
||||
DC_SYNC_INFO("Aligning DP DTOs\n");
|
||||
|
||||
master = dcn10_align_pixel_clocks(dc, group_size, grouped_pipes);
|
||||
|
||||
DC_SYNC_INFO("Synchronizing VBlanks\n");
|
||||
|
||||
if (master >= 0) {
|
||||
for (i = 0; i < group_size; i++) {
|
||||
if (i != master && !grouped_pipes[i]->stream->has_non_synchronizable_pclk)
|
||||
grouped_pipes[i]->stream_res.tg->funcs->align_vblanks(
|
||||
grouped_pipes[master]->stream_res.tg,
|
||||
grouped_pipes[i]->stream_res.tg,
|
||||
grouped_pipes[master]->stream->timing.pix_clk_100hz,
|
||||
grouped_pipes[i]->stream->timing.pix_clk_100hz,
|
||||
get_clock_divider(grouped_pipes[master], false),
|
||||
get_clock_divider(grouped_pipes[i], false));
|
||||
grouped_pipes[i]->stream->vblank_synchronized = true;
|
||||
}
|
||||
grouped_pipes[master]->stream->vblank_synchronized = true;
|
||||
DC_SYNC_INFO("Sync complete\n");
|
||||
}
|
||||
|
||||
for (i = 1; i < group_size; i++) {
|
||||
opp = grouped_pipes[i]->stream_res.opp;
|
||||
tg = grouped_pipes[i]->stream_res.tg;
|
||||
tg->funcs->get_otg_active_size(tg, &width, &height);
|
||||
if (opp->funcs->opp_program_dpg_dimensions)
|
||||
opp->funcs->opp_program_dpg_dimensions(opp, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void dcn10_enable_timing_synchronization(
|
||||
struct dc *dc,
|
||||
int group_index,
|
||||
@ -1872,6 +2091,12 @@ void dcn10_enable_timing_synchronization(
|
||||
opp->funcs->opp_program_dpg_dimensions(opp, width, 2*(height) + 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < group_size; i++) {
|
||||
if (grouped_pipes[i]->stream == NULL)
|
||||
continue;
|
||||
grouped_pipes[i]->stream->vblank_synchronized = false;
|
||||
}
|
||||
|
||||
for (i = 1; i < group_size; i++)
|
||||
grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger(
|
||||
grouped_pipes[i]->stream_res.tg,
|
||||
|
@ -123,6 +123,11 @@ void dcn10_enable_timing_synchronization(
|
||||
int group_index,
|
||||
int group_size,
|
||||
struct pipe_ctx *grouped_pipes[]);
|
||||
void dcn10_enable_vblanks_synchronization(
|
||||
struct dc *dc,
|
||||
int group_index,
|
||||
int group_size,
|
||||
struct pipe_ctx *grouped_pipes[]);
|
||||
void dcn10_enable_per_frame_crtc_position_reset(
|
||||
struct dc *dc,
|
||||
int group_size,
|
||||
|
@ -212,6 +212,7 @@ struct dcn_optc_registers {
|
||||
SF(OTG0_OTG_CONTROL, OTG_START_POINT_CNTL, mask_sh),\
|
||||
SF(OTG0_OTG_CONTROL, OTG_DISABLE_POINT_CNTL, mask_sh),\
|
||||
SF(OTG0_OTG_CONTROL, OTG_FIELD_NUMBER_CNTL, mask_sh),\
|
||||
SF(OTG0_OTG_CONTROL, OTG_CURRENT_MASTER_EN_STATE, mask_sh),\
|
||||
SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_EN, mask_sh),\
|
||||
SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_LINE_NUM, mask_sh),\
|
||||
SF(OTG0_OTG_STEREO_CONTROL, OTG_STEREO_SYNC_OUTPUT_POLARITY, mask_sh),\
|
||||
@ -352,6 +353,7 @@ struct dcn_optc_registers {
|
||||
type OTG_START_POINT_CNTL;\
|
||||
type OTG_DISABLE_POINT_CNTL;\
|
||||
type OTG_FIELD_NUMBER_CNTL;\
|
||||
type OTG_CURRENT_MASTER_EN_STATE;\
|
||||
type OTG_STEREO_EN;\
|
||||
type OTG_STEREO_SYNC_OUTPUT_LINE_NUM;\
|
||||
type OTG_STEREO_SYNC_OUTPUT_POLARITY;\
|
||||
|
@ -738,7 +738,6 @@ void enc1_stream_encoder_update_dp_info_packets(
|
||||
REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, info_frame->spd.valid);
|
||||
REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, info_frame->hdrsmd.valid);
|
||||
|
||||
|
||||
/* This bit is the master enable bit.
|
||||
* When enabling secondary stream engine,
|
||||
* this master bit must also be set.
|
||||
|
@ -42,6 +42,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = {
|
||||
.program_output_csc = dcn20_program_output_csc,
|
||||
.enable_accelerated_mode = dce110_enable_accelerated_mode,
|
||||
.enable_timing_synchronization = dcn10_enable_timing_synchronization,
|
||||
.enable_vblanks_synchronization = dcn10_enable_vblanks_synchronization,
|
||||
.enable_per_frame_crtc_position_reset = dcn10_enable_per_frame_crtc_position_reset,
|
||||
.update_info_frame = dce110_update_info_frame,
|
||||
.send_immediate_sdp_message = dcn10_send_immediate_sdp_message,
|
||||
|
@ -309,6 +309,128 @@ void optc2_set_dwb_source(struct timing_generator *optc,
|
||||
OPTC_DWB1_SOURCE_SELECT, optc->inst);
|
||||
}
|
||||
|
||||
void optc2_align_vblanks(
|
||||
struct timing_generator *optc_master,
|
||||
struct timing_generator *optc_slave,
|
||||
uint32_t master_pixel_clock_100Hz,
|
||||
uint32_t slave_pixel_clock_100Hz,
|
||||
uint8_t master_clock_divider,
|
||||
uint8_t slave_clock_divider)
|
||||
{
|
||||
/* accessing slave OTG registers */
|
||||
struct optc *optc1 = DCN10TG_FROM_TG(optc_slave);
|
||||
|
||||
uint32_t master_v_active = 0;
|
||||
uint32_t master_h_total = 0;
|
||||
uint32_t slave_h_total = 0;
|
||||
uint64_t L, XY, p = 10000;
|
||||
uint32_t X, Y;
|
||||
uint32_t master_update_lock;
|
||||
|
||||
/* disable slave OTG */
|
||||
REG_UPDATE(OTG_CONTROL, OTG_MASTER_EN, 0);
|
||||
/* wait until disabled */
|
||||
REG_WAIT(OTG_CONTROL,
|
||||
OTG_CURRENT_MASTER_EN_STATE,
|
||||
0, 10, 5000);
|
||||
|
||||
REG_GET(OTG_H_TOTAL, OTG_H_TOTAL, &slave_h_total);
|
||||
|
||||
/* assign slave OTG to be controlled by master update lock */
|
||||
REG_SET(OTG_GLOBAL_CONTROL0, 0,
|
||||
OTG_MASTER_UPDATE_LOCK_SEL, optc_master->inst);
|
||||
|
||||
/* accessing master OTG registers */
|
||||
optc1 = DCN10TG_FROM_TG(optc_master);
|
||||
|
||||
/* saving update lock state, not sure if it's needed */
|
||||
REG_GET(OTG_MASTER_UPDATE_LOCK,
|
||||
OTG_MASTER_UPDATE_LOCK, &master_update_lock);
|
||||
/* unlocking master OTG */
|
||||
REG_SET(OTG_MASTER_UPDATE_LOCK, 0,
|
||||
OTG_MASTER_UPDATE_LOCK, 0);
|
||||
|
||||
REG_GET(OTG_V_BLANK_START_END,
|
||||
OTG_V_BLANK_START, &master_v_active);
|
||||
REG_GET(OTG_H_TOTAL, OTG_H_TOTAL, &master_h_total);
|
||||
|
||||
/* calculate when to enable slave OTG */
|
||||
L = p * slave_h_total * master_pixel_clock_100Hz /
|
||||
master_h_total / slave_pixel_clock_100Hz;
|
||||
XY = L / p;
|
||||
Y = master_v_active - XY - 1;
|
||||
X = ((XY + 1) * p - L) * master_h_total / master_clock_divider / p;
|
||||
|
||||
/*
|
||||
* set master OTG to unlock when V/H
|
||||
* counters reach calculated values
|
||||
*/
|
||||
REG_UPDATE(OTG_GLOBAL_CONTROL1,
|
||||
MASTER_UPDATE_LOCK_DB_EN, 1);
|
||||
REG_UPDATE_2(OTG_GLOBAL_CONTROL1,
|
||||
MASTER_UPDATE_LOCK_DB_X,
|
||||
X,
|
||||
MASTER_UPDATE_LOCK_DB_Y,
|
||||
Y);
|
||||
|
||||
/* lock master OTG */
|
||||
REG_SET(OTG_MASTER_UPDATE_LOCK, 0,
|
||||
OTG_MASTER_UPDATE_LOCK, 1);
|
||||
REG_WAIT(OTG_MASTER_UPDATE_LOCK,
|
||||
UPDATE_LOCK_STATUS, 1, 1, 10);
|
||||
|
||||
/* accessing slave OTG registers */
|
||||
optc1 = DCN10TG_FROM_TG(optc_slave);
|
||||
|
||||
/*
|
||||
* enable slave OTG, the OTG is locked with
|
||||
* master's update lock, so it will not run
|
||||
*/
|
||||
REG_UPDATE(OTG_CONTROL,
|
||||
OTG_MASTER_EN, 1);
|
||||
|
||||
/* accessing master OTG registers */
|
||||
optc1 = DCN10TG_FROM_TG(optc_master);
|
||||
|
||||
/*
|
||||
* unlock master OTG. When master H/V counters reach
|
||||
* DB_XY point, slave OTG will start
|
||||
*/
|
||||
REG_SET(OTG_MASTER_UPDATE_LOCK, 0,
|
||||
OTG_MASTER_UPDATE_LOCK, 0);
|
||||
|
||||
/* accessing slave OTG registers */
|
||||
optc1 = DCN10TG_FROM_TG(optc_slave);
|
||||
|
||||
/* wait for slave OTG to start running*/
|
||||
REG_WAIT(OTG_CONTROL,
|
||||
OTG_CURRENT_MASTER_EN_STATE,
|
||||
1, 10, 5000);
|
||||
|
||||
/* accessing master OTG registers */
|
||||
optc1 = DCN10TG_FROM_TG(optc_master);
|
||||
|
||||
/* disable the XY point*/
|
||||
REG_UPDATE(OTG_GLOBAL_CONTROL1,
|
||||
MASTER_UPDATE_LOCK_DB_EN, 0);
|
||||
REG_UPDATE_2(OTG_GLOBAL_CONTROL1,
|
||||
MASTER_UPDATE_LOCK_DB_X,
|
||||
0,
|
||||
MASTER_UPDATE_LOCK_DB_Y,
|
||||
0);
|
||||
|
||||
/*restore master update lock*/
|
||||
REG_SET(OTG_MASTER_UPDATE_LOCK, 0,
|
||||
OTG_MASTER_UPDATE_LOCK, master_update_lock);
|
||||
|
||||
/* accessing slave OTG registers */
|
||||
optc1 = DCN10TG_FROM_TG(optc_slave);
|
||||
/* restore slave to be controlled by it's own */
|
||||
REG_SET(OTG_GLOBAL_CONTROL0, 0,
|
||||
OTG_MASTER_UPDATE_LOCK_SEL, optc_slave->inst);
|
||||
|
||||
}
|
||||
|
||||
void optc2_triplebuffer_lock(struct timing_generator *optc)
|
||||
{
|
||||
struct optc *optc1 = DCN10TG_FROM_TG(optc);
|
||||
@ -468,6 +590,7 @@ static struct timing_generator_funcs dcn20_tg_funcs = {
|
||||
.program_manual_trigger = optc2_program_manual_trigger,
|
||||
.setup_manual_trigger = optc2_setup_manual_trigger,
|
||||
.get_hw_timing = optc1_get_hw_timing,
|
||||
.align_vblanks = optc2_align_vblanks,
|
||||
};
|
||||
|
||||
void dcn20_timing_generator_init(struct optc *optc1)
|
||||
|
@ -2033,9 +2033,13 @@ int dcn20_populate_dml_pipes_from_context(
|
||||
if (res_ctx->pipe_ctx[pipe_cnt].stream == res_ctx->pipe_ctx[i].stream)
|
||||
continue;
|
||||
|
||||
if (dc->debug.disable_timing_sync || !resource_are_streams_timing_synchronizable(
|
||||
if (dc->debug.disable_timing_sync ||
|
||||
(!resource_are_streams_timing_synchronizable(
|
||||
res_ctx->pipe_ctx[pipe_cnt].stream,
|
||||
res_ctx->pipe_ctx[i].stream)) {
|
||||
res_ctx->pipe_ctx[i].stream) &&
|
||||
!resource_are_vblanks_synchronizable(
|
||||
res_ctx->pipe_ctx[pipe_cnt].stream,
|
||||
res_ctx->pipe_ctx[i].stream))) {
|
||||
synchronized_vblank = false;
|
||||
break;
|
||||
}
|
||||
|
@ -454,7 +454,6 @@ static void enc3_stream_encoder_update_dp_info_packets(
|
||||
REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, info_frame->spd.valid);
|
||||
REG_UPDATE(DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, info_frame->hdrsmd.valid);
|
||||
|
||||
|
||||
/* This bit is the master enable bit.
|
||||
* When enabling secondary stream engine,
|
||||
* this master bit must also be set.
|
||||
|
@ -170,6 +170,11 @@ struct clock_source_funcs {
|
||||
const struct clock_source *clock_source,
|
||||
unsigned int inst,
|
||||
unsigned int *pixel_clk_khz);
|
||||
bool (*override_dp_pix_clk)(
|
||||
struct clock_source *clock_source,
|
||||
unsigned int inst,
|
||||
unsigned int pixel_clk,
|
||||
unsigned int ref_clk);
|
||||
};
|
||||
|
||||
struct clock_source {
|
||||
|
@ -109,6 +109,12 @@ enum h_timing_div_mode {
|
||||
H_TIMING_DIV_BY4,
|
||||
};
|
||||
|
||||
enum timing_synchronization_type {
|
||||
NOT_SYNCHRONIZABLE,
|
||||
TIMING_SYNCHRONIZABLE,
|
||||
VBLANK_SYNCHRONIZABLE
|
||||
};
|
||||
|
||||
struct crc_params {
|
||||
/* Regions used to calculate CRC*/
|
||||
uint16_t windowa_x_start;
|
||||
@ -292,6 +298,12 @@ struct timing_generator_funcs {
|
||||
uint32_t window_start, uint32_t window_end);
|
||||
void (*set_vtotal_change_limit)(struct timing_generator *optc,
|
||||
uint32_t limit);
|
||||
void (*align_vblanks)(struct timing_generator *master_optc,
|
||||
struct timing_generator *slave_optc,
|
||||
uint32_t master_pixel_clock_100Hz,
|
||||
uint32_t slave_pixel_clock_100Hz,
|
||||
uint8_t master_clock_divider,
|
||||
uint8_t slave_clock_divider);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -111,6 +111,9 @@ struct hw_sequencer_funcs {
|
||||
void (*enable_timing_synchronization)(struct dc *dc,
|
||||
int group_index, int group_size,
|
||||
struct pipe_ctx *grouped_pipes[]);
|
||||
void (*enable_vblanks_synchronization)(struct dc *dc,
|
||||
int group_index, int group_size,
|
||||
struct pipe_ctx *grouped_pipes[]);
|
||||
void (*setup_periodic_interrupt)(struct dc *dc,
|
||||
struct pipe_ctx *pipe_ctx,
|
||||
enum vline_select vline);
|
||||
|
@ -115,6 +115,10 @@ bool resource_are_streams_timing_synchronizable(
|
||||
struct dc_stream_state *stream1,
|
||||
struct dc_stream_state *stream2);
|
||||
|
||||
bool resource_are_vblanks_synchronizable(
|
||||
struct dc_stream_state *stream1,
|
||||
struct dc_stream_state *stream2);
|
||||
|
||||
struct clock_source *resource_find_used_clk_src_for_sharing(
|
||||
struct resource_context *res_ctx,
|
||||
struct pipe_ctx *pipe_ctx);
|
||||
|
Loading…
x
Reference in New Issue
Block a user