backend/drm: refuse to switch CRTC for enabled connector

match_obj() might return a configuration where the CRTC for an
enabled connector is switched to another one.

We don't support this correctly: the wlr_output common code would
need to query again the supported formats, re-allocate the
swapchain, etc.

What's more, the kernel doesn't even support this [1]: it
requires planes to be disabled to change their CRTC, it rejects
commits directly switching the CRTC used by a plane.

[1]: https://cgit.freedesktop.org/drm/drm-misc/tree/drivers/gpu/drm/drm_atomic.c?h=6e90293618ed476d6b11f82ce724efbb9e9a071b#n697
This commit is contained in:
Simon Ser 2022-12-06 19:01:24 +01:00 committed by Simon Zeni
parent 99fb2fefc3
commit bde68b1df7

View file

@ -1087,47 +1087,43 @@ static void realloc_crtcs(struct wlr_drm_backend *drm,
} }
} }
/* // Refuse to remove a CRTC from an enabled connector, and refuse to
* In the case that we add a new connector (hotplug) and we fail to // change the CRTC of an enabled connector.
* match everything, we prefer to fail the new connector and keep all
* of the old mappings instead.
*/
for (size_t i = 0; i < num_connectors; ++i) { for (size_t i = 0; i < num_connectors; ++i) {
struct wlr_drm_connector *conn = connectors[i]; struct wlr_drm_connector *conn = connectors[i];
if (conn->status == DRM_MODE_CONNECTED && conn->output.enabled && if (conn->status != DRM_MODE_CONNECTED || !conn->output.enabled) {
connector_match[i] == -1) { continue;
}
if (connector_match[i] == -1) {
wlr_log(WLR_DEBUG, "Could not match a CRTC for previously connected output; " wlr_log(WLR_DEBUG, "Could not match a CRTC for previously connected output; "
"keeping old configuration"); "keeping old configuration");
return;
}
assert(conn->crtc != NULL);
if (connector_match[i] != conn->crtc - drm->crtcs) {
wlr_log(WLR_DEBUG, "Cannot switch CRTC for enabled output; "
"keeping old configuration");
return; return;
} }
} }
wlr_log(WLR_DEBUG, "State after reallocation:");
// Apply new configuration // Apply new configuration
wlr_log(WLR_DEBUG, "State after reallocation:");
for (size_t i = 0; i < num_connectors; ++i) { for (size_t i = 0; i < num_connectors; ++i) {
struct wlr_drm_connector *conn = connectors[i]; struct wlr_drm_connector *conn = connectors[i];
bool prev_enabled = conn->crtc;
wlr_log(WLR_DEBUG, " '%s': crtc=%zd", wlr_log(WLR_DEBUG, " '%s': crtc=%zd",
conn->name, connector_match[i]); conn->name, connector_match[i]);
// We don't need to change anything. if (conn->crtc != NULL && connector_match[i] == conn->crtc - drm->crtcs) {
if (prev_enabled && connector_match[i] == conn->crtc - drm->crtcs) { // We don't need to change anything
continue; continue;
} }
dealloc_crtc(conn); dealloc_crtc(conn);
if (connector_match[i] >= 0) {
if (connector_match[i] == -1) { conn->crtc = &drm->crtcs[connector_match[i]];
if (prev_enabled) {
wlr_drm_conn_log(conn, WLR_DEBUG, "Output has lost its CRTC");
wlr_output_update_enabled(&conn->output, false);
wlr_output_update_mode(&conn->output, NULL);
}
continue;
} }
conn->crtc = &drm->crtcs[connector_match[i]];
} }
} }