Commit 77a2b726 authored by Vladimir Stempen's avatar Vladimir Stempen Committed by Alex Deucher

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: default avatarVladimir Stempen <vladimir.stempen@amd.com>
Acked-by: default avatarBindu Ramamurthy <bindu.r@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 860b0cf5
......@@ -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) {
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]);
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);
......
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