12c9916cdSFrançois Tigeot /*
22c9916cdSFrançois Tigeot * Copyright (C) 2014 Red Hat
32c9916cdSFrançois Tigeot * Copyright (C) 2014 Intel Corp.
42c9916cdSFrançois Tigeot *
52c9916cdSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a
62c9916cdSFrançois Tigeot * copy of this software and associated documentation files (the "Software"),
72c9916cdSFrançois Tigeot * to deal in the Software without restriction, including without limitation
82c9916cdSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense,
92c9916cdSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the
102c9916cdSFrançois Tigeot * Software is furnished to do so, subject to the following conditions:
112c9916cdSFrançois Tigeot *
122c9916cdSFrançois Tigeot * The above copyright notice and this permission notice shall be included in
132c9916cdSFrançois Tigeot * all copies or substantial portions of the Software.
142c9916cdSFrançois Tigeot *
152c9916cdSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
162c9916cdSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
172c9916cdSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
182c9916cdSFrançois Tigeot * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
192c9916cdSFrançois Tigeot * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
202c9916cdSFrançois Tigeot * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
212c9916cdSFrançois Tigeot * OTHER DEALINGS IN THE SOFTWARE.
222c9916cdSFrançois Tigeot *
232c9916cdSFrançois Tigeot * Authors:
242c9916cdSFrançois Tigeot * Rob Clark <robdclark@gmail.com>
252c9916cdSFrançois Tigeot * Daniel Vetter <daniel.vetter@ffwll.ch>
262c9916cdSFrançois Tigeot */
272c9916cdSFrançois Tigeot
282c9916cdSFrançois Tigeot #include <drm/drmP.h>
292c9916cdSFrançois Tigeot #include <drm/drm_atomic.h>
302c9916cdSFrançois Tigeot #include <drm/drm_plane_helper.h>
312c9916cdSFrançois Tigeot #include <drm/drm_crtc_helper.h>
322c9916cdSFrançois Tigeot #include <drm/drm_atomic_helper.h>
336559babbSFrançois Tigeot #include <linux/dma-fence.h>
342c9916cdSFrançois Tigeot
35*3f2dd94aSFrançois Tigeot #include "drm_crtc_helper_internal.h"
361dedbd3bSFrançois Tigeot #include "drm_crtc_internal.h"
371dedbd3bSFrançois Tigeot
382c9916cdSFrançois Tigeot /**
392c9916cdSFrançois Tigeot * DOC: overview
402c9916cdSFrançois Tigeot *
412c9916cdSFrançois Tigeot * This helper library provides implementations of check and commit functions on
422c9916cdSFrançois Tigeot * top of the CRTC modeset helper callbacks and the plane helper callbacks. It
432c9916cdSFrançois Tigeot * also provides convenience implementations for the atomic state handling
442c9916cdSFrançois Tigeot * callbacks for drivers which don't need to subclass the drm core structures to
452c9916cdSFrançois Tigeot * add their own additional internal state.
462c9916cdSFrançois Tigeot *
472c9916cdSFrançois Tigeot * This library also provides default implementations for the check callback in
48352ff8bdSFrançois Tigeot * drm_atomic_helper_check() and for the commit callback with
49352ff8bdSFrançois Tigeot * drm_atomic_helper_commit(). But the individual stages and callbacks are
50352ff8bdSFrançois Tigeot * exposed to allow drivers to mix and match and e.g. use the plane helpers only
512c9916cdSFrançois Tigeot * together with a driver private modeset implementation.
522c9916cdSFrançois Tigeot *
532c9916cdSFrançois Tigeot * This library also provides implementations for all the legacy driver
54352ff8bdSFrançois Tigeot * interfaces on top of the atomic interface. See drm_atomic_helper_set_config(),
55352ff8bdSFrançois Tigeot * drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the
562c9916cdSFrançois Tigeot * various functions to implement set_property callbacks. New drivers must not
572c9916cdSFrançois Tigeot * implement these functions themselves but must use the provided helpers.
58aee94f86SFrançois Tigeot *
59aee94f86SFrançois Tigeot * The atomic helper uses the same function table structures as all other
60a85cb24fSFrançois Tigeot * modesetting helpers. See the documentation for &struct drm_crtc_helper_funcs,
61a85cb24fSFrançois Tigeot * struct &drm_encoder_helper_funcs and &struct drm_connector_helper_funcs. It
62a85cb24fSFrançois Tigeot * also shares the &struct drm_plane_helper_funcs function table with the plane
63aee94f86SFrançois Tigeot * helpers.
642c9916cdSFrançois Tigeot */
652c9916cdSFrançois Tigeot static void
drm_atomic_helper_plane_changed(struct drm_atomic_state * state,struct drm_plane_state * old_plane_state,struct drm_plane_state * plane_state,struct drm_plane * plane)662c9916cdSFrançois Tigeot drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
67a85cb24fSFrançois Tigeot struct drm_plane_state *old_plane_state,
682c9916cdSFrançois Tigeot struct drm_plane_state *plane_state,
692c9916cdSFrançois Tigeot struct drm_plane *plane)
702c9916cdSFrançois Tigeot {
712c9916cdSFrançois Tigeot struct drm_crtc_state *crtc_state;
722c9916cdSFrançois Tigeot
73a85cb24fSFrançois Tigeot if (old_plane_state->crtc) {
74a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state,
75a85cb24fSFrançois Tigeot old_plane_state->crtc);
762c9916cdSFrançois Tigeot
772c9916cdSFrançois Tigeot if (WARN_ON(!crtc_state))
782c9916cdSFrançois Tigeot return;
792c9916cdSFrançois Tigeot
802c9916cdSFrançois Tigeot crtc_state->planes_changed = true;
812c9916cdSFrançois Tigeot }
822c9916cdSFrançois Tigeot
832c9916cdSFrançois Tigeot if (plane_state->crtc) {
84a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
852c9916cdSFrançois Tigeot
862c9916cdSFrançois Tigeot if (WARN_ON(!crtc_state))
872c9916cdSFrançois Tigeot return;
882c9916cdSFrançois Tigeot
892c9916cdSFrançois Tigeot crtc_state->planes_changed = true;
902c9916cdSFrançois Tigeot }
912c9916cdSFrançois Tigeot }
922c9916cdSFrançois Tigeot
handle_conflicting_encoders(struct drm_atomic_state * state,bool disable_conflicting_encoders)93c0e85e96SFrançois Tigeot static int handle_conflicting_encoders(struct drm_atomic_state *state,
94c0e85e96SFrançois Tigeot bool disable_conflicting_encoders)
95aee94f86SFrançois Tigeot {
96a85cb24fSFrançois Tigeot struct drm_connector_state *new_conn_state;
97c0e85e96SFrançois Tigeot struct drm_connector *connector;
98a85cb24fSFrançois Tigeot struct drm_connector_list_iter conn_iter;
99c0e85e96SFrançois Tigeot struct drm_encoder *encoder;
100c0e85e96SFrançois Tigeot unsigned encoder_mask = 0;
101a85cb24fSFrançois Tigeot int i, ret = 0;
102aee94f86SFrançois Tigeot
103c0e85e96SFrançois Tigeot /*
104c0e85e96SFrançois Tigeot * First loop, find all newly assigned encoders from the connectors
105c0e85e96SFrançois Tigeot * part of the state. If the same encoder is assigned to multiple
106c0e85e96SFrançois Tigeot * connectors bail out.
107c0e85e96SFrançois Tigeot */
108a85cb24fSFrançois Tigeot for_each_new_connector_in_state(state, connector, new_conn_state, i) {
109c0e85e96SFrançois Tigeot const struct drm_connector_helper_funcs *funcs = connector->helper_private;
110c0e85e96SFrançois Tigeot struct drm_encoder *new_encoder;
111c0e85e96SFrançois Tigeot
112a85cb24fSFrançois Tigeot if (!new_conn_state->crtc)
113aee94f86SFrançois Tigeot continue;
114aee94f86SFrançois Tigeot
115c0e85e96SFrançois Tigeot if (funcs->atomic_best_encoder)
116a85cb24fSFrançois Tigeot new_encoder = funcs->atomic_best_encoder(connector, new_conn_state);
1171dedbd3bSFrançois Tigeot else if (funcs->best_encoder)
118c0e85e96SFrançois Tigeot new_encoder = funcs->best_encoder(connector);
1191dedbd3bSFrançois Tigeot else
1201dedbd3bSFrançois Tigeot new_encoder = drm_atomic_helper_best_encoder(connector);
121c0e85e96SFrançois Tigeot
122c0e85e96SFrançois Tigeot if (new_encoder) {
123c0e85e96SFrançois Tigeot if (encoder_mask & (1 << drm_encoder_index(new_encoder))) {
124c0e85e96SFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] on [CONNECTOR:%d:%s] already assigned\n",
125c0e85e96SFrançois Tigeot new_encoder->base.id, new_encoder->name,
126c0e85e96SFrançois Tigeot connector->base.id, connector->name);
127c0e85e96SFrançois Tigeot
128c0e85e96SFrançois Tigeot return -EINVAL;
129aee94f86SFrançois Tigeot }
130aee94f86SFrançois Tigeot
131c0e85e96SFrançois Tigeot encoder_mask |= 1 << drm_encoder_index(new_encoder);
132c0e85e96SFrançois Tigeot }
133aee94f86SFrançois Tigeot }
134aee94f86SFrançois Tigeot
135c0e85e96SFrançois Tigeot if (!encoder_mask)
136c0e85e96SFrançois Tigeot return 0;
137c0e85e96SFrançois Tigeot
138c0e85e96SFrançois Tigeot /*
139c0e85e96SFrançois Tigeot * Second loop, iterate over all connectors not part of the state.
140c0e85e96SFrançois Tigeot *
141c0e85e96SFrançois Tigeot * If a conflicting encoder is found and disable_conflicting_encoders
142c0e85e96SFrançois Tigeot * is not set, an error is returned. Userspace can provide a solution
143c0e85e96SFrançois Tigeot * through the atomic ioctl.
144c0e85e96SFrançois Tigeot *
145c0e85e96SFrançois Tigeot * If the flag is set conflicting connectors are removed from the crtc
146c0e85e96SFrançois Tigeot * and the crtc is disabled if no encoder is left. This preserves
147c0e85e96SFrançois Tigeot * compatibility with the legacy set_config behavior.
148c0e85e96SFrançois Tigeot */
149a85cb24fSFrançois Tigeot drm_connector_list_iter_begin(state->dev, &conn_iter);
150a85cb24fSFrançois Tigeot drm_for_each_connector_iter(connector, &conn_iter) {
151c0e85e96SFrançois Tigeot struct drm_crtc_state *crtc_state;
152c0e85e96SFrançois Tigeot
153a85cb24fSFrançois Tigeot if (drm_atomic_get_new_connector_state(state, connector))
154c0e85e96SFrançois Tigeot continue;
155c0e85e96SFrançois Tigeot
156c0e85e96SFrançois Tigeot encoder = connector->state->best_encoder;
157c0e85e96SFrançois Tigeot if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder))))
158c0e85e96SFrançois Tigeot continue;
159c0e85e96SFrançois Tigeot
160c0e85e96SFrançois Tigeot if (!disable_conflicting_encoders) {
161c0e85e96SFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s] by [CONNECTOR:%d:%s]\n",
162c0e85e96SFrançois Tigeot encoder->base.id, encoder->name,
163c0e85e96SFrançois Tigeot connector->state->crtc->base.id,
164c0e85e96SFrançois Tigeot connector->state->crtc->name,
165c0e85e96SFrançois Tigeot connector->base.id, connector->name);
166a85cb24fSFrançois Tigeot ret = -EINVAL;
167a85cb24fSFrançois Tigeot goto out;
168c0e85e96SFrançois Tigeot }
169c0e85e96SFrançois Tigeot
170a85cb24fSFrançois Tigeot new_conn_state = drm_atomic_get_connector_state(state, connector);
171a85cb24fSFrançois Tigeot if (IS_ERR(new_conn_state)) {
172a85cb24fSFrançois Tigeot ret = PTR_ERR(new_conn_state);
173a85cb24fSFrançois Tigeot goto out;
174a85cb24fSFrançois Tigeot }
175c0e85e96SFrançois Tigeot
176c0e85e96SFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n",
177c0e85e96SFrançois Tigeot encoder->base.id, encoder->name,
178a85cb24fSFrançois Tigeot new_conn_state->crtc->base.id, new_conn_state->crtc->name,
179c0e85e96SFrançois Tigeot connector->base.id, connector->name);
180c0e85e96SFrançois Tigeot
181a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
182c0e85e96SFrançois Tigeot
183a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_connector(new_conn_state, NULL);
184c0e85e96SFrançois Tigeot if (ret)
185a85cb24fSFrançois Tigeot goto out;
186c0e85e96SFrançois Tigeot
187c0e85e96SFrançois Tigeot if (!crtc_state->connector_mask) {
188c0e85e96SFrançois Tigeot ret = drm_atomic_set_mode_prop_for_crtc(crtc_state,
189c0e85e96SFrançois Tigeot NULL);
190c0e85e96SFrançois Tigeot if (ret < 0)
191a85cb24fSFrançois Tigeot goto out;
192c0e85e96SFrançois Tigeot
193c0e85e96SFrançois Tigeot crtc_state->active = false;
194c0e85e96SFrançois Tigeot }
195c0e85e96SFrançois Tigeot }
196a85cb24fSFrançois Tigeot out:
197a85cb24fSFrançois Tigeot drm_connector_list_iter_end(&conn_iter);
198c0e85e96SFrançois Tigeot
199a85cb24fSFrançois Tigeot return ret;
200c0e85e96SFrançois Tigeot }
201c0e85e96SFrançois Tigeot
202c0e85e96SFrançois Tigeot static void
set_best_encoder(struct drm_atomic_state * state,struct drm_connector_state * conn_state,struct drm_encoder * encoder)203c0e85e96SFrançois Tigeot set_best_encoder(struct drm_atomic_state *state,
204c0e85e96SFrançois Tigeot struct drm_connector_state *conn_state,
2052c9916cdSFrançois Tigeot struct drm_encoder *encoder)
2062c9916cdSFrançois Tigeot {
207c0e85e96SFrançois Tigeot struct drm_crtc_state *crtc_state;
208c0e85e96SFrançois Tigeot struct drm_crtc *crtc;
2092c9916cdSFrançois Tigeot
210c0e85e96SFrançois Tigeot if (conn_state->best_encoder) {
211c0e85e96SFrançois Tigeot /* Unset the encoder_mask in the old crtc state. */
212c0e85e96SFrançois Tigeot crtc = conn_state->connector->state->crtc;
2132c9916cdSFrançois Tigeot
214c0e85e96SFrançois Tigeot /* A NULL crtc is an error here because we should have
215c0e85e96SFrançois Tigeot * duplicated a NULL best_encoder when crtc was NULL.
216c0e85e96SFrançois Tigeot * As an exception restoring duplicated atomic state
217c0e85e96SFrançois Tigeot * during resume is allowed, so don't warn when
218c0e85e96SFrançois Tigeot * best_encoder is equal to encoder we intend to set.
219c0e85e96SFrançois Tigeot */
220c0e85e96SFrançois Tigeot WARN_ON(!crtc && encoder != conn_state->best_encoder);
221c0e85e96SFrançois Tigeot if (crtc) {
222a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
2232c9916cdSFrançois Tigeot
224c0e85e96SFrançois Tigeot crtc_state->encoder_mask &=
225c0e85e96SFrançois Tigeot ~(1 << drm_encoder_index(conn_state->best_encoder));
226c0e85e96SFrançois Tigeot }
2272c9916cdSFrançois Tigeot }
2282c9916cdSFrançois Tigeot
229c0e85e96SFrançois Tigeot if (encoder) {
230c0e85e96SFrançois Tigeot crtc = conn_state->crtc;
231c0e85e96SFrançois Tigeot WARN_ON(!crtc);
232c0e85e96SFrançois Tigeot if (crtc) {
233a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
234c0e85e96SFrançois Tigeot
235c0e85e96SFrançois Tigeot crtc_state->encoder_mask |=
236c0e85e96SFrançois Tigeot 1 << drm_encoder_index(encoder);
237c0e85e96SFrançois Tigeot }
2382c9916cdSFrançois Tigeot }
2392c9916cdSFrançois Tigeot
240c0e85e96SFrançois Tigeot conn_state->best_encoder = encoder;
241c0e85e96SFrançois Tigeot }
242c0e85e96SFrançois Tigeot
243c0e85e96SFrançois Tigeot static void
steal_encoder(struct drm_atomic_state * state,struct drm_encoder * encoder)2442c9916cdSFrançois Tigeot steal_encoder(struct drm_atomic_state *state,
245c0e85e96SFrançois Tigeot struct drm_encoder *encoder)
2462c9916cdSFrançois Tigeot {
2472c9916cdSFrançois Tigeot struct drm_crtc_state *crtc_state;
2482c9916cdSFrançois Tigeot struct drm_connector *connector;
249a85cb24fSFrançois Tigeot struct drm_connector_state *old_connector_state, *new_connector_state;
250c0e85e96SFrançois Tigeot int i;
2512c9916cdSFrançois Tigeot
252a85cb24fSFrançois Tigeot for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) {
253c0e85e96SFrançois Tigeot struct drm_crtc *encoder_crtc;
254c0e85e96SFrançois Tigeot
255a85cb24fSFrançois Tigeot if (new_connector_state->best_encoder != encoder)
256c0e85e96SFrançois Tigeot continue;
257c0e85e96SFrançois Tigeot
258a85cb24fSFrançois Tigeot encoder_crtc = old_connector_state->crtc;
2592c9916cdSFrançois Tigeot
260aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n",
2612c9916cdSFrançois Tigeot encoder->base.id, encoder->name,
262aee94f86SFrançois Tigeot encoder_crtc->base.id, encoder_crtc->name);
2632c9916cdSFrançois Tigeot
264a85cb24fSFrançois Tigeot set_best_encoder(state, new_connector_state, NULL);
2652c9916cdSFrançois Tigeot
266a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, encoder_crtc);
267a05eeebfSFrançois Tigeot crtc_state->connectors_changed = true;
2682c9916cdSFrançois Tigeot
269c0e85e96SFrançois Tigeot return;
2702c9916cdSFrançois Tigeot }
2712c9916cdSFrançois Tigeot }
2722c9916cdSFrançois Tigeot
2732c9916cdSFrançois Tigeot static int
update_connector_routing(struct drm_atomic_state * state,struct drm_connector * connector,struct drm_connector_state * old_connector_state,struct drm_connector_state * new_connector_state)274c0e85e96SFrançois Tigeot update_connector_routing(struct drm_atomic_state *state,
275c0e85e96SFrançois Tigeot struct drm_connector *connector,
276a85cb24fSFrançois Tigeot struct drm_connector_state *old_connector_state,
277a85cb24fSFrançois Tigeot struct drm_connector_state *new_connector_state)
2782c9916cdSFrançois Tigeot {
279477eb7f9SFrançois Tigeot const struct drm_connector_helper_funcs *funcs;
2802c9916cdSFrançois Tigeot struct drm_encoder *new_encoder;
2812c9916cdSFrançois Tigeot struct drm_crtc_state *crtc_state;
2822c9916cdSFrançois Tigeot
283477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n",
2842c9916cdSFrançois Tigeot connector->base.id,
2852c9916cdSFrançois Tigeot connector->name);
2862c9916cdSFrançois Tigeot
287a85cb24fSFrançois Tigeot if (old_connector_state->crtc != new_connector_state->crtc) {
288a85cb24fSFrançois Tigeot if (old_connector_state->crtc) {
289a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, old_connector_state->crtc);
290a05eeebfSFrançois Tigeot crtc_state->connectors_changed = true;
2912c9916cdSFrançois Tigeot }
2922c9916cdSFrançois Tigeot
293a85cb24fSFrançois Tigeot if (new_connector_state->crtc) {
294a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc);
295a05eeebfSFrançois Tigeot crtc_state->connectors_changed = true;
2962c9916cdSFrançois Tigeot }
2972c9916cdSFrançois Tigeot }
2982c9916cdSFrançois Tigeot
299a85cb24fSFrançois Tigeot if (!new_connector_state->crtc) {
300477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("Disabling [CONNECTOR:%d:%s]\n",
3012c9916cdSFrançois Tigeot connector->base.id,
3022c9916cdSFrançois Tigeot connector->name);
3032c9916cdSFrançois Tigeot
304a85cb24fSFrançois Tigeot set_best_encoder(state, new_connector_state, NULL);
3052c9916cdSFrançois Tigeot
3062c9916cdSFrançois Tigeot return 0;
3072c9916cdSFrançois Tigeot }
3082c9916cdSFrançois Tigeot
3092c9916cdSFrançois Tigeot funcs = connector->helper_private;
31019c468b4SFrançois Tigeot
31119c468b4SFrançois Tigeot if (funcs->atomic_best_encoder)
31219c468b4SFrançois Tigeot new_encoder = funcs->atomic_best_encoder(connector,
313a85cb24fSFrançois Tigeot new_connector_state);
3141dedbd3bSFrançois Tigeot else if (funcs->best_encoder)
3152c9916cdSFrançois Tigeot new_encoder = funcs->best_encoder(connector);
3161dedbd3bSFrançois Tigeot else
3171dedbd3bSFrançois Tigeot new_encoder = drm_atomic_helper_best_encoder(connector);
3182c9916cdSFrançois Tigeot
3192c9916cdSFrançois Tigeot if (!new_encoder) {
320477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
3212c9916cdSFrançois Tigeot connector->base.id,
3222c9916cdSFrançois Tigeot connector->name);
3232c9916cdSFrançois Tigeot return -EINVAL;
3242c9916cdSFrançois Tigeot }
3252c9916cdSFrançois Tigeot
326a85cb24fSFrançois Tigeot if (!drm_encoder_crtc_ok(new_encoder, new_connector_state->crtc)) {
327a85cb24fSFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] incompatible with [CRTC:%d:%s]\n",
328352ff8bdSFrançois Tigeot new_encoder->base.id,
329352ff8bdSFrançois Tigeot new_encoder->name,
330a85cb24fSFrançois Tigeot new_connector_state->crtc->base.id,
331a85cb24fSFrançois Tigeot new_connector_state->crtc->name);
332352ff8bdSFrançois Tigeot return -EINVAL;
333352ff8bdSFrançois Tigeot }
334352ff8bdSFrançois Tigeot
335a85cb24fSFrançois Tigeot if (new_encoder == new_connector_state->best_encoder) {
336a85cb24fSFrançois Tigeot set_best_encoder(state, new_connector_state, new_encoder);
337c0e85e96SFrançois Tigeot
338aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n",
3392c9916cdSFrançois Tigeot connector->base.id,
3402c9916cdSFrançois Tigeot connector->name,
3412c9916cdSFrançois Tigeot new_encoder->base.id,
3422c9916cdSFrançois Tigeot new_encoder->name,
343a85cb24fSFrançois Tigeot new_connector_state->crtc->base.id,
344a85cb24fSFrançois Tigeot new_connector_state->crtc->name);
3452c9916cdSFrançois Tigeot
3462c9916cdSFrançois Tigeot return 0;
3472c9916cdSFrançois Tigeot }
3482c9916cdSFrançois Tigeot
349c0e85e96SFrançois Tigeot steal_encoder(state, new_encoder);
350aee94f86SFrançois Tigeot
351a85cb24fSFrançois Tigeot set_best_encoder(state, new_connector_state, new_encoder);
3522c9916cdSFrançois Tigeot
353a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc);
354a05eeebfSFrançois Tigeot crtc_state->connectors_changed = true;
3552c9916cdSFrançois Tigeot
356aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n",
3572c9916cdSFrançois Tigeot connector->base.id,
3582c9916cdSFrançois Tigeot connector->name,
3592c9916cdSFrançois Tigeot new_encoder->base.id,
3602c9916cdSFrançois Tigeot new_encoder->name,
361a85cb24fSFrançois Tigeot new_connector_state->crtc->base.id,
362a85cb24fSFrançois Tigeot new_connector_state->crtc->name);
3632c9916cdSFrançois Tigeot
3642c9916cdSFrançois Tigeot return 0;
3652c9916cdSFrançois Tigeot }
3662c9916cdSFrançois Tigeot
3672c9916cdSFrançois Tigeot static int
mode_fixup(struct drm_atomic_state * state)3682c9916cdSFrançois Tigeot mode_fixup(struct drm_atomic_state *state)
3692c9916cdSFrançois Tigeot {
370477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
371a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
372477eb7f9SFrançois Tigeot struct drm_connector *connector;
373a85cb24fSFrançois Tigeot struct drm_connector_state *new_conn_state;
3742c9916cdSFrançois Tigeot int i;
3754be47400SFrançois Tigeot int ret;
3762c9916cdSFrançois Tigeot
377a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
378a85cb24fSFrançois Tigeot if (!new_crtc_state->mode_changed &&
379a85cb24fSFrançois Tigeot !new_crtc_state->connectors_changed)
3802c9916cdSFrançois Tigeot continue;
3812c9916cdSFrançois Tigeot
382a85cb24fSFrançois Tigeot drm_mode_copy(&new_crtc_state->adjusted_mode, &new_crtc_state->mode);
3832c9916cdSFrançois Tigeot }
3842c9916cdSFrançois Tigeot
385a85cb24fSFrançois Tigeot for_each_new_connector_in_state(state, connector, new_conn_state, i) {
386477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *funcs;
3872c9916cdSFrançois Tigeot struct drm_encoder *encoder;
3882c9916cdSFrançois Tigeot
389a85cb24fSFrançois Tigeot WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc);
3902c9916cdSFrançois Tigeot
391a85cb24fSFrançois Tigeot if (!new_conn_state->crtc || !new_conn_state->best_encoder)
3922c9916cdSFrançois Tigeot continue;
3932c9916cdSFrançois Tigeot
394a85cb24fSFrançois Tigeot new_crtc_state =
395a85cb24fSFrançois Tigeot drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
3962c9916cdSFrançois Tigeot
3972c9916cdSFrançois Tigeot /*
3982c9916cdSFrançois Tigeot * Each encoder has at most one connector (since we always steal
3992c9916cdSFrançois Tigeot * it away), so we won't call ->mode_fixup twice.
4002c9916cdSFrançois Tigeot */
401a85cb24fSFrançois Tigeot encoder = new_conn_state->best_encoder;
4022c9916cdSFrançois Tigeot funcs = encoder->helper_private;
4032c9916cdSFrançois Tigeot
404a85cb24fSFrançois Tigeot ret = drm_bridge_mode_fixup(encoder->bridge, &new_crtc_state->mode,
405a85cb24fSFrançois Tigeot &new_crtc_state->adjusted_mode);
4062c9916cdSFrançois Tigeot if (!ret) {
407477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
4082c9916cdSFrançois Tigeot return -EINVAL;
4092c9916cdSFrançois Tigeot }
4102c9916cdSFrançois Tigeot
4118621f407SFrançois Tigeot if (funcs && funcs->atomic_check) {
412a85cb24fSFrançois Tigeot ret = funcs->atomic_check(encoder, new_crtc_state,
413a85cb24fSFrançois Tigeot new_conn_state);
4142c9916cdSFrançois Tigeot if (ret) {
415477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] check failed\n",
4162c9916cdSFrançois Tigeot encoder->base.id, encoder->name);
4172c9916cdSFrançois Tigeot return ret;
4182c9916cdSFrançois Tigeot }
4198621f407SFrançois Tigeot } else if (funcs && funcs->mode_fixup) {
420a85cb24fSFrançois Tigeot ret = funcs->mode_fixup(encoder, &new_crtc_state->mode,
421a85cb24fSFrançois Tigeot &new_crtc_state->adjusted_mode);
4222c9916cdSFrançois Tigeot if (!ret) {
423477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] fixup failed\n",
4242c9916cdSFrançois Tigeot encoder->base.id, encoder->name);
4252c9916cdSFrançois Tigeot return -EINVAL;
4262c9916cdSFrançois Tigeot }
4272c9916cdSFrançois Tigeot }
4282c9916cdSFrançois Tigeot }
4292c9916cdSFrançois Tigeot
430a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
431477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
4322c9916cdSFrançois Tigeot
433a85cb24fSFrançois Tigeot if (!new_crtc_state->enable)
4341dedbd3bSFrançois Tigeot continue;
4351dedbd3bSFrançois Tigeot
436a85cb24fSFrançois Tigeot if (!new_crtc_state->mode_changed &&
437a85cb24fSFrançois Tigeot !new_crtc_state->connectors_changed)
4382c9916cdSFrançois Tigeot continue;
4392c9916cdSFrançois Tigeot
4402c9916cdSFrançois Tigeot funcs = crtc->helper_private;
44119c468b4SFrançois Tigeot if (!funcs->mode_fixup)
44219c468b4SFrançois Tigeot continue;
44319c468b4SFrançois Tigeot
444a85cb24fSFrançois Tigeot ret = funcs->mode_fixup(crtc, &new_crtc_state->mode,
445a85cb24fSFrançois Tigeot &new_crtc_state->adjusted_mode);
4462c9916cdSFrançois Tigeot if (!ret) {
447aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] fixup failed\n",
448aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
4492c9916cdSFrançois Tigeot return -EINVAL;
4502c9916cdSFrançois Tigeot }
4512c9916cdSFrançois Tigeot }
4522c9916cdSFrançois Tigeot
4532c9916cdSFrançois Tigeot return 0;
4542c9916cdSFrançois Tigeot }
4552c9916cdSFrançois Tigeot
mode_valid_path(struct drm_connector * connector,struct drm_encoder * encoder,struct drm_crtc * crtc,struct drm_display_mode * mode)456*3f2dd94aSFrançois Tigeot static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
457*3f2dd94aSFrançois Tigeot struct drm_encoder *encoder,
458*3f2dd94aSFrançois Tigeot struct drm_crtc *crtc,
459*3f2dd94aSFrançois Tigeot struct drm_display_mode *mode)
460*3f2dd94aSFrançois Tigeot {
461*3f2dd94aSFrançois Tigeot enum drm_mode_status ret;
462*3f2dd94aSFrançois Tigeot
463*3f2dd94aSFrançois Tigeot ret = drm_encoder_mode_valid(encoder, mode);
464*3f2dd94aSFrançois Tigeot if (ret != MODE_OK) {
465*3f2dd94aSFrançois Tigeot DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] mode_valid() failed\n",
466*3f2dd94aSFrançois Tigeot encoder->base.id, encoder->name);
467*3f2dd94aSFrançois Tigeot return ret;
468*3f2dd94aSFrançois Tigeot }
469*3f2dd94aSFrançois Tigeot
470*3f2dd94aSFrançois Tigeot ret = drm_bridge_mode_valid(encoder->bridge, mode);
471*3f2dd94aSFrançois Tigeot if (ret != MODE_OK) {
472*3f2dd94aSFrançois Tigeot DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n");
473*3f2dd94aSFrançois Tigeot return ret;
474*3f2dd94aSFrançois Tigeot }
475*3f2dd94aSFrançois Tigeot
476*3f2dd94aSFrançois Tigeot ret = drm_crtc_mode_valid(crtc, mode);
477*3f2dd94aSFrançois Tigeot if (ret != MODE_OK) {
478*3f2dd94aSFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode_valid() failed\n",
479*3f2dd94aSFrançois Tigeot crtc->base.id, crtc->name);
480*3f2dd94aSFrançois Tigeot return ret;
481*3f2dd94aSFrançois Tigeot }
482*3f2dd94aSFrançois Tigeot
483*3f2dd94aSFrançois Tigeot return ret;
484*3f2dd94aSFrançois Tigeot }
485*3f2dd94aSFrançois Tigeot
486*3f2dd94aSFrançois Tigeot static int
mode_valid(struct drm_atomic_state * state)487*3f2dd94aSFrançois Tigeot mode_valid(struct drm_atomic_state *state)
488*3f2dd94aSFrançois Tigeot {
489*3f2dd94aSFrançois Tigeot struct drm_connector_state *conn_state;
490*3f2dd94aSFrançois Tigeot struct drm_connector *connector;
491*3f2dd94aSFrançois Tigeot int i;
492*3f2dd94aSFrançois Tigeot
493*3f2dd94aSFrançois Tigeot for_each_new_connector_in_state(state, connector, conn_state, i) {
494*3f2dd94aSFrançois Tigeot struct drm_encoder *encoder = conn_state->best_encoder;
495*3f2dd94aSFrançois Tigeot struct drm_crtc *crtc = conn_state->crtc;
496*3f2dd94aSFrançois Tigeot struct drm_crtc_state *crtc_state;
497*3f2dd94aSFrançois Tigeot enum drm_mode_status mode_status;
498*3f2dd94aSFrançois Tigeot struct drm_display_mode *mode;
499*3f2dd94aSFrançois Tigeot
500*3f2dd94aSFrançois Tigeot if (!crtc || !encoder)
501*3f2dd94aSFrançois Tigeot continue;
502*3f2dd94aSFrançois Tigeot
503*3f2dd94aSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
504*3f2dd94aSFrançois Tigeot if (!crtc_state)
505*3f2dd94aSFrançois Tigeot continue;
506*3f2dd94aSFrançois Tigeot if (!crtc_state->mode_changed && !crtc_state->connectors_changed)
507*3f2dd94aSFrançois Tigeot continue;
508*3f2dd94aSFrançois Tigeot
509*3f2dd94aSFrançois Tigeot mode = &crtc_state->mode;
510*3f2dd94aSFrançois Tigeot
511*3f2dd94aSFrançois Tigeot mode_status = mode_valid_path(connector, encoder, crtc, mode);
512*3f2dd94aSFrançois Tigeot if (mode_status != MODE_OK)
513*3f2dd94aSFrançois Tigeot return -EINVAL;
514*3f2dd94aSFrançois Tigeot }
515*3f2dd94aSFrançois Tigeot
516*3f2dd94aSFrançois Tigeot return 0;
517*3f2dd94aSFrançois Tigeot }
518*3f2dd94aSFrançois Tigeot
5192c9916cdSFrançois Tigeot /**
520477eb7f9SFrançois Tigeot * drm_atomic_helper_check_modeset - validate state object for modeset changes
5212c9916cdSFrançois Tigeot * @dev: DRM device
5222c9916cdSFrançois Tigeot * @state: the driver state object
5232c9916cdSFrançois Tigeot *
5242c9916cdSFrançois Tigeot * Check the state object to see if the requested state is physically possible.
5252c9916cdSFrançois Tigeot * This does all the crtc and connector related computations for an atomic
526a85cb24fSFrançois Tigeot * update and adds any additional connectors needed for full modesets. It calls
527a85cb24fSFrançois Tigeot * the various per-object callbacks in the follow order:
528a05eeebfSFrançois Tigeot *
529a85cb24fSFrançois Tigeot * 1. &drm_connector_helper_funcs.atomic_best_encoder for determining the new encoder.
530a85cb24fSFrançois Tigeot * 2. &drm_connector_helper_funcs.atomic_check to validate the connector state.
531a85cb24fSFrançois Tigeot * 3. If it's determined a modeset is needed then all connectors on the affected crtc
532a85cb24fSFrançois Tigeot * crtc are added and &drm_connector_helper_funcs.atomic_check is run on them.
533*3f2dd94aSFrançois Tigeot * 4. &drm_encoder_helper_funcs.mode_valid, &drm_bridge_funcs.mode_valid and
534*3f2dd94aSFrançois Tigeot * &drm_crtc_helper_funcs.mode_valid are called on the affected components.
535*3f2dd94aSFrançois Tigeot * 5. &drm_bridge_funcs.mode_fixup is called on all encoder bridges.
536*3f2dd94aSFrançois Tigeot * 6. &drm_encoder_helper_funcs.atomic_check is called to validate any encoder state.
537a85cb24fSFrançois Tigeot * This function is only called when the encoder will be part of a configured crtc,
538a85cb24fSFrançois Tigeot * it must not be used for implementing connector property validation.
539a85cb24fSFrançois Tigeot * If this function is NULL, &drm_atomic_encoder_helper_funcs.mode_fixup is called
540a85cb24fSFrançois Tigeot * instead.
541*3f2dd94aSFrançois Tigeot * 7. &drm_crtc_helper_funcs.mode_fixup is called last, to fix up the mode with crtc constraints.
542a85cb24fSFrançois Tigeot *
543a85cb24fSFrançois Tigeot * &drm_crtc_state.mode_changed is set when the input mode is changed.
544a85cb24fSFrançois Tigeot * &drm_crtc_state.connectors_changed is set when a connector is added or
545a85cb24fSFrançois Tigeot * removed from the crtc. &drm_crtc_state.active_changed is set when
546a85cb24fSFrançois Tigeot * &drm_crtc_state.active changes, which is used for DPMS.
5474be47400SFrançois Tigeot * See also: drm_atomic_crtc_needs_modeset()
5482c9916cdSFrançois Tigeot *
5492c9916cdSFrançois Tigeot * IMPORTANT:
5502c9916cdSFrançois Tigeot *
551a85cb24fSFrançois Tigeot * Drivers which set &drm_crtc_state.mode_changed (e.g. in their
552a85cb24fSFrançois Tigeot * &drm_plane_helper_funcs.atomic_check hooks if a plane update can't be done
553a85cb24fSFrançois Tigeot * without a full modeset) _must_ call this function afterwards after that
554a85cb24fSFrançois Tigeot * change. It is permitted to call this function multiple times for the same
555a85cb24fSFrançois Tigeot * update, e.g. when the &drm_crtc_helper_funcs.atomic_check functions depend
556a85cb24fSFrançois Tigeot * upon the adjusted dotclock for fifo space allocation and watermark
557a85cb24fSFrançois Tigeot * computation.
5582c9916cdSFrançois Tigeot *
5591dedbd3bSFrançois Tigeot * RETURNS:
5602c9916cdSFrançois Tigeot * Zero for success or -errno
5612c9916cdSFrançois Tigeot */
5622c9916cdSFrançois Tigeot int
drm_atomic_helper_check_modeset(struct drm_device * dev,struct drm_atomic_state * state)5632c9916cdSFrançois Tigeot drm_atomic_helper_check_modeset(struct drm_device *dev,
5642c9916cdSFrançois Tigeot struct drm_atomic_state *state)
5652c9916cdSFrançois Tigeot {
5662c9916cdSFrançois Tigeot struct drm_crtc *crtc;
567a85cb24fSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
568477eb7f9SFrançois Tigeot struct drm_connector *connector;
569a85cb24fSFrançois Tigeot struct drm_connector_state *old_connector_state, *new_connector_state;
5702c9916cdSFrançois Tigeot int i, ret;
571a85cb24fSFrançois Tigeot unsigned connectors_mask = 0;
5722c9916cdSFrançois Tigeot
573a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
574a85cb24fSFrançois Tigeot bool has_connectors =
575a85cb24fSFrançois Tigeot !!new_crtc_state->connector_mask;
576a85cb24fSFrançois Tigeot
577a85cb24fSFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
578a85cb24fSFrançois Tigeot
579a85cb24fSFrançois Tigeot if (!drm_mode_equal(&old_crtc_state->mode, &new_crtc_state->mode)) {
580aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n",
581aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
582a85cb24fSFrançois Tigeot new_crtc_state->mode_changed = true;
5832c9916cdSFrançois Tigeot }
5842c9916cdSFrançois Tigeot
585a85cb24fSFrançois Tigeot if (old_crtc_state->enable != new_crtc_state->enable) {
586aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enable changed\n",
587aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
588a05eeebfSFrançois Tigeot
589a05eeebfSFrançois Tigeot /*
590a05eeebfSFrançois Tigeot * For clarity this assignment is done here, but
591a05eeebfSFrançois Tigeot * enable == 0 is only true when there are no
592a05eeebfSFrançois Tigeot * connectors and a NULL mode.
593a05eeebfSFrançois Tigeot *
594a05eeebfSFrançois Tigeot * The other way around is true as well. enable != 0
595a05eeebfSFrançois Tigeot * iff connectors are attached and a mode is set.
596a05eeebfSFrançois Tigeot */
597a85cb24fSFrançois Tigeot new_crtc_state->mode_changed = true;
598a85cb24fSFrançois Tigeot new_crtc_state->connectors_changed = true;
599a85cb24fSFrançois Tigeot }
600a85cb24fSFrançois Tigeot
601a85cb24fSFrançois Tigeot if (old_crtc_state->active != new_crtc_state->active) {
602a85cb24fSFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active changed\n",
603a85cb24fSFrançois Tigeot crtc->base.id, crtc->name);
604a85cb24fSFrançois Tigeot new_crtc_state->active_changed = true;
605a85cb24fSFrançois Tigeot }
606a85cb24fSFrançois Tigeot
607a85cb24fSFrançois Tigeot if (new_crtc_state->enable != has_connectors) {
608a85cb24fSFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n",
609a85cb24fSFrançois Tigeot crtc->base.id, crtc->name);
610a85cb24fSFrançois Tigeot
611a85cb24fSFrançois Tigeot return -EINVAL;
6122c9916cdSFrançois Tigeot }
6132c9916cdSFrançois Tigeot }
6142c9916cdSFrançois Tigeot
615a85cb24fSFrançois Tigeot ret = handle_conflicting_encoders(state, false);
616c0e85e96SFrançois Tigeot if (ret)
617c0e85e96SFrançois Tigeot return ret;
618c0e85e96SFrançois Tigeot
619a85cb24fSFrançois Tigeot for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) {
620a85cb24fSFrançois Tigeot const struct drm_connector_helper_funcs *funcs = connector->helper_private;
621a85cb24fSFrançois Tigeot
622a85cb24fSFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
623a85cb24fSFrançois Tigeot
6242c9916cdSFrançois Tigeot /*
6254be47400SFrançois Tigeot * This only sets crtc->connectors_changed for routing changes,
6264be47400SFrançois Tigeot * drivers must set crtc->connectors_changed themselves when
6274be47400SFrançois Tigeot * connector properties need to be updated.
6282c9916cdSFrançois Tigeot */
629c0e85e96SFrançois Tigeot ret = update_connector_routing(state, connector,
630a85cb24fSFrançois Tigeot old_connector_state,
631a85cb24fSFrançois Tigeot new_connector_state);
6322c9916cdSFrançois Tigeot if (ret)
6332c9916cdSFrançois Tigeot return ret;
634a85cb24fSFrançois Tigeot if (old_connector_state->crtc) {
635a85cb24fSFrançois Tigeot new_crtc_state = drm_atomic_get_new_crtc_state(state,
636a85cb24fSFrançois Tigeot old_connector_state->crtc);
637a85cb24fSFrançois Tigeot if (old_connector_state->link_status !=
638a85cb24fSFrançois Tigeot new_connector_state->link_status)
639a85cb24fSFrançois Tigeot new_crtc_state->connectors_changed = true;
640a85cb24fSFrançois Tigeot }
641a85cb24fSFrançois Tigeot
642a85cb24fSFrançois Tigeot if (funcs->atomic_check)
643a85cb24fSFrançois Tigeot ret = funcs->atomic_check(connector, new_connector_state);
644a85cb24fSFrançois Tigeot if (ret)
645a85cb24fSFrançois Tigeot return ret;
646a85cb24fSFrançois Tigeot
647a85cb24fSFrançois Tigeot connectors_mask += BIT(i);
6482c9916cdSFrançois Tigeot }
6492c9916cdSFrançois Tigeot
6502c9916cdSFrançois Tigeot /*
6512c9916cdSFrançois Tigeot * After all the routing has been prepared we need to add in any
6522c9916cdSFrançois Tigeot * connector which is itself unchanged, but who's crtc changes it's
6532c9916cdSFrançois Tigeot * configuration. This must be done before calling mode_fixup in case a
6542c9916cdSFrançois Tigeot * crtc only changed its mode but has the same set of connectors.
6552c9916cdSFrançois Tigeot */
656a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
657a85cb24fSFrançois Tigeot if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
6582c9916cdSFrançois Tigeot continue;
6592c9916cdSFrançois Tigeot
660aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] needs all connectors, enable: %c, active: %c\n",
661aee94f86SFrançois Tigeot crtc->base.id, crtc->name,
662a85cb24fSFrançois Tigeot new_crtc_state->enable ? 'y' : 'n',
663a85cb24fSFrançois Tigeot new_crtc_state->active ? 'y' : 'n');
6642c9916cdSFrançois Tigeot
6652c9916cdSFrançois Tigeot ret = drm_atomic_add_affected_connectors(state, crtc);
6662c9916cdSFrançois Tigeot if (ret != 0)
6672c9916cdSFrançois Tigeot return ret;
6682c9916cdSFrançois Tigeot
66919c468b4SFrançois Tigeot ret = drm_atomic_add_affected_planes(state, crtc);
67019c468b4SFrançois Tigeot if (ret != 0)
67119c468b4SFrançois Tigeot return ret;
6722c9916cdSFrançois Tigeot }
673a85cb24fSFrançois Tigeot
674a85cb24fSFrançois Tigeot /*
675a85cb24fSFrançois Tigeot * Iterate over all connectors again, to make sure atomic_check()
676a85cb24fSFrançois Tigeot * has been called on them when a modeset is forced.
677a85cb24fSFrançois Tigeot */
678a85cb24fSFrançois Tigeot for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) {
679a85cb24fSFrançois Tigeot const struct drm_connector_helper_funcs *funcs = connector->helper_private;
680a85cb24fSFrançois Tigeot
681a85cb24fSFrançois Tigeot if (connectors_mask & BIT(i))
682a85cb24fSFrançois Tigeot continue;
683a85cb24fSFrançois Tigeot
684a85cb24fSFrançois Tigeot if (funcs->atomic_check)
685a85cb24fSFrançois Tigeot ret = funcs->atomic_check(connector, new_connector_state);
686a85cb24fSFrançois Tigeot if (ret)
687a85cb24fSFrançois Tigeot return ret;
6882c9916cdSFrançois Tigeot }
6892c9916cdSFrançois Tigeot
690*3f2dd94aSFrançois Tigeot ret = mode_valid(state);
691*3f2dd94aSFrançois Tigeot if (ret)
692*3f2dd94aSFrançois Tigeot return ret;
693*3f2dd94aSFrançois Tigeot
6942c9916cdSFrançois Tigeot return mode_fixup(state);
6952c9916cdSFrançois Tigeot }
6962c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
6972c9916cdSFrançois Tigeot
6982c9916cdSFrançois Tigeot /**
699477eb7f9SFrançois Tigeot * drm_atomic_helper_check_planes - validate state object for planes changes
7002c9916cdSFrançois Tigeot * @dev: DRM device
7012c9916cdSFrançois Tigeot * @state: the driver state object
7022c9916cdSFrançois Tigeot *
7032c9916cdSFrançois Tigeot * Check the state object to see if the requested state is physically possible.
7042c9916cdSFrançois Tigeot * This does all the plane update related checks using by calling into the
705a85cb24fSFrançois Tigeot * &drm_crtc_helper_funcs.atomic_check and &drm_plane_helper_funcs.atomic_check
706a85cb24fSFrançois Tigeot * hooks provided by the driver.
7072c9916cdSFrançois Tigeot *
708a85cb24fSFrançois Tigeot * It also sets &drm_crtc_state.planes_changed to indicate that a crtc has
709a05eeebfSFrançois Tigeot * updated planes.
710a05eeebfSFrançois Tigeot *
7111dedbd3bSFrançois Tigeot * RETURNS:
7122c9916cdSFrançois Tigeot * Zero for success or -errno
7132c9916cdSFrançois Tigeot */
7142c9916cdSFrançois Tigeot int
drm_atomic_helper_check_planes(struct drm_device * dev,struct drm_atomic_state * state)7152c9916cdSFrançois Tigeot drm_atomic_helper_check_planes(struct drm_device *dev,
7162c9916cdSFrançois Tigeot struct drm_atomic_state *state)
7172c9916cdSFrançois Tigeot {
718477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
719a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
720477eb7f9SFrançois Tigeot struct drm_plane *plane;
721a85cb24fSFrançois Tigeot struct drm_plane_state *new_plane_state, *old_plane_state;
7222c9916cdSFrançois Tigeot int i, ret = 0;
7232c9916cdSFrançois Tigeot
724a85cb24fSFrançois Tigeot for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
725477eb7f9SFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
7262c9916cdSFrançois Tigeot
727a85cb24fSFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&plane->mutex));
728a85cb24fSFrançois Tigeot
7292c9916cdSFrançois Tigeot funcs = plane->helper_private;
7302c9916cdSFrançois Tigeot
731a85cb24fSFrançois Tigeot drm_atomic_helper_plane_changed(state, old_plane_state, new_plane_state, plane);
7322c9916cdSFrançois Tigeot
7332c9916cdSFrançois Tigeot if (!funcs || !funcs->atomic_check)
7342c9916cdSFrançois Tigeot continue;
7352c9916cdSFrançois Tigeot
736a85cb24fSFrançois Tigeot ret = funcs->atomic_check(plane, new_plane_state);
7372c9916cdSFrançois Tigeot if (ret) {
738aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[PLANE:%d:%s] atomic driver check failed\n",
739aee94f86SFrançois Tigeot plane->base.id, plane->name);
7402c9916cdSFrançois Tigeot return ret;
7412c9916cdSFrançois Tigeot }
7422c9916cdSFrançois Tigeot }
7432c9916cdSFrançois Tigeot
744a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
745477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
7462c9916cdSFrançois Tigeot
7472c9916cdSFrançois Tigeot funcs = crtc->helper_private;
7482c9916cdSFrançois Tigeot
7492c9916cdSFrançois Tigeot if (!funcs || !funcs->atomic_check)
7502c9916cdSFrançois Tigeot continue;
7512c9916cdSFrançois Tigeot
752a85cb24fSFrançois Tigeot ret = funcs->atomic_check(crtc, new_crtc_state);
7532c9916cdSFrançois Tigeot if (ret) {
754aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n",
755aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
7562c9916cdSFrançois Tigeot return ret;
7572c9916cdSFrançois Tigeot }
7582c9916cdSFrançois Tigeot }
7592c9916cdSFrançois Tigeot
7602c9916cdSFrançois Tigeot return ret;
7612c9916cdSFrançois Tigeot }
7622c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_check_planes);
7632c9916cdSFrançois Tigeot
7642c9916cdSFrançois Tigeot /**
7652c9916cdSFrançois Tigeot * drm_atomic_helper_check - validate state object
7662c9916cdSFrançois Tigeot * @dev: DRM device
7672c9916cdSFrançois Tigeot * @state: the driver state object
7682c9916cdSFrançois Tigeot *
7692c9916cdSFrançois Tigeot * Check the state object to see if the requested state is physically possible.
7702c9916cdSFrançois Tigeot * Only crtcs and planes have check callbacks, so for any additional (global)
7712c9916cdSFrançois Tigeot * checking that a driver needs it can simply wrap that around this function.
772a85cb24fSFrançois Tigeot * Drivers without such needs can directly use this as their
773a85cb24fSFrançois Tigeot * &drm_mode_config_funcs.atomic_check callback.
7742c9916cdSFrançois Tigeot *
7752c9916cdSFrançois Tigeot * This just wraps the two parts of the state checking for planes and modeset
7762c9916cdSFrançois Tigeot * state in the default order: First it calls drm_atomic_helper_check_modeset()
7772c9916cdSFrançois Tigeot * and then drm_atomic_helper_check_planes(). The assumption is that the
778a85cb24fSFrançois Tigeot * @drm_plane_helper_funcs.atomic_check and @drm_crtc_helper_funcs.atomic_check
779a85cb24fSFrançois Tigeot * functions depend upon an updated adjusted_mode.clock to e.g. properly compute
780a85cb24fSFrançois Tigeot * watermarks.
7812c9916cdSFrançois Tigeot *
7821dedbd3bSFrançois Tigeot * RETURNS:
7832c9916cdSFrançois Tigeot * Zero for success or -errno
7842c9916cdSFrançois Tigeot */
drm_atomic_helper_check(struct drm_device * dev,struct drm_atomic_state * state)7852c9916cdSFrançois Tigeot int drm_atomic_helper_check(struct drm_device *dev,
7862c9916cdSFrançois Tigeot struct drm_atomic_state *state)
7872c9916cdSFrançois Tigeot {
7882c9916cdSFrançois Tigeot int ret;
7892c9916cdSFrançois Tigeot
7902c9916cdSFrançois Tigeot ret = drm_atomic_helper_check_modeset(dev, state);
7912c9916cdSFrançois Tigeot if (ret)
7922c9916cdSFrançois Tigeot return ret;
7932c9916cdSFrançois Tigeot
7942c9916cdSFrançois Tigeot ret = drm_atomic_helper_check_planes(dev, state);
7952c9916cdSFrançois Tigeot if (ret)
7962c9916cdSFrançois Tigeot return ret;
7972c9916cdSFrançois Tigeot
798*3f2dd94aSFrançois Tigeot if (state->legacy_cursor_update)
799*3f2dd94aSFrançois Tigeot state->async_update = !drm_atomic_helper_async_check(dev, state);
800*3f2dd94aSFrançois Tigeot
8012c9916cdSFrançois Tigeot return ret;
8022c9916cdSFrançois Tigeot }
8032c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_check);
8042c9916cdSFrançois Tigeot
8052c9916cdSFrançois Tigeot static void
disable_outputs(struct drm_device * dev,struct drm_atomic_state * old_state)8062c9916cdSFrançois Tigeot disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
8072c9916cdSFrançois Tigeot {
808477eb7f9SFrançois Tigeot struct drm_connector *connector;
809a85cb24fSFrançois Tigeot struct drm_connector_state *old_conn_state, *new_conn_state;
810477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
811a85cb24fSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
8122c9916cdSFrançois Tigeot int i;
8132c9916cdSFrançois Tigeot
814a85cb24fSFrançois Tigeot for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) {
815477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *funcs;
8162c9916cdSFrançois Tigeot struct drm_encoder *encoder;
8172c9916cdSFrançois Tigeot
8182c9916cdSFrançois Tigeot /* Shut down everything that's in the changeset and currently
8192c9916cdSFrançois Tigeot * still on. So need to check the old, saved state. */
820477eb7f9SFrançois Tigeot if (!old_conn_state->crtc)
8212c9916cdSFrançois Tigeot continue;
8222c9916cdSFrançois Tigeot
823a85cb24fSFrançois Tigeot old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc);
8242c9916cdSFrançois Tigeot
825477eb7f9SFrançois Tigeot if (!old_crtc_state->active ||
82619c468b4SFrançois Tigeot !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
8272c9916cdSFrançois Tigeot continue;
8282c9916cdSFrançois Tigeot
8292c9916cdSFrançois Tigeot encoder = old_conn_state->best_encoder;
8302c9916cdSFrançois Tigeot
8312c9916cdSFrançois Tigeot /* We shouldn't get this far if we didn't previously have
8322c9916cdSFrançois Tigeot * an encoder.. but WARN_ON() rather than explode.
8332c9916cdSFrançois Tigeot */
8342c9916cdSFrançois Tigeot if (WARN_ON(!encoder))
8352c9916cdSFrançois Tigeot continue;
8362c9916cdSFrançois Tigeot
8372c9916cdSFrançois Tigeot funcs = encoder->helper_private;
8382c9916cdSFrançois Tigeot
839477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n",
8402c9916cdSFrançois Tigeot encoder->base.id, encoder->name);
8412c9916cdSFrançois Tigeot
8422c9916cdSFrançois Tigeot /*
8432c9916cdSFrançois Tigeot * Each encoder has at most one connector (since we always steal
844477eb7f9SFrançois Tigeot * it away), so we won't call disable hooks twice.
8452c9916cdSFrançois Tigeot */
84619c468b4SFrançois Tigeot drm_bridge_disable(encoder->bridge);
8472c9916cdSFrançois Tigeot
8482c9916cdSFrançois Tigeot /* Right function depends upon target state. */
8498621f407SFrançois Tigeot if (funcs) {
850a85cb24fSFrançois Tigeot if (new_conn_state->crtc && funcs->prepare)
8512c9916cdSFrançois Tigeot funcs->prepare(encoder);
8522c9916cdSFrançois Tigeot else if (funcs->disable)
8532c9916cdSFrançois Tigeot funcs->disable(encoder);
8548621f407SFrançois Tigeot else if (funcs->dpms)
8552c9916cdSFrançois Tigeot funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
8568621f407SFrançois Tigeot }
8572c9916cdSFrançois Tigeot
85819c468b4SFrançois Tigeot drm_bridge_post_disable(encoder->bridge);
8592c9916cdSFrançois Tigeot }
8602c9916cdSFrançois Tigeot
861a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
862477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
863*3f2dd94aSFrançois Tigeot int ret;
8642c9916cdSFrançois Tigeot
8652c9916cdSFrançois Tigeot /* Shut down everything that needs a full modeset. */
866a85cb24fSFrançois Tigeot if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
8672c9916cdSFrançois Tigeot continue;
8682c9916cdSFrançois Tigeot
8692c9916cdSFrançois Tigeot if (!old_crtc_state->active)
8702c9916cdSFrançois Tigeot continue;
8712c9916cdSFrançois Tigeot
8722c9916cdSFrançois Tigeot funcs = crtc->helper_private;
8732c9916cdSFrançois Tigeot
874aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("disabling [CRTC:%d:%s]\n",
875aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
8762c9916cdSFrançois Tigeot
8772c9916cdSFrançois Tigeot
8782c9916cdSFrançois Tigeot /* Right function depends upon target state. */
879a85cb24fSFrançois Tigeot if (new_crtc_state->enable && funcs->prepare)
8802c9916cdSFrançois Tigeot funcs->prepare(crtc);
8811dedbd3bSFrançois Tigeot else if (funcs->atomic_disable)
8821dedbd3bSFrançois Tigeot funcs->atomic_disable(crtc, old_crtc_state);
8832c9916cdSFrançois Tigeot else if (funcs->disable)
8842c9916cdSFrançois Tigeot funcs->disable(crtc);
8852c9916cdSFrançois Tigeot else
8862c9916cdSFrançois Tigeot funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
887*3f2dd94aSFrançois Tigeot
888*3f2dd94aSFrançois Tigeot if (!(dev->irq_enabled && dev->num_crtcs))
889*3f2dd94aSFrançois Tigeot continue;
890*3f2dd94aSFrançois Tigeot
891*3f2dd94aSFrançois Tigeot ret = drm_crtc_vblank_get(crtc);
892*3f2dd94aSFrançois Tigeot WARN_ONCE(ret != -EINVAL, "driver forgot to call drm_crtc_vblank_off()\n");
893*3f2dd94aSFrançois Tigeot if (ret == 0)
894*3f2dd94aSFrançois Tigeot drm_crtc_vblank_put(crtc);
8952c9916cdSFrançois Tigeot }
8962c9916cdSFrançois Tigeot }
8972c9916cdSFrançois Tigeot
89819c468b4SFrançois Tigeot /**
89919c468b4SFrançois Tigeot * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state
90019c468b4SFrançois Tigeot * @dev: DRM device
90119c468b4SFrançois Tigeot * @old_state: atomic state object with old state structures
90219c468b4SFrançois Tigeot *
90319c468b4SFrançois Tigeot * This function updates all the various legacy modeset state pointers in
90419c468b4SFrançois Tigeot * connectors, encoders and crtcs. It also updates the timestamping constants
90519c468b4SFrançois Tigeot * used for precise vblank timestamps by calling
90619c468b4SFrançois Tigeot * drm_calc_timestamping_constants().
90719c468b4SFrançois Tigeot *
90819c468b4SFrançois Tigeot * Drivers can use this for building their own atomic commit if they don't have
90919c468b4SFrançois Tigeot * a pure helper-based modeset implementation.
91019c468b4SFrançois Tigeot */
91119c468b4SFrançois Tigeot void
drm_atomic_helper_update_legacy_modeset_state(struct drm_device * dev,struct drm_atomic_state * old_state)91219c468b4SFrançois Tigeot drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
91319c468b4SFrançois Tigeot struct drm_atomic_state *old_state)
9142c9916cdSFrançois Tigeot {
915477eb7f9SFrançois Tigeot struct drm_connector *connector;
916a85cb24fSFrançois Tigeot struct drm_connector_state *old_conn_state, *new_conn_state;
917477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
918a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
9192c9916cdSFrançois Tigeot int i;
9202c9916cdSFrançois Tigeot
921a05eeebfSFrançois Tigeot /* clear out existing links and update dpms */
922a85cb24fSFrançois Tigeot for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) {
923a05eeebfSFrançois Tigeot if (connector->encoder) {
9242c9916cdSFrançois Tigeot WARN_ON(!connector->encoder->crtc);
9252c9916cdSFrançois Tigeot
9262c9916cdSFrançois Tigeot connector->encoder->crtc = NULL;
9272c9916cdSFrançois Tigeot connector->encoder = NULL;
9282c9916cdSFrançois Tigeot }
9292c9916cdSFrançois Tigeot
930a85cb24fSFrançois Tigeot crtc = new_conn_state->crtc;
931a05eeebfSFrançois Tigeot if ((!crtc && old_conn_state->crtc) ||
932a05eeebfSFrançois Tigeot (crtc && drm_atomic_crtc_needs_modeset(crtc->state))) {
933a05eeebfSFrançois Tigeot int mode = DRM_MODE_DPMS_OFF;
934a05eeebfSFrançois Tigeot
935a05eeebfSFrançois Tigeot if (crtc && crtc->state->active)
936a05eeebfSFrançois Tigeot mode = DRM_MODE_DPMS_ON;
937a05eeebfSFrançois Tigeot
938a05eeebfSFrançois Tigeot connector->dpms = mode;
939a05eeebfSFrançois Tigeot }
940a05eeebfSFrançois Tigeot }
941a05eeebfSFrançois Tigeot
9422c9916cdSFrançois Tigeot /* set new links */
943a85cb24fSFrançois Tigeot for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
944a85cb24fSFrançois Tigeot if (!new_conn_state->crtc)
9452c9916cdSFrançois Tigeot continue;
9462c9916cdSFrançois Tigeot
947a85cb24fSFrançois Tigeot if (WARN_ON(!new_conn_state->best_encoder))
9482c9916cdSFrançois Tigeot continue;
9492c9916cdSFrançois Tigeot
950a85cb24fSFrançois Tigeot connector->encoder = new_conn_state->best_encoder;
951a85cb24fSFrançois Tigeot connector->encoder->crtc = new_conn_state->crtc;
9522c9916cdSFrançois Tigeot }
9532c9916cdSFrançois Tigeot
9542c9916cdSFrançois Tigeot /* set legacy state in the crtc structure */
955a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
956a05eeebfSFrançois Tigeot struct drm_plane *primary = crtc->primary;
957a85cb24fSFrançois Tigeot struct drm_plane_state *new_plane_state;
958a05eeebfSFrançois Tigeot
959a85cb24fSFrançois Tigeot crtc->mode = new_crtc_state->mode;
960a85cb24fSFrançois Tigeot crtc->enabled = new_crtc_state->enable;
961a05eeebfSFrançois Tigeot
962a85cb24fSFrançois Tigeot new_plane_state =
963a85cb24fSFrançois Tigeot drm_atomic_get_new_plane_state(old_state, primary);
964a85cb24fSFrançois Tigeot
965a85cb24fSFrançois Tigeot if (new_plane_state && new_plane_state->crtc == crtc) {
966a85cb24fSFrançois Tigeot crtc->x = new_plane_state->src_x >> 16;
967a85cb24fSFrançois Tigeot crtc->y = new_plane_state->src_y >> 16;
968a05eeebfSFrançois Tigeot }
96919c468b4SFrançois Tigeot
970a85cb24fSFrançois Tigeot if (new_crtc_state->enable)
97119c468b4SFrançois Tigeot drm_calc_timestamping_constants(crtc,
972a85cb24fSFrançois Tigeot &new_crtc_state->adjusted_mode);
9732c9916cdSFrançois Tigeot }
9742c9916cdSFrançois Tigeot }
97519c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state);
9762c9916cdSFrançois Tigeot
9772c9916cdSFrançois Tigeot static void
crtc_set_mode(struct drm_device * dev,struct drm_atomic_state * old_state)9782c9916cdSFrançois Tigeot crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
9792c9916cdSFrançois Tigeot {
980477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
981a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
982477eb7f9SFrançois Tigeot struct drm_connector *connector;
983a85cb24fSFrançois Tigeot struct drm_connector_state *new_conn_state;
9842c9916cdSFrançois Tigeot int i;
9852c9916cdSFrançois Tigeot
986a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
987477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
9882c9916cdSFrançois Tigeot
989a85cb24fSFrançois Tigeot if (!new_crtc_state->mode_changed)
9902c9916cdSFrançois Tigeot continue;
9912c9916cdSFrançois Tigeot
9922c9916cdSFrançois Tigeot funcs = crtc->helper_private;
9932c9916cdSFrançois Tigeot
994a85cb24fSFrançois Tigeot if (new_crtc_state->enable && funcs->mode_set_nofb) {
995aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("modeset on [CRTC:%d:%s]\n",
996aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
9972c9916cdSFrançois Tigeot
9982c9916cdSFrançois Tigeot funcs->mode_set_nofb(crtc);
9992c9916cdSFrançois Tigeot }
10002c9916cdSFrançois Tigeot }
10012c9916cdSFrançois Tigeot
1002a85cb24fSFrançois Tigeot for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
1003477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *funcs;
10042c9916cdSFrançois Tigeot struct drm_encoder *encoder;
10052c9916cdSFrançois Tigeot struct drm_display_mode *mode, *adjusted_mode;
10062c9916cdSFrançois Tigeot
1007a85cb24fSFrançois Tigeot if (!new_conn_state->best_encoder)
10082c9916cdSFrançois Tigeot continue;
10092c9916cdSFrançois Tigeot
1010a85cb24fSFrançois Tigeot encoder = new_conn_state->best_encoder;
10112c9916cdSFrançois Tigeot funcs = encoder->helper_private;
1012a85cb24fSFrançois Tigeot new_crtc_state = new_conn_state->crtc->state;
10132c9916cdSFrançois Tigeot mode = &new_crtc_state->mode;
10142c9916cdSFrançois Tigeot adjusted_mode = &new_crtc_state->adjusted_mode;
10152c9916cdSFrançois Tigeot
10162c9916cdSFrançois Tigeot if (!new_crtc_state->mode_changed)
10172c9916cdSFrançois Tigeot continue;
10182c9916cdSFrançois Tigeot
1019477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("modeset on [ENCODER:%d:%s]\n",
10202c9916cdSFrançois Tigeot encoder->base.id, encoder->name);
10212c9916cdSFrançois Tigeot
10222c9916cdSFrançois Tigeot /*
10232c9916cdSFrançois Tigeot * Each encoder has at most one connector (since we always steal
1024477eb7f9SFrançois Tigeot * it away), so we won't call mode_set hooks twice.
10252c9916cdSFrançois Tigeot */
10261dedbd3bSFrançois Tigeot if (funcs && funcs->atomic_mode_set) {
10271dedbd3bSFrançois Tigeot funcs->atomic_mode_set(encoder, new_crtc_state,
1028a85cb24fSFrançois Tigeot new_conn_state);
10291dedbd3bSFrançois Tigeot } else if (funcs && funcs->mode_set) {
10302c9916cdSFrançois Tigeot funcs->mode_set(encoder, mode, adjusted_mode);
10311dedbd3bSFrançois Tigeot }
10322c9916cdSFrançois Tigeot
103319c468b4SFrançois Tigeot drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
10342c9916cdSFrançois Tigeot }
10352c9916cdSFrançois Tigeot }
10362c9916cdSFrançois Tigeot
10372c9916cdSFrançois Tigeot /**
1038477eb7f9SFrançois Tigeot * drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs
10392c9916cdSFrançois Tigeot * @dev: DRM device
10402c9916cdSFrançois Tigeot * @old_state: atomic state object with old state structures
10412c9916cdSFrançois Tigeot *
1042477eb7f9SFrançois Tigeot * This function shuts down all the outputs that need to be shut down and
1043477eb7f9SFrançois Tigeot * prepares them (if required) with the new mode.
1044477eb7f9SFrançois Tigeot *
1045a05eeebfSFrançois Tigeot * For compatibility with legacy crtc helpers this should be called before
1046477eb7f9SFrançois Tigeot * drm_atomic_helper_commit_planes(), which is what the default commit function
1047477eb7f9SFrançois Tigeot * does. But drivers with different needs can group the modeset commits together
1048477eb7f9SFrançois Tigeot * and do the plane commits at the end. This is useful for drivers doing runtime
1049477eb7f9SFrançois Tigeot * PM since planes updates then only happen when the CRTC is actually enabled.
10502c9916cdSFrançois Tigeot */
drm_atomic_helper_commit_modeset_disables(struct drm_device * dev,struct drm_atomic_state * old_state)1051477eb7f9SFrançois Tigeot void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
10522c9916cdSFrançois Tigeot struct drm_atomic_state *old_state)
10532c9916cdSFrançois Tigeot {
1054477eb7f9SFrançois Tigeot disable_outputs(dev, old_state);
105519c468b4SFrançois Tigeot
105619c468b4SFrançois Tigeot drm_atomic_helper_update_legacy_modeset_state(dev, old_state);
105719c468b4SFrançois Tigeot
1058477eb7f9SFrançois Tigeot crtc_set_mode(dev, old_state);
1059477eb7f9SFrançois Tigeot }
1060477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables);
1061477eb7f9SFrançois Tigeot
1062477eb7f9SFrançois Tigeot /**
1063477eb7f9SFrançois Tigeot * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs
1064477eb7f9SFrançois Tigeot * @dev: DRM device
1065477eb7f9SFrançois Tigeot * @old_state: atomic state object with old state structures
1066477eb7f9SFrançois Tigeot *
1067477eb7f9SFrançois Tigeot * This function enables all the outputs with the new configuration which had to
1068477eb7f9SFrançois Tigeot * be turned off for the update.
1069477eb7f9SFrançois Tigeot *
1070a05eeebfSFrançois Tigeot * For compatibility with legacy crtc helpers this should be called after
1071477eb7f9SFrançois Tigeot * drm_atomic_helper_commit_planes(), which is what the default commit function
1072477eb7f9SFrançois Tigeot * does. But drivers with different needs can group the modeset commits together
1073477eb7f9SFrançois Tigeot * and do the plane commits at the end. This is useful for drivers doing runtime
1074477eb7f9SFrançois Tigeot * PM since planes updates then only happen when the CRTC is actually enabled.
1075477eb7f9SFrançois Tigeot */
drm_atomic_helper_commit_modeset_enables(struct drm_device * dev,struct drm_atomic_state * old_state)1076477eb7f9SFrançois Tigeot void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
1077477eb7f9SFrançois Tigeot struct drm_atomic_state *old_state)
1078477eb7f9SFrançois Tigeot {
1079477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
1080*3f2dd94aSFrançois Tigeot struct drm_crtc_state *old_crtc_state;
1081a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
1082477eb7f9SFrançois Tigeot struct drm_connector *connector;
1083a85cb24fSFrançois Tigeot struct drm_connector_state *new_conn_state;
10842c9916cdSFrançois Tigeot int i;
10852c9916cdSFrançois Tigeot
1086*3f2dd94aSFrançois Tigeot for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
1087477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
10882c9916cdSFrançois Tigeot
10892c9916cdSFrançois Tigeot /* Need to filter out CRTCs where only planes change. */
1090a85cb24fSFrançois Tigeot if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
10912c9916cdSFrançois Tigeot continue;
10922c9916cdSFrançois Tigeot
1093a85cb24fSFrançois Tigeot if (!new_crtc_state->active)
10942c9916cdSFrançois Tigeot continue;
10952c9916cdSFrançois Tigeot
10962c9916cdSFrançois Tigeot funcs = crtc->helper_private;
10972c9916cdSFrançois Tigeot
1098a85cb24fSFrançois Tigeot if (new_crtc_state->enable) {
1099aee94f86SFrançois Tigeot DRM_DEBUG_ATOMIC("enabling [CRTC:%d:%s]\n",
1100aee94f86SFrançois Tigeot crtc->base.id, crtc->name);
11012c9916cdSFrançois Tigeot
1102*3f2dd94aSFrançois Tigeot if (funcs->atomic_enable)
1103*3f2dd94aSFrançois Tigeot funcs->atomic_enable(crtc, old_crtc_state);
11042c9916cdSFrançois Tigeot else
11052c9916cdSFrançois Tigeot funcs->commit(crtc);
11062c9916cdSFrançois Tigeot }
11072c9916cdSFrançois Tigeot }
11082c9916cdSFrançois Tigeot
1109a85cb24fSFrançois Tigeot for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
1110477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *funcs;
11112c9916cdSFrançois Tigeot struct drm_encoder *encoder;
11122c9916cdSFrançois Tigeot
1113a85cb24fSFrançois Tigeot if (!new_conn_state->best_encoder)
11142c9916cdSFrançois Tigeot continue;
11152c9916cdSFrançois Tigeot
1116a85cb24fSFrançois Tigeot if (!new_conn_state->crtc->state->active ||
1117a85cb24fSFrançois Tigeot !drm_atomic_crtc_needs_modeset(new_conn_state->crtc->state))
11182c9916cdSFrançois Tigeot continue;
11192c9916cdSFrançois Tigeot
1120a85cb24fSFrançois Tigeot encoder = new_conn_state->best_encoder;
11212c9916cdSFrançois Tigeot funcs = encoder->helper_private;
11222c9916cdSFrançois Tigeot
1123477eb7f9SFrançois Tigeot DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n",
11242c9916cdSFrançois Tigeot encoder->base.id, encoder->name);
11252c9916cdSFrançois Tigeot
11262c9916cdSFrançois Tigeot /*
11272c9916cdSFrançois Tigeot * Each encoder has at most one connector (since we always steal
1128477eb7f9SFrançois Tigeot * it away), so we won't call enable hooks twice.
11292c9916cdSFrançois Tigeot */
113019c468b4SFrançois Tigeot drm_bridge_pre_enable(encoder->bridge);
11312c9916cdSFrançois Tigeot
11328621f407SFrançois Tigeot if (funcs) {
11332c9916cdSFrançois Tigeot if (funcs->enable)
11342c9916cdSFrançois Tigeot funcs->enable(encoder);
11358621f407SFrançois Tigeot else if (funcs->commit)
11362c9916cdSFrançois Tigeot funcs->commit(encoder);
11378621f407SFrançois Tigeot }
11382c9916cdSFrançois Tigeot
113919c468b4SFrançois Tigeot drm_bridge_enable(encoder->bridge);
11402c9916cdSFrançois Tigeot }
11412c9916cdSFrançois Tigeot }
1142477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
11432c9916cdSFrançois Tigeot
11448621f407SFrançois Tigeot /**
11458621f407SFrançois Tigeot * drm_atomic_helper_wait_for_fences - wait for fences stashed in plane state
11468621f407SFrançois Tigeot * @dev: DRM device
11478621f407SFrançois Tigeot * @state: atomic state object with old state structures
11484be47400SFrançois Tigeot * @pre_swap: If true, do an interruptible wait, and @state is the new state.
11494be47400SFrançois Tigeot * Otherwise @state is the old state.
11508621f407SFrançois Tigeot *
11518621f407SFrançois Tigeot * For implicit sync, driver should fish the exclusive fence out from the
11528621f407SFrançois Tigeot * incoming fb's and stash it in the drm_plane_state. This is called after
11538621f407SFrançois Tigeot * drm_atomic_helper_swap_state() so it uses the current plane state (and
11548621f407SFrançois Tigeot * just uses the atomic state to find the changed planes)
11551dedbd3bSFrançois Tigeot *
11564be47400SFrançois Tigeot * Note that @pre_swap is needed since the point where we block for fences moves
11574be47400SFrançois Tigeot * around depending upon whether an atomic commit is blocking or
1158*3f2dd94aSFrançois Tigeot * non-blocking. For non-blocking commit all waiting needs to happen after
1159*3f2dd94aSFrançois Tigeot * drm_atomic_helper_swap_state() is called, but for blocking commits we want
11604be47400SFrançois Tigeot * to wait **before** we do anything that can't be easily rolled back. That is
11614be47400SFrançois Tigeot * before we call drm_atomic_helper_swap_state().
11624be47400SFrançois Tigeot *
11636559babbSFrançois Tigeot * Returns zero if success or < 0 if dma_fence_wait() fails.
11648621f407SFrançois Tigeot */
drm_atomic_helper_wait_for_fences(struct drm_device * dev,struct drm_atomic_state * state,bool pre_swap)11651dedbd3bSFrançois Tigeot int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
11661dedbd3bSFrançois Tigeot struct drm_atomic_state *state,
11671dedbd3bSFrançois Tigeot bool pre_swap)
11682c9916cdSFrançois Tigeot {
1169477eb7f9SFrançois Tigeot struct drm_plane *plane;
1170a85cb24fSFrançois Tigeot struct drm_plane_state *new_plane_state;
11711dedbd3bSFrançois Tigeot int i, ret;
11722c9916cdSFrançois Tigeot
1173a85cb24fSFrançois Tigeot for_each_new_plane_in_state(state, plane, new_plane_state, i) {
1174a85cb24fSFrançois Tigeot if (!new_plane_state->fence)
11752c9916cdSFrançois Tigeot continue;
11762c9916cdSFrançois Tigeot
1177a85cb24fSFrançois Tigeot WARN_ON(!new_plane_state->fb);
11782c9916cdSFrançois Tigeot
11791dedbd3bSFrançois Tigeot /*
11801dedbd3bSFrançois Tigeot * If waiting for fences pre-swap (ie: nonblock), userspace can
11811dedbd3bSFrançois Tigeot * still interrupt the operation. Instead of blocking until the
11821dedbd3bSFrançois Tigeot * timer expires, make the wait interruptible.
11831dedbd3bSFrançois Tigeot */
1184a85cb24fSFrançois Tigeot ret = dma_fence_wait(new_plane_state->fence, pre_swap);
11851dedbd3bSFrançois Tigeot if (ret)
11861dedbd3bSFrançois Tigeot return ret;
11871dedbd3bSFrançois Tigeot
1188a85cb24fSFrançois Tigeot dma_fence_put(new_plane_state->fence);
1189a85cb24fSFrançois Tigeot new_plane_state->fence = NULL;
11902c9916cdSFrançois Tigeot }
11911dedbd3bSFrançois Tigeot
11921dedbd3bSFrançois Tigeot return 0;
11932c9916cdSFrançois Tigeot }
11948621f407SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences);
11952c9916cdSFrançois Tigeot
1196aee94f86SFrançois Tigeot /**
11972c9916cdSFrançois Tigeot * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs
11982c9916cdSFrançois Tigeot * @dev: DRM device
11992c9916cdSFrançois Tigeot * @old_state: atomic state object with old state structures
12002c9916cdSFrançois Tigeot *
12012c9916cdSFrançois Tigeot * Helper to, after atomic commit, wait for vblanks on all effected
12022c9916cdSFrançois Tigeot * crtcs (ie. before cleaning up old framebuffers using
1203*3f2dd94aSFrançois Tigeot * drm_atomic_helper_cleanup_planes()). It will only wait on CRTCs where the
12042c9916cdSFrançois Tigeot * framebuffers have actually changed to optimize for the legacy cursor and
12052c9916cdSFrançois Tigeot * plane update use-case.
1206*3f2dd94aSFrançois Tigeot *
1207*3f2dd94aSFrançois Tigeot * Drivers using the nonblocking commit tracking support initialized by calling
1208*3f2dd94aSFrançois Tigeot * drm_atomic_helper_setup_commit() should look at
1209*3f2dd94aSFrançois Tigeot * drm_atomic_helper_wait_for_flip_done() as an alternative.
12102c9916cdSFrançois Tigeot */
12112c9916cdSFrançois Tigeot void
drm_atomic_helper_wait_for_vblanks(struct drm_device * dev,struct drm_atomic_state * old_state)12122c9916cdSFrançois Tigeot drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
12132c9916cdSFrançois Tigeot struct drm_atomic_state *old_state)
12142c9916cdSFrançois Tigeot {
12152c9916cdSFrançois Tigeot struct drm_crtc *crtc;
1216a85cb24fSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
12172c9916cdSFrançois Tigeot int i, ret;
1218a85cb24fSFrançois Tigeot unsigned crtc_mask = 0;
12192c9916cdSFrançois Tigeot
1220a85cb24fSFrançois Tigeot /*
1221a85cb24fSFrançois Tigeot * Legacy cursor ioctls are completely unsynced, and userspace
1222a85cb24fSFrançois Tigeot * relies on that (by doing tons of cursor updates).
1223a85cb24fSFrançois Tigeot */
12242c9916cdSFrançois Tigeot if (old_state->legacy_cursor_update)
1225a85cb24fSFrançois Tigeot return;
12262c9916cdSFrançois Tigeot
1227a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
1228*3f2dd94aSFrançois Tigeot if (!new_crtc_state->active)
12292c9916cdSFrançois Tigeot continue;
12302c9916cdSFrançois Tigeot
12312c9916cdSFrançois Tigeot ret = drm_crtc_vblank_get(crtc);
12322c9916cdSFrançois Tigeot if (ret != 0)
12332c9916cdSFrançois Tigeot continue;
12342c9916cdSFrançois Tigeot
1235a85cb24fSFrançois Tigeot crtc_mask |= drm_crtc_mask(crtc);
1236a85cb24fSFrançois Tigeot old_state->crtcs[i].last_vblank_count = drm_crtc_vblank_count(crtc);
12372c9916cdSFrançois Tigeot }
12382c9916cdSFrançois Tigeot
1239a85cb24fSFrançois Tigeot for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
1240a85cb24fSFrançois Tigeot if (!(crtc_mask & drm_crtc_mask(crtc)))
12412c9916cdSFrançois Tigeot continue;
12422c9916cdSFrançois Tigeot
12432c9916cdSFrançois Tigeot ret = wait_event_timeout(dev->vblank[i].queue,
1244a85cb24fSFrançois Tigeot old_state->crtcs[i].last_vblank_count !=
1245a05eeebfSFrançois Tigeot drm_crtc_vblank_count(crtc),
12462c9916cdSFrançois Tigeot msecs_to_jiffies(50));
12472c9916cdSFrançois Tigeot
1248a85cb24fSFrançois Tigeot WARN(!ret, "[CRTC:%d:%s] vblank wait timed out\n",
1249a85cb24fSFrançois Tigeot crtc->base.id, crtc->name);
12508621f407SFrançois Tigeot
12512c9916cdSFrançois Tigeot drm_crtc_vblank_put(crtc);
12522c9916cdSFrançois Tigeot }
12532c9916cdSFrançois Tigeot }
12542c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
12552c9916cdSFrançois Tigeot
12562c9916cdSFrançois Tigeot /**
1257*3f2dd94aSFrançois Tigeot * drm_atomic_helper_wait_for_flip_done - wait for all page flips to be done
1258*3f2dd94aSFrançois Tigeot * @dev: DRM device
1259*3f2dd94aSFrançois Tigeot * @old_state: atomic state object with old state structures
1260*3f2dd94aSFrançois Tigeot *
1261*3f2dd94aSFrançois Tigeot * Helper to, after atomic commit, wait for page flips on all effected
1262*3f2dd94aSFrançois Tigeot * crtcs (ie. before cleaning up old framebuffers using
1263*3f2dd94aSFrançois Tigeot * drm_atomic_helper_cleanup_planes()). Compared to
1264*3f2dd94aSFrançois Tigeot * drm_atomic_helper_wait_for_vblanks() this waits for the completion of on all
1265*3f2dd94aSFrançois Tigeot * CRTCs, assuming that cursors-only updates are signalling their completion
1266*3f2dd94aSFrançois Tigeot * immediately (or using a different path).
1267*3f2dd94aSFrançois Tigeot *
1268*3f2dd94aSFrançois Tigeot * This requires that drivers use the nonblocking commit tracking support
1269*3f2dd94aSFrançois Tigeot * initialized using drm_atomic_helper_setup_commit().
1270*3f2dd94aSFrançois Tigeot */
drm_atomic_helper_wait_for_flip_done(struct drm_device * dev,struct drm_atomic_state * old_state)1271*3f2dd94aSFrançois Tigeot void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev,
1272*3f2dd94aSFrançois Tigeot struct drm_atomic_state *old_state)
1273*3f2dd94aSFrançois Tigeot {
1274*3f2dd94aSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
1275*3f2dd94aSFrançois Tigeot struct drm_crtc *crtc;
1276*3f2dd94aSFrançois Tigeot int i;
1277*3f2dd94aSFrançois Tigeot
1278*3f2dd94aSFrançois Tigeot for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
1279*3f2dd94aSFrançois Tigeot struct drm_crtc_commit *commit = new_crtc_state->commit;
1280*3f2dd94aSFrançois Tigeot int ret;
1281*3f2dd94aSFrançois Tigeot
1282*3f2dd94aSFrançois Tigeot if (!commit)
1283*3f2dd94aSFrançois Tigeot continue;
1284*3f2dd94aSFrançois Tigeot
1285*3f2dd94aSFrançois Tigeot ret = wait_for_completion_timeout(&commit->flip_done, 10 * HZ);
1286*3f2dd94aSFrançois Tigeot if (ret == 0)
1287*3f2dd94aSFrançois Tigeot DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
1288*3f2dd94aSFrançois Tigeot crtc->base.id, crtc->name);
1289*3f2dd94aSFrançois Tigeot }
1290*3f2dd94aSFrançois Tigeot }
1291*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done);
1292*3f2dd94aSFrançois Tigeot
1293*3f2dd94aSFrançois Tigeot /**
12941dedbd3bSFrançois Tigeot * drm_atomic_helper_commit_tail - commit atomic update to hardware
12954be47400SFrançois Tigeot * @old_state: atomic state object with old state structures
12962c9916cdSFrançois Tigeot *
1297a85cb24fSFrançois Tigeot * This is the default implementation for the
1298*3f2dd94aSFrançois Tigeot * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers
1299*3f2dd94aSFrançois Tigeot * that do not support runtime_pm or do not need the CRTC to be
1300*3f2dd94aSFrançois Tigeot * enabled to perform a commit. Otherwise, see
1301*3f2dd94aSFrançois Tigeot * drm_atomic_helper_commit_tail_rpm().
13022c9916cdSFrançois Tigeot *
13031dedbd3bSFrançois Tigeot * Note that the default ordering of how the various stages are called is to
1304*3f2dd94aSFrançois Tigeot * match the legacy modeset helper library closest.
13051dedbd3bSFrançois Tigeot */
drm_atomic_helper_commit_tail(struct drm_atomic_state * old_state)13064be47400SFrançois Tigeot void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state)
13071dedbd3bSFrançois Tigeot {
13084be47400SFrançois Tigeot struct drm_device *dev = old_state->dev;
13091dedbd3bSFrançois Tigeot
13104be47400SFrançois Tigeot drm_atomic_helper_commit_modeset_disables(dev, old_state);
13111dedbd3bSFrançois Tigeot
13124be47400SFrançois Tigeot drm_atomic_helper_commit_planes(dev, old_state, 0);
13131dedbd3bSFrançois Tigeot
13144be47400SFrançois Tigeot drm_atomic_helper_commit_modeset_enables(dev, old_state);
13151dedbd3bSFrançois Tigeot
13164be47400SFrançois Tigeot drm_atomic_helper_commit_hw_done(old_state);
13171dedbd3bSFrançois Tigeot
13184be47400SFrançois Tigeot drm_atomic_helper_wait_for_vblanks(dev, old_state);
13191dedbd3bSFrançois Tigeot
13204be47400SFrançois Tigeot drm_atomic_helper_cleanup_planes(dev, old_state);
13211dedbd3bSFrançois Tigeot }
13221dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_tail);
13231dedbd3bSFrançois Tigeot
1324*3f2dd94aSFrançois Tigeot /**
1325*3f2dd94aSFrançois Tigeot * drm_atomic_helper_commit_tail_rpm - commit atomic update to hardware
1326*3f2dd94aSFrançois Tigeot * @old_state: new modeset state to be committed
1327*3f2dd94aSFrançois Tigeot *
1328*3f2dd94aSFrançois Tigeot * This is an alternative implementation for the
1329*3f2dd94aSFrançois Tigeot * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers
1330*3f2dd94aSFrançois Tigeot * that support runtime_pm or need the CRTC to be enabled to perform a
1331*3f2dd94aSFrançois Tigeot * commit. Otherwise, one should use the default implementation
1332*3f2dd94aSFrançois Tigeot * drm_atomic_helper_commit_tail().
1333*3f2dd94aSFrançois Tigeot */
drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state * old_state)1334*3f2dd94aSFrançois Tigeot void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
1335*3f2dd94aSFrançois Tigeot {
1336*3f2dd94aSFrançois Tigeot struct drm_device *dev = old_state->dev;
1337*3f2dd94aSFrançois Tigeot
1338*3f2dd94aSFrançois Tigeot drm_atomic_helper_commit_modeset_disables(dev, old_state);
1339*3f2dd94aSFrançois Tigeot
1340*3f2dd94aSFrançois Tigeot drm_atomic_helper_commit_modeset_enables(dev, old_state);
1341*3f2dd94aSFrançois Tigeot
1342*3f2dd94aSFrançois Tigeot drm_atomic_helper_commit_planes(dev, old_state,
1343*3f2dd94aSFrançois Tigeot DRM_PLANE_COMMIT_ACTIVE_ONLY);
1344*3f2dd94aSFrançois Tigeot
1345*3f2dd94aSFrançois Tigeot drm_atomic_helper_commit_hw_done(old_state);
1346*3f2dd94aSFrançois Tigeot
1347*3f2dd94aSFrançois Tigeot drm_atomic_helper_wait_for_vblanks(dev, old_state);
1348*3f2dd94aSFrançois Tigeot
1349*3f2dd94aSFrançois Tigeot drm_atomic_helper_cleanup_planes(dev, old_state);
1350*3f2dd94aSFrançois Tigeot }
1351*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_tail_rpm);
1352*3f2dd94aSFrançois Tigeot
commit_tail(struct drm_atomic_state * old_state)13534be47400SFrançois Tigeot static void commit_tail(struct drm_atomic_state *old_state)
13541dedbd3bSFrançois Tigeot {
13554be47400SFrançois Tigeot struct drm_device *dev = old_state->dev;
1356a85cb24fSFrançois Tigeot const struct drm_mode_config_helper_funcs *funcs;
13571dedbd3bSFrançois Tigeot
13581dedbd3bSFrançois Tigeot funcs = dev->mode_config.helper_private;
13591dedbd3bSFrançois Tigeot
13604be47400SFrançois Tigeot drm_atomic_helper_wait_for_fences(dev, old_state, false);
13611dedbd3bSFrançois Tigeot
13624be47400SFrançois Tigeot drm_atomic_helper_wait_for_dependencies(old_state);
13631dedbd3bSFrançois Tigeot
13641dedbd3bSFrançois Tigeot if (funcs && funcs->atomic_commit_tail)
13654be47400SFrançois Tigeot funcs->atomic_commit_tail(old_state);
13661dedbd3bSFrançois Tigeot else
13674be47400SFrançois Tigeot drm_atomic_helper_commit_tail(old_state);
13681dedbd3bSFrançois Tigeot
13694be47400SFrançois Tigeot drm_atomic_helper_commit_cleanup_done(old_state);
13701dedbd3bSFrançois Tigeot
13714be47400SFrançois Tigeot drm_atomic_state_put(old_state);
13721dedbd3bSFrançois Tigeot }
13731dedbd3bSFrançois Tigeot
commit_work(struct work_struct * work)13741dedbd3bSFrançois Tigeot static void commit_work(struct work_struct *work)
13751dedbd3bSFrançois Tigeot {
13761dedbd3bSFrançois Tigeot struct drm_atomic_state *state = container_of(work,
13771dedbd3bSFrançois Tigeot struct drm_atomic_state,
13781dedbd3bSFrançois Tigeot commit_work);
13791dedbd3bSFrançois Tigeot commit_tail(state);
13801dedbd3bSFrançois Tigeot }
13811dedbd3bSFrançois Tigeot
13821dedbd3bSFrançois Tigeot /**
1383*3f2dd94aSFrançois Tigeot * drm_atomic_helper_async_check - check if state can be commited asynchronously
1384*3f2dd94aSFrançois Tigeot * @dev: DRM device
1385*3f2dd94aSFrançois Tigeot * @state: the driver state object
1386*3f2dd94aSFrançois Tigeot *
1387*3f2dd94aSFrançois Tigeot * This helper will check if it is possible to commit the state asynchronously.
1388*3f2dd94aSFrançois Tigeot * Async commits are not supposed to swap the states like normal sync commits
1389*3f2dd94aSFrançois Tigeot * but just do in-place changes on the current state.
1390*3f2dd94aSFrançois Tigeot *
1391*3f2dd94aSFrançois Tigeot * It will return 0 if the commit can happen in an asynchronous fashion or error
1392*3f2dd94aSFrançois Tigeot * if not. Note that error just mean it can't be commited asynchronously, if it
1393*3f2dd94aSFrançois Tigeot * fails the commit should be treated like a normal synchronous commit.
1394*3f2dd94aSFrançois Tigeot */
drm_atomic_helper_async_check(struct drm_device * dev,struct drm_atomic_state * state)1395*3f2dd94aSFrançois Tigeot int drm_atomic_helper_async_check(struct drm_device *dev,
1396*3f2dd94aSFrançois Tigeot struct drm_atomic_state *state)
1397*3f2dd94aSFrançois Tigeot {
1398*3f2dd94aSFrançois Tigeot struct drm_crtc *crtc;
1399*3f2dd94aSFrançois Tigeot struct drm_crtc_state *crtc_state;
1400*3f2dd94aSFrançois Tigeot struct drm_plane *plane;
1401*3f2dd94aSFrançois Tigeot struct drm_plane_state *old_plane_state, *new_plane_state;
1402*3f2dd94aSFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
1403*3f2dd94aSFrançois Tigeot int i, n_planes = 0;
1404*3f2dd94aSFrançois Tigeot
1405*3f2dd94aSFrançois Tigeot for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
1406*3f2dd94aSFrançois Tigeot if (drm_atomic_crtc_needs_modeset(crtc_state))
1407*3f2dd94aSFrançois Tigeot return -EINVAL;
1408*3f2dd94aSFrançois Tigeot }
1409*3f2dd94aSFrançois Tigeot
1410*3f2dd94aSFrançois Tigeot for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i)
1411*3f2dd94aSFrançois Tigeot n_planes++;
1412*3f2dd94aSFrançois Tigeot
1413*3f2dd94aSFrançois Tigeot /* FIXME: we support only single plane updates for now */
1414*3f2dd94aSFrançois Tigeot if (n_planes != 1)
1415*3f2dd94aSFrançois Tigeot return -EINVAL;
1416*3f2dd94aSFrançois Tigeot
1417*3f2dd94aSFrançois Tigeot if (!new_plane_state->crtc)
1418*3f2dd94aSFrançois Tigeot return -EINVAL;
1419*3f2dd94aSFrançois Tigeot
1420*3f2dd94aSFrançois Tigeot funcs = plane->helper_private;
1421*3f2dd94aSFrançois Tigeot if (!funcs->atomic_async_update)
1422*3f2dd94aSFrançois Tigeot return -EINVAL;
1423*3f2dd94aSFrançois Tigeot
1424*3f2dd94aSFrançois Tigeot if (new_plane_state->fence)
1425*3f2dd94aSFrançois Tigeot return -EINVAL;
1426*3f2dd94aSFrançois Tigeot
1427*3f2dd94aSFrançois Tigeot /*
1428*3f2dd94aSFrançois Tigeot * Don't do an async update if there is an outstanding commit modifying
1429*3f2dd94aSFrançois Tigeot * the plane. This prevents our async update's changes from getting
1430*3f2dd94aSFrançois Tigeot * overridden by a previous synchronous update's state.
1431*3f2dd94aSFrançois Tigeot */
1432*3f2dd94aSFrançois Tigeot if (old_plane_state->commit &&
1433*3f2dd94aSFrançois Tigeot !try_wait_for_completion(&old_plane_state->commit->hw_done))
1434*3f2dd94aSFrançois Tigeot return -EBUSY;
1435*3f2dd94aSFrançois Tigeot
1436*3f2dd94aSFrançois Tigeot return funcs->atomic_async_check(plane, new_plane_state);
1437*3f2dd94aSFrançois Tigeot }
1438*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_async_check);
1439*3f2dd94aSFrançois Tigeot
1440*3f2dd94aSFrançois Tigeot /**
1441*3f2dd94aSFrançois Tigeot * drm_atomic_helper_async_commit - commit state asynchronously
1442*3f2dd94aSFrançois Tigeot * @dev: DRM device
1443*3f2dd94aSFrançois Tigeot * @state: the driver state object
1444*3f2dd94aSFrançois Tigeot *
1445*3f2dd94aSFrançois Tigeot * This function commits a state asynchronously, i.e., not vblank
1446*3f2dd94aSFrançois Tigeot * synchronized. It should be used on a state only when
1447*3f2dd94aSFrançois Tigeot * drm_atomic_async_check() succeeds. Async commits are not supposed to swap
1448*3f2dd94aSFrançois Tigeot * the states like normal sync commits, but just do in-place changes on the
1449*3f2dd94aSFrançois Tigeot * current state.
1450*3f2dd94aSFrançois Tigeot */
drm_atomic_helper_async_commit(struct drm_device * dev,struct drm_atomic_state * state)1451*3f2dd94aSFrançois Tigeot void drm_atomic_helper_async_commit(struct drm_device *dev,
1452*3f2dd94aSFrançois Tigeot struct drm_atomic_state *state)
1453*3f2dd94aSFrançois Tigeot {
1454*3f2dd94aSFrançois Tigeot struct drm_plane *plane;
1455*3f2dd94aSFrançois Tigeot struct drm_plane_state *plane_state;
1456*3f2dd94aSFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
1457*3f2dd94aSFrançois Tigeot int i;
1458*3f2dd94aSFrançois Tigeot
1459*3f2dd94aSFrançois Tigeot for_each_new_plane_in_state(state, plane, plane_state, i) {
1460*3f2dd94aSFrançois Tigeot funcs = plane->helper_private;
1461*3f2dd94aSFrançois Tigeot funcs->atomic_async_update(plane, plane_state);
1462*3f2dd94aSFrançois Tigeot }
1463*3f2dd94aSFrançois Tigeot }
1464*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_async_commit);
1465*3f2dd94aSFrançois Tigeot
1466*3f2dd94aSFrançois Tigeot /**
14671dedbd3bSFrançois Tigeot * drm_atomic_helper_commit - commit validated state object
14681dedbd3bSFrançois Tigeot * @dev: DRM device
14691dedbd3bSFrançois Tigeot * @state: the driver state object
14701dedbd3bSFrançois Tigeot * @nonblock: whether nonblocking behavior is requested.
1471352ff8bdSFrançois Tigeot *
14721dedbd3bSFrançois Tigeot * This function commits a with drm_atomic_helper_check() pre-validated state
14731dedbd3bSFrançois Tigeot * object. This can still fail when e.g. the framebuffer reservation fails. This
14741dedbd3bSFrançois Tigeot * function implements nonblocking commits, using
14751dedbd3bSFrançois Tigeot * drm_atomic_helper_setup_commit() and related functions.
14761dedbd3bSFrançois Tigeot *
14771dedbd3bSFrançois Tigeot * Committing the actual hardware state is done through the
1478a85cb24fSFrançois Tigeot * &drm_mode_config_helper_funcs.atomic_commit_tail callback, or it's default
1479a85cb24fSFrançois Tigeot * implementation drm_atomic_helper_commit_tail().
14801dedbd3bSFrançois Tigeot *
14811dedbd3bSFrançois Tigeot * RETURNS:
14822c9916cdSFrançois Tigeot * Zero for success or -errno.
14832c9916cdSFrançois Tigeot */
drm_atomic_helper_commit(struct drm_device * dev,struct drm_atomic_state * state,bool nonblock)14842c9916cdSFrançois Tigeot int drm_atomic_helper_commit(struct drm_device *dev,
14852c9916cdSFrançois Tigeot struct drm_atomic_state *state,
14868621f407SFrançois Tigeot bool nonblock)
14872c9916cdSFrançois Tigeot {
14882c9916cdSFrançois Tigeot int ret;
14892c9916cdSFrançois Tigeot
1490*3f2dd94aSFrançois Tigeot if (state->async_update) {
1491*3f2dd94aSFrançois Tigeot ret = drm_atomic_helper_prepare_planes(dev, state);
1492*3f2dd94aSFrançois Tigeot if (ret)
1493*3f2dd94aSFrançois Tigeot return ret;
1494*3f2dd94aSFrançois Tigeot
1495*3f2dd94aSFrançois Tigeot drm_atomic_helper_async_commit(dev, state);
1496*3f2dd94aSFrançois Tigeot drm_atomic_helper_cleanup_planes(dev, state);
1497*3f2dd94aSFrançois Tigeot
1498*3f2dd94aSFrançois Tigeot return 0;
1499*3f2dd94aSFrançois Tigeot }
1500*3f2dd94aSFrançois Tigeot
15011dedbd3bSFrançois Tigeot ret = drm_atomic_helper_setup_commit(state, nonblock);
15021dedbd3bSFrançois Tigeot if (ret)
15031dedbd3bSFrançois Tigeot return ret;
15041dedbd3bSFrançois Tigeot
15051dedbd3bSFrançois Tigeot INIT_WORK(&state->commit_work, commit_work);
15062c9916cdSFrançois Tigeot
15072c9916cdSFrançois Tigeot ret = drm_atomic_helper_prepare_planes(dev, state);
15082c9916cdSFrançois Tigeot if (ret)
15092c9916cdSFrançois Tigeot return ret;
15102c9916cdSFrançois Tigeot
15111dedbd3bSFrançois Tigeot if (!nonblock) {
15121dedbd3bSFrançois Tigeot ret = drm_atomic_helper_wait_for_fences(dev, state, true);
1513*3f2dd94aSFrançois Tigeot if (ret)
1514*3f2dd94aSFrançois Tigeot goto err;
15154be47400SFrançois Tigeot }
15161dedbd3bSFrançois Tigeot
15172c9916cdSFrançois Tigeot /*
15182c9916cdSFrançois Tigeot * This is the point of no return - everything below never fails except
15192c9916cdSFrançois Tigeot * when the hw goes bonghits. Which means we can commit the new state on
15202c9916cdSFrançois Tigeot * the software side now.
15212c9916cdSFrançois Tigeot */
15222c9916cdSFrançois Tigeot
1523*3f2dd94aSFrançois Tigeot ret = drm_atomic_helper_swap_state(state, true);
1524*3f2dd94aSFrançois Tigeot if (ret)
1525*3f2dd94aSFrançois Tigeot goto err;
15262c9916cdSFrançois Tigeot
15272c9916cdSFrançois Tigeot /*
15282c9916cdSFrançois Tigeot * Everything below can be run asynchronously without the need to grab
1529477eb7f9SFrançois Tigeot * any modeset locks at all under one condition: It must be guaranteed
15302c9916cdSFrançois Tigeot * that the asynchronous work has either been cancelled (if the driver
15312c9916cdSFrançois Tigeot * supports it, which at least requires that the framebuffers get
15322c9916cdSFrançois Tigeot * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
15332c9916cdSFrançois Tigeot * before the new state gets committed on the software side with
15342c9916cdSFrançois Tigeot * drm_atomic_helper_swap_state().
15352c9916cdSFrançois Tigeot *
15362c9916cdSFrançois Tigeot * This scheme allows new atomic state updates to be prepared and
15372c9916cdSFrançois Tigeot * checked in parallel to the asynchronous completion of the previous
15382c9916cdSFrançois Tigeot * update. Which is important since compositors need to figure out the
15392c9916cdSFrançois Tigeot * composition of the next frame right after having submitted the
15402c9916cdSFrançois Tigeot * current layout.
15411dedbd3bSFrançois Tigeot *
15421dedbd3bSFrançois Tigeot * NOTE: Commit work has multiple phases, first hardware commit, then
15431dedbd3bSFrançois Tigeot * cleanup. We want them to overlap, hence need system_unbound_wq to
15441dedbd3bSFrançois Tigeot * make sure work items don't artifically stall on each another.
15452c9916cdSFrançois Tigeot */
15462c9916cdSFrançois Tigeot
15474be47400SFrançois Tigeot drm_atomic_state_get(state);
15481dedbd3bSFrançois Tigeot if (nonblock)
15491dedbd3bSFrançois Tigeot queue_work(system_unbound_wq, &state->commit_work);
15501dedbd3bSFrançois Tigeot else
15511dedbd3bSFrançois Tigeot commit_tail(state);
15522c9916cdSFrançois Tigeot
15532c9916cdSFrançois Tigeot return 0;
1554*3f2dd94aSFrançois Tigeot
1555*3f2dd94aSFrançois Tigeot err:
1556*3f2dd94aSFrançois Tigeot drm_atomic_helper_cleanup_planes(dev, state);
1557*3f2dd94aSFrançois Tigeot return ret;
15582c9916cdSFrançois Tigeot }
15592c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit);
15602c9916cdSFrançois Tigeot
15612c9916cdSFrançois Tigeot /**
15628621f407SFrançois Tigeot * DOC: implementing nonblocking commit
15632c9916cdSFrançois Tigeot *
15641dedbd3bSFrançois Tigeot * Nonblocking atomic commits have to be implemented in the following sequence:
15652c9916cdSFrançois Tigeot *
15662c9916cdSFrançois Tigeot * 1. Run drm_atomic_helper_prepare_planes() first. This is the only function
15672c9916cdSFrançois Tigeot * which commit needs to call which can fail, so we want to run it first and
15682c9916cdSFrançois Tigeot * synchronously.
15692c9916cdSFrançois Tigeot *
15708621f407SFrançois Tigeot * 2. Synchronize with any outstanding nonblocking commit worker threads which
15712c9916cdSFrançois Tigeot * might be affected the new state update. This can be done by either cancelling
15722c9916cdSFrançois Tigeot * or flushing the work items, depending upon whether the driver can deal with
15732c9916cdSFrançois Tigeot * cancelled updates. Note that it is important to ensure that the framebuffer
15742c9916cdSFrançois Tigeot * cleanup is still done when cancelling.
15752c9916cdSFrançois Tigeot *
15761dedbd3bSFrançois Tigeot * Asynchronous workers need to have sufficient parallelism to be able to run
15771dedbd3bSFrançois Tigeot * different atomic commits on different CRTCs in parallel. The simplest way to
15781dedbd3bSFrançois Tigeot * achive this is by running them on the &system_unbound_wq work queue. Note
15791dedbd3bSFrançois Tigeot * that drivers are not required to split up atomic commits and run an
15801dedbd3bSFrançois Tigeot * individual commit in parallel - userspace is supposed to do that if it cares.
15811dedbd3bSFrançois Tigeot * But it might be beneficial to do that for modesets, since those necessarily
15821dedbd3bSFrançois Tigeot * must be done as one global operation, and enabling or disabling a CRTC can
15831dedbd3bSFrançois Tigeot * take a long time. But even that is not required.
15842c9916cdSFrançois Tigeot *
15852c9916cdSFrançois Tigeot * 3. The software state is updated synchronously with
1586352ff8bdSFrançois Tigeot * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
15872c9916cdSFrançois Tigeot * locks means concurrent callers never see inconsistent state. And doing this
15888621f407SFrançois Tigeot * while it's guaranteed that no relevant nonblocking worker runs means that
15898621f407SFrançois Tigeot * nonblocking workers do not need grab any locks. Actually they must not grab
15908621f407SFrançois Tigeot * locks, for otherwise the work flushing will deadlock.
15912c9916cdSFrançois Tigeot *
15922c9916cdSFrançois Tigeot * 4. Schedule a work item to do all subsequent steps, using the split-out
15932c9916cdSFrançois Tigeot * commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and
15942c9916cdSFrançois Tigeot * then cleaning up the framebuffers after the old framebuffer is no longer
15952c9916cdSFrançois Tigeot * being displayed.
15961dedbd3bSFrançois Tigeot *
15971dedbd3bSFrançois Tigeot * The above scheme is implemented in the atomic helper libraries in
15981dedbd3bSFrançois Tigeot * drm_atomic_helper_commit() using a bunch of helper functions. See
15991dedbd3bSFrançois Tigeot * drm_atomic_helper_setup_commit() for a starting point.
16002c9916cdSFrançois Tigeot */
16012c9916cdSFrançois Tigeot
stall_checks(struct drm_crtc * crtc,bool nonblock)16021dedbd3bSFrançois Tigeot static int stall_checks(struct drm_crtc *crtc, bool nonblock)
16031dedbd3bSFrançois Tigeot {
16041dedbd3bSFrançois Tigeot struct drm_crtc_commit *commit, *stall_commit = NULL;
16051dedbd3bSFrançois Tigeot bool completed = true;
16061dedbd3bSFrançois Tigeot int i;
16071dedbd3bSFrançois Tigeot long ret = 0;
16081dedbd3bSFrançois Tigeot
1609ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_EXCLUSIVE);
16101dedbd3bSFrançois Tigeot i = 0;
16111dedbd3bSFrançois Tigeot list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
16121dedbd3bSFrançois Tigeot if (i == 0) {
16131dedbd3bSFrançois Tigeot completed = try_wait_for_completion(&commit->flip_done);
16141dedbd3bSFrançois Tigeot /* Userspace is not allowed to get ahead of the previous
16151dedbd3bSFrançois Tigeot * commit with nonblocking ones. */
16161dedbd3bSFrançois Tigeot if (!completed && nonblock) {
1617ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_RELEASE);
16181dedbd3bSFrançois Tigeot return -EBUSY;
16191dedbd3bSFrançois Tigeot }
16201dedbd3bSFrançois Tigeot } else if (i == 1) {
1621*3f2dd94aSFrançois Tigeot stall_commit = drm_crtc_commit_get(commit);
16221dedbd3bSFrançois Tigeot break;
16231dedbd3bSFrançois Tigeot }
16241dedbd3bSFrançois Tigeot
16251dedbd3bSFrançois Tigeot i++;
16261dedbd3bSFrançois Tigeot }
1627ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_RELEASE);
16281dedbd3bSFrançois Tigeot
16291dedbd3bSFrançois Tigeot if (!stall_commit)
16301dedbd3bSFrançois Tigeot return 0;
16311dedbd3bSFrançois Tigeot
16321dedbd3bSFrançois Tigeot /* We don't want to let commits get ahead of cleanup work too much,
16331dedbd3bSFrançois Tigeot * stalling on 2nd previous commit means triple-buffer won't ever stall.
16341dedbd3bSFrançois Tigeot */
16351dedbd3bSFrançois Tigeot ret = wait_for_completion_interruptible_timeout(&stall_commit->cleanup_done,
16361dedbd3bSFrançois Tigeot 10*HZ);
16371dedbd3bSFrançois Tigeot if (ret == 0)
16381dedbd3bSFrançois Tigeot DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n",
16391dedbd3bSFrançois Tigeot crtc->base.id, crtc->name);
16401dedbd3bSFrançois Tigeot
16411dedbd3bSFrançois Tigeot drm_crtc_commit_put(stall_commit);
16421dedbd3bSFrançois Tigeot
16431dedbd3bSFrançois Tigeot return ret < 0 ? ret : 0;
16441dedbd3bSFrançois Tigeot }
16451dedbd3bSFrançois Tigeot
release_crtc_commit(struct completion * completion)1646a85cb24fSFrançois Tigeot static void release_crtc_commit(struct completion *completion)
16474be47400SFrançois Tigeot {
16484be47400SFrançois Tigeot struct drm_crtc_commit *commit = container_of(completion,
16494be47400SFrançois Tigeot typeof(*commit),
16504be47400SFrançois Tigeot flip_done);
16514be47400SFrançois Tigeot
16524be47400SFrançois Tigeot drm_crtc_commit_put(commit);
16534be47400SFrançois Tigeot }
16544be47400SFrançois Tigeot
init_commit(struct drm_crtc_commit * commit,struct drm_crtc * crtc)1655*3f2dd94aSFrançois Tigeot static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
1656*3f2dd94aSFrançois Tigeot {
1657*3f2dd94aSFrançois Tigeot init_completion(&commit->flip_done);
1658*3f2dd94aSFrançois Tigeot init_completion(&commit->hw_done);
1659*3f2dd94aSFrançois Tigeot init_completion(&commit->cleanup_done);
1660*3f2dd94aSFrançois Tigeot INIT_LIST_HEAD(&commit->commit_entry);
1661*3f2dd94aSFrançois Tigeot kref_init(&commit->ref);
1662*3f2dd94aSFrançois Tigeot commit->crtc = crtc;
1663*3f2dd94aSFrançois Tigeot }
1664*3f2dd94aSFrançois Tigeot
1665*3f2dd94aSFrançois Tigeot static struct drm_crtc_commit *
crtc_or_fake_commit(struct drm_atomic_state * state,struct drm_crtc * crtc)1666*3f2dd94aSFrançois Tigeot crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
1667*3f2dd94aSFrançois Tigeot {
1668*3f2dd94aSFrançois Tigeot if (crtc) {
1669*3f2dd94aSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
1670*3f2dd94aSFrançois Tigeot
1671*3f2dd94aSFrançois Tigeot new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
1672*3f2dd94aSFrançois Tigeot
1673*3f2dd94aSFrançois Tigeot return new_crtc_state->commit;
1674*3f2dd94aSFrançois Tigeot }
1675*3f2dd94aSFrançois Tigeot
1676*3f2dd94aSFrançois Tigeot if (!state->fake_commit) {
1677*3f2dd94aSFrançois Tigeot state->fake_commit = kzalloc(sizeof(*state->fake_commit), GFP_KERNEL);
1678*3f2dd94aSFrançois Tigeot if (!state->fake_commit)
1679*3f2dd94aSFrançois Tigeot return NULL;
1680*3f2dd94aSFrançois Tigeot
1681*3f2dd94aSFrançois Tigeot init_commit(state->fake_commit, NULL);
1682*3f2dd94aSFrançois Tigeot }
1683*3f2dd94aSFrançois Tigeot
1684*3f2dd94aSFrançois Tigeot return state->fake_commit;
1685*3f2dd94aSFrançois Tigeot }
1686*3f2dd94aSFrançois Tigeot
16871dedbd3bSFrançois Tigeot /**
16881dedbd3bSFrançois Tigeot * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
16891dedbd3bSFrançois Tigeot * @state: new modeset state to be committed
16901dedbd3bSFrançois Tigeot * @nonblock: whether nonblocking behavior is requested.
16911dedbd3bSFrançois Tigeot *
16921dedbd3bSFrançois Tigeot * This function prepares @state to be used by the atomic helper's support for
16931dedbd3bSFrançois Tigeot * nonblocking commits. Drivers using the nonblocking commit infrastructure
1694a85cb24fSFrançois Tigeot * should always call this function from their
1695a85cb24fSFrançois Tigeot * &drm_mode_config_funcs.atomic_commit hook.
16961dedbd3bSFrançois Tigeot *
16971dedbd3bSFrançois Tigeot * To be able to use this support drivers need to use a few more helper
16981dedbd3bSFrançois Tigeot * functions. drm_atomic_helper_wait_for_dependencies() must be called before
16991dedbd3bSFrançois Tigeot * actually committing the hardware state, and for nonblocking commits this call
17001dedbd3bSFrançois Tigeot * must be placed in the async worker. See also drm_atomic_helper_swap_state()
17011dedbd3bSFrançois Tigeot * and it's stall parameter, for when a driver's commit hooks look at the
1702a85cb24fSFrançois Tigeot * &drm_crtc.state, &drm_plane.state or &drm_connector.state pointer directly.
17031dedbd3bSFrançois Tigeot *
17041dedbd3bSFrançois Tigeot * Completion of the hardware commit step must be signalled using
17051dedbd3bSFrançois Tigeot * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed
17061dedbd3bSFrançois Tigeot * to read or change any permanent software or hardware modeset state. The only
17071dedbd3bSFrançois Tigeot * exception is state protected by other means than &drm_modeset_lock locks.
17081dedbd3bSFrançois Tigeot * Only the free standing @state with pointers to the old state structures can
17091dedbd3bSFrançois Tigeot * be inspected, e.g. to clean up old buffers using
17101dedbd3bSFrançois Tigeot * drm_atomic_helper_cleanup_planes().
17111dedbd3bSFrançois Tigeot *
17121dedbd3bSFrançois Tigeot * At the very end, before cleaning up @state drivers must call
17131dedbd3bSFrançois Tigeot * drm_atomic_helper_commit_cleanup_done().
17141dedbd3bSFrançois Tigeot *
17151dedbd3bSFrançois Tigeot * This is all implemented by in drm_atomic_helper_commit(), giving drivers a
1716*3f2dd94aSFrançois Tigeot * complete and easy-to-use default implementation of the atomic_commit() hook.
17171dedbd3bSFrançois Tigeot *
17181dedbd3bSFrançois Tigeot * The tracking of asynchronously executed and still pending commits is done
17191dedbd3bSFrançois Tigeot * using the core structure &drm_crtc_commit.
17201dedbd3bSFrançois Tigeot *
17211dedbd3bSFrançois Tigeot * By default there's no need to clean up resources allocated by this function
17221dedbd3bSFrançois Tigeot * explicitly: drm_atomic_state_default_clear() will take care of that
17231dedbd3bSFrançois Tigeot * automatically.
17241dedbd3bSFrançois Tigeot *
17251dedbd3bSFrançois Tigeot * Returns:
17261dedbd3bSFrançois Tigeot *
17271dedbd3bSFrançois Tigeot * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast,
17281dedbd3bSFrançois Tigeot * -ENOMEM on allocation failures and -EINTR when a signal is pending.
17291dedbd3bSFrançois Tigeot */
drm_atomic_helper_setup_commit(struct drm_atomic_state * state,bool nonblock)17301dedbd3bSFrançois Tigeot int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
17311dedbd3bSFrançois Tigeot bool nonblock)
17321dedbd3bSFrançois Tigeot {
17331dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1734a85cb24fSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
1735*3f2dd94aSFrançois Tigeot struct drm_connector *conn;
1736*3f2dd94aSFrançois Tigeot struct drm_connector_state *old_conn_state, *new_conn_state;
1737*3f2dd94aSFrançois Tigeot struct drm_plane *plane;
1738*3f2dd94aSFrançois Tigeot struct drm_plane_state *old_plane_state, *new_plane_state;
17391dedbd3bSFrançois Tigeot struct drm_crtc_commit *commit;
17401dedbd3bSFrançois Tigeot int i, ret;
17411dedbd3bSFrançois Tigeot
1742a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
17431dedbd3bSFrançois Tigeot commit = kzalloc(sizeof(*commit), GFP_KERNEL);
17441dedbd3bSFrançois Tigeot if (!commit)
17451dedbd3bSFrançois Tigeot return -ENOMEM;
17461dedbd3bSFrançois Tigeot
1747*3f2dd94aSFrançois Tigeot init_commit(commit, crtc);
17481dedbd3bSFrançois Tigeot
1749*3f2dd94aSFrançois Tigeot new_crtc_state->commit = commit;
17501dedbd3bSFrançois Tigeot
17511dedbd3bSFrançois Tigeot ret = stall_checks(crtc, nonblock);
17521dedbd3bSFrançois Tigeot if (ret)
17531dedbd3bSFrançois Tigeot return ret;
17541dedbd3bSFrançois Tigeot
17551dedbd3bSFrançois Tigeot /* Drivers only send out events when at least either current or
17561dedbd3bSFrançois Tigeot * new CRTC state is active. Complete right away if everything
17571dedbd3bSFrançois Tigeot * stays off. */
1758a85cb24fSFrançois Tigeot if (!old_crtc_state->active && !new_crtc_state->active) {
17591dedbd3bSFrançois Tigeot complete_all(&commit->flip_done);
17601dedbd3bSFrançois Tigeot continue;
17611dedbd3bSFrançois Tigeot }
17621dedbd3bSFrançois Tigeot
17631dedbd3bSFrançois Tigeot /* Legacy cursor updates are fully unsynced. */
17641dedbd3bSFrançois Tigeot if (state->legacy_cursor_update) {
17651dedbd3bSFrançois Tigeot complete_all(&commit->flip_done);
17661dedbd3bSFrançois Tigeot continue;
17671dedbd3bSFrançois Tigeot }
17681dedbd3bSFrançois Tigeot
1769a85cb24fSFrançois Tigeot if (!new_crtc_state->event) {
17701dedbd3bSFrançois Tigeot commit->event = kzalloc(sizeof(*commit->event),
17711dedbd3bSFrançois Tigeot GFP_KERNEL);
17721dedbd3bSFrançois Tigeot if (!commit->event)
17731dedbd3bSFrançois Tigeot return -ENOMEM;
17741dedbd3bSFrançois Tigeot
1775a85cb24fSFrançois Tigeot new_crtc_state->event = commit->event;
17761dedbd3bSFrançois Tigeot }
17771dedbd3bSFrançois Tigeot
1778a85cb24fSFrançois Tigeot new_crtc_state->event->base.completion = &commit->flip_done;
1779a85cb24fSFrançois Tigeot new_crtc_state->event->base.completion_release = release_crtc_commit;
17804be47400SFrançois Tigeot drm_crtc_commit_get(commit);
1781*3f2dd94aSFrançois Tigeot
1782*3f2dd94aSFrançois Tigeot commit->abort_completion = true;
1783*3f2dd94aSFrançois Tigeot }
1784*3f2dd94aSFrançois Tigeot
1785*3f2dd94aSFrançois Tigeot for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
1786*3f2dd94aSFrançois Tigeot /* Userspace is not allowed to get ahead of the previous
1787*3f2dd94aSFrançois Tigeot * commit with nonblocking ones. */
1788*3f2dd94aSFrançois Tigeot if (nonblock && old_conn_state->commit &&
1789*3f2dd94aSFrançois Tigeot !try_wait_for_completion(&old_conn_state->commit->flip_done))
1790*3f2dd94aSFrançois Tigeot return -EBUSY;
1791*3f2dd94aSFrançois Tigeot
1792*3f2dd94aSFrançois Tigeot /* commit tracked through new_crtc_state->commit, no need to do it explicitly */
1793*3f2dd94aSFrançois Tigeot if (new_conn_state->crtc)
1794*3f2dd94aSFrançois Tigeot continue;
1795*3f2dd94aSFrançois Tigeot
1796*3f2dd94aSFrançois Tigeot commit = crtc_or_fake_commit(state, old_conn_state->crtc);
1797*3f2dd94aSFrançois Tigeot if (!commit)
1798*3f2dd94aSFrançois Tigeot return -ENOMEM;
1799*3f2dd94aSFrançois Tigeot
1800*3f2dd94aSFrançois Tigeot new_conn_state->commit = drm_crtc_commit_get(commit);
1801*3f2dd94aSFrançois Tigeot }
1802*3f2dd94aSFrançois Tigeot
1803*3f2dd94aSFrançois Tigeot for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
1804*3f2dd94aSFrançois Tigeot /* Userspace is not allowed to get ahead of the previous
1805*3f2dd94aSFrançois Tigeot * commit with nonblocking ones. */
1806*3f2dd94aSFrançois Tigeot if (nonblock && old_plane_state->commit &&
1807*3f2dd94aSFrançois Tigeot !try_wait_for_completion(&old_plane_state->commit->flip_done))
1808*3f2dd94aSFrançois Tigeot return -EBUSY;
1809*3f2dd94aSFrançois Tigeot
1810*3f2dd94aSFrançois Tigeot /*
1811*3f2dd94aSFrançois Tigeot * Unlike connectors, always track planes explicitly for
1812*3f2dd94aSFrançois Tigeot * async pageflip support.
1813*3f2dd94aSFrançois Tigeot */
1814*3f2dd94aSFrançois Tigeot commit = crtc_or_fake_commit(state, new_plane_state->crtc ?: old_plane_state->crtc);
1815*3f2dd94aSFrançois Tigeot if (!commit)
1816*3f2dd94aSFrançois Tigeot return -ENOMEM;
1817*3f2dd94aSFrançois Tigeot
1818*3f2dd94aSFrançois Tigeot new_plane_state->commit = drm_crtc_commit_get(commit);
18191dedbd3bSFrançois Tigeot }
18201dedbd3bSFrançois Tigeot
18211dedbd3bSFrançois Tigeot return 0;
18221dedbd3bSFrançois Tigeot }
18231dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
18241dedbd3bSFrançois Tigeot
18251dedbd3bSFrançois Tigeot /**
18261dedbd3bSFrançois Tigeot * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits
18274be47400SFrançois Tigeot * @old_state: atomic state object with old state structures
18281dedbd3bSFrançois Tigeot *
18291dedbd3bSFrançois Tigeot * This function waits for all preceeding commits that touch the same CRTC as
18304be47400SFrançois Tigeot * @old_state to both be committed to the hardware (as signalled by
18311dedbd3bSFrançois Tigeot * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
1832*3f2dd94aSFrançois Tigeot * by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event).
18331dedbd3bSFrançois Tigeot *
18341dedbd3bSFrançois Tigeot * This is part of the atomic helper support for nonblocking commits, see
18351dedbd3bSFrançois Tigeot * drm_atomic_helper_setup_commit() for an overview.
18361dedbd3bSFrançois Tigeot */
drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state * old_state)18374be47400SFrançois Tigeot void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state)
18381dedbd3bSFrançois Tigeot {
18391dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1840*3f2dd94aSFrançois Tigeot struct drm_crtc_state *old_crtc_state;
1841*3f2dd94aSFrançois Tigeot struct drm_plane *plane;
1842*3f2dd94aSFrançois Tigeot struct drm_plane_state *old_plane_state;
1843*3f2dd94aSFrançois Tigeot struct drm_connector *conn;
1844*3f2dd94aSFrançois Tigeot struct drm_connector_state *old_conn_state;
18451dedbd3bSFrançois Tigeot struct drm_crtc_commit *commit;
18461dedbd3bSFrançois Tigeot int i;
18471dedbd3bSFrançois Tigeot long ret;
18481dedbd3bSFrançois Tigeot
1849*3f2dd94aSFrançois Tigeot for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
1850*3f2dd94aSFrançois Tigeot commit = old_crtc_state->commit;
18511dedbd3bSFrançois Tigeot
18521dedbd3bSFrançois Tigeot if (!commit)
18531dedbd3bSFrançois Tigeot continue;
18541dedbd3bSFrançois Tigeot
18551dedbd3bSFrançois Tigeot ret = wait_for_completion_timeout(&commit->hw_done,
18561dedbd3bSFrançois Tigeot 10*HZ);
18571dedbd3bSFrançois Tigeot if (ret == 0)
18581dedbd3bSFrançois Tigeot DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n",
18591dedbd3bSFrançois Tigeot crtc->base.id, crtc->name);
18601dedbd3bSFrançois Tigeot
18611dedbd3bSFrançois Tigeot /* Currently no support for overwriting flips, hence
18621dedbd3bSFrançois Tigeot * stall for previous one to execute completely. */
18631dedbd3bSFrançois Tigeot ret = wait_for_completion_timeout(&commit->flip_done,
18641dedbd3bSFrançois Tigeot 10*HZ);
18651dedbd3bSFrançois Tigeot if (ret == 0)
18661dedbd3bSFrançois Tigeot DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
18671dedbd3bSFrançois Tigeot crtc->base.id, crtc->name);
1868*3f2dd94aSFrançois Tigeot }
18691dedbd3bSFrançois Tigeot
1870*3f2dd94aSFrançois Tigeot for_each_old_connector_in_state(old_state, conn, old_conn_state, i) {
1871*3f2dd94aSFrançois Tigeot commit = old_conn_state->commit;
1872*3f2dd94aSFrançois Tigeot
1873*3f2dd94aSFrançois Tigeot if (!commit)
1874*3f2dd94aSFrançois Tigeot continue;
1875*3f2dd94aSFrançois Tigeot
1876*3f2dd94aSFrançois Tigeot ret = wait_for_completion_timeout(&commit->hw_done,
1877*3f2dd94aSFrançois Tigeot 10*HZ);
1878*3f2dd94aSFrançois Tigeot if (ret == 0)
1879*3f2dd94aSFrançois Tigeot DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n",
1880*3f2dd94aSFrançois Tigeot conn->base.id, conn->name);
1881*3f2dd94aSFrançois Tigeot
1882*3f2dd94aSFrançois Tigeot /* Currently no support for overwriting flips, hence
1883*3f2dd94aSFrançois Tigeot * stall for previous one to execute completely. */
1884*3f2dd94aSFrançois Tigeot ret = wait_for_completion_timeout(&commit->flip_done,
1885*3f2dd94aSFrançois Tigeot 10*HZ);
1886*3f2dd94aSFrançois Tigeot if (ret == 0)
1887*3f2dd94aSFrançois Tigeot DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n",
1888*3f2dd94aSFrançois Tigeot conn->base.id, conn->name);
1889*3f2dd94aSFrançois Tigeot }
1890*3f2dd94aSFrançois Tigeot
1891*3f2dd94aSFrançois Tigeot for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
1892*3f2dd94aSFrançois Tigeot commit = old_plane_state->commit;
1893*3f2dd94aSFrançois Tigeot
1894*3f2dd94aSFrançois Tigeot if (!commit)
1895*3f2dd94aSFrançois Tigeot continue;
1896*3f2dd94aSFrançois Tigeot
1897*3f2dd94aSFrançois Tigeot ret = wait_for_completion_timeout(&commit->hw_done,
1898*3f2dd94aSFrançois Tigeot 10*HZ);
1899*3f2dd94aSFrançois Tigeot if (ret == 0)
1900*3f2dd94aSFrançois Tigeot DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n",
1901*3f2dd94aSFrançois Tigeot plane->base.id, plane->name);
1902*3f2dd94aSFrançois Tigeot
1903*3f2dd94aSFrançois Tigeot /* Currently no support for overwriting flips, hence
1904*3f2dd94aSFrançois Tigeot * stall for previous one to execute completely. */
1905*3f2dd94aSFrançois Tigeot ret = wait_for_completion_timeout(&commit->flip_done,
1906*3f2dd94aSFrançois Tigeot 10*HZ);
1907*3f2dd94aSFrançois Tigeot if (ret == 0)
1908*3f2dd94aSFrançois Tigeot DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n",
1909*3f2dd94aSFrançois Tigeot plane->base.id, plane->name);
19101dedbd3bSFrançois Tigeot }
19111dedbd3bSFrançois Tigeot }
19121dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
19131dedbd3bSFrançois Tigeot
19141dedbd3bSFrançois Tigeot /**
19151dedbd3bSFrançois Tigeot * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit
19164be47400SFrançois Tigeot * @old_state: atomic state object with old state structures
19171dedbd3bSFrançois Tigeot *
19181dedbd3bSFrançois Tigeot * This function is used to signal completion of the hardware commit step. After
19191dedbd3bSFrançois Tigeot * this step the driver is not allowed to read or change any permanent software
19201dedbd3bSFrançois Tigeot * or hardware modeset state. The only exception is state protected by other
19211dedbd3bSFrançois Tigeot * means than &drm_modeset_lock locks.
19221dedbd3bSFrançois Tigeot *
19231dedbd3bSFrançois Tigeot * Drivers should try to postpone any expensive or delayed cleanup work after
19241dedbd3bSFrançois Tigeot * this function is called.
19251dedbd3bSFrançois Tigeot *
19261dedbd3bSFrançois Tigeot * This is part of the atomic helper support for nonblocking commits, see
19271dedbd3bSFrançois Tigeot * drm_atomic_helper_setup_commit() for an overview.
19281dedbd3bSFrançois Tigeot */
drm_atomic_helper_commit_hw_done(struct drm_atomic_state * old_state)19294be47400SFrançois Tigeot void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state)
19301dedbd3bSFrançois Tigeot {
19311dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1932*3f2dd94aSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
19331dedbd3bSFrançois Tigeot struct drm_crtc_commit *commit;
19341dedbd3bSFrançois Tigeot int i;
19351dedbd3bSFrançois Tigeot
1936*3f2dd94aSFrançois Tigeot for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
1937*3f2dd94aSFrançois Tigeot commit = new_crtc_state->commit;
19381dedbd3bSFrançois Tigeot if (!commit)
19391dedbd3bSFrançois Tigeot continue;
19401dedbd3bSFrançois Tigeot
1941*3f2dd94aSFrançois Tigeot /*
1942*3f2dd94aSFrançois Tigeot * copy new_crtc_state->commit to old_crtc_state->commit,
1943*3f2dd94aSFrançois Tigeot * it's unsafe to touch new_crtc_state after hw_done,
1944*3f2dd94aSFrançois Tigeot * but we still need to do so in cleanup_done().
1945*3f2dd94aSFrançois Tigeot */
1946*3f2dd94aSFrançois Tigeot if (old_crtc_state->commit)
1947*3f2dd94aSFrançois Tigeot drm_crtc_commit_put(old_crtc_state->commit);
1948*3f2dd94aSFrançois Tigeot
1949*3f2dd94aSFrançois Tigeot old_crtc_state->commit = drm_crtc_commit_get(commit);
1950*3f2dd94aSFrançois Tigeot
19511dedbd3bSFrançois Tigeot /* backend must have consumed any event by now */
1952a85cb24fSFrançois Tigeot WARN_ON(new_crtc_state->event);
19531dedbd3bSFrançois Tigeot complete_all(&commit->hw_done);
1954*3f2dd94aSFrançois Tigeot }
1955*3f2dd94aSFrançois Tigeot
1956*3f2dd94aSFrançois Tigeot if (old_state->fake_commit) {
1957*3f2dd94aSFrançois Tigeot complete_all(&old_state->fake_commit->hw_done);
1958*3f2dd94aSFrançois Tigeot complete_all(&old_state->fake_commit->flip_done);
19591dedbd3bSFrançois Tigeot }
19601dedbd3bSFrançois Tigeot }
19611dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
19621dedbd3bSFrançois Tigeot
19631dedbd3bSFrançois Tigeot /**
19641dedbd3bSFrançois Tigeot * drm_atomic_helper_commit_cleanup_done - signal completion of commit
19654be47400SFrançois Tigeot * @old_state: atomic state object with old state structures
19661dedbd3bSFrançois Tigeot *
19674be47400SFrançois Tigeot * This signals completion of the atomic update @old_state, including any
19684be47400SFrançois Tigeot * cleanup work. If used, it must be called right before calling
19694be47400SFrançois Tigeot * drm_atomic_state_put().
19701dedbd3bSFrançois Tigeot *
19711dedbd3bSFrançois Tigeot * This is part of the atomic helper support for nonblocking commits, see
19721dedbd3bSFrançois Tigeot * drm_atomic_helper_setup_commit() for an overview.
19731dedbd3bSFrançois Tigeot */
drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state * old_state)19744be47400SFrançois Tigeot void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state)
19751dedbd3bSFrançois Tigeot {
19761dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1977*3f2dd94aSFrançois Tigeot struct drm_crtc_state *old_crtc_state;
19781dedbd3bSFrançois Tigeot struct drm_crtc_commit *commit;
19791dedbd3bSFrançois Tigeot int i;
19801dedbd3bSFrançois Tigeot
1981*3f2dd94aSFrançois Tigeot for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
1982*3f2dd94aSFrançois Tigeot commit = old_crtc_state->commit;
19831dedbd3bSFrançois Tigeot if (WARN_ON(!commit))
19841dedbd3bSFrançois Tigeot continue;
19851dedbd3bSFrançois Tigeot
19861dedbd3bSFrançois Tigeot complete_all(&commit->cleanup_done);
19871dedbd3bSFrançois Tigeot WARN_ON(!try_wait_for_completion(&commit->hw_done));
19881dedbd3bSFrançois Tigeot
1989ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_EXCLUSIVE);
19901dedbd3bSFrançois Tigeot list_del(&commit->commit_entry);
1991ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_RELEASE);
19921dedbd3bSFrançois Tigeot }
1993*3f2dd94aSFrançois Tigeot
1994*3f2dd94aSFrançois Tigeot if (old_state->fake_commit)
1995*3f2dd94aSFrançois Tigeot complete_all(&old_state->fake_commit->cleanup_done);
19961dedbd3bSFrançois Tigeot }
19971dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
19981dedbd3bSFrançois Tigeot
19992c9916cdSFrançois Tigeot /**
2000477eb7f9SFrançois Tigeot * drm_atomic_helper_prepare_planes - prepare plane resources before commit
20012c9916cdSFrançois Tigeot * @dev: DRM device
2002477eb7f9SFrançois Tigeot * @state: atomic state object with new state structures
20032c9916cdSFrançois Tigeot *
20042c9916cdSFrançois Tigeot * This function prepares plane state, specifically framebuffers, for the new
2005a85cb24fSFrançois Tigeot * configuration, by calling &drm_plane_helper_funcs.prepare_fb. If any failure
2006a85cb24fSFrançois Tigeot * is encountered this function will call &drm_plane_helper_funcs.cleanup_fb on
2007a85cb24fSFrançois Tigeot * any already successfully prepared framebuffer.
20082c9916cdSFrançois Tigeot *
20092c9916cdSFrançois Tigeot * Returns:
20102c9916cdSFrançois Tigeot * 0 on success, negative error code on failure.
20112c9916cdSFrançois Tigeot */
drm_atomic_helper_prepare_planes(struct drm_device * dev,struct drm_atomic_state * state)20122c9916cdSFrançois Tigeot int drm_atomic_helper_prepare_planes(struct drm_device *dev,
20132c9916cdSFrançois Tigeot struct drm_atomic_state *state)
20142c9916cdSFrançois Tigeot {
20151dedbd3bSFrançois Tigeot struct drm_plane *plane;
2016a85cb24fSFrançois Tigeot struct drm_plane_state *new_plane_state;
20171dedbd3bSFrançois Tigeot int ret, i, j;
20182c9916cdSFrançois Tigeot
2019a85cb24fSFrançois Tigeot for_each_new_plane_in_state(state, plane, new_plane_state, i) {
2020477eb7f9SFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
20212c9916cdSFrançois Tigeot
20222c9916cdSFrançois Tigeot funcs = plane->helper_private;
20232c9916cdSFrançois Tigeot
2024352ff8bdSFrançois Tigeot if (funcs->prepare_fb) {
2025a85cb24fSFrançois Tigeot ret = funcs->prepare_fb(plane, new_plane_state);
20262c9916cdSFrançois Tigeot if (ret)
20272c9916cdSFrançois Tigeot goto fail;
20282c9916cdSFrançois Tigeot }
20292c9916cdSFrançois Tigeot }
20302c9916cdSFrançois Tigeot
20312c9916cdSFrançois Tigeot return 0;
20322c9916cdSFrançois Tigeot
20332c9916cdSFrançois Tigeot fail:
2034a85cb24fSFrançois Tigeot for_each_new_plane_in_state(state, plane, new_plane_state, j) {
2035477eb7f9SFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
20362c9916cdSFrançois Tigeot
20371dedbd3bSFrançois Tigeot if (j >= i)
20381dedbd3bSFrançois Tigeot continue;
20391dedbd3bSFrançois Tigeot
20402c9916cdSFrançois Tigeot funcs = plane->helper_private;
20412c9916cdSFrançois Tigeot
2042352ff8bdSFrançois Tigeot if (funcs->cleanup_fb)
2043a85cb24fSFrançois Tigeot funcs->cleanup_fb(plane, new_plane_state);
20442c9916cdSFrançois Tigeot }
20452c9916cdSFrançois Tigeot
20462c9916cdSFrançois Tigeot return ret;
20472c9916cdSFrançois Tigeot }
20482c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
20492c9916cdSFrançois Tigeot
plane_crtc_active(const struct drm_plane_state * state)20501e12ee3bSFrançois Tigeot static bool plane_crtc_active(const struct drm_plane_state *state)
2051352ff8bdSFrançois Tigeot {
2052352ff8bdSFrançois Tigeot return state->crtc && state->crtc->state->active;
2053352ff8bdSFrançois Tigeot }
2054352ff8bdSFrançois Tigeot
20552c9916cdSFrançois Tigeot /**
20562c9916cdSFrançois Tigeot * drm_atomic_helper_commit_planes - commit plane state
20572c9916cdSFrançois Tigeot * @dev: DRM device
20582c9916cdSFrançois Tigeot * @old_state: atomic state object with old state structures
20591dedbd3bSFrançois Tigeot * @flags: flags for committing plane state
20602c9916cdSFrançois Tigeot *
20612c9916cdSFrançois Tigeot * This function commits the new plane state using the plane and atomic helper
20622c9916cdSFrançois Tigeot * functions for planes and crtcs. It assumes that the atomic state has already
20632c9916cdSFrançois Tigeot * been pushed into the relevant object state pointers, since this step can no
20642c9916cdSFrançois Tigeot * longer fail.
20652c9916cdSFrançois Tigeot *
20662c9916cdSFrançois Tigeot * It still requires the global state object @old_state to know which planes and
20672c9916cdSFrançois Tigeot * crtcs need to be updated though.
206819c468b4SFrançois Tigeot *
206919c468b4SFrançois Tigeot * Note that this function does all plane updates across all CRTCs in one step.
207019c468b4SFrançois Tigeot * If the hardware can't support this approach look at
207119c468b4SFrançois Tigeot * drm_atomic_helper_commit_planes_on_crtc() instead.
2072352ff8bdSFrançois Tigeot *
2073352ff8bdSFrançois Tigeot * Plane parameters can be updated by applications while the associated CRTC is
2074352ff8bdSFrançois Tigeot * disabled. The DRM/KMS core will store the parameters in the plane state,
2075352ff8bdSFrançois Tigeot * which will be available to the driver when the CRTC is turned on. As a result
2076352ff8bdSFrançois Tigeot * most drivers don't need to be immediately notified of plane updates for a
2077352ff8bdSFrançois Tigeot * disabled CRTC.
2078352ff8bdSFrançois Tigeot *
20791dedbd3bSFrançois Tigeot * Unless otherwise needed, drivers are advised to set the ACTIVE_ONLY flag in
20801dedbd3bSFrançois Tigeot * @flags in order not to receive plane update notifications related to a
20811dedbd3bSFrançois Tigeot * disabled CRTC. This avoids the need to manually ignore plane updates in
2082352ff8bdSFrançois Tigeot * driver code when the driver and/or hardware can't or just don't need to deal
2083352ff8bdSFrançois Tigeot * with updates on disabled CRTCs, for example when supporting runtime PM.
2084352ff8bdSFrançois Tigeot *
20851dedbd3bSFrançois Tigeot * Drivers may set the NO_DISABLE_AFTER_MODESET flag in @flags if the relevant
20861dedbd3bSFrançois Tigeot * display controllers require to disable a CRTC's planes when the CRTC is
2087a85cb24fSFrançois Tigeot * disabled. This function would skip the &drm_plane_helper_funcs.atomic_disable
2088a85cb24fSFrançois Tigeot * call for a plane if the CRTC of the old plane state needs a modesetting
2089a85cb24fSFrançois Tigeot * operation. Of course, the drivers need to disable the planes in their CRTC
2090a85cb24fSFrançois Tigeot * disable callbacks since no one else would do that.
20911dedbd3bSFrançois Tigeot *
20921dedbd3bSFrançois Tigeot * The drm_atomic_helper_commit() default implementation doesn't set the
20931dedbd3bSFrançois Tigeot * ACTIVE_ONLY flag to most closely match the behaviour of the legacy helpers.
20941dedbd3bSFrançois Tigeot * This should not be copied blindly by drivers.
20952c9916cdSFrançois Tigeot */
drm_atomic_helper_commit_planes(struct drm_device * dev,struct drm_atomic_state * old_state,uint32_t flags)20962c9916cdSFrançois Tigeot void drm_atomic_helper_commit_planes(struct drm_device *dev,
2097352ff8bdSFrançois Tigeot struct drm_atomic_state *old_state,
20981dedbd3bSFrançois Tigeot uint32_t flags)
20992c9916cdSFrançois Tigeot {
2100477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
2101a85cb24fSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
2102477eb7f9SFrançois Tigeot struct drm_plane *plane;
2103a85cb24fSFrançois Tigeot struct drm_plane_state *old_plane_state, *new_plane_state;
21042c9916cdSFrançois Tigeot int i;
21051dedbd3bSFrançois Tigeot bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY;
21061dedbd3bSFrançois Tigeot bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET;
21072c9916cdSFrançois Tigeot
2108a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
2109477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
21102c9916cdSFrançois Tigeot
21112c9916cdSFrançois Tigeot funcs = crtc->helper_private;
21122c9916cdSFrançois Tigeot
21132c9916cdSFrançois Tigeot if (!funcs || !funcs->atomic_begin)
21142c9916cdSFrançois Tigeot continue;
21152c9916cdSFrançois Tigeot
2116a85cb24fSFrançois Tigeot if (active_only && !new_crtc_state->active)
2117352ff8bdSFrançois Tigeot continue;
2118352ff8bdSFrançois Tigeot
2119a05eeebfSFrançois Tigeot funcs->atomic_begin(crtc, old_crtc_state);
21202c9916cdSFrançois Tigeot }
21212c9916cdSFrançois Tigeot
2122a85cb24fSFrançois Tigeot for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
2123477eb7f9SFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
2124352ff8bdSFrançois Tigeot bool disabling;
21252c9916cdSFrançois Tigeot
21262c9916cdSFrançois Tigeot funcs = plane->helper_private;
21272c9916cdSFrançois Tigeot
21282c9916cdSFrançois Tigeot if (!funcs)
21292c9916cdSFrançois Tigeot continue;
21302c9916cdSFrançois Tigeot
2131a85cb24fSFrançois Tigeot disabling = drm_atomic_plane_disabling(old_plane_state,
2132a85cb24fSFrançois Tigeot new_plane_state);
2133352ff8bdSFrançois Tigeot
2134352ff8bdSFrançois Tigeot if (active_only) {
2135352ff8bdSFrançois Tigeot /*
2136352ff8bdSFrançois Tigeot * Skip planes related to inactive CRTCs. If the plane
2137352ff8bdSFrançois Tigeot * is enabled use the state of the current CRTC. If the
2138352ff8bdSFrançois Tigeot * plane is being disabled use the state of the old
2139352ff8bdSFrançois Tigeot * CRTC to avoid skipping planes being disabled on an
2140352ff8bdSFrançois Tigeot * active CRTC.
2141352ff8bdSFrançois Tigeot */
2142a85cb24fSFrançois Tigeot if (!disabling && !plane_crtc_active(new_plane_state))
2143352ff8bdSFrançois Tigeot continue;
2144352ff8bdSFrançois Tigeot if (disabling && !plane_crtc_active(old_plane_state))
2145352ff8bdSFrançois Tigeot continue;
2146352ff8bdSFrançois Tigeot }
2147352ff8bdSFrançois Tigeot
21482c9916cdSFrançois Tigeot /*
21492c9916cdSFrançois Tigeot * Special-case disabling the plane if drivers support it.
21502c9916cdSFrançois Tigeot */
21511dedbd3bSFrançois Tigeot if (disabling && funcs->atomic_disable) {
21521dedbd3bSFrançois Tigeot struct drm_crtc_state *crtc_state;
21531dedbd3bSFrançois Tigeot
21541dedbd3bSFrançois Tigeot crtc_state = old_plane_state->crtc->state;
21551dedbd3bSFrançois Tigeot
21561dedbd3bSFrançois Tigeot if (drm_atomic_crtc_needs_modeset(crtc_state) &&
21571dedbd3bSFrançois Tigeot no_disable)
21581dedbd3bSFrançois Tigeot continue;
21591dedbd3bSFrançois Tigeot
21602c9916cdSFrançois Tigeot funcs->atomic_disable(plane, old_plane_state);
2161a85cb24fSFrançois Tigeot } else if (new_plane_state->crtc || disabling) {
21622c9916cdSFrançois Tigeot funcs->atomic_update(plane, old_plane_state);
21632c9916cdSFrançois Tigeot }
21641dedbd3bSFrançois Tigeot }
21652c9916cdSFrançois Tigeot
2166a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
2167477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *funcs;
21682c9916cdSFrançois Tigeot
21692c9916cdSFrançois Tigeot funcs = crtc->helper_private;
21702c9916cdSFrançois Tigeot
21712c9916cdSFrançois Tigeot if (!funcs || !funcs->atomic_flush)
21722c9916cdSFrançois Tigeot continue;
21732c9916cdSFrançois Tigeot
2174a85cb24fSFrançois Tigeot if (active_only && !new_crtc_state->active)
2175352ff8bdSFrançois Tigeot continue;
2176352ff8bdSFrançois Tigeot
2177a05eeebfSFrançois Tigeot funcs->atomic_flush(crtc, old_crtc_state);
21782c9916cdSFrançois Tigeot }
21792c9916cdSFrançois Tigeot }
21802c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
21812c9916cdSFrançois Tigeot
21822c9916cdSFrançois Tigeot /**
218319c468b4SFrançois Tigeot * drm_atomic_helper_commit_planes_on_crtc - commit plane state for a crtc
218419c468b4SFrançois Tigeot * @old_crtc_state: atomic state object with the old crtc state
218519c468b4SFrançois Tigeot *
218619c468b4SFrançois Tigeot * This function commits the new plane state using the plane and atomic helper
218719c468b4SFrançois Tigeot * functions for planes on the specific crtc. It assumes that the atomic state
218819c468b4SFrançois Tigeot * has already been pushed into the relevant object state pointers, since this
218919c468b4SFrançois Tigeot * step can no longer fail.
219019c468b4SFrançois Tigeot *
219119c468b4SFrançois Tigeot * This function is useful when plane updates should be done crtc-by-crtc
219219c468b4SFrançois Tigeot * instead of one global step like drm_atomic_helper_commit_planes() does.
219319c468b4SFrançois Tigeot *
219419c468b4SFrançois Tigeot * This function can only be savely used when planes are not allowed to move
219519c468b4SFrançois Tigeot * between different CRTCs because this function doesn't handle inter-CRTC
219619c468b4SFrançois Tigeot * depencies. Callers need to ensure that either no such depencies exist,
219719c468b4SFrançois Tigeot * resolve them through ordering of commit calls or through some other means.
219819c468b4SFrançois Tigeot */
219919c468b4SFrançois Tigeot void
drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state * old_crtc_state)220019c468b4SFrançois Tigeot drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)
220119c468b4SFrançois Tigeot {
220219c468b4SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs;
220319c468b4SFrançois Tigeot struct drm_crtc *crtc = old_crtc_state->crtc;
220419c468b4SFrançois Tigeot struct drm_atomic_state *old_state = old_crtc_state->state;
220519c468b4SFrançois Tigeot struct drm_plane *plane;
220619c468b4SFrançois Tigeot unsigned plane_mask;
220719c468b4SFrançois Tigeot
220819c468b4SFrançois Tigeot plane_mask = old_crtc_state->plane_mask;
220919c468b4SFrançois Tigeot plane_mask |= crtc->state->plane_mask;
221019c468b4SFrançois Tigeot
221119c468b4SFrançois Tigeot crtc_funcs = crtc->helper_private;
221219c468b4SFrançois Tigeot if (crtc_funcs && crtc_funcs->atomic_begin)
2213a05eeebfSFrançois Tigeot crtc_funcs->atomic_begin(crtc, old_crtc_state);
221419c468b4SFrançois Tigeot
221519c468b4SFrançois Tigeot drm_for_each_plane_mask(plane, crtc->dev, plane_mask) {
221619c468b4SFrançois Tigeot struct drm_plane_state *old_plane_state =
2217a85cb24fSFrançois Tigeot drm_atomic_get_old_plane_state(old_state, plane);
221819c468b4SFrançois Tigeot const struct drm_plane_helper_funcs *plane_funcs;
221919c468b4SFrançois Tigeot
222019c468b4SFrançois Tigeot plane_funcs = plane->helper_private;
222119c468b4SFrançois Tigeot
222219c468b4SFrançois Tigeot if (!old_plane_state || !plane_funcs)
222319c468b4SFrançois Tigeot continue;
222419c468b4SFrançois Tigeot
222519c468b4SFrançois Tigeot WARN_ON(plane->state->crtc && plane->state->crtc != crtc);
222619c468b4SFrançois Tigeot
2227a85cb24fSFrançois Tigeot if (drm_atomic_plane_disabling(old_plane_state, plane->state) &&
222819c468b4SFrançois Tigeot plane_funcs->atomic_disable)
222919c468b4SFrançois Tigeot plane_funcs->atomic_disable(plane, old_plane_state);
223019c468b4SFrançois Tigeot else if (plane->state->crtc ||
2231a85cb24fSFrançois Tigeot drm_atomic_plane_disabling(old_plane_state, plane->state))
223219c468b4SFrançois Tigeot plane_funcs->atomic_update(plane, old_plane_state);
223319c468b4SFrançois Tigeot }
223419c468b4SFrançois Tigeot
223519c468b4SFrançois Tigeot if (crtc_funcs && crtc_funcs->atomic_flush)
2236a05eeebfSFrançois Tigeot crtc_funcs->atomic_flush(crtc, old_crtc_state);
223719c468b4SFrançois Tigeot }
223819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
223919c468b4SFrançois Tigeot
224019c468b4SFrançois Tigeot /**
2241aee94f86SFrançois Tigeot * drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes
22421dedbd3bSFrançois Tigeot * @old_crtc_state: atomic state object with the old CRTC state
2243aee94f86SFrançois Tigeot * @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks
2244aee94f86SFrançois Tigeot *
2245aee94f86SFrançois Tigeot * Disables all planes associated with the given CRTC. This can be
22461dedbd3bSFrançois Tigeot * used for instance in the CRTC helper atomic_disable callback to disable
22471dedbd3bSFrançois Tigeot * all planes.
2248aee94f86SFrançois Tigeot *
2249aee94f86SFrançois Tigeot * If the atomic-parameter is set the function calls the CRTC's
2250aee94f86SFrançois Tigeot * atomic_begin hook before and atomic_flush hook after disabling the
2251aee94f86SFrançois Tigeot * planes.
2252aee94f86SFrançois Tigeot *
2253aee94f86SFrançois Tigeot * It is a bug to call this function without having implemented the
2254a85cb24fSFrançois Tigeot * &drm_plane_helper_funcs.atomic_disable plane hook.
2255aee94f86SFrançois Tigeot */
22561dedbd3bSFrançois Tigeot void
drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state * old_crtc_state,bool atomic)22571dedbd3bSFrançois Tigeot drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state,
2258aee94f86SFrançois Tigeot bool atomic)
2259aee94f86SFrançois Tigeot {
22601dedbd3bSFrançois Tigeot struct drm_crtc *crtc = old_crtc_state->crtc;
2261aee94f86SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs =
2262aee94f86SFrançois Tigeot crtc->helper_private;
2263aee94f86SFrançois Tigeot struct drm_plane *plane;
2264aee94f86SFrançois Tigeot
2265aee94f86SFrançois Tigeot if (atomic && crtc_funcs && crtc_funcs->atomic_begin)
2266aee94f86SFrançois Tigeot crtc_funcs->atomic_begin(crtc, NULL);
2267aee94f86SFrançois Tigeot
22681dedbd3bSFrançois Tigeot drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) {
2269aee94f86SFrançois Tigeot const struct drm_plane_helper_funcs *plane_funcs =
2270aee94f86SFrançois Tigeot plane->helper_private;
2271aee94f86SFrançois Tigeot
22721dedbd3bSFrançois Tigeot if (!plane_funcs)
2273aee94f86SFrançois Tigeot continue;
2274aee94f86SFrançois Tigeot
2275aee94f86SFrançois Tigeot WARN_ON(!plane_funcs->atomic_disable);
2276aee94f86SFrançois Tigeot if (plane_funcs->atomic_disable)
2277aee94f86SFrançois Tigeot plane_funcs->atomic_disable(plane, NULL);
2278aee94f86SFrançois Tigeot }
2279aee94f86SFrançois Tigeot
2280aee94f86SFrançois Tigeot if (atomic && crtc_funcs && crtc_funcs->atomic_flush)
2281aee94f86SFrançois Tigeot crtc_funcs->atomic_flush(crtc, NULL);
2282aee94f86SFrançois Tigeot }
2283aee94f86SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_disable_planes_on_crtc);
2284aee94f86SFrançois Tigeot
2285aee94f86SFrançois Tigeot /**
22862c9916cdSFrançois Tigeot * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
22872c9916cdSFrançois Tigeot * @dev: DRM device
22882c9916cdSFrançois Tigeot * @old_state: atomic state object with old state structures
22892c9916cdSFrançois Tigeot *
22902c9916cdSFrançois Tigeot * This function cleans up plane state, specifically framebuffers, from the old
22912c9916cdSFrançois Tigeot * configuration. Hence the old configuration must be perserved in @old_state to
22922c9916cdSFrançois Tigeot * be able to call this function.
22932c9916cdSFrançois Tigeot *
22942c9916cdSFrançois Tigeot * This function must also be called on the new state when the atomic update
22952c9916cdSFrançois Tigeot * fails at any point after calling drm_atomic_helper_prepare_planes().
22962c9916cdSFrançois Tigeot */
drm_atomic_helper_cleanup_planes(struct drm_device * dev,struct drm_atomic_state * old_state)22972c9916cdSFrançois Tigeot void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
22982c9916cdSFrançois Tigeot struct drm_atomic_state *old_state)
22992c9916cdSFrançois Tigeot {
2300477eb7f9SFrançois Tigeot struct drm_plane *plane;
2301a85cb24fSFrançois Tigeot struct drm_plane_state *old_plane_state, *new_plane_state;
23022c9916cdSFrançois Tigeot int i;
23032c9916cdSFrançois Tigeot
2304a85cb24fSFrançois Tigeot for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
2305477eb7f9SFrançois Tigeot const struct drm_plane_helper_funcs *funcs;
2306a85cb24fSFrançois Tigeot struct drm_plane_state *plane_state;
2307a85cb24fSFrançois Tigeot
2308a85cb24fSFrançois Tigeot /*
2309a85cb24fSFrançois Tigeot * This might be called before swapping when commit is aborted,
2310a85cb24fSFrançois Tigeot * in which case we have to cleanup the new state.
2311a85cb24fSFrançois Tigeot */
2312a85cb24fSFrançois Tigeot if (old_plane_state == plane->state)
2313a85cb24fSFrançois Tigeot plane_state = new_plane_state;
2314a85cb24fSFrançois Tigeot else
2315a85cb24fSFrançois Tigeot plane_state = old_plane_state;
23162c9916cdSFrançois Tigeot
23172c9916cdSFrançois Tigeot funcs = plane->helper_private;
23182c9916cdSFrançois Tigeot
2319352ff8bdSFrançois Tigeot if (funcs->cleanup_fb)
2320352ff8bdSFrançois Tigeot funcs->cleanup_fb(plane, plane_state);
23212c9916cdSFrançois Tigeot }
23222c9916cdSFrançois Tigeot }
23232c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
23242c9916cdSFrançois Tigeot
23252c9916cdSFrançois Tigeot /**
23262c9916cdSFrançois Tigeot * drm_atomic_helper_swap_state - store atomic state into current sw state
23272c9916cdSFrançois Tigeot * @state: atomic state
2328*3f2dd94aSFrançois Tigeot * @stall: stall for preceeding commits
23292c9916cdSFrançois Tigeot *
23302c9916cdSFrançois Tigeot * This function stores the atomic state into the current state pointers in all
23312c9916cdSFrançois Tigeot * driver objects. It should be called after all failing steps have been done
23322c9916cdSFrançois Tigeot * and succeeded, but before the actual hardware state is committed.
23332c9916cdSFrançois Tigeot *
23342c9916cdSFrançois Tigeot * For cleanup and error recovery the current state for all changed objects will
2335*3f2dd94aSFrançois Tigeot * be swapped into @state.
23362c9916cdSFrançois Tigeot *
23372c9916cdSFrançois Tigeot * With that sequence it fits perfectly into the plane prepare/cleanup sequence:
23382c9916cdSFrançois Tigeot *
23392c9916cdSFrançois Tigeot * 1. Call drm_atomic_helper_prepare_planes() with the staged atomic state.
23402c9916cdSFrançois Tigeot *
23412c9916cdSFrançois Tigeot * 2. Do any other steps that might fail.
23422c9916cdSFrançois Tigeot *
23432c9916cdSFrançois Tigeot * 3. Put the staged state into the current state pointers with this function.
23442c9916cdSFrançois Tigeot *
23452c9916cdSFrançois Tigeot * 4. Actually commit the hardware state.
23462c9916cdSFrançois Tigeot *
2347352ff8bdSFrançois Tigeot * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
23482c9916cdSFrançois Tigeot * contains the old state. Also do any other cleanup required with that state.
23491dedbd3bSFrançois Tigeot *
23501dedbd3bSFrançois Tigeot * @stall must be set when nonblocking commits for this driver directly access
2351a85cb24fSFrançois Tigeot * the &drm_plane.state, &drm_crtc.state or &drm_connector.state pointer. With
2352a85cb24fSFrançois Tigeot * the current atomic helpers this is almost always the case, since the helpers
23531dedbd3bSFrançois Tigeot * don't pass the right state structures to the callbacks.
2354*3f2dd94aSFrançois Tigeot *
2355*3f2dd94aSFrançois Tigeot * Returns:
2356*3f2dd94aSFrançois Tigeot *
2357*3f2dd94aSFrançois Tigeot * Returns 0 on success. Can return -ERESTARTSYS when @stall is true and the
2358*3f2dd94aSFrançois Tigeot * waiting for the previous commits has been interrupted.
23592c9916cdSFrançois Tigeot */
drm_atomic_helper_swap_state(struct drm_atomic_state * state,bool stall)2360*3f2dd94aSFrançois Tigeot int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
23611dedbd3bSFrançois Tigeot bool stall)
23622c9916cdSFrançois Tigeot {
2363*3f2dd94aSFrançois Tigeot int i, ret;
23641dedbd3bSFrançois Tigeot struct drm_connector *connector;
2365a85cb24fSFrançois Tigeot struct drm_connector_state *old_conn_state, *new_conn_state;
23661dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
2367a85cb24fSFrançois Tigeot struct drm_crtc_state *old_crtc_state, *new_crtc_state;
23681dedbd3bSFrançois Tigeot struct drm_plane *plane;
2369a85cb24fSFrançois Tigeot struct drm_plane_state *old_plane_state, *new_plane_state;
23701dedbd3bSFrançois Tigeot struct drm_crtc_commit *commit;
2371*3f2dd94aSFrançois Tigeot struct drm_private_obj *obj;
2372*3f2dd94aSFrançois Tigeot struct drm_private_state *old_obj_state, *new_obj_state;
23732c9916cdSFrançois Tigeot
23741dedbd3bSFrançois Tigeot if (stall) {
2375*3f2dd94aSFrançois Tigeot /*
2376*3f2dd94aSFrançois Tigeot * We have to stall for hw_done here before
2377*3f2dd94aSFrançois Tigeot * drm_atomic_helper_wait_for_dependencies() because flip
2378*3f2dd94aSFrançois Tigeot * depth > 1 is not yet supported by all drivers. As long as
2379*3f2dd94aSFrançois Tigeot * obj->state is directly dereferenced anywhere in the drivers
2380*3f2dd94aSFrançois Tigeot * atomic_commit_tail function, then it's unsafe to swap state
2381*3f2dd94aSFrançois Tigeot * before drm_atomic_helper_commit_hw_done() is called.
2382*3f2dd94aSFrançois Tigeot */
2383*3f2dd94aSFrançois Tigeot
2384*3f2dd94aSFrançois Tigeot for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
2385*3f2dd94aSFrançois Tigeot commit = old_crtc_state->commit;
23862c9916cdSFrançois Tigeot
23871dedbd3bSFrançois Tigeot if (!commit)
23882c9916cdSFrançois Tigeot continue;
23892c9916cdSFrançois Tigeot
2390*3f2dd94aSFrançois Tigeot ret = wait_for_completion_interruptible(&commit->hw_done);
2391*3f2dd94aSFrançois Tigeot if (ret)
2392*3f2dd94aSFrançois Tigeot return ret;
2393*3f2dd94aSFrançois Tigeot }
2394*3f2dd94aSFrançois Tigeot
2395*3f2dd94aSFrançois Tigeot for_each_old_connector_in_state(state, connector, old_conn_state, i) {
2396*3f2dd94aSFrançois Tigeot commit = old_conn_state->commit;
2397*3f2dd94aSFrançois Tigeot
2398*3f2dd94aSFrançois Tigeot if (!commit)
2399*3f2dd94aSFrançois Tigeot continue;
2400*3f2dd94aSFrançois Tigeot
2401*3f2dd94aSFrançois Tigeot ret = wait_for_completion_interruptible(&commit->hw_done);
2402*3f2dd94aSFrançois Tigeot if (ret)
2403*3f2dd94aSFrançois Tigeot return ret;
2404*3f2dd94aSFrançois Tigeot }
2405*3f2dd94aSFrançois Tigeot
2406*3f2dd94aSFrançois Tigeot for_each_old_plane_in_state(state, plane, old_plane_state, i) {
2407*3f2dd94aSFrançois Tigeot commit = old_plane_state->commit;
2408*3f2dd94aSFrançois Tigeot
2409*3f2dd94aSFrançois Tigeot if (!commit)
2410*3f2dd94aSFrançois Tigeot continue;
2411*3f2dd94aSFrançois Tigeot
2412*3f2dd94aSFrançois Tigeot ret = wait_for_completion_interruptible(&commit->hw_done);
2413*3f2dd94aSFrançois Tigeot if (ret)
2414*3f2dd94aSFrançois Tigeot return ret;
24151dedbd3bSFrançois Tigeot }
24161dedbd3bSFrançois Tigeot }
24171dedbd3bSFrançois Tigeot
2418a85cb24fSFrançois Tigeot for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) {
2419a85cb24fSFrançois Tigeot WARN_ON(connector->state != old_conn_state);
2420a85cb24fSFrançois Tigeot
2421a85cb24fSFrançois Tigeot old_conn_state->state = state;
2422a85cb24fSFrançois Tigeot new_conn_state->state = NULL;
2423a85cb24fSFrançois Tigeot
2424a85cb24fSFrançois Tigeot state->connectors[i].state = old_conn_state;
2425a85cb24fSFrançois Tigeot connector->state = new_conn_state;
24262c9916cdSFrançois Tigeot }
24272c9916cdSFrançois Tigeot
2428a85cb24fSFrançois Tigeot for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
2429a85cb24fSFrançois Tigeot WARN_ON(crtc->state != old_crtc_state);
2430a85cb24fSFrançois Tigeot
2431a85cb24fSFrançois Tigeot old_crtc_state->state = state;
2432a85cb24fSFrançois Tigeot new_crtc_state->state = NULL;
2433a85cb24fSFrançois Tigeot
2434a85cb24fSFrançois Tigeot state->crtcs[i].state = old_crtc_state;
2435a85cb24fSFrançois Tigeot crtc->state = new_crtc_state;
24361dedbd3bSFrançois Tigeot
2437*3f2dd94aSFrançois Tigeot if (new_crtc_state->commit) {
2438ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_EXCLUSIVE);
2439*3f2dd94aSFrançois Tigeot list_add(&new_crtc_state->commit->commit_entry,
24401dedbd3bSFrançois Tigeot &crtc->commit_list);
2441ec5b6af4SFrançois Tigeot lockmgr(&crtc->commit_lock, LK_RELEASE);
24421dedbd3bSFrançois Tigeot
2443*3f2dd94aSFrançois Tigeot new_crtc_state->commit->event = NULL;
24441dedbd3bSFrançois Tigeot }
24452c9916cdSFrançois Tigeot }
24462c9916cdSFrançois Tigeot
2447a85cb24fSFrançois Tigeot for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
2448a85cb24fSFrançois Tigeot WARN_ON(plane->state != old_plane_state);
2449a85cb24fSFrançois Tigeot
2450a85cb24fSFrançois Tigeot old_plane_state->state = state;
2451a85cb24fSFrançois Tigeot new_plane_state->state = NULL;
2452a85cb24fSFrançois Tigeot
2453a85cb24fSFrançois Tigeot state->planes[i].state = old_plane_state;
2454a85cb24fSFrançois Tigeot plane->state = new_plane_state;
24552c9916cdSFrançois Tigeot }
2456*3f2dd94aSFrançois Tigeot
2457*3f2dd94aSFrançois Tigeot for_each_oldnew_private_obj_in_state(state, obj, old_obj_state, new_obj_state, i) {
2458*3f2dd94aSFrançois Tigeot WARN_ON(obj->state != old_obj_state);
2459*3f2dd94aSFrançois Tigeot
2460*3f2dd94aSFrançois Tigeot old_obj_state->state = state;
2461*3f2dd94aSFrançois Tigeot new_obj_state->state = NULL;
2462*3f2dd94aSFrançois Tigeot
2463*3f2dd94aSFrançois Tigeot state->private_objs[i].state = old_obj_state;
2464*3f2dd94aSFrançois Tigeot obj->state = new_obj_state;
2465*3f2dd94aSFrançois Tigeot }
2466*3f2dd94aSFrançois Tigeot
2467*3f2dd94aSFrançois Tigeot return 0;
24682c9916cdSFrançois Tigeot }
24692c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_swap_state);
24702c9916cdSFrançois Tigeot
24712c9916cdSFrançois Tigeot /**
24722c9916cdSFrançois Tigeot * drm_atomic_helper_update_plane - Helper for primary plane update using atomic
24732c9916cdSFrançois Tigeot * @plane: plane object to update
24742c9916cdSFrançois Tigeot * @crtc: owning CRTC of owning plane
24752c9916cdSFrançois Tigeot * @fb: framebuffer to flip onto plane
24762c9916cdSFrançois Tigeot * @crtc_x: x offset of primary plane on crtc
24772c9916cdSFrançois Tigeot * @crtc_y: y offset of primary plane on crtc
24782c9916cdSFrançois Tigeot * @crtc_w: width of primary plane rectangle on crtc
24792c9916cdSFrançois Tigeot * @crtc_h: height of primary plane rectangle on crtc
24802c9916cdSFrançois Tigeot * @src_x: x offset of @fb for panning
24812c9916cdSFrançois Tigeot * @src_y: y offset of @fb for panning
24822c9916cdSFrançois Tigeot * @src_w: width of source rectangle in @fb
24832c9916cdSFrançois Tigeot * @src_h: height of source rectangle in @fb
2484a85cb24fSFrançois Tigeot * @ctx: lock acquire context
24852c9916cdSFrançois Tigeot *
24862c9916cdSFrançois Tigeot * Provides a default plane update handler using the atomic driver interface.
24872c9916cdSFrançois Tigeot *
24882c9916cdSFrançois Tigeot * RETURNS:
24892c9916cdSFrançois Tigeot * Zero on success, error code on failure
24902c9916cdSFrançois Tigeot */
drm_atomic_helper_update_plane(struct drm_plane * plane,struct drm_crtc * crtc,struct drm_framebuffer * fb,int crtc_x,int crtc_y,unsigned int crtc_w,unsigned int crtc_h,uint32_t src_x,uint32_t src_y,uint32_t src_w,uint32_t src_h,struct drm_modeset_acquire_ctx * ctx)24912c9916cdSFrançois Tigeot int drm_atomic_helper_update_plane(struct drm_plane *plane,
24922c9916cdSFrançois Tigeot struct drm_crtc *crtc,
24932c9916cdSFrançois Tigeot struct drm_framebuffer *fb,
24942c9916cdSFrançois Tigeot int crtc_x, int crtc_y,
24952c9916cdSFrançois Tigeot unsigned int crtc_w, unsigned int crtc_h,
24962c9916cdSFrançois Tigeot uint32_t src_x, uint32_t src_y,
2497a85cb24fSFrançois Tigeot uint32_t src_w, uint32_t src_h,
2498a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
24992c9916cdSFrançois Tigeot {
25002c9916cdSFrançois Tigeot struct drm_atomic_state *state;
25012c9916cdSFrançois Tigeot struct drm_plane_state *plane_state;
25022c9916cdSFrançois Tigeot int ret = 0;
25032c9916cdSFrançois Tigeot
25042c9916cdSFrançois Tigeot state = drm_atomic_state_alloc(plane->dev);
25052c9916cdSFrançois Tigeot if (!state)
25062c9916cdSFrançois Tigeot return -ENOMEM;
25072c9916cdSFrançois Tigeot
2508a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
25092c9916cdSFrançois Tigeot plane_state = drm_atomic_get_plane_state(state, plane);
25102c9916cdSFrançois Tigeot if (IS_ERR(plane_state)) {
25112c9916cdSFrançois Tigeot ret = PTR_ERR(plane_state);
25122c9916cdSFrançois Tigeot goto fail;
25132c9916cdSFrançois Tigeot }
25142c9916cdSFrançois Tigeot
25152c9916cdSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
25162c9916cdSFrançois Tigeot if (ret != 0)
25172c9916cdSFrançois Tigeot goto fail;
25182c9916cdSFrançois Tigeot drm_atomic_set_fb_for_plane(plane_state, fb);
25192c9916cdSFrançois Tigeot plane_state->crtc_x = crtc_x;
25202c9916cdSFrançois Tigeot plane_state->crtc_y = crtc_y;
25212c9916cdSFrançois Tigeot plane_state->crtc_w = crtc_w;
2522aee94f86SFrançois Tigeot plane_state->crtc_h = crtc_h;
25232c9916cdSFrançois Tigeot plane_state->src_x = src_x;
25242c9916cdSFrançois Tigeot plane_state->src_y = src_y;
25252c9916cdSFrançois Tigeot plane_state->src_w = src_w;
2526aee94f86SFrançois Tigeot plane_state->src_h = src_h;
25272c9916cdSFrançois Tigeot
252819c468b4SFrançois Tigeot if (plane == crtc->cursor)
252919c468b4SFrançois Tigeot state->legacy_cursor_update = true;
253019c468b4SFrançois Tigeot
25312c9916cdSFrançois Tigeot ret = drm_atomic_commit(state);
25322c9916cdSFrançois Tigeot fail:
25334be47400SFrançois Tigeot drm_atomic_state_put(state);
25342c9916cdSFrançois Tigeot return ret;
25352c9916cdSFrançois Tigeot }
25362c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_update_plane);
25372c9916cdSFrançois Tigeot
25382c9916cdSFrançois Tigeot /**
25392c9916cdSFrançois Tigeot * drm_atomic_helper_disable_plane - Helper for primary plane disable using * atomic
25402c9916cdSFrançois Tigeot * @plane: plane to disable
2541a85cb24fSFrançois Tigeot * @ctx: lock acquire context
25422c9916cdSFrançois Tigeot *
25432c9916cdSFrançois Tigeot * Provides a default plane disable handler using the atomic driver interface.
25442c9916cdSFrançois Tigeot *
25452c9916cdSFrançois Tigeot * RETURNS:
25462c9916cdSFrançois Tigeot * Zero on success, error code on failure
25472c9916cdSFrançois Tigeot */
drm_atomic_helper_disable_plane(struct drm_plane * plane,struct drm_modeset_acquire_ctx * ctx)2548a85cb24fSFrançois Tigeot int drm_atomic_helper_disable_plane(struct drm_plane *plane,
2549a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
25502c9916cdSFrançois Tigeot {
25512c9916cdSFrançois Tigeot struct drm_atomic_state *state;
25522c9916cdSFrançois Tigeot struct drm_plane_state *plane_state;
25532c9916cdSFrançois Tigeot int ret = 0;
25542c9916cdSFrançois Tigeot
25552c9916cdSFrançois Tigeot state = drm_atomic_state_alloc(plane->dev);
25562c9916cdSFrançois Tigeot if (!state)
25572c9916cdSFrançois Tigeot return -ENOMEM;
25582c9916cdSFrançois Tigeot
2559a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
25602c9916cdSFrançois Tigeot plane_state = drm_atomic_get_plane_state(state, plane);
25612c9916cdSFrançois Tigeot if (IS_ERR(plane_state)) {
25622c9916cdSFrançois Tigeot ret = PTR_ERR(plane_state);
25632c9916cdSFrançois Tigeot goto fail;
25642c9916cdSFrançois Tigeot }
25652c9916cdSFrançois Tigeot
2566352ff8bdSFrançois Tigeot if (plane_state->crtc && (plane == plane->crtc->cursor))
2567352ff8bdSFrançois Tigeot plane_state->state->legacy_cursor_update = true;
2568352ff8bdSFrançois Tigeot
2569352ff8bdSFrançois Tigeot ret = __drm_atomic_helper_disable_plane(plane, plane_state);
25702c9916cdSFrançois Tigeot if (ret != 0)
25712c9916cdSFrançois Tigeot goto fail;
25722c9916cdSFrançois Tigeot
25732c9916cdSFrançois Tigeot ret = drm_atomic_commit(state);
25742c9916cdSFrançois Tigeot fail:
25754be47400SFrançois Tigeot drm_atomic_state_put(state);
25762c9916cdSFrançois Tigeot return ret;
25772c9916cdSFrançois Tigeot }
25782c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_disable_plane);
25792c9916cdSFrançois Tigeot
2580352ff8bdSFrançois Tigeot /* just used from fb-helper and atomic-helper: */
__drm_atomic_helper_disable_plane(struct drm_plane * plane,struct drm_plane_state * plane_state)2581352ff8bdSFrançois Tigeot int __drm_atomic_helper_disable_plane(struct drm_plane *plane,
2582352ff8bdSFrançois Tigeot struct drm_plane_state *plane_state)
2583352ff8bdSFrançois Tigeot {
2584352ff8bdSFrançois Tigeot int ret;
2585352ff8bdSFrançois Tigeot
2586352ff8bdSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
2587352ff8bdSFrançois Tigeot if (ret != 0)
2588352ff8bdSFrançois Tigeot return ret;
2589352ff8bdSFrançois Tigeot
2590352ff8bdSFrançois Tigeot drm_atomic_set_fb_for_plane(plane_state, NULL);
2591352ff8bdSFrançois Tigeot plane_state->crtc_x = 0;
2592352ff8bdSFrançois Tigeot plane_state->crtc_y = 0;
2593352ff8bdSFrançois Tigeot plane_state->crtc_w = 0;
2594aee94f86SFrançois Tigeot plane_state->crtc_h = 0;
2595352ff8bdSFrançois Tigeot plane_state->src_x = 0;
2596352ff8bdSFrançois Tigeot plane_state->src_y = 0;
2597352ff8bdSFrançois Tigeot plane_state->src_w = 0;
2598aee94f86SFrançois Tigeot plane_state->src_h = 0;
2599352ff8bdSFrançois Tigeot
2600352ff8bdSFrançois Tigeot return 0;
2601352ff8bdSFrançois Tigeot }
2602352ff8bdSFrançois Tigeot
update_output_state(struct drm_atomic_state * state,struct drm_mode_set * set)26032c9916cdSFrançois Tigeot static int update_output_state(struct drm_atomic_state *state,
26042c9916cdSFrançois Tigeot struct drm_mode_set *set)
26052c9916cdSFrançois Tigeot {
26062c9916cdSFrançois Tigeot struct drm_device *dev = set->crtc->dev;
2607477eb7f9SFrançois Tigeot struct drm_crtc *crtc;
2608a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
2609477eb7f9SFrançois Tigeot struct drm_connector *connector;
2610a85cb24fSFrançois Tigeot struct drm_connector_state *new_conn_state;
2611c0e85e96SFrançois Tigeot int ret, i;
26122c9916cdSFrançois Tigeot
26132c9916cdSFrançois Tigeot ret = drm_modeset_lock(&dev->mode_config.connection_mutex,
26142c9916cdSFrançois Tigeot state->acquire_ctx);
26152c9916cdSFrançois Tigeot if (ret)
26162c9916cdSFrançois Tigeot return ret;
26172c9916cdSFrançois Tigeot
2618c0e85e96SFrançois Tigeot /* First disable all connectors on the target crtc. */
2619c0e85e96SFrançois Tigeot ret = drm_atomic_add_affected_connectors(state, set->crtc);
26202c9916cdSFrançois Tigeot if (ret)
26212c9916cdSFrançois Tigeot return ret;
26222c9916cdSFrançois Tigeot
2623a85cb24fSFrançois Tigeot for_each_new_connector_in_state(state, connector, new_conn_state, i) {
2624a85cb24fSFrançois Tigeot if (new_conn_state->crtc == set->crtc) {
2625a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_connector(new_conn_state,
26262c9916cdSFrançois Tigeot NULL);
26272c9916cdSFrançois Tigeot if (ret)
26282c9916cdSFrançois Tigeot return ret;
2629a85cb24fSFrançois Tigeot
2630a85cb24fSFrançois Tigeot /* Make sure legacy setCrtc always re-trains */
2631a85cb24fSFrançois Tigeot new_conn_state->link_status = DRM_LINK_STATUS_GOOD;
26322c9916cdSFrançois Tigeot }
2633c0e85e96SFrançois Tigeot }
26342c9916cdSFrançois Tigeot
2635c0e85e96SFrançois Tigeot /* Then set all connectors from set->connectors on the target crtc */
2636c0e85e96SFrançois Tigeot for (i = 0; i < set->num_connectors; i++) {
2637a85cb24fSFrançois Tigeot new_conn_state = drm_atomic_get_connector_state(state,
2638c0e85e96SFrançois Tigeot set->connectors[i]);
2639a85cb24fSFrançois Tigeot if (IS_ERR(new_conn_state))
2640a85cb24fSFrançois Tigeot return PTR_ERR(new_conn_state);
2641c0e85e96SFrançois Tigeot
2642a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_connector(new_conn_state,
26432c9916cdSFrançois Tigeot set->crtc);
26442c9916cdSFrançois Tigeot if (ret)
26452c9916cdSFrançois Tigeot return ret;
26462c9916cdSFrançois Tigeot }
26472c9916cdSFrançois Tigeot
2648a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
26492c9916cdSFrançois Tigeot /* Don't update ->enable for the CRTC in the set_config request,
26502c9916cdSFrançois Tigeot * since a mismatch would indicate a bug in the upper layers.
26512c9916cdSFrançois Tigeot * The actual modeset code later on will catch any
26522c9916cdSFrançois Tigeot * inconsistencies here. */
26532c9916cdSFrançois Tigeot if (crtc == set->crtc)
26542c9916cdSFrançois Tigeot continue;
26552c9916cdSFrançois Tigeot
2656a85cb24fSFrançois Tigeot if (!new_crtc_state->connector_mask) {
2657a85cb24fSFrançois Tigeot ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state,
265819c468b4SFrançois Tigeot NULL);
265919c468b4SFrançois Tigeot if (ret < 0)
266019c468b4SFrançois Tigeot return ret;
266119c468b4SFrançois Tigeot
2662a85cb24fSFrançois Tigeot new_crtc_state->active = false;
266319c468b4SFrançois Tigeot }
26642c9916cdSFrançois Tigeot }
26652c9916cdSFrançois Tigeot
26662c9916cdSFrançois Tigeot return 0;
26672c9916cdSFrançois Tigeot }
26682c9916cdSFrançois Tigeot
26692c9916cdSFrançois Tigeot /**
26702c9916cdSFrançois Tigeot * drm_atomic_helper_set_config - set a new config from userspace
26712c9916cdSFrançois Tigeot * @set: mode set configuration
2672a85cb24fSFrançois Tigeot * @ctx: lock acquisition context
26732c9916cdSFrançois Tigeot *
26742c9916cdSFrançois Tigeot * Provides a default crtc set_config handler using the atomic driver interface.
26752c9916cdSFrançois Tigeot *
2676a85cb24fSFrançois Tigeot * NOTE: For backwards compatibility with old userspace this automatically
2677a85cb24fSFrançois Tigeot * resets the "link-status" property to GOOD, to force any link
2678a85cb24fSFrançois Tigeot * re-training. The SETCRTC ioctl does not define whether an update does
2679a85cb24fSFrançois Tigeot * need a full modeset or just a plane update, hence we're allowed to do
2680a85cb24fSFrançois Tigeot * that. See also drm_mode_connector_set_link_status_property().
2681a85cb24fSFrançois Tigeot *
26822c9916cdSFrançois Tigeot * Returns:
26832c9916cdSFrançois Tigeot * Returns 0 on success, negative errno numbers on failure.
26842c9916cdSFrançois Tigeot */
drm_atomic_helper_set_config(struct drm_mode_set * set,struct drm_modeset_acquire_ctx * ctx)2685a85cb24fSFrançois Tigeot int drm_atomic_helper_set_config(struct drm_mode_set *set,
2686a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
26872c9916cdSFrançois Tigeot {
26882c9916cdSFrançois Tigeot struct drm_atomic_state *state;
26892c9916cdSFrançois Tigeot struct drm_crtc *crtc = set->crtc;
26902c9916cdSFrançois Tigeot int ret = 0;
26912c9916cdSFrançois Tigeot
26922c9916cdSFrançois Tigeot state = drm_atomic_state_alloc(crtc->dev);
26932c9916cdSFrançois Tigeot if (!state)
26942c9916cdSFrançois Tigeot return -ENOMEM;
26952c9916cdSFrançois Tigeot
2696a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
2697352ff8bdSFrançois Tigeot ret = __drm_atomic_helper_set_config(set, state);
269819c468b4SFrançois Tigeot if (ret != 0)
269919c468b4SFrançois Tigeot goto fail;
270019c468b4SFrançois Tigeot
2701a85cb24fSFrançois Tigeot ret = handle_conflicting_encoders(state, true);
2702a85cb24fSFrançois Tigeot if (ret)
27032c9916cdSFrançois Tigeot return ret;
27044be47400SFrançois Tigeot
2705a85cb24fSFrançois Tigeot ret = drm_atomic_commit(state);
27062c9916cdSFrançois Tigeot
2707a85cb24fSFrançois Tigeot fail:
2708a85cb24fSFrançois Tigeot drm_atomic_state_put(state);
2709a85cb24fSFrançois Tigeot return ret;
27102c9916cdSFrançois Tigeot }
27112c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_set_config);
27122c9916cdSFrançois Tigeot
2713352ff8bdSFrançois Tigeot /* just used from fb-helper and atomic-helper: */
__drm_atomic_helper_set_config(struct drm_mode_set * set,struct drm_atomic_state * state)2714352ff8bdSFrançois Tigeot int __drm_atomic_helper_set_config(struct drm_mode_set *set,
2715352ff8bdSFrançois Tigeot struct drm_atomic_state *state)
2716352ff8bdSFrançois Tigeot {
2717352ff8bdSFrançois Tigeot struct drm_crtc_state *crtc_state;
2718352ff8bdSFrançois Tigeot struct drm_plane_state *primary_state;
2719352ff8bdSFrançois Tigeot struct drm_crtc *crtc = set->crtc;
2720352ff8bdSFrançois Tigeot int hdisplay, vdisplay;
2721352ff8bdSFrançois Tigeot int ret;
2722352ff8bdSFrançois Tigeot
2723352ff8bdSFrançois Tigeot crtc_state = drm_atomic_get_crtc_state(state, crtc);
2724352ff8bdSFrançois Tigeot if (IS_ERR(crtc_state))
2725352ff8bdSFrançois Tigeot return PTR_ERR(crtc_state);
2726352ff8bdSFrançois Tigeot
2727352ff8bdSFrançois Tigeot primary_state = drm_atomic_get_plane_state(state, crtc->primary);
2728352ff8bdSFrançois Tigeot if (IS_ERR(primary_state))
2729352ff8bdSFrançois Tigeot return PTR_ERR(primary_state);
2730352ff8bdSFrançois Tigeot
2731352ff8bdSFrançois Tigeot if (!set->mode) {
2732352ff8bdSFrançois Tigeot WARN_ON(set->fb);
2733352ff8bdSFrançois Tigeot WARN_ON(set->num_connectors);
2734352ff8bdSFrançois Tigeot
2735352ff8bdSFrançois Tigeot ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
2736352ff8bdSFrançois Tigeot if (ret != 0)
2737352ff8bdSFrançois Tigeot return ret;
2738352ff8bdSFrançois Tigeot
2739352ff8bdSFrançois Tigeot crtc_state->active = false;
2740352ff8bdSFrançois Tigeot
2741352ff8bdSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
2742352ff8bdSFrançois Tigeot if (ret != 0)
2743352ff8bdSFrançois Tigeot return ret;
2744352ff8bdSFrançois Tigeot
2745352ff8bdSFrançois Tigeot drm_atomic_set_fb_for_plane(primary_state, NULL);
2746352ff8bdSFrançois Tigeot
2747352ff8bdSFrançois Tigeot goto commit;
2748352ff8bdSFrançois Tigeot }
2749352ff8bdSFrançois Tigeot
2750352ff8bdSFrançois Tigeot WARN_ON(!set->fb);
2751352ff8bdSFrançois Tigeot WARN_ON(!set->num_connectors);
2752352ff8bdSFrançois Tigeot
2753352ff8bdSFrançois Tigeot ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
2754352ff8bdSFrançois Tigeot if (ret != 0)
2755352ff8bdSFrançois Tigeot return ret;
2756352ff8bdSFrançois Tigeot
2757352ff8bdSFrançois Tigeot crtc_state->active = true;
2758352ff8bdSFrançois Tigeot
2759352ff8bdSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
2760352ff8bdSFrançois Tigeot if (ret != 0)
2761352ff8bdSFrançois Tigeot return ret;
2762352ff8bdSFrançois Tigeot
2763a85cb24fSFrançois Tigeot drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay);
2764352ff8bdSFrançois Tigeot
2765352ff8bdSFrançois Tigeot drm_atomic_set_fb_for_plane(primary_state, set->fb);
2766352ff8bdSFrançois Tigeot primary_state->crtc_x = 0;
2767352ff8bdSFrançois Tigeot primary_state->crtc_y = 0;
2768352ff8bdSFrançois Tigeot primary_state->crtc_w = hdisplay;
2769aee94f86SFrançois Tigeot primary_state->crtc_h = vdisplay;
2770352ff8bdSFrançois Tigeot primary_state->src_x = set->x << 16;
2771352ff8bdSFrançois Tigeot primary_state->src_y = set->y << 16;
27724be47400SFrançois Tigeot if (drm_rotation_90_or_270(primary_state->rotation)) {
2773352ff8bdSFrançois Tigeot primary_state->src_w = vdisplay << 16;
2774aee94f86SFrançois Tigeot primary_state->src_h = hdisplay << 16;
2775352ff8bdSFrançois Tigeot } else {
2776352ff8bdSFrançois Tigeot primary_state->src_w = hdisplay << 16;
2777aee94f86SFrançois Tigeot primary_state->src_h = vdisplay << 16;
2778352ff8bdSFrançois Tigeot }
2779352ff8bdSFrançois Tigeot
2780352ff8bdSFrançois Tigeot commit:
2781352ff8bdSFrançois Tigeot ret = update_output_state(state, set);
2782352ff8bdSFrançois Tigeot if (ret)
2783352ff8bdSFrançois Tigeot return ret;
2784352ff8bdSFrançois Tigeot
2785352ff8bdSFrançois Tigeot return 0;
2786352ff8bdSFrançois Tigeot }
2787352ff8bdSFrançois Tigeot
27882c9916cdSFrançois Tigeot /**
2789aee94f86SFrançois Tigeot * drm_atomic_helper_disable_all - disable all currently active outputs
2790aee94f86SFrançois Tigeot * @dev: DRM device
2791aee94f86SFrançois Tigeot * @ctx: lock acquisition context
2792aee94f86SFrançois Tigeot *
2793aee94f86SFrançois Tigeot * Loops through all connectors, finding those that aren't turned off and then
2794aee94f86SFrançois Tigeot * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
2795aee94f86SFrançois Tigeot * that they are connected to.
2796aee94f86SFrançois Tigeot *
2797aee94f86SFrançois Tigeot * This is used for example in suspend/resume to disable all currently active
2798*3f2dd94aSFrançois Tigeot * functions when suspending. If you just want to shut down everything at e.g.
2799*3f2dd94aSFrançois Tigeot * driver unload, look at drm_atomic_helper_shutdown().
2800aee94f86SFrançois Tigeot *
2801aee94f86SFrançois Tigeot * Note that if callers haven't already acquired all modeset locks this might
2802aee94f86SFrançois Tigeot * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
2803aee94f86SFrançois Tigeot *
2804aee94f86SFrançois Tigeot * Returns:
2805aee94f86SFrançois Tigeot * 0 on success or a negative error code on failure.
2806aee94f86SFrançois Tigeot *
2807aee94f86SFrançois Tigeot * See also:
2808*3f2dd94aSFrançois Tigeot * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
2809*3f2dd94aSFrançois Tigeot * drm_atomic_helper_shutdown().
2810aee94f86SFrançois Tigeot */
drm_atomic_helper_disable_all(struct drm_device * dev,struct drm_modeset_acquire_ctx * ctx)2811aee94f86SFrançois Tigeot int drm_atomic_helper_disable_all(struct drm_device *dev,
2812aee94f86SFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
2813aee94f86SFrançois Tigeot {
2814aee94f86SFrançois Tigeot struct drm_atomic_state *state;
2815a85cb24fSFrançois Tigeot struct drm_connector_state *conn_state;
2816aee94f86SFrançois Tigeot struct drm_connector *conn;
2817a85cb24fSFrançois Tigeot struct drm_plane_state *plane_state;
2818a85cb24fSFrançois Tigeot struct drm_plane *plane;
2819a85cb24fSFrançois Tigeot struct drm_crtc_state *crtc_state;
2820a85cb24fSFrançois Tigeot struct drm_crtc *crtc;
2821*3f2dd94aSFrançois Tigeot unsigned plane_mask = 0;
2822a85cb24fSFrançois Tigeot int ret, i;
2823aee94f86SFrançois Tigeot
2824aee94f86SFrançois Tigeot state = drm_atomic_state_alloc(dev);
2825aee94f86SFrançois Tigeot if (!state)
2826aee94f86SFrançois Tigeot return -ENOMEM;
2827aee94f86SFrançois Tigeot
2828aee94f86SFrançois Tigeot state->acquire_ctx = ctx;
2829aee94f86SFrançois Tigeot
2830a85cb24fSFrançois Tigeot drm_for_each_crtc(crtc, dev) {
2831aee94f86SFrançois Tigeot crtc_state = drm_atomic_get_crtc_state(state, crtc);
2832aee94f86SFrançois Tigeot if (IS_ERR(crtc_state)) {
2833a85cb24fSFrançois Tigeot ret = PTR_ERR(crtc_state);
2834aee94f86SFrançois Tigeot goto free;
2835aee94f86SFrançois Tigeot }
2836aee94f86SFrançois Tigeot
2837aee94f86SFrançois Tigeot crtc_state->active = false;
2838a85cb24fSFrançois Tigeot
2839a85cb24fSFrançois Tigeot ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
2840a85cb24fSFrançois Tigeot if (ret < 0)
2841a85cb24fSFrançois Tigeot goto free;
2842a85cb24fSFrançois Tigeot
2843a85cb24fSFrançois Tigeot ret = drm_atomic_add_affected_planes(state, crtc);
2844a85cb24fSFrançois Tigeot if (ret < 0)
2845a85cb24fSFrançois Tigeot goto free;
2846a85cb24fSFrançois Tigeot
2847a85cb24fSFrançois Tigeot ret = drm_atomic_add_affected_connectors(state, crtc);
2848a85cb24fSFrançois Tigeot if (ret < 0)
2849a85cb24fSFrançois Tigeot goto free;
2850aee94f86SFrançois Tigeot }
2851aee94f86SFrançois Tigeot
2852*3f2dd94aSFrançois Tigeot for_each_new_connector_in_state(state, conn, conn_state, i) {
2853a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
2854a85cb24fSFrançois Tigeot if (ret < 0)
2855a85cb24fSFrançois Tigeot goto free;
2856a85cb24fSFrançois Tigeot }
2857a85cb24fSFrançois Tigeot
2858*3f2dd94aSFrançois Tigeot for_each_new_plane_in_state(state, plane, plane_state, i) {
2859a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
2860a85cb24fSFrançois Tigeot if (ret < 0)
2861a85cb24fSFrançois Tigeot goto free;
2862a85cb24fSFrançois Tigeot
2863a85cb24fSFrançois Tigeot drm_atomic_set_fb_for_plane(plane_state, NULL);
2864*3f2dd94aSFrançois Tigeot plane_mask |= BIT(drm_plane_index(plane));
2865*3f2dd94aSFrançois Tigeot plane->old_fb = plane->fb;
2866a85cb24fSFrançois Tigeot }
2867a85cb24fSFrançois Tigeot
2868a85cb24fSFrançois Tigeot ret = drm_atomic_commit(state);
2869aee94f86SFrançois Tigeot free:
2870*3f2dd94aSFrançois Tigeot if (plane_mask)
2871*3f2dd94aSFrançois Tigeot drm_atomic_clean_old_fb(dev, plane_mask, ret);
28724be47400SFrançois Tigeot drm_atomic_state_put(state);
2873a85cb24fSFrançois Tigeot return ret;
2874aee94f86SFrançois Tigeot }
2875a85cb24fSFrançois Tigeot
2876aee94f86SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_disable_all);
2877aee94f86SFrançois Tigeot
2878aee94f86SFrançois Tigeot /**
2879*3f2dd94aSFrançois Tigeot * drm_atomic_helper_shutdown - shutdown all CRTC
2880*3f2dd94aSFrançois Tigeot * @dev: DRM device
2881*3f2dd94aSFrançois Tigeot *
2882*3f2dd94aSFrançois Tigeot * This shuts down all CRTC, which is useful for driver unloading. Shutdown on
2883*3f2dd94aSFrançois Tigeot * suspend should instead be handled with drm_atomic_helper_suspend(), since
2884*3f2dd94aSFrançois Tigeot * that also takes a snapshot of the modeset state to be restored on resume.
2885*3f2dd94aSFrançois Tigeot *
2886*3f2dd94aSFrançois Tigeot * This is just a convenience wrapper around drm_atomic_helper_disable_all(),
2887*3f2dd94aSFrançois Tigeot * and it is the atomic version of drm_crtc_force_disable_all().
2888*3f2dd94aSFrançois Tigeot */
drm_atomic_helper_shutdown(struct drm_device * dev)2889*3f2dd94aSFrançois Tigeot void drm_atomic_helper_shutdown(struct drm_device *dev)
2890*3f2dd94aSFrançois Tigeot {
2891*3f2dd94aSFrançois Tigeot struct drm_modeset_acquire_ctx ctx;
2892*3f2dd94aSFrançois Tigeot int ret;
2893*3f2dd94aSFrançois Tigeot
2894*3f2dd94aSFrançois Tigeot drm_modeset_acquire_init(&ctx, 0);
2895*3f2dd94aSFrançois Tigeot while (1) {
2896*3f2dd94aSFrançois Tigeot ret = drm_modeset_lock_all_ctx(dev, &ctx);
2897*3f2dd94aSFrançois Tigeot if (!ret)
2898*3f2dd94aSFrançois Tigeot ret = drm_atomic_helper_disable_all(dev, &ctx);
2899*3f2dd94aSFrançois Tigeot
2900*3f2dd94aSFrançois Tigeot if (ret != -EDEADLK)
2901*3f2dd94aSFrançois Tigeot break;
2902*3f2dd94aSFrançois Tigeot
2903*3f2dd94aSFrançois Tigeot drm_modeset_backoff(&ctx);
2904*3f2dd94aSFrançois Tigeot }
2905*3f2dd94aSFrançois Tigeot
2906*3f2dd94aSFrançois Tigeot if (ret)
2907*3f2dd94aSFrançois Tigeot DRM_ERROR("Disabling all crtc's during unload failed with %i\n", ret);
2908*3f2dd94aSFrançois Tigeot
2909*3f2dd94aSFrançois Tigeot drm_modeset_drop_locks(&ctx);
2910*3f2dd94aSFrançois Tigeot drm_modeset_acquire_fini(&ctx);
2911*3f2dd94aSFrançois Tigeot }
2912*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_shutdown);
2913*3f2dd94aSFrançois Tigeot
2914*3f2dd94aSFrançois Tigeot /**
2915aee94f86SFrançois Tigeot * drm_atomic_helper_suspend - subsystem-level suspend helper
2916aee94f86SFrançois Tigeot * @dev: DRM device
2917aee94f86SFrançois Tigeot *
2918aee94f86SFrançois Tigeot * Duplicates the current atomic state, disables all active outputs and then
2919aee94f86SFrançois Tigeot * returns a pointer to the original atomic state to the caller. Drivers can
2920aee94f86SFrançois Tigeot * pass this pointer to the drm_atomic_helper_resume() helper upon resume to
2921aee94f86SFrançois Tigeot * restore the output configuration that was active at the time the system
2922aee94f86SFrançois Tigeot * entered suspend.
2923aee94f86SFrançois Tigeot *
2924aee94f86SFrançois Tigeot * Note that it is potentially unsafe to use this. The atomic state object
2925aee94f86SFrançois Tigeot * returned by this function is assumed to be persistent. Drivers must ensure
2926aee94f86SFrançois Tigeot * that this holds true. Before calling this function, drivers must make sure
2927aee94f86SFrançois Tigeot * to suspend fbdev emulation so that nothing can be using the device.
2928aee94f86SFrançois Tigeot *
2929aee94f86SFrançois Tigeot * Returns:
2930aee94f86SFrançois Tigeot * A pointer to a copy of the state before suspend on success or an ERR_PTR()-
2931aee94f86SFrançois Tigeot * encoded error code on failure. Drivers should store the returned atomic
2932aee94f86SFrançois Tigeot * state object and pass it to the drm_atomic_helper_resume() helper upon
2933aee94f86SFrançois Tigeot * resume.
2934aee94f86SFrançois Tigeot *
2935aee94f86SFrançois Tigeot * See also:
2936aee94f86SFrançois Tigeot * drm_atomic_helper_duplicate_state(), drm_atomic_helper_disable_all(),
2937a85cb24fSFrançois Tigeot * drm_atomic_helper_resume(), drm_atomic_helper_commit_duplicated_state()
2938aee94f86SFrançois Tigeot */
drm_atomic_helper_suspend(struct drm_device * dev)2939aee94f86SFrançois Tigeot struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev)
2940aee94f86SFrançois Tigeot {
2941aee94f86SFrançois Tigeot struct drm_modeset_acquire_ctx ctx;
2942aee94f86SFrançois Tigeot struct drm_atomic_state *state;
2943aee94f86SFrançois Tigeot int err;
2944aee94f86SFrançois Tigeot
2945aee94f86SFrançois Tigeot drm_modeset_acquire_init(&ctx, 0);
2946aee94f86SFrançois Tigeot
2947aee94f86SFrançois Tigeot retry:
2948aee94f86SFrançois Tigeot err = drm_modeset_lock_all_ctx(dev, &ctx);
2949aee94f86SFrançois Tigeot if (err < 0) {
2950aee94f86SFrançois Tigeot state = ERR_PTR(err);
2951aee94f86SFrançois Tigeot goto unlock;
2952aee94f86SFrançois Tigeot }
2953aee94f86SFrançois Tigeot
2954aee94f86SFrançois Tigeot state = drm_atomic_helper_duplicate_state(dev, &ctx);
2955aee94f86SFrançois Tigeot if (IS_ERR(state))
2956aee94f86SFrançois Tigeot goto unlock;
2957aee94f86SFrançois Tigeot
2958aee94f86SFrançois Tigeot err = drm_atomic_helper_disable_all(dev, &ctx);
2959aee94f86SFrançois Tigeot if (err < 0) {
29604be47400SFrançois Tigeot drm_atomic_state_put(state);
2961aee94f86SFrançois Tigeot state = ERR_PTR(err);
2962aee94f86SFrançois Tigeot goto unlock;
2963aee94f86SFrançois Tigeot }
2964aee94f86SFrançois Tigeot
2965aee94f86SFrançois Tigeot unlock:
2966aee94f86SFrançois Tigeot if (PTR_ERR(state) == -EDEADLK) {
2967aee94f86SFrançois Tigeot drm_modeset_backoff(&ctx);
2968aee94f86SFrançois Tigeot goto retry;
2969aee94f86SFrançois Tigeot }
2970aee94f86SFrançois Tigeot
2971aee94f86SFrançois Tigeot drm_modeset_drop_locks(&ctx);
2972aee94f86SFrançois Tigeot drm_modeset_acquire_fini(&ctx);
2973aee94f86SFrançois Tigeot return state;
2974aee94f86SFrançois Tigeot }
2975aee94f86SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_suspend);
2976aee94f86SFrançois Tigeot
2977aee94f86SFrançois Tigeot /**
2978a85cb24fSFrançois Tigeot * drm_atomic_helper_commit_duplicated_state - commit duplicated state
2979a85cb24fSFrançois Tigeot * @state: duplicated atomic state to commit
2980a85cb24fSFrançois Tigeot * @ctx: pointer to acquire_ctx to use for commit.
2981a85cb24fSFrançois Tigeot *
2982a85cb24fSFrançois Tigeot * The state returned by drm_atomic_helper_duplicate_state() and
2983a85cb24fSFrançois Tigeot * drm_atomic_helper_suspend() is partially invalid, and needs to
2984a85cb24fSFrançois Tigeot * be fixed up before commit.
2985a85cb24fSFrançois Tigeot *
2986a85cb24fSFrançois Tigeot * Returns:
2987a85cb24fSFrançois Tigeot * 0 on success or a negative error code on failure.
2988a85cb24fSFrançois Tigeot *
2989a85cb24fSFrançois Tigeot * See also:
2990a85cb24fSFrançois Tigeot * drm_atomic_helper_suspend()
2991a85cb24fSFrançois Tigeot */
drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state * state,struct drm_modeset_acquire_ctx * ctx)2992a85cb24fSFrançois Tigeot int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state,
2993a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
2994a85cb24fSFrançois Tigeot {
2995a85cb24fSFrançois Tigeot int i;
2996a85cb24fSFrançois Tigeot struct drm_plane *plane;
2997a85cb24fSFrançois Tigeot struct drm_plane_state *new_plane_state;
2998a85cb24fSFrançois Tigeot struct drm_connector *connector;
2999a85cb24fSFrançois Tigeot struct drm_connector_state *new_conn_state;
3000a85cb24fSFrançois Tigeot struct drm_crtc *crtc;
3001a85cb24fSFrançois Tigeot struct drm_crtc_state *new_crtc_state;
3002*3f2dd94aSFrançois Tigeot unsigned plane_mask = 0;
3003*3f2dd94aSFrançois Tigeot struct drm_device *dev = state->dev;
3004*3f2dd94aSFrançois Tigeot int ret;
3005a85cb24fSFrançois Tigeot
3006a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
3007a85cb24fSFrançois Tigeot
3008*3f2dd94aSFrançois Tigeot for_each_new_plane_in_state(state, plane, new_plane_state, i) {
3009*3f2dd94aSFrançois Tigeot plane_mask |= BIT(drm_plane_index(plane));
3010a85cb24fSFrançois Tigeot state->planes[i].old_state = plane->state;
3011*3f2dd94aSFrançois Tigeot }
3012a85cb24fSFrançois Tigeot
3013a85cb24fSFrançois Tigeot for_each_new_crtc_in_state(state, crtc, new_crtc_state, i)
3014a85cb24fSFrançois Tigeot state->crtcs[i].old_state = crtc->state;
3015a85cb24fSFrançois Tigeot
3016a85cb24fSFrançois Tigeot for_each_new_connector_in_state(state, connector, new_conn_state, i)
3017a85cb24fSFrançois Tigeot state->connectors[i].old_state = connector->state;
3018a85cb24fSFrançois Tigeot
3019*3f2dd94aSFrançois Tigeot ret = drm_atomic_commit(state);
3020*3f2dd94aSFrançois Tigeot if (plane_mask)
3021*3f2dd94aSFrançois Tigeot drm_atomic_clean_old_fb(dev, plane_mask, ret);
3022*3f2dd94aSFrançois Tigeot
3023*3f2dd94aSFrançois Tigeot return ret;
3024a85cb24fSFrançois Tigeot }
3025a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_commit_duplicated_state);
3026a85cb24fSFrançois Tigeot
3027a85cb24fSFrançois Tigeot /**
3028aee94f86SFrançois Tigeot * drm_atomic_helper_resume - subsystem-level resume helper
3029aee94f86SFrançois Tigeot * @dev: DRM device
3030aee94f86SFrançois Tigeot * @state: atomic state to resume to
3031aee94f86SFrançois Tigeot *
3032aee94f86SFrançois Tigeot * Calls drm_mode_config_reset() to synchronize hardware and software states,
3033aee94f86SFrançois Tigeot * grabs all modeset locks and commits the atomic state object. This can be
3034aee94f86SFrançois Tigeot * used in conjunction with the drm_atomic_helper_suspend() helper to
3035aee94f86SFrançois Tigeot * implement suspend/resume for drivers that support atomic mode-setting.
3036aee94f86SFrançois Tigeot *
3037aee94f86SFrançois Tigeot * Returns:
3038aee94f86SFrançois Tigeot * 0 on success or a negative error code on failure.
3039aee94f86SFrançois Tigeot *
3040aee94f86SFrançois Tigeot * See also:
3041aee94f86SFrançois Tigeot * drm_atomic_helper_suspend()
3042aee94f86SFrançois Tigeot */
drm_atomic_helper_resume(struct drm_device * dev,struct drm_atomic_state * state)3043aee94f86SFrançois Tigeot int drm_atomic_helper_resume(struct drm_device *dev,
3044aee94f86SFrançois Tigeot struct drm_atomic_state *state)
3045aee94f86SFrançois Tigeot {
3046a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx ctx;
3047aee94f86SFrançois Tigeot int err;
3048aee94f86SFrançois Tigeot
3049aee94f86SFrançois Tigeot drm_mode_config_reset(dev);
3050a85cb24fSFrançois Tigeot
3051a85cb24fSFrançois Tigeot drm_modeset_acquire_init(&ctx, 0);
3052a85cb24fSFrançois Tigeot while (1) {
3053a85cb24fSFrançois Tigeot err = drm_modeset_lock_all_ctx(dev, &ctx);
3054a85cb24fSFrançois Tigeot if (err)
3055a85cb24fSFrançois Tigeot goto out;
3056a85cb24fSFrançois Tigeot
3057a85cb24fSFrançois Tigeot err = drm_atomic_helper_commit_duplicated_state(state, &ctx);
3058a85cb24fSFrançois Tigeot out:
3059a85cb24fSFrançois Tigeot if (err != -EDEADLK)
3060a85cb24fSFrançois Tigeot break;
3061a85cb24fSFrançois Tigeot
3062a85cb24fSFrançois Tigeot drm_modeset_backoff(&ctx);
3063a85cb24fSFrançois Tigeot }
3064a85cb24fSFrançois Tigeot
3065*3f2dd94aSFrançois Tigeot drm_atomic_state_put(state);
3066a85cb24fSFrançois Tigeot drm_modeset_drop_locks(&ctx);
3067a85cb24fSFrançois Tigeot drm_modeset_acquire_fini(&ctx);
3068aee94f86SFrançois Tigeot
3069aee94f86SFrançois Tigeot return err;
3070aee94f86SFrançois Tigeot }
3071aee94f86SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_resume);
3072aee94f86SFrançois Tigeot
page_flip_common(struct drm_atomic_state * state,struct drm_crtc * crtc,struct drm_framebuffer * fb,struct drm_pending_vblank_event * event,uint32_t flags)3073*3f2dd94aSFrançois Tigeot static int page_flip_common(struct drm_atomic_state *state,
3074a85cb24fSFrançois Tigeot struct drm_crtc *crtc,
3075a85cb24fSFrançois Tigeot struct drm_framebuffer *fb,
3076a85cb24fSFrançois Tigeot struct drm_pending_vblank_event *event,
3077a85cb24fSFrançois Tigeot uint32_t flags)
3078a85cb24fSFrançois Tigeot {
3079a85cb24fSFrançois Tigeot struct drm_plane *plane = crtc->primary;
3080a85cb24fSFrançois Tigeot struct drm_plane_state *plane_state;
3081a85cb24fSFrançois Tigeot struct drm_crtc_state *crtc_state;
3082a85cb24fSFrançois Tigeot int ret = 0;
3083a85cb24fSFrançois Tigeot
3084a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_crtc_state(state, crtc);
3085a85cb24fSFrançois Tigeot if (IS_ERR(crtc_state))
3086a85cb24fSFrançois Tigeot return PTR_ERR(crtc_state);
3087a85cb24fSFrançois Tigeot
3088a85cb24fSFrançois Tigeot crtc_state->event = event;
3089a85cb24fSFrançois Tigeot crtc_state->pageflip_flags = flags;
3090a85cb24fSFrançois Tigeot
3091a85cb24fSFrançois Tigeot plane_state = drm_atomic_get_plane_state(state, plane);
3092a85cb24fSFrançois Tigeot if (IS_ERR(plane_state))
3093a85cb24fSFrançois Tigeot return PTR_ERR(plane_state);
3094a85cb24fSFrançois Tigeot
3095a85cb24fSFrançois Tigeot ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
3096a85cb24fSFrançois Tigeot if (ret != 0)
3097a85cb24fSFrançois Tigeot return ret;
3098a85cb24fSFrançois Tigeot drm_atomic_set_fb_for_plane(plane_state, fb);
3099a85cb24fSFrançois Tigeot
3100a85cb24fSFrançois Tigeot /* Make sure we don't accidentally do a full modeset. */
3101a85cb24fSFrançois Tigeot state->allow_modeset = false;
3102a85cb24fSFrançois Tigeot if (!crtc_state->active) {
3103a85cb24fSFrançois Tigeot DRM_DEBUG_ATOMIC("[CRTC:%d:%s] disabled, rejecting legacy flip\n",
3104a85cb24fSFrançois Tigeot crtc->base.id, crtc->name);
3105a85cb24fSFrançois Tigeot return -EINVAL;
3106a85cb24fSFrançois Tigeot }
3107a85cb24fSFrançois Tigeot
3108a85cb24fSFrançois Tigeot return ret;
3109a85cb24fSFrançois Tigeot }
3110a85cb24fSFrançois Tigeot
31112c9916cdSFrançois Tigeot /**
31122c9916cdSFrançois Tigeot * drm_atomic_helper_page_flip - execute a legacy page flip
31132c9916cdSFrançois Tigeot * @crtc: DRM crtc
31142c9916cdSFrançois Tigeot * @fb: DRM framebuffer
31152c9916cdSFrançois Tigeot * @event: optional DRM event to signal upon completion
31162c9916cdSFrançois Tigeot * @flags: flip flags for non-vblank sync'ed updates
3117a85cb24fSFrançois Tigeot * @ctx: lock acquisition context
31182c9916cdSFrançois Tigeot *
3119a85cb24fSFrançois Tigeot * Provides a default &drm_crtc_funcs.page_flip implementation
3120a85cb24fSFrançois Tigeot * using the atomic driver interface.
31212c9916cdSFrançois Tigeot *
31222c9916cdSFrançois Tigeot * Returns:
31232c9916cdSFrançois Tigeot * Returns 0 on success, negative errno numbers on failure.
3124a85cb24fSFrançois Tigeot *
3125a85cb24fSFrançois Tigeot * See also:
3126a85cb24fSFrançois Tigeot * drm_atomic_helper_page_flip_target()
31272c9916cdSFrançois Tigeot */
drm_atomic_helper_page_flip(struct drm_crtc * crtc,struct drm_framebuffer * fb,struct drm_pending_vblank_event * event,uint32_t flags,struct drm_modeset_acquire_ctx * ctx)31282c9916cdSFrançois Tigeot int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
31292c9916cdSFrançois Tigeot struct drm_framebuffer *fb,
31302c9916cdSFrançois Tigeot struct drm_pending_vblank_event *event,
3131a85cb24fSFrançois Tigeot uint32_t flags,
3132a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
31332c9916cdSFrançois Tigeot {
31342c9916cdSFrançois Tigeot struct drm_plane *plane = crtc->primary;
31352c9916cdSFrançois Tigeot struct drm_atomic_state *state;
31362c9916cdSFrançois Tigeot int ret = 0;
31372c9916cdSFrançois Tigeot
31382c9916cdSFrançois Tigeot state = drm_atomic_state_alloc(plane->dev);
31392c9916cdSFrançois Tigeot if (!state)
31402c9916cdSFrançois Tigeot return -ENOMEM;
31412c9916cdSFrançois Tigeot
3142a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
31432c9916cdSFrançois Tigeot
3144a85cb24fSFrançois Tigeot ret = page_flip_common(state, crtc, fb, event, flags);
31452c9916cdSFrançois Tigeot if (ret != 0)
31462c9916cdSFrançois Tigeot goto fail;
3147aee94f86SFrançois Tigeot
31488621f407SFrançois Tigeot ret = drm_atomic_nonblocking_commit(state);
31492c9916cdSFrançois Tigeot fail:
31504be47400SFrançois Tigeot drm_atomic_state_put(state);
31512c9916cdSFrançois Tigeot return ret;
31522c9916cdSFrançois Tigeot }
31532c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_page_flip);
31542c9916cdSFrançois Tigeot
31552c9916cdSFrançois Tigeot /**
3156a85cb24fSFrançois Tigeot * drm_atomic_helper_page_flip_target - do page flip on target vblank period.
3157a85cb24fSFrançois Tigeot * @crtc: DRM crtc
3158a85cb24fSFrançois Tigeot * @fb: DRM framebuffer
3159a85cb24fSFrançois Tigeot * @event: optional DRM event to signal upon completion
3160a85cb24fSFrançois Tigeot * @flags: flip flags for non-vblank sync'ed updates
3161a85cb24fSFrançois Tigeot * @target: specifying the target vblank period when the flip to take effect
3162a85cb24fSFrançois Tigeot * @ctx: lock acquisition context
3163a85cb24fSFrançois Tigeot *
3164a85cb24fSFrançois Tigeot * Provides a default &drm_crtc_funcs.page_flip_target implementation.
3165a85cb24fSFrançois Tigeot * Similar to drm_atomic_helper_page_flip() with extra parameter to specify
3166a85cb24fSFrançois Tigeot * target vblank period to flip.
3167a85cb24fSFrançois Tigeot *
3168a85cb24fSFrançois Tigeot * Returns:
3169a85cb24fSFrançois Tigeot * Returns 0 on success, negative errno numbers on failure.
3170a85cb24fSFrançois Tigeot */
drm_atomic_helper_page_flip_target(struct drm_crtc * crtc,struct drm_framebuffer * fb,struct drm_pending_vblank_event * event,uint32_t flags,uint32_t target,struct drm_modeset_acquire_ctx * ctx)3171*3f2dd94aSFrançois Tigeot int drm_atomic_helper_page_flip_target(struct drm_crtc *crtc,
3172a85cb24fSFrançois Tigeot struct drm_framebuffer *fb,
3173a85cb24fSFrançois Tigeot struct drm_pending_vblank_event *event,
3174a85cb24fSFrançois Tigeot uint32_t flags,
3175a85cb24fSFrançois Tigeot uint32_t target,
3176a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
3177a85cb24fSFrançois Tigeot {
3178a85cb24fSFrançois Tigeot struct drm_plane *plane = crtc->primary;
3179a85cb24fSFrançois Tigeot struct drm_atomic_state *state;
3180a85cb24fSFrançois Tigeot struct drm_crtc_state *crtc_state;
3181a85cb24fSFrançois Tigeot int ret = 0;
3182a85cb24fSFrançois Tigeot
3183a85cb24fSFrançois Tigeot state = drm_atomic_state_alloc(plane->dev);
3184a85cb24fSFrançois Tigeot if (!state)
3185a85cb24fSFrançois Tigeot return -ENOMEM;
3186a85cb24fSFrançois Tigeot
3187a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
3188a85cb24fSFrançois Tigeot
3189a85cb24fSFrançois Tigeot ret = page_flip_common(state, crtc, fb, event, flags);
3190a85cb24fSFrançois Tigeot if (ret != 0)
3191a85cb24fSFrançois Tigeot goto fail;
3192a85cb24fSFrançois Tigeot
3193a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
3194a85cb24fSFrançois Tigeot if (WARN_ON(!crtc_state)) {
3195a85cb24fSFrançois Tigeot ret = -EINVAL;
3196a85cb24fSFrançois Tigeot goto fail;
3197a85cb24fSFrançois Tigeot }
3198a85cb24fSFrançois Tigeot crtc_state->target_vblank = target;
3199a85cb24fSFrançois Tigeot
3200a85cb24fSFrançois Tigeot ret = drm_atomic_nonblocking_commit(state);
3201a85cb24fSFrançois Tigeot fail:
3202a85cb24fSFrançois Tigeot drm_atomic_state_put(state);
3203a85cb24fSFrançois Tigeot return ret;
3204a85cb24fSFrançois Tigeot }
3205a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_page_flip_target);
3206a85cb24fSFrançois Tigeot
3207a85cb24fSFrançois Tigeot /**
3208a85cb24fSFrançois Tigeot * drm_atomic_helper_best_encoder - Helper for
3209a85cb24fSFrançois Tigeot * &drm_connector_helper_funcs.best_encoder callback
32108621f407SFrançois Tigeot * @connector: Connector control structure
32118621f407SFrançois Tigeot *
3212a85cb24fSFrançois Tigeot * This is a &drm_connector_helper_funcs.best_encoder callback helper for
32138621f407SFrançois Tigeot * connectors that support exactly 1 encoder, statically determined at driver
32148621f407SFrançois Tigeot * init time.
32158621f407SFrançois Tigeot */
32168621f407SFrançois Tigeot struct drm_encoder *
drm_atomic_helper_best_encoder(struct drm_connector * connector)32178621f407SFrançois Tigeot drm_atomic_helper_best_encoder(struct drm_connector *connector)
32188621f407SFrançois Tigeot {
32198621f407SFrançois Tigeot WARN_ON(connector->encoder_ids[1]);
3220*3f2dd94aSFrançois Tigeot return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
32218621f407SFrançois Tigeot }
32228621f407SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_best_encoder);
32238621f407SFrançois Tigeot
32248621f407SFrançois Tigeot /**
32252c9916cdSFrançois Tigeot * DOC: atomic state reset and initialization
32262c9916cdSFrançois Tigeot *
32272c9916cdSFrançois Tigeot * Both the drm core and the atomic helpers assume that there is always the full
32282c9916cdSFrançois Tigeot * and correct atomic software state for all connectors, CRTCs and planes
32292c9916cdSFrançois Tigeot * available. Which is a bit a problem on driver load and also after system
32302c9916cdSFrançois Tigeot * suspend. One way to solve this is to have a hardware state read-out
32312c9916cdSFrançois Tigeot * infrastructure which reconstructs the full software state (e.g. the i915
32322c9916cdSFrançois Tigeot * driver).
32332c9916cdSFrançois Tigeot *
32342c9916cdSFrançois Tigeot * The simpler solution is to just reset the software state to everything off,
32352c9916cdSFrançois Tigeot * which is easiest to do by calling drm_mode_config_reset(). To facilitate this
32362c9916cdSFrançois Tigeot * the atomic helpers provide default reset implementations for all hooks.
3237aee94f86SFrançois Tigeot *
3238aee94f86SFrançois Tigeot * On the upside the precise state tracking of atomic simplifies system suspend
3239aee94f86SFrançois Tigeot * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe
3240aee94f86SFrançois Tigeot * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume().
3241aee94f86SFrançois Tigeot * For other drivers the building blocks are split out, see the documentation
3242aee94f86SFrançois Tigeot * for these functions.
32432c9916cdSFrançois Tigeot */
32442c9916cdSFrançois Tigeot
32452c9916cdSFrançois Tigeot /**
3246a85cb24fSFrançois Tigeot * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs
32472c9916cdSFrançois Tigeot * @crtc: drm CRTC
32482c9916cdSFrançois Tigeot *
32492c9916cdSFrançois Tigeot * Resets the atomic state for @crtc by freeing the state pointer (which might
32502c9916cdSFrançois Tigeot * be NULL, e.g. at driver load time) and allocating a new empty state object.
32512c9916cdSFrançois Tigeot */
drm_atomic_helper_crtc_reset(struct drm_crtc * crtc)32522c9916cdSFrançois Tigeot void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
32532c9916cdSFrançois Tigeot {
32548621f407SFrançois Tigeot if (crtc->state)
32558621f407SFrançois Tigeot __drm_atomic_helper_crtc_destroy_state(crtc->state);
32568621f407SFrançois Tigeot
32572c9916cdSFrançois Tigeot kfree(crtc->state);
32582c9916cdSFrançois Tigeot crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
32592c9916cdSFrançois Tigeot
32602c9916cdSFrançois Tigeot if (crtc->state)
32612c9916cdSFrançois Tigeot crtc->state->crtc = crtc;
32622c9916cdSFrançois Tigeot }
32632c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
32642c9916cdSFrançois Tigeot
32652c9916cdSFrançois Tigeot /**
3266477eb7f9SFrançois Tigeot * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
3267477eb7f9SFrançois Tigeot * @crtc: CRTC object
3268477eb7f9SFrançois Tigeot * @state: atomic CRTC state
3269477eb7f9SFrançois Tigeot *
3270477eb7f9SFrançois Tigeot * Copies atomic state from a CRTC's current state and resets inferred values.
3271477eb7f9SFrançois Tigeot * This is useful for drivers that subclass the CRTC state.
3272477eb7f9SFrançois Tigeot */
__drm_atomic_helper_crtc_duplicate_state(struct drm_crtc * crtc,struct drm_crtc_state * state)3273477eb7f9SFrançois Tigeot void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
3274477eb7f9SFrançois Tigeot struct drm_crtc_state *state)
3275477eb7f9SFrançois Tigeot {
3276477eb7f9SFrançois Tigeot memcpy(state, crtc->state, sizeof(*state));
3277477eb7f9SFrançois Tigeot
327819c468b4SFrançois Tigeot if (state->mode_blob)
3279a85cb24fSFrançois Tigeot drm_property_blob_get(state->mode_blob);
3280c0e85e96SFrançois Tigeot if (state->degamma_lut)
3281a85cb24fSFrançois Tigeot drm_property_blob_get(state->degamma_lut);
3282c0e85e96SFrançois Tigeot if (state->ctm)
3283a85cb24fSFrançois Tigeot drm_property_blob_get(state->ctm);
3284c0e85e96SFrançois Tigeot if (state->gamma_lut)
3285a85cb24fSFrançois Tigeot drm_property_blob_get(state->gamma_lut);
3286477eb7f9SFrançois Tigeot state->mode_changed = false;
3287477eb7f9SFrançois Tigeot state->active_changed = false;
3288477eb7f9SFrançois Tigeot state->planes_changed = false;
3289a05eeebfSFrançois Tigeot state->connectors_changed = false;
3290c0e85e96SFrançois Tigeot state->color_mgmt_changed = false;
32911dedbd3bSFrançois Tigeot state->zpos_changed = false;
3292*3f2dd94aSFrançois Tigeot state->commit = NULL;
3293477eb7f9SFrançois Tigeot state->event = NULL;
3294a85cb24fSFrançois Tigeot state->pageflip_flags = 0;
3295477eb7f9SFrançois Tigeot }
3296477eb7f9SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);
3297477eb7f9SFrançois Tigeot
3298477eb7f9SFrançois Tigeot /**
32992c9916cdSFrançois Tigeot * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook
33002c9916cdSFrançois Tigeot * @crtc: drm CRTC
33012c9916cdSFrançois Tigeot *
33022c9916cdSFrançois Tigeot * Default CRTC state duplicate hook for drivers which don't have their own
33032c9916cdSFrançois Tigeot * subclassed CRTC state structure.
33042c9916cdSFrançois Tigeot */
33052c9916cdSFrançois Tigeot struct drm_crtc_state *
drm_atomic_helper_crtc_duplicate_state(struct drm_crtc * crtc)33062c9916cdSFrançois Tigeot drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
33072c9916cdSFrançois Tigeot {
33082c9916cdSFrançois Tigeot struct drm_crtc_state *state;
33092c9916cdSFrançois Tigeot
33102c9916cdSFrançois Tigeot if (WARN_ON(!crtc->state))
33112c9916cdSFrançois Tigeot return NULL;
33122c9916cdSFrançois Tigeot
33131e12ee3bSFrançois Tigeot state = kmalloc(sizeof(*state), M_DRM, GFP_KERNEL);
3314477eb7f9SFrançois Tigeot if (state)
3315477eb7f9SFrançois Tigeot __drm_atomic_helper_crtc_duplicate_state(crtc, state);
33162c9916cdSFrançois Tigeot
33172c9916cdSFrançois Tigeot return state;
33182c9916cdSFrançois Tigeot }
33192c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
33202c9916cdSFrançois Tigeot
33212c9916cdSFrançois Tigeot /**
3322477eb7f9SFrançois Tigeot * __drm_atomic_helper_crtc_destroy_state - release CRTC state
3323477eb7f9SFrançois Tigeot * @state: CRTC state object to release
3324477eb7f9SFrançois Tigeot *
3325477eb7f9SFrançois Tigeot * Releases all resources stored in the CRTC state without actually freeing
3326477eb7f9SFrançois Tigeot * the memory of the CRTC state. This is useful for drivers that subclass the
3327477eb7f9SFrançois Tigeot * CRTC state.
3328477eb7f9SFrançois Tigeot */
__drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state * state)33298621f407SFrançois Tigeot void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
3330477eb7f9SFrançois Tigeot {
3331*3f2dd94aSFrançois Tigeot if (state->commit) {
3332*3f2dd94aSFrançois Tigeot /*
3333*3f2dd94aSFrançois Tigeot * In the event that a non-blocking commit returns
3334*3f2dd94aSFrançois Tigeot * -ERESTARTSYS before the commit_tail work is queued, we will
3335*3f2dd94aSFrançois Tigeot * have an extra reference to the commit object. Release it, if
3336*3f2dd94aSFrançois Tigeot * the event has not been consumed by the worker.
3337*3f2dd94aSFrançois Tigeot *
3338*3f2dd94aSFrançois Tigeot * state->event may be freed, so we can't directly look at
3339*3f2dd94aSFrançois Tigeot * state->event->base.completion.
3340*3f2dd94aSFrançois Tigeot */
3341*3f2dd94aSFrançois Tigeot if (state->event && state->commit->abort_completion)
3342*3f2dd94aSFrançois Tigeot drm_crtc_commit_put(state->commit);
3343*3f2dd94aSFrançois Tigeot
3344*3f2dd94aSFrançois Tigeot kfree(state->commit->event);
3345*3f2dd94aSFrançois Tigeot state->commit->event = NULL;
3346*3f2dd94aSFrançois Tigeot
3347*3f2dd94aSFrançois Tigeot drm_crtc_commit_put(state->commit);
3348*3f2dd94aSFrançois Tigeot }
3349*3f2dd94aSFrançois Tigeot
3350a85cb24fSFrançois Tigeot drm_property_blob_put(state->mode_blob);
3351a85cb24fSFrançois Tigeot drm_property_blob_put(state->degamma_lut);
3352a85cb24fSFrançois Tigeot drm_property_blob_put(state->ctm);
3353a85cb24fSFrançois Tigeot drm_property_blob_put(state->gamma_lut);
3354477eb7f9SFrançois Tigeot }
3355477eb7f9SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
3356477eb7f9SFrançois Tigeot
3357477eb7f9SFrançois Tigeot /**
33582c9916cdSFrançois Tigeot * drm_atomic_helper_crtc_destroy_state - default state destroy hook
33592c9916cdSFrançois Tigeot * @crtc: drm CRTC
33602c9916cdSFrançois Tigeot * @state: CRTC state object to release
33612c9916cdSFrançois Tigeot *
33622c9916cdSFrançois Tigeot * Default CRTC state destroy hook for drivers which don't have their own
33632c9916cdSFrançois Tigeot * subclassed CRTC state structure.
33642c9916cdSFrançois Tigeot */
drm_atomic_helper_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)33652c9916cdSFrançois Tigeot void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
33662c9916cdSFrançois Tigeot struct drm_crtc_state *state)
33672c9916cdSFrançois Tigeot {
33688621f407SFrançois Tigeot __drm_atomic_helper_crtc_destroy_state(state);
33692c9916cdSFrançois Tigeot kfree(state);
33702c9916cdSFrançois Tigeot }
33712c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
33722c9916cdSFrançois Tigeot
33732c9916cdSFrançois Tigeot /**
3374a85cb24fSFrançois Tigeot * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes
33752c9916cdSFrançois Tigeot * @plane: drm plane
33762c9916cdSFrançois Tigeot *
33772c9916cdSFrançois Tigeot * Resets the atomic state for @plane by freeing the state pointer (which might
33782c9916cdSFrançois Tigeot * be NULL, e.g. at driver load time) and allocating a new empty state object.
33792c9916cdSFrançois Tigeot */
drm_atomic_helper_plane_reset(struct drm_plane * plane)33802c9916cdSFrançois Tigeot void drm_atomic_helper_plane_reset(struct drm_plane *plane)
33812c9916cdSFrançois Tigeot {
33828621f407SFrançois Tigeot if (plane->state)
33838621f407SFrançois Tigeot __drm_atomic_helper_plane_destroy_state(plane->state);
33842c9916cdSFrançois Tigeot
33852c9916cdSFrançois Tigeot kfree(plane->state);
33862c9916cdSFrançois Tigeot plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
33872c9916cdSFrançois Tigeot
3388c0e85e96SFrançois Tigeot if (plane->state) {
33892c9916cdSFrançois Tigeot plane->state->plane = plane;
3390*3f2dd94aSFrançois Tigeot plane->state->rotation = DRM_MODE_ROTATE_0;
3391c0e85e96SFrançois Tigeot }
33922c9916cdSFrançois Tigeot }
33932c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
33942c9916cdSFrançois Tigeot
33952c9916cdSFrançois Tigeot /**
3396477eb7f9SFrançois Tigeot * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
3397477eb7f9SFrançois Tigeot * @plane: plane object
3398477eb7f9SFrançois Tigeot * @state: atomic plane state
3399477eb7f9SFrançois Tigeot *
3400477eb7f9SFrançois Tigeot * Copies atomic state from a plane's current state. This is useful for
3401477eb7f9SFrançois Tigeot * drivers that subclass the plane state.
3402477eb7f9SFrançois Tigeot */
__drm_atomic_helper_plane_duplicate_state(struct drm_plane * plane,struct drm_plane_state * state)3403477eb7f9SFrançois Tigeot void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
3404477eb7f9SFrançois Tigeot struct drm_plane_state *state)
3405477eb7f9SFrançois Tigeot {
3406477eb7f9SFrançois Tigeot memcpy(state, plane->state, sizeof(*state));
3407477eb7f9SFrançois Tigeot
3408477eb7f9SFrançois Tigeot if (state->fb)
3409a85cb24fSFrançois Tigeot drm_framebuffer_get(state->fb);
34104be47400SFrançois Tigeot
34114be47400SFrançois Tigeot state->fence = NULL;
3412*3f2dd94aSFrançois Tigeot state->commit = NULL;
3413477eb7f9SFrançois Tigeot }
3414477eb7f9SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
3415477eb7f9SFrançois Tigeot
3416477eb7f9SFrançois Tigeot /**
34172c9916cdSFrançois Tigeot * drm_atomic_helper_plane_duplicate_state - default state duplicate hook
34182c9916cdSFrançois Tigeot * @plane: drm plane
34192c9916cdSFrançois Tigeot *
34202c9916cdSFrançois Tigeot * Default plane state duplicate hook for drivers which don't have their own
34212c9916cdSFrançois Tigeot * subclassed plane state structure.
34222c9916cdSFrançois Tigeot */
34232c9916cdSFrançois Tigeot struct drm_plane_state *
drm_atomic_helper_plane_duplicate_state(struct drm_plane * plane)34242c9916cdSFrançois Tigeot drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane)
34252c9916cdSFrançois Tigeot {
34262c9916cdSFrançois Tigeot struct drm_plane_state *state;
34272c9916cdSFrançois Tigeot
34282c9916cdSFrançois Tigeot if (WARN_ON(!plane->state))
34292c9916cdSFrançois Tigeot return NULL;
34302c9916cdSFrançois Tigeot
34311e12ee3bSFrançois Tigeot state = kmalloc(sizeof(*state), M_DRM, GFP_KERNEL);
3432477eb7f9SFrançois Tigeot if (state)
3433477eb7f9SFrançois Tigeot __drm_atomic_helper_plane_duplicate_state(plane, state);
34342c9916cdSFrançois Tigeot
34352c9916cdSFrançois Tigeot return state;
34362c9916cdSFrançois Tigeot }
34372c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
34382c9916cdSFrançois Tigeot
34392c9916cdSFrançois Tigeot /**
3440477eb7f9SFrançois Tigeot * __drm_atomic_helper_plane_destroy_state - release plane state
3441477eb7f9SFrançois Tigeot * @state: plane state object to release
3442477eb7f9SFrançois Tigeot *
3443477eb7f9SFrançois Tigeot * Releases all resources stored in the plane state without actually freeing
3444477eb7f9SFrançois Tigeot * the memory of the plane state. This is useful for drivers that subclass the
3445477eb7f9SFrançois Tigeot * plane state.
3446477eb7f9SFrançois Tigeot */
__drm_atomic_helper_plane_destroy_state(struct drm_plane_state * state)34478621f407SFrançois Tigeot void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state)
3448477eb7f9SFrançois Tigeot {
3449477eb7f9SFrançois Tigeot if (state->fb)
3450a85cb24fSFrançois Tigeot drm_framebuffer_put(state->fb);
34514be47400SFrançois Tigeot
34524be47400SFrançois Tigeot if (state->fence)
34534be47400SFrançois Tigeot dma_fence_put(state->fence);
3454*3f2dd94aSFrançois Tigeot
3455*3f2dd94aSFrançois Tigeot if (state->commit)
3456*3f2dd94aSFrançois Tigeot drm_crtc_commit_put(state->commit);
3457477eb7f9SFrançois Tigeot }
3458477eb7f9SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
3459477eb7f9SFrançois Tigeot
3460477eb7f9SFrançois Tigeot /**
34612c9916cdSFrançois Tigeot * drm_atomic_helper_plane_destroy_state - default state destroy hook
34622c9916cdSFrançois Tigeot * @plane: drm plane
34632c9916cdSFrançois Tigeot * @state: plane state object to release
34642c9916cdSFrançois Tigeot *
34652c9916cdSFrançois Tigeot * Default plane state destroy hook for drivers which don't have their own
34662c9916cdSFrançois Tigeot * subclassed plane state structure.
34672c9916cdSFrançois Tigeot */
drm_atomic_helper_plane_destroy_state(struct drm_plane * plane,struct drm_plane_state * state)34682c9916cdSFrançois Tigeot void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
34692c9916cdSFrançois Tigeot struct drm_plane_state *state)
34702c9916cdSFrançois Tigeot {
34718621f407SFrançois Tigeot __drm_atomic_helper_plane_destroy_state(state);
34722c9916cdSFrançois Tigeot kfree(state);
34732c9916cdSFrançois Tigeot }
34742c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
34752c9916cdSFrançois Tigeot
34762c9916cdSFrançois Tigeot /**
3477aee94f86SFrançois Tigeot * __drm_atomic_helper_connector_reset - reset state on connector
3478aee94f86SFrançois Tigeot * @connector: drm connector
3479aee94f86SFrançois Tigeot * @conn_state: connector state to assign
3480aee94f86SFrançois Tigeot *
3481aee94f86SFrançois Tigeot * Initializes the newly allocated @conn_state and assigns it to
3482a85cb24fSFrançois Tigeot * the &drm_conector->state pointer of @connector, usually required when
3483a85cb24fSFrançois Tigeot * initializing the drivers or when called from the &drm_connector_funcs.reset
3484a85cb24fSFrançois Tigeot * hook.
3485aee94f86SFrançois Tigeot *
3486aee94f86SFrançois Tigeot * This is useful for drivers that subclass the connector state.
3487aee94f86SFrançois Tigeot */
3488aee94f86SFrançois Tigeot void
__drm_atomic_helper_connector_reset(struct drm_connector * connector,struct drm_connector_state * conn_state)3489aee94f86SFrançois Tigeot __drm_atomic_helper_connector_reset(struct drm_connector *connector,
3490aee94f86SFrançois Tigeot struct drm_connector_state *conn_state)
3491aee94f86SFrançois Tigeot {
3492aee94f86SFrançois Tigeot if (conn_state)
3493aee94f86SFrançois Tigeot conn_state->connector = connector;
3494aee94f86SFrançois Tigeot
3495aee94f86SFrançois Tigeot connector->state = conn_state;
3496aee94f86SFrançois Tigeot }
3497aee94f86SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_connector_reset);
3498aee94f86SFrançois Tigeot
3499aee94f86SFrançois Tigeot /**
3500a85cb24fSFrançois Tigeot * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors
35012c9916cdSFrançois Tigeot * @connector: drm connector
35022c9916cdSFrançois Tigeot *
35032c9916cdSFrançois Tigeot * Resets the atomic state for @connector by freeing the state pointer (which
35042c9916cdSFrançois Tigeot * might be NULL, e.g. at driver load time) and allocating a new empty state
35052c9916cdSFrançois Tigeot * object.
35062c9916cdSFrançois Tigeot */
drm_atomic_helper_connector_reset(struct drm_connector * connector)35072c9916cdSFrançois Tigeot void drm_atomic_helper_connector_reset(struct drm_connector *connector)
35082c9916cdSFrançois Tigeot {
3509aee94f86SFrançois Tigeot struct drm_connector_state *conn_state =
3510aee94f86SFrançois Tigeot kzalloc(sizeof(*conn_state), GFP_KERNEL);
35112c9916cdSFrançois Tigeot
35128621f407SFrançois Tigeot if (connector->state)
35138621f407SFrançois Tigeot __drm_atomic_helper_connector_destroy_state(connector->state);
35148621f407SFrançois Tigeot
3515aee94f86SFrançois Tigeot kfree(connector->state);
3516aee94f86SFrançois Tigeot __drm_atomic_helper_connector_reset(connector, conn_state);
35172c9916cdSFrançois Tigeot }
35182c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
35192c9916cdSFrançois Tigeot
35202c9916cdSFrançois Tigeot /**
3521477eb7f9SFrançois Tigeot * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
3522477eb7f9SFrançois Tigeot * @connector: connector object
3523477eb7f9SFrançois Tigeot * @state: atomic connector state
3524477eb7f9SFrançois Tigeot *
3525477eb7f9SFrançois Tigeot * Copies atomic state from a connector's current state. This is useful for
3526477eb7f9SFrançois Tigeot * drivers that subclass the connector state.
3527477eb7f9SFrançois Tigeot */
3528477eb7f9SFrançois Tigeot void
__drm_atomic_helper_connector_duplicate_state(struct drm_connector * connector,struct drm_connector_state * state)3529477eb7f9SFrançois Tigeot __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
3530477eb7f9SFrançois Tigeot struct drm_connector_state *state)
3531477eb7f9SFrançois Tigeot {
3532477eb7f9SFrançois Tigeot memcpy(state, connector->state, sizeof(*state));
35338621f407SFrançois Tigeot if (state->crtc)
3534a85cb24fSFrançois Tigeot drm_connector_get(connector);
3535*3f2dd94aSFrançois Tigeot state->commit = NULL;
3536477eb7f9SFrançois Tigeot }
3537477eb7f9SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
3538477eb7f9SFrançois Tigeot
3539477eb7f9SFrançois Tigeot /**
35402c9916cdSFrançois Tigeot * drm_atomic_helper_connector_duplicate_state - default state duplicate hook
35412c9916cdSFrançois Tigeot * @connector: drm connector
35422c9916cdSFrançois Tigeot *
35432c9916cdSFrançois Tigeot * Default connector state duplicate hook for drivers which don't have their own
35442c9916cdSFrançois Tigeot * subclassed connector state structure.
35452c9916cdSFrançois Tigeot */
35462c9916cdSFrançois Tigeot struct drm_connector_state *
drm_atomic_helper_connector_duplicate_state(struct drm_connector * connector)35472c9916cdSFrançois Tigeot drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
35482c9916cdSFrançois Tigeot {
3549477eb7f9SFrançois Tigeot struct drm_connector_state *state;
3550477eb7f9SFrançois Tigeot
35512c9916cdSFrançois Tigeot if (WARN_ON(!connector->state))
35522c9916cdSFrançois Tigeot return NULL;
35532c9916cdSFrançois Tigeot
35541e12ee3bSFrançois Tigeot state = kmalloc(sizeof(*state), M_DRM, GFP_KERNEL);
3555477eb7f9SFrançois Tigeot if (state)
3556477eb7f9SFrançois Tigeot __drm_atomic_helper_connector_duplicate_state(connector, state);
3557477eb7f9SFrançois Tigeot
3558477eb7f9SFrançois Tigeot return state;
35592c9916cdSFrançois Tigeot }
35602c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
35612c9916cdSFrançois Tigeot
35622c9916cdSFrançois Tigeot /**
3563352ff8bdSFrançois Tigeot * drm_atomic_helper_duplicate_state - duplicate an atomic state object
3564352ff8bdSFrançois Tigeot * @dev: DRM device
3565352ff8bdSFrançois Tigeot * @ctx: lock acquisition context
3566352ff8bdSFrançois Tigeot *
3567352ff8bdSFrançois Tigeot * Makes a copy of the current atomic state by looping over all objects and
3568aee94f86SFrançois Tigeot * duplicating their respective states. This is used for example by suspend/
3569aee94f86SFrançois Tigeot * resume support code to save the state prior to suspend such that it can
3570aee94f86SFrançois Tigeot * be restored upon resume.
3571352ff8bdSFrançois Tigeot *
3572352ff8bdSFrançois Tigeot * Note that this treats atomic state as persistent between save and restore.
3573352ff8bdSFrançois Tigeot * Drivers must make sure that this is possible and won't result in confusion
3574352ff8bdSFrançois Tigeot * or erroneous behaviour.
3575352ff8bdSFrançois Tigeot *
3576352ff8bdSFrançois Tigeot * Note that if callers haven't already acquired all modeset locks this might
3577352ff8bdSFrançois Tigeot * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
3578352ff8bdSFrançois Tigeot *
3579352ff8bdSFrançois Tigeot * Returns:
3580352ff8bdSFrançois Tigeot * A pointer to the copy of the atomic state object on success or an
3581352ff8bdSFrançois Tigeot * ERR_PTR()-encoded error code on failure.
3582aee94f86SFrançois Tigeot *
3583aee94f86SFrançois Tigeot * See also:
3584aee94f86SFrançois Tigeot * drm_atomic_helper_suspend(), drm_atomic_helper_resume()
3585352ff8bdSFrançois Tigeot */
3586352ff8bdSFrançois Tigeot struct drm_atomic_state *
drm_atomic_helper_duplicate_state(struct drm_device * dev,struct drm_modeset_acquire_ctx * ctx)3587352ff8bdSFrançois Tigeot drm_atomic_helper_duplicate_state(struct drm_device *dev,
3588352ff8bdSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
3589352ff8bdSFrançois Tigeot {
3590352ff8bdSFrançois Tigeot struct drm_atomic_state *state;
3591352ff8bdSFrançois Tigeot struct drm_connector *conn;
3592a85cb24fSFrançois Tigeot struct drm_connector_list_iter conn_iter;
3593352ff8bdSFrançois Tigeot struct drm_plane *plane;
3594352ff8bdSFrançois Tigeot struct drm_crtc *crtc;
3595352ff8bdSFrançois Tigeot int err = 0;
3596352ff8bdSFrançois Tigeot
3597352ff8bdSFrançois Tigeot state = drm_atomic_state_alloc(dev);
3598352ff8bdSFrançois Tigeot if (!state)
3599352ff8bdSFrançois Tigeot return ERR_PTR(-ENOMEM);
3600352ff8bdSFrançois Tigeot
3601352ff8bdSFrançois Tigeot state->acquire_ctx = ctx;
3602352ff8bdSFrançois Tigeot
3603352ff8bdSFrançois Tigeot drm_for_each_crtc(crtc, dev) {
3604352ff8bdSFrançois Tigeot struct drm_crtc_state *crtc_state;
3605352ff8bdSFrançois Tigeot
3606352ff8bdSFrançois Tigeot crtc_state = drm_atomic_get_crtc_state(state, crtc);
3607352ff8bdSFrançois Tigeot if (IS_ERR(crtc_state)) {
3608352ff8bdSFrançois Tigeot err = PTR_ERR(crtc_state);
3609352ff8bdSFrançois Tigeot goto free;
3610352ff8bdSFrançois Tigeot }
3611352ff8bdSFrançois Tigeot }
3612352ff8bdSFrançois Tigeot
3613352ff8bdSFrançois Tigeot drm_for_each_plane(plane, dev) {
3614352ff8bdSFrançois Tigeot struct drm_plane_state *plane_state;
3615352ff8bdSFrançois Tigeot
3616352ff8bdSFrançois Tigeot plane_state = drm_atomic_get_plane_state(state, plane);
3617352ff8bdSFrançois Tigeot if (IS_ERR(plane_state)) {
3618352ff8bdSFrançois Tigeot err = PTR_ERR(plane_state);
3619352ff8bdSFrançois Tigeot goto free;
3620352ff8bdSFrançois Tigeot }
3621352ff8bdSFrançois Tigeot }
3622352ff8bdSFrançois Tigeot
3623a85cb24fSFrançois Tigeot drm_connector_list_iter_begin(dev, &conn_iter);
3624a85cb24fSFrançois Tigeot drm_for_each_connector_iter(conn, &conn_iter) {
3625352ff8bdSFrançois Tigeot struct drm_connector_state *conn_state;
3626352ff8bdSFrançois Tigeot
3627352ff8bdSFrançois Tigeot conn_state = drm_atomic_get_connector_state(state, conn);
3628352ff8bdSFrançois Tigeot if (IS_ERR(conn_state)) {
3629352ff8bdSFrançois Tigeot err = PTR_ERR(conn_state);
3630a85cb24fSFrançois Tigeot drm_connector_list_iter_end(&conn_iter);
3631352ff8bdSFrançois Tigeot goto free;
3632352ff8bdSFrançois Tigeot }
3633352ff8bdSFrançois Tigeot }
3634a85cb24fSFrançois Tigeot drm_connector_list_iter_end(&conn_iter);
3635352ff8bdSFrançois Tigeot
3636352ff8bdSFrançois Tigeot /* clear the acquire context so that it isn't accidentally reused */
3637352ff8bdSFrançois Tigeot state->acquire_ctx = NULL;
3638352ff8bdSFrançois Tigeot
3639352ff8bdSFrançois Tigeot free:
3640352ff8bdSFrançois Tigeot if (err < 0) {
36414be47400SFrançois Tigeot drm_atomic_state_put(state);
3642352ff8bdSFrançois Tigeot state = ERR_PTR(err);
3643352ff8bdSFrançois Tigeot }
3644352ff8bdSFrançois Tigeot
3645352ff8bdSFrançois Tigeot return state;
3646352ff8bdSFrançois Tigeot }
3647352ff8bdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
3648352ff8bdSFrançois Tigeot
3649352ff8bdSFrançois Tigeot /**
3650477eb7f9SFrançois Tigeot * __drm_atomic_helper_connector_destroy_state - release connector state
3651477eb7f9SFrançois Tigeot * @state: connector state object to release
3652477eb7f9SFrançois Tigeot *
3653477eb7f9SFrançois Tigeot * Releases all resources stored in the connector state without actually
3654477eb7f9SFrançois Tigeot * freeing the memory of the connector state. This is useful for drivers that
3655477eb7f9SFrançois Tigeot * subclass the connector state.
3656477eb7f9SFrançois Tigeot */
3657477eb7f9SFrançois Tigeot void
__drm_atomic_helper_connector_destroy_state(struct drm_connector_state * state)36588621f407SFrançois Tigeot __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state)
3659477eb7f9SFrançois Tigeot {
36608621f407SFrançois Tigeot if (state->crtc)
3661a85cb24fSFrançois Tigeot drm_connector_put(state->connector);
3662*3f2dd94aSFrançois Tigeot
3663*3f2dd94aSFrançois Tigeot if (state->commit)
3664*3f2dd94aSFrançois Tigeot drm_crtc_commit_put(state->commit);
3665477eb7f9SFrançois Tigeot }
3666477eb7f9SFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
3667477eb7f9SFrançois Tigeot
3668477eb7f9SFrançois Tigeot /**
36692c9916cdSFrançois Tigeot * drm_atomic_helper_connector_destroy_state - default state destroy hook
36702c9916cdSFrançois Tigeot * @connector: drm connector
36712c9916cdSFrançois Tigeot * @state: connector state object to release
36722c9916cdSFrançois Tigeot *
36732c9916cdSFrançois Tigeot * Default connector state destroy hook for drivers which don't have their own
36742c9916cdSFrançois Tigeot * subclassed connector state structure.
36752c9916cdSFrançois Tigeot */
drm_atomic_helper_connector_destroy_state(struct drm_connector * connector,struct drm_connector_state * state)36762c9916cdSFrançois Tigeot void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
36772c9916cdSFrançois Tigeot struct drm_connector_state *state)
36782c9916cdSFrançois Tigeot {
36798621f407SFrançois Tigeot __drm_atomic_helper_connector_destroy_state(state);
36802c9916cdSFrançois Tigeot kfree(state);
36812c9916cdSFrançois Tigeot }
36822c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
3683c0e85e96SFrançois Tigeot
3684c0e85e96SFrançois Tigeot /**
3685c0e85e96SFrançois Tigeot * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table
3686c0e85e96SFrançois Tigeot * @crtc: CRTC object
3687c0e85e96SFrançois Tigeot * @red: red correction table
3688c0e85e96SFrançois Tigeot * @green: green correction table
3689c0e85e96SFrançois Tigeot * @blue: green correction table
3690c0e85e96SFrançois Tigeot * @size: size of the tables
3691a85cb24fSFrançois Tigeot * @ctx: lock acquire context
3692c0e85e96SFrançois Tigeot *
3693c0e85e96SFrançois Tigeot * Implements support for legacy gamma correction table for drivers
3694c0e85e96SFrançois Tigeot * that support color management through the DEGAMMA_LUT/GAMMA_LUT
3695*3f2dd94aSFrançois Tigeot * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
3696*3f2dd94aSFrançois Tigeot * how the atomic color management and gamma tables work.
3697c0e85e96SFrançois Tigeot */
drm_atomic_helper_legacy_gamma_set(struct drm_crtc * crtc,u16 * red,u16 * green,u16 * blue,uint32_t size,struct drm_modeset_acquire_ctx * ctx)36981dedbd3bSFrançois Tigeot int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
3699c0e85e96SFrançois Tigeot u16 *red, u16 *green, u16 *blue,
3700a85cb24fSFrançois Tigeot uint32_t size,
3701a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
3702c0e85e96SFrançois Tigeot {
3703c0e85e96SFrançois Tigeot struct drm_device *dev = crtc->dev;
3704c0e85e96SFrançois Tigeot struct drm_atomic_state *state;
3705c0e85e96SFrançois Tigeot struct drm_crtc_state *crtc_state;
3706c0e85e96SFrançois Tigeot struct drm_property_blob *blob = NULL;
3707c0e85e96SFrançois Tigeot struct drm_color_lut *blob_data;
3708c0e85e96SFrançois Tigeot int i, ret = 0;
3709*3f2dd94aSFrançois Tigeot bool replaced;
3710c0e85e96SFrançois Tigeot
3711c0e85e96SFrançois Tigeot state = drm_atomic_state_alloc(crtc->dev);
3712c0e85e96SFrançois Tigeot if (!state)
37131dedbd3bSFrançois Tigeot return -ENOMEM;
3714c0e85e96SFrançois Tigeot
3715c0e85e96SFrançois Tigeot blob = drm_property_create_blob(dev,
3716c0e85e96SFrançois Tigeot sizeof(struct drm_color_lut) * size,
3717c0e85e96SFrançois Tigeot NULL);
3718c0e85e96SFrançois Tigeot if (IS_ERR(blob)) {
3719c0e85e96SFrançois Tigeot ret = PTR_ERR(blob);
3720c0e85e96SFrançois Tigeot blob = NULL;
3721c0e85e96SFrançois Tigeot goto fail;
3722c0e85e96SFrançois Tigeot }
3723c0e85e96SFrançois Tigeot
3724c0e85e96SFrançois Tigeot /* Prepare GAMMA_LUT with the legacy values. */
3725c0e85e96SFrançois Tigeot blob_data = (struct drm_color_lut *) blob->data;
3726c0e85e96SFrançois Tigeot for (i = 0; i < size; i++) {
3727c0e85e96SFrançois Tigeot blob_data[i].red = red[i];
3728c0e85e96SFrançois Tigeot blob_data[i].green = green[i];
3729c0e85e96SFrançois Tigeot blob_data[i].blue = blue[i];
3730c0e85e96SFrançois Tigeot }
3731c0e85e96SFrançois Tigeot
3732a85cb24fSFrançois Tigeot state->acquire_ctx = ctx;
3733c0e85e96SFrançois Tigeot crtc_state = drm_atomic_get_crtc_state(state, crtc);
3734c0e85e96SFrançois Tigeot if (IS_ERR(crtc_state)) {
3735c0e85e96SFrançois Tigeot ret = PTR_ERR(crtc_state);
3736c0e85e96SFrançois Tigeot goto fail;
3737c0e85e96SFrançois Tigeot }
3738c0e85e96SFrançois Tigeot
3739c0e85e96SFrançois Tigeot /* Reset DEGAMMA_LUT and CTM properties. */
3740*3f2dd94aSFrançois Tigeot replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
3741*3f2dd94aSFrançois Tigeot replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
3742*3f2dd94aSFrançois Tigeot replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
3743*3f2dd94aSFrançois Tigeot crtc_state->color_mgmt_changed |= replaced;
3744c0e85e96SFrançois Tigeot
3745c0e85e96SFrançois Tigeot ret = drm_atomic_commit(state);
3746a85cb24fSFrançois Tigeot
3747c0e85e96SFrançois Tigeot fail:
37484be47400SFrançois Tigeot drm_atomic_state_put(state);
3749a85cb24fSFrançois Tigeot drm_property_blob_put(blob);
37501dedbd3bSFrançois Tigeot return ret;
3751c0e85e96SFrançois Tigeot }
3752c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set);
3753*3f2dd94aSFrançois Tigeot
3754*3f2dd94aSFrançois Tigeot /**
3755*3f2dd94aSFrançois Tigeot * __drm_atomic_helper_private_duplicate_state - copy atomic private state
3756*3f2dd94aSFrançois Tigeot * @obj: CRTC object
3757*3f2dd94aSFrançois Tigeot * @state: new private object state
3758*3f2dd94aSFrançois Tigeot *
3759*3f2dd94aSFrançois Tigeot * Copies atomic state from a private objects's current state and resets inferred values.
3760*3f2dd94aSFrançois Tigeot * This is useful for drivers that subclass the private state.
3761*3f2dd94aSFrançois Tigeot */
__drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj * obj,struct drm_private_state * state)3762*3f2dd94aSFrançois Tigeot void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj,
3763*3f2dd94aSFrançois Tigeot struct drm_private_state *state)
3764*3f2dd94aSFrançois Tigeot {
3765*3f2dd94aSFrançois Tigeot memcpy(state, obj->state, sizeof(*state));
3766*3f2dd94aSFrançois Tigeot }
3767*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state);
3768