summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_client_sysrq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_client_sysrq.c')
-rw-r--r--drivers/gpu/drm/drm_client_sysrq.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_client_sysrq.c b/drivers/gpu/drm/drm_client_sysrq.c
new file mode 100644
index 000000000000..eea660096f1b
--- /dev/null
+++ b/drivers/gpu/drm/drm_client_sysrq.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+#include <linux/sysrq.h>
+
+#include <drm/drm_client_event.h>
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+
+#include "drm_internal.h"
+
+#ifdef CONFIG_MAGIC_SYSRQ
+static LIST_HEAD(drm_client_sysrq_dev_list);
+static DEFINE_MUTEX(drm_client_sysrq_dev_lock);
+
+/* emergency restore, don't bother with error reporting */
+static void drm_client_sysrq_restore_work_fn(struct work_struct *ignored)
+{
+ struct drm_device *dev;
+
+ guard(mutex)(&drm_client_sysrq_dev_lock);
+
+ list_for_each_entry(dev, &drm_client_sysrq_dev_list, client_sysrq_list) {
+ if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ continue;
+
+ drm_client_dev_restore(dev, true);
+ }
+}
+
+static DECLARE_WORK(drm_client_sysrq_restore_work, drm_client_sysrq_restore_work_fn);
+
+static void drm_client_sysrq_restore_handler(u8 ignored)
+{
+ schedule_work(&drm_client_sysrq_restore_work);
+}
+
+static const struct sysrq_key_op drm_client_sysrq_restore_op = {
+ .handler = drm_client_sysrq_restore_handler,
+ .help_msg = "force-fb(v)",
+ .action_msg = "Restore framebuffer console",
+};
+
+void drm_client_sysrq_register(struct drm_device *dev)
+{
+ guard(mutex)(&drm_client_sysrq_dev_lock);
+
+ if (list_empty(&drm_client_sysrq_dev_list))
+ register_sysrq_key('v', &drm_client_sysrq_restore_op);
+
+ list_add(&dev->client_sysrq_list, &drm_client_sysrq_dev_list);
+}
+
+void drm_client_sysrq_unregister(struct drm_device *dev)
+{
+ guard(mutex)(&drm_client_sysrq_dev_lock);
+
+ /* remove device from global restore list */
+ if (!drm_WARN_ON(dev, list_empty(&dev->client_sysrq_list)))
+ list_del(&dev->client_sysrq_list);
+
+ /* no devices left; unregister key */
+ if (list_empty(&drm_client_sysrq_dev_list))
+ unregister_sysrq_key('v', &drm_client_sysrq_restore_op);
+}
+#endif