| // 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 |