Merge tag 'for-linus-5.0' of git://github.com/cminyard/linux-ipmi
[sfrench/cifs-2.6.git] / drivers / gpu / drm / amd / display / amdgpu_dm / amdgpu_dm.c
index d01315965af06c1b6d82c38fb796faaf61d3ba2c..f4fa40c387d367cb006c95733aa53712fbb10bd3 100644 (file)
@@ -331,12 +331,29 @@ static void dm_crtc_high_irq(void *interrupt_params)
        struct common_irq_params *irq_params = interrupt_params;
        struct amdgpu_device *adev = irq_params->adev;
        struct amdgpu_crtc *acrtc;
+       struct dm_crtc_state *acrtc_state;
 
        acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
 
        if (acrtc) {
                drm_crtc_handle_vblank(&acrtc->base);
                amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
+
+               acrtc_state = to_dm_crtc_state(acrtc->base.state);
+
+               if (acrtc_state->stream &&
+                   acrtc_state->vrr_params.supported &&
+                   acrtc_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE) {
+                       mod_freesync_handle_v_update(
+                               adev->dm.freesync_module,
+                               acrtc_state->stream,
+                               &acrtc_state->vrr_params);
+
+                       dc_stream_adjust_vmin_vmax(
+                               adev->dm.dc,
+                               acrtc_state->stream,
+                               &acrtc_state->vrr_params.adjust);
+               }
        }
 }
 
@@ -682,22 +699,36 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend)
 {
        struct amdgpu_dm_connector *aconnector;
        struct drm_connector *connector;
+       struct drm_dp_mst_topology_mgr *mgr;
+       int ret;
+       bool need_hotplug = false;
 
        drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-                  aconnector = to_amdgpu_dm_connector(connector);
-                  if (aconnector->dc_link->type == dc_connection_mst_branch &&
-                                  !aconnector->mst_port) {
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           head) {
+               aconnector = to_amdgpu_dm_connector(connector);
+               if (aconnector->dc_link->type != dc_connection_mst_branch ||
+                   aconnector->mst_port)
+                       continue;
+
+               mgr = &aconnector->mst_mgr;
 
-                          if (suspend)
-                                  drm_dp_mst_topology_mgr_suspend(&aconnector->mst_mgr);
-                          else
-                                  drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr);
-                  }
+               if (suspend) {
+                       drm_dp_mst_topology_mgr_suspend(mgr);
+               } else {
+                       ret = drm_dp_mst_topology_mgr_resume(mgr);
+                       if (ret < 0) {
+                               drm_dp_mst_topology_mgr_set_mst(mgr, false);
+                               need_hotplug = true;
+                       }
+               }
        }
 
        drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+       if (need_hotplug)
+               drm_kms_helper_hotplug_event(dev);
 }
 
 /**
@@ -881,7 +912,6 @@ static int dm_resume(void *handle)
        struct drm_plane_state *new_plane_state;
        struct dm_plane_state *dm_new_plane_state;
        enum dc_connection_type new_connection_type = dc_connection_none;
-       int ret;
        int i;
 
        /* power on hardware */
@@ -954,13 +984,13 @@ static int dm_resume(void *handle)
                }
        }
 
-       ret = drm_atomic_helper_resume(ddev, dm->cached_state);
+       drm_atomic_helper_resume(ddev, dm->cached_state);
 
        dm->cached_state = NULL;
 
        amdgpu_dm_irq_resume_late(adev);
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -1742,7 +1772,7 @@ static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
                + caps.min_input_signal * 0x101;
 
        if (dc_link_set_backlight_level(dm->backlight_link,
-                       brightness, 0, 0))
+                       brightness, 0))
                return 0;
        else
                return 1;
@@ -3009,7 +3039,7 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
                dc_stream_retain(state->stream);
        }
 
-       state->adjust = cur->adjust;
+       state->vrr_params = cur->vrr_params;
        state->vrr_infopacket = cur->vrr_infopacket;
        state->abm_level = cur->abm_level;
        state->vrr_supported = cur->vrr_supported;
@@ -3628,10 +3658,20 @@ static int dm_plane_atomic_check(struct drm_plane *plane,
 static int dm_plane_atomic_async_check(struct drm_plane *plane,
                                       struct drm_plane_state *new_plane_state)
 {
+       struct drm_plane_state *old_plane_state =
+               drm_atomic_get_old_plane_state(new_plane_state->state, plane);
+
        /* Only support async updates on cursor planes. */
        if (plane->type != DRM_PLANE_TYPE_CURSOR)
                return -EINVAL;
 
+       /*
+        * DRM calls prepare_fb and cleanup_fb on new_plane_state for
+        * async commits so don't allow fb changes.
+        */
+       if (old_plane_state->fb != new_plane_state->fb)
+               return -EINVAL;
+
        return 0;
 }
 
@@ -4445,9 +4485,11 @@ struct dc_stream_status *dc_state_get_stream_status(
 static void update_freesync_state_on_stream(
        struct amdgpu_display_manager *dm,
        struct dm_crtc_state *new_crtc_state,
-       struct dc_stream_state *new_stream)
+       struct dc_stream_state *new_stream,
+       struct dc_plane_state *surface,
+       u32 flip_timestamp_in_us)
 {
-       struct mod_vrr_params vrr = {0};
+       struct mod_vrr_params vrr_params = new_crtc_state->vrr_params;
        struct dc_info_packet vrr_infopacket = {0};
        struct mod_freesync_config config = new_crtc_state->freesync_config;
 
@@ -4474,43 +4516,52 @@ static void update_freesync_state_on_stream(
 
        mod_freesync_build_vrr_params(dm->freesync_module,
                                      new_stream,
-                                     &config, &vrr);
+                                     &config, &vrr_params);
+
+       if (surface) {
+               mod_freesync_handle_preflip(
+                       dm->freesync_module,
+                       surface,
+                       new_stream,
+                       flip_timestamp_in_us,
+                       &vrr_params);
+       }
 
        mod_freesync_build_vrr_infopacket(
                dm->freesync_module,
                new_stream,
-               &vrr,
+               &vrr_params,
                PACKET_TYPE_VRR,
                TRANSFER_FUNC_UNKNOWN,
                &vrr_infopacket);
 
        new_crtc_state->freesync_timing_changed =
-               (memcmp(&new_crtc_state->adjust,
-                       &vrr.adjust,
-                       sizeof(vrr.adjust)) != 0);
+               (memcmp(&new_crtc_state->vrr_params.adjust,
+                       &vrr_params.adjust,
+                       sizeof(vrr_params.adjust)) != 0);
 
        new_crtc_state->freesync_vrr_info_changed =
                (memcmp(&new_crtc_state->vrr_infopacket,
                        &vrr_infopacket,
                        sizeof(vrr_infopacket)) != 0);
 
-       new_crtc_state->adjust = vrr.adjust;
+       new_crtc_state->vrr_params = vrr_params;
        new_crtc_state->vrr_infopacket = vrr_infopacket;
 
-       new_stream->adjust = new_crtc_state->adjust;
+       new_stream->adjust = new_crtc_state->vrr_params.adjust;
        new_stream->vrr_infopacket = vrr_infopacket;
 
        if (new_crtc_state->freesync_vrr_info_changed)
                DRM_DEBUG_KMS("VRR packet update: crtc=%u enabled=%d state=%d",
                              new_crtc_state->base.crtc->base.id,
                              (int)new_crtc_state->base.vrr_enabled,
-                             (int)vrr.state);
+                             (int)vrr_params.state);
 
        if (new_crtc_state->freesync_timing_changed)
                DRM_DEBUG_KMS("VRR timing update: crtc=%u min=%u max=%u\n",
                              new_crtc_state->base.crtc->base.id,
-                             vrr.adjust.v_total_min,
-                             vrr.adjust.v_total_max);
+                                 vrr_params.adjust.v_total_min,
+                                 vrr_params.adjust.v_total_max);
 }
 
 /*
@@ -4524,6 +4575,7 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc,
                              struct dc_state *state)
 {
        unsigned long flags;
+       uint64_t timestamp_ns;
        uint32_t target_vblank;
        int r, vpos, hpos;
        struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
@@ -4537,6 +4589,7 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc,
        struct dc_stream_update stream_update = {0};
        struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc->state);
        struct dc_stream_status *stream_status;
+       struct dc_plane_state *surface;
 
 
        /* Prepare wait for target vblank early - before the fence-waits */
@@ -4586,6 +4639,9 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc,
        addr.address.grph.addr.high_part = upper_32_bits(afb->address);
        addr.flip_immediate = async_flip;
 
+       timestamp_ns = ktime_get_ns();
+       addr.flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
+
 
        if (acrtc->base.state->event)
                prepare_flip_isr(acrtc);
@@ -4599,8 +4655,10 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc,
                return;
        }
 
-       surface_updates->surface = stream_status->plane_states[0];
-       if (!surface_updates->surface) {
+       surface = stream_status->plane_states[0];
+       surface_updates->surface = surface;
+
+       if (!surface) {
                DRM_ERROR("No surface for CRTC: id=%d\n",
                        acrtc->crtc_id);
                return;
@@ -4611,7 +4669,9 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc,
                update_freesync_state_on_stream(
                        &adev->dm,
                        acrtc_state,
-                       acrtc_state->stream);
+                       acrtc_state->stream,
+                       surface,
+                       addr.flip_timestamp_in_us);
 
                if (acrtc_state->freesync_timing_changed)
                        stream_update.adjust =
@@ -4622,7 +4682,16 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc,
                                &acrtc_state->stream->vrr_infopacket;
        }
 
+       /* Update surface timing information. */
+       surface->time.time_elapsed_in_us[surface->time.index] =
+               addr.flip_timestamp_in_us - surface->time.prev_update_time_in_us;
+       surface->time.prev_update_time_in_us = addr.flip_timestamp_in_us;
+       surface->time.index++;
+       if (surface->time.index >= DC_PLANE_UPDATE_TIMES_MAX)
+               surface->time.index = 0;
+
        mutex_lock(&adev->dm.dc_lock);
+
        dc_commit_updates_for_stream(adev->dm.dc,
                                             surface_updates,
                                             1,
@@ -5314,6 +5383,7 @@ static void get_freesync_config_for_crtc(
                config.max_refresh_in_uhz =
                                aconnector->max_vfreq * 1000000;
                config.vsif_supported = true;
+               config.btr = true;
        }
 
        new_crtc_state->freesync_config = config;
@@ -5324,8 +5394,8 @@ static void reset_freesync_config_for_crtc(
 {
        new_crtc_state->vrr_supported = false;
 
-       memset(&new_crtc_state->adjust, 0,
-              sizeof(new_crtc_state->adjust));
+       memset(&new_crtc_state->vrr_params, 0,
+              sizeof(new_crtc_state->vrr_params));
        memset(&new_crtc_state->vrr_infopacket, 0,
               sizeof(new_crtc_state->vrr_infopacket));
 }
@@ -5863,7 +5933,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
        for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
                if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
                    !new_crtc_state->color_mgmt_changed &&
-                   !new_crtc_state->vrr_enabled)
+                   old_crtc_state->vrr_enabled == new_crtc_state->vrr_enabled)
                        continue;
 
                if (!new_crtc_state->enable)