Introduce two write-only debugfs controls for testing PSR recovery.

amdgpu_dm_trigger_psr_recovery directly queues the PSR recovery worker
without first creating a display or firmware failure.  Trigger it with:

  echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_psr_recovery

amdgpu_dm_halt_dmub_for_psr_recovery selects an active PSR-enabled eDP
stream, enters PSR, halts DMUB firmware, and requests a synchronous PSR
exit.  The failed exit should schedule the normal PSR recovery path.
Trigger it with:

  echo 0xDEADDEAD > \
    /sys/kernel/debug/dri/0/amdgpu_dm_halt_dmub_for_psr_recovery

Halting DMUB is destructive until reset.  The second control therefore
requires the existing STOP_FW response value 0xDEADDEAD as a safety
cookie.  Both controls refuse to run when GPU recovery is disabled, no
reset domain is available, teardown has started, or another PSR recovery
is pending.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: David Weber <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 177 ++++++++++++++++++
 1 file changed, 177 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
index 7db38ad3f848..a80e13b98ad7 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
@@ -29,6 +29,7 @@
 #include <media/cec-notifier.h>
 
 #include "dc.h"
+#include "core_types.h"
 #include "amdgpu.h"
 #include "amdgpu_dm.h"
 #include "amdgpu_dm_debugfs.h"
@@ -4483,6 +4484,176 @@ 
DEFINE_DEBUGFS_ATTRIBUTE(skip_detection_link_training_fops,
                         skip_detection_link_training_get,
                         skip_detection_link_training_set, "%llu\n");
 
+static int psr_recovery_test_available(struct amdgpu_device *adev)
+{
+       unsigned long flags;
+       bool pending;
+       bool stopping;
+
+       if (!amdgpu_device_should_recover_gpu(adev) || !adev->reset_domain)
+               return -EOPNOTSUPP;
+
+       spin_lock_irqsave(&adev->dm.psr_recovery_lock, flags);
+       pending = adev->dm.psr_recovery_pending;
+       stopping = adev->dm.psr_recovery_stopping;
+       spin_unlock_irqrestore(&adev->dm.psr_recovery_lock, flags);
+
+       if (stopping)
+               return -ESHUTDOWN;
+
+       return pending ? -EBUSY : 0;
+}
+
+static int trigger_psr_recovery_set(void *data, u64 val)
+{
+       struct amdgpu_device *adev = data;
+       int ret;
+
+       if (val != 1)
+               return -EINVAL;
+       if (!adev->dm.dc || !adev->dm.dc->ctx)
+               return -ENODEV;
+       ret = psr_recovery_test_available(adev);
+       if (ret)
+               return ret;
+
+       drm_warn(adev_to_drm(adev),
+                "injecting a fatal PSR failure to test display recovery\n");
+       if (!amdgpu_dm_schedule_psr_recovery(&adev->dm, NULL, NULL))
+               return -EIO;
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(trigger_psr_recovery_fops, NULL,
+                        trigger_psr_recovery_set, "%llu\n");
+
+static int halt_dmub_for_psr_recovery_set(void *data, u64 val)
+{
+       struct amdgpu_device *adev = data;
+       struct dc *dc = adev->dm.dc;
+       struct dc_dmub_srv *dc_dmub_srv;
+       struct dmub_srv *dmub;
+       struct dc_link *link = NULL;
+       enum dmub_status status;
+       u32 response = 0;
+       bool allow_active;
+       bool pending;
+       bool programmed;
+       unsigned long flags;
+       int ret = 0;
+       int i;
+
+       /*
+        * Halting DMUB is destructive until reset. Require the STOP_FW ABI
+        * response cookie to make an accidental write less likely.
+        */
+       if (val != DMUB_GPINT__STOP_FW_RESPONSE)
+               return -EINVAL;
+       ret = psr_recovery_test_available(adev);
+       if (ret)
+               return ret;
+       if (!dc || !dc->ctx || !dc->ctx->dmub_srv ||
+           !dc->res_pool || !dc->res_pool->psr)
+               return -ENODEV;
+
+       dc_dmub_srv = dc->ctx->dmub_srv;
+       dmub = dc_dmub_srv->dmub;
+       if (!dmub)
+               return -ENODEV;
+
+       mutex_lock(&adev->dm.dc_lock);
+       if (dc->current_state) {
+               for (i = 0; i < dc->current_state->stream_count; i++) {
+                       struct dc_stream_state *stream =
+                               dc->current_state->streams[i];
+
+                       if (stream && stream->link &&
+                           stream->link->connector_signal == SIGNAL_TYPE_EDP &&
+                           stream->link->psr_settings.psr_feature_enabled) {
+                               link = stream->link;
+                               break;
+                       }
+               }
+       }
+
+       if (!link) {
+               mutex_unlock(&adev->dm.dc_lock);
+               return -ENODEV;
+       }
+       ret = psr_recovery_test_available(adev);
+       if (ret)
+               goto unlock;
+
+       if (dc_dmub_srv->idle_allowed)
+               dc_dmub_srv_apply_idle_power_optimizations(dc, false);
+
+       /* Enter PSR before halting firmware so the test exercises failed exit. 
*/
+       link->psr_settings.psr_allow_active_valid = false;
+       allow_active = true;
+       programmed = dc_link_set_psr_allow_active(link, &allow_active, true,
+                                                 false, NULL);
+       if (!programmed) {
+               drm_err(adev_to_drm(adev),
+                       "failed to enter PSR before halting DMUB\n");
+               ret = -EIO;
+               goto unlock;
+       }
+
+       drm_warn(adev_to_drm(adev),
+                "halting DMUB firmware to test PSR exit recovery\n");
+       status = dmub_srv_send_gpint_command(dmub, DMUB_GPINT__STOP_FW, 0,
+                                            100000);
+       if (status != DMUB_STATUS_OK) {
+               drm_err(adev_to_drm(adev),
+                       "failed to halt DMUB firmware: %d; recovering\n", 
status);
+               if (!amdgpu_dm_schedule_psr_recovery(&adev->dm, NULL, NULL))
+                       drm_err(adev_to_drm(adev),
+                               "failed to schedule recovery after DMUB halt 
error\n");
+               ret = -EIO;
+               goto unlock;
+       }
+
+       for (i = 0; i < 100000; i++) {
+               status = dmub_srv_get_gpint_response(dmub, &response);
+               if (status == DMUB_STATUS_OK &&
+                   response == DMUB_GPINT__STOP_FW_RESPONSE)
+                       break;
+               udelay(1);
+       }
+
+       if (response != DMUB_GPINT__STOP_FW_RESPONSE)
+               drm_warn(adev_to_drm(adev),
+                        "DMUB halt response timed out; testing PSR exit 
anyway\n");
+
+       allow_active = false;
+       programmed = dc_link_set_psr_allow_active(link, &allow_active, true,
+                                                 false, NULL);
+       spin_lock_irqsave(&adev->dm.psr_recovery_lock, flags);
+       pending = adev->dm.psr_recovery_pending;
+       spin_unlock_irqrestore(&adev->dm.psr_recovery_lock, flags);
+       if (programmed) {
+               drm_err(adev_to_drm(adev),
+                       "PSR exit unexpectedly succeeded after DMUB halt\n");
+               ret = -EIO;
+       } else if (!pending) {
+               drm_err(adev_to_drm(adev),
+                       "PSR exit timeout did not schedule recovery\n");
+               ret = -EIO;
+       } else {
+               drm_info(adev_to_drm(adev),
+                        "PSR exit timeout scheduled recovery\n");
+       }
+
+unlock:
+       mutex_unlock(&adev->dm.dc_lock);
+
+       return ret;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(halt_dmub_for_psr_recovery_fops, NULL,
+                        halt_dmub_for_psr_recovery_set, "%llu\n");
+
 /*
  * Dumps the DCC_EN bit for each pipe.
  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dcc_en
@@ -4579,6 +4750,12 @@ void dtn_debugfs_init(struct amdgpu_device *adev)
        debugfs_create_file_unsafe("amdgpu_dm_skip_detection_link_training", 
0644, root, adev,
                                   &skip_detection_link_training_fops);
 
+       debugfs_create_file_unsafe("amdgpu_dm_trigger_psr_recovery", 0200,
+                                  root, adev, &trigger_psr_recovery_fops);
+       debugfs_create_file_unsafe("amdgpu_dm_halt_dmub_for_psr_recovery", 0200,
+                                  root, adev,
+                                  &halt_dmub_for_psr_recovery_fops);
+
        debugfs_create_file_unsafe("amdgpu_dm_dmub_tracebuffer", 0644, root,
                                   adev, &dmub_tracebuffer_fops);
 
-- 
2.54.0

Reply via email to