]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/gpu/drm/amd/display/dc/core/dc.c
drm/amd/display: Force full update on pixel_format_change
[linux.git] / drivers / gpu / drm / amd / display / dc / core / dc.c
index 7240db2e6f095ebde8d2b685c0dd17b281c8e6d3..a78e0364b1b26a2db121f6cf0de4e7f9b489b85e 100644 (file)
@@ -29,6 +29,7 @@
 #include "core_status.h"
 #include "core_types.h"
 #include "hw_sequencer.h"
+#include "dce/dce_hwseq.h"
 
 #include "resource.h"
 
 /*******************************************************************************
  * Private functions
  ******************************************************************************/
+
+static inline void elevate_update_type(enum surface_update_type *original, enum surface_update_type new)
+{
+       if (new > *original)
+               *original = new;
+}
+
 static void destroy_links(struct dc *dc)
 {
        uint32_t i;
@@ -157,7 +165,7 @@ static bool create_links(
        return false;
 }
 
-static bool stream_adjust_vmin_vmax(struct dc *dc,
+bool dc_stream_adjust_vmin_vmax(struct dc *dc,
                struct dc_stream_state **streams, int num_streams,
                int vmin, int vmax)
 {
@@ -182,7 +190,7 @@ static bool stream_adjust_vmin_vmax(struct dc *dc,
        return ret;
 }
 
-static bool stream_get_crtc_position(struct dc *dc,
+bool dc_stream_get_crtc_position(struct dc *dc,
                struct dc_stream_state **streams, int num_streams,
                unsigned int *v_pos, unsigned int *nom_v_pos)
 {
@@ -207,45 +215,92 @@ static bool stream_get_crtc_position(struct dc *dc,
        return ret;
 }
 
-static bool set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream)
+/**
+ * dc_stream_configure_crc: Configure CRC capture for the given stream.
+ * @dc: DC Object
+ * @stream: The stream to configure CRC on.
+ * @enable: Enable CRC if true, disable otherwise.
+ * @continuous: Capture CRC on every frame if true. Otherwise, only capture
+ *              once.
+ *
+ * By default, only CRC0 is configured, and the entire frame is used to
+ * calculate the crc.
+ */
+bool dc_stream_configure_crc(struct dc *dc, struct dc_stream_state *stream,
+                            bool enable, bool continuous)
 {
-       int i = 0;
-       bool ret = false;
-       struct pipe_ctx *pipes;
+       int i;
+       struct pipe_ctx *pipe;
+       struct crc_params param;
+       struct timing_generator *tg;
 
        for (i = 0; i < MAX_PIPES; i++) {
-               if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) {
-                       pipes = &dc->current_state->res_ctx.pipe_ctx[i];
-                       dc->hwss.program_gamut_remap(pipes);
-                       ret = true;
-               }
+               pipe = &dc->current_state->res_ctx.pipe_ctx[i];
+               if (pipe->stream == stream)
+                       break;
        }
+       /* Stream not found */
+       if (i == MAX_PIPES)
+               return false;
 
-       return ret;
+       /* Always capture the full frame */
+       param.windowa_x_start = 0;
+       param.windowa_y_start = 0;
+       param.windowa_x_end = pipe->stream->timing.h_addressable;
+       param.windowa_y_end = pipe->stream->timing.v_addressable;
+       param.windowb_x_start = 0;
+       param.windowb_y_start = 0;
+       param.windowb_x_end = pipe->stream->timing.h_addressable;
+       param.windowb_y_end = pipe->stream->timing.v_addressable;
+
+       /* Default to the union of both windows */
+       param.selection = UNION_WINDOW_A_B;
+       param.continuous_mode = continuous;
+       param.enable = enable;
+
+       tg = pipe->stream_res.tg;
+
+       /* Only call if supported */
+       if (tg->funcs->configure_crc)
+               return tg->funcs->configure_crc(tg, &param);
+       dm_logger_write(dc->ctx->logger, LOG_WARNING, "CRC capture not supported.");
+       return false;
 }
 
-static bool program_csc_matrix(struct dc *dc, struct dc_stream_state *stream)
+/**
+ * dc_stream_get_crc: Get CRC values for the given stream.
+ * @dc: DC object
+ * @stream: The DC stream state of the stream to get CRCs from.
+ * @r_cr, g_y, b_cb: CRC values for the three channels are stored here.
+ *
+ * dc_stream_configure_crc needs to be called beforehand to enable CRCs.
+ * Return false if stream is not found, or if CRCs are not enabled.
+ */
+bool dc_stream_get_crc(struct dc *dc, struct dc_stream_state *stream,
+                      uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb)
 {
-       int i = 0;
-       bool ret = false;
-       struct pipe_ctx *pipes;
+       int i;
+       struct pipe_ctx *pipe;
+       struct timing_generator *tg;
 
        for (i = 0; i < MAX_PIPES; i++) {
-               if (dc->current_state->res_ctx.pipe_ctx[i].stream
-                               == stream) {
-
-                       pipes = &dc->current_state->res_ctx.pipe_ctx[i];
-                       dc->hwss.program_csc_matrix(pipes,
-                       stream->output_color_space,
-                       stream->csc_color_matrix.matrix);
-                       ret = true;
-               }
+               pipe = &dc->current_state->res_ctx.pipe_ctx[i];
+               if (pipe->stream == stream)
+                       break;
        }
+       /* Stream not found */
+       if (i == MAX_PIPES)
+               return false;
 
-       return ret;
+       tg = pipe->stream_res.tg;
+
+       if (tg->funcs->get_crc)
+               return tg->funcs->get_crc(tg, r_cr, g_y, b_cb);
+       dm_logger_write(dc->ctx->logger, LOG_WARNING, "CRC capture not supported.");
+       return false;
 }
 
-static void set_static_screen_events(struct dc *dc,
+void dc_stream_set_static_screen_events(struct dc *dc,
                struct dc_stream_state **streams,
                int num_streams,
                const struct dc_static_screen_events *events)
@@ -270,177 +325,6 @@ static void set_static_screen_events(struct dc *dc,
        dc->hwss.set_static_screen_control(pipes_affected, num_pipes_affected, events);
 }
 
-static void set_drive_settings(struct dc *dc,
-               struct link_training_settings *lt_settings,
-               const struct dc_link *link)
-{
-
-       int i;
-
-       for (i = 0; i < dc->link_count; i++) {
-               if (dc->links[i] == link)
-                       break;
-       }
-
-       if (i >= dc->link_count)
-               ASSERT_CRITICAL(false);
-
-       dc_link_dp_set_drive_settings(dc->links[i], lt_settings);
-}
-
-static void perform_link_training(struct dc *dc,
-               struct dc_link_settings *link_setting,
-               bool skip_video_pattern)
-{
-       int i;
-
-       for (i = 0; i < dc->link_count; i++)
-               dc_link_dp_perform_link_training(
-                       dc->links[i],
-                       link_setting,
-                       skip_video_pattern);
-}
-
-static void set_preferred_link_settings(struct dc *dc,
-               struct dc_link_settings *link_setting,
-               struct dc_link *link)
-{
-       link->preferred_link_setting = *link_setting;
-       dp_retrain_link_dp_test(link, link_setting, false);
-}
-
-static void enable_hpd(const struct dc_link *link)
-{
-       dc_link_dp_enable_hpd(link);
-}
-
-static void disable_hpd(const struct dc_link *link)
-{
-       dc_link_dp_disable_hpd(link);
-}
-
-
-static void set_test_pattern(
-               struct dc_link *link,
-               enum dp_test_pattern test_pattern,
-               const struct link_training_settings *p_link_settings,
-               const unsigned char *p_custom_pattern,
-               unsigned int cust_pattern_size)
-{
-       if (link != NULL)
-               dc_link_dp_set_test_pattern(
-                       link,
-                       test_pattern,
-                       p_link_settings,
-                       p_custom_pattern,
-                       cust_pattern_size);
-}
-
-static void set_dither_option(struct dc_stream_state *stream,
-               enum dc_dither_option option)
-{
-       struct bit_depth_reduction_params params;
-       struct dc_link *link = stream->status.link;
-       struct pipe_ctx *pipes = NULL;
-       int i;
-
-       for (i = 0; i < MAX_PIPES; i++) {
-               if (link->dc->current_state->res_ctx.pipe_ctx[i].stream ==
-                               stream) {
-                       pipes = &link->dc->current_state->res_ctx.pipe_ctx[i];
-                       break;
-               }
-       }
-
-       memset(&params, 0, sizeof(params));
-       if (!pipes)
-               return;
-       if (option > DITHER_OPTION_MAX)
-               return;
-
-       stream->dither_option = option;
-
-       resource_build_bit_depth_reduction_params(stream,
-                               &params);
-       stream->bit_depth_params = params;
-       pipes->stream_res.opp->funcs->
-               opp_program_bit_depth_reduction(pipes->stream_res.opp, &params);
-}
-
-void set_dpms(
-       struct dc *dc,
-       struct dc_stream_state *stream,
-       bool dpms_off)
-{
-       struct pipe_ctx *pipe_ctx = NULL;
-       int i;
-
-       for (i = 0; i < MAX_PIPES; i++) {
-               if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) {
-                       pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
-                       break;
-               }
-       }
-
-       if (!pipe_ctx) {
-               ASSERT(0);
-               return;
-       }
-
-       if (stream->dpms_off != dpms_off) {
-               stream->dpms_off = dpms_off;
-               if (dpms_off)
-                       core_link_disable_stream(pipe_ctx,
-                                       KEEP_ACQUIRED_RESOURCE);
-               else
-                       core_link_enable_stream(dc->current_state, pipe_ctx);
-       }
-}
-
-static void allocate_dc_stream_funcs(struct dc  *dc)
-{
-       if (dc->hwss.set_drr != NULL) {
-               dc->stream_funcs.adjust_vmin_vmax =
-                               stream_adjust_vmin_vmax;
-       }
-
-       dc->stream_funcs.set_static_screen_events =
-                       set_static_screen_events;
-
-       dc->stream_funcs.get_crtc_position =
-                       stream_get_crtc_position;
-
-       dc->stream_funcs.set_gamut_remap =
-                       set_gamut_remap;
-
-       dc->stream_funcs.program_csc_matrix =
-                       program_csc_matrix;
-
-       dc->stream_funcs.set_dither_option =
-                       set_dither_option;
-
-       dc->stream_funcs.set_dpms =
-                       set_dpms;
-
-       dc->link_funcs.set_drive_settings =
-                       set_drive_settings;
-
-       dc->link_funcs.perform_link_training =
-                       perform_link_training;
-
-       dc->link_funcs.set_preferred_link_settings =
-                       set_preferred_link_settings;
-
-       dc->link_funcs.enable_hpd =
-                       enable_hpd;
-
-       dc->link_funcs.disable_hpd =
-                       disable_hpd;
-
-       dc->link_funcs.set_test_pattern =
-                       set_test_pattern;
-}
-
 static void destruct(struct dc *dc)
 {
        dc_release_state(dc->current_state);
@@ -485,19 +369,17 @@ static bool construct(struct dc *dc,
                const struct dc_init_data *init_params)
 {
        struct dal_logger *logger;
-       struct dc_context *dc_ctx = kzalloc(sizeof(*dc_ctx), GFP_KERNEL);
-       struct bw_calcs_dceip *dc_dceip = kzalloc(sizeof(*dc_dceip),
-                                                 GFP_KERNEL);
-       struct bw_calcs_vbios *dc_vbios = kzalloc(sizeof(*dc_vbios),
-                                                 GFP_KERNEL);
+       struct dc_context *dc_ctx;
+       struct bw_calcs_dceip *dc_dceip;
+       struct bw_calcs_vbios *dc_vbios;
 #ifdef CONFIG_DRM_AMD_DC_DCN1_0
-       struct dcn_soc_bounding_box *dcn_soc = kzalloc(sizeof(*dcn_soc),
-                                                      GFP_KERNEL);
-       struct dcn_ip_params *dcn_ip = kzalloc(sizeof(*dcn_ip), GFP_KERNEL);
+       struct dcn_soc_bounding_box *dcn_soc;
+       struct dcn_ip_params *dcn_ip;
 #endif
 
        enum dce_version dc_version = DCE_VERSION_UNKNOWN;
 
+       dc_dceip = kzalloc(sizeof(*dc_dceip), GFP_KERNEL);
        if (!dc_dceip) {
                dm_error("%s: failed to create dceip\n", __func__);
                goto fail;
@@ -505,6 +387,7 @@ static bool construct(struct dc *dc,
 
        dc->bw_dceip = dc_dceip;
 
+       dc_vbios = kzalloc(sizeof(*dc_vbios), GFP_KERNEL);
        if (!dc_vbios) {
                dm_error("%s: failed to create vbios\n", __func__);
                goto fail;
@@ -512,6 +395,7 @@ static bool construct(struct dc *dc,
 
        dc->bw_vbios = dc_vbios;
 #ifdef CONFIG_DRM_AMD_DC_DCN1_0
+       dcn_soc = kzalloc(sizeof(*dcn_soc), GFP_KERNEL);
        if (!dcn_soc) {
                dm_error("%s: failed to create dcn_soc\n", __func__);
                goto fail;
@@ -519,6 +403,7 @@ static bool construct(struct dc *dc,
 
        dc->dcn_soc = dcn_soc;
 
+       dcn_ip = kzalloc(sizeof(*dcn_ip), GFP_KERNEL);
        if (!dcn_ip) {
                dm_error("%s: failed to create dcn_ip\n", __func__);
                goto fail;
@@ -527,11 +412,18 @@ static bool construct(struct dc *dc,
        dc->dcn_ip = dcn_ip;
 #endif
 
+       dc_ctx = kzalloc(sizeof(*dc_ctx), GFP_KERNEL);
        if (!dc_ctx) {
                dm_error("%s: failed to create ctx\n", __func__);
                goto fail;
        }
 
+       dc_ctx->cgs_device = init_params->cgs_device;
+       dc_ctx->driver_context = init_params->driver;
+       dc_ctx->dc = dc;
+       dc_ctx->asic_id = init_params->asic_id;
+       dc->ctx = dc_ctx;
+
        dc->current_state = dc_create_state();
 
        if (!dc->current_state) {
@@ -539,11 +431,6 @@ static bool construct(struct dc *dc,
                goto fail;
        }
 
-       dc_ctx->cgs_device = init_params->cgs_device;
-       dc_ctx->driver_context = init_params->driver;
-       dc_ctx->dc = dc;
-       dc_ctx->asic_id = init_params->asic_id;
-
        /* Create logger */
        logger = dal_logger_create(dc_ctx, init_params->log_mask);
 
@@ -553,14 +440,11 @@ static bool construct(struct dc *dc,
                goto fail;
        }
        dc_ctx->logger = logger;
-       dc->ctx = dc_ctx;
-       dc->ctx->dce_environment = init_params->dce_environment;
+       dc_ctx->dce_environment = init_params->dce_environment;
 
        dc_version = resource_parse_asic_id(init_params->asic_id);
-       dc->ctx->dce_version = dc_version;
-#if defined(CONFIG_DRM_AMD_DC_FBC)
-       dc->ctx->fbc_gpu_addr = init_params->fbc_gpu_addr;
-#endif
+       dc_ctx->dce_version = dc_version;
+
        /* Resource should construct all asic specific resources.
         * This should be the only place where we need to parse the asic id
         */
@@ -616,8 +500,6 @@ static bool construct(struct dc *dc,
        if (!create_links(dc, init_params->num_virtual_links))
                goto fail;
 
-       allocate_dc_stream_funcs(dc);
-
        return true;
 
 fail:
@@ -686,6 +568,7 @@ struct dc *dc_create(const struct dc_init_data *init_params)
 
        dc->caps.max_links = dc->link_count;
        dc->caps.max_audios = dc->res_pool->audio_count;
+       dc->caps.linear_pitch_alignment = 64;
 
        dc->config = init_params->flags;
 
@@ -712,6 +595,30 @@ void dc_destroy(struct dc **dc)
        *dc = NULL;
 }
 
+static void enable_timing_multisync(
+               struct dc *dc,
+               struct dc_state *ctx)
+{
+       int i = 0, multisync_count = 0;
+       int pipe_count = dc->res_pool->pipe_count;
+       struct pipe_ctx *multisync_pipes[MAX_PIPES] = { NULL };
+
+       for (i = 0; i < pipe_count; i++) {
+               if (!ctx->res_ctx.pipe_ctx[i].stream ||
+                               !ctx->res_ctx.pipe_ctx[i].stream->triggered_crtc_reset.enabled)
+                       continue;
+               if (ctx->res_ctx.pipe_ctx[i].stream == ctx->res_ctx.pipe_ctx[i].stream->triggered_crtc_reset.event_source)
+                       continue;
+               multisync_pipes[multisync_count] = &ctx->res_ctx.pipe_ctx[i];
+               multisync_count++;
+       }
+
+       if (multisync_count > 0) {
+               dc->hwss.enable_per_frame_crtc_position_reset(
+                       dc, multisync_count, multisync_pipes);
+       }
+}
+
 static void program_timing_sync(
                struct dc *dc,
                struct dc_state *ctx)
@@ -758,7 +665,7 @@ static void program_timing_sync(
                for (j = 0; j < group_size; j++) {
                        struct pipe_ctx *temp;
 
-                       if (!pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) {
+                       if (pipe_set[j]->stream_res.tg->funcs->is_blanked && !pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) {
                                if (j == 0)
                                        break;
 
@@ -771,7 +678,7 @@ static void program_timing_sync(
 
                /* remove any other unblanked pipes as they have already been synced */
                for (j = j + 1; j < group_size; j++) {
-                       if (!pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) {
+                       if (pipe_set[j]->stream_res.tg->funcs->is_blanked && !pipe_set[j]->stream_res.tg->funcs->is_blanked(pipe_set[j]->stream_res.tg)) {
                                group_size--;
                                pipe_set[j] = pipe_set[group_size];
                                j--;
@@ -828,7 +735,6 @@ bool dc_enable_stereo(
        return ret;
 }
 
-
 /*
  * Applies given context to HW and copy it into current context.
  * It's up to the user to release the src context afterwards.
@@ -838,7 +744,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
        struct dc_bios *dcb = dc->ctx->dc_bios;
        enum dc_status result = DC_ERROR_UNEXPECTED;
        struct pipe_ctx *pipe;
-       int i, j, k, l;
+       int i, k, l;
        struct dc_stream_state *dc_streams[MAX_STREAMS] = {0};
 
        disable_dangling_plane(dc, context);
@@ -847,11 +753,46 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
                dc_streams[i] =  context->streams[i];
 
        if (!dcb->funcs->is_accelerated_mode(dcb))
-               dc->hwss.enable_accelerated_mode(dc);
+               dc->hwss.enable_accelerated_mode(dc, context);
+
+       /* re-program planes for existing stream, in case we need to
+        * free up plane resource for later use
+        */
+       for (i = 0; i < context->stream_count; i++) {
+               if (context->streams[i]->mode_changed)
+                       continue;
+
+               dc->hwss.apply_ctx_for_surface(
+                       dc, context->streams[i],
+                       context->stream_status[i].plane_count,
+                       context); /* use new pipe config in new context */
+       }
+
+       /* Program hardware */
+       dc->hwss.ready_shared_resources(dc, context);
+
+       for (i = 0; i < dc->res_pool->pipe_count; i++) {
+               pipe = &context->res_ctx.pipe_ctx[i];
+               dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe);
+       }
+
+       result = dc->hwss.apply_ctx_to_hw(dc, context);
+
+       if (result != DC_OK)
+               return result;
 
+       if (context->stream_count > 1) {
+               enable_timing_multisync(dc, context);
+               program_timing_sync(dc, context);
+       }
+
+       /* Program all planes within new context*/
        for (i = 0; i < context->stream_count; i++) {
                const struct dc_sink *sink = context->streams[i]->sink;
 
+               if (!context->streams[i]->mode_changed)
+                       continue;
+
                dc->hwss.apply_ctx_for_surface(
                                dc, context->streams[i],
                                context->stream_status[i].plane_count,
@@ -880,27 +821,8 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
                                context->streams[i]->timing.pix_clk_khz);
        }
 
-       dc->hwss.ready_shared_resources(dc, context);
-
-       for (i = 0; i < dc->res_pool->pipe_count; i++) {
-               pipe = &context->res_ctx.pipe_ctx[i];
-               dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe);
-       }
-       result = dc->hwss.apply_ctx_to_hw(dc, context);
-
-       program_timing_sync(dc, context);
-
        dc_enable_stereo(dc, context, dc_streams, context->stream_count);
 
-       for (i = 0; i < context->stream_count; i++) {
-               for (j = 0; j < MAX_PIPES; j++) {
-                       pipe = &context->res_ctx.pipe_ctx[j];
-
-                       if (!pipe->top_pipe && pipe->stream == context->streams[i])
-                               dc->hwss.pipe_control_lock(dc, pipe, false);
-               }
-       }
-
        dc_release_state(dc->current_state);
 
        dc->current_state = context;
@@ -936,7 +858,6 @@ bool dc_commit_state(struct dc *dc, struct dc_state *context)
        return (result == DC_OK);
 }
 
-
 bool dc_post_update_surfaces_to_stream(struct dc *dc)
 {
        int i;
@@ -945,9 +866,13 @@ bool dc_post_update_surfaces_to_stream(struct dc *dc)
        post_surface_trace(dc);
 
        for (i = 0; i < dc->res_pool->pipe_count; i++)
-               if (context->res_ctx.pipe_ctx[i].stream == NULL
-                               || context->res_ctx.pipe_ctx[i].plane_state == NULL)
-                       dc->hwss.power_down_front_end(dc, i);
+               if (context->res_ctx.pipe_ctx[i].stream == NULL ||
+                   context->res_ctx.pipe_ctx[i].plane_state == NULL) {
+                       context->res_ctx.pipe_ctx[i].pipe_idx = i;
+                       dc->hwss.disable_plane(dc, &context->res_ctx.pipe_ctx[i]);
+               }
+
+       dc->optimized_required = false;
 
        /* 3rd param should be true, temp w/a for RV*/
 #if defined(CONFIG_DRM_AMD_DC_DCN1_0)
@@ -1014,6 +939,7 @@ bool dc_commit_planes_to_stream(
                flip_addr[i].address = plane_states[i]->address;
                flip_addr[i].flip_immediate = plane_states[i]->flip_immediate;
                plane_info[i].color_space = plane_states[i]->color_space;
+               plane_info[i].input_tf = plane_states[i]->input_tf;
                plane_info[i].format = plane_states[i]->format;
                plane_info[i].plane_size = plane_states[i]->plane_size;
                plane_info[i].rotation = plane_states[i]->rotation;
@@ -1118,79 +1044,108 @@ static unsigned int pixel_format_to_bpp(enum surface_pixel_format format)
        }
 }
 
-static enum surface_update_type get_plane_info_update_type(
-               const struct dc_surface_update *u,
-               int surface_index)
+static enum surface_update_type get_plane_info_update_type(const struct dc_surface_update *u)
 {
-       struct dc_plane_info temp_plane_info;
-       memset(&temp_plane_info, 0, sizeof(temp_plane_info));
+       union surface_update_flags *update_flags = &u->surface->update_flags;
 
        if (!u->plane_info)
                return UPDATE_TYPE_FAST;
 
-       temp_plane_info = *u->plane_info;
+       if (u->plane_info->color_space != u->surface->color_space)
+               update_flags->bits.color_space_change = 1;
 
-       /* Copy all parameters that will cause a full update
-        * from current surface, the rest of the parameters
-        * from provided plane configuration.
-        * Perform memory compare and special validation
-        * for those that can cause fast/medium updates
-        */
+       if (u->plane_info->input_tf != u->surface->input_tf)
+               update_flags->bits.input_tf_change = 1;
 
-       /* Full update parameters */
-       temp_plane_info.color_space = u->surface->color_space;
-       temp_plane_info.dcc = u->surface->dcc;
-       temp_plane_info.horizontal_mirror = u->surface->horizontal_mirror;
-       temp_plane_info.plane_size = u->surface->plane_size;
-       temp_plane_info.rotation = u->surface->rotation;
-       temp_plane_info.stereo_format = u->surface->stereo_format;
-
-       if (surface_index == 0)
-               temp_plane_info.visible = u->plane_info->visible;
-       else
-               temp_plane_info.visible = u->surface->visible;
-
-       if (memcmp(u->plane_info, &temp_plane_info,
-                       sizeof(struct dc_plane_info)) != 0)
-               return UPDATE_TYPE_FULL;
+       if (u->plane_info->sdr_white_level != u->surface->sdr_white_level)
+               update_flags->bits.output_tf_change = 1;
+
+       if (u->plane_info->horizontal_mirror != u->surface->horizontal_mirror)
+               update_flags->bits.horizontal_mirror_change = 1;
+
+       if (u->plane_info->rotation != u->surface->rotation)
+               update_flags->bits.rotation_change = 1;
+
+       if (u->plane_info->format != u->surface->format)
+               update_flags->bits.pixel_format_change = 1;
+
+       if (u->plane_info->stereo_format != u->surface->stereo_format)
+               update_flags->bits.stereo_format_change = 1;
+
+       if (u->plane_info->per_pixel_alpha != u->surface->per_pixel_alpha)
+               update_flags->bits.per_pixel_alpha_change = 1;
+
+       if (u->plane_info->dcc.enable != u->surface->dcc.enable
+                       || u->plane_info->dcc.grph.independent_64b_blks != u->surface->dcc.grph.independent_64b_blks
+                       || u->plane_info->dcc.grph.meta_pitch != u->surface->dcc.grph.meta_pitch)
+               update_flags->bits.dcc_change = 1;
 
        if (pixel_format_to_bpp(u->plane_info->format) !=
-                       pixel_format_to_bpp(u->surface->format)) {
+                       pixel_format_to_bpp(u->surface->format))
                /* different bytes per element will require full bandwidth
                 * and DML calculation
                 */
-               return UPDATE_TYPE_FULL;
-       }
+               update_flags->bits.bpp_change = 1;
+
+       if (u->gamma && dce_use_lut(u->plane_info->format))
+               update_flags->bits.gamma_change = 1;
 
        if (memcmp(&u->plane_info->tiling_info, &u->surface->tiling_info,
                        sizeof(union dc_tiling_info)) != 0) {
+               update_flags->bits.swizzle_change = 1;
                /* todo: below are HW dependent, we should add a hook to
                 * DCE/N resource and validated there.
                 */
-               if (u->plane_info->tiling_info.gfx9.swizzle != DC_SW_LINEAR) {
+               if (u->plane_info->tiling_info.gfx9.swizzle != DC_SW_LINEAR)
                        /* swizzled mode requires RQ to be setup properly,
                         * thus need to run DML to calculate RQ settings
                         */
-                       return UPDATE_TYPE_FULL;
-               }
+                       update_flags->bits.bandwidth_change = 1;
        }
 
+       if (update_flags->bits.rotation_change
+                       || update_flags->bits.stereo_format_change
+                       || update_flags->bits.pixel_format_change
+                       || update_flags->bits.gamma_change
+                       || update_flags->bits.bpp_change
+                       || update_flags->bits.bandwidth_change
+                       || update_flags->bits.output_tf_change)
+               return UPDATE_TYPE_FULL;
+
        return UPDATE_TYPE_MED;
 }
 
-static enum surface_update_type  get_scaling_info_update_type(
+static enum surface_update_type get_scaling_info_update_type(
                const struct dc_surface_update *u)
 {
+       union surface_update_flags *update_flags = &u->surface->update_flags;
+
        if (!u->scaling_info)
                return UPDATE_TYPE_FAST;
 
-       if (u->scaling_info->src_rect.width != u->surface->src_rect.width
-                       || u->scaling_info->src_rect.height != u->surface->src_rect.height
-                       || u->scaling_info->clip_rect.width != u->surface->clip_rect.width
+       if (u->scaling_info->clip_rect.width != u->surface->clip_rect.width
                        || u->scaling_info->clip_rect.height != u->surface->clip_rect.height
                        || u->scaling_info->dst_rect.width != u->surface->dst_rect.width
-                       || u->scaling_info->dst_rect.height != u->surface->dst_rect.height)
-               return UPDATE_TYPE_FULL;
+                       || u->scaling_info->dst_rect.height != u->surface->dst_rect.height) {
+               update_flags->bits.scaling_change = 1;
+
+               if ((u->scaling_info->dst_rect.width < u->surface->dst_rect.width
+                       || u->scaling_info->dst_rect.height < u->surface->dst_rect.height)
+                               && (u->scaling_info->dst_rect.width < u->surface->src_rect.width
+                                       || u->scaling_info->dst_rect.height < u->surface->src_rect.height))
+                       /* Making dst rect smaller requires a bandwidth change */
+                       update_flags->bits.bandwidth_change = 1;
+       }
+
+       if (u->scaling_info->src_rect.width != u->surface->src_rect.width
+               || u->scaling_info->src_rect.height != u->surface->src_rect.height) {
+
+               update_flags->bits.scaling_change = 1;
+               if (u->scaling_info->src_rect.width > u->surface->src_rect.width
+                               && u->scaling_info->src_rect.height > u->surface->src_rect.height)
+                       /* Making src rect bigger requires a bandwidth change */
+                       update_flags->bits.clock_change = 1;
+       }
 
        if (u->scaling_info->src_rect.x != u->surface->src_rect.x
                        || u->scaling_info->src_rect.y != u->surface->src_rect.y
@@ -1198,41 +1153,56 @@ static enum surface_update_type  get_scaling_info_update_type(
                        || u->scaling_info->clip_rect.y != u->surface->clip_rect.y
                        || u->scaling_info->dst_rect.x != u->surface->dst_rect.x
                        || u->scaling_info->dst_rect.y != u->surface->dst_rect.y)
+               update_flags->bits.position_change = 1;
+
+       if (update_flags->bits.clock_change
+                       || update_flags->bits.bandwidth_change)
+               return UPDATE_TYPE_FULL;
+
+       if (update_flags->bits.scaling_change
+                       || update_flags->bits.position_change)
                return UPDATE_TYPE_MED;
 
        return UPDATE_TYPE_FAST;
 }
 
-static enum surface_update_type det_surface_update(
-               const struct dc *dc,
-               const struct dc_surface_update *u,
-               int surface_index)
+static enum surface_update_type det_surface_update(const struct dc *dc,
+               const struct dc_surface_update *u)
 {
        const struct dc_state *context = dc->current_state;
-       enum surface_update_type type = UPDATE_TYPE_FAST;
+       enum surface_update_type type;
        enum surface_update_type overall_type = UPDATE_TYPE_FAST;
+       union surface_update_flags *update_flags = &u->surface->update_flags;
+
+       update_flags->raw = 0; // Reset all flags
 
-       if (!is_surface_in_context(context, u->surface))
+       if (!is_surface_in_context(context, u->surface)) {
+               update_flags->bits.new_plane = 1;
                return UPDATE_TYPE_FULL;
+       }
 
-       type = get_plane_info_update_type(u, surface_index);
-       if (overall_type < type)
-               overall_type = type;
+       type = get_plane_info_update_type(u);
+       elevate_update_type(&overall_type, type);
 
        type = get_scaling_info_update_type(u);
-       if (overall_type < type)
-               overall_type = type;
+       elevate_update_type(&overall_type, type);
+
+       if (u->in_transfer_func)
+               update_flags->bits.in_transfer_func_change = 1;
+
+       if (u->input_csc_color_matrix)
+               update_flags->bits.input_csc_change = 1;
 
-       if (u->in_transfer_func ||
-               u->hdr_static_metadata) {
-               if (overall_type < UPDATE_TYPE_MED)
-                       overall_type = UPDATE_TYPE_MED;
+       if (update_flags->bits.in_transfer_func_change
+                       || update_flags->bits.input_csc_change) {
+               type = UPDATE_TYPE_MED;
+               elevate_update_type(&overall_type, type);
        }
 
        return overall_type;
 }
 
-enum surface_update_type dc_check_update_surfaces_for_stream(
+static enum surface_update_type check_update_surfaces_for_stream(
                struct dc *dc,
                struct dc_surface_update *updates,
                int surface_count,
@@ -1250,18 +1220,38 @@ enum surface_update_type dc_check_update_surfaces_for_stream(
 
        for (i = 0 ; i < surface_count; i++) {
                enum surface_update_type type =
-                               det_surface_update(dc, &updates[i], i);
+                               det_surface_update(dc, &updates[i]);
 
                if (type == UPDATE_TYPE_FULL)
                        return type;
 
-               if (overall_type < type)
-                       overall_type = type;
+               elevate_update_type(&overall_type, type);
        }
 
        return overall_type;
 }
 
+enum surface_update_type dc_check_update_surfaces_for_stream(
+               struct dc *dc,
+               struct dc_surface_update *updates,
+               int surface_count,
+               struct dc_stream_update *stream_update,
+               const struct dc_stream_status *stream_status)
+{
+       int i;
+       enum surface_update_type type;
+
+       for (i = 0; i < surface_count; i++)
+               updates[i].surface->update_flags.raw = 0;
+
+       type = check_update_surfaces_for_stream(dc, updates, surface_count, stream_update, stream_status);
+       if (type == UPDATE_TYPE_FULL)
+               for (i = 0; i < surface_count; i++)
+                       updates[i].surface->update_flags.bits.full_update = 1;
+
+       return type;
+}
+
 static struct dc_stream_status *stream_get_status(
        struct dc_state *ctx,
        struct dc_stream_state *stream)
@@ -1289,20 +1279,13 @@ static void commit_planes_for_stream(struct dc *dc,
                struct dc_state *context)
 {
        int i, j;
+       struct pipe_ctx *top_pipe_to_program = NULL;
 
        if (update_type == UPDATE_TYPE_FULL) {
                dc->hwss.set_bandwidth(dc, context, false);
                context_clock_trace(dc, context);
        }
 
-       if (update_type > UPDATE_TYPE_FAST) {
-               for (j = 0; j < dc->res_pool->pipe_count; j++) {
-                       struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
-
-                       dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx);
-               }
-       }
-
        if (surface_count == 0) {
                /*
                 * In case of turning off screen, no need to program front end a second time.
@@ -1312,103 +1295,72 @@ static void commit_planes_for_stream(struct dc *dc,
                return;
        }
 
-       /* Lock pipes for provided surfaces, or all active if full update*/
-       for (i = 0; i < surface_count; i++) {
-               struct dc_plane_state *plane_state = srf_updates[i].surface;
-
-               for (j = 0; j < dc->res_pool->pipe_count; j++) {
-                       struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
-
-                       if (update_type != UPDATE_TYPE_FULL && pipe_ctx->plane_state != plane_state)
-                               continue;
-                       if (!pipe_ctx->plane_state || pipe_ctx->top_pipe)
-                               continue;
-
-                       dc->hwss.pipe_control_lock(
-                                       dc,
-                                       pipe_ctx,
-                                       true);
-               }
-               if (update_type == UPDATE_TYPE_FULL)
-                       break;
-       }
-
        /* Full fe update*/
        for (j = 0; j < dc->res_pool->pipe_count; j++) {
                struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
 
-               if (update_type != UPDATE_TYPE_FULL || !pipe_ctx->plane_state)
-                       continue;
+               if (!pipe_ctx->top_pipe &&
+                       pipe_ctx->stream &&
+                       pipe_ctx->stream == stream) {
+                       struct dc_stream_status *stream_status = NULL;
 
-               if (!pipe_ctx->top_pipe && pipe_ctx->stream) {
-                       struct dc_stream_status *stream_status = stream_get_status(context, pipe_ctx->stream);
+                       top_pipe_to_program = pipe_ctx;
+
+                       if (update_type == UPDATE_TYPE_FAST || !pipe_ctx->plane_state)
+                               continue;
+
+                       stream_status =
+                                       stream_get_status(context, pipe_ctx->stream);
 
                        dc->hwss.apply_ctx_for_surface(
                                        dc, pipe_ctx->stream, stream_status->plane_count, context);
                }
        }
 
-       if (update_type > UPDATE_TYPE_FAST)
+       if (update_type == UPDATE_TYPE_FULL)
                context_timing_trace(dc, &context->res_ctx);
 
-       /* Perform requested Updates */
-       for (i = 0; i < surface_count; i++) {
-               struct dc_plane_state *plane_state = srf_updates[i].surface;
-
-               if (update_type == UPDATE_TYPE_MED)
-                       dc->hwss.apply_ctx_for_surface(
-                                       dc, stream, surface_count, context);
-
-               for (j = 0; j < dc->res_pool->pipe_count; j++) {
-                       struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
-
-                       if (pipe_ctx->plane_state != plane_state)
-                               continue;
+       /* Lock the top pipe while updating plane addrs, since freesync requires
+        *  plane addr update event triggers to be synchronized.
+        *  top_pipe_to_program is expected to never be NULL
+        */
+       if (update_type == UPDATE_TYPE_FAST) {
+               dc->hwss.pipe_control_lock(dc, top_pipe_to_program, true);
 
-                       if (srf_updates[i].flip_addr)
-                               dc->hwss.update_plane_addr(dc, pipe_ctx);
+               /* Perform requested Updates */
+               for (i = 0; i < surface_count; i++) {
+                       struct dc_plane_state *plane_state = srf_updates[i].surface;
 
-                       if (update_type == UPDATE_TYPE_FAST)
-                               continue;
+                       for (j = 0; j < dc->res_pool->pipe_count; j++) {
+                               struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
 
-                       /* work around to program degamma regs for split pipe after set mode. */
-                       if (srf_updates[i].in_transfer_func || (pipe_ctx->top_pipe &&
-                                       pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state))
-                               dc->hwss.set_input_transfer_func(
-                                               pipe_ctx, pipe_ctx->plane_state);
+                               if (pipe_ctx->stream != stream)
+                                       continue;
 
-                       if (stream_update != NULL &&
-                                       stream_update->out_transfer_func != NULL) {
-                               dc->hwss.set_output_transfer_func(
-                                               pipe_ctx, pipe_ctx->stream);
-                       }
+                               if (pipe_ctx->plane_state != plane_state)
+                                       continue;
 
-                       if (srf_updates[i].hdr_static_metadata) {
-                               resource_build_info_frame(pipe_ctx);
-                               dc->hwss.update_info_frame(pipe_ctx);
+                               if (srf_updates[i].flip_addr)
+                                       dc->hwss.update_plane_addr(dc, pipe_ctx);
                        }
                }
+
+               dc->hwss.pipe_control_lock(dc, top_pipe_to_program, false);
        }
 
-       /* Unlock pipes */
-       for (i = dc->res_pool->pipe_count - 1; i >= 0; i--) {
-               struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
+       if (stream && stream_update && update_type > UPDATE_TYPE_FAST)
+               for (j = 0; j < dc->res_pool->pipe_count; j++) {
+                       struct pipe_ctx *pipe_ctx =
+                                       &context->res_ctx.pipe_ctx[j];
 
-               for (j = 0; j < surface_count; j++) {
-                       if (update_type != UPDATE_TYPE_FULL &&
-                           srf_updates[j].surface != pipe_ctx->plane_state)
-                               continue;
-                       if (!pipe_ctx->plane_state || pipe_ctx->top_pipe)
+                       if (pipe_ctx->stream != stream)
                                continue;
 
-                       dc->hwss.pipe_control_lock(
-                                       dc,
-                                       pipe_ctx,
-                                       false);
-
-                       break;
+                       if (stream_update->hdr_static_metadata) {
+                               resource_build_info_frame(pipe_ctx);
+                               dc->hwss.update_info_frame(pipe_ctx);
+                       }
                }
-       }
 }
 
 void dc_commit_updates_for_stream(struct dc *dc,
@@ -1480,10 +1432,7 @@ void dc_commit_updates_for_stream(struct dc *dc,
                                stream_update,
                                update_type,
                                context);
-
-       if (update_type >= UPDATE_TYPE_FULL)
-               dc_post_update_surfaces_to_stream(dc);
-
+       /*update current_State*/
        if (dc->current_state != context) {
 
                struct dc_state *old = dc->current_state;
@@ -1492,6 +1441,9 @@ void dc_commit_updates_for_stream(struct dc *dc,
                dc_release_state(old);
 
        }
+       /*let's use current_state to update watermark etc*/
+       if (update_type >= UPDATE_TYPE_FULL)
+               dc_post_update_surfaces_to_stream(dc);
 
        return;