drm/amd/display: Synchronize displays with different timings
authorVladimir Stempen <vladimir.stempen@amd.com>
Tue, 29 Dec 2020 20:01:12 +0000 (15:01 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 22 Feb 2021 23:05:48 +0000 (18:05 -0500)
[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>
18 files changed:
drivers/gpu/drm/amd/display/dc/core/dc.c
drivers/gpu/drm/amd/display/dc/core/dc_resource.c
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dc_hw_types.h
drivers/gpu/drm/amd/display/dc/dc_stream.h
drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_optc.h
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_stream_encoder.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_optc.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dio_stream_encoder.c
drivers/gpu/drm/amd/display/dc/inc/clock_source.h
drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h
drivers/gpu/drm/amd/display/dc/inc/resource.h

index 0388200..bbf088a 100644 (file)
@@ -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++;
index 0c26c2a..0241c9d 100644 (file)
@@ -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)
index a10daf6..9e63198 100644 (file)
@@ -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 {
index b41e636..48d3ed9 100644 (file)
@@ -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 {
index b791097..a597eca 100644 (file)
@@ -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
index dec58b3..6f47f9b 100644 (file)
@@ -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)
index 361a97e..72b8459 100644 (file)
@@ -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,
index dee8ad1..e0800cd 100644 (file)
@@ -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,
index b222c67..2529723 100644 (file)
@@ -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;\
index 73ac78b..f1a08a7 100644 (file)
@@ -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.
index de9dcbe..7218ed9 100644 (file)
@@ -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,
index d8b18c5..ef6ee03 100644 (file)
@@ -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)
index 2c2dbfc..a65efed 100644 (file)
@@ -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;
                }
index 6c0f7ef..72bee63 100644 (file)
@@ -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.
index 1b01a9a..e2b3a2c 100644 (file)
@@ -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 {
index 754832d..9ff68b6 100644 (file)
@@ -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
index 613b3e3..2fedfca 100644 (file)
@@ -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);
index d89815a..3d97078 100644 (file)
@@ -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);