xref: /dflybsd-src/sys/dev/drm/drm_plane_helper.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
1ba55f2f5SFrançois Tigeot /*
2ba55f2f5SFrançois Tigeot  * Copyright (C) 2014 Intel Corporation
3ba55f2f5SFrançois Tigeot  *
4ba55f2f5SFrançois Tigeot  * DRM universal plane helper functions
5ba55f2f5SFrançois Tigeot  *
6ba55f2f5SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
7ba55f2f5SFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
8ba55f2f5SFrançois Tigeot  * to deal in the Software without restriction, including without limitation
9ba55f2f5SFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10ba55f2f5SFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
11ba55f2f5SFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
12ba55f2f5SFrançois Tigeot  *
13ba55f2f5SFrançois Tigeot  * The above copyright notice and this permission notice (including the next
14ba55f2f5SFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
15ba55f2f5SFrançois Tigeot  * Software.
16ba55f2f5SFrançois Tigeot  *
17ba55f2f5SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18ba55f2f5SFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19ba55f2f5SFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20ba55f2f5SFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21ba55f2f5SFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22ba55f2f5SFrançois Tigeot  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23ba55f2f5SFrançois Tigeot  * SOFTWARE.
24ba55f2f5SFrançois Tigeot  */
25ba55f2f5SFrançois Tigeot 
26ba55f2f5SFrançois Tigeot #include <linux/list.h>
27ba55f2f5SFrançois Tigeot #include <drm/drmP.h>
28ba55f2f5SFrançois Tigeot #include <drm/drm_plane_helper.h>
292c9916cdSFrançois Tigeot #include <drm/drm_rect.h>
302c9916cdSFrançois Tigeot #include <drm/drm_atomic.h>
312c9916cdSFrançois Tigeot #include <drm/drm_crtc_helper.h>
32a85cb24fSFrançois Tigeot #include <drm/drm_encoder.h>
332c9916cdSFrançois Tigeot #include <drm/drm_atomic_helper.h>
34ba55f2f5SFrançois Tigeot 
35ba55f2f5SFrançois Tigeot #define SUBPIXEL_MASK 0xffff
36ba55f2f5SFrançois Tigeot 
372c9916cdSFrançois Tigeot /**
382c9916cdSFrançois Tigeot  * DOC: overview
392c9916cdSFrançois Tigeot  *
402c9916cdSFrançois Tigeot  * This helper library has two parts. The first part has support to implement
412c9916cdSFrançois Tigeot  * primary plane support on top of the normal CRTC configuration interface.
42a85cb24fSFrançois Tigeot  * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary
43a85cb24fSFrançois Tigeot  * plane together with the CRTC state this does not allow userspace to disable
44a85cb24fSFrançois Tigeot  * the primary plane itself.  To avoid too much duplicated code use
452c9916cdSFrançois Tigeot  * drm_plane_helper_check_update() which can be used to enforce the same
462c9916cdSFrançois Tigeot  * restrictions as primary planes had thus. The default primary plane only
472c9916cdSFrançois Tigeot  * expose XRBG8888 and ARGB8888 as valid pixel formats for the attached
482c9916cdSFrançois Tigeot  * framebuffer.
492c9916cdSFrançois Tigeot  *
502c9916cdSFrançois Tigeot  * Drivers are highly recommended to implement proper support for primary
512c9916cdSFrançois Tigeot  * planes, and newly merged drivers must not rely upon these transitional
522c9916cdSFrançois Tigeot  * helpers.
532c9916cdSFrançois Tigeot  *
542c9916cdSFrançois Tigeot  * The second part also implements transitional helpers which allow drivers to
552c9916cdSFrançois Tigeot  * gradually switch to the atomic helper infrastructure for plane updates. Once
562c9916cdSFrançois Tigeot  * that switch is complete drivers shouldn't use these any longer, instead using
572c9916cdSFrançois Tigeot  * the proper legacy implementations for update and disable plane hooks provided
582c9916cdSFrançois Tigeot  * by the atomic helpers.
592c9916cdSFrançois Tigeot  *
602c9916cdSFrançois Tigeot  * Again drivers are strongly urged to switch to the new interfaces.
61aee94f86SFrançois Tigeot  *
62aee94f86SFrançois Tigeot  * The plane helpers share the function table structures with other helpers,
63a85cb24fSFrançois Tigeot  * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for
64aee94f86SFrançois Tigeot  * the details.
652c9916cdSFrançois Tigeot  */
662c9916cdSFrançois Tigeot 
67ba55f2f5SFrançois Tigeot /*
68ba55f2f5SFrançois Tigeot  * Returns the connectors currently associated with a CRTC.  This function
69ba55f2f5SFrançois Tigeot  * should be called twice:  once with a NULL connector list to retrieve
70ba55f2f5SFrançois Tigeot  * the list size, and once with the properly allocated list to be filled in.
71ba55f2f5SFrançois Tigeot  */
get_connectors_for_crtc(struct drm_crtc * crtc,struct drm_connector ** connector_list,int num_connectors)72ba55f2f5SFrançois Tigeot static int get_connectors_for_crtc(struct drm_crtc *crtc,
73ba55f2f5SFrançois Tigeot 				   struct drm_connector **connector_list,
74ba55f2f5SFrançois Tigeot 				   int num_connectors)
75ba55f2f5SFrançois Tigeot {
76ba55f2f5SFrançois Tigeot 	struct drm_device *dev = crtc->dev;
77ba55f2f5SFrançois Tigeot 	struct drm_connector *connector;
78a85cb24fSFrançois Tigeot 	struct drm_connector_list_iter conn_iter;
79ba55f2f5SFrançois Tigeot 	int count = 0;
80ba55f2f5SFrançois Tigeot 
81ba55f2f5SFrançois Tigeot 	/*
82ba55f2f5SFrançois Tigeot 	 * Note: Once we change the plane hooks to more fine-grained locking we
83ba55f2f5SFrançois Tigeot 	 * need to grab the connection_mutex here to be able to make these
84ba55f2f5SFrançois Tigeot 	 * checks.
85ba55f2f5SFrançois Tigeot 	 */
86ba55f2f5SFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
87ba55f2f5SFrançois Tigeot 
88a85cb24fSFrançois Tigeot 	drm_connector_list_iter_begin(dev, &conn_iter);
89a85cb24fSFrançois Tigeot 	drm_for_each_connector_iter(connector, &conn_iter) {
90ba55f2f5SFrançois Tigeot 		if (connector->encoder && connector->encoder->crtc == crtc) {
91ba55f2f5SFrançois Tigeot 			if (connector_list != NULL && count < num_connectors)
92ba55f2f5SFrançois Tigeot 				*(connector_list++) = connector;
93ba55f2f5SFrançois Tigeot 
94ba55f2f5SFrançois Tigeot 			count++;
95ba55f2f5SFrançois Tigeot 		}
96a05eeebfSFrançois Tigeot 	}
97a85cb24fSFrançois Tigeot 	drm_connector_list_iter_end(&conn_iter);
98ba55f2f5SFrançois Tigeot 
99ba55f2f5SFrançois Tigeot 	return count;
100ba55f2f5SFrançois Tigeot }
101ba55f2f5SFrançois Tigeot 
102ba55f2f5SFrançois Tigeot /**
1031dedbd3bSFrançois Tigeot  * drm_plane_helper_check_state() - Check plane state for validity
1041dedbd3bSFrançois Tigeot  * @state: plane state to check
1051dedbd3bSFrançois Tigeot  * @clip: integer clipping coordinates
1061dedbd3bSFrançois Tigeot  * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
1071dedbd3bSFrançois Tigeot  * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
1081dedbd3bSFrançois Tigeot  * @can_position: is it legal to position the plane such that it
1091dedbd3bSFrançois Tigeot  *                doesn't cover the entire crtc?  This will generally
1101dedbd3bSFrançois Tigeot  *                only be false for primary planes.
1111dedbd3bSFrançois Tigeot  * @can_update_disabled: can the plane be updated while the crtc
1121dedbd3bSFrançois Tigeot  *                       is disabled?
1131dedbd3bSFrançois Tigeot  *
1141dedbd3bSFrançois Tigeot  * Checks that a desired plane update is valid, and updates various
1151dedbd3bSFrançois Tigeot  * bits of derived state (clipped coordinates etc.). Drivers that provide
1161dedbd3bSFrançois Tigeot  * their own plane handling rather than helper-provided implementations may
1171dedbd3bSFrançois Tigeot  * still wish to call this function to avoid duplication of error checking
1181dedbd3bSFrançois Tigeot  * code.
1191dedbd3bSFrançois Tigeot  *
1201dedbd3bSFrançois Tigeot  * RETURNS:
1211dedbd3bSFrançois Tigeot  * Zero if update appears valid, error code on failure
1221dedbd3bSFrançois Tigeot  */
drm_plane_helper_check_state(struct drm_plane_state * state,const struct drm_rect * clip,int min_scale,int max_scale,bool can_position,bool can_update_disabled)1231dedbd3bSFrançois Tigeot int drm_plane_helper_check_state(struct drm_plane_state *state,
1241dedbd3bSFrançois Tigeot 				 const struct drm_rect *clip,
1251dedbd3bSFrançois Tigeot 				 int min_scale,
1261dedbd3bSFrançois Tigeot 				 int max_scale,
1271dedbd3bSFrançois Tigeot 				 bool can_position,
1281dedbd3bSFrançois Tigeot 				 bool can_update_disabled)
1291dedbd3bSFrançois Tigeot {
1301dedbd3bSFrançois Tigeot 	struct drm_crtc *crtc = state->crtc;
1311dedbd3bSFrançois Tigeot 	struct drm_framebuffer *fb = state->fb;
1321dedbd3bSFrançois Tigeot 	struct drm_rect *src = &state->src;
1331dedbd3bSFrançois Tigeot 	struct drm_rect *dst = &state->dst;
1341dedbd3bSFrançois Tigeot 	unsigned int rotation = state->rotation;
1351dedbd3bSFrançois Tigeot 	int hscale, vscale;
1361dedbd3bSFrançois Tigeot 
1374be47400SFrançois Tigeot 	*src = drm_plane_state_src(state);
1384be47400SFrançois Tigeot 	*dst = drm_plane_state_dest(state);
1391dedbd3bSFrançois Tigeot 
1401dedbd3bSFrançois Tigeot 	if (!fb) {
1411dedbd3bSFrançois Tigeot 		state->visible = false;
1421dedbd3bSFrançois Tigeot 		return 0;
1431dedbd3bSFrançois Tigeot 	}
1441dedbd3bSFrançois Tigeot 
1451dedbd3bSFrançois Tigeot 	/* crtc should only be NULL when disabling (i.e., !fb) */
1461dedbd3bSFrançois Tigeot 	if (WARN_ON(!crtc)) {
1471dedbd3bSFrançois Tigeot 		state->visible = false;
1481dedbd3bSFrançois Tigeot 		return 0;
1491dedbd3bSFrançois Tigeot 	}
1501dedbd3bSFrançois Tigeot 
1511dedbd3bSFrançois Tigeot 	if (!crtc->enabled && !can_update_disabled) {
1521dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n");
1531dedbd3bSFrançois Tigeot 		return -EINVAL;
1541dedbd3bSFrançois Tigeot 	}
1551dedbd3bSFrançois Tigeot 
1561dedbd3bSFrançois Tigeot 	drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
1571dedbd3bSFrançois Tigeot 
1581dedbd3bSFrançois Tigeot 	/* Check scaling */
1591dedbd3bSFrançois Tigeot 	hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale);
1601dedbd3bSFrançois Tigeot 	vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale);
1611dedbd3bSFrançois Tigeot 	if (hscale < 0 || vscale < 0) {
1621dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid scaling of plane\n");
1631dedbd3bSFrançois Tigeot 		drm_rect_debug_print("src: ", &state->src, true);
1641dedbd3bSFrançois Tigeot 		drm_rect_debug_print("dst: ", &state->dst, false);
1651dedbd3bSFrançois Tigeot 		return -ERANGE;
1661dedbd3bSFrançois Tigeot 	}
1671dedbd3bSFrançois Tigeot 
1681dedbd3bSFrançois Tigeot 	state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
1691dedbd3bSFrançois Tigeot 
1701dedbd3bSFrançois Tigeot 	drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
1711dedbd3bSFrançois Tigeot 
1721dedbd3bSFrançois Tigeot 	if (!state->visible)
1731dedbd3bSFrançois Tigeot 		/*
1741dedbd3bSFrançois Tigeot 		 * Plane isn't visible; some drivers can handle this
1751dedbd3bSFrançois Tigeot 		 * so we just return success here.  Drivers that can't
1761dedbd3bSFrançois Tigeot 		 * (including those that use the primary plane helper's
1771dedbd3bSFrançois Tigeot 		 * update function) will return an error from their
1781dedbd3bSFrançois Tigeot 		 * update_plane handler.
1791dedbd3bSFrançois Tigeot 		 */
1801dedbd3bSFrançois Tigeot 		return 0;
1811dedbd3bSFrançois Tigeot 
1821dedbd3bSFrançois Tigeot 	if (!can_position && !drm_rect_equals(dst, clip)) {
1831dedbd3bSFrançois Tigeot 		DRM_DEBUG_KMS("Plane must cover entire CRTC\n");
1841dedbd3bSFrançois Tigeot 		drm_rect_debug_print("dst: ", dst, false);
1851dedbd3bSFrançois Tigeot 		drm_rect_debug_print("clip: ", clip, false);
1861dedbd3bSFrançois Tigeot 		return -EINVAL;
1871dedbd3bSFrançois Tigeot 	}
1881dedbd3bSFrançois Tigeot 
1891dedbd3bSFrançois Tigeot 	return 0;
1901dedbd3bSFrançois Tigeot }
1911dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_plane_helper_check_state);
1921dedbd3bSFrançois Tigeot 
1931dedbd3bSFrançois Tigeot /**
19424edb884SFrançois Tigeot  * drm_plane_helper_check_update() - Check plane update for validity
19524edb884SFrançois Tigeot  * @plane: plane object to update
19624edb884SFrançois Tigeot  * @crtc: owning CRTC of owning plane
19724edb884SFrançois Tigeot  * @fb: framebuffer to flip onto plane
19824edb884SFrançois Tigeot  * @src: source coordinates in 16.16 fixed point
1991dedbd3bSFrançois Tigeot  * @dst: integer destination coordinates
20024edb884SFrançois Tigeot  * @clip: integer clipping coordinates
2011dedbd3bSFrançois Tigeot  * @rotation: plane rotation
20224edb884SFrançois Tigeot  * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
20324edb884SFrançois Tigeot  * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
20424edb884SFrançois Tigeot  * @can_position: is it legal to position the plane such that it
20524edb884SFrançois Tigeot  *                doesn't cover the entire crtc?  This will generally
20624edb884SFrançois Tigeot  *                only be false for primary planes.
20724edb884SFrançois Tigeot  * @can_update_disabled: can the plane be updated while the crtc
20824edb884SFrançois Tigeot  *                       is disabled?
20924edb884SFrançois Tigeot  * @visible: output parameter indicating whether plane is still visible after
21024edb884SFrançois Tigeot  *           clipping
21124edb884SFrançois Tigeot  *
21224edb884SFrançois Tigeot  * Checks that a desired plane update is valid.  Drivers that provide
21324edb884SFrançois Tigeot  * their own plane handling rather than helper-provided implementations may
21424edb884SFrançois Tigeot  * still wish to call this function to avoid duplication of error checking
21524edb884SFrançois Tigeot  * code.
21624edb884SFrançois Tigeot  *
21724edb884SFrançois Tigeot  * RETURNS:
21824edb884SFrançois Tigeot  * Zero if update appears valid, error code on failure
21924edb884SFrançois Tigeot  */
drm_plane_helper_check_update(struct drm_plane * plane,struct drm_crtc * crtc,struct drm_framebuffer * fb,struct drm_rect * src,struct drm_rect * dst,const struct drm_rect * clip,unsigned int rotation,int min_scale,int max_scale,bool can_position,bool can_update_disabled,bool * visible)22024edb884SFrançois Tigeot int drm_plane_helper_check_update(struct drm_plane *plane,
22124edb884SFrançois Tigeot 				  struct drm_crtc *crtc,
22224edb884SFrançois Tigeot 				  struct drm_framebuffer *fb,
22324edb884SFrançois Tigeot 				  struct drm_rect *src,
2241dedbd3bSFrançois Tigeot 				  struct drm_rect *dst,
22524edb884SFrançois Tigeot 				  const struct drm_rect *clip,
2261dedbd3bSFrançois Tigeot 				  unsigned int rotation,
22724edb884SFrançois Tigeot 				  int min_scale,
22824edb884SFrançois Tigeot 				  int max_scale,
22924edb884SFrançois Tigeot 				  bool can_position,
23024edb884SFrançois Tigeot 				  bool can_update_disabled,
23124edb884SFrançois Tigeot 				  bool *visible)
23224edb884SFrançois Tigeot {
2331dedbd3bSFrançois Tigeot 	struct drm_plane_state state = {
2341dedbd3bSFrançois Tigeot 		.plane = plane,
2351dedbd3bSFrançois Tigeot 		.crtc = crtc,
2361dedbd3bSFrançois Tigeot 		.fb = fb,
2371dedbd3bSFrançois Tigeot 		.src_x = src->x1,
2381dedbd3bSFrançois Tigeot 		.src_y = src->y1,
2391dedbd3bSFrançois Tigeot 		.src_w = drm_rect_width(src),
2401dedbd3bSFrançois Tigeot 		.src_h = drm_rect_height(src),
2411dedbd3bSFrançois Tigeot 		.crtc_x = dst->x1,
2421dedbd3bSFrançois Tigeot 		.crtc_y = dst->y1,
2431dedbd3bSFrançois Tigeot 		.crtc_w = drm_rect_width(dst),
2441dedbd3bSFrançois Tigeot 		.crtc_h = drm_rect_height(dst),
2451dedbd3bSFrançois Tigeot 		.rotation = rotation,
2461dedbd3bSFrançois Tigeot 		.visible = *visible,
2471dedbd3bSFrançois Tigeot 	};
2481dedbd3bSFrançois Tigeot 	int ret;
24924edb884SFrançois Tigeot 
2501dedbd3bSFrançois Tigeot 	ret = drm_plane_helper_check_state(&state, clip,
2511dedbd3bSFrançois Tigeot 					   min_scale, max_scale,
2521dedbd3bSFrançois Tigeot 					   can_position,
2531dedbd3bSFrançois Tigeot 					   can_update_disabled);
2541dedbd3bSFrançois Tigeot 	if (ret)
2551dedbd3bSFrançois Tigeot 		return ret;
2562c9916cdSFrançois Tigeot 
2571dedbd3bSFrançois Tigeot 	*src = state.src;
2581dedbd3bSFrançois Tigeot 	*dst = state.dst;
2591dedbd3bSFrançois Tigeot 	*visible = state.visible;
26024edb884SFrançois Tigeot 
26124edb884SFrançois Tigeot 	return 0;
26224edb884SFrançois Tigeot }
26324edb884SFrançois Tigeot EXPORT_SYMBOL(drm_plane_helper_check_update);
26424edb884SFrançois Tigeot 
26524edb884SFrançois Tigeot /**
266ba55f2f5SFrançois Tigeot  * drm_primary_helper_update() - Helper for primary plane update
267ba55f2f5SFrançois Tigeot  * @plane: plane object to update
268ba55f2f5SFrançois Tigeot  * @crtc: owning CRTC of owning plane
269ba55f2f5SFrançois Tigeot  * @fb: framebuffer to flip onto plane
270ba55f2f5SFrançois Tigeot  * @crtc_x: x offset of primary plane on crtc
271ba55f2f5SFrançois Tigeot  * @crtc_y: y offset of primary plane on crtc
272ba55f2f5SFrançois Tigeot  * @crtc_w: width of primary plane rectangle on crtc
273ba55f2f5SFrançois Tigeot  * @crtc_h: height of primary plane rectangle on crtc
274ba55f2f5SFrançois Tigeot  * @src_x: x offset of @fb for panning
275ba55f2f5SFrançois Tigeot  * @src_y: y offset of @fb for panning
276ba55f2f5SFrançois Tigeot  * @src_w: width of source rectangle in @fb
277ba55f2f5SFrançois Tigeot  * @src_h: height of source rectangle in @fb
278a85cb24fSFrançois Tigeot  * @ctx: lock acquire context, not used here
279ba55f2f5SFrançois Tigeot  *
280ba55f2f5SFrançois Tigeot  * Provides a default plane update handler for primary planes.  This is handler
281ba55f2f5SFrançois Tigeot  * is called in response to a userspace SetPlane operation on the plane with a
282ba55f2f5SFrançois Tigeot  * non-NULL framebuffer.  We call the driver's modeset handler to update the
283ba55f2f5SFrançois Tigeot  * framebuffer.
284ba55f2f5SFrançois Tigeot  *
285ba55f2f5SFrançois Tigeot  * SetPlane() on a primary plane of a disabled CRTC is not supported, and will
286ba55f2f5SFrançois Tigeot  * return an error.
287ba55f2f5SFrançois Tigeot  *
288ba55f2f5SFrançois Tigeot  * Note that we make some assumptions about hardware limitations that may not be
289ba55f2f5SFrançois Tigeot  * true for all hardware --
2901dedbd3bSFrançois Tigeot  *
2911dedbd3bSFrançois Tigeot  * 1. Primary plane cannot be repositioned.
2921dedbd3bSFrançois Tigeot  * 2. Primary plane cannot be scaled.
2931dedbd3bSFrançois Tigeot  * 3. Primary plane must cover the entire CRTC.
2941dedbd3bSFrançois Tigeot  * 4. Subpixel positioning is not supported.
2951dedbd3bSFrançois Tigeot  *
296ba55f2f5SFrançois Tigeot  * Drivers for hardware that don't have these restrictions can provide their
297ba55f2f5SFrançois Tigeot  * own implementation rather than using this helper.
298ba55f2f5SFrançois Tigeot  *
299ba55f2f5SFrançois Tigeot  * RETURNS:
300ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure
301ba55f2f5SFrançois Tigeot  */
drm_primary_helper_update(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)302ba55f2f5SFrançois Tigeot int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
303ba55f2f5SFrançois Tigeot 			      struct drm_framebuffer *fb,
304ba55f2f5SFrançois Tigeot 			      int crtc_x, int crtc_y,
305ba55f2f5SFrançois Tigeot 			      unsigned int crtc_w, unsigned int crtc_h,
306ba55f2f5SFrançois Tigeot 			      uint32_t src_x, uint32_t src_y,
307a85cb24fSFrançois Tigeot 			      uint32_t src_w, uint32_t src_h,
308a85cb24fSFrançois Tigeot 			      struct drm_modeset_acquire_ctx *ctx)
309ba55f2f5SFrançois Tigeot {
310ba55f2f5SFrançois Tigeot 	struct drm_mode_set set = {
311ba55f2f5SFrançois Tigeot 		.crtc = crtc,
312ba55f2f5SFrançois Tigeot 		.fb = fb,
313ba55f2f5SFrançois Tigeot 		.mode = &crtc->mode,
314ba55f2f5SFrançois Tigeot 		.x = src_x >> 16,
315ba55f2f5SFrançois Tigeot 		.y = src_y >> 16,
316ba55f2f5SFrançois Tigeot 	};
3172c9916cdSFrançois Tigeot 	struct drm_rect src = {
3182c9916cdSFrançois Tigeot 		.x1 = src_x,
3192c9916cdSFrançois Tigeot 		.y1 = src_y,
3202c9916cdSFrançois Tigeot 		.x2 = src_x + src_w,
3212c9916cdSFrançois Tigeot 		.y2 = src_y + src_h,
3222c9916cdSFrançois Tigeot 	};
323ba55f2f5SFrançois Tigeot 	struct drm_rect dest = {
324ba55f2f5SFrançois Tigeot 		.x1 = crtc_x,
325ba55f2f5SFrançois Tigeot 		.y1 = crtc_y,
326ba55f2f5SFrançois Tigeot 		.x2 = crtc_x + crtc_w,
327ba55f2f5SFrançois Tigeot 		.y2 = crtc_y + crtc_h,
328ba55f2f5SFrançois Tigeot 	};
3292c9916cdSFrançois Tigeot 	const struct drm_rect clip = {
330ba55f2f5SFrançois Tigeot 		.x2 = crtc->mode.hdisplay,
331ba55f2f5SFrançois Tigeot 		.y2 = crtc->mode.vdisplay,
332ba55f2f5SFrançois Tigeot 	};
333ba55f2f5SFrançois Tigeot 	struct drm_connector **connector_list;
334ba55f2f5SFrançois Tigeot 	int num_connectors, ret;
3352c9916cdSFrançois Tigeot 	bool visible;
336ba55f2f5SFrançois Tigeot 
3372c9916cdSFrançois Tigeot 	ret = drm_plane_helper_check_update(plane, crtc, fb,
3382c9916cdSFrançois Tigeot 					    &src, &dest, &clip,
339*3f2dd94aSFrançois Tigeot 					    DRM_MODE_ROTATE_0,
3402c9916cdSFrançois Tigeot 					    DRM_PLANE_HELPER_NO_SCALING,
3412c9916cdSFrançois Tigeot 					    DRM_PLANE_HELPER_NO_SCALING,
3422c9916cdSFrançois Tigeot 					    false, false, &visible);
343ba55f2f5SFrançois Tigeot 	if (ret)
344ba55f2f5SFrançois Tigeot 		return ret;
345ba55f2f5SFrançois Tigeot 
3462c9916cdSFrançois Tigeot 	if (!visible)
3472c9916cdSFrançois Tigeot 		/*
3482c9916cdSFrançois Tigeot 		 * Primary plane isn't visible.  Note that unless a driver
3492c9916cdSFrançois Tigeot 		 * provides their own disable function, this will just
3502c9916cdSFrançois Tigeot 		 * wind up returning -EINVAL to userspace.
3512c9916cdSFrançois Tigeot 		 */
352a85cb24fSFrançois Tigeot 		return plane->funcs->disable_plane(plane, ctx);
3532c9916cdSFrançois Tigeot 
354ba55f2f5SFrançois Tigeot 	/* Find current connectors for CRTC */
355ba55f2f5SFrançois Tigeot 	num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
356ba55f2f5SFrançois Tigeot 	BUG_ON(num_connectors == 0);
357*3f2dd94aSFrançois Tigeot 	connector_list = kcalloc(num_connectors, sizeof(*connector_list),
358ba55f2f5SFrançois Tigeot 				 GFP_KERNEL);
359ba55f2f5SFrançois Tigeot 	if (!connector_list)
360ba55f2f5SFrançois Tigeot 		return -ENOMEM;
361ba55f2f5SFrançois Tigeot 	get_connectors_for_crtc(crtc, connector_list, num_connectors);
362ba55f2f5SFrançois Tigeot 
363ba55f2f5SFrançois Tigeot 	set.connectors = connector_list;
364ba55f2f5SFrançois Tigeot 	set.num_connectors = num_connectors;
365ba55f2f5SFrançois Tigeot 
366ba55f2f5SFrançois Tigeot 	/*
367ba55f2f5SFrançois Tigeot 	 * We call set_config() directly here rather than using
368ba55f2f5SFrançois Tigeot 	 * drm_mode_set_config_internal.  We're reprogramming the same
369ba55f2f5SFrançois Tigeot 	 * connectors that were already in use, so we shouldn't need the extra
370ba55f2f5SFrançois Tigeot 	 * cross-CRTC fb refcounting to accomodate stealing connectors.
371ba55f2f5SFrançois Tigeot 	 * drm_mode_setplane() already handles the basic refcounting for the
372ba55f2f5SFrançois Tigeot 	 * framebuffers involved in this operation.
373ba55f2f5SFrançois Tigeot 	 */
374a85cb24fSFrançois Tigeot 	ret = crtc->funcs->set_config(&set, ctx);
375ba55f2f5SFrançois Tigeot 
376ba55f2f5SFrançois Tigeot 	kfree(connector_list);
377ba55f2f5SFrançois Tigeot 	return ret;
378ba55f2f5SFrançois Tigeot }
379ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_primary_helper_update);
380ba55f2f5SFrançois Tigeot 
381ba55f2f5SFrançois Tigeot /**
382ba55f2f5SFrançois Tigeot  * drm_primary_helper_disable() - Helper for primary plane disable
383ba55f2f5SFrançois Tigeot  * @plane: plane to disable
384*3f2dd94aSFrançois Tigeot  * @ctx: lock acquire context, not used here
385ba55f2f5SFrançois Tigeot  *
386ba55f2f5SFrançois Tigeot  * Provides a default plane disable handler for primary planes.  This is handler
387ba55f2f5SFrançois Tigeot  * is called in response to a userspace SetPlane operation on the plane with a
3882c9916cdSFrançois Tigeot  * NULL framebuffer parameter.  It unconditionally fails the disable call with
3892c9916cdSFrançois Tigeot  * -EINVAL the only way to disable the primary plane without driver support is
390a85cb24fSFrançois Tigeot  * to disable the entire CRTC. Which does not match the plane
391a85cb24fSFrançois Tigeot  * &drm_plane_funcs.disable_plane hook.
392ba55f2f5SFrançois Tigeot  *
393ba55f2f5SFrançois Tigeot  * Note that some hardware may be able to disable the primary plane without
394ba55f2f5SFrançois Tigeot  * disabling the whole CRTC.  Drivers for such hardware should provide their
395ba55f2f5SFrançois Tigeot  * own disable handler that disables just the primary plane (and they'll likely
396ba55f2f5SFrançois Tigeot  * need to provide their own update handler as well to properly re-enable a
397ba55f2f5SFrançois Tigeot  * disabled primary plane).
398ba55f2f5SFrançois Tigeot  *
399ba55f2f5SFrançois Tigeot  * RETURNS:
4002c9916cdSFrançois Tigeot  * Unconditionally returns -EINVAL.
401ba55f2f5SFrançois Tigeot  */
drm_primary_helper_disable(struct drm_plane * plane,struct drm_modeset_acquire_ctx * ctx)402a85cb24fSFrançois Tigeot int drm_primary_helper_disable(struct drm_plane *plane,
403a85cb24fSFrançois Tigeot 			       struct drm_modeset_acquire_ctx *ctx)
404ba55f2f5SFrançois Tigeot {
4052c9916cdSFrançois Tigeot 	return -EINVAL;
406ba55f2f5SFrançois Tigeot }
407ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_primary_helper_disable);
408ba55f2f5SFrançois Tigeot 
409ba55f2f5SFrançois Tigeot /**
410ba55f2f5SFrançois Tigeot  * drm_primary_helper_destroy() - Helper for primary plane destruction
411ba55f2f5SFrançois Tigeot  * @plane: plane to destroy
412ba55f2f5SFrançois Tigeot  *
413ba55f2f5SFrançois Tigeot  * Provides a default plane destroy handler for primary planes.  This handler
414ba55f2f5SFrançois Tigeot  * is called during CRTC destruction.  We disable the primary plane, remove
415ba55f2f5SFrançois Tigeot  * it from the DRM plane list, and deallocate the plane structure.
416ba55f2f5SFrançois Tigeot  */
drm_primary_helper_destroy(struct drm_plane * plane)417ba55f2f5SFrançois Tigeot void drm_primary_helper_destroy(struct drm_plane *plane)
418ba55f2f5SFrançois Tigeot {
419ba55f2f5SFrançois Tigeot 	drm_plane_cleanup(plane);
420ba55f2f5SFrançois Tigeot 	kfree(plane);
421ba55f2f5SFrançois Tigeot }
422ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_primary_helper_destroy);
423ba55f2f5SFrançois Tigeot 
424ba55f2f5SFrançois Tigeot const struct drm_plane_funcs drm_primary_helper_funcs = {
425ba55f2f5SFrançois Tigeot 	.update_plane = drm_primary_helper_update,
426ba55f2f5SFrançois Tigeot 	.disable_plane = drm_primary_helper_disable,
427ba55f2f5SFrançois Tigeot 	.destroy = drm_primary_helper_destroy,
428ba55f2f5SFrançois Tigeot };
429ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_primary_helper_funcs);
430ba55f2f5SFrançois Tigeot 
drm_plane_helper_commit(struct drm_plane * plane,struct drm_plane_state * plane_state,struct drm_framebuffer * old_fb)4312c9916cdSFrançois Tigeot int drm_plane_helper_commit(struct drm_plane *plane,
4322c9916cdSFrançois Tigeot 			    struct drm_plane_state *plane_state,
4332c9916cdSFrançois Tigeot 			    struct drm_framebuffer *old_fb)
4342c9916cdSFrançois Tigeot {
435477eb7f9SFrançois Tigeot 	const struct drm_plane_helper_funcs *plane_funcs;
4362c9916cdSFrançois Tigeot 	struct drm_crtc *crtc[2];
437477eb7f9SFrançois Tigeot 	const struct drm_crtc_helper_funcs *crtc_funcs[2];
4382c9916cdSFrançois Tigeot 	int i, ret = 0;
4392c9916cdSFrançois Tigeot 
4402c9916cdSFrançois Tigeot 	plane_funcs = plane->helper_private;
4412c9916cdSFrançois Tigeot 
4422c9916cdSFrançois Tigeot 	/* Since this is a transitional helper we can't assume that plane->state
4432c9916cdSFrançois Tigeot 	 * is always valid. Hence we need to use plane->crtc instead of
4442c9916cdSFrançois Tigeot 	 * plane->state->crtc as the old crtc. */
4452c9916cdSFrançois Tigeot 	crtc[0] = plane->crtc;
4462c9916cdSFrançois Tigeot 	crtc[1] = crtc[0] != plane_state->crtc ? plane_state->crtc : NULL;
4472c9916cdSFrançois Tigeot 
4482c9916cdSFrançois Tigeot 	for (i = 0; i < 2; i++)
4492c9916cdSFrançois Tigeot 		crtc_funcs[i] = crtc[i] ? crtc[i]->helper_private : NULL;
4502c9916cdSFrançois Tigeot 
4512c9916cdSFrançois Tigeot 	if (plane_funcs->atomic_check) {
4522c9916cdSFrançois Tigeot 		ret = plane_funcs->atomic_check(plane, plane_state);
4532c9916cdSFrançois Tigeot 		if (ret)
4542c9916cdSFrançois Tigeot 			goto out;
4552c9916cdSFrançois Tigeot 	}
4562c9916cdSFrançois Tigeot 
457a85cb24fSFrançois Tigeot 	if (plane_funcs->prepare_fb && plane_state->fb != old_fb) {
458352ff8bdSFrançois Tigeot 		ret = plane_funcs->prepare_fb(plane,
459477eb7f9SFrançois Tigeot 					      plane_state);
4602c9916cdSFrançois Tigeot 		if (ret)
4612c9916cdSFrançois Tigeot 			goto out;
4622c9916cdSFrançois Tigeot 	}
4632c9916cdSFrançois Tigeot 
4642c9916cdSFrançois Tigeot 	/* Point of no return, commit sw state. */
4652c9916cdSFrançois Tigeot 	swap(plane->state, plane_state);
4662c9916cdSFrançois Tigeot 
4672c9916cdSFrançois Tigeot 	for (i = 0; i < 2; i++) {
4682c9916cdSFrançois Tigeot 		if (crtc_funcs[i] && crtc_funcs[i]->atomic_begin)
469a05eeebfSFrançois Tigeot 			crtc_funcs[i]->atomic_begin(crtc[i], crtc[i]->state);
4702c9916cdSFrançois Tigeot 	}
4712c9916cdSFrançois Tigeot 
472477eb7f9SFrançois Tigeot 	/*
473477eb7f9SFrançois Tigeot 	 * Drivers may optionally implement the ->atomic_disable callback, so
474477eb7f9SFrançois Tigeot 	 * special-case that here.
475477eb7f9SFrançois Tigeot 	 */
476a85cb24fSFrançois Tigeot 	if (drm_atomic_plane_disabling(plane_state, plane->state) &&
477477eb7f9SFrançois Tigeot 	    plane_funcs->atomic_disable)
478477eb7f9SFrançois Tigeot 		plane_funcs->atomic_disable(plane, plane_state);
479477eb7f9SFrançois Tigeot 	else
4802c9916cdSFrançois Tigeot 		plane_funcs->atomic_update(plane, plane_state);
4812c9916cdSFrançois Tigeot 
4822c9916cdSFrançois Tigeot 	for (i = 0; i < 2; i++) {
4832c9916cdSFrançois Tigeot 		if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush)
484a05eeebfSFrançois Tigeot 			crtc_funcs[i]->atomic_flush(crtc[i], crtc[i]->state);
4852c9916cdSFrançois Tigeot 	}
4862c9916cdSFrançois Tigeot 
487477eb7f9SFrançois Tigeot 	/*
488477eb7f9SFrançois Tigeot 	 * If we only moved the plane and didn't change fb's, there's no need to
489477eb7f9SFrançois Tigeot 	 * wait for vblank.
490477eb7f9SFrançois Tigeot 	 */
491477eb7f9SFrançois Tigeot 	if (plane->state->fb == old_fb)
492477eb7f9SFrançois Tigeot 		goto out;
493477eb7f9SFrançois Tigeot 
4942c9916cdSFrançois Tigeot 	for (i = 0; i < 2; i++) {
4952c9916cdSFrançois Tigeot 		if (!crtc[i])
4962c9916cdSFrançois Tigeot 			continue;
4972c9916cdSFrançois Tigeot 
498477eb7f9SFrançois Tigeot 		if (crtc[i]->cursor == plane)
499477eb7f9SFrançois Tigeot 			continue;
500477eb7f9SFrançois Tigeot 
501477eb7f9SFrançois Tigeot 		/* There's no other way to figure out whether the crtc is running. */
5022c9916cdSFrançois Tigeot 		ret = drm_crtc_vblank_get(crtc[i]);
5032c9916cdSFrançois Tigeot 		if (ret == 0) {
5042c9916cdSFrançois Tigeot 			drm_crtc_wait_one_vblank(crtc[i]);
5052c9916cdSFrançois Tigeot 			drm_crtc_vblank_put(crtc[i]);
5062c9916cdSFrançois Tigeot 		}
5072c9916cdSFrançois Tigeot 
5082c9916cdSFrançois Tigeot 		ret = 0;
5092c9916cdSFrançois Tigeot 	}
5102c9916cdSFrançois Tigeot 
511352ff8bdSFrançois Tigeot 	if (plane_funcs->cleanup_fb)
512352ff8bdSFrançois Tigeot 		plane_funcs->cleanup_fb(plane, plane_state);
5132c9916cdSFrançois Tigeot out:
5142c9916cdSFrançois Tigeot 	if (plane->funcs->atomic_destroy_state)
5152c9916cdSFrançois Tigeot 		plane->funcs->atomic_destroy_state(plane, plane_state);
5162c9916cdSFrançois Tigeot 	else
5172c9916cdSFrançois Tigeot 		drm_atomic_helper_plane_destroy_state(plane, plane_state);
5182c9916cdSFrançois Tigeot 
5192c9916cdSFrançois Tigeot 	return ret;
5202c9916cdSFrançois Tigeot }
5212c9916cdSFrançois Tigeot 
5222c9916cdSFrançois Tigeot /**
5232c9916cdSFrançois Tigeot  * drm_plane_helper_update() - Transitional helper for plane update
5242c9916cdSFrançois Tigeot  * @plane: plane object to update
5252c9916cdSFrançois Tigeot  * @crtc: owning CRTC of owning plane
5262c9916cdSFrançois Tigeot  * @fb: framebuffer to flip onto plane
5272c9916cdSFrançois Tigeot  * @crtc_x: x offset of primary plane on crtc
5282c9916cdSFrançois Tigeot  * @crtc_y: y offset of primary plane on crtc
5292c9916cdSFrançois Tigeot  * @crtc_w: width of primary plane rectangle on crtc
5302c9916cdSFrançois Tigeot  * @crtc_h: height of primary plane rectangle on crtc
5312c9916cdSFrançois Tigeot  * @src_x: x offset of @fb for panning
5322c9916cdSFrançois Tigeot  * @src_y: y offset of @fb for panning
5332c9916cdSFrançois Tigeot  * @src_w: width of source rectangle in @fb
5342c9916cdSFrançois Tigeot  * @src_h: height of source rectangle in @fb
5352c9916cdSFrançois Tigeot  *
5362c9916cdSFrançois Tigeot  * Provides a default plane update handler using the atomic plane update
5372c9916cdSFrançois Tigeot  * functions. It is fully left to the driver to check plane constraints and
5382c9916cdSFrançois Tigeot  * handle corner-cases like a fully occluded or otherwise invisible plane.
5392c9916cdSFrançois Tigeot  *
5402c9916cdSFrançois Tigeot  * This is useful for piecewise transitioning of a driver to the atomic helpers.
5412c9916cdSFrançois Tigeot  *
5422c9916cdSFrançois Tigeot  * RETURNS:
5432c9916cdSFrançois Tigeot  * Zero on success, error code on failure
5442c9916cdSFrançois Tigeot  */
drm_plane_helper_update(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)5452c9916cdSFrançois Tigeot int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
5462c9916cdSFrançois Tigeot 			    struct drm_framebuffer *fb,
5472c9916cdSFrançois Tigeot 			    int crtc_x, int crtc_y,
5482c9916cdSFrançois Tigeot 			    unsigned int crtc_w, unsigned int crtc_h,
5492c9916cdSFrançois Tigeot 			    uint32_t src_x, uint32_t src_y,
5502c9916cdSFrançois Tigeot 			    uint32_t src_w, uint32_t src_h)
5512c9916cdSFrançois Tigeot {
5522c9916cdSFrançois Tigeot 	struct drm_plane_state *plane_state;
5532c9916cdSFrançois Tigeot 
5542c9916cdSFrançois Tigeot 	if (plane->funcs->atomic_duplicate_state)
5552c9916cdSFrançois Tigeot 		plane_state = plane->funcs->atomic_duplicate_state(plane);
556a05eeebfSFrançois Tigeot 	else {
557a05eeebfSFrançois Tigeot 		if (!plane->state)
558a05eeebfSFrançois Tigeot 			drm_atomic_helper_plane_reset(plane);
559a05eeebfSFrançois Tigeot 
5602c9916cdSFrançois Tigeot 		plane_state = drm_atomic_helper_plane_duplicate_state(plane);
561a05eeebfSFrançois Tigeot 	}
5622c9916cdSFrançois Tigeot 	if (!plane_state)
5632c9916cdSFrançois Tigeot 		return -ENOMEM;
5642c9916cdSFrançois Tigeot 	plane_state->plane = plane;
5652c9916cdSFrançois Tigeot 
5662c9916cdSFrançois Tigeot 	plane_state->crtc = crtc;
5672c9916cdSFrançois Tigeot 	drm_atomic_set_fb_for_plane(plane_state, fb);
5682c9916cdSFrançois Tigeot 	plane_state->crtc_x = crtc_x;
5692c9916cdSFrançois Tigeot 	plane_state->crtc_y = crtc_y;
5702c9916cdSFrançois Tigeot 	plane_state->crtc_h = crtc_h;
5712c9916cdSFrançois Tigeot 	plane_state->crtc_w = crtc_w;
5722c9916cdSFrançois Tigeot 	plane_state->src_x = src_x;
5732c9916cdSFrançois Tigeot 	plane_state->src_y = src_y;
5742c9916cdSFrançois Tigeot 	plane_state->src_h = src_h;
5752c9916cdSFrançois Tigeot 	plane_state->src_w = src_w;
5762c9916cdSFrançois Tigeot 
5772c9916cdSFrançois Tigeot 	return drm_plane_helper_commit(plane, plane_state, plane->fb);
5782c9916cdSFrançois Tigeot }
5792c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_plane_helper_update);
5802c9916cdSFrançois Tigeot 
5812c9916cdSFrançois Tigeot /**
58219c468b4SFrançois Tigeot  * drm_plane_helper_disable() - Transitional helper for plane disable
5832c9916cdSFrançois Tigeot  * @plane: plane to disable
5842c9916cdSFrançois Tigeot  *
5852c9916cdSFrançois Tigeot  * Provides a default plane disable handler using the atomic plane update
5862c9916cdSFrançois Tigeot  * functions. It is fully left to the driver to check plane constraints and
5872c9916cdSFrançois Tigeot  * handle corner-cases like a fully occluded or otherwise invisible plane.
5882c9916cdSFrançois Tigeot  *
5892c9916cdSFrançois Tigeot  * This is useful for piecewise transitioning of a driver to the atomic helpers.
5902c9916cdSFrançois Tigeot  *
5912c9916cdSFrançois Tigeot  * RETURNS:
5922c9916cdSFrançois Tigeot  * Zero on success, error code on failure
5932c9916cdSFrançois Tigeot  */
drm_plane_helper_disable(struct drm_plane * plane)5942c9916cdSFrançois Tigeot int drm_plane_helper_disable(struct drm_plane *plane)
5952c9916cdSFrançois Tigeot {
5962c9916cdSFrançois Tigeot 	struct drm_plane_state *plane_state;
5972c9916cdSFrançois Tigeot 
5982c9916cdSFrançois Tigeot 	/* crtc helpers love to call disable functions for already disabled hw
5992c9916cdSFrançois Tigeot 	 * functions. So cope with that. */
6002c9916cdSFrançois Tigeot 	if (!plane->crtc)
6012c9916cdSFrançois Tigeot 		return 0;
6022c9916cdSFrançois Tigeot 
6032c9916cdSFrançois Tigeot 	if (plane->funcs->atomic_duplicate_state)
6042c9916cdSFrançois Tigeot 		plane_state = plane->funcs->atomic_duplicate_state(plane);
605a05eeebfSFrançois Tigeot 	else {
606a05eeebfSFrançois Tigeot 		if (!plane->state)
607a05eeebfSFrançois Tigeot 			drm_atomic_helper_plane_reset(plane);
608a05eeebfSFrançois Tigeot 
6092c9916cdSFrançois Tigeot 		plane_state = drm_atomic_helper_plane_duplicate_state(plane);
610a05eeebfSFrançois Tigeot 	}
6112c9916cdSFrançois Tigeot 	if (!plane_state)
6122c9916cdSFrançois Tigeot 		return -ENOMEM;
6132c9916cdSFrançois Tigeot 	plane_state->plane = plane;
6142c9916cdSFrançois Tigeot 
6152c9916cdSFrançois Tigeot 	plane_state->crtc = NULL;
6162c9916cdSFrançois Tigeot 	drm_atomic_set_fb_for_plane(plane_state, NULL);
6172c9916cdSFrançois Tigeot 
6182c9916cdSFrançois Tigeot 	return drm_plane_helper_commit(plane, plane_state, plane->fb);
6192c9916cdSFrançois Tigeot }
6202c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_plane_helper_disable);
621