From 324eeaa0cd0014c7ec7b8a3a700fb712a83f3c4a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 15 Jan 2023 16:21:21 +0100 Subject: backend/drm: disable all CRTCs after VT switch When the user switches away from the VT where wlroots is running, the new DRM master may mutate the KMS state in an arbitrary manner. For instance, let's say wlroots uses the following connector/CRTC mapping: - CRTC 42 drives connector DP-1 - CRTC 43 drives connector DP-2 Then the new DRM master may swap the mapping like so: - CRTC 42 drives connector DP-2 - CRTC 43 drives connector DP-1 wlroots needs to restore its own state when the user switches back. Some state is attached to wlr_drm_crtc (e.g. current FB), so reading back and adopting the CRTC/connector mapping left by the previous DRM master would be complicated (this was the source of other bugs in the past, see [1]). With the previous logic, wlroots merely tries to restore the state of each connector one after the other. This fails in the scenario described above: the kernel refuses to use CRTC 42 for DP-1, because that CRTC is already in-use for DP-2. Unfortunately with the legacy uAPI it's not possible to restore the state in one go. We need to support both legacy and atomic uAPIs, so let's fix the bug for the legacy uAPI first, and then improve the situation for the atomic uAPI as a second step [2]. We need to disable the CRTCs we're going to switch the connectors for. This sounds complicated, so let's just disable all CRTCs to simplify. This causes a black screen because of the on/off modesets, but makes VT switch much more reliable, so I'll take it. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/c6d8a11d2c438d514473b1cbe20e5550e7227472 [2]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3794 Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3342 --- backend/drm/backend.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'backend') diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 1492c4d4..d061272d 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -103,6 +103,21 @@ static void handle_session_active(struct wl_listener *listener, void *data) { wlr_log(WLR_INFO, "DRM fd resumed"); scan_drm_connectors(drm, NULL); + // The previous DRM master leaves KMS in an undefined state. We need + // to restore out own state, but be careful to avoid invalid + // configurations. The connector/CRTC mapping may have changed, so + // first disable all CRTCs, then light up the ones we were using + // before the VT switch. + // TODO: use the atomic API to improve restoration after a VT switch + for (size_t i = 0; i < drm->num_crtcs; i++) { + struct wlr_drm_crtc *crtc = &drm->crtcs[i]; + + if (drmModeSetCrtc(drm->fd, crtc->id, 0, 0, 0, NULL, 0, NULL) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to disable CRTC %"PRIu32" after VT switch", + crtc->id); + } + } + struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { struct wlr_output_mode *mode = NULL; -- cgit v1.2.3