diff options
author | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2016-03-03 04:17:40 -0500 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2016-03-08 05:04:41 -0500 |
commit | 8248b65df653c0a536bdd64f8a6c8bb150bfdd60 (patch) | |
tree | bc2e7fb1366594ca515bc7692dba4da5b3a42c66 | |
parent | 40616a26d1c68e4c80e2358a02297ba492f4cc17 (diff) |
drm/atomic: Handle encoder assignment conflicts in a separate check, v3.
The current check doesn't handle the case where we don't steal an
encoder, but keep it on the current connector. If we repurpose
disable_conflicting_encoders to do the checking, we just have
to reject the ones that conflict.
Changes since v1:
- Return early with empty encoder_mask, drm_for_each_connector
requires connection_mutex held.
Changes since v2:
- Add comments for the loops.
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Testcase: kms_setmode.invalid-clone-single-crtc-stealing
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1456996662-8704-6-git-send-email-maarten.lankhorst@linux.intel.com
-rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 77 |
1 files changed, 43 insertions, 34 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 9fab9860999b..52c03cea6cf9 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c | |||
@@ -86,7 +86,8 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, | |||
86 | } | 86 | } |
87 | } | 87 | } |
88 | 88 | ||
89 | static int disable_conflicting_connectors(struct drm_atomic_state *state) | 89 | static int handle_conflicting_encoders(struct drm_atomic_state *state, |
90 | bool disable_conflicting_encoders) | ||
90 | { | 91 | { |
91 | struct drm_connector_state *conn_state; | 92 | struct drm_connector_state *conn_state; |
92 | struct drm_connector *connector; | 93 | struct drm_connector *connector; |
@@ -94,6 +95,11 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) | |||
94 | unsigned encoder_mask = 0; | 95 | unsigned encoder_mask = 0; |
95 | int i, ret; | 96 | int i, ret; |
96 | 97 | ||
98 | /* | ||
99 | * First loop, find all newly assigned encoders from the connectors | ||
100 | * part of the state. If the same encoder is assigned to multiple | ||
101 | * connectors bail out. | ||
102 | */ | ||
97 | for_each_connector_in_state(state, connector, conn_state, i) { | 103 | for_each_connector_in_state(state, connector, conn_state, i) { |
98 | const struct drm_connector_helper_funcs *funcs = connector->helper_private; | 104 | const struct drm_connector_helper_funcs *funcs = connector->helper_private; |
99 | struct drm_encoder *new_encoder; | 105 | struct drm_encoder *new_encoder; |
@@ -106,10 +112,33 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) | |||
106 | else | 112 | else |
107 | new_encoder = funcs->best_encoder(connector); | 113 | new_encoder = funcs->best_encoder(connector); |
108 | 114 | ||
109 | if (new_encoder) | 115 | if (new_encoder) { |
116 | if (encoder_mask & (1 << drm_encoder_index(new_encoder))) { | ||
117 | DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] on [CONNECTOR:%d:%s] already assigned\n", | ||
118 | new_encoder->base.id, new_encoder->name, | ||
119 | connector->base.id, connector->name); | ||
120 | |||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
110 | encoder_mask |= 1 << drm_encoder_index(new_encoder); | 124 | encoder_mask |= 1 << drm_encoder_index(new_encoder); |
125 | } | ||
111 | } | 126 | } |
112 | 127 | ||
128 | if (!encoder_mask) | ||
129 | return 0; | ||
130 | |||
131 | /* | ||
132 | * Second loop, iterate over all connectors not part of the state. | ||
133 | * | ||
134 | * If a conflicting encoder is found and disable_conflicting_encoders | ||
135 | * is not set, an error is returned. Userspace can provide a solution | ||
136 | * through the atomic ioctl. | ||
137 | * | ||
138 | * If the flag is set conflicting connectors are removed from the crtc | ||
139 | * and the crtc is disabled if no encoder is left. This preserves | ||
140 | * compatibility with the legacy set_config behavior. | ||
141 | */ | ||
113 | drm_for_each_connector(connector, state->dev) { | 142 | drm_for_each_connector(connector, state->dev) { |
114 | struct drm_crtc_state *crtc_state; | 143 | struct drm_crtc_state *crtc_state; |
115 | 144 | ||
@@ -120,6 +149,15 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) | |||
120 | if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder)))) | 149 | if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder)))) |
121 | continue; | 150 | continue; |
122 | 151 | ||
152 | if (!disable_conflicting_encoders) { | ||
153 | DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s] by [CONNECTOR:%d:%s]\n", | ||
154 | encoder->base.id, encoder->name, | ||
155 | connector->state->crtc->base.id, | ||
156 | connector->state->crtc->name, | ||
157 | connector->base.id, connector->name); | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
123 | conn_state = drm_atomic_get_connector_state(state, connector); | 161 | conn_state = drm_atomic_get_connector_state(state, connector); |
124 | if (IS_ERR(conn_state)) | 162 | if (IS_ERR(conn_state)) |
125 | return PTR_ERR(conn_state); | 163 | return PTR_ERR(conn_state); |
@@ -148,26 +186,6 @@ static int disable_conflicting_connectors(struct drm_atomic_state *state) | |||
148 | return 0; | 186 | return 0; |
149 | } | 187 | } |
150 | 188 | ||
151 | static bool | ||
152 | check_pending_encoder_assignment(struct drm_atomic_state *state, | ||
153 | struct drm_encoder *new_encoder) | ||
154 | { | ||
155 | struct drm_connector *connector; | ||
156 | struct drm_connector_state *conn_state; | ||
157 | int i; | ||
158 | |||
159 | for_each_connector_in_state(state, connector, conn_state, i) { | ||
160 | if (conn_state->best_encoder != new_encoder) | ||
161 | continue; | ||
162 | |||
163 | /* encoder already assigned and we're trying to re-steal it! */ | ||
164 | if (connector->state->best_encoder != conn_state->best_encoder) | ||
165 | return false; | ||
166 | } | ||
167 | |||
168 | return true; | ||
169 | } | ||
170 | |||
171 | static void | 189 | static void |
172 | set_best_encoder(struct drm_atomic_state *state, | 190 | set_best_encoder(struct drm_atomic_state *state, |
173 | struct drm_connector_state *conn_state, | 191 | struct drm_connector_state *conn_state, |
@@ -325,13 +343,6 @@ update_connector_routing(struct drm_atomic_state *state, | |||
325 | return 0; | 343 | return 0; |
326 | } | 344 | } |
327 | 345 | ||
328 | if (!check_pending_encoder_assignment(state, new_encoder)) { | ||
329 | DRM_DEBUG_ATOMIC("Encoder for [CONNECTOR:%d:%s] already assigned\n", | ||
330 | connector->base.id, | ||
331 | connector->name); | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | |||
335 | ret = steal_encoder(state, new_encoder); | 346 | ret = steal_encoder(state, new_encoder); |
336 | if (ret) { | 347 | if (ret) { |
337 | DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", | 348 | DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", |
@@ -510,11 +521,9 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, | |||
510 | } | 521 | } |
511 | } | 522 | } |
512 | 523 | ||
513 | if (state->legacy_set_config) { | 524 | ret = handle_conflicting_encoders(state, state->legacy_set_config); |
514 | ret = disable_conflicting_connectors(state); | 525 | if (ret) |
515 | if (ret) | 526 | return ret; |
516 | return ret; | ||
517 | } | ||
518 | 527 | ||
519 | for_each_connector_in_state(state, connector, connector_state, i) { | 528 | for_each_connector_in_state(state, connector, connector_state, i) { |
520 | /* | 529 | /* |