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])
                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++;
                        }
                }
 
-
                for (k = 0; k < group_size; k++) {
                        struct dc_stream_status *status = dc_stream_get_status_from_state(ctx, pipe_set[k]->stream);
 
                }
 
                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++;
 
        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)
 
 #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 {
        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 {
 
 #ifndef TRIM_FSFT
        uint32_t FAST_TRANSPORT: 1;
 #endif
+       uint32_t VBLANK_SYNCHRONIZABLE: 1;
 };
 
 enum dc_timing_3d_format {
 
        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
 
 {
        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;
        }
 
                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;
 }
 
        .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)
 
        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,
                        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,
 
                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,
 
        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),\
        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;\
 
        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.
 
        .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,
 
                                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);
                .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)
 
                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;
                }
 
        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.
 
                        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 {
 
        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;
                        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
 
        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);
 
                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);