diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 32f6f57fc66ea79c718eea093f8f31c5b804ff2b..9fab9860999b35359be6fd942bb9d812a3683886 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -86,6 +86,68 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, } } +static int disable_conflicting_connectors(struct drm_atomic_state *state) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_encoder *encoder; + unsigned encoder_mask = 0; + int i, ret; + + for_each_connector_in_state(state, connector, conn_state, i) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + struct drm_encoder *new_encoder; + + if (!conn_state->crtc) + continue; + + if (funcs->atomic_best_encoder) + new_encoder = funcs->atomic_best_encoder(connector, conn_state); + else + new_encoder = funcs->best_encoder(connector); + + if (new_encoder) + encoder_mask |= 1 << drm_encoder_index(new_encoder); + } + + drm_for_each_connector(connector, state->dev) { + struct drm_crtc_state *crtc_state; + + if (drm_atomic_get_existing_connector_state(state, connector)) + continue; + + encoder = connector->state->best_encoder; + if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder)))) + continue; + + conn_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n", + encoder->base.id, encoder->name, + conn_state->crtc->base.id, conn_state->crtc->name, + connector->base.id, connector->name); + + crtc_state = drm_atomic_get_existing_crtc_state(state, conn_state->crtc); + + ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); + if (ret) + return ret; + + if (!crtc_state->connector_mask) { + ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, + NULL); + if (ret < 0) + return ret; + + crtc_state->active = false; + } + } + + return 0; +} + static bool check_pending_encoder_assignment(struct drm_atomic_state *state, struct drm_encoder *new_encoder) @@ -448,6 +510,12 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } } + if (state->legacy_set_config) { + ret = disable_conflicting_connectors(state); + if (ret) + return ret; + } + for_each_connector_in_state(state, connector, connector_state, i) { /* * This only sets crtc->mode_changed for routing changes, @@ -1796,6 +1864,7 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set) if (!state) return -ENOMEM; + state->legacy_set_config = true; state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); retry: ret = __drm_atomic_helper_set_config(set, state); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8c7fb3d0f9d0f621bea427be0462cbb27ba51d1a..75b28deeaf2458d313e5655e8290c5026edcc0bb 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1676,6 +1676,7 @@ struct drm_bridge { * @dev: parent DRM device * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics + * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL. * @planes: pointer to array of plane pointers * @plane_states: pointer to array of plane states pointers * @crtcs: pointer to array of CRTC pointers @@ -1689,6 +1690,7 @@ struct drm_atomic_state { struct drm_device *dev; bool allow_modeset : 1; bool legacy_cursor_update : 1; + bool legacy_set_config : 1; struct drm_plane **planes; struct drm_plane_state **plane_states; struct drm_crtc **crtcs;