xref: /dflybsd-src/sys/dev/drm/drm_atomic_helper.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
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