summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2020-07-02 03:31:24 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2020-07-02 03:31:24 +0000
commit489416f0738905ad084c83ee8ce5f372eeb87ade (patch)
treeb28fa799f26f946e11950339aa6975da0aebb028 /sys
parent63263e9e1344d83a7e8b88d01403f2cbd0600b33 (diff)
drm/fb-helper: Fix vt restore
From Daniel Vetter 7b6902118146835fa67b52f624576d30b1c9e09f in linux 5.7.y/5.7.7 dc5bdb68b5b369d5bc7d1de96fa64cc1737a6320 in mainline linux
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/drm/drm_fb_helper.c75
1 files changed, 57 insertions, 18 deletions
diff --git a/sys/dev/pci/drm/drm_fb_helper.c b/sys/dev/pci/drm/drm_fb_helper.c
index 3763157e8b3..6ddad45c990 100644
--- a/sys/dev/pci/drm/drm_fb_helper.c
+++ b/sys/dev/pci/drm/drm_fb_helper.c
@@ -229,18 +229,9 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
}
EXPORT_SYMBOL(drm_fb_helper_debug_leave);
-/**
- * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
- * @fb_helper: driver-allocated fbdev helper, can be NULL
- *
- * This should be called from driver's drm &drm_driver.lastclose callback
- * when implementing an fbcon on top of kms using this helper. This ensures that
- * the user isn't greeted with a black screen when e.g. X dies.
- *
- * RETURNS:
- * Zero if everything went ok, negative error code otherwise.
- */
-int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+static int
+__drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,
+ bool force)
{
bool do_delayed;
int ret;
@@ -251,13 +242,22 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
if (READ_ONCE(fb_helper->deferred_setup))
return 0;
- mutex_lock(&fb_helper->lock);
-#ifdef __linux__
- ret = drm_client_modeset_commit(&fb_helper->client);
-#else
- ret = drm_client_modeset_commit_locked(&fb_helper->client);
+#ifdef __OpenBSD__
+ force = true;
#endif
+ mutex_lock(&fb_helper->lock);
+ if (force) {
+ /*
+ * Yes this is the _locked version which expects the master lock
+ * to be held. But for forced restores we're intentionally
+ * racing here, see drm_fb_helper_set_par().
+ */
+ ret = drm_client_modeset_commit_locked(&fb_helper->client);
+ } else {
+ ret = drm_client_modeset_commit(&fb_helper->client);
+ }
+
do_delayed = fb_helper->delayed_hotplug;
if (do_delayed)
fb_helper->delayed_hotplug = false;
@@ -268,6 +268,22 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
return ret;
}
+
+/**
+ * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
+ * @fb_helper: driver-allocated fbdev helper, can be NULL
+ *
+ * This should be called from driver's drm &drm_driver.lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * RETURNS:
+ * Zero if everything went ok, negative error code otherwise.
+ */
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+{
+ return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false);
+}
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
#ifdef CONFIG_MAGIC_SYSRQ
@@ -1334,6 +1350,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
{
struct drm_fb_helper *fb_helper = info->par;
struct fb_var_screeninfo *var = &info->var;
+ bool force;
if (oops_in_progress)
return -EBUSY;
@@ -1343,7 +1360,29 @@ int drm_fb_helper_set_par(struct fb_info *info)
return -EINVAL;
}
- drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
+ /*
+ * Normally we want to make sure that a kms master takes precedence over
+ * fbdev, to avoid fbdev flickering and occasionally stealing the
+ * display status. But Xorg first sets the vt back to text mode using
+ * the KDSET IOCTL with KD_TEXT, and only after that drops the master
+ * status when exiting.
+ *
+ * In the past this was caught by drm_fb_helper_lastclose(), but on
+ * modern systems where logind always keeps a drm fd open to orchestrate
+ * the vt switching, this doesn't work.
+ *
+ * To not break the userspace ABI we have this special case here, which
+ * is only used for the above case. Everything else uses the normal
+ * commit function, which ensures that we never steal the display from
+ * an active drm master.
+ */
+#ifdef __linux__
+ force = var->activate & FB_ACTIVATE_KD_TEXT;
+#else
+ force = true;
+#endif
+
+ __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
return 0;
}