xref: /dflybsd-src/sys/dev/drm/drm_crtc.c (revision 24edb8848e2499ece59b84a04f554a7a897feeab)
15718399fSFrançois Tigeot /*
25718399fSFrançois Tigeot  * Copyright (c) 2006-2008 Intel Corporation
35718399fSFrançois Tigeot  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
45718399fSFrançois Tigeot  * Copyright (c) 2008 Red Hat Inc.
55718399fSFrançois Tigeot  *
65718399fSFrançois Tigeot  * DRM core CRTC related functions
75718399fSFrançois Tigeot  *
85718399fSFrançois Tigeot  * Permission to use, copy, modify, distribute, and sell this software and its
95718399fSFrançois Tigeot  * documentation for any purpose is hereby granted without fee, provided that
105718399fSFrançois Tigeot  * the above copyright notice appear in all copies and that both that copyright
115718399fSFrançois Tigeot  * notice and this permission notice appear in supporting documentation, and
125718399fSFrançois Tigeot  * that the name of the copyright holders not be used in advertising or
135718399fSFrançois Tigeot  * publicity pertaining to distribution of the software without specific,
145718399fSFrançois Tigeot  * written prior permission.  The copyright holders make no representations
155718399fSFrançois Tigeot  * about the suitability of this software for any purpose.  It is provided "as
165718399fSFrançois Tigeot  * is" without express or implied warranty.
175718399fSFrançois Tigeot  *
185718399fSFrançois Tigeot  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
195718399fSFrançois Tigeot  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
205718399fSFrançois Tigeot  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
215718399fSFrançois Tigeot  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
225718399fSFrançois Tigeot  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
235718399fSFrançois Tigeot  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
245718399fSFrançois Tigeot  * OF THIS SOFTWARE.
255718399fSFrançois Tigeot  *
265718399fSFrançois Tigeot  * Authors:
275718399fSFrançois Tigeot  *      Keith Packard
285718399fSFrançois Tigeot  *	Eric Anholt <eric@anholt.net>
295718399fSFrançois Tigeot  *      Dave Airlie <airlied@linux.ie>
305718399fSFrançois Tigeot  *      Jesse Barnes <jesse.barnes@intel.com>
315718399fSFrançois Tigeot  */
3206fede5aSFrançois Tigeot #include <linux/ctype.h>
33b5162e19SFrançois Tigeot #include <linux/list.h>
34b5162e19SFrançois Tigeot #include <linux/export.h>
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3718e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
38b5162e19SFrançois Tigeot #include <uapi_drm/drm_fourcc.h>
39ba55f2f5SFrançois Tigeot #include <linux/slab.h>
40ba55f2f5SFrançois Tigeot #include <drm/drm_modeset_lock.h>
41ba55f2f5SFrançois Tigeot 
42ba55f2f5SFrançois Tigeot #include "drm_crtc_internal.h"
435718399fSFrançois Tigeot 
44*24edb884SFrançois Tigeot static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
45*24edb884SFrançois Tigeot 							struct drm_mode_fb_cmd2 *r,
46*24edb884SFrançois Tigeot 							struct drm_file *file_priv);
47*24edb884SFrançois Tigeot 
48a2fdbec6SFrançois Tigeot /**
49a2fdbec6SFrançois Tigeot  * drm_modeset_lock_all - take all modeset locks
50a2fdbec6SFrançois Tigeot  * @dev: drm device
51a2fdbec6SFrançois Tigeot  *
52a2fdbec6SFrançois Tigeot  * This function takes all modeset locks, suitable where a more fine-grained
53ba55f2f5SFrançois Tigeot  * scheme isn't (yet) implemented. Locks must be dropped with
54ba55f2f5SFrançois Tigeot  * drm_modeset_unlock_all.
55a2fdbec6SFrançois Tigeot  */
56a2fdbec6SFrançois Tigeot void drm_modeset_lock_all(struct drm_device *dev)
57a2fdbec6SFrançois Tigeot {
58ba55f2f5SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
59ba55f2f5SFrançois Tigeot 	struct drm_modeset_acquire_ctx *ctx;
60ba55f2f5SFrançois Tigeot 	int ret;
61a2fdbec6SFrançois Tigeot 
62ba55f2f5SFrançois Tigeot 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
63ba55f2f5SFrançois Tigeot 	if (WARN_ON(!ctx))
64ba55f2f5SFrançois Tigeot 		return;
65a2fdbec6SFrançois Tigeot 
66ba55f2f5SFrançois Tigeot 	mutex_lock(&config->mutex);
67ba55f2f5SFrançois Tigeot 
68ba55f2f5SFrançois Tigeot 	drm_modeset_acquire_init(ctx, 0);
69ba55f2f5SFrançois Tigeot 
70ba55f2f5SFrançois Tigeot retry:
71ba55f2f5SFrançois Tigeot 	ret = drm_modeset_lock(&config->connection_mutex, ctx);
72ba55f2f5SFrançois Tigeot 	if (ret)
73ba55f2f5SFrançois Tigeot 		goto fail;
74ba55f2f5SFrançois Tigeot 	ret = drm_modeset_lock_all_crtcs(dev, ctx);
75ba55f2f5SFrançois Tigeot 	if (ret)
76ba55f2f5SFrançois Tigeot 		goto fail;
77ba55f2f5SFrançois Tigeot 
78ba55f2f5SFrançois Tigeot 	WARN_ON(config->acquire_ctx);
79ba55f2f5SFrançois Tigeot 
80ba55f2f5SFrançois Tigeot 	/* now we hold the locks, so now that it is safe, stash the
81ba55f2f5SFrançois Tigeot 	 * ctx for drm_modeset_unlock_all():
82ba55f2f5SFrançois Tigeot 	 */
83ba55f2f5SFrançois Tigeot 	config->acquire_ctx = ctx;
84ba55f2f5SFrançois Tigeot 
85ba55f2f5SFrançois Tigeot 	drm_warn_on_modeset_not_all_locked(dev);
86ba55f2f5SFrançois Tigeot 
87ba55f2f5SFrançois Tigeot 	return;
88ba55f2f5SFrançois Tigeot 
89ba55f2f5SFrançois Tigeot fail:
90ba55f2f5SFrançois Tigeot 	if (ret == -EDEADLK) {
91ba55f2f5SFrançois Tigeot 		drm_modeset_backoff(ctx);
92ba55f2f5SFrançois Tigeot 		goto retry;
93ba55f2f5SFrançois Tigeot 	}
94a2fdbec6SFrançois Tigeot }
95a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_all);
96a2fdbec6SFrançois Tigeot 
97a2fdbec6SFrançois Tigeot /**
98a2fdbec6SFrançois Tigeot  * drm_modeset_unlock_all - drop all modeset locks
99a2fdbec6SFrançois Tigeot  * @dev: device
100ba55f2f5SFrançois Tigeot  *
101ba55f2f5SFrançois Tigeot  * This function drop all modeset locks taken by drm_modeset_lock_all.
102a2fdbec6SFrançois Tigeot  */
103a2fdbec6SFrançois Tigeot void drm_modeset_unlock_all(struct drm_device *dev)
104a2fdbec6SFrançois Tigeot {
105ba55f2f5SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
106ba55f2f5SFrançois Tigeot 	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
107a2fdbec6SFrançois Tigeot 
108ba55f2f5SFrançois Tigeot 	if (WARN_ON(!ctx))
109ba55f2f5SFrançois Tigeot 		return;
110ba55f2f5SFrançois Tigeot 
111ba55f2f5SFrançois Tigeot 	config->acquire_ctx = NULL;
112ba55f2f5SFrançois Tigeot 	drm_modeset_drop_locks(ctx);
113ba55f2f5SFrançois Tigeot 	drm_modeset_acquire_fini(ctx);
114ba55f2f5SFrançois Tigeot 
115ba55f2f5SFrançois Tigeot 	kfree(ctx);
116a2fdbec6SFrançois Tigeot 
1174dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
118a2fdbec6SFrançois Tigeot }
119a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_unlock_all);
120a2fdbec6SFrançois Tigeot 
1214dbb207bSFrançois Tigeot /**
1224dbb207bSFrançois Tigeot  * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
1234dbb207bSFrançois Tigeot  * @dev: device
124ba55f2f5SFrançois Tigeot  *
125ba55f2f5SFrançois Tigeot  * Useful as a debug assert.
1264dbb207bSFrançois Tigeot  */
1274dbb207bSFrançois Tigeot void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
1284dbb207bSFrançois Tigeot {
1294dbb207bSFrançois Tigeot 	struct drm_crtc *crtc;
1304dbb207bSFrançois Tigeot 
1314dbb207bSFrançois Tigeot 	/* Locking is currently fubar in the panic handler. */
1329edbd4a0SFrançois Tigeot #if 0
1334dbb207bSFrançois Tigeot 	if (oops_in_progress)
1344dbb207bSFrançois Tigeot 		return;
1354dbb207bSFrançois Tigeot #endif
1364dbb207bSFrançois Tigeot 
1374dbb207bSFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
138ba55f2f5SFrançois Tigeot 		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
1394dbb207bSFrançois Tigeot 
140ba55f2f5SFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
1414dbb207bSFrançois Tigeot 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
1424dbb207bSFrançois Tigeot }
1434dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
1444dbb207bSFrançois Tigeot 
1455718399fSFrançois Tigeot /* Avoid boilerplate.  I'm tired of typing. */
1465718399fSFrançois Tigeot #define DRM_ENUM_NAME_FN(fnname, list)				\
14706fede5aSFrançois Tigeot 	const char *fnname(int val)				\
1485718399fSFrançois Tigeot 	{							\
1495718399fSFrançois Tigeot 		int i;						\
150b5162e19SFrançois Tigeot 		for (i = 0; i < ARRAY_SIZE(list); i++) {	\
1515718399fSFrançois Tigeot 			if (list[i].type == val)		\
1525718399fSFrançois Tigeot 				return list[i].name;		\
1535718399fSFrançois Tigeot 		}						\
1545718399fSFrançois Tigeot 		return "(unknown)";				\
1555718399fSFrançois Tigeot 	}
1565718399fSFrançois Tigeot 
1575718399fSFrançois Tigeot /*
1585718399fSFrançois Tigeot  * Global properties
1595718399fSFrançois Tigeot  */
16006fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] =
1615718399fSFrançois Tigeot {	{ DRM_MODE_DPMS_ON, "On" },
1625718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_STANDBY, "Standby" },
1635718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_SUSPEND, "Suspend" },
1645718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_OFF, "Off" }
1655718399fSFrançois Tigeot };
1665718399fSFrançois Tigeot 
1675718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
1685718399fSFrançois Tigeot 
169ba55f2f5SFrançois Tigeot static const struct drm_prop_enum_list drm_plane_type_enum_list[] =
170ba55f2f5SFrançois Tigeot {
171ba55f2f5SFrançois Tigeot 	{ DRM_PLANE_TYPE_OVERLAY, "Overlay" },
172ba55f2f5SFrançois Tigeot 	{ DRM_PLANE_TYPE_PRIMARY, "Primary" },
173ba55f2f5SFrançois Tigeot 	{ DRM_PLANE_TYPE_CURSOR, "Cursor" },
174ba55f2f5SFrançois Tigeot };
175ba55f2f5SFrançois Tigeot 
1765718399fSFrançois Tigeot /*
1775718399fSFrançois Tigeot  * Optional properties
1785718399fSFrançois Tigeot  */
17906fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
1805718399fSFrançois Tigeot {
1815718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_NONE, "None" },
1825718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_FULLSCREEN, "Full" },
1835718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_CENTER, "Center" },
1845718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_ASPECT, "Full aspect" },
1855718399fSFrançois Tigeot };
1865718399fSFrançois Tigeot 
187*24edb884SFrançois Tigeot static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
188*24edb884SFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
189*24edb884SFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
190*24edb884SFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
191*24edb884SFrançois Tigeot };
192*24edb884SFrançois Tigeot 
1935718399fSFrançois Tigeot /*
1945718399fSFrançois Tigeot  * Non-global properties, but "required" for certain connectors.
1955718399fSFrançois Tigeot  */
19606fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
1975718399fSFrançois Tigeot {
1985718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
1995718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
2005718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
2015718399fSFrançois Tigeot };
2025718399fSFrançois Tigeot 
2035718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
2045718399fSFrançois Tigeot 
20506fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
2065718399fSFrançois Tigeot {
2075718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
2085718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
2095718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
2105718399fSFrançois Tigeot };
2115718399fSFrançois Tigeot 
2125718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
2135718399fSFrançois Tigeot 		 drm_dvi_i_subconnector_enum_list)
2145718399fSFrançois Tigeot 
21506fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] =
2165718399fSFrançois Tigeot {
2175718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
2185718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
2195718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
2205718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
2215718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
2225718399fSFrançois Tigeot };
2235718399fSFrançois Tigeot 
2245718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
2255718399fSFrançois Tigeot 
22606fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
2275718399fSFrançois Tigeot {
2285718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
2295718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
2305718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
2315718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
2325718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
2335718399fSFrançois Tigeot };
2345718399fSFrançois Tigeot 
2355718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
2365718399fSFrançois Tigeot 		 drm_tv_subconnector_enum_list)
2375718399fSFrançois Tigeot 
23806fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
2395718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_OFF,      "Off"      },
2405718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_ON,       "On"       },
2415718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
2425718399fSFrançois Tigeot };
2435718399fSFrançois Tigeot 
2445718399fSFrançois Tigeot struct drm_conn_prop_enum_list {
2455718399fSFrançois Tigeot 	int type;
24606fede5aSFrançois Tigeot 	const char *name;
2475718399fSFrançois Tigeot 	int count;
2485718399fSFrançois Tigeot };
2495718399fSFrançois Tigeot 
2505718399fSFrançois Tigeot /*
2515718399fSFrançois Tigeot  * Connector and encoder types.
2525718399fSFrançois Tigeot  */
2535718399fSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
2549edbd4a0SFrançois Tigeot {	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
2559edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
2569edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
2579edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
2589edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
2599edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Composite, "Composite" },
2609edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
2619edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
2629edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Component, "Component" },
2639edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
2649edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
2659edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
2669edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
2679edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_TV, "TV" },
2689edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
2699edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
2709edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
2715718399fSFrançois Tigeot };
2725718399fSFrançois Tigeot 
27306fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_encoder_enum_list[] =
2745718399fSFrançois Tigeot {	{ DRM_MODE_ENCODER_NONE, "None" },
2755718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_DAC, "DAC" },
2765718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
2775718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
2785718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_TVDAC, "TV" },
279b5162e19SFrançois Tigeot 	{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
2809edbd4a0SFrançois Tigeot 	{ DRM_MODE_ENCODER_DSI, "DSI" },
281ba55f2f5SFrançois Tigeot 	{ DRM_MODE_ENCODER_DPMST, "DP MST" },
2825718399fSFrançois Tigeot };
2835718399fSFrançois Tigeot 
284ba55f2f5SFrançois Tigeot static const struct drm_prop_enum_list drm_subpixel_enum_list[] =
2855718399fSFrançois Tigeot {
286ba55f2f5SFrançois Tigeot 	{ SubPixelUnknown, "Unknown" },
287ba55f2f5SFrançois Tigeot 	{ SubPixelHorizontalRGB, "Horizontal RGB" },
288ba55f2f5SFrançois Tigeot 	{ SubPixelHorizontalBGR, "Horizontal BGR" },
289ba55f2f5SFrançois Tigeot 	{ SubPixelVerticalRGB, "Vertical RGB" },
290ba55f2f5SFrançois Tigeot 	{ SubPixelVerticalBGR, "Vertical BGR" },
291ba55f2f5SFrançois Tigeot 	{ SubPixelNone, "None" },
292ba55f2f5SFrançois Tigeot };
2935718399fSFrançois Tigeot 
294ba55f2f5SFrançois Tigeot void drm_connector_ida_init(void)
2955718399fSFrançois Tigeot {
296ba55f2f5SFrançois Tigeot #if 0
297ba55f2f5SFrançois Tigeot 	int i;
2985718399fSFrançois Tigeot 
299ba55f2f5SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
300ba55f2f5SFrançois Tigeot 		ida_init(&drm_connector_enum_list[i].ida);
301ba55f2f5SFrançois Tigeot #endif
3025718399fSFrançois Tigeot }
3035718399fSFrançois Tigeot 
304ba55f2f5SFrançois Tigeot void drm_connector_ida_destroy(void)
305ba55f2f5SFrançois Tigeot {
306ba55f2f5SFrançois Tigeot #if 0
307ba55f2f5SFrançois Tigeot 	int i;
308ba55f2f5SFrançois Tigeot 
309ba55f2f5SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
310ba55f2f5SFrançois Tigeot 		ida_destroy(&drm_connector_enum_list[i].ida);
311ba55f2f5SFrançois Tigeot #endif
312ba55f2f5SFrançois Tigeot }
313ba55f2f5SFrançois Tigeot 
314ba55f2f5SFrançois Tigeot /**
315ba55f2f5SFrançois Tigeot  * drm_get_connector_status_name - return a string for connector status
316ba55f2f5SFrançois Tigeot  * @status: connector status to compute name of
317ba55f2f5SFrançois Tigeot  *
318ba55f2f5SFrançois Tigeot  * In contrast to the other drm_get_*_name functions this one here returns a
319ba55f2f5SFrançois Tigeot  * const pointer and hence is threadsafe.
320ba55f2f5SFrançois Tigeot  */
32106fede5aSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status)
3225718399fSFrançois Tigeot {
3235718399fSFrançois Tigeot 	if (status == connector_status_connected)
3245718399fSFrançois Tigeot 		return "connected";
3255718399fSFrançois Tigeot 	else if (status == connector_status_disconnected)
3265718399fSFrançois Tigeot 		return "disconnected";
3275718399fSFrançois Tigeot 	else
3285718399fSFrançois Tigeot 		return "unknown";
3295718399fSFrançois Tigeot }
3304dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name);
3315718399fSFrançois Tigeot 
332ba55f2f5SFrançois Tigeot /**
333ba55f2f5SFrançois Tigeot  * drm_get_subpixel_order_name - return a string for a given subpixel enum
334ba55f2f5SFrançois Tigeot  * @order: enum of subpixel_order
335ba55f2f5SFrançois Tigeot  *
336ba55f2f5SFrançois Tigeot  * Note you could abuse this and return something out of bounds, but that
337ba55f2f5SFrançois Tigeot  * would be a caller error.  No unscrubbed user data should make it here.
338ba55f2f5SFrançois Tigeot  */
339ba55f2f5SFrançois Tigeot const char *drm_get_subpixel_order_name(enum subpixel_order order)
340ba55f2f5SFrançois Tigeot {
341ba55f2f5SFrançois Tigeot 	return drm_subpixel_enum_list[order].name;
342ba55f2f5SFrançois Tigeot }
343ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_subpixel_order_name);
344ba55f2f5SFrançois Tigeot 
34506fede5aSFrançois Tigeot static char printable_char(int c)
34606fede5aSFrançois Tigeot {
34706fede5aSFrançois Tigeot 	return isascii(c) && isprint(c) ? c : '?';
34806fede5aSFrançois Tigeot }
34906fede5aSFrançois Tigeot 
350ba55f2f5SFrançois Tigeot /**
351ba55f2f5SFrançois Tigeot  * drm_get_format_name - return a string for drm fourcc format
352ba55f2f5SFrançois Tigeot  * @format: format to compute name of
353ba55f2f5SFrançois Tigeot  *
354ba55f2f5SFrançois Tigeot  * Note that the buffer used by this function is globally shared and owned by
355ba55f2f5SFrançois Tigeot  * the function itself.
356ba55f2f5SFrançois Tigeot  *
357ba55f2f5SFrançois Tigeot  * FIXME: This isn't really multithreading safe.
358ba55f2f5SFrançois Tigeot  */
35906fede5aSFrançois Tigeot const char *drm_get_format_name(uint32_t format)
36006fede5aSFrançois Tigeot {
36106fede5aSFrançois Tigeot 	static char buf[32];
36206fede5aSFrançois Tigeot 
36306fede5aSFrançois Tigeot 	ksnprintf(buf, sizeof(buf),
36406fede5aSFrançois Tigeot 		 "%c%c%c%c %s-endian (0x%08x)",
36506fede5aSFrançois Tigeot 		 printable_char(format & 0xff),
36606fede5aSFrançois Tigeot 		 printable_char((format >> 8) & 0xff),
36706fede5aSFrançois Tigeot 		 printable_char((format >> 16) & 0xff),
36806fede5aSFrançois Tigeot 		 printable_char((format >> 24) & 0x7f),
36906fede5aSFrançois Tigeot 		 format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little",
37006fede5aSFrançois Tigeot 		 format);
37106fede5aSFrançois Tigeot 
37206fede5aSFrançois Tigeot 	return buf;
37306fede5aSFrançois Tigeot }
37406fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_get_format_name);
37506fede5aSFrançois Tigeot 
3765718399fSFrançois Tigeot /**
3774dbb207bSFrançois Tigeot  * drm_mode_object_get - allocate a new modeset identifier
3785718399fSFrançois Tigeot  * @dev: DRM device
3794dbb207bSFrançois Tigeot  * @obj: object pointer, used to generate unique ID
3804dbb207bSFrançois Tigeot  * @obj_type: object type
3815718399fSFrançois Tigeot  *
3825718399fSFrançois Tigeot  * Create a unique identifier based on @ptr in @dev's identifier space.  Used
383ba55f2f5SFrançois Tigeot  * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
384ba55f2f5SFrançois Tigeot  * modeset identifiers are _not_ reference counted. Hence don't use this for
385ba55f2f5SFrançois Tigeot  * reference counted modeset objects like framebuffers.
3865718399fSFrançois Tigeot  *
387ba55f2f5SFrançois Tigeot  * Returns:
3885718399fSFrançois Tigeot  * New unique (relative to other objects in @dev) integer identifier for the
3895718399fSFrançois Tigeot  * object.
3905718399fSFrançois Tigeot  */
391ba55f2f5SFrançois Tigeot int drm_mode_object_get(struct drm_device *dev,
3925718399fSFrançois Tigeot 			struct drm_mode_object *obj, uint32_t obj_type)
3935718399fSFrançois Tigeot {
3945718399fSFrançois Tigeot 	int ret;
3955718399fSFrançois Tigeot 
3969f0f5970SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
3979f0f5970SFrançois Tigeot 	ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL);
3989f0f5970SFrançois Tigeot 	if (ret >= 0) {
3994dbb207bSFrançois Tigeot 		/*
4004dbb207bSFrançois Tigeot 		 * Set up the object linking under the protection of the idr
4014dbb207bSFrançois Tigeot 		 * lock so that other users can't see inconsistent state.
4024dbb207bSFrançois Tigeot 		 */
4039f0f5970SFrançois Tigeot 		obj->id = ret;
4045718399fSFrançois Tigeot 		obj->type = obj_type;
4059f0f5970SFrançois Tigeot 	}
4069f0f5970SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
4079f0f5970SFrançois Tigeot 
4089f0f5970SFrançois Tigeot 	return ret < 0 ? ret : 0;
4095718399fSFrançois Tigeot }
4105718399fSFrançois Tigeot 
4115718399fSFrançois Tigeot /**
4124dbb207bSFrançois Tigeot  * drm_mode_object_put - free a modeset identifer
4135718399fSFrançois Tigeot  * @dev: DRM device
4144dbb207bSFrançois Tigeot  * @object: object to free
4155718399fSFrançois Tigeot  *
416ba55f2f5SFrançois Tigeot  * Free @id from @dev's unique identifier pool. Note that despite the _get
417ba55f2f5SFrançois Tigeot  * postfix modeset identifiers are _not_ reference counted. Hence don't use this
418ba55f2f5SFrançois Tigeot  * for reference counted modeset objects like framebuffers.
4195718399fSFrançois Tigeot  */
420ba55f2f5SFrançois Tigeot void drm_mode_object_put(struct drm_device *dev,
4215718399fSFrançois Tigeot 			 struct drm_mode_object *object)
4225718399fSFrançois Tigeot {
4234dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
424aea8bdbdSFrançois Tigeot 	idr_remove(&dev->mode_config.crtc_idr, object->id);
4254dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
4265718399fSFrançois Tigeot }
4275718399fSFrançois Tigeot 
428ba55f2f5SFrançois Tigeot static struct drm_mode_object *_object_find(struct drm_device *dev,
429ba55f2f5SFrançois Tigeot 		uint32_t id, uint32_t type)
430ba55f2f5SFrançois Tigeot {
431ba55f2f5SFrançois Tigeot 	struct drm_mode_object *obj = NULL;
432ba55f2f5SFrançois Tigeot 
433ba55f2f5SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
434ba55f2f5SFrançois Tigeot 	obj = idr_find(&dev->mode_config.crtc_idr, id);
435*24edb884SFrançois Tigeot 	if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
436*24edb884SFrançois Tigeot 		obj = NULL;
437*24edb884SFrançois Tigeot 	if (obj && obj->id != id)
438*24edb884SFrançois Tigeot 		obj = NULL;
439*24edb884SFrançois Tigeot 	/* don't leak out unref'd fb's */
440*24edb884SFrançois Tigeot 	if (obj && (obj->type == DRM_MODE_OBJECT_FB))
441ba55f2f5SFrançois Tigeot 		obj = NULL;
442ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
443ba55f2f5SFrançois Tigeot 
444ba55f2f5SFrançois Tigeot 	return obj;
445ba55f2f5SFrançois Tigeot }
446ba55f2f5SFrançois Tigeot 
4474dbb207bSFrançois Tigeot /**
4484dbb207bSFrançois Tigeot  * drm_mode_object_find - look up a drm object with static lifetime
4494dbb207bSFrançois Tigeot  * @dev: drm device
4504dbb207bSFrançois Tigeot  * @id: id of the mode object
4514dbb207bSFrançois Tigeot  * @type: type of the mode object
4524dbb207bSFrançois Tigeot  *
4534dbb207bSFrançois Tigeot  * Note that framebuffers cannot be looked up with this functions - since those
454ba55f2f5SFrançois Tigeot  * are reference counted, they need special treatment.  Even with
455ba55f2f5SFrançois Tigeot  * DRM_MODE_OBJECT_ANY (although that will simply return NULL
456ba55f2f5SFrançois Tigeot  * rather than WARN_ON()).
4574dbb207bSFrançois Tigeot  */
4585718399fSFrançois Tigeot struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
4595718399fSFrançois Tigeot 		uint32_t id, uint32_t type)
4605718399fSFrançois Tigeot {
461aea8bdbdSFrançois Tigeot 	struct drm_mode_object *obj = NULL;
4625718399fSFrançois Tigeot 
4634dbb207bSFrançois Tigeot 	/* Framebuffers are reference counted and need their own lookup
4644dbb207bSFrançois Tigeot 	 * function.*/
4654dbb207bSFrançois Tigeot 	WARN_ON(type == DRM_MODE_OBJECT_FB);
466ba55f2f5SFrançois Tigeot 	obj = _object_find(dev, id, type);
4675718399fSFrançois Tigeot 	return obj;
4685718399fSFrançois Tigeot }
469b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_find);
4705718399fSFrançois Tigeot 
4715718399fSFrançois Tigeot /**
4725718399fSFrançois Tigeot  * drm_framebuffer_init - initialize a framebuffer
4735718399fSFrançois Tigeot  * @dev: DRM device
4744dbb207bSFrançois Tigeot  * @fb: framebuffer to be initialized
4754dbb207bSFrançois Tigeot  * @funcs: ... with these functions
4765718399fSFrançois Tigeot  *
4775718399fSFrançois Tigeot  * Allocates an ID for the framebuffer's parent mode object, sets its mode
4785718399fSFrançois Tigeot  * functions & device file and adds it to the master fd list.
4795718399fSFrançois Tigeot  *
4804dbb207bSFrançois Tigeot  * IMPORTANT:
4814dbb207bSFrançois Tigeot  * This functions publishes the fb and makes it available for concurrent access
4824dbb207bSFrançois Tigeot  * by other users. Which means by this point the fb _must_ be fully set up -
4834dbb207bSFrançois Tigeot  * since all the fb attributes are invariant over its lifetime, no further
4844dbb207bSFrançois Tigeot  * locking but only correct reference counting is required.
4854dbb207bSFrançois Tigeot  *
486ba55f2f5SFrançois Tigeot  * Returns:
4875718399fSFrançois Tigeot  * Zero on success, error code on failure.
4885718399fSFrançois Tigeot  */
4895718399fSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
4905718399fSFrançois Tigeot 			 const struct drm_framebuffer_funcs *funcs)
4915718399fSFrançois Tigeot {
4925718399fSFrançois Tigeot 	int ret;
4935718399fSFrançois Tigeot 
4944dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
495b5162e19SFrançois Tigeot 	kref_init(&fb->refcount);
4964dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&fb->filp_head);
4974dbb207bSFrançois Tigeot 	fb->dev = dev;
4984dbb207bSFrançois Tigeot 	fb->funcs = funcs;
499b5162e19SFrançois Tigeot 
5005718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
5015718399fSFrançois Tigeot 	if (ret)
5024dbb207bSFrançois Tigeot 		goto out;
5035718399fSFrançois Tigeot 
5044dbb207bSFrançois Tigeot 	/* Grab the idr reference. */
5054dbb207bSFrançois Tigeot 	drm_framebuffer_reference(fb);
5064dbb207bSFrançois Tigeot 
5075718399fSFrançois Tigeot 	dev->mode_config.num_fb++;
5085718399fSFrançois Tigeot 	list_add(&fb->head, &dev->mode_config.fb_list);
5094dbb207bSFrançois Tigeot out:
5104dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
5115718399fSFrançois Tigeot 
5125718399fSFrançois Tigeot 	return 0;
5135718399fSFrançois Tigeot }
514b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init);
515b5162e19SFrançois Tigeot 
516b5162e19SFrançois Tigeot static void drm_framebuffer_free(struct kref *kref)
517b5162e19SFrançois Tigeot {
518b5162e19SFrançois Tigeot 	struct drm_framebuffer *fb =
519b5162e19SFrançois Tigeot 			container_of(kref, struct drm_framebuffer, refcount);
520b5162e19SFrançois Tigeot 	fb->funcs->destroy(fb);
521b5162e19SFrançois Tigeot }
522b5162e19SFrançois Tigeot 
5234dbb207bSFrançois Tigeot static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev,
5244dbb207bSFrançois Tigeot 							uint32_t id)
5254dbb207bSFrançois Tigeot {
5264dbb207bSFrançois Tigeot 	struct drm_mode_object *obj = NULL;
5274dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb;
5284dbb207bSFrançois Tigeot 
5294dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
5304dbb207bSFrançois Tigeot 	obj = idr_find(&dev->mode_config.crtc_idr, id);
5314dbb207bSFrançois Tigeot 	if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id))
5324dbb207bSFrançois Tigeot 		fb = NULL;
5334dbb207bSFrançois Tigeot 	else
5344dbb207bSFrançois Tigeot 		fb = obj_to_fb(obj);
5354dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
5364dbb207bSFrançois Tigeot 
5374dbb207bSFrançois Tigeot 	return fb;
5384dbb207bSFrançois Tigeot }
5394dbb207bSFrançois Tigeot 
5404dbb207bSFrançois Tigeot /**
5414dbb207bSFrançois Tigeot  * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
5424dbb207bSFrançois Tigeot  * @dev: drm device
5434dbb207bSFrançois Tigeot  * @id: id of the fb object
5444dbb207bSFrançois Tigeot  *
5454dbb207bSFrançois Tigeot  * If successful, this grabs an additional reference to the framebuffer -
5464dbb207bSFrançois Tigeot  * callers need to make sure to eventually unreference the returned framebuffer
547ba55f2f5SFrançois Tigeot  * again, using @drm_framebuffer_unreference.
5484dbb207bSFrançois Tigeot  */
5494dbb207bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
5504dbb207bSFrançois Tigeot 					       uint32_t id)
5514dbb207bSFrançois Tigeot {
5524dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb;
5534dbb207bSFrançois Tigeot 
5544dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
5554dbb207bSFrançois Tigeot 	fb = __drm_framebuffer_lookup(dev, id);
5564dbb207bSFrançois Tigeot 	if (fb)
5574dbb207bSFrançois Tigeot 		drm_framebuffer_reference(fb);
5584dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
5594dbb207bSFrançois Tigeot 
5604dbb207bSFrançois Tigeot 	return fb;
5614dbb207bSFrançois Tigeot }
5624dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup);
5634dbb207bSFrançois Tigeot 
564b5162e19SFrançois Tigeot /**
565b5162e19SFrançois Tigeot  * drm_framebuffer_unreference - unref a framebuffer
5664dbb207bSFrançois Tigeot  * @fb: framebuffer to unref
567b5162e19SFrançois Tigeot  *
5684dbb207bSFrançois Tigeot  * This functions decrements the fb's refcount and frees it if it drops to zero.
569b5162e19SFrançois Tigeot  */
570b5162e19SFrançois Tigeot void drm_framebuffer_unreference(struct drm_framebuffer *fb)
571b5162e19SFrançois Tigeot {
572ba55f2f5SFrançois Tigeot 	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
573b5162e19SFrançois Tigeot 	kref_put(&fb->refcount, drm_framebuffer_free);
574b5162e19SFrançois Tigeot }
575b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unreference);
576b5162e19SFrançois Tigeot 
577b5162e19SFrançois Tigeot /**
578b5162e19SFrançois Tigeot  * drm_framebuffer_reference - incr the fb refcnt
5794dbb207bSFrançois Tigeot  * @fb: framebuffer
580ba55f2f5SFrançois Tigeot  *
581ba55f2f5SFrançois Tigeot  * This functions increments the fb's refcount.
582b5162e19SFrançois Tigeot  */
583b5162e19SFrançois Tigeot void drm_framebuffer_reference(struct drm_framebuffer *fb)
584b5162e19SFrançois Tigeot {
585ba55f2f5SFrançois Tigeot 	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
586b5162e19SFrançois Tigeot 	kref_get(&fb->refcount);
587b5162e19SFrançois Tigeot }
588b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_reference);
5895718399fSFrançois Tigeot 
590a2fdbec6SFrançois Tigeot static void drm_framebuffer_free_bug(struct kref *kref)
591a2fdbec6SFrançois Tigeot {
592a2fdbec6SFrançois Tigeot 	BUG();
593a2fdbec6SFrançois Tigeot }
594a2fdbec6SFrançois Tigeot 
595a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
596a2fdbec6SFrançois Tigeot {
597ba55f2f5SFrançois Tigeot 	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
598a2fdbec6SFrançois Tigeot 	kref_put(&fb->refcount, drm_framebuffer_free_bug);
599a2fdbec6SFrançois Tigeot }
600a2fdbec6SFrançois Tigeot 
601a2fdbec6SFrançois Tigeot /* dev->mode_config.fb_lock must be held! */
602a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unregister(struct drm_device *dev,
603a2fdbec6SFrançois Tigeot 					 struct drm_framebuffer *fb)
604a2fdbec6SFrançois Tigeot {
605a2fdbec6SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
606a2fdbec6SFrançois Tigeot 	idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
607a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
608a2fdbec6SFrançois Tigeot 
609a2fdbec6SFrançois Tigeot 	fb->base.id = 0;
610a2fdbec6SFrançois Tigeot 
611a2fdbec6SFrançois Tigeot 	__drm_framebuffer_unreference(fb);
612a2fdbec6SFrançois Tigeot }
613a2fdbec6SFrançois Tigeot 
614a2fdbec6SFrançois Tigeot /**
615a2fdbec6SFrançois Tigeot  * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
616a2fdbec6SFrançois Tigeot  * @fb: fb to unregister
617a2fdbec6SFrançois Tigeot  *
618a2fdbec6SFrançois Tigeot  * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
619a2fdbec6SFrançois Tigeot  * those used for fbdev. Note that the caller must hold a reference of it's own,
620a2fdbec6SFrançois Tigeot  * i.e. the object may not be destroyed through this call (since it'll lead to a
621a2fdbec6SFrançois Tigeot  * locking inversion).
622a2fdbec6SFrançois Tigeot  */
623a2fdbec6SFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
624a2fdbec6SFrançois Tigeot {
625a2fdbec6SFrançois Tigeot 	struct drm_device *dev = fb->dev;
626a2fdbec6SFrançois Tigeot 
627a2fdbec6SFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
628a2fdbec6SFrançois Tigeot 	/* Mark fb as reaped and drop idr ref. */
629a2fdbec6SFrançois Tigeot 	__drm_framebuffer_unregister(dev, fb);
630a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
631a2fdbec6SFrançois Tigeot }
632a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private);
633a2fdbec6SFrançois Tigeot 
6345718399fSFrançois Tigeot /**
6355718399fSFrançois Tigeot  * drm_framebuffer_cleanup - remove a framebuffer object
6365718399fSFrançois Tigeot  * @fb: framebuffer to remove
6375718399fSFrançois Tigeot  *
638ba55f2f5SFrançois Tigeot  * Cleanup framebuffer. This function is intended to be used from the drivers
639ba55f2f5SFrançois Tigeot  * ->destroy callback. It can also be used to clean up driver private
640ba55f2f5SFrançois Tigeot  *  framebuffers embedded into a larger structure.
6415718399fSFrançois Tigeot  *
6424dbb207bSFrançois Tigeot  * Note that this function does not remove the fb from active usuage - if it is
6434dbb207bSFrançois Tigeot  * still used anywhere, hilarity can ensue since userspace could call getfb on
6444dbb207bSFrançois Tigeot  * the id and get back -EINVAL. Obviously no concern at driver unload time.
6454dbb207bSFrançois Tigeot  *
6464dbb207bSFrançois Tigeot  * Also, the framebuffer will not be removed from the lookup idr - for
6474dbb207bSFrançois Tigeot  * user-created framebuffers this will happen in in the rmfb ioctl. For
6484dbb207bSFrançois Tigeot  * driver-private objects (e.g. for fbdev) drivers need to explicitly call
6494dbb207bSFrançois Tigeot  * drm_framebuffer_unregister_private.
6505718399fSFrançois Tigeot  */
6515718399fSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
6525718399fSFrançois Tigeot {
6535718399fSFrançois Tigeot 	struct drm_device *dev = fb->dev;
6544dbb207bSFrançois Tigeot 
6554dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
656b5162e19SFrançois Tigeot 	list_del(&fb->head);
657b5162e19SFrançois Tigeot 	dev->mode_config.num_fb--;
6584dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
659b5162e19SFrançois Tigeot }
660b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup);
661b5162e19SFrançois Tigeot 
662b5162e19SFrançois Tigeot /**
663b5162e19SFrançois Tigeot  * drm_framebuffer_remove - remove and unreference a framebuffer object
664b5162e19SFrançois Tigeot  * @fb: framebuffer to remove
665b5162e19SFrançois Tigeot  *
666b5162e19SFrançois Tigeot  * Scans all the CRTCs and planes in @dev's mode_config.  If they're
6674dbb207bSFrançois Tigeot  * using @fb, removes it, setting it to NULL. Then drops the reference to the
6684dbb207bSFrançois Tigeot  * passed-in framebuffer. Might take the modeset locks.
6694dbb207bSFrançois Tigeot  *
6704dbb207bSFrançois Tigeot  * Note that this function optimizes the cleanup away if the caller holds the
6714dbb207bSFrançois Tigeot  * last reference to the framebuffer. It is also guaranteed to not take the
6724dbb207bSFrançois Tigeot  * modeset locks in this case.
673b5162e19SFrançois Tigeot  */
674b5162e19SFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb)
675b5162e19SFrançois Tigeot {
676b5162e19SFrançois Tigeot 	struct drm_device *dev = fb->dev;
6775718399fSFrançois Tigeot 	struct drm_crtc *crtc;
6785718399fSFrançois Tigeot 	struct drm_plane *plane;
6795718399fSFrançois Tigeot 	struct drm_mode_set set;
6805718399fSFrançois Tigeot 	int ret;
6815718399fSFrançois Tigeot 
6824dbb207bSFrançois Tigeot 	WARN_ON(!list_empty(&fb->filp_head));
6834dbb207bSFrançois Tigeot 
6844dbb207bSFrançois Tigeot 	/*
6854dbb207bSFrançois Tigeot 	 * drm ABI mandates that we remove any deleted framebuffers from active
6864dbb207bSFrançois Tigeot 	 * useage. But since most sane clients only remove framebuffers they no
6874dbb207bSFrançois Tigeot 	 * longer need, try to optimize this away.
6884dbb207bSFrançois Tigeot 	 *
6894dbb207bSFrançois Tigeot 	 * Since we're holding a reference ourselves, observing a refcount of 1
6904dbb207bSFrançois Tigeot 	 * means that we're the last holder and can skip it. Also, the refcount
6914dbb207bSFrançois Tigeot 	 * can never increase from 1 again, so we don't need any barriers or
6924dbb207bSFrançois Tigeot 	 * locks.
6934dbb207bSFrançois Tigeot 	 *
6944dbb207bSFrançois Tigeot 	 * Note that userspace could try to race with use and instate a new
6954dbb207bSFrançois Tigeot 	 * usage _after_ we've cleared all current ones. End result will be an
6964dbb207bSFrançois Tigeot 	 * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
6974dbb207bSFrançois Tigeot 	 * in this manner.
6984dbb207bSFrançois Tigeot 	 */
6994dbb207bSFrançois Tigeot 	if (atomic_read(&fb->refcount.refcount) > 1) {
7004dbb207bSFrançois Tigeot 		drm_modeset_lock_all(dev);
7015718399fSFrançois Tigeot 		/* remove from any CRTC */
7025718399fSFrançois Tigeot 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
703ba55f2f5SFrançois Tigeot 			if (crtc->primary->fb == fb) {
7045718399fSFrançois Tigeot 				/* should turn off the crtc */
7055718399fSFrançois Tigeot 				memset(&set, 0, sizeof(struct drm_mode_set));
7065718399fSFrançois Tigeot 				set.crtc = crtc;
7075718399fSFrançois Tigeot 				set.fb = NULL;
7084dbb207bSFrançois Tigeot 				ret = drm_mode_set_config_internal(&set);
7095718399fSFrançois Tigeot 				if (ret)
7105718399fSFrançois Tigeot 					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
7115718399fSFrançois Tigeot 			}
7125718399fSFrançois Tigeot 		}
7135718399fSFrançois Tigeot 
7145718399fSFrançois Tigeot 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
71506fede5aSFrançois Tigeot 			if (plane->fb == fb)
71606fede5aSFrançois Tigeot 				drm_plane_force_disable(plane);
7175718399fSFrançois Tigeot 		}
7184dbb207bSFrançois Tigeot 		drm_modeset_unlock_all(dev);
7194dbb207bSFrançois Tigeot 	}
720b5162e19SFrançois Tigeot 
721b5162e19SFrançois Tigeot 	drm_framebuffer_unreference(fb);
7225718399fSFrançois Tigeot }
723b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove);
7245718399fSFrançois Tigeot 
725ba55f2f5SFrançois Tigeot DEFINE_WW_CLASS(crtc_ww_class);
726ba55f2f5SFrançois Tigeot 
7275718399fSFrançois Tigeot /**
728ba55f2f5SFrançois Tigeot  * drm_crtc_init_with_planes - Initialise a new CRTC object with
729ba55f2f5SFrançois Tigeot  *    specified primary and cursor planes.
7305718399fSFrançois Tigeot  * @dev: DRM device
7315718399fSFrançois Tigeot  * @crtc: CRTC object to init
732ba55f2f5SFrançois Tigeot  * @primary: Primary plane for CRTC
733ba55f2f5SFrançois Tigeot  * @cursor: Cursor plane for CRTC
7345718399fSFrançois Tigeot  * @funcs: callbacks for the new CRTC
7355718399fSFrançois Tigeot  *
73606fede5aSFrançois Tigeot  * Inits a new object created as base part of a driver crtc object.
7375718399fSFrançois Tigeot  *
738ba55f2f5SFrançois Tigeot  * Returns:
7395718399fSFrançois Tigeot  * Zero on success, error code on failure.
7405718399fSFrançois Tigeot  */
741ba55f2f5SFrançois Tigeot int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
742ba55f2f5SFrançois Tigeot 			      struct drm_plane *primary,
743*24edb884SFrançois Tigeot 			      struct drm_plane *cursor,
7445718399fSFrançois Tigeot 			      const struct drm_crtc_funcs *funcs)
7455718399fSFrançois Tigeot {
746ba55f2f5SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
7475718399fSFrançois Tigeot 	int ret;
7485718399fSFrançois Tigeot 
7495718399fSFrançois Tigeot 	crtc->dev = dev;
7505718399fSFrançois Tigeot 	crtc->funcs = funcs;
751b5162e19SFrançois Tigeot 	crtc->invert_dimensions = false;
7525718399fSFrançois Tigeot 
7534dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
754ba55f2f5SFrançois Tigeot 	drm_modeset_lock_init(&crtc->mutex);
755ba55f2f5SFrançois Tigeot 	/* dropped by _unlock_all(): */
756ba55f2f5SFrançois Tigeot 	drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
757b5162e19SFrançois Tigeot 
7585718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
7595718399fSFrançois Tigeot 	if (ret)
7605718399fSFrançois Tigeot 		goto out;
7615718399fSFrançois Tigeot 
762b5162e19SFrançois Tigeot 	crtc->base.properties = &crtc->properties;
763b5162e19SFrançois Tigeot 
764ba55f2f5SFrançois Tigeot 	list_add_tail(&crtc->head, &config->crtc_list);
765ba55f2f5SFrançois Tigeot 	config->num_crtc++;
766ba55f2f5SFrançois Tigeot 
767ba55f2f5SFrançois Tigeot 	crtc->primary = primary;
768*24edb884SFrançois Tigeot 	crtc->cursor = cursor;
769ba55f2f5SFrançois Tigeot 	if (primary)
770ba55f2f5SFrançois Tigeot 		primary->possible_crtcs = 1 << drm_crtc_index(crtc);
771*24edb884SFrançois Tigeot 	if (cursor)
772*24edb884SFrançois Tigeot 		cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
773b5162e19SFrançois Tigeot 
7745718399fSFrançois Tigeot  out:
7754dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
7765718399fSFrançois Tigeot 
7775718399fSFrançois Tigeot 	return ret;
7785718399fSFrançois Tigeot }
779ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init_with_planes);
7805718399fSFrançois Tigeot 
7815718399fSFrançois Tigeot /**
78206fede5aSFrançois Tigeot  * drm_crtc_cleanup - Clean up the core crtc usage
7835718399fSFrançois Tigeot  * @crtc: CRTC to cleanup
7845718399fSFrançois Tigeot  *
78506fede5aSFrançois Tigeot  * This function cleans up @crtc and removes it from the DRM mode setting
78606fede5aSFrançois Tigeot  * core. Note that the function does *not* free the crtc structure itself,
78706fede5aSFrançois Tigeot  * this is the responsibility of the caller.
7885718399fSFrançois Tigeot  */
7895718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc)
7905718399fSFrançois Tigeot {
7915718399fSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
7925718399fSFrançois Tigeot 
7934dbb207bSFrançois Tigeot 	kfree(crtc->gamma_store);
7945718399fSFrançois Tigeot 	crtc->gamma_store = NULL;
7955718399fSFrançois Tigeot 
796ba55f2f5SFrançois Tigeot 	drm_modeset_lock_fini(&crtc->mutex);
797ba55f2f5SFrançois Tigeot 
7985718399fSFrançois Tigeot 	drm_mode_object_put(dev, &crtc->base);
7995718399fSFrançois Tigeot 	list_del(&crtc->head);
8005718399fSFrançois Tigeot 	dev->mode_config.num_crtc--;
8015718399fSFrançois Tigeot }
802b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup);
8035718399fSFrançois Tigeot 
8045718399fSFrançois Tigeot /**
8059edbd4a0SFrançois Tigeot  * drm_crtc_index - find the index of a registered CRTC
8069edbd4a0SFrançois Tigeot  * @crtc: CRTC to find index for
8079edbd4a0SFrançois Tigeot  *
8089edbd4a0SFrançois Tigeot  * Given a registered CRTC, return the index of that CRTC within a DRM
8099edbd4a0SFrançois Tigeot  * device's list of CRTCs.
8109edbd4a0SFrançois Tigeot  */
8119edbd4a0SFrançois Tigeot unsigned int drm_crtc_index(struct drm_crtc *crtc)
8129edbd4a0SFrançois Tigeot {
8139edbd4a0SFrançois Tigeot 	unsigned int index = 0;
8149edbd4a0SFrançois Tigeot 	struct drm_crtc *tmp;
8159edbd4a0SFrançois Tigeot 
8169edbd4a0SFrançois Tigeot 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
8179edbd4a0SFrançois Tigeot 		if (tmp == crtc)
8189edbd4a0SFrançois Tigeot 			return index;
8199edbd4a0SFrançois Tigeot 
8209edbd4a0SFrançois Tigeot 		index++;
8219edbd4a0SFrançois Tigeot 	}
8229edbd4a0SFrançois Tigeot 
8239edbd4a0SFrançois Tigeot 	BUG();
8249edbd4a0SFrançois Tigeot }
8259edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_index);
8269edbd4a0SFrançois Tigeot 
8279edbd4a0SFrançois Tigeot /*
8285718399fSFrançois Tigeot  * drm_mode_remove - remove and free a mode
8295718399fSFrançois Tigeot  * @connector: connector list to modify
8305718399fSFrançois Tigeot  * @mode: mode to remove
8315718399fSFrançois Tigeot  *
8325718399fSFrançois Tigeot  * Remove @mode from @connector's mode list, then free it.
8335718399fSFrançois Tigeot  */
8349edbd4a0SFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector,
8355718399fSFrançois Tigeot 			    struct drm_display_mode *mode)
8365718399fSFrançois Tigeot {
8375718399fSFrançois Tigeot 	list_del(&mode->head);
8385718399fSFrançois Tigeot 	drm_mode_destroy(connector->dev, mode);
8395718399fSFrançois Tigeot }
8405718399fSFrançois Tigeot 
8415718399fSFrançois Tigeot /**
8425718399fSFrançois Tigeot  * drm_connector_init - Init a preallocated connector
8435718399fSFrançois Tigeot  * @dev: DRM device
8445718399fSFrançois Tigeot  * @connector: the connector to init
8455718399fSFrançois Tigeot  * @funcs: callbacks for this connector
846a2fdbec6SFrançois Tigeot  * @connector_type: user visible type of the connector
8475718399fSFrançois Tigeot  *
8485718399fSFrançois Tigeot  * Initialises a preallocated connector. Connectors should be
8495718399fSFrançois Tigeot  * subclassed as part of driver connector objects.
8505718399fSFrançois Tigeot  *
851ba55f2f5SFrançois Tigeot  * Returns:
8525718399fSFrançois Tigeot  * Zero on success, error code on failure.
8535718399fSFrançois Tigeot  */
8545718399fSFrançois Tigeot int drm_connector_init(struct drm_device *dev,
8555718399fSFrançois Tigeot 		       struct drm_connector *connector,
8565718399fSFrançois Tigeot 		       const struct drm_connector_funcs *funcs,
8575718399fSFrançois Tigeot 		       int connector_type)
8585718399fSFrançois Tigeot {
8595718399fSFrançois Tigeot 	int ret;
8605718399fSFrançois Tigeot 
8614dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
8625718399fSFrançois Tigeot 
8635718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
8645718399fSFrançois Tigeot 	if (ret)
865ba55f2f5SFrançois Tigeot 		goto out_unlock;
8665718399fSFrançois Tigeot 
867b5162e19SFrançois Tigeot 	connector->base.properties = &connector->properties;
8685718399fSFrançois Tigeot 	connector->dev = dev;
8695718399fSFrançois Tigeot 	connector->funcs = funcs;
8705718399fSFrançois Tigeot 	connector->connector_type = connector_type;
8715718399fSFrançois Tigeot 	connector->connector_type_id =
8725718399fSFrançois Tigeot 		++drm_connector_enum_list[connector_type].count; /* TODO */
8739edbd4a0SFrançois Tigeot 	if (connector->connector_type_id < 0) {
8749edbd4a0SFrançois Tigeot 		ret = connector->connector_type_id;
875ba55f2f5SFrançois Tigeot 		goto out_put;
8769edbd4a0SFrançois Tigeot 	}
877b4efbf42Szrj 	connector->name =
878b4efbf42Szrj 		drm_asprintf(GFP_KERNEL, "%s-%d",
879b4efbf42Szrj 			  drm_connector_enum_list[connector_type].name,
880b4efbf42Szrj 			  connector->connector_type_id);
881b4efbf42Szrj 	if (!connector->name) {
882b4efbf42Szrj 		ret = -ENOMEM;
883b4efbf42Szrj 		goto out_put;
884b4efbf42Szrj 	}
885ba55f2f5SFrançois Tigeot 
8865718399fSFrançois Tigeot 	INIT_LIST_HEAD(&connector->probed_modes);
8875718399fSFrançois Tigeot 	INIT_LIST_HEAD(&connector->modes);
8885718399fSFrançois Tigeot 	connector->edid_blob_ptr = NULL;
889b5162e19SFrançois Tigeot 	connector->status = connector_status_unknown;
8905718399fSFrançois Tigeot 
8915718399fSFrançois Tigeot 	list_add_tail(&connector->head, &dev->mode_config.connector_list);
8925718399fSFrançois Tigeot 	dev->mode_config.num_connector++;
8935718399fSFrançois Tigeot 
894b5162e19SFrançois Tigeot 	if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
895b5162e19SFrançois Tigeot 		drm_object_attach_property(&connector->base,
896b5162e19SFrançois Tigeot 					      dev->mode_config.edid_property,
897b5162e19SFrançois Tigeot 					      0);
8985718399fSFrançois Tigeot 
899b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
9005718399fSFrançois Tigeot 				      dev->mode_config.dpms_property, 0);
9015718399fSFrançois Tigeot 
902*24edb884SFrançois Tigeot 	connector->debugfs_entry = NULL;
903*24edb884SFrançois Tigeot 
904ba55f2f5SFrançois Tigeot out_put:
905ba55f2f5SFrançois Tigeot 	if (ret)
906ba55f2f5SFrançois Tigeot 		drm_mode_object_put(dev, &connector->base);
907ba55f2f5SFrançois Tigeot 
908ba55f2f5SFrançois Tigeot out_unlock:
9094dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
9105718399fSFrançois Tigeot 
9115718399fSFrançois Tigeot 	return ret;
9125718399fSFrançois Tigeot }
913b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_init);
9145718399fSFrançois Tigeot 
9155718399fSFrançois Tigeot /**
9165718399fSFrançois Tigeot  * drm_connector_cleanup - cleans up an initialised connector
9175718399fSFrançois Tigeot  * @connector: connector to cleanup
9185718399fSFrançois Tigeot  *
9195718399fSFrançois Tigeot  * Cleans up the connector but doesn't free the object.
9205718399fSFrançois Tigeot  */
9215718399fSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector)
9225718399fSFrançois Tigeot {
9235718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
9245718399fSFrançois Tigeot 	struct drm_display_mode *mode, *t;
9255718399fSFrançois Tigeot 
9265718399fSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
9275718399fSFrançois Tigeot 		drm_mode_remove(connector, mode);
9285718399fSFrançois Tigeot 
9295718399fSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->modes, head)
9305718399fSFrançois Tigeot 		drm_mode_remove(connector, mode);
9315718399fSFrançois Tigeot 
9325718399fSFrançois Tigeot 	drm_mode_object_put(dev, &connector->base);
933b4efbf42Szrj 	kfree(connector->name);
934b4efbf42Szrj 	connector->name = NULL;
9355718399fSFrançois Tigeot 	list_del(&connector->head);
9365718399fSFrançois Tigeot 	dev->mode_config.num_connector--;
9375718399fSFrançois Tigeot }
938b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup);
939b5162e19SFrançois Tigeot 
940ba55f2f5SFrançois Tigeot /**
941c6f73aabSFrançois Tigeot  * drm_connector_register - register a connector
942c6f73aabSFrançois Tigeot  * @connector: the connector to register
943c6f73aabSFrançois Tigeot  *
944c6f73aabSFrançois Tigeot  * Register userspace interfaces for a connector
945c6f73aabSFrançois Tigeot  *
946c6f73aabSFrançois Tigeot  * Returns:
947c6f73aabSFrançois Tigeot  * Zero on success, error code on failure.
948c6f73aabSFrançois Tigeot  */
949c6f73aabSFrançois Tigeot int drm_connector_register(struct drm_connector *connector)
950c6f73aabSFrançois Tigeot {
951*24edb884SFrançois Tigeot 	int ret;
952*24edb884SFrançois Tigeot 
953*24edb884SFrançois Tigeot 	ret = drm_sysfs_connector_add(connector);
954*24edb884SFrançois Tigeot 	if (ret)
955*24edb884SFrançois Tigeot 		return ret;
956*24edb884SFrançois Tigeot 
957*24edb884SFrançois Tigeot #if 0
958*24edb884SFrançois Tigeot 	ret = drm_debugfs_connector_add(connector);
959*24edb884SFrançois Tigeot #endif
960*24edb884SFrançois Tigeot 	if (ret) {
961*24edb884SFrançois Tigeot 		drm_sysfs_connector_remove(connector);
962*24edb884SFrançois Tigeot 		return ret;
963*24edb884SFrançois Tigeot 	}
964*24edb884SFrançois Tigeot 
965*24edb884SFrançois Tigeot 	return 0;
966c6f73aabSFrançois Tigeot }
967c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_register);
968c6f73aabSFrançois Tigeot 
969c6f73aabSFrançois Tigeot /**
970c6f73aabSFrançois Tigeot  * drm_connector_unregister - unregister a connector
971c6f73aabSFrançois Tigeot  * @connector: the connector to unregister
972c6f73aabSFrançois Tigeot  *
973c6f73aabSFrançois Tigeot  * Unregister userspace interfaces for a connector
974c6f73aabSFrançois Tigeot  */
975c6f73aabSFrançois Tigeot void drm_connector_unregister(struct drm_connector *connector)
976c6f73aabSFrançois Tigeot {
977c6f73aabSFrançois Tigeot 	drm_sysfs_connector_remove(connector);
978*24edb884SFrançois Tigeot #if 0
979*24edb884SFrançois Tigeot 	drm_debugfs_connector_remove(connector);
980*24edb884SFrançois Tigeot #endif
981c6f73aabSFrançois Tigeot }
982c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister);
983c6f73aabSFrançois Tigeot 
984c6f73aabSFrançois Tigeot 
985c6f73aabSFrançois Tigeot /**
986ba55f2f5SFrançois Tigeot  * drm_connector_unplug_all - unregister connector userspace interfaces
987ba55f2f5SFrançois Tigeot  * @dev: drm device
988ba55f2f5SFrançois Tigeot  *
989ba55f2f5SFrançois Tigeot  * This function unregisters all connector userspace interfaces in sysfs. Should
990ba55f2f5SFrançois Tigeot  * be call when the device is disconnected, e.g. from an usb driver's
991ba55f2f5SFrançois Tigeot  * ->disconnect callback.
992ba55f2f5SFrançois Tigeot  */
993b5162e19SFrançois Tigeot void drm_connector_unplug_all(struct drm_device *dev)
994b5162e19SFrançois Tigeot {
995b5162e19SFrançois Tigeot 	struct drm_connector *connector;
996b5162e19SFrançois Tigeot 
997b5162e19SFrançois Tigeot 	/* taking the mode config mutex ends up in a clash with sysfs */
998b5162e19SFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
999c6f73aabSFrançois Tigeot 		drm_connector_unregister(connector);
10004dbb207bSFrançois Tigeot 
1001b5162e19SFrançois Tigeot }
1002b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_unplug_all);
10035718399fSFrançois Tigeot 
1004ba55f2f5SFrançois Tigeot /**
1005ba55f2f5SFrançois Tigeot  * drm_bridge_init - initialize a drm transcoder/bridge
1006ba55f2f5SFrançois Tigeot  * @dev: drm device
1007ba55f2f5SFrançois Tigeot  * @bridge: transcoder/bridge to set up
1008ba55f2f5SFrançois Tigeot  * @funcs: bridge function table
1009ba55f2f5SFrançois Tigeot  *
1010ba55f2f5SFrançois Tigeot  * Initialises a preallocated bridge. Bridges should be
1011ba55f2f5SFrançois Tigeot  * subclassed as part of driver connector objects.
1012ba55f2f5SFrançois Tigeot  *
1013ba55f2f5SFrançois Tigeot  * Returns:
1014ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
1015ba55f2f5SFrançois Tigeot  */
1016ba55f2f5SFrançois Tigeot int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
1017ba55f2f5SFrançois Tigeot 		const struct drm_bridge_funcs *funcs)
1018ba55f2f5SFrançois Tigeot {
1019ba55f2f5SFrançois Tigeot 	int ret;
1020ba55f2f5SFrançois Tigeot 
1021ba55f2f5SFrançois Tigeot 	drm_modeset_lock_all(dev);
1022ba55f2f5SFrançois Tigeot 
1023ba55f2f5SFrançois Tigeot 	ret = drm_mode_object_get(dev, &bridge->base, DRM_MODE_OBJECT_BRIDGE);
1024ba55f2f5SFrançois Tigeot 	if (ret)
1025ba55f2f5SFrançois Tigeot 		goto out;
1026ba55f2f5SFrançois Tigeot 
1027ba55f2f5SFrançois Tigeot 	bridge->dev = dev;
1028ba55f2f5SFrançois Tigeot 	bridge->funcs = funcs;
1029ba55f2f5SFrançois Tigeot 
1030ba55f2f5SFrançois Tigeot 	list_add_tail(&bridge->head, &dev->mode_config.bridge_list);
1031ba55f2f5SFrançois Tigeot 	dev->mode_config.num_bridge++;
1032ba55f2f5SFrançois Tigeot 
1033ba55f2f5SFrançois Tigeot  out:
1034ba55f2f5SFrançois Tigeot 	drm_modeset_unlock_all(dev);
1035ba55f2f5SFrançois Tigeot 	return ret;
1036ba55f2f5SFrançois Tigeot }
1037ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_bridge_init);
1038ba55f2f5SFrançois Tigeot 
1039ba55f2f5SFrançois Tigeot /**
1040ba55f2f5SFrançois Tigeot  * drm_bridge_cleanup - cleans up an initialised bridge
1041ba55f2f5SFrançois Tigeot  * @bridge: bridge to cleanup
1042ba55f2f5SFrançois Tigeot  *
1043ba55f2f5SFrançois Tigeot  * Cleans up the bridge but doesn't free the object.
1044ba55f2f5SFrançois Tigeot  */
1045ba55f2f5SFrançois Tigeot void drm_bridge_cleanup(struct drm_bridge *bridge)
1046ba55f2f5SFrançois Tigeot {
1047ba55f2f5SFrançois Tigeot 	struct drm_device *dev = bridge->dev;
1048ba55f2f5SFrançois Tigeot 
1049ba55f2f5SFrançois Tigeot 	drm_modeset_lock_all(dev);
1050ba55f2f5SFrançois Tigeot 	drm_mode_object_put(dev, &bridge->base);
1051ba55f2f5SFrançois Tigeot 	list_del(&bridge->head);
1052ba55f2f5SFrançois Tigeot 	dev->mode_config.num_bridge--;
1053ba55f2f5SFrançois Tigeot 	drm_modeset_unlock_all(dev);
1054ba55f2f5SFrançois Tigeot }
1055ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_bridge_cleanup);
1056ba55f2f5SFrançois Tigeot 
1057ba55f2f5SFrançois Tigeot /**
1058ba55f2f5SFrançois Tigeot  * drm_encoder_init - Init a preallocated encoder
1059ba55f2f5SFrançois Tigeot  * @dev: drm device
1060ba55f2f5SFrançois Tigeot  * @encoder: the encoder to init
1061ba55f2f5SFrançois Tigeot  * @funcs: callbacks for this encoder
1062ba55f2f5SFrançois Tigeot  * @encoder_type: user visible type of the encoder
1063ba55f2f5SFrançois Tigeot  *
1064ba55f2f5SFrançois Tigeot  * Initialises a preallocated encoder. Encoder should be
1065ba55f2f5SFrançois Tigeot  * subclassed as part of driver encoder objects.
1066ba55f2f5SFrançois Tigeot  *
1067ba55f2f5SFrançois Tigeot  * Returns:
1068ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
1069ba55f2f5SFrançois Tigeot  */
10705718399fSFrançois Tigeot int drm_encoder_init(struct drm_device *dev,
10715718399fSFrançois Tigeot 		      struct drm_encoder *encoder,
10725718399fSFrançois Tigeot 		      const struct drm_encoder_funcs *funcs,
10735718399fSFrançois Tigeot 		      int encoder_type)
10745718399fSFrançois Tigeot {
10755718399fSFrançois Tigeot 	int ret;
10765718399fSFrançois Tigeot 
10774dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
10785718399fSFrançois Tigeot 
10795718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
10805718399fSFrançois Tigeot 	if (ret)
1081ba55f2f5SFrançois Tigeot 		goto out_unlock;
10825718399fSFrançois Tigeot 
10835718399fSFrançois Tigeot 	encoder->dev = dev;
10845718399fSFrançois Tigeot 	encoder->encoder_type = encoder_type;
10855718399fSFrançois Tigeot 	encoder->funcs = funcs;
1086b4efbf42Szrj 	encoder->name = drm_asprintf(GFP_KERNEL, "%s-%d",
1087b4efbf42Szrj 				  drm_encoder_enum_list[encoder_type].name,
1088b4efbf42Szrj 				  encoder->base.id);
1089b4efbf42Szrj 	if (!encoder->name) {
1090b4efbf42Szrj 		ret = -ENOMEM;
1091b4efbf42Szrj 		goto out_put;
1092b4efbf42Szrj 	}
10935718399fSFrançois Tigeot 
10945718399fSFrançois Tigeot 	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
10955718399fSFrançois Tigeot 	dev->mode_config.num_encoder++;
10965718399fSFrançois Tigeot 
1097b4efbf42Szrj out_put:
1098b4efbf42Szrj 	if (ret)
1099b4efbf42Szrj 		drm_mode_object_put(dev, &encoder->base);
1100b4efbf42Szrj 
1101ba55f2f5SFrançois Tigeot out_unlock:
11024dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
11035718399fSFrançois Tigeot 
11045718399fSFrançois Tigeot 	return ret;
11055718399fSFrançois Tigeot }
1106b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_init);
11075718399fSFrançois Tigeot 
1108ba55f2f5SFrançois Tigeot /**
1109ba55f2f5SFrançois Tigeot  * drm_encoder_cleanup - cleans up an initialised encoder
1110ba55f2f5SFrançois Tigeot  * @encoder: encoder to cleanup
1111ba55f2f5SFrançois Tigeot  *
1112ba55f2f5SFrançois Tigeot  * Cleans up the encoder but doesn't free the object.
1113ba55f2f5SFrançois Tigeot  */
11145718399fSFrançois Tigeot void drm_encoder_cleanup(struct drm_encoder *encoder)
11155718399fSFrançois Tigeot {
11165718399fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
11174dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
11185718399fSFrançois Tigeot 	drm_mode_object_put(dev, &encoder->base);
1119ba55f2f5SFrançois Tigeot 	kfree(encoder->name);
1120ba55f2f5SFrançois Tigeot 	encoder->name = NULL;
11215718399fSFrançois Tigeot 	list_del(&encoder->head);
11225718399fSFrançois Tigeot 	dev->mode_config.num_encoder--;
11234dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
11245718399fSFrançois Tigeot }
1125b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_cleanup);
11265718399fSFrançois Tigeot 
112706fede5aSFrançois Tigeot /**
1128ba55f2f5SFrançois Tigeot  * drm_universal_plane_init - Initialize a new universal plane object
112906fede5aSFrançois Tigeot  * @dev: DRM device
113006fede5aSFrançois Tigeot  * @plane: plane object to init
113106fede5aSFrançois Tigeot  * @possible_crtcs: bitmask of possible CRTCs
113206fede5aSFrançois Tigeot  * @funcs: callbacks for the new plane
113306fede5aSFrançois Tigeot  * @formats: array of supported formats (%DRM_FORMAT_*)
113406fede5aSFrançois Tigeot  * @format_count: number of elements in @formats
1135ba55f2f5SFrançois Tigeot  * @type: type of plane (overlay, primary, cursor)
113606fede5aSFrançois Tigeot  *
1137ba55f2f5SFrançois Tigeot  * Initializes a plane object of type @type.
113806fede5aSFrançois Tigeot  *
1139ba55f2f5SFrançois Tigeot  * Returns:
114006fede5aSFrançois Tigeot  * Zero on success, error code on failure.
114106fede5aSFrançois Tigeot  */
1142ba55f2f5SFrançois Tigeot int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
11435718399fSFrançois Tigeot 			     unsigned long possible_crtcs,
11445718399fSFrançois Tigeot 			     const struct drm_plane_funcs *funcs,
11455718399fSFrançois Tigeot 			     const uint32_t *formats, uint32_t format_count,
1146ba55f2f5SFrançois Tigeot 			     enum drm_plane_type type)
11475718399fSFrançois Tigeot {
11485718399fSFrançois Tigeot 	int ret;
11495718399fSFrançois Tigeot 
11504dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
11515718399fSFrançois Tigeot 
11525718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
11535718399fSFrançois Tigeot 	if (ret)
11545718399fSFrançois Tigeot 		goto out;
11555718399fSFrançois Tigeot 
1156b5162e19SFrançois Tigeot 	plane->base.properties = &plane->properties;
11575718399fSFrançois Tigeot 	plane->dev = dev;
11585718399fSFrançois Tigeot 	plane->funcs = funcs;
11595718399fSFrançois Tigeot 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
11605a3b77d5SFrançois Tigeot 				      M_DRM, M_WAITOK);
1161b5162e19SFrançois Tigeot 	if (!plane->format_types) {
1162b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("out of memory when allocating plane\n");
1163b5162e19SFrançois Tigeot 		drm_mode_object_put(dev, &plane->base);
1164b5162e19SFrançois Tigeot 		ret = -ENOMEM;
1165b5162e19SFrançois Tigeot 		goto out;
1166b5162e19SFrançois Tigeot 	}
11675718399fSFrançois Tigeot 
11685718399fSFrançois Tigeot 	memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
11695718399fSFrançois Tigeot 	plane->format_count = format_count;
11705718399fSFrançois Tigeot 	plane->possible_crtcs = possible_crtcs;
1171ba55f2f5SFrançois Tigeot 	plane->type = type;
11725718399fSFrançois Tigeot 
11735718399fSFrançois Tigeot 	list_add_tail(&plane->head, &dev->mode_config.plane_list);
1174ba55f2f5SFrançois Tigeot 	dev->mode_config.num_total_plane++;
1175ba55f2f5SFrançois Tigeot 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
1176ba55f2f5SFrançois Tigeot 		dev->mode_config.num_overlay_plane++;
1177ba55f2f5SFrançois Tigeot 
1178ba55f2f5SFrançois Tigeot 	drm_object_attach_property(&plane->base,
1179ba55f2f5SFrançois Tigeot 				   dev->mode_config.plane_type_property,
1180ba55f2f5SFrançois Tigeot 				   plane->type);
11815718399fSFrançois Tigeot 
11825718399fSFrançois Tigeot  out:
11834dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
11845718399fSFrançois Tigeot 
11855718399fSFrançois Tigeot 	return ret;
11865718399fSFrançois Tigeot }
1187ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_universal_plane_init);
1188ba55f2f5SFrançois Tigeot 
1189ba55f2f5SFrançois Tigeot /**
1190ba55f2f5SFrançois Tigeot  * drm_plane_init - Initialize a legacy plane
1191ba55f2f5SFrançois Tigeot  * @dev: DRM device
1192ba55f2f5SFrançois Tigeot  * @plane: plane object to init
1193ba55f2f5SFrançois Tigeot  * @possible_crtcs: bitmask of possible CRTCs
1194ba55f2f5SFrançois Tigeot  * @funcs: callbacks for the new plane
1195ba55f2f5SFrançois Tigeot  * @formats: array of supported formats (%DRM_FORMAT_*)
1196ba55f2f5SFrançois Tigeot  * @format_count: number of elements in @formats
1197ba55f2f5SFrançois Tigeot  * @is_primary: plane type (primary vs overlay)
1198ba55f2f5SFrançois Tigeot  *
1199ba55f2f5SFrançois Tigeot  * Legacy API to initialize a DRM plane.
1200ba55f2f5SFrançois Tigeot  *
1201ba55f2f5SFrançois Tigeot  * New drivers should call drm_universal_plane_init() instead.
1202ba55f2f5SFrançois Tigeot  *
1203ba55f2f5SFrançois Tigeot  * Returns:
1204ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
1205ba55f2f5SFrançois Tigeot  */
1206ba55f2f5SFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
1207ba55f2f5SFrançois Tigeot 		   unsigned long possible_crtcs,
1208ba55f2f5SFrançois Tigeot 		   const struct drm_plane_funcs *funcs,
1209ba55f2f5SFrançois Tigeot 		   const uint32_t *formats, uint32_t format_count,
1210ba55f2f5SFrançois Tigeot 		   bool is_primary)
1211ba55f2f5SFrançois Tigeot {
1212ba55f2f5SFrançois Tigeot 	enum drm_plane_type type;
1213ba55f2f5SFrançois Tigeot 
1214ba55f2f5SFrançois Tigeot 	type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
1215ba55f2f5SFrançois Tigeot 	return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
1216ba55f2f5SFrançois Tigeot 					formats, format_count, type);
1217ba55f2f5SFrançois Tigeot }
1218b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_init);
12195718399fSFrançois Tigeot 
122006fede5aSFrançois Tigeot /**
122106fede5aSFrançois Tigeot  * drm_plane_cleanup - Clean up the core plane usage
122206fede5aSFrançois Tigeot  * @plane: plane to cleanup
122306fede5aSFrançois Tigeot  *
122406fede5aSFrançois Tigeot  * This function cleans up @plane and removes it from the DRM mode setting
122506fede5aSFrançois Tigeot  * core. Note that the function does *not* free the plane structure itself,
122606fede5aSFrançois Tigeot  * this is the responsibility of the caller.
122706fede5aSFrançois Tigeot  */
12285718399fSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane)
12295718399fSFrançois Tigeot {
12305718399fSFrançois Tigeot 	struct drm_device *dev = plane->dev;
12315718399fSFrançois Tigeot 
12324dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
12334dbb207bSFrançois Tigeot 	kfree(plane->format_types);
12345718399fSFrançois Tigeot 	drm_mode_object_put(dev, &plane->base);
1235ba55f2f5SFrançois Tigeot 
1236ba55f2f5SFrançois Tigeot 	BUG_ON(list_empty(&plane->head));
1237ba55f2f5SFrançois Tigeot 
12385718399fSFrançois Tigeot 	list_del(&plane->head);
1239ba55f2f5SFrançois Tigeot 	dev->mode_config.num_total_plane--;
1240ba55f2f5SFrançois Tigeot 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
1241ba55f2f5SFrançois Tigeot 		dev->mode_config.num_overlay_plane--;
12424dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
12435718399fSFrançois Tigeot }
1244b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup);
12455718399fSFrançois Tigeot 
12465718399fSFrançois Tigeot /**
124706fede5aSFrançois Tigeot  * drm_plane_force_disable - Forcibly disable a plane
124806fede5aSFrançois Tigeot  * @plane: plane to disable
124906fede5aSFrançois Tigeot  *
125006fede5aSFrançois Tigeot  * Forces the plane to be disabled.
125106fede5aSFrançois Tigeot  *
125206fede5aSFrançois Tigeot  * Used when the plane's current framebuffer is destroyed,
125306fede5aSFrançois Tigeot  * and when restoring fbdev mode.
125406fede5aSFrançois Tigeot  */
125506fede5aSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane)
125606fede5aSFrançois Tigeot {
1257ba55f2f5SFrançois Tigeot 	struct drm_framebuffer *old_fb = plane->fb;
125806fede5aSFrançois Tigeot 	int ret;
125906fede5aSFrançois Tigeot 
1260ba55f2f5SFrançois Tigeot 	if (!old_fb)
126106fede5aSFrançois Tigeot 		return;
126206fede5aSFrançois Tigeot 
126306fede5aSFrançois Tigeot 	ret = plane->funcs->disable_plane(plane);
1264ba55f2f5SFrançois Tigeot 	if (ret) {
126506fede5aSFrançois Tigeot 		DRM_ERROR("failed to disable plane with busy fb\n");
1266ba55f2f5SFrançois Tigeot 		return;
1267ba55f2f5SFrançois Tigeot 	}
126806fede5aSFrançois Tigeot 	/* disconnect the plane from the fb and crtc: */
1269ba55f2f5SFrançois Tigeot 	__drm_framebuffer_unreference(old_fb);
127006fede5aSFrançois Tigeot 	plane->fb = NULL;
127106fede5aSFrançois Tigeot 	plane->crtc = NULL;
127206fede5aSFrançois Tigeot }
127306fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable);
127406fede5aSFrançois Tigeot 
12755718399fSFrançois Tigeot static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
12765718399fSFrançois Tigeot {
12775718399fSFrançois Tigeot 	struct drm_property *edid;
12785718399fSFrançois Tigeot 	struct drm_property *dpms;
1279*24edb884SFrançois Tigeot 	struct drm_property *dev_path;
12805718399fSFrançois Tigeot 
12815718399fSFrançois Tigeot 	/*
12825718399fSFrançois Tigeot 	 * Standard properties (apply to all connectors)
12835718399fSFrançois Tigeot 	 */
12845718399fSFrançois Tigeot 	edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
12855718399fSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
12865718399fSFrançois Tigeot 				   "EDID", 0);
12875718399fSFrançois Tigeot 	dev->mode_config.edid_property = edid;
12885718399fSFrançois Tigeot 
12895718399fSFrançois Tigeot 	dpms = drm_property_create_enum(dev, 0,
12905718399fSFrançois Tigeot 				   "DPMS", drm_dpms_enum_list,
1291b5162e19SFrançois Tigeot 				   ARRAY_SIZE(drm_dpms_enum_list));
12925718399fSFrançois Tigeot 	dev->mode_config.dpms_property = dpms;
12935718399fSFrançois Tigeot 
1294*24edb884SFrançois Tigeot 	dev_path = drm_property_create(dev,
1295*24edb884SFrançois Tigeot 				       DRM_MODE_PROP_BLOB |
1296*24edb884SFrançois Tigeot 				       DRM_MODE_PROP_IMMUTABLE,
1297*24edb884SFrançois Tigeot 				       "PATH", 0);
1298*24edb884SFrançois Tigeot 	dev->mode_config.path_property = dev_path;
1299*24edb884SFrançois Tigeot 
13005718399fSFrançois Tigeot 	return 0;
13015718399fSFrançois Tigeot }
13025718399fSFrançois Tigeot 
1303ba55f2f5SFrançois Tigeot static int drm_mode_create_standard_plane_properties(struct drm_device *dev)
1304ba55f2f5SFrançois Tigeot {
1305ba55f2f5SFrançois Tigeot 	struct drm_property *type;
1306ba55f2f5SFrançois Tigeot 
1307ba55f2f5SFrançois Tigeot 	/*
1308ba55f2f5SFrançois Tigeot 	 * Standard properties (apply to all planes)
1309ba55f2f5SFrançois Tigeot 	 */
1310ba55f2f5SFrançois Tigeot 	type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
1311ba55f2f5SFrançois Tigeot 					"type", drm_plane_type_enum_list,
1312ba55f2f5SFrançois Tigeot 					ARRAY_SIZE(drm_plane_type_enum_list));
1313ba55f2f5SFrançois Tigeot 	dev->mode_config.plane_type_property = type;
1314ba55f2f5SFrançois Tigeot 
1315ba55f2f5SFrançois Tigeot 	return 0;
1316ba55f2f5SFrançois Tigeot }
1317ba55f2f5SFrançois Tigeot 
13185718399fSFrançois Tigeot /**
13195718399fSFrançois Tigeot  * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
13205718399fSFrançois Tigeot  * @dev: DRM device
13215718399fSFrançois Tigeot  *
13225718399fSFrançois Tigeot  * Called by a driver the first time a DVI-I connector is made.
13235718399fSFrançois Tigeot  */
13245718399fSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev)
13255718399fSFrançois Tigeot {
13265718399fSFrançois Tigeot 	struct drm_property *dvi_i_selector;
13275718399fSFrançois Tigeot 	struct drm_property *dvi_i_subconnector;
13285718399fSFrançois Tigeot 
13295718399fSFrançois Tigeot 	if (dev->mode_config.dvi_i_select_subconnector_property)
13305718399fSFrançois Tigeot 		return 0;
13315718399fSFrançois Tigeot 
13325718399fSFrançois Tigeot 	dvi_i_selector =
13335718399fSFrançois Tigeot 		drm_property_create_enum(dev, 0,
13345718399fSFrançois Tigeot 				    "select subconnector",
13355718399fSFrançois Tigeot 				    drm_dvi_i_select_enum_list,
1336b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_select_enum_list));
13375718399fSFrançois Tigeot 	dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
13385718399fSFrançois Tigeot 
13395718399fSFrançois Tigeot 	dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
13405718399fSFrançois Tigeot 				    "subconnector",
13415718399fSFrançois Tigeot 				    drm_dvi_i_subconnector_enum_list,
1342b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
13435718399fSFrançois Tigeot 	dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
13445718399fSFrançois Tigeot 
13455718399fSFrançois Tigeot 	return 0;
13465718399fSFrançois Tigeot }
1347b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
13485718399fSFrançois Tigeot 
13495718399fSFrançois Tigeot /**
13505718399fSFrançois Tigeot  * drm_create_tv_properties - create TV specific connector properties
13515718399fSFrançois Tigeot  * @dev: DRM device
13525718399fSFrançois Tigeot  * @num_modes: number of different TV formats (modes) supported
13535718399fSFrançois Tigeot  * @modes: array of pointers to strings containing name of each format
13545718399fSFrançois Tigeot  *
13555718399fSFrançois Tigeot  * Called by a driver's TV initialization routine, this function creates
13565718399fSFrançois Tigeot  * the TV specific connector properties for a given device.  Caller is
13575718399fSFrançois Tigeot  * responsible for allocating a list of format names and passing them to
13585718399fSFrançois Tigeot  * this routine.
13595718399fSFrançois Tigeot  */
13605718399fSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
13615718399fSFrançois Tigeot 				  char *modes[])
13625718399fSFrançois Tigeot {
13635718399fSFrançois Tigeot 	struct drm_property *tv_selector;
13645718399fSFrançois Tigeot 	struct drm_property *tv_subconnector;
13655718399fSFrançois Tigeot 	int i;
13665718399fSFrançois Tigeot 
13675718399fSFrançois Tigeot 	if (dev->mode_config.tv_select_subconnector_property)
13685718399fSFrançois Tigeot 		return 0;
13695718399fSFrançois Tigeot 
13705718399fSFrançois Tigeot 	/*
13715718399fSFrançois Tigeot 	 * Basic connector properties
13725718399fSFrançois Tigeot 	 */
13735718399fSFrançois Tigeot 	tv_selector = drm_property_create_enum(dev, 0,
13745718399fSFrançois Tigeot 					  "select subconnector",
13755718399fSFrançois Tigeot 					  drm_tv_select_enum_list,
1376b5162e19SFrançois Tigeot 					  ARRAY_SIZE(drm_tv_select_enum_list));
13775718399fSFrançois Tigeot 	dev->mode_config.tv_select_subconnector_property = tv_selector;
13785718399fSFrançois Tigeot 
13795718399fSFrançois Tigeot 	tv_subconnector =
13805718399fSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
13815718399fSFrançois Tigeot 				    "subconnector",
13825718399fSFrançois Tigeot 				    drm_tv_subconnector_enum_list,
1383b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_tv_subconnector_enum_list));
13845718399fSFrançois Tigeot 	dev->mode_config.tv_subconnector_property = tv_subconnector;
13855718399fSFrançois Tigeot 
13865718399fSFrançois Tigeot 	/*
13875718399fSFrançois Tigeot 	 * Other, TV specific properties: margins & TV modes.
13885718399fSFrançois Tigeot 	 */
13895718399fSFrançois Tigeot 	dev->mode_config.tv_left_margin_property =
13905718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "left margin", 0, 100);
13915718399fSFrançois Tigeot 
13925718399fSFrançois Tigeot 	dev->mode_config.tv_right_margin_property =
13935718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "right margin", 0, 100);
13945718399fSFrançois Tigeot 
13955718399fSFrançois Tigeot 	dev->mode_config.tv_top_margin_property =
13965718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "top margin", 0, 100);
13975718399fSFrançois Tigeot 
13985718399fSFrançois Tigeot 	dev->mode_config.tv_bottom_margin_property =
13995718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "bottom margin", 0, 100);
14005718399fSFrançois Tigeot 
14015718399fSFrançois Tigeot 	dev->mode_config.tv_mode_property =
14025718399fSFrançois Tigeot 		drm_property_create(dev, DRM_MODE_PROP_ENUM,
14035718399fSFrançois Tigeot 				    "mode", num_modes);
14045718399fSFrançois Tigeot 	for (i = 0; i < num_modes; i++)
14055718399fSFrançois Tigeot 		drm_property_add_enum(dev->mode_config.tv_mode_property, i,
14065718399fSFrançois Tigeot 				      i, modes[i]);
14075718399fSFrançois Tigeot 
14085718399fSFrançois Tigeot 	dev->mode_config.tv_brightness_property =
14095718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "brightness", 0, 100);
14105718399fSFrançois Tigeot 
14115718399fSFrançois Tigeot 	dev->mode_config.tv_contrast_property =
14125718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "contrast", 0, 100);
14135718399fSFrançois Tigeot 
14145718399fSFrançois Tigeot 	dev->mode_config.tv_flicker_reduction_property =
14155718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
14165718399fSFrançois Tigeot 
14175718399fSFrançois Tigeot 	dev->mode_config.tv_overscan_property =
14185718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "overscan", 0, 100);
14195718399fSFrançois Tigeot 
14205718399fSFrançois Tigeot 	dev->mode_config.tv_saturation_property =
14215718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "saturation", 0, 100);
14225718399fSFrançois Tigeot 
14235718399fSFrançois Tigeot 	dev->mode_config.tv_hue_property =
14245718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "hue", 0, 100);
14255718399fSFrançois Tigeot 
14265718399fSFrançois Tigeot 	return 0;
14275718399fSFrançois Tigeot }
1428b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties);
14295718399fSFrançois Tigeot 
14305718399fSFrançois Tigeot /**
14315718399fSFrançois Tigeot  * drm_mode_create_scaling_mode_property - create scaling mode property
14325718399fSFrançois Tigeot  * @dev: DRM device
14335718399fSFrançois Tigeot  *
14345718399fSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
14355718399fSFrançois Tigeot  * connectors.
14365718399fSFrançois Tigeot  */
14375718399fSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev)
14385718399fSFrançois Tigeot {
14395718399fSFrançois Tigeot 	struct drm_property *scaling_mode;
14405718399fSFrançois Tigeot 
14415718399fSFrançois Tigeot 	if (dev->mode_config.scaling_mode_property)
14425718399fSFrançois Tigeot 		return 0;
14435718399fSFrançois Tigeot 
14445718399fSFrançois Tigeot 	scaling_mode =
14455718399fSFrançois Tigeot 		drm_property_create_enum(dev, 0, "scaling mode",
14465718399fSFrançois Tigeot 				drm_scaling_mode_enum_list,
1447b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_scaling_mode_enum_list));
14485718399fSFrançois Tigeot 
14495718399fSFrançois Tigeot 	dev->mode_config.scaling_mode_property = scaling_mode;
14505718399fSFrançois Tigeot 
14515718399fSFrançois Tigeot 	return 0;
14525718399fSFrançois Tigeot }
1453b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
14545718399fSFrançois Tigeot 
14555718399fSFrançois Tigeot /**
1456*24edb884SFrançois Tigeot  * drm_mode_create_aspect_ratio_property - create aspect ratio property
1457*24edb884SFrançois Tigeot  * @dev: DRM device
1458*24edb884SFrançois Tigeot  *
1459*24edb884SFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
1460*24edb884SFrançois Tigeot  * connectors.
1461*24edb884SFrançois Tigeot  *
1462*24edb884SFrançois Tigeot  * Returns:
1463*24edb884SFrançois Tigeot  * Zero on success, errno on failure.
1464*24edb884SFrançois Tigeot  */
1465*24edb884SFrançois Tigeot int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
1466*24edb884SFrançois Tigeot {
1467*24edb884SFrançois Tigeot 	if (dev->mode_config.aspect_ratio_property)
1468*24edb884SFrançois Tigeot 		return 0;
1469*24edb884SFrançois Tigeot 
1470*24edb884SFrançois Tigeot 	dev->mode_config.aspect_ratio_property =
1471*24edb884SFrançois Tigeot 		drm_property_create_enum(dev, 0, "aspect ratio",
1472*24edb884SFrançois Tigeot 				drm_aspect_ratio_enum_list,
1473*24edb884SFrançois Tigeot 				ARRAY_SIZE(drm_aspect_ratio_enum_list));
1474*24edb884SFrançois Tigeot 
1475*24edb884SFrançois Tigeot 	if (dev->mode_config.aspect_ratio_property == NULL)
1476*24edb884SFrançois Tigeot 		return -ENOMEM;
1477*24edb884SFrançois Tigeot 
1478*24edb884SFrançois Tigeot 	return 0;
1479*24edb884SFrançois Tigeot }
1480*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
1481*24edb884SFrançois Tigeot 
1482*24edb884SFrançois Tigeot /**
14835718399fSFrançois Tigeot  * drm_mode_create_dirty_property - create dirty property
14845718399fSFrançois Tigeot  * @dev: DRM device
14855718399fSFrançois Tigeot  *
14865718399fSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
14875718399fSFrançois Tigeot  * connectors.
14885718399fSFrançois Tigeot  */
14895718399fSFrançois Tigeot int drm_mode_create_dirty_info_property(struct drm_device *dev)
14905718399fSFrançois Tigeot {
14915718399fSFrançois Tigeot 	struct drm_property *dirty_info;
14925718399fSFrançois Tigeot 
14935718399fSFrançois Tigeot 	if (dev->mode_config.dirty_info_property)
14945718399fSFrançois Tigeot 		return 0;
14955718399fSFrançois Tigeot 
14965718399fSFrançois Tigeot 	dirty_info =
14975718399fSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
14985718399fSFrançois Tigeot 				    "dirty",
14995718399fSFrançois Tigeot 				    drm_dirty_info_enum_list,
1500b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dirty_info_enum_list));
15015718399fSFrançois Tigeot 	dev->mode_config.dirty_info_property = dirty_info;
15025718399fSFrançois Tigeot 
15035718399fSFrançois Tigeot 	return 0;
15045718399fSFrançois Tigeot }
1505b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
15065718399fSFrançois Tigeot 
1507b5162e19SFrançois Tigeot static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
15085718399fSFrançois Tigeot {
15095718399fSFrançois Tigeot 	uint32_t total_objects = 0;
15105718399fSFrançois Tigeot 
15115718399fSFrançois Tigeot 	total_objects += dev->mode_config.num_crtc;
15125718399fSFrançois Tigeot 	total_objects += dev->mode_config.num_connector;
15135718399fSFrançois Tigeot 	total_objects += dev->mode_config.num_encoder;
1514ba55f2f5SFrançois Tigeot 	total_objects += dev->mode_config.num_bridge;
15155718399fSFrançois Tigeot 
15164dbb207bSFrançois Tigeot 	group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
1517b5162e19SFrançois Tigeot 	if (!group->id_list)
1518b5162e19SFrançois Tigeot 		return -ENOMEM;
15195718399fSFrançois Tigeot 
15205718399fSFrançois Tigeot 	group->num_crtcs = 0;
15215718399fSFrançois Tigeot 	group->num_connectors = 0;
15225718399fSFrançois Tigeot 	group->num_encoders = 0;
1523ba55f2f5SFrançois Tigeot 	group->num_bridges = 0;
15245718399fSFrançois Tigeot 	return 0;
15255718399fSFrançois Tigeot }
15265718399fSFrançois Tigeot 
1527ba55f2f5SFrançois Tigeot void drm_mode_group_destroy(struct drm_mode_group *group)
1528ba55f2f5SFrançois Tigeot {
1529ba55f2f5SFrançois Tigeot 	kfree(group->id_list);
1530ba55f2f5SFrançois Tigeot 	group->id_list = NULL;
1531ba55f2f5SFrançois Tigeot }
1532ba55f2f5SFrançois Tigeot 
1533ba55f2f5SFrançois Tigeot /*
1534ba55f2f5SFrançois Tigeot  * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is
1535ba55f2f5SFrançois Tigeot  * the drm core's responsibility to set up mode control groups.
1536ba55f2f5SFrançois Tigeot  */
15375718399fSFrançois Tigeot int drm_mode_group_init_legacy_group(struct drm_device *dev,
15385718399fSFrançois Tigeot 				     struct drm_mode_group *group)
15395718399fSFrançois Tigeot {
15405718399fSFrançois Tigeot 	struct drm_crtc *crtc;
15415718399fSFrançois Tigeot 	struct drm_encoder *encoder;
15425718399fSFrançois Tigeot 	struct drm_connector *connector;
1543ba55f2f5SFrançois Tigeot 	struct drm_bridge *bridge;
15445718399fSFrançois Tigeot 	int ret;
15455718399fSFrançois Tigeot 
15465718399fSFrançois Tigeot 	if ((ret = drm_mode_group_init(dev, group)))
15475718399fSFrançois Tigeot 		return ret;
15485718399fSFrançois Tigeot 
15495718399fSFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
15505718399fSFrançois Tigeot 		group->id_list[group->num_crtcs++] = crtc->base.id;
15515718399fSFrançois Tigeot 
15525718399fSFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
15535718399fSFrançois Tigeot 		group->id_list[group->num_crtcs + group->num_encoders++] =
15545718399fSFrançois Tigeot 		encoder->base.id;
15555718399fSFrançois Tigeot 
15565718399fSFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
15575718399fSFrançois Tigeot 		group->id_list[group->num_crtcs + group->num_encoders +
15585718399fSFrançois Tigeot 			       group->num_connectors++] = connector->base.id;
15595718399fSFrançois Tigeot 
1560ba55f2f5SFrançois Tigeot 	list_for_each_entry(bridge, &dev->mode_config.bridge_list, head)
1561ba55f2f5SFrançois Tigeot 		group->id_list[group->num_crtcs + group->num_encoders +
1562ba55f2f5SFrançois Tigeot 			       group->num_connectors + group->num_bridges++] =
1563ba55f2f5SFrançois Tigeot 					bridge->base.id;
1564ba55f2f5SFrançois Tigeot 
15655718399fSFrançois Tigeot 	return 0;
15665718399fSFrançois Tigeot }
1567b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
15685718399fSFrançois Tigeot 
1569*24edb884SFrançois Tigeot void drm_reinit_primary_mode_group(struct drm_device *dev)
1570*24edb884SFrançois Tigeot {
1571*24edb884SFrançois Tigeot 	drm_modeset_lock_all(dev);
1572*24edb884SFrançois Tigeot 	drm_mode_group_destroy(&dev->primary->mode_group);
1573*24edb884SFrançois Tigeot 	drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
1574*24edb884SFrançois Tigeot 	drm_modeset_unlock_all(dev);
1575*24edb884SFrançois Tigeot }
1576*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_reinit_primary_mode_group);
1577*24edb884SFrançois Tigeot 
15785718399fSFrançois Tigeot /**
15795718399fSFrançois Tigeot  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
15805718399fSFrançois Tigeot  * @out: drm_mode_modeinfo struct to return to the user
15815718399fSFrançois Tigeot  * @in: drm_display_mode to use
15825718399fSFrançois Tigeot  *
15835718399fSFrançois Tigeot  * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
15845718399fSFrançois Tigeot  * the user.
15855718399fSFrançois Tigeot  */
15865718399fSFrançois Tigeot static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
15875718399fSFrançois Tigeot 				      const struct drm_display_mode *in)
15885718399fSFrançois Tigeot {
1589b5162e19SFrançois Tigeot 	WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX ||
15905718399fSFrançois Tigeot 	     in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX ||
15915718399fSFrançois Tigeot 	     in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX ||
15925718399fSFrançois Tigeot 	     in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX ||
1593b5162e19SFrançois Tigeot 	     in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX,
1594b5162e19SFrançois Tigeot 	     "timing values too large for mode info\n");
15955718399fSFrançois Tigeot 
15965718399fSFrançois Tigeot 	out->clock = in->clock;
15975718399fSFrançois Tigeot 	out->hdisplay = in->hdisplay;
15985718399fSFrançois Tigeot 	out->hsync_start = in->hsync_start;
15995718399fSFrançois Tigeot 	out->hsync_end = in->hsync_end;
16005718399fSFrançois Tigeot 	out->htotal = in->htotal;
16015718399fSFrançois Tigeot 	out->hskew = in->hskew;
16025718399fSFrançois Tigeot 	out->vdisplay = in->vdisplay;
16035718399fSFrançois Tigeot 	out->vsync_start = in->vsync_start;
16045718399fSFrançois Tigeot 	out->vsync_end = in->vsync_end;
16055718399fSFrançois Tigeot 	out->vtotal = in->vtotal;
16065718399fSFrançois Tigeot 	out->vscan = in->vscan;
16075718399fSFrançois Tigeot 	out->vrefresh = in->vrefresh;
16085718399fSFrançois Tigeot 	out->flags = in->flags;
16095718399fSFrançois Tigeot 	out->type = in->type;
16105718399fSFrançois Tigeot 	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
16115718399fSFrançois Tigeot 	out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
16125718399fSFrançois Tigeot }
16135718399fSFrançois Tigeot 
16145718399fSFrançois Tigeot /**
16159edbd4a0SFrançois Tigeot  * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode
16165718399fSFrançois Tigeot  * @out: drm_display_mode to return to the user
16175718399fSFrançois Tigeot  * @in: drm_mode_modeinfo to use
16185718399fSFrançois Tigeot  *
16195718399fSFrançois Tigeot  * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
16205718399fSFrançois Tigeot  * the caller.
16215718399fSFrançois Tigeot  *
1622ba55f2f5SFrançois Tigeot  * Returns:
16235718399fSFrançois Tigeot  * Zero on success, errno on failure.
16245718399fSFrançois Tigeot  */
16255718399fSFrançois Tigeot static int drm_crtc_convert_umode(struct drm_display_mode *out,
16265718399fSFrançois Tigeot 				  const struct drm_mode_modeinfo *in)
16275718399fSFrançois Tigeot {
16285718399fSFrançois Tigeot 	if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
1629b5162e19SFrançois Tigeot 		return -ERANGE;
16305718399fSFrançois Tigeot 
16319edbd4a0SFrançois Tigeot 	if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX)
16329edbd4a0SFrançois Tigeot 		return -EINVAL;
16339edbd4a0SFrançois Tigeot 
16345718399fSFrançois Tigeot 	out->clock = in->clock;
16355718399fSFrançois Tigeot 	out->hdisplay = in->hdisplay;
16365718399fSFrançois Tigeot 	out->hsync_start = in->hsync_start;
16375718399fSFrançois Tigeot 	out->hsync_end = in->hsync_end;
16385718399fSFrançois Tigeot 	out->htotal = in->htotal;
16395718399fSFrançois Tigeot 	out->hskew = in->hskew;
16405718399fSFrançois Tigeot 	out->vdisplay = in->vdisplay;
16415718399fSFrançois Tigeot 	out->vsync_start = in->vsync_start;
16425718399fSFrançois Tigeot 	out->vsync_end = in->vsync_end;
16435718399fSFrançois Tigeot 	out->vtotal = in->vtotal;
16445718399fSFrançois Tigeot 	out->vscan = in->vscan;
16455718399fSFrançois Tigeot 	out->vrefresh = in->vrefresh;
16465718399fSFrançois Tigeot 	out->flags = in->flags;
16475718399fSFrançois Tigeot 	out->type = in->type;
16485718399fSFrançois Tigeot 	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
16495718399fSFrançois Tigeot 	out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
16505718399fSFrançois Tigeot 
16515718399fSFrançois Tigeot 	return 0;
16525718399fSFrançois Tigeot }
16535718399fSFrançois Tigeot 
16545718399fSFrançois Tigeot /**
16555718399fSFrançois Tigeot  * drm_mode_getresources - get graphics configuration
16564dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
16574dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
16584dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
16595718399fSFrançois Tigeot  *
16605718399fSFrançois Tigeot  * Construct a set of configuration description structures and return
16615718399fSFrançois Tigeot  * them to the user, including CRTC, connector and framebuffer configuration.
16625718399fSFrançois Tigeot  *
16635718399fSFrançois Tigeot  * Called by the user via ioctl.
16645718399fSFrançois Tigeot  *
1665ba55f2f5SFrançois Tigeot  * Returns:
16665718399fSFrançois Tigeot  * Zero on success, errno on failure.
16675718399fSFrançois Tigeot  */
16685718399fSFrançois Tigeot int drm_mode_getresources(struct drm_device *dev, void *data,
16695718399fSFrançois Tigeot 			  struct drm_file *file_priv)
16705718399fSFrançois Tigeot {
16715718399fSFrançois Tigeot 	struct drm_mode_card_res *card_res = data;
16725718399fSFrançois Tigeot 	struct list_head *lh;
16735718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
16745718399fSFrançois Tigeot 	struct drm_connector *connector;
16755718399fSFrançois Tigeot 	struct drm_crtc *crtc;
16765718399fSFrançois Tigeot 	struct drm_encoder *encoder;
16775718399fSFrançois Tigeot 	int ret = 0;
16785718399fSFrançois Tigeot 	int connector_count = 0;
16795718399fSFrançois Tigeot 	int crtc_count = 0;
16805718399fSFrançois Tigeot 	int fb_count = 0;
16815718399fSFrançois Tigeot 	int encoder_count = 0;
16825718399fSFrançois Tigeot 	int copied = 0, i;
16835718399fSFrançois Tigeot 	uint32_t __user *fb_id;
16845718399fSFrançois Tigeot 	uint32_t __user *crtc_id;
16855718399fSFrançois Tigeot 	uint32_t __user *connector_id;
16865718399fSFrançois Tigeot 	uint32_t __user *encoder_id;
16875718399fSFrançois Tigeot 	struct drm_mode_group *mode_group;
16885718399fSFrançois Tigeot 
16895718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1690b5162e19SFrançois Tigeot 		return -EINVAL;
16915718399fSFrançois Tigeot 
16925718399fSFrançois Tigeot 
16934dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
16945718399fSFrançois Tigeot 	/*
16955718399fSFrançois Tigeot 	 * For the non-control nodes we need to limit the list of resources
16965718399fSFrançois Tigeot 	 * by IDs in the group list for this node
16975718399fSFrançois Tigeot 	 */
16985718399fSFrançois Tigeot 	list_for_each(lh, &file_priv->fbs)
16995718399fSFrançois Tigeot 		fb_count++;
17005718399fSFrançois Tigeot 
17014dbb207bSFrançois Tigeot 	/* handle this in 4 parts */
17024dbb207bSFrançois Tigeot 	/* FBs */
17034dbb207bSFrançois Tigeot 	if (card_res->count_fbs >= fb_count) {
17044dbb207bSFrançois Tigeot 		copied = 0;
17054dbb207bSFrançois Tigeot 		fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
17064dbb207bSFrançois Tigeot 		list_for_each_entry(fb, &file_priv->fbs, filp_head) {
17074dbb207bSFrançois Tigeot 			if (put_user(fb->base.id, fb_id + copied)) {
17084dbb207bSFrançois Tigeot 				mutex_unlock(&file_priv->fbs_lock);
17094dbb207bSFrançois Tigeot 				return -EFAULT;
17104dbb207bSFrançois Tigeot 			}
17114dbb207bSFrançois Tigeot 			copied++;
17124dbb207bSFrançois Tigeot 		}
17134dbb207bSFrançois Tigeot 	}
17144dbb207bSFrançois Tigeot 	card_res->count_fbs = fb_count;
17154dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
17164dbb207bSFrançois Tigeot 
17174dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
1718ba55f2f5SFrançois Tigeot 	if (!drm_is_primary_client(file_priv)) {
1719ba55f2f5SFrançois Tigeot 
1720ba55f2f5SFrançois Tigeot 		mode_group = NULL;
17215718399fSFrançois Tigeot 		list_for_each(lh, &dev->mode_config.crtc_list)
17225718399fSFrançois Tigeot 			crtc_count++;
17235718399fSFrançois Tigeot 
17245718399fSFrançois Tigeot 		list_for_each(lh, &dev->mode_config.connector_list)
17255718399fSFrançois Tigeot 			connector_count++;
17265718399fSFrançois Tigeot 
17275718399fSFrançois Tigeot 		list_for_each(lh, &dev->mode_config.encoder_list)
17285718399fSFrançois Tigeot 			encoder_count++;
17295718399fSFrançois Tigeot 	} else {
17305718399fSFrançois Tigeot 
17315718399fSFrançois Tigeot 		crtc_count = mode_group->num_crtcs;
17325718399fSFrançois Tigeot 		connector_count = mode_group->num_connectors;
17335718399fSFrançois Tigeot 		encoder_count = mode_group->num_encoders;
17345718399fSFrançois Tigeot 	}
17355718399fSFrançois Tigeot 
17365718399fSFrançois Tigeot 	card_res->max_height = dev->mode_config.max_height;
17375718399fSFrançois Tigeot 	card_res->min_height = dev->mode_config.min_height;
17385718399fSFrançois Tigeot 	card_res->max_width = dev->mode_config.max_width;
17395718399fSFrançois Tigeot 	card_res->min_width = dev->mode_config.min_width;
17405718399fSFrançois Tigeot 
17415718399fSFrançois Tigeot 	/* CRTCs */
17425718399fSFrançois Tigeot 	if (card_res->count_crtcs >= crtc_count) {
17435718399fSFrançois Tigeot 		copied = 0;
1744b5162e19SFrançois Tigeot 		crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
1745ba55f2f5SFrançois Tigeot 		if (!mode_group) {
17465718399fSFrançois Tigeot 			list_for_each_entry(crtc, &dev->mode_config.crtc_list,
17475718399fSFrançois Tigeot 					    head) {
17485718399fSFrançois Tigeot 				DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
1749b5162e19SFrançois Tigeot 				if (put_user(crtc->base.id, crtc_id + copied)) {
1750b5162e19SFrançois Tigeot 					ret = -EFAULT;
17515718399fSFrançois Tigeot 					goto out;
17525718399fSFrançois Tigeot 				}
17535718399fSFrançois Tigeot 				copied++;
17545718399fSFrançois Tigeot 			}
17555718399fSFrançois Tigeot 		} else {
17565718399fSFrançois Tigeot 			for (i = 0; i < mode_group->num_crtcs; i++) {
1757b5162e19SFrançois Tigeot 				if (put_user(mode_group->id_list[i],
1758b5162e19SFrançois Tigeot 					     crtc_id + copied)) {
1759b5162e19SFrançois Tigeot 					ret = -EFAULT;
17605718399fSFrançois Tigeot 					goto out;
17615718399fSFrançois Tigeot 				}
17625718399fSFrançois Tigeot 				copied++;
17635718399fSFrançois Tigeot 			}
17645718399fSFrançois Tigeot 		}
17655718399fSFrançois Tigeot 	}
17665718399fSFrançois Tigeot 	card_res->count_crtcs = crtc_count;
17675718399fSFrançois Tigeot 
17685718399fSFrançois Tigeot 	/* Encoders */
17695718399fSFrançois Tigeot 	if (card_res->count_encoders >= encoder_count) {
17705718399fSFrançois Tigeot 		copied = 0;
1771b5162e19SFrançois Tigeot 		encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
1772ba55f2f5SFrançois Tigeot 		if (!mode_group) {
17735718399fSFrançois Tigeot 			list_for_each_entry(encoder,
17745718399fSFrançois Tigeot 					    &dev->mode_config.encoder_list,
17755718399fSFrançois Tigeot 					    head) {
17765718399fSFrançois Tigeot 				DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
1777ba55f2f5SFrançois Tigeot 						encoder->name);
1778b5162e19SFrançois Tigeot 				if (put_user(encoder->base.id, encoder_id +
1779b5162e19SFrançois Tigeot 					     copied)) {
1780b5162e19SFrançois Tigeot 					ret = -EFAULT;
17815718399fSFrançois Tigeot 					goto out;
17825718399fSFrançois Tigeot 				}
17835718399fSFrançois Tigeot 				copied++;
17845718399fSFrançois Tigeot 			}
17855718399fSFrançois Tigeot 		} else {
1786b5162e19SFrançois Tigeot 			for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
1787b5162e19SFrançois Tigeot 				if (put_user(mode_group->id_list[i],
1788b5162e19SFrançois Tigeot 					     encoder_id + copied)) {
1789b5162e19SFrançois Tigeot 					ret = -EFAULT;
17905718399fSFrançois Tigeot 					goto out;
17915718399fSFrançois Tigeot 				}
17925718399fSFrançois Tigeot 				copied++;
17935718399fSFrançois Tigeot 			}
17945718399fSFrançois Tigeot 
17955718399fSFrançois Tigeot 		}
17965718399fSFrançois Tigeot 	}
17975718399fSFrançois Tigeot 	card_res->count_encoders = encoder_count;
17985718399fSFrançois Tigeot 
17995718399fSFrançois Tigeot 	/* Connectors */
18005718399fSFrançois Tigeot 	if (card_res->count_connectors >= connector_count) {
18015718399fSFrançois Tigeot 		copied = 0;
1802b5162e19SFrançois Tigeot 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
1803ba55f2f5SFrançois Tigeot 		if (!mode_group) {
18045718399fSFrançois Tigeot 			list_for_each_entry(connector,
18055718399fSFrançois Tigeot 					    &dev->mode_config.connector_list,
18065718399fSFrançois Tigeot 					    head) {
18075718399fSFrançois Tigeot 				DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
18085718399fSFrançois Tigeot 					connector->base.id,
1809ba55f2f5SFrançois Tigeot 					connector->name);
1810b5162e19SFrançois Tigeot 				if (put_user(connector->base.id,
1811b5162e19SFrançois Tigeot 					     connector_id + copied)) {
1812b5162e19SFrançois Tigeot 					ret = -EFAULT;
18135718399fSFrançois Tigeot 					goto out;
18145718399fSFrançois Tigeot 				}
18155718399fSFrançois Tigeot 				copied++;
18165718399fSFrançois Tigeot 			}
18175718399fSFrançois Tigeot 		} else {
18185718399fSFrançois Tigeot 			int start = mode_group->num_crtcs +
18195718399fSFrançois Tigeot 				mode_group->num_encoders;
18205718399fSFrançois Tigeot 			for (i = start; i < start + mode_group->num_connectors; i++) {
1821b5162e19SFrançois Tigeot 				if (put_user(mode_group->id_list[i],
1822b5162e19SFrançois Tigeot 					     connector_id + copied)) {
1823b5162e19SFrançois Tigeot 					ret = -EFAULT;
18245718399fSFrançois Tigeot 					goto out;
18255718399fSFrançois Tigeot 				}
18265718399fSFrançois Tigeot 				copied++;
18275718399fSFrançois Tigeot 			}
18285718399fSFrançois Tigeot 		}
18295718399fSFrançois Tigeot 	}
18305718399fSFrançois Tigeot 	card_res->count_connectors = connector_count;
18315718399fSFrançois Tigeot 
18325718399fSFrançois Tigeot 	DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs,
18335718399fSFrançois Tigeot 		  card_res->count_connectors, card_res->count_encoders);
18345718399fSFrançois Tigeot 
18355718399fSFrançois Tigeot out:
18364dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
18375718399fSFrançois Tigeot 	return ret;
18385718399fSFrançois Tigeot }
18395718399fSFrançois Tigeot 
18405718399fSFrançois Tigeot /**
18415718399fSFrançois Tigeot  * drm_mode_getcrtc - get CRTC configuration
18424dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
18434dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
18444dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
18455718399fSFrançois Tigeot  *
18465718399fSFrançois Tigeot  * Construct a CRTC configuration structure to return to the user.
18475718399fSFrançois Tigeot  *
18485718399fSFrançois Tigeot  * Called by the user via ioctl.
18495718399fSFrançois Tigeot  *
1850ba55f2f5SFrançois Tigeot  * Returns:
18515718399fSFrançois Tigeot  * Zero on success, errno on failure.
18525718399fSFrançois Tigeot  */
18535718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev,
18545718399fSFrançois Tigeot 		     void *data, struct drm_file *file_priv)
18555718399fSFrançois Tigeot {
18565718399fSFrançois Tigeot 	struct drm_mode_crtc *crtc_resp = data;
18575718399fSFrançois Tigeot 	struct drm_crtc *crtc;
18585718399fSFrançois Tigeot 	int ret = 0;
18595718399fSFrançois Tigeot 
18605718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1861b5162e19SFrançois Tigeot 		return -EINVAL;
18625718399fSFrançois Tigeot 
18634dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
18645718399fSFrançois Tigeot 
1865ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
1866ba55f2f5SFrançois Tigeot 	if (!crtc) {
18679edbd4a0SFrançois Tigeot 		ret = -ENOENT;
18685718399fSFrançois Tigeot 		goto out;
18695718399fSFrançois Tigeot 	}
18705718399fSFrançois Tigeot 
18715718399fSFrançois Tigeot 	crtc_resp->x = crtc->x;
18725718399fSFrançois Tigeot 	crtc_resp->y = crtc->y;
18735718399fSFrançois Tigeot 	crtc_resp->gamma_size = crtc->gamma_size;
1874ba55f2f5SFrançois Tigeot 	if (crtc->primary->fb)
1875ba55f2f5SFrançois Tigeot 		crtc_resp->fb_id = crtc->primary->fb->base.id;
18765718399fSFrançois Tigeot 	else
18775718399fSFrançois Tigeot 		crtc_resp->fb_id = 0;
18785718399fSFrançois Tigeot 
18795718399fSFrançois Tigeot 	if (crtc->enabled) {
18805718399fSFrançois Tigeot 
18815718399fSFrançois Tigeot 		drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
18825718399fSFrançois Tigeot 		crtc_resp->mode_valid = 1;
18835718399fSFrançois Tigeot 
18845718399fSFrançois Tigeot 	} else {
18855718399fSFrançois Tigeot 		crtc_resp->mode_valid = 0;
18865718399fSFrançois Tigeot 	}
18875718399fSFrançois Tigeot 
18885718399fSFrançois Tigeot out:
18894dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
18905718399fSFrançois Tigeot 	return ret;
18915718399fSFrançois Tigeot }
18925718399fSFrançois Tigeot 
18939edbd4a0SFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
18949edbd4a0SFrançois Tigeot 					 const struct drm_file *file_priv)
18959edbd4a0SFrançois Tigeot {
18969edbd4a0SFrançois Tigeot 	/*
18979edbd4a0SFrançois Tigeot 	 * If user-space hasn't configured the driver to expose the stereo 3D
18989edbd4a0SFrançois Tigeot 	 * modes, don't expose them.
18999edbd4a0SFrançois Tigeot 	 */
19009edbd4a0SFrançois Tigeot 	if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
19019edbd4a0SFrançois Tigeot 		return false;
19029edbd4a0SFrançois Tigeot 
19039edbd4a0SFrançois Tigeot 	return true;
19049edbd4a0SFrançois Tigeot }
19059edbd4a0SFrançois Tigeot 
19065718399fSFrançois Tigeot /**
19075718399fSFrançois Tigeot  * drm_mode_getconnector - get connector configuration
19084dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
19094dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
19104dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
19115718399fSFrançois Tigeot  *
19125718399fSFrançois Tigeot  * Construct a connector configuration structure to return to the user.
19135718399fSFrançois Tigeot  *
19145718399fSFrançois Tigeot  * Called by the user via ioctl.
19155718399fSFrançois Tigeot  *
1916ba55f2f5SFrançois Tigeot  * Returns:
19175718399fSFrançois Tigeot  * Zero on success, errno on failure.
19185718399fSFrançois Tigeot  */
19195718399fSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data,
19205718399fSFrançois Tigeot 			  struct drm_file *file_priv)
19215718399fSFrançois Tigeot {
19225718399fSFrançois Tigeot 	struct drm_mode_get_connector *out_resp = data;
19235718399fSFrançois Tigeot 	struct drm_connector *connector;
19245718399fSFrançois Tigeot 	struct drm_display_mode *mode;
19255718399fSFrançois Tigeot 	int mode_count = 0;
19265718399fSFrançois Tigeot 	int props_count = 0;
19275718399fSFrançois Tigeot 	int encoders_count = 0;
19285718399fSFrançois Tigeot 	int ret = 0;
19295718399fSFrançois Tigeot 	int copied = 0;
19305718399fSFrançois Tigeot 	int i;
19315718399fSFrançois Tigeot 	struct drm_mode_modeinfo u_mode;
19325718399fSFrançois Tigeot 	struct drm_mode_modeinfo __user *mode_ptr;
1933b5162e19SFrançois Tigeot 	uint32_t __user *prop_ptr;
1934b5162e19SFrançois Tigeot 	uint64_t __user *prop_values;
1935b5162e19SFrançois Tigeot 	uint32_t __user *encoder_ptr;
19365718399fSFrançois Tigeot 
19375718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1938b5162e19SFrançois Tigeot 		return -EINVAL;
19395718399fSFrançois Tigeot 
19405718399fSFrançois Tigeot 	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
19415718399fSFrançois Tigeot 
19425718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
19435718399fSFrançois Tigeot 
19444dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
1945ba55f2f5SFrançois Tigeot 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
19465718399fSFrançois Tigeot 
1947ba55f2f5SFrançois Tigeot 	connector = drm_connector_find(dev, out_resp->connector_id);
1948ba55f2f5SFrançois Tigeot 	if (!connector) {
19499edbd4a0SFrançois Tigeot 		ret = -ENOENT;
19505718399fSFrançois Tigeot 		goto out;
19515718399fSFrançois Tigeot 	}
19525718399fSFrançois Tigeot 
1953b5162e19SFrançois Tigeot 	props_count = connector->properties.count;
19545718399fSFrançois Tigeot 
19555718399fSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
19565718399fSFrançois Tigeot 		if (connector->encoder_ids[i] != 0) {
19575718399fSFrançois Tigeot 			encoders_count++;
19585718399fSFrançois Tigeot 		}
19595718399fSFrançois Tigeot 	}
19605718399fSFrançois Tigeot 
19615718399fSFrançois Tigeot 	if (out_resp->count_modes == 0) {
19625718399fSFrançois Tigeot 		connector->funcs->fill_modes(connector,
19635718399fSFrançois Tigeot 					     dev->mode_config.max_width,
19645718399fSFrançois Tigeot 					     dev->mode_config.max_height);
19655718399fSFrançois Tigeot 	}
19665718399fSFrançois Tigeot 
19675718399fSFrançois Tigeot 	/* delayed so we get modes regardless of pre-fill_modes state */
19685718399fSFrançois Tigeot 	list_for_each_entry(mode, &connector->modes, head)
19699edbd4a0SFrançois Tigeot 		if (drm_mode_expose_to_userspace(mode, file_priv))
19705718399fSFrançois Tigeot 			mode_count++;
19715718399fSFrançois Tigeot 
19725718399fSFrançois Tigeot 	out_resp->connector_id = connector->base.id;
19735718399fSFrançois Tigeot 	out_resp->connector_type = connector->connector_type;
19745718399fSFrançois Tigeot 	out_resp->connector_type_id = connector->connector_type_id;
19755718399fSFrançois Tigeot 	out_resp->mm_width = connector->display_info.width_mm;
19765718399fSFrançois Tigeot 	out_resp->mm_height = connector->display_info.height_mm;
19775718399fSFrançois Tigeot 	out_resp->subpixel = connector->display_info.subpixel_order;
19785718399fSFrançois Tigeot 	out_resp->connection = connector->status;
19795718399fSFrançois Tigeot 	if (connector->encoder)
19805718399fSFrançois Tigeot 		out_resp->encoder_id = connector->encoder->base.id;
19815718399fSFrançois Tigeot 	else
19825718399fSFrançois Tigeot 		out_resp->encoder_id = 0;
19835718399fSFrançois Tigeot 
19845718399fSFrançois Tigeot 	/*
19855718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
19865718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
19875718399fSFrançois Tigeot 	 */
19885718399fSFrançois Tigeot 	if ((out_resp->count_modes >= mode_count) && mode_count) {
19895718399fSFrançois Tigeot 		copied = 0;
1990b5162e19SFrançois Tigeot 		mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
19915718399fSFrançois Tigeot 		list_for_each_entry(mode, &connector->modes, head) {
19929edbd4a0SFrançois Tigeot 			if (!drm_mode_expose_to_userspace(mode, file_priv))
19939edbd4a0SFrançois Tigeot 				continue;
19949edbd4a0SFrançois Tigeot 
19955718399fSFrançois Tigeot 			drm_crtc_convert_to_umode(&u_mode, mode);
1996b5162e19SFrançois Tigeot 			if (copy_to_user(mode_ptr + copied,
1997b5162e19SFrançois Tigeot 					 &u_mode, sizeof(u_mode))) {
1998b5162e19SFrançois Tigeot 				ret = -EFAULT;
19995718399fSFrançois Tigeot 				goto out;
20005718399fSFrançois Tigeot 			}
20015718399fSFrançois Tigeot 			copied++;
20025718399fSFrançois Tigeot 		}
20035718399fSFrançois Tigeot 	}
20045718399fSFrançois Tigeot 	out_resp->count_modes = mode_count;
20055718399fSFrançois Tigeot 
20065718399fSFrançois Tigeot 	if ((out_resp->count_props >= props_count) && props_count) {
20075718399fSFrançois Tigeot 		copied = 0;
2008b5162e19SFrançois Tigeot 		prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr);
2009b5162e19SFrançois Tigeot 		prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr);
2010b5162e19SFrançois Tigeot 		for (i = 0; i < connector->properties.count; i++) {
2011b5162e19SFrançois Tigeot 			if (put_user(connector->properties.ids[i],
2012b5162e19SFrançois Tigeot 				     prop_ptr + copied)) {
2013b5162e19SFrançois Tigeot 				ret = -EFAULT;
20145718399fSFrançois Tigeot 				goto out;
20155718399fSFrançois Tigeot 			}
20165718399fSFrançois Tigeot 
2017b5162e19SFrançois Tigeot 			if (put_user(connector->properties.values[i],
2018b5162e19SFrançois Tigeot 				     prop_values + copied)) {
2019b5162e19SFrançois Tigeot 				ret = -EFAULT;
20205718399fSFrançois Tigeot 				goto out;
20215718399fSFrançois Tigeot 			}
20225718399fSFrançois Tigeot 			copied++;
20235718399fSFrançois Tigeot 		}
20245718399fSFrançois Tigeot 	}
20255718399fSFrançois Tigeot 	out_resp->count_props = props_count;
20265718399fSFrançois Tigeot 
20275718399fSFrançois Tigeot 	if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
20285718399fSFrançois Tigeot 		copied = 0;
2029b5162e19SFrançois Tigeot 		encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
20305718399fSFrançois Tigeot 		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
20315718399fSFrançois Tigeot 			if (connector->encoder_ids[i] != 0) {
2032b5162e19SFrançois Tigeot 				if (put_user(connector->encoder_ids[i],
2033b5162e19SFrançois Tigeot 					     encoder_ptr + copied)) {
2034b5162e19SFrançois Tigeot 					ret = -EFAULT;
20355718399fSFrançois Tigeot 					goto out;
20365718399fSFrançois Tigeot 				}
20375718399fSFrançois Tigeot 				copied++;
20385718399fSFrançois Tigeot 			}
20395718399fSFrançois Tigeot 		}
20405718399fSFrançois Tigeot 	}
20415718399fSFrançois Tigeot 	out_resp->count_encoders = encoders_count;
20425718399fSFrançois Tigeot 
20435718399fSFrançois Tigeot out:
2044ba55f2f5SFrançois Tigeot 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
20454dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
20464dbb207bSFrançois Tigeot 
20475718399fSFrançois Tigeot 	return ret;
20485718399fSFrançois Tigeot }
20495718399fSFrançois Tigeot 
2050ba55f2f5SFrançois Tigeot /**
2051ba55f2f5SFrançois Tigeot  * drm_mode_getencoder - get encoder configuration
2052ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
2053ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
2054ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
2055ba55f2f5SFrançois Tigeot  *
2056ba55f2f5SFrançois Tigeot  * Construct a encoder configuration structure to return to the user.
2057ba55f2f5SFrançois Tigeot  *
2058ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2059ba55f2f5SFrançois Tigeot  *
2060ba55f2f5SFrançois Tigeot  * Returns:
2061ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
2062ba55f2f5SFrançois Tigeot  */
20635718399fSFrançois Tigeot int drm_mode_getencoder(struct drm_device *dev, void *data,
20645718399fSFrançois Tigeot 			struct drm_file *file_priv)
20655718399fSFrançois Tigeot {
20665718399fSFrançois Tigeot 	struct drm_mode_get_encoder *enc_resp = data;
20675718399fSFrançois Tigeot 	struct drm_encoder *encoder;
20685718399fSFrançois Tigeot 	int ret = 0;
20695718399fSFrançois Tigeot 
20705718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2071b5162e19SFrançois Tigeot 		return -EINVAL;
20725718399fSFrançois Tigeot 
20734dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
2074ba55f2f5SFrançois Tigeot 	encoder = drm_encoder_find(dev, enc_resp->encoder_id);
2075ba55f2f5SFrançois Tigeot 	if (!encoder) {
20769edbd4a0SFrançois Tigeot 		ret = -ENOENT;
20775718399fSFrançois Tigeot 		goto out;
20785718399fSFrançois Tigeot 	}
20795718399fSFrançois Tigeot 
20805718399fSFrançois Tigeot 	if (encoder->crtc)
20815718399fSFrançois Tigeot 		enc_resp->crtc_id = encoder->crtc->base.id;
20825718399fSFrançois Tigeot 	else
20835718399fSFrançois Tigeot 		enc_resp->crtc_id = 0;
20845718399fSFrançois Tigeot 	enc_resp->encoder_type = encoder->encoder_type;
20855718399fSFrançois Tigeot 	enc_resp->encoder_id = encoder->base.id;
20865718399fSFrançois Tigeot 	enc_resp->possible_crtcs = encoder->possible_crtcs;
20875718399fSFrançois Tigeot 	enc_resp->possible_clones = encoder->possible_clones;
20885718399fSFrançois Tigeot 
20895718399fSFrançois Tigeot out:
20904dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
20915718399fSFrançois Tigeot 	return ret;
20925718399fSFrançois Tigeot }
20935718399fSFrançois Tigeot 
20945718399fSFrançois Tigeot /**
2095ba55f2f5SFrançois Tigeot  * drm_mode_getplane_res - enumerate all plane resources
20965718399fSFrançois Tigeot  * @dev: DRM device
20975718399fSFrançois Tigeot  * @data: ioctl data
20985718399fSFrançois Tigeot  * @file_priv: DRM file info
20995718399fSFrançois Tigeot  *
2100ba55f2f5SFrançois Tigeot  * Construct a list of plane ids to return to the user.
2101ba55f2f5SFrançois Tigeot  *
2102ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2103ba55f2f5SFrançois Tigeot  *
2104ba55f2f5SFrançois Tigeot  * Returns:
2105ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
21065718399fSFrançois Tigeot  */
21075718399fSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data,
21085718399fSFrançois Tigeot 			  struct drm_file *file_priv)
21095718399fSFrançois Tigeot {
21105718399fSFrançois Tigeot 	struct drm_mode_get_plane_res *plane_resp = data;
21115718399fSFrançois Tigeot 	struct drm_mode_config *config;
21125718399fSFrançois Tigeot 	struct drm_plane *plane;
2113b5162e19SFrançois Tigeot 	uint32_t __user *plane_ptr;
21145718399fSFrançois Tigeot 	int copied = 0, ret = 0;
2115ba55f2f5SFrançois Tigeot 	unsigned num_planes;
21165718399fSFrançois Tigeot 
21175718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2118b5162e19SFrançois Tigeot 		return -EINVAL;
21195718399fSFrançois Tigeot 
21204dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
21215718399fSFrançois Tigeot 	config = &dev->mode_config;
21225718399fSFrançois Tigeot 
2123ba55f2f5SFrançois Tigeot 	if (file_priv->universal_planes)
2124ba55f2f5SFrançois Tigeot 		num_planes = config->num_total_plane;
2125ba55f2f5SFrançois Tigeot 	else
2126ba55f2f5SFrançois Tigeot 		num_planes = config->num_overlay_plane;
2127ba55f2f5SFrançois Tigeot 
21285718399fSFrançois Tigeot 	/*
21295718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
21305718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
21315718399fSFrançois Tigeot 	 */
2132ba55f2f5SFrançois Tigeot 	if (num_planes &&
2133ba55f2f5SFrançois Tigeot 	    (plane_resp->count_planes >= num_planes)) {
2134b5162e19SFrançois Tigeot 		plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
21355718399fSFrançois Tigeot 
21365718399fSFrançois Tigeot 		list_for_each_entry(plane, &config->plane_list, head) {
2137ba55f2f5SFrançois Tigeot 			/*
2138ba55f2f5SFrançois Tigeot 			 * Unless userspace set the 'universal planes'
2139ba55f2f5SFrançois Tigeot 			 * capability bit, only advertise overlays.
2140ba55f2f5SFrançois Tigeot 			 */
2141ba55f2f5SFrançois Tigeot 			if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
2142ba55f2f5SFrançois Tigeot 			    !file_priv->universal_planes)
2143ba55f2f5SFrançois Tigeot 				continue;
2144ba55f2f5SFrançois Tigeot 
2145b5162e19SFrançois Tigeot 			if (put_user(plane->base.id, plane_ptr + copied)) {
2146b5162e19SFrançois Tigeot 				ret = -EFAULT;
21475718399fSFrançois Tigeot 				goto out;
21485718399fSFrançois Tigeot 			}
21495718399fSFrançois Tigeot 			copied++;
21505718399fSFrançois Tigeot 		}
21515718399fSFrançois Tigeot 	}
2152ba55f2f5SFrançois Tigeot 	plane_resp->count_planes = num_planes;
21535718399fSFrançois Tigeot 
21545718399fSFrançois Tigeot out:
21554dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
21565718399fSFrançois Tigeot 	return ret;
21575718399fSFrançois Tigeot }
21585718399fSFrançois Tigeot 
21595718399fSFrançois Tigeot /**
2160ba55f2f5SFrançois Tigeot  * drm_mode_getplane - get plane configuration
21615718399fSFrançois Tigeot  * @dev: DRM device
21625718399fSFrançois Tigeot  * @data: ioctl data
21635718399fSFrançois Tigeot  * @file_priv: DRM file info
21645718399fSFrançois Tigeot  *
2165ba55f2f5SFrançois Tigeot  * Construct a plane configuration structure to return to the user.
2166ba55f2f5SFrançois Tigeot  *
2167ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2168ba55f2f5SFrançois Tigeot  *
2169ba55f2f5SFrançois Tigeot  * Returns:
2170ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
21715718399fSFrançois Tigeot  */
21725718399fSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data,
21735718399fSFrançois Tigeot 		      struct drm_file *file_priv)
21745718399fSFrançois Tigeot {
21755718399fSFrançois Tigeot 	struct drm_mode_get_plane *plane_resp = data;
21765718399fSFrançois Tigeot 	struct drm_plane *plane;
2177b5162e19SFrançois Tigeot 	uint32_t __user *format_ptr;
21785718399fSFrançois Tigeot 	int ret = 0;
21795718399fSFrançois Tigeot 
21805718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2181b5162e19SFrançois Tigeot 		return -EINVAL;
21825718399fSFrançois Tigeot 
21834dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
2184ba55f2f5SFrançois Tigeot 	plane = drm_plane_find(dev, plane_resp->plane_id);
2185ba55f2f5SFrançois Tigeot 	if (!plane) {
2186b5162e19SFrançois Tigeot 		ret = -ENOENT;
21875718399fSFrançois Tigeot 		goto out;
21885718399fSFrançois Tigeot 	}
21895718399fSFrançois Tigeot 
21905718399fSFrançois Tigeot 	if (plane->crtc)
21915718399fSFrançois Tigeot 		plane_resp->crtc_id = plane->crtc->base.id;
21925718399fSFrançois Tigeot 	else
21935718399fSFrançois Tigeot 		plane_resp->crtc_id = 0;
21945718399fSFrançois Tigeot 
21955718399fSFrançois Tigeot 	if (plane->fb)
21965718399fSFrançois Tigeot 		plane_resp->fb_id = plane->fb->base.id;
21975718399fSFrançois Tigeot 	else
21985718399fSFrançois Tigeot 		plane_resp->fb_id = 0;
21995718399fSFrançois Tigeot 
22005718399fSFrançois Tigeot 	plane_resp->plane_id = plane->base.id;
22015718399fSFrançois Tigeot 	plane_resp->possible_crtcs = plane->possible_crtcs;
220206fede5aSFrançois Tigeot 	plane_resp->gamma_size = 0;
22035718399fSFrançois Tigeot 
22045718399fSFrançois Tigeot 	/*
22055718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
22065718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
22075718399fSFrançois Tigeot 	 */
22085718399fSFrançois Tigeot 	if (plane->format_count &&
22095718399fSFrançois Tigeot 	    (plane_resp->count_format_types >= plane->format_count)) {
2210b5162e19SFrançois Tigeot 		format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
2211b5162e19SFrançois Tigeot 		if (copy_to_user(format_ptr,
22125718399fSFrançois Tigeot 				 plane->format_types,
22135718399fSFrançois Tigeot 				 sizeof(uint32_t) * plane->format_count)) {
2214b5162e19SFrançois Tigeot 			ret = -EFAULT;
22155718399fSFrançois Tigeot 			goto out;
22165718399fSFrançois Tigeot 		}
22175718399fSFrançois Tigeot 	}
22185718399fSFrançois Tigeot 	plane_resp->count_format_types = plane->format_count;
22195718399fSFrançois Tigeot 
22205718399fSFrançois Tigeot out:
22214dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
22225718399fSFrançois Tigeot 	return ret;
22235718399fSFrançois Tigeot }
22245718399fSFrançois Tigeot 
2225*24edb884SFrançois Tigeot /*
2226*24edb884SFrançois Tigeot  * setplane_internal - setplane handler for internal callers
22275718399fSFrançois Tigeot  *
2228*24edb884SFrançois Tigeot  * Note that we assume an extra reference has already been taken on fb.  If the
2229*24edb884SFrançois Tigeot  * update fails, this reference will be dropped before return; if it succeeds,
2230*24edb884SFrançois Tigeot  * the previous framebuffer (if any) will be unreferenced instead.
2231ba55f2f5SFrançois Tigeot  *
2232*24edb884SFrançois Tigeot  * src_{x,y,w,h} are provided in 16.16 fixed point format
22335718399fSFrançois Tigeot  */
2234*24edb884SFrançois Tigeot static int setplane_internal(struct drm_plane *plane,
2235*24edb884SFrançois Tigeot 			     struct drm_crtc *crtc,
2236*24edb884SFrançois Tigeot 			     struct drm_framebuffer *fb,
2237*24edb884SFrançois Tigeot 			     int32_t crtc_x, int32_t crtc_y,
2238*24edb884SFrançois Tigeot 			     uint32_t crtc_w, uint32_t crtc_h,
2239*24edb884SFrançois Tigeot 			     /* src_{x,y,w,h} values are 16.16 fixed point */
2240*24edb884SFrançois Tigeot 			     uint32_t src_x, uint32_t src_y,
2241*24edb884SFrançois Tigeot 			     uint32_t src_w, uint32_t src_h)
22425718399fSFrançois Tigeot {
2243*24edb884SFrançois Tigeot 	struct drm_device *dev = plane->dev;
2244*24edb884SFrançois Tigeot 	struct drm_framebuffer *old_fb = NULL;
22455718399fSFrançois Tigeot 	int ret = 0;
22465718399fSFrançois Tigeot 	unsigned int fb_width, fb_height;
22475718399fSFrançois Tigeot 	int i;
22485718399fSFrançois Tigeot 
22495718399fSFrançois Tigeot 	/* No fb means shut it down */
2250*24edb884SFrançois Tigeot 	if (!fb) {
22514dbb207bSFrançois Tigeot 		drm_modeset_lock_all(dev);
22524dbb207bSFrançois Tigeot 		old_fb = plane->fb;
2253ba55f2f5SFrançois Tigeot 		ret = plane->funcs->disable_plane(plane);
2254ba55f2f5SFrançois Tigeot 		if (!ret) {
22555718399fSFrançois Tigeot 			plane->crtc = NULL;
22565718399fSFrançois Tigeot 			plane->fb = NULL;
2257ba55f2f5SFrançois Tigeot 		} else {
2258ba55f2f5SFrançois Tigeot 			old_fb = NULL;
2259ba55f2f5SFrançois Tigeot 		}
22604dbb207bSFrançois Tigeot 		drm_modeset_unlock_all(dev);
22615718399fSFrançois Tigeot 		goto out;
22625718399fSFrançois Tigeot 	}
22635718399fSFrançois Tigeot 
2264*24edb884SFrançois Tigeot 	/* Check whether this plane is usable on this CRTC */
2265*24edb884SFrançois Tigeot 	if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
2266*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("Invalid crtc for plane\n");
2267*24edb884SFrançois Tigeot 		ret = -EINVAL;
22685718399fSFrançois Tigeot 		goto out;
22695718399fSFrançois Tigeot 	}
22705718399fSFrançois Tigeot 
22715718399fSFrançois Tigeot 	/* Check whether this plane supports the fb pixel format. */
22725718399fSFrançois Tigeot 	for (i = 0; i < plane->format_count; i++)
22735718399fSFrançois Tigeot 		if (fb->pixel_format == plane->format_types[i])
22745718399fSFrançois Tigeot 			break;
22755718399fSFrançois Tigeot 	if (i == plane->format_count) {
227606fede5aSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid pixel format %s\n",
227706fede5aSFrançois Tigeot 			      drm_get_format_name(fb->pixel_format));
2278b5162e19SFrançois Tigeot 		ret = -EINVAL;
22795718399fSFrançois Tigeot 		goto out;
22805718399fSFrançois Tigeot 	}
22815718399fSFrançois Tigeot 
22825718399fSFrançois Tigeot 	fb_width = fb->width << 16;
22835718399fSFrançois Tigeot 	fb_height = fb->height << 16;
22845718399fSFrançois Tigeot 
22855718399fSFrançois Tigeot 	/* Make sure source coordinates are inside the fb. */
2286*24edb884SFrançois Tigeot 	if (src_w > fb_width ||
2287*24edb884SFrançois Tigeot 	    src_x > fb_width - src_w ||
2288*24edb884SFrançois Tigeot 	    src_h > fb_height ||
2289*24edb884SFrançois Tigeot 	    src_y > fb_height - src_h) {
22905718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid source coordinates "
22915718399fSFrançois Tigeot 			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
2292*24edb884SFrançois Tigeot 			      src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
2293*24edb884SFrançois Tigeot 			      src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
2294*24edb884SFrançois Tigeot 			      src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
2295*24edb884SFrançois Tigeot 			      src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
2296b5162e19SFrançois Tigeot 		ret = -ENOSPC;
22975718399fSFrançois Tigeot 		goto out;
22985718399fSFrançois Tigeot 	}
22995718399fSFrançois Tigeot 
23004dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
2301ba55f2f5SFrançois Tigeot 	old_fb = plane->fb;
2302b5162e19SFrançois Tigeot 	ret = plane->funcs->update_plane(plane, crtc, fb,
2303*24edb884SFrançois Tigeot 					 crtc_x, crtc_y, crtc_w, crtc_h,
2304*24edb884SFrançois Tigeot 					 src_x, src_y, src_w, src_h);
23055718399fSFrançois Tigeot 	if (!ret) {
23065718399fSFrançois Tigeot 		plane->crtc = crtc;
23075718399fSFrançois Tigeot 		plane->fb = fb;
23084dbb207bSFrançois Tigeot 		fb = NULL;
2309ba55f2f5SFrançois Tigeot 	} else {
2310ba55f2f5SFrançois Tigeot 		old_fb = NULL;
23115718399fSFrançois Tigeot 	}
23124dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
23135718399fSFrançois Tigeot 
23145718399fSFrançois Tigeot out:
23154dbb207bSFrançois Tigeot 	if (fb)
23164dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
23174dbb207bSFrançois Tigeot 	if (old_fb)
23184dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(old_fb);
23195718399fSFrançois Tigeot 
23205718399fSFrançois Tigeot 	return ret;
2321*24edb884SFrançois Tigeot 
2322*24edb884SFrançois Tigeot }
2323*24edb884SFrançois Tigeot 
2324*24edb884SFrançois Tigeot /**
2325*24edb884SFrançois Tigeot  * drm_mode_setplane - configure a plane's configuration
2326*24edb884SFrançois Tigeot  * @dev: DRM device
2327*24edb884SFrançois Tigeot  * @data: ioctl data*
2328*24edb884SFrançois Tigeot  * @file_priv: DRM file info
2329*24edb884SFrançois Tigeot  *
2330*24edb884SFrançois Tigeot  * Set plane configuration, including placement, fb, scaling, and other factors.
2331*24edb884SFrançois Tigeot  * Or pass a NULL fb to disable (planes may be disabled without providing a
2332*24edb884SFrançois Tigeot  * valid crtc).
2333*24edb884SFrançois Tigeot  *
2334*24edb884SFrançois Tigeot  * Returns:
2335*24edb884SFrançois Tigeot  * Zero on success, errno on failure.
2336*24edb884SFrançois Tigeot  */
2337*24edb884SFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data,
2338*24edb884SFrançois Tigeot 		      struct drm_file *file_priv)
2339*24edb884SFrançois Tigeot {
2340*24edb884SFrançois Tigeot 	struct drm_mode_set_plane *plane_req = data;
2341*24edb884SFrançois Tigeot 	struct drm_mode_object *obj;
2342*24edb884SFrançois Tigeot 	struct drm_plane *plane;
2343*24edb884SFrançois Tigeot 	struct drm_crtc *crtc = NULL;
2344*24edb884SFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
2345*24edb884SFrançois Tigeot 
2346*24edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2347*24edb884SFrançois Tigeot 		return -EINVAL;
2348*24edb884SFrançois Tigeot 
2349*24edb884SFrançois Tigeot 	/* Give drivers some help against integer overflows */
2350*24edb884SFrançois Tigeot 	if (plane_req->crtc_w > INT_MAX ||
2351*24edb884SFrançois Tigeot 	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
2352*24edb884SFrançois Tigeot 	    plane_req->crtc_h > INT_MAX ||
2353*24edb884SFrançois Tigeot 	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
2354*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
2355*24edb884SFrançois Tigeot 			      plane_req->crtc_w, plane_req->crtc_h,
2356*24edb884SFrançois Tigeot 			      plane_req->crtc_x, plane_req->crtc_y);
2357*24edb884SFrançois Tigeot 		return -ERANGE;
2358*24edb884SFrançois Tigeot 	}
2359*24edb884SFrançois Tigeot 
2360*24edb884SFrançois Tigeot 	/*
2361*24edb884SFrançois Tigeot 	 * First, find the plane, crtc, and fb objects.  If not available,
2362*24edb884SFrançois Tigeot 	 * we don't bother to call the driver.
2363*24edb884SFrançois Tigeot 	 */
2364*24edb884SFrançois Tigeot 	obj = drm_mode_object_find(dev, plane_req->plane_id,
2365*24edb884SFrançois Tigeot 				   DRM_MODE_OBJECT_PLANE);
2366*24edb884SFrançois Tigeot 	if (!obj) {
2367*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
2368*24edb884SFrançois Tigeot 			      plane_req->plane_id);
2369*24edb884SFrançois Tigeot 		return -ENOENT;
2370*24edb884SFrançois Tigeot 	}
2371*24edb884SFrançois Tigeot 	plane = obj_to_plane(obj);
2372*24edb884SFrançois Tigeot 
2373*24edb884SFrançois Tigeot 	if (plane_req->fb_id) {
2374*24edb884SFrançois Tigeot 		fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
2375*24edb884SFrançois Tigeot 		if (!fb) {
2376*24edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
2377*24edb884SFrançois Tigeot 				      plane_req->fb_id);
2378*24edb884SFrançois Tigeot 			return -ENOENT;
2379*24edb884SFrançois Tigeot 		}
2380*24edb884SFrançois Tigeot 
2381*24edb884SFrançois Tigeot 		obj = drm_mode_object_find(dev, plane_req->crtc_id,
2382*24edb884SFrançois Tigeot 					   DRM_MODE_OBJECT_CRTC);
2383*24edb884SFrançois Tigeot 		if (!obj) {
2384*24edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Unknown crtc ID %d\n",
2385*24edb884SFrançois Tigeot 				      plane_req->crtc_id);
2386*24edb884SFrançois Tigeot 			return -ENOENT;
2387*24edb884SFrançois Tigeot 		}
2388*24edb884SFrançois Tigeot 		crtc = obj_to_crtc(obj);
2389*24edb884SFrançois Tigeot 	}
2390*24edb884SFrançois Tigeot 
2391*24edb884SFrançois Tigeot 	/*
2392*24edb884SFrançois Tigeot 	 * setplane_internal will take care of deref'ing either the old or new
2393*24edb884SFrançois Tigeot 	 * framebuffer depending on success.
2394*24edb884SFrançois Tigeot 	 */
2395*24edb884SFrançois Tigeot 	return setplane_internal(plane, crtc, fb,
2396*24edb884SFrançois Tigeot 				 plane_req->crtc_x, plane_req->crtc_y,
2397*24edb884SFrançois Tigeot 				 plane_req->crtc_w, plane_req->crtc_h,
2398*24edb884SFrançois Tigeot 				 plane_req->src_x, plane_req->src_y,
2399*24edb884SFrançois Tigeot 				 plane_req->src_w, plane_req->src_h);
24005718399fSFrançois Tigeot }
24015718399fSFrançois Tigeot 
24025718399fSFrançois Tigeot /**
24034dbb207bSFrançois Tigeot  * drm_mode_set_config_internal - helper to call ->set_config
24044dbb207bSFrançois Tigeot  * @set: modeset config to set
24055718399fSFrançois Tigeot  *
24064dbb207bSFrançois Tigeot  * This is a little helper to wrap internal calls to the ->set_config driver
24074dbb207bSFrançois Tigeot  * interface. The only thing it adds is correct refcounting dance.
2408ba55f2f5SFrançois Tigeot  *
2409ba55f2f5SFrançois Tigeot  * Returns:
2410ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
24114dbb207bSFrançois Tigeot  */
24124dbb207bSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set)
24134dbb207bSFrançois Tigeot {
24144dbb207bSFrançois Tigeot 	struct drm_crtc *crtc = set->crtc;
241506fede5aSFrançois Tigeot 	struct drm_framebuffer *fb;
241606fede5aSFrançois Tigeot 	struct drm_crtc *tmp;
24174dbb207bSFrançois Tigeot 	int ret;
24184dbb207bSFrançois Tigeot 
241906fede5aSFrançois Tigeot 	/*
242006fede5aSFrançois Tigeot 	 * NOTE: ->set_config can also disable other crtcs (if we steal all
242106fede5aSFrançois Tigeot 	 * connectors from it), hence we need to refcount the fbs across all
242206fede5aSFrançois Tigeot 	 * crtcs. Atomic modeset will have saner semantics ...
242306fede5aSFrançois Tigeot 	 */
242406fede5aSFrançois Tigeot 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
2425ba55f2f5SFrançois Tigeot 		tmp->old_fb = tmp->primary->fb;
242606fede5aSFrançois Tigeot 
24274dbb207bSFrançois Tigeot 	fb = set->fb;
24284dbb207bSFrançois Tigeot 
24294dbb207bSFrançois Tigeot 	ret = crtc->funcs->set_config(set);
24304dbb207bSFrançois Tigeot 	if (ret == 0) {
2431ba55f2f5SFrançois Tigeot 		crtc->primary->crtc = crtc;
2432ba55f2f5SFrançois Tigeot 		crtc->primary->fb = fb;
243306fede5aSFrançois Tigeot 	}
243406fede5aSFrançois Tigeot 
243506fede5aSFrançois Tigeot 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
2436ba55f2f5SFrançois Tigeot 		if (tmp->primary->fb)
2437ba55f2f5SFrançois Tigeot 			drm_framebuffer_reference(tmp->primary->fb);
243806fede5aSFrançois Tigeot 		if (tmp->old_fb)
243906fede5aSFrançois Tigeot 			drm_framebuffer_unreference(tmp->old_fb);
24404dbb207bSFrançois Tigeot 	}
24414dbb207bSFrançois Tigeot 
24424dbb207bSFrançois Tigeot 	return ret;
24434dbb207bSFrançois Tigeot }
24444dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal);
24454dbb207bSFrançois Tigeot 
2446ba55f2f5SFrançois Tigeot /**
2447ba55f2f5SFrançois Tigeot  * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
2448ba55f2f5SFrançois Tigeot  *     CRTC viewport
2449ba55f2f5SFrançois Tigeot  * @crtc: CRTC that framebuffer will be displayed on
2450ba55f2f5SFrançois Tigeot  * @x: x panning
2451ba55f2f5SFrançois Tigeot  * @y: y panning
2452ba55f2f5SFrançois Tigeot  * @mode: mode that framebuffer will be displayed under
2453ba55f2f5SFrançois Tigeot  * @fb: framebuffer to check size of
24549edbd4a0SFrançois Tigeot  */
2455ba55f2f5SFrançois Tigeot int drm_crtc_check_viewport(const struct drm_crtc *crtc,
24569edbd4a0SFrançois Tigeot 			    int x, int y,
24579edbd4a0SFrançois Tigeot 			    const struct drm_display_mode *mode,
24589edbd4a0SFrançois Tigeot 			    const struct drm_framebuffer *fb)
24599edbd4a0SFrançois Tigeot 
24609edbd4a0SFrançois Tigeot {
24619edbd4a0SFrançois Tigeot 	int hdisplay, vdisplay;
24629edbd4a0SFrançois Tigeot 
24639edbd4a0SFrançois Tigeot 	hdisplay = mode->hdisplay;
24649edbd4a0SFrançois Tigeot 	vdisplay = mode->vdisplay;
24659edbd4a0SFrançois Tigeot 
24669edbd4a0SFrançois Tigeot 	if (drm_mode_is_stereo(mode)) {
24679edbd4a0SFrançois Tigeot 		struct drm_display_mode adjusted = *mode;
24689edbd4a0SFrançois Tigeot 
24699edbd4a0SFrançois Tigeot 		drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
24709edbd4a0SFrançois Tigeot 		hdisplay = adjusted.crtc_hdisplay;
24719edbd4a0SFrançois Tigeot 		vdisplay = adjusted.crtc_vdisplay;
24729edbd4a0SFrançois Tigeot 	}
24739edbd4a0SFrançois Tigeot 
24749edbd4a0SFrançois Tigeot 	if (crtc->invert_dimensions)
24759edbd4a0SFrançois Tigeot 		swap(hdisplay, vdisplay);
24769edbd4a0SFrançois Tigeot 
24779edbd4a0SFrançois Tigeot 	if (hdisplay > fb->width ||
24789edbd4a0SFrançois Tigeot 	    vdisplay > fb->height ||
24799edbd4a0SFrançois Tigeot 	    x > fb->width - hdisplay ||
24809edbd4a0SFrançois Tigeot 	    y > fb->height - vdisplay) {
24819edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
24829edbd4a0SFrançois Tigeot 			      fb->width, fb->height, hdisplay, vdisplay, x, y,
24839edbd4a0SFrançois Tigeot 			      crtc->invert_dimensions ? " (inverted)" : "");
24849edbd4a0SFrançois Tigeot 		return -ENOSPC;
24859edbd4a0SFrançois Tigeot 	}
24869edbd4a0SFrançois Tigeot 
24879edbd4a0SFrançois Tigeot 	return 0;
24889edbd4a0SFrançois Tigeot }
2489ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_check_viewport);
24909edbd4a0SFrançois Tigeot 
24914dbb207bSFrançois Tigeot /**
24924dbb207bSFrançois Tigeot  * drm_mode_setcrtc - set CRTC configuration
24934dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
24944dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
24954dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
24965718399fSFrançois Tigeot  *
24975718399fSFrançois Tigeot  * Build a new CRTC configuration based on user request.
24985718399fSFrançois Tigeot  *
24995718399fSFrançois Tigeot  * Called by the user via ioctl.
25005718399fSFrançois Tigeot  *
2501ba55f2f5SFrançois Tigeot  * Returns:
25025718399fSFrançois Tigeot  * Zero on success, errno on failure.
25035718399fSFrançois Tigeot  */
25045718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data,
25055718399fSFrançois Tigeot 		     struct drm_file *file_priv)
25065718399fSFrançois Tigeot {
25075718399fSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
25085718399fSFrançois Tigeot 	struct drm_mode_crtc *crtc_req = data;
25095718399fSFrançois Tigeot 	struct drm_crtc *crtc;
25105718399fSFrançois Tigeot 	struct drm_connector **connector_set = NULL, *connector;
25115718399fSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
25125718399fSFrançois Tigeot 	struct drm_display_mode *mode = NULL;
25135718399fSFrançois Tigeot 	struct drm_mode_set set;
2514b5162e19SFrançois Tigeot 	uint32_t __user *set_connectors_ptr;
2515b5162e19SFrançois Tigeot 	int ret;
25165718399fSFrançois Tigeot 	int i;
25175718399fSFrançois Tigeot 
25185718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2519b5162e19SFrançois Tigeot 		return -EINVAL;
25205718399fSFrançois Tigeot 
25215718399fSFrançois Tigeot 	/* For some reason crtc x/y offsets are signed internally. */
25225718399fSFrançois Tigeot 	if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
2523b5162e19SFrançois Tigeot 		return -ERANGE;
25245718399fSFrançois Tigeot 
25254dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
2526ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
2527ba55f2f5SFrançois Tigeot 	if (!crtc) {
25285718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
25299edbd4a0SFrançois Tigeot 		ret = -ENOENT;
25305718399fSFrançois Tigeot 		goto out;
25315718399fSFrançois Tigeot 	}
25325718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
25335718399fSFrançois Tigeot 
25345718399fSFrançois Tigeot 	if (crtc_req->mode_valid) {
25355718399fSFrançois Tigeot 		/* If we have a mode we need a framebuffer. */
25365718399fSFrançois Tigeot 		/* If we pass -1, set the mode with the currently bound fb */
25375718399fSFrançois Tigeot 		if (crtc_req->fb_id == -1) {
2538ba55f2f5SFrançois Tigeot 			if (!crtc->primary->fb) {
25395718399fSFrançois Tigeot 				DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
25405718399fSFrançois Tigeot 				ret = -EINVAL;
25415718399fSFrançois Tigeot 				goto out;
25425718399fSFrançois Tigeot 			}
2543ba55f2f5SFrançois Tigeot 			fb = crtc->primary->fb;
25444dbb207bSFrançois Tigeot 			/* Make refcounting symmetric with the lookup path. */
25454dbb207bSFrançois Tigeot 			drm_framebuffer_reference(fb);
25465718399fSFrançois Tigeot 		} else {
25474dbb207bSFrançois Tigeot 			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
25484dbb207bSFrançois Tigeot 			if (!fb) {
25495718399fSFrançois Tigeot 				DRM_DEBUG_KMS("Unknown FB ID%d\n",
25505718399fSFrançois Tigeot 						crtc_req->fb_id);
25519edbd4a0SFrançois Tigeot 				ret = -ENOENT;
25525718399fSFrançois Tigeot 				goto out;
25535718399fSFrançois Tigeot 			}
25545718399fSFrançois Tigeot 		}
25555718399fSFrançois Tigeot 
25565718399fSFrançois Tigeot 		mode = drm_mode_create(dev);
25575718399fSFrançois Tigeot 		if (!mode) {
2558b5162e19SFrançois Tigeot 			ret = -ENOMEM;
25595718399fSFrançois Tigeot 			goto out;
25605718399fSFrançois Tigeot 		}
25615718399fSFrançois Tigeot 
25625718399fSFrançois Tigeot 		ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
25635718399fSFrançois Tigeot 		if (ret) {
25645718399fSFrançois Tigeot 			DRM_DEBUG_KMS("Invalid mode\n");
25655718399fSFrançois Tigeot 			goto out;
25665718399fSFrançois Tigeot 		}
25675718399fSFrançois Tigeot 
25685718399fSFrançois Tigeot 		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
25695718399fSFrançois Tigeot 
25709edbd4a0SFrançois Tigeot 		ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
25719edbd4a0SFrançois Tigeot 					      mode, fb);
25729edbd4a0SFrançois Tigeot 		if (ret)
25735718399fSFrançois Tigeot 			goto out;
25749edbd4a0SFrançois Tigeot 
25755718399fSFrançois Tigeot 	}
25765718399fSFrançois Tigeot 
25775718399fSFrançois Tigeot 	if (crtc_req->count_connectors == 0 && mode) {
25785718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
2579b5162e19SFrançois Tigeot 		ret = -EINVAL;
25805718399fSFrançois Tigeot 		goto out;
25815718399fSFrançois Tigeot 	}
25825718399fSFrançois Tigeot 
25835718399fSFrançois Tigeot 	if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
25845718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
25855718399fSFrançois Tigeot 			  crtc_req->count_connectors);
2586b5162e19SFrançois Tigeot 		ret = -EINVAL;
25875718399fSFrançois Tigeot 		goto out;
25885718399fSFrançois Tigeot 	}
25895718399fSFrançois Tigeot 
25905718399fSFrançois Tigeot 	if (crtc_req->count_connectors > 0) {
25915718399fSFrançois Tigeot 		u32 out_id;
25925718399fSFrançois Tigeot 
25935718399fSFrançois Tigeot 		/* Avoid unbounded kernel memory allocation */
25945718399fSFrançois Tigeot 		if (crtc_req->count_connectors > config->num_connector) {
2595b5162e19SFrançois Tigeot 			ret = -EINVAL;
25965718399fSFrançois Tigeot 			goto out;
25975718399fSFrançois Tigeot 		}
25985718399fSFrançois Tigeot 
25995718399fSFrançois Tigeot 		connector_set = kmalloc(crtc_req->count_connectors *
26004dbb207bSFrançois Tigeot 					sizeof(struct drm_connector *),
26014dbb207bSFrançois Tigeot 					M_DRM, M_WAITOK);
2602b5162e19SFrançois Tigeot 		if (!connector_set) {
2603b5162e19SFrançois Tigeot 			ret = -ENOMEM;
2604b5162e19SFrançois Tigeot 			goto out;
2605b5162e19SFrançois Tigeot 		}
26065718399fSFrançois Tigeot 
26075718399fSFrançois Tigeot 		for (i = 0; i < crtc_req->count_connectors; i++) {
2608b5162e19SFrançois Tigeot 			set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
2609b5162e19SFrançois Tigeot 			if (get_user(out_id, &set_connectors_ptr[i])) {
2610b5162e19SFrançois Tigeot 				ret = -EFAULT;
26115718399fSFrançois Tigeot 				goto out;
26125718399fSFrançois Tigeot 			}
26135718399fSFrançois Tigeot 
2614ba55f2f5SFrançois Tigeot 			connector = drm_connector_find(dev, out_id);
2615ba55f2f5SFrançois Tigeot 			if (!connector) {
26165718399fSFrançois Tigeot 				DRM_DEBUG_KMS("Connector id %d unknown\n",
26175718399fSFrançois Tigeot 						out_id);
26189edbd4a0SFrançois Tigeot 				ret = -ENOENT;
26195718399fSFrançois Tigeot 				goto out;
26205718399fSFrançois Tigeot 			}
26215718399fSFrançois Tigeot 			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
26225718399fSFrançois Tigeot 					connector->base.id,
2623ba55f2f5SFrançois Tigeot 					connector->name);
26245718399fSFrançois Tigeot 
26255718399fSFrançois Tigeot 			connector_set[i] = connector;
26265718399fSFrançois Tigeot 		}
26275718399fSFrançois Tigeot 	}
26285718399fSFrançois Tigeot 
26295718399fSFrançois Tigeot 	set.crtc = crtc;
26305718399fSFrançois Tigeot 	set.x = crtc_req->x;
26315718399fSFrançois Tigeot 	set.y = crtc_req->y;
26325718399fSFrançois Tigeot 	set.mode = mode;
26335718399fSFrançois Tigeot 	set.connectors = connector_set;
26345718399fSFrançois Tigeot 	set.num_connectors = crtc_req->count_connectors;
26355718399fSFrançois Tigeot 	set.fb = fb;
26364dbb207bSFrançois Tigeot 	ret = drm_mode_set_config_internal(&set);
26375718399fSFrançois Tigeot 
26385718399fSFrançois Tigeot out:
26394dbb207bSFrançois Tigeot 	if (fb)
26404dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
26414dbb207bSFrançois Tigeot 
26424dbb207bSFrançois Tigeot 	kfree(connector_set);
26435718399fSFrançois Tigeot 	drm_mode_destroy(dev, mode);
26444dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
26455718399fSFrançois Tigeot 	return ret;
26465718399fSFrançois Tigeot }
26475718399fSFrançois Tigeot 
2648*24edb884SFrançois Tigeot /**
2649*24edb884SFrançois Tigeot  * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
2650*24edb884SFrançois Tigeot  *     universal plane handler call
2651*24edb884SFrançois Tigeot  * @crtc: crtc to update cursor for
2652*24edb884SFrançois Tigeot  * @req: data pointer for the ioctl
2653*24edb884SFrançois Tigeot  * @file_priv: drm file for the ioctl call
2654*24edb884SFrançois Tigeot  *
2655*24edb884SFrançois Tigeot  * Legacy cursor ioctl's work directly with driver buffer handles.  To
2656*24edb884SFrançois Tigeot  * translate legacy ioctl calls into universal plane handler calls, we need to
2657*24edb884SFrançois Tigeot  * wrap the native buffer handle in a drm_framebuffer.
2658*24edb884SFrançois Tigeot  *
2659*24edb884SFrançois Tigeot  * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
2660*24edb884SFrançois Tigeot  * buffer with a pitch of 4*width; the universal plane interface should be used
2661*24edb884SFrançois Tigeot  * directly in cases where the hardware can support other buffer settings and
2662*24edb884SFrançois Tigeot  * userspace wants to make use of these capabilities.
2663*24edb884SFrançois Tigeot  *
2664*24edb884SFrançois Tigeot  * Returns:
2665*24edb884SFrançois Tigeot  * Zero on success, errno on failure.
2666*24edb884SFrançois Tigeot  */
2667*24edb884SFrançois Tigeot static int drm_mode_cursor_universal(struct drm_crtc *crtc,
2668*24edb884SFrançois Tigeot 				     struct drm_mode_cursor2 *req,
2669*24edb884SFrançois Tigeot 				     struct drm_file *file_priv)
2670*24edb884SFrançois Tigeot {
2671*24edb884SFrançois Tigeot 	struct drm_device *dev = crtc->dev;
2672*24edb884SFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
2673*24edb884SFrançois Tigeot 	struct drm_mode_fb_cmd2 fbreq = {
2674*24edb884SFrançois Tigeot 		.width = req->width,
2675*24edb884SFrançois Tigeot 		.height = req->height,
2676*24edb884SFrançois Tigeot 		.pixel_format = DRM_FORMAT_ARGB8888,
2677*24edb884SFrançois Tigeot 		.pitches = { req->width * 4 },
2678*24edb884SFrançois Tigeot 		.handles = { req->handle },
2679*24edb884SFrançois Tigeot 	};
2680*24edb884SFrançois Tigeot 	int32_t crtc_x, crtc_y;
2681*24edb884SFrançois Tigeot 	uint32_t crtc_w = 0, crtc_h = 0;
2682*24edb884SFrançois Tigeot 	uint32_t src_w = 0, src_h = 0;
2683*24edb884SFrançois Tigeot 	int ret = 0;
2684*24edb884SFrançois Tigeot 
2685*24edb884SFrançois Tigeot 	BUG_ON(!crtc->cursor);
2686*24edb884SFrançois Tigeot 
2687*24edb884SFrançois Tigeot 	/*
2688*24edb884SFrançois Tigeot 	 * Obtain fb we'll be using (either new or existing) and take an extra
2689*24edb884SFrançois Tigeot 	 * reference to it if fb != null.  setplane will take care of dropping
2690*24edb884SFrançois Tigeot 	 * the reference if the plane update fails.
2691*24edb884SFrançois Tigeot 	 */
2692*24edb884SFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_BO) {
2693*24edb884SFrançois Tigeot 		if (req->handle) {
2694*24edb884SFrançois Tigeot 			fb = add_framebuffer_internal(dev, &fbreq, file_priv);
2695*24edb884SFrançois Tigeot 			if (IS_ERR(fb)) {
2696*24edb884SFrançois Tigeot 				DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
2697*24edb884SFrançois Tigeot 				return PTR_ERR(fb);
2698*24edb884SFrançois Tigeot 			}
2699*24edb884SFrançois Tigeot 
2700*24edb884SFrançois Tigeot 			drm_framebuffer_reference(fb);
2701*24edb884SFrançois Tigeot 		} else {
2702*24edb884SFrançois Tigeot 			fb = NULL;
2703*24edb884SFrançois Tigeot 		}
2704*24edb884SFrançois Tigeot 	} else {
2705*24edb884SFrançois Tigeot 		mutex_lock(&dev->mode_config.mutex);
2706*24edb884SFrançois Tigeot 		fb = crtc->cursor->fb;
2707*24edb884SFrançois Tigeot 		if (fb)
2708*24edb884SFrançois Tigeot 			drm_framebuffer_reference(fb);
2709*24edb884SFrançois Tigeot 		mutex_unlock(&dev->mode_config.mutex);
2710*24edb884SFrançois Tigeot 	}
2711*24edb884SFrançois Tigeot 
2712*24edb884SFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_MOVE) {
2713*24edb884SFrançois Tigeot 		crtc_x = req->x;
2714*24edb884SFrançois Tigeot 		crtc_y = req->y;
2715*24edb884SFrançois Tigeot 	} else {
2716*24edb884SFrançois Tigeot 		crtc_x = crtc->cursor_x;
2717*24edb884SFrançois Tigeot 		crtc_y = crtc->cursor_y;
2718*24edb884SFrançois Tigeot 	}
2719*24edb884SFrançois Tigeot 
2720*24edb884SFrançois Tigeot 	if (fb) {
2721*24edb884SFrançois Tigeot 		crtc_w = fb->width;
2722*24edb884SFrançois Tigeot 		crtc_h = fb->height;
2723*24edb884SFrançois Tigeot 		src_w = fb->width << 16;
2724*24edb884SFrançois Tigeot 		src_h = fb->height << 16;
2725*24edb884SFrançois Tigeot 	}
2726*24edb884SFrançois Tigeot 
2727*24edb884SFrançois Tigeot 	/*
2728*24edb884SFrançois Tigeot 	 * setplane_internal will take care of deref'ing either the old or new
2729*24edb884SFrançois Tigeot 	 * framebuffer depending on success.
2730*24edb884SFrançois Tigeot 	 */
2731*24edb884SFrançois Tigeot 	ret = setplane_internal(crtc->cursor, crtc, fb,
2732*24edb884SFrançois Tigeot 				crtc_x, crtc_y, crtc_w, crtc_h,
2733*24edb884SFrançois Tigeot 				0, 0, src_w, src_h);
2734*24edb884SFrançois Tigeot 
2735*24edb884SFrançois Tigeot 	/* Update successful; save new cursor position, if necessary */
2736*24edb884SFrançois Tigeot 	if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
2737*24edb884SFrançois Tigeot 		crtc->cursor_x = req->x;
2738*24edb884SFrançois Tigeot 		crtc->cursor_y = req->y;
2739*24edb884SFrançois Tigeot 	}
2740*24edb884SFrançois Tigeot 
2741*24edb884SFrançois Tigeot 	return ret;
2742*24edb884SFrançois Tigeot }
2743*24edb884SFrançois Tigeot 
274406fede5aSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev,
274506fede5aSFrançois Tigeot 				  struct drm_mode_cursor2 *req,
274606fede5aSFrançois Tigeot 				  struct drm_file *file_priv)
27475718399fSFrançois Tigeot {
27485718399fSFrançois Tigeot 	struct drm_crtc *crtc;
27495718399fSFrançois Tigeot 	int ret = 0;
27505718399fSFrançois Tigeot 
27515718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2752b5162e19SFrançois Tigeot 		return -EINVAL;
27535718399fSFrançois Tigeot 
2754b5162e19SFrançois Tigeot 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
2755b5162e19SFrançois Tigeot 		return -EINVAL;
27565718399fSFrançois Tigeot 
2757ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, req->crtc_id);
2758ba55f2f5SFrançois Tigeot 	if (!crtc) {
27595718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
27609edbd4a0SFrançois Tigeot 		return -ENOENT;
27615718399fSFrançois Tigeot 	}
27625718399fSFrançois Tigeot 
2763*24edb884SFrançois Tigeot 	/*
2764*24edb884SFrançois Tigeot 	 * If this crtc has a universal cursor plane, call that plane's update
2765*24edb884SFrançois Tigeot 	 * handler rather than using legacy cursor handlers.
2766*24edb884SFrançois Tigeot 	 */
2767*24edb884SFrançois Tigeot 	if (crtc->cursor)
2768*24edb884SFrançois Tigeot 		return drm_mode_cursor_universal(crtc, req, file_priv);
2769*24edb884SFrançois Tigeot 
2770ba55f2f5SFrançois Tigeot 	drm_modeset_lock(&crtc->mutex, NULL);
27715718399fSFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_BO) {
277206fede5aSFrançois Tigeot 		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
2773b5162e19SFrançois Tigeot 			ret = -ENXIO;
27745718399fSFrançois Tigeot 			goto out;
27755718399fSFrançois Tigeot 		}
27765718399fSFrançois Tigeot 		/* Turns off the cursor if handle is 0 */
277706fede5aSFrançois Tigeot 		if (crtc->funcs->cursor_set2)
277806fede5aSFrançois Tigeot 			ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
277906fede5aSFrançois Tigeot 						      req->width, req->height, req->hot_x, req->hot_y);
278006fede5aSFrançois Tigeot 		else
2781b5162e19SFrançois Tigeot 			ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
27825718399fSFrançois Tigeot 						      req->width, req->height);
27835718399fSFrançois Tigeot 	}
27845718399fSFrançois Tigeot 
27855718399fSFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_MOVE) {
27865718399fSFrançois Tigeot 		if (crtc->funcs->cursor_move) {
27875718399fSFrançois Tigeot 			ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
27885718399fSFrançois Tigeot 		} else {
2789b5162e19SFrançois Tigeot 			ret = -EFAULT;
27905718399fSFrançois Tigeot 			goto out;
27915718399fSFrançois Tigeot 		}
27925718399fSFrançois Tigeot 	}
27935718399fSFrançois Tigeot out:
2794ba55f2f5SFrançois Tigeot 	drm_modeset_unlock(&crtc->mutex);
27954dbb207bSFrançois Tigeot 
27965718399fSFrançois Tigeot 	return ret;
279706fede5aSFrançois Tigeot 
279806fede5aSFrançois Tigeot }
2799ba55f2f5SFrançois Tigeot 
2800ba55f2f5SFrançois Tigeot 
2801ba55f2f5SFrançois Tigeot /**
2802ba55f2f5SFrançois Tigeot  * drm_mode_cursor_ioctl - set CRTC's cursor configuration
2803ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
2804ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
2805ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
2806ba55f2f5SFrançois Tigeot  *
2807ba55f2f5SFrançois Tigeot  * Set the cursor configuration based on user request.
2808ba55f2f5SFrançois Tigeot  *
2809ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2810ba55f2f5SFrançois Tigeot  *
2811ba55f2f5SFrançois Tigeot  * Returns:
2812ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
2813ba55f2f5SFrançois Tigeot  */
281406fede5aSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev,
281506fede5aSFrançois Tigeot 			  void *data, struct drm_file *file_priv)
281606fede5aSFrançois Tigeot {
281706fede5aSFrançois Tigeot 	struct drm_mode_cursor *req = data;
281806fede5aSFrançois Tigeot 	struct drm_mode_cursor2 new_req;
281906fede5aSFrançois Tigeot 
282006fede5aSFrançois Tigeot 	memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
282106fede5aSFrançois Tigeot 	new_req.hot_x = new_req.hot_y = 0;
282206fede5aSFrançois Tigeot 
282306fede5aSFrançois Tigeot 	return drm_mode_cursor_common(dev, &new_req, file_priv);
282406fede5aSFrançois Tigeot }
282506fede5aSFrançois Tigeot 
2826ba55f2f5SFrançois Tigeot /**
2827ba55f2f5SFrançois Tigeot  * drm_mode_cursor2_ioctl - set CRTC's cursor configuration
2828ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
2829ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
2830ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
2831ba55f2f5SFrançois Tigeot  *
2832ba55f2f5SFrançois Tigeot  * Set the cursor configuration based on user request. This implements the 2nd
2833ba55f2f5SFrançois Tigeot  * version of the cursor ioctl, which allows userspace to additionally specify
2834ba55f2f5SFrançois Tigeot  * the hotspot of the pointer.
2835ba55f2f5SFrançois Tigeot  *
2836ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2837ba55f2f5SFrançois Tigeot  *
2838ba55f2f5SFrançois Tigeot  * Returns:
2839ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
2840ba55f2f5SFrançois Tigeot  */
284106fede5aSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev,
284206fede5aSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
284306fede5aSFrançois Tigeot {
284406fede5aSFrançois Tigeot 	struct drm_mode_cursor2 *req = data;
284506fede5aSFrançois Tigeot 	return drm_mode_cursor_common(dev, req, file_priv);
28465718399fSFrançois Tigeot }
28475718399fSFrançois Tigeot 
2848ba55f2f5SFrançois Tigeot /**
2849ba55f2f5SFrançois Tigeot  * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
2850ba55f2f5SFrançois Tigeot  * @bpp: bits per pixels
2851ba55f2f5SFrançois Tigeot  * @depth: bit depth per pixel
2852ba55f2f5SFrançois Tigeot  *
2853ba55f2f5SFrançois Tigeot  * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
2854ba55f2f5SFrançois Tigeot  * Useful in fbdev emulation code, since that deals in those values.
2855ba55f2f5SFrançois Tigeot  */
28565718399fSFrançois Tigeot uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
28575718399fSFrançois Tigeot {
28585718399fSFrançois Tigeot 	uint32_t fmt;
28595718399fSFrançois Tigeot 
28605718399fSFrançois Tigeot 	switch (bpp) {
28615718399fSFrançois Tigeot 	case 8:
28624dbb207bSFrançois Tigeot 		fmt = DRM_FORMAT_C8;
28635718399fSFrançois Tigeot 		break;
28645718399fSFrançois Tigeot 	case 16:
28655718399fSFrançois Tigeot 		if (depth == 15)
28665718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB1555;
28675718399fSFrançois Tigeot 		else
28685718399fSFrançois Tigeot 			fmt = DRM_FORMAT_RGB565;
28695718399fSFrançois Tigeot 		break;
28705718399fSFrançois Tigeot 	case 24:
28715718399fSFrançois Tigeot 		fmt = DRM_FORMAT_RGB888;
28725718399fSFrançois Tigeot 		break;
28735718399fSFrançois Tigeot 	case 32:
28745718399fSFrançois Tigeot 		if (depth == 24)
28755718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB8888;
28765718399fSFrançois Tigeot 		else if (depth == 30)
28775718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB2101010;
28785718399fSFrançois Tigeot 		else
28795718399fSFrançois Tigeot 			fmt = DRM_FORMAT_ARGB8888;
28805718399fSFrançois Tigeot 		break;
28815718399fSFrançois Tigeot 	default:
2882b5162e19SFrançois Tigeot 		DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
28835718399fSFrançois Tigeot 		fmt = DRM_FORMAT_XRGB8888;
28845718399fSFrançois Tigeot 		break;
28855718399fSFrançois Tigeot 	}
28865718399fSFrançois Tigeot 
28875718399fSFrançois Tigeot 	return fmt;
28885718399fSFrançois Tigeot }
2889b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_legacy_fb_format);
28905718399fSFrançois Tigeot 
28915718399fSFrançois Tigeot /**
28925718399fSFrançois Tigeot  * drm_mode_addfb - add an FB to the graphics configuration
28934dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
28944dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
28954dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
28965718399fSFrançois Tigeot  *
2897ba55f2f5SFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request. This is the
2898ba55f2f5SFrançois Tigeot  * original addfb ioclt which only supported RGB formats.
28995718399fSFrançois Tigeot  *
29005718399fSFrançois Tigeot  * Called by the user via ioctl.
29015718399fSFrançois Tigeot  *
2902ba55f2f5SFrançois Tigeot  * Returns:
29035718399fSFrançois Tigeot  * Zero on success, errno on failure.
29045718399fSFrançois Tigeot  */
29055718399fSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev,
29065718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
29075718399fSFrançois Tigeot {
29085718399fSFrançois Tigeot 	struct drm_mode_fb_cmd *or = data;
29095718399fSFrançois Tigeot 	struct drm_mode_fb_cmd2 r = {};
29105718399fSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
29115718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
29125718399fSFrançois Tigeot 	int ret = 0;
29135718399fSFrançois Tigeot 
29145718399fSFrançois Tigeot 	/* Use new struct with format internally */
29155718399fSFrançois Tigeot 	r.fb_id = or->fb_id;
29165718399fSFrançois Tigeot 	r.width = or->width;
29175718399fSFrançois Tigeot 	r.height = or->height;
29185718399fSFrançois Tigeot 	r.pitches[0] = or->pitch;
29195718399fSFrançois Tigeot 	r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
29205718399fSFrançois Tigeot 	r.handles[0] = or->handle;
29215718399fSFrançois Tigeot 
29225718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2923b5162e19SFrançois Tigeot 		return -EINVAL;
29245718399fSFrançois Tigeot 
29255718399fSFrançois Tigeot 	if ((config->min_width > r.width) || (r.width > config->max_width))
2926b5162e19SFrançois Tigeot 		return -EINVAL;
2927b5162e19SFrançois Tigeot 
29285718399fSFrançois Tigeot 	if ((config->min_height > r.height) || (r.height > config->max_height))
2929b5162e19SFrançois Tigeot 		return -EINVAL;
29305718399fSFrançois Tigeot 
293160fc7eecSFrançois Tigeot 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);
293260fc7eecSFrançois Tigeot 	if (IS_ERR(fb)) {
293360fc7eecSFrançois Tigeot 		DRM_DEBUG_KMS("could not create framebuffer\n");
29344dbb207bSFrançois Tigeot 		return PTR_ERR(fb);
29355718399fSFrançois Tigeot 	}
29365718399fSFrançois Tigeot 
29374dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
29385718399fSFrançois Tigeot 	or->fb_id = fb->base.id;
29395718399fSFrançois Tigeot 	list_add(&fb->filp_head, &file_priv->fbs);
29405718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
29414dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
29425718399fSFrançois Tigeot 
29435718399fSFrançois Tigeot 	return ret;
29445718399fSFrançois Tigeot }
29455718399fSFrançois Tigeot 
2946b5162e19SFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r)
29475718399fSFrançois Tigeot {
29485718399fSFrançois Tigeot 	uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
29495718399fSFrançois Tigeot 
29505718399fSFrançois Tigeot 	switch (format) {
29515718399fSFrançois Tigeot 	case DRM_FORMAT_C8:
29525718399fSFrançois Tigeot 	case DRM_FORMAT_RGB332:
29535718399fSFrançois Tigeot 	case DRM_FORMAT_BGR233:
29545718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB4444:
29555718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR4444:
29565718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX4444:
29575718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX4444:
29585718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB4444:
29595718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR4444:
29605718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA4444:
29615718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA4444:
29625718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB1555:
29635718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR1555:
29645718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX5551:
29655718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX5551:
29665718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB1555:
29675718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR1555:
29685718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA5551:
29695718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA5551:
29705718399fSFrançois Tigeot 	case DRM_FORMAT_RGB565:
29715718399fSFrançois Tigeot 	case DRM_FORMAT_BGR565:
29725718399fSFrançois Tigeot 	case DRM_FORMAT_RGB888:
29735718399fSFrançois Tigeot 	case DRM_FORMAT_BGR888:
29745718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB8888:
29755718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR8888:
29765718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX8888:
29775718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX8888:
29785718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB8888:
29795718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR8888:
29805718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA8888:
29815718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA8888:
29825718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB2101010:
29835718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR2101010:
29845718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX1010102:
29855718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX1010102:
29865718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB2101010:
29875718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR2101010:
29885718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA1010102:
29895718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA1010102:
29905718399fSFrançois Tigeot 	case DRM_FORMAT_YUYV:
29915718399fSFrançois Tigeot 	case DRM_FORMAT_YVYU:
29925718399fSFrançois Tigeot 	case DRM_FORMAT_UYVY:
29935718399fSFrançois Tigeot 	case DRM_FORMAT_VYUY:
29945718399fSFrançois Tigeot 	case DRM_FORMAT_AYUV:
29955718399fSFrançois Tigeot 	case DRM_FORMAT_NV12:
29965718399fSFrançois Tigeot 	case DRM_FORMAT_NV21:
29975718399fSFrançois Tigeot 	case DRM_FORMAT_NV16:
29985718399fSFrançois Tigeot 	case DRM_FORMAT_NV61:
2999b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
3000b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
30015718399fSFrançois Tigeot 	case DRM_FORMAT_YUV410:
30025718399fSFrançois Tigeot 	case DRM_FORMAT_YVU410:
30035718399fSFrançois Tigeot 	case DRM_FORMAT_YUV411:
30045718399fSFrançois Tigeot 	case DRM_FORMAT_YVU411:
30055718399fSFrançois Tigeot 	case DRM_FORMAT_YUV420:
30065718399fSFrançois Tigeot 	case DRM_FORMAT_YVU420:
30075718399fSFrançois Tigeot 	case DRM_FORMAT_YUV422:
30085718399fSFrançois Tigeot 	case DRM_FORMAT_YVU422:
30095718399fSFrançois Tigeot 	case DRM_FORMAT_YUV444:
30105718399fSFrançois Tigeot 	case DRM_FORMAT_YVU444:
30115718399fSFrançois Tigeot 		return 0;
30125718399fSFrançois Tigeot 	default:
30139edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("invalid pixel format %s\n",
30149edbd4a0SFrançois Tigeot 			      drm_get_format_name(r->pixel_format));
3015b5162e19SFrançois Tigeot 		return -EINVAL;
30165718399fSFrançois Tigeot 	}
30175718399fSFrançois Tigeot }
30185718399fSFrançois Tigeot 
3019b5162e19SFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
3020b5162e19SFrançois Tigeot {
3021b5162e19SFrançois Tigeot 	int ret, hsub, vsub, num_planes, i;
3022b5162e19SFrançois Tigeot 
3023b5162e19SFrançois Tigeot 	ret = format_check(r);
3024b5162e19SFrançois Tigeot 	if (ret) {
302506fede5aSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer format %s\n",
302606fede5aSFrançois Tigeot 			      drm_get_format_name(r->pixel_format));
3027b5162e19SFrançois Tigeot 		return ret;
3028b5162e19SFrançois Tigeot 	}
3029b5162e19SFrançois Tigeot 
3030b5162e19SFrançois Tigeot 	hsub = drm_format_horz_chroma_subsampling(r->pixel_format);
3031b5162e19SFrançois Tigeot 	vsub = drm_format_vert_chroma_subsampling(r->pixel_format);
3032b5162e19SFrançois Tigeot 	num_planes = drm_format_num_planes(r->pixel_format);
3033b5162e19SFrançois Tigeot 
3034b5162e19SFrançois Tigeot 	if (r->width == 0 || r->width % hsub) {
3035b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height);
3036b5162e19SFrançois Tigeot 		return -EINVAL;
3037b5162e19SFrançois Tigeot 	}
3038b5162e19SFrançois Tigeot 
3039b5162e19SFrançois Tigeot 	if (r->height == 0 || r->height % vsub) {
3040b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
3041b5162e19SFrançois Tigeot 		return -EINVAL;
3042b5162e19SFrançois Tigeot 	}
3043b5162e19SFrançois Tigeot 
3044b5162e19SFrançois Tigeot 	for (i = 0; i < num_planes; i++) {
3045b5162e19SFrançois Tigeot 		unsigned int width = r->width / (i != 0 ? hsub : 1);
3046b5162e19SFrançois Tigeot 		unsigned int height = r->height / (i != 0 ? vsub : 1);
3047b5162e19SFrançois Tigeot 		unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
3048b5162e19SFrançois Tigeot 
3049b5162e19SFrançois Tigeot 		if (!r->handles[i]) {
3050b5162e19SFrançois Tigeot 			DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
3051b5162e19SFrançois Tigeot 			return -EINVAL;
3052b5162e19SFrançois Tigeot 		}
3053b5162e19SFrançois Tigeot 
3054b5162e19SFrançois Tigeot 		if ((uint64_t) width * cpp > UINT_MAX)
3055b5162e19SFrançois Tigeot 			return -ERANGE;
3056b5162e19SFrançois Tigeot 
3057b5162e19SFrançois Tigeot 		if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
3058b5162e19SFrançois Tigeot 			return -ERANGE;
3059b5162e19SFrançois Tigeot 
3060b5162e19SFrançois Tigeot 		if (r->pitches[i] < width * cpp) {
3061b5162e19SFrançois Tigeot 			DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
3062b5162e19SFrançois Tigeot 			return -EINVAL;
3063b5162e19SFrançois Tigeot 		}
3064b5162e19SFrançois Tigeot 	}
3065b5162e19SFrançois Tigeot 
3066b5162e19SFrançois Tigeot 	return 0;
3067b5162e19SFrançois Tigeot }
3068b5162e19SFrançois Tigeot 
3069*24edb884SFrançois Tigeot static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
3070*24edb884SFrançois Tigeot 							struct drm_mode_fb_cmd2 *r,
3071*24edb884SFrançois Tigeot 							struct drm_file *file_priv)
3072*24edb884SFrançois Tigeot {
3073*24edb884SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
3074*24edb884SFrançois Tigeot 	struct drm_framebuffer *fb;
3075*24edb884SFrançois Tigeot 	int ret;
3076*24edb884SFrançois Tigeot 
3077*24edb884SFrançois Tigeot 	if (r->flags & ~DRM_MODE_FB_INTERLACED) {
3078*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
3079*24edb884SFrançois Tigeot 		return ERR_PTR(-EINVAL);
3080*24edb884SFrançois Tigeot 	}
3081*24edb884SFrançois Tigeot 
3082*24edb884SFrançois Tigeot 	if ((config->min_width > r->width) || (r->width > config->max_width)) {
3083*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
3084*24edb884SFrançois Tigeot 			  r->width, config->min_width, config->max_width);
3085*24edb884SFrançois Tigeot 		return ERR_PTR(-EINVAL);
3086*24edb884SFrançois Tigeot 	}
3087*24edb884SFrançois Tigeot 	if ((config->min_height > r->height) || (r->height > config->max_height)) {
3088*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
3089*24edb884SFrançois Tigeot 			  r->height, config->min_height, config->max_height);
3090*24edb884SFrançois Tigeot 		return ERR_PTR(-EINVAL);
3091*24edb884SFrançois Tigeot 	}
3092*24edb884SFrançois Tigeot 
3093*24edb884SFrançois Tigeot 	ret = framebuffer_check(r);
3094*24edb884SFrançois Tigeot 	if (ret)
3095*24edb884SFrançois Tigeot 		return ERR_PTR(ret);
3096*24edb884SFrançois Tigeot 
3097*24edb884SFrançois Tigeot 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
3098*24edb884SFrançois Tigeot 	if (IS_ERR(fb)) {
3099*24edb884SFrançois Tigeot 		DRM_DEBUG_KMS("could not create framebuffer\n");
3100*24edb884SFrançois Tigeot 		return fb;
3101*24edb884SFrançois Tigeot 	}
3102*24edb884SFrançois Tigeot 
3103*24edb884SFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
3104*24edb884SFrançois Tigeot 	r->fb_id = fb->base.id;
3105*24edb884SFrançois Tigeot 	list_add(&fb->filp_head, &file_priv->fbs);
3106*24edb884SFrançois Tigeot 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
3107*24edb884SFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
3108*24edb884SFrançois Tigeot 
3109*24edb884SFrançois Tigeot 	return fb;
3110*24edb884SFrançois Tigeot }
3111*24edb884SFrançois Tigeot 
31125718399fSFrançois Tigeot /**
31135718399fSFrançois Tigeot  * drm_mode_addfb2 - add an FB to the graphics configuration
31144dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
31154dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
31164dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
31175718399fSFrançois Tigeot  *
3118ba55f2f5SFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request with format. This is
3119ba55f2f5SFrançois Tigeot  * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
3120ba55f2f5SFrançois Tigeot  * and uses fourcc codes as pixel format specifiers.
31215718399fSFrançois Tigeot  *
31225718399fSFrançois Tigeot  * Called by the user via ioctl.
31235718399fSFrançois Tigeot  *
3124ba55f2f5SFrançois Tigeot  * Returns:
31255718399fSFrançois Tigeot  * Zero on success, errno on failure.
31265718399fSFrançois Tigeot  */
31275718399fSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev,
31285718399fSFrançois Tigeot 		    void *data, struct drm_file *file_priv)
31295718399fSFrançois Tigeot {
31305718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
31315718399fSFrançois Tigeot 
31325718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3133b5162e19SFrançois Tigeot 		return -EINVAL;
3134b5162e19SFrançois Tigeot 
3135*24edb884SFrançois Tigeot 	fb = add_framebuffer_internal(dev, data, file_priv);
3136*24edb884SFrançois Tigeot 	if (IS_ERR(fb))
31374dbb207bSFrançois Tigeot 		return PTR_ERR(fb);
31385718399fSFrançois Tigeot 
3139*24edb884SFrançois Tigeot 	return 0;
31405718399fSFrançois Tigeot }
31415718399fSFrançois Tigeot 
31425718399fSFrançois Tigeot /**
31435718399fSFrançois Tigeot  * drm_mode_rmfb - remove an FB from the configuration
31444dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
31454dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
31464dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
31475718399fSFrançois Tigeot  *
31485718399fSFrançois Tigeot  * Remove the FB specified by the user.
31495718399fSFrançois Tigeot  *
31505718399fSFrançois Tigeot  * Called by the user via ioctl.
31515718399fSFrançois Tigeot  *
3152ba55f2f5SFrançois Tigeot  * Returns:
31535718399fSFrançois Tigeot  * Zero on success, errno on failure.
31545718399fSFrançois Tigeot  */
31555718399fSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev,
31565718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
31575718399fSFrançois Tigeot {
31585718399fSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
31595718399fSFrançois Tigeot 	struct drm_framebuffer *fbl = NULL;
31605718399fSFrançois Tigeot 	uint32_t *id = data;
31615718399fSFrançois Tigeot 	int found = 0;
31625718399fSFrançois Tigeot 
31635718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3164b5162e19SFrançois Tigeot 		return -EINVAL;
31655718399fSFrançois Tigeot 
31664dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
31674dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
31684dbb207bSFrançois Tigeot 	fb = __drm_framebuffer_lookup(dev, *id);
31694dbb207bSFrançois Tigeot 	if (!fb)
31704dbb207bSFrançois Tigeot 		goto fail_lookup;
31715718399fSFrançois Tigeot 
31725718399fSFrançois Tigeot 	list_for_each_entry(fbl, &file_priv->fbs, filp_head)
31735718399fSFrançois Tigeot 		if (fb == fbl)
31745718399fSFrançois Tigeot 			found = 1;
31754dbb207bSFrançois Tigeot 	if (!found)
31764dbb207bSFrançois Tigeot 		goto fail_lookup;
31775718399fSFrançois Tigeot 
31784dbb207bSFrançois Tigeot 	/* Mark fb as reaped, we still have a ref from fpriv->fbs. */
31794dbb207bSFrançois Tigeot 	__drm_framebuffer_unregister(dev, fb);
31804dbb207bSFrançois Tigeot 
31814dbb207bSFrançois Tigeot 	list_del_init(&fb->filp_head);
31824dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
31834dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
31845718399fSFrançois Tigeot 
3185b5162e19SFrançois Tigeot 	drm_framebuffer_remove(fb);
31865718399fSFrançois Tigeot 
31874dbb207bSFrançois Tigeot 	return 0;
31884dbb207bSFrançois Tigeot 
31894dbb207bSFrançois Tigeot fail_lookup:
31904dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
31914dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
31924dbb207bSFrançois Tigeot 
31939edbd4a0SFrançois Tigeot 	return -ENOENT;
31945718399fSFrançois Tigeot }
31955718399fSFrançois Tigeot 
31965718399fSFrançois Tigeot /**
31975718399fSFrançois Tigeot  * drm_mode_getfb - get FB info
31984dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
31994dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
32004dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
32015718399fSFrançois Tigeot  *
32025718399fSFrançois Tigeot  * Lookup the FB given its ID and return info about it.
32035718399fSFrançois Tigeot  *
32045718399fSFrançois Tigeot  * Called by the user via ioctl.
32055718399fSFrançois Tigeot  *
3206ba55f2f5SFrançois Tigeot  * Returns:
32075718399fSFrançois Tigeot  * Zero on success, errno on failure.
32085718399fSFrançois Tigeot  */
32095718399fSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev,
32105718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
32115718399fSFrançois Tigeot {
32125718399fSFrançois Tigeot 	struct drm_mode_fb_cmd *r = data;
32135718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
32144dbb207bSFrançois Tigeot 	int ret;
32155718399fSFrançois Tigeot 
32165718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3217b5162e19SFrançois Tigeot 		return -EINVAL;
32185718399fSFrançois Tigeot 
32194dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
32204dbb207bSFrançois Tigeot 	if (!fb)
32219edbd4a0SFrançois Tigeot 		return -ENOENT;
32225718399fSFrançois Tigeot 
32235718399fSFrançois Tigeot 	r->height = fb->height;
32245718399fSFrançois Tigeot 	r->width = fb->width;
32255718399fSFrançois Tigeot 	r->depth = fb->depth;
32265718399fSFrançois Tigeot 	r->bpp = fb->bits_per_pixel;
32275718399fSFrançois Tigeot 	r->pitch = fb->pitches[0];
3228ba55f2f5SFrançois Tigeot 	if (fb->funcs->create_handle) {
32294dbb207bSFrançois Tigeot 		ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
3230ba55f2f5SFrançois Tigeot 	} else {
32314dbb207bSFrançois Tigeot 		ret = -ENODEV;
3232ba55f2f5SFrançois Tigeot 	}
32335718399fSFrançois Tigeot 
32344dbb207bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
32354dbb207bSFrançois Tigeot 
32365718399fSFrançois Tigeot 	return ret;
32375718399fSFrançois Tigeot }
32385718399fSFrançois Tigeot 
3239ba55f2f5SFrançois Tigeot /**
3240ba55f2f5SFrançois Tigeot  * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
3241ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
3242ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
3243ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
3244ba55f2f5SFrançois Tigeot  *
3245ba55f2f5SFrançois Tigeot  * Lookup the FB and flush out the damaged area supplied by userspace as a clip
3246ba55f2f5SFrançois Tigeot  * rectangle list. Generic userspace which does frontbuffer rendering must call
3247ba55f2f5SFrançois Tigeot  * this ioctl to flush out the changes on manual-update display outputs, e.g.
3248ba55f2f5SFrançois Tigeot  * usb display-link, mipi manual update panels or edp panel self refresh modes.
3249ba55f2f5SFrançois Tigeot  *
3250ba55f2f5SFrançois Tigeot  * Modesetting drivers which always update the frontbuffer do not need to
3251ba55f2f5SFrançois Tigeot  * implement the corresponding ->dirty framebuffer callback.
3252ba55f2f5SFrançois Tigeot  *
3253ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
3254ba55f2f5SFrançois Tigeot  *
3255ba55f2f5SFrançois Tigeot  * Returns:
3256ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
3257ba55f2f5SFrançois Tigeot  */
32585718399fSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
32595718399fSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
32605718399fSFrançois Tigeot {
32615718399fSFrançois Tigeot 	struct drm_clip_rect __user *clips_ptr;
32625718399fSFrançois Tigeot 	struct drm_clip_rect *clips = NULL;
32635718399fSFrançois Tigeot 	struct drm_mode_fb_dirty_cmd *r = data;
32645718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
32655718399fSFrançois Tigeot 	unsigned flags;
32665718399fSFrançois Tigeot 	int num_clips;
3267b5162e19SFrançois Tigeot 	int ret;
32685718399fSFrançois Tigeot 
32695718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3270b5162e19SFrançois Tigeot 		return -EINVAL;
32715718399fSFrançois Tigeot 
32724dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
32734dbb207bSFrançois Tigeot 	if (!fb)
32749edbd4a0SFrançois Tigeot 		return -ENOENT;
32755718399fSFrançois Tigeot 
32765718399fSFrançois Tigeot 	num_clips = r->num_clips;
3277b5162e19SFrançois Tigeot 	clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
32785718399fSFrançois Tigeot 
32795718399fSFrançois Tigeot 	if (!num_clips != !clips_ptr) {
3280b5162e19SFrançois Tigeot 		ret = -EINVAL;
32815718399fSFrançois Tigeot 		goto out_err1;
32825718399fSFrançois Tigeot 	}
32835718399fSFrançois Tigeot 
32845718399fSFrançois Tigeot 	flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
32855718399fSFrançois Tigeot 
32865718399fSFrançois Tigeot 	/* If userspace annotates copy, clips must come in pairs */
32875718399fSFrançois Tigeot 	if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
3288b5162e19SFrançois Tigeot 		ret = -EINVAL;
32895718399fSFrançois Tigeot 		goto out_err1;
32905718399fSFrançois Tigeot 	}
32915718399fSFrançois Tigeot 
32925718399fSFrançois Tigeot 	if (num_clips && clips_ptr) {
32935718399fSFrançois Tigeot 		if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
3294b5162e19SFrançois Tigeot 			ret = -EINVAL;
32955718399fSFrançois Tigeot 			goto out_err1;
32965718399fSFrançois Tigeot 		}
32974dbb207bSFrançois Tigeot 		clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
3298b5162e19SFrançois Tigeot 		if (!clips) {
3299b5162e19SFrançois Tigeot 			ret = -ENOMEM;
3300b5162e19SFrançois Tigeot 			goto out_err1;
3301b5162e19SFrançois Tigeot 		}
33025718399fSFrançois Tigeot 
3303b5162e19SFrançois Tigeot 		ret = copy_from_user(clips, clips_ptr,
3304b5162e19SFrançois Tigeot 				     num_clips * sizeof(*clips));
3305b5162e19SFrançois Tigeot 		if (ret) {
3306b5162e19SFrançois Tigeot 			ret = -EFAULT;
33075718399fSFrançois Tigeot 			goto out_err2;
33085718399fSFrançois Tigeot 		}
3309b5162e19SFrançois Tigeot 	}
33105718399fSFrançois Tigeot 
33115718399fSFrançois Tigeot 	if (fb->funcs->dirty) {
3312b5162e19SFrançois Tigeot 		ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
33135718399fSFrançois Tigeot 				       clips, num_clips);
33145718399fSFrançois Tigeot 	} else {
3315b5162e19SFrançois Tigeot 		ret = -ENOSYS;
33165718399fSFrançois Tigeot 	}
33175718399fSFrançois Tigeot 
33185718399fSFrançois Tigeot out_err2:
33194dbb207bSFrançois Tigeot 	kfree(clips);
33205718399fSFrançois Tigeot out_err1:
33214dbb207bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
33224dbb207bSFrançois Tigeot 
33235718399fSFrançois Tigeot 	return ret;
33245718399fSFrançois Tigeot }
33255718399fSFrançois Tigeot 
33265718399fSFrançois Tigeot 
33275718399fSFrançois Tigeot /**
33285718399fSFrançois Tigeot  * drm_fb_release - remove and free the FBs on this file
33294dbb207bSFrançois Tigeot  * @priv: drm file for the ioctl
33305718399fSFrançois Tigeot  *
33315718399fSFrançois Tigeot  * Destroy all the FBs associated with @filp.
33325718399fSFrançois Tigeot  *
33335718399fSFrançois Tigeot  * Called by the user via ioctl.
33345718399fSFrançois Tigeot  *
3335ba55f2f5SFrançois Tigeot  * Returns:
33365718399fSFrançois Tigeot  * Zero on success, errno on failure.
33375718399fSFrançois Tigeot  */
33385718399fSFrançois Tigeot void drm_fb_release(struct drm_file *priv)
33395718399fSFrançois Tigeot {
33404dbb207bSFrançois Tigeot 	struct drm_device *dev = priv->dev;
33415718399fSFrançois Tigeot 	struct drm_framebuffer *fb, *tfb;
33425718399fSFrançois Tigeot 
33434dbb207bSFrançois Tigeot 	mutex_lock(&priv->fbs_lock);
33445718399fSFrançois Tigeot 	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
33454dbb207bSFrançois Tigeot 
33464dbb207bSFrançois Tigeot 		mutex_lock(&dev->mode_config.fb_lock);
33474dbb207bSFrançois Tigeot 		/* Mark fb as reaped, we still have a ref from fpriv->fbs. */
33484dbb207bSFrançois Tigeot 		__drm_framebuffer_unregister(dev, fb);
33494dbb207bSFrançois Tigeot 		mutex_unlock(&dev->mode_config.fb_lock);
33504dbb207bSFrançois Tigeot 
33514dbb207bSFrançois Tigeot 		list_del_init(&fb->filp_head);
33524dbb207bSFrançois Tigeot 
33534dbb207bSFrançois Tigeot 		/* This will also drop the fpriv->fbs reference. */
3354b5162e19SFrançois Tigeot 		drm_framebuffer_remove(fb);
33555718399fSFrançois Tigeot 	}
33564dbb207bSFrançois Tigeot 	mutex_unlock(&priv->fbs_lock);
33575718399fSFrançois Tigeot }
33585718399fSFrançois Tigeot 
3359ba55f2f5SFrançois Tigeot /**
3360ba55f2f5SFrançois Tigeot  * drm_property_create - create a new property type
3361ba55f2f5SFrançois Tigeot  * @dev: drm device
3362ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3363ba55f2f5SFrançois Tigeot  * @name: name of the property
3364ba55f2f5SFrançois Tigeot  * @num_values: number of pre-defined values
3365ba55f2f5SFrançois Tigeot  *
3366ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3367ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3368ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3369ba55f2f5SFrançois Tigeot  *
3370ba55f2f5SFrançois Tigeot  * Returns:
3371ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3372ba55f2f5SFrançois Tigeot  */
33735718399fSFrançois Tigeot struct drm_property *drm_property_create(struct drm_device *dev, int flags,
33745718399fSFrançois Tigeot 					 const char *name, int num_values)
33755718399fSFrançois Tigeot {
33765718399fSFrançois Tigeot 	struct drm_property *property = NULL;
33775718399fSFrançois Tigeot 	int ret;
33785718399fSFrançois Tigeot 
33794dbb207bSFrançois Tigeot 	property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
3380b5162e19SFrançois Tigeot 	if (!property)
3381b5162e19SFrançois Tigeot 		return NULL;
33825718399fSFrançois Tigeot 
3383ba55f2f5SFrançois Tigeot 	property->dev = dev;
3384ba55f2f5SFrançois Tigeot 
33855718399fSFrançois Tigeot 	if (num_values) {
33864dbb207bSFrançois Tigeot 		property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
3387b5162e19SFrançois Tigeot 		if (!property->values)
3388b5162e19SFrançois Tigeot 			goto fail;
33895718399fSFrançois Tigeot 	}
33905718399fSFrançois Tigeot 
33915718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
33925718399fSFrançois Tigeot 	if (ret)
33935718399fSFrançois Tigeot 		goto fail;
3394b5162e19SFrançois Tigeot 
33955718399fSFrançois Tigeot 	property->flags = flags;
33965718399fSFrançois Tigeot 	property->num_values = num_values;
33975718399fSFrançois Tigeot 	INIT_LIST_HEAD(&property->enum_blob_list);
33985718399fSFrançois Tigeot 
33995718399fSFrançois Tigeot 	if (name) {
34005718399fSFrançois Tigeot 		strncpy(property->name, name, DRM_PROP_NAME_LEN);
34015718399fSFrançois Tigeot 		property->name[DRM_PROP_NAME_LEN-1] = '\0';
34025718399fSFrançois Tigeot 	}
34035718399fSFrançois Tigeot 
34045718399fSFrançois Tigeot 	list_add_tail(&property->head, &dev->mode_config.property_list);
3405ba55f2f5SFrançois Tigeot 
3406ba55f2f5SFrançois Tigeot 	WARN_ON(!drm_property_type_valid(property));
3407ba55f2f5SFrançois Tigeot 
34085718399fSFrançois Tigeot 	return property;
34095718399fSFrançois Tigeot fail:
34104dbb207bSFrançois Tigeot 	kfree(property->values);
34114dbb207bSFrançois Tigeot 	kfree(property);
3412b5162e19SFrançois Tigeot 	return NULL;
34135718399fSFrançois Tigeot }
3414b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create);
34155718399fSFrançois Tigeot 
3416ba55f2f5SFrançois Tigeot /**
3417*24edb884SFrançois Tigeot  * drm_property_create_enum - create a new enumeration property type
3418ba55f2f5SFrançois Tigeot  * @dev: drm device
3419ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3420ba55f2f5SFrançois Tigeot  * @name: name of the property
3421ba55f2f5SFrançois Tigeot  * @props: enumeration lists with property values
3422ba55f2f5SFrançois Tigeot  * @num_values: number of pre-defined values
3423ba55f2f5SFrançois Tigeot  *
3424ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3425ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3426ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3427ba55f2f5SFrançois Tigeot  *
3428ba55f2f5SFrançois Tigeot  * Userspace is only allowed to set one of the predefined values for enumeration
3429ba55f2f5SFrançois Tigeot  * properties.
3430ba55f2f5SFrançois Tigeot  *
3431ba55f2f5SFrançois Tigeot  * Returns:
3432ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3433ba55f2f5SFrançois Tigeot  */
34345718399fSFrançois Tigeot struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
34355718399fSFrançois Tigeot 					 const char *name,
34365718399fSFrançois Tigeot 					 const struct drm_prop_enum_list *props,
34375718399fSFrançois Tigeot 					 int num_values)
34385718399fSFrançois Tigeot {
34395718399fSFrançois Tigeot 	struct drm_property *property;
34405718399fSFrançois Tigeot 	int i, ret;
34415718399fSFrançois Tigeot 
34425718399fSFrançois Tigeot 	flags |= DRM_MODE_PROP_ENUM;
34435718399fSFrançois Tigeot 
34445718399fSFrançois Tigeot 	property = drm_property_create(dev, flags, name, num_values);
34455718399fSFrançois Tigeot 	if (!property)
34465718399fSFrançois Tigeot 		return NULL;
34475718399fSFrançois Tigeot 
34485718399fSFrançois Tigeot 	for (i = 0; i < num_values; i++) {
34495718399fSFrançois Tigeot 		ret = drm_property_add_enum(property, i,
34505718399fSFrançois Tigeot 				      props[i].type,
34515718399fSFrançois Tigeot 				      props[i].name);
34525718399fSFrançois Tigeot 		if (ret) {
34535718399fSFrançois Tigeot 			drm_property_destroy(dev, property);
34545718399fSFrançois Tigeot 			return NULL;
34555718399fSFrançois Tigeot 		}
34565718399fSFrançois Tigeot 	}
34575718399fSFrançois Tigeot 
34585718399fSFrançois Tigeot 	return property;
34595718399fSFrançois Tigeot }
3460b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_enum);
3461b5162e19SFrançois Tigeot 
3462ba55f2f5SFrançois Tigeot /**
3463*24edb884SFrançois Tigeot  * drm_property_create_bitmask - create a new bitmask property type
3464ba55f2f5SFrançois Tigeot  * @dev: drm device
3465ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3466ba55f2f5SFrançois Tigeot  * @name: name of the property
3467ba55f2f5SFrançois Tigeot  * @props: enumeration lists with property bitflags
3468ba55f2f5SFrançois Tigeot  * @num_values: number of pre-defined values
3469ba55f2f5SFrançois Tigeot  *
3470ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3471ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3472ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3473ba55f2f5SFrançois Tigeot  *
3474ba55f2f5SFrançois Tigeot  * Compared to plain enumeration properties userspace is allowed to set any
3475ba55f2f5SFrançois Tigeot  * or'ed together combination of the predefined property bitflag values
3476ba55f2f5SFrançois Tigeot  *
3477ba55f2f5SFrançois Tigeot  * Returns:
3478ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3479ba55f2f5SFrançois Tigeot  */
3480b5162e19SFrançois Tigeot struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
3481b5162e19SFrançois Tigeot 					 int flags, const char *name,
3482b5162e19SFrançois Tigeot 					 const struct drm_prop_enum_list *props,
3483b5162e19SFrançois Tigeot 					 int num_values)
3484b5162e19SFrançois Tigeot {
3485b5162e19SFrançois Tigeot 	struct drm_property *property;
3486b5162e19SFrançois Tigeot 	int i, ret;
3487b5162e19SFrançois Tigeot 
3488b5162e19SFrançois Tigeot 	flags |= DRM_MODE_PROP_BITMASK;
3489b5162e19SFrançois Tigeot 
3490b5162e19SFrançois Tigeot 	property = drm_property_create(dev, flags, name, num_values);
3491b5162e19SFrançois Tigeot 	if (!property)
3492b5162e19SFrançois Tigeot 		return NULL;
3493b5162e19SFrançois Tigeot 
3494b5162e19SFrançois Tigeot 	for (i = 0; i < num_values; i++) {
3495b5162e19SFrançois Tigeot 		ret = drm_property_add_enum(property, i,
3496b5162e19SFrançois Tigeot 				      props[i].type,
3497b5162e19SFrançois Tigeot 				      props[i].name);
3498b5162e19SFrançois Tigeot 		if (ret) {
3499b5162e19SFrançois Tigeot 			drm_property_destroy(dev, property);
3500b5162e19SFrançois Tigeot 			return NULL;
3501b5162e19SFrançois Tigeot 		}
3502b5162e19SFrançois Tigeot 	}
3503b5162e19SFrançois Tigeot 
3504b5162e19SFrançois Tigeot 	return property;
3505b5162e19SFrançois Tigeot }
3506b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bitmask);
35075718399fSFrançois Tigeot 
3508ba55f2f5SFrançois Tigeot static struct drm_property *property_create_range(struct drm_device *dev,
3509ba55f2f5SFrançois Tigeot 					 int flags, const char *name,
35105718399fSFrançois Tigeot 					 uint64_t min, uint64_t max)
35115718399fSFrançois Tigeot {
35125718399fSFrançois Tigeot 	struct drm_property *property;
35135718399fSFrançois Tigeot 
35145718399fSFrançois Tigeot 	property = drm_property_create(dev, flags, name, 2);
35155718399fSFrançois Tigeot 	if (!property)
35165718399fSFrançois Tigeot 		return NULL;
35175718399fSFrançois Tigeot 
35185718399fSFrançois Tigeot 	property->values[0] = min;
35195718399fSFrançois Tigeot 	property->values[1] = max;
35205718399fSFrançois Tigeot 
35215718399fSFrançois Tigeot 	return property;
35225718399fSFrançois Tigeot }
3523ba55f2f5SFrançois Tigeot 
3524ba55f2f5SFrançois Tigeot /**
3525*24edb884SFrançois Tigeot  * drm_property_create_range - create a new ranged property type
3526ba55f2f5SFrançois Tigeot  * @dev: drm device
3527ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3528ba55f2f5SFrançois Tigeot  * @name: name of the property
3529ba55f2f5SFrançois Tigeot  * @min: minimum value of the property
3530ba55f2f5SFrançois Tigeot  * @max: maximum value of the property
3531ba55f2f5SFrançois Tigeot  *
3532ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3533ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3534ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3535ba55f2f5SFrançois Tigeot  *
3536ba55f2f5SFrançois Tigeot  * Userspace is allowed to set any interger value in the (min, max) range
3537ba55f2f5SFrançois Tigeot  * inclusive.
3538ba55f2f5SFrançois Tigeot  *
3539ba55f2f5SFrançois Tigeot  * Returns:
3540ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3541ba55f2f5SFrançois Tigeot  */
3542ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
3543ba55f2f5SFrançois Tigeot 					 const char *name,
3544ba55f2f5SFrançois Tigeot 					 uint64_t min, uint64_t max)
3545ba55f2f5SFrançois Tigeot {
3546ba55f2f5SFrançois Tigeot 	return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
3547ba55f2f5SFrançois Tigeot 			name, min, max);
3548ba55f2f5SFrançois Tigeot }
3549b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_range);
35505718399fSFrançois Tigeot 
3551ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
3552ba55f2f5SFrançois Tigeot 					 int flags, const char *name,
3553ba55f2f5SFrançois Tigeot 					 int64_t min, int64_t max)
3554ba55f2f5SFrançois Tigeot {
3555ba55f2f5SFrançois Tigeot 	return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
3556ba55f2f5SFrançois Tigeot 			name, I642U64(min), I642U64(max));
3557ba55f2f5SFrançois Tigeot }
3558ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_signed_range);
3559ba55f2f5SFrançois Tigeot 
3560ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_object(struct drm_device *dev,
3561ba55f2f5SFrançois Tigeot 					 int flags, const char *name, uint32_t type)
3562ba55f2f5SFrançois Tigeot {
3563ba55f2f5SFrançois Tigeot 	struct drm_property *property;
3564ba55f2f5SFrançois Tigeot 
3565ba55f2f5SFrançois Tigeot 	flags |= DRM_MODE_PROP_OBJECT;
3566ba55f2f5SFrançois Tigeot 
3567ba55f2f5SFrançois Tigeot 	property = drm_property_create(dev, flags, name, 1);
3568ba55f2f5SFrançois Tigeot 	if (!property)
3569ba55f2f5SFrançois Tigeot 		return NULL;
3570ba55f2f5SFrançois Tigeot 
3571ba55f2f5SFrançois Tigeot 	property->values[0] = type;
3572ba55f2f5SFrançois Tigeot 
3573ba55f2f5SFrançois Tigeot 	return property;
3574ba55f2f5SFrançois Tigeot }
3575ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_object);
3576ba55f2f5SFrançois Tigeot 
3577ba55f2f5SFrançois Tigeot /**
3578ba55f2f5SFrançois Tigeot  * drm_property_add_enum - add a possible value to an enumeration property
3579ba55f2f5SFrançois Tigeot  * @property: enumeration property to change
3580ba55f2f5SFrançois Tigeot  * @index: index of the new enumeration
3581ba55f2f5SFrançois Tigeot  * @value: value of the new enumeration
3582ba55f2f5SFrançois Tigeot  * @name: symbolic name of the new enumeration
3583ba55f2f5SFrançois Tigeot  *
3584ba55f2f5SFrançois Tigeot  * This functions adds enumerations to a property.
3585ba55f2f5SFrançois Tigeot  *
3586ba55f2f5SFrançois Tigeot  * It's use is deprecated, drivers should use one of the more specific helpers
3587ba55f2f5SFrançois Tigeot  * to directly create the property with all enumerations already attached.
3588ba55f2f5SFrançois Tigeot  *
3589ba55f2f5SFrançois Tigeot  * Returns:
3590ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
3591ba55f2f5SFrançois Tigeot  */
35925718399fSFrançois Tigeot int drm_property_add_enum(struct drm_property *property, int index,
35935718399fSFrançois Tigeot 			  uint64_t value, const char *name)
35945718399fSFrançois Tigeot {
35955718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum;
35965718399fSFrançois Tigeot 
3597ba55f2f5SFrançois Tigeot 	if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
3598ba55f2f5SFrançois Tigeot 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
3599b5162e19SFrançois Tigeot 		return -EINVAL;
3600b5162e19SFrançois Tigeot 
3601b5162e19SFrançois Tigeot 	/*
3602b5162e19SFrançois Tigeot 	 * Bitmask enum properties have the additional constraint of values
3603b5162e19SFrançois Tigeot 	 * from 0 to 63
3604b5162e19SFrançois Tigeot 	 */
3605ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
3606ba55f2f5SFrançois Tigeot 			(value > 63))
36075718399fSFrançois Tigeot 		return -EINVAL;
36085718399fSFrançois Tigeot 
36095718399fSFrançois Tigeot 	if (!list_empty(&property->enum_blob_list)) {
36105718399fSFrançois Tigeot 		list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
36115718399fSFrançois Tigeot 			if (prop_enum->value == value) {
36125718399fSFrançois Tigeot 				strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
36135718399fSFrançois Tigeot 				prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
36145718399fSFrançois Tigeot 				return 0;
36155718399fSFrançois Tigeot 			}
36165718399fSFrançois Tigeot 		}
36175718399fSFrançois Tigeot 	}
36185718399fSFrançois Tigeot 
36194dbb207bSFrançois Tigeot 	prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
3620b5162e19SFrançois Tigeot 	if (!prop_enum)
3621b5162e19SFrançois Tigeot 		return -ENOMEM;
36225718399fSFrançois Tigeot 
36235718399fSFrançois Tigeot 	strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
36245718399fSFrançois Tigeot 	prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
36255718399fSFrançois Tigeot 	prop_enum->value = value;
36265718399fSFrançois Tigeot 
36275718399fSFrançois Tigeot 	property->values[index] = value;
36285718399fSFrançois Tigeot 	list_add_tail(&prop_enum->head, &property->enum_blob_list);
36295718399fSFrançois Tigeot 	return 0;
36305718399fSFrançois Tigeot }
3631b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_add_enum);
36325718399fSFrançois Tigeot 
3633ba55f2f5SFrançois Tigeot /**
3634ba55f2f5SFrançois Tigeot  * drm_property_destroy - destroy a drm property
3635ba55f2f5SFrançois Tigeot  * @dev: drm device
3636ba55f2f5SFrançois Tigeot  * @property: property to destry
3637ba55f2f5SFrançois Tigeot  *
3638ba55f2f5SFrançois Tigeot  * This function frees a property including any attached resources like
3639ba55f2f5SFrançois Tigeot  * enumeration values.
3640ba55f2f5SFrançois Tigeot  */
36415718399fSFrançois Tigeot void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
36425718399fSFrançois Tigeot {
36435718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum, *pt;
36445718399fSFrançois Tigeot 
36455718399fSFrançois Tigeot 	list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) {
36465718399fSFrançois Tigeot 		list_del(&prop_enum->head);
36474dbb207bSFrançois Tigeot 		kfree(prop_enum);
36485718399fSFrançois Tigeot 	}
36495718399fSFrançois Tigeot 
36505718399fSFrançois Tigeot 	if (property->num_values)
36514dbb207bSFrançois Tigeot 		kfree(property->values);
36525718399fSFrançois Tigeot 	drm_mode_object_put(dev, &property->base);
36535718399fSFrançois Tigeot 	list_del(&property->head);
36544dbb207bSFrançois Tigeot 	kfree(property);
36555718399fSFrançois Tigeot }
3656b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_destroy);
36575718399fSFrançois Tigeot 
3658ba55f2f5SFrançois Tigeot /**
3659ba55f2f5SFrançois Tigeot  * drm_object_attach_property - attach a property to a modeset object
3660ba55f2f5SFrançois Tigeot  * @obj: drm modeset object
3661ba55f2f5SFrançois Tigeot  * @property: property to attach
3662ba55f2f5SFrançois Tigeot  * @init_val: initial value of the property
3663ba55f2f5SFrançois Tigeot  *
3664ba55f2f5SFrançois Tigeot  * This attaches the given property to the modeset object with the given initial
3665ba55f2f5SFrançois Tigeot  * value. Currently this function cannot fail since the properties are stored in
3666ba55f2f5SFrançois Tigeot  * a statically sized array.
3667ba55f2f5SFrançois Tigeot  */
3668b5162e19SFrançois Tigeot void drm_object_attach_property(struct drm_mode_object *obj,
3669b5162e19SFrançois Tigeot 				struct drm_property *property,
3670b5162e19SFrançois Tigeot 				uint64_t init_val)
3671b5162e19SFrançois Tigeot {
3672b5162e19SFrançois Tigeot 	int count = obj->properties->count;
3673b5162e19SFrançois Tigeot 
3674b5162e19SFrançois Tigeot 	if (count == DRM_OBJECT_MAX_PROPERTY) {
3675b5162e19SFrançois Tigeot 		WARN(1, "Failed to attach object property (type: 0x%x). Please "
3676b5162e19SFrançois Tigeot 			"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
3677b5162e19SFrançois Tigeot 			"you see this message on the same object type.\n",
3678b5162e19SFrançois Tigeot 			obj->type);
3679b5162e19SFrançois Tigeot 		return;
3680b5162e19SFrançois Tigeot 	}
3681b5162e19SFrançois Tigeot 
3682b5162e19SFrançois Tigeot 	obj->properties->ids[count] = property->base.id;
3683b5162e19SFrançois Tigeot 	obj->properties->values[count] = init_val;
3684b5162e19SFrançois Tigeot 	obj->properties->count++;
3685b5162e19SFrançois Tigeot }
3686b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_attach_property);
3687b5162e19SFrançois Tigeot 
3688ba55f2f5SFrançois Tigeot /**
3689ba55f2f5SFrançois Tigeot  * drm_object_property_set_value - set the value of a property
3690ba55f2f5SFrançois Tigeot  * @obj: drm mode object to set property value for
3691ba55f2f5SFrançois Tigeot  * @property: property to set
3692ba55f2f5SFrançois Tigeot  * @val: value the property should be set to
3693ba55f2f5SFrançois Tigeot  *
3694ba55f2f5SFrançois Tigeot  * This functions sets a given property on a given object. This function only
3695ba55f2f5SFrançois Tigeot  * changes the software state of the property, it does not call into the
3696ba55f2f5SFrançois Tigeot  * driver's ->set_property callback.
3697ba55f2f5SFrançois Tigeot  *
3698ba55f2f5SFrançois Tigeot  * Returns:
3699ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
3700ba55f2f5SFrançois Tigeot  */
3701b5162e19SFrançois Tigeot int drm_object_property_set_value(struct drm_mode_object *obj,
3702b5162e19SFrançois Tigeot 				  struct drm_property *property, uint64_t val)
37035718399fSFrançois Tigeot {
37045718399fSFrançois Tigeot 	int i;
37055718399fSFrançois Tigeot 
3706b5162e19SFrançois Tigeot 	for (i = 0; i < obj->properties->count; i++) {
3707b5162e19SFrançois Tigeot 		if (obj->properties->ids[i] == property->base.id) {
3708b5162e19SFrançois Tigeot 			obj->properties->values[i] = val;
37095718399fSFrançois Tigeot 			return 0;
37105718399fSFrançois Tigeot 		}
37115718399fSFrançois Tigeot 	}
37125718399fSFrançois Tigeot 
37135718399fSFrançois Tigeot 	return -EINVAL;
37145718399fSFrançois Tigeot }
3715b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_set_value);
37165718399fSFrançois Tigeot 
3717ba55f2f5SFrançois Tigeot /**
3718ba55f2f5SFrançois Tigeot  * drm_object_property_get_value - retrieve the value of a property
3719ba55f2f5SFrançois Tigeot  * @obj: drm mode object to get property value from
3720ba55f2f5SFrançois Tigeot  * @property: property to retrieve
3721ba55f2f5SFrançois Tigeot  * @val: storage for the property value
3722ba55f2f5SFrançois Tigeot  *
3723ba55f2f5SFrançois Tigeot  * This function retrieves the softare state of the given property for the given
3724ba55f2f5SFrançois Tigeot  * property. Since there is no driver callback to retrieve the current property
3725ba55f2f5SFrançois Tigeot  * value this might be out of sync with the hardware, depending upon the driver
3726ba55f2f5SFrançois Tigeot  * and property.
3727ba55f2f5SFrançois Tigeot  *
3728ba55f2f5SFrançois Tigeot  * Returns:
3729ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
3730ba55f2f5SFrançois Tigeot  */
3731b5162e19SFrançois Tigeot int drm_object_property_get_value(struct drm_mode_object *obj,
37325718399fSFrançois Tigeot 				  struct drm_property *property, uint64_t *val)
37335718399fSFrançois Tigeot {
37345718399fSFrançois Tigeot 	int i;
37355718399fSFrançois Tigeot 
3736b5162e19SFrançois Tigeot 	for (i = 0; i < obj->properties->count; i++) {
3737b5162e19SFrançois Tigeot 		if (obj->properties->ids[i] == property->base.id) {
3738b5162e19SFrançois Tigeot 			*val = obj->properties->values[i];
3739b5162e19SFrançois Tigeot 			return 0;
37405718399fSFrançois Tigeot 		}
37415718399fSFrançois Tigeot 	}
37425718399fSFrançois Tigeot 
37435718399fSFrançois Tigeot 	return -EINVAL;
37445718399fSFrançois Tigeot }
3745b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_get_value);
37465718399fSFrançois Tigeot 
3747ba55f2f5SFrançois Tigeot /**
3748ba55f2f5SFrançois Tigeot  * drm_mode_getproperty_ioctl - get the current value of a connector's property
3749ba55f2f5SFrançois Tigeot  * @dev: DRM device
3750ba55f2f5SFrançois Tigeot  * @data: ioctl data
3751ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
3752ba55f2f5SFrançois Tigeot  *
3753ba55f2f5SFrançois Tigeot  * This function retrieves the current value for an connectors's property.
3754ba55f2f5SFrançois Tigeot  *
3755ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
3756ba55f2f5SFrançois Tigeot  *
3757ba55f2f5SFrançois Tigeot  * Returns:
3758ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
3759ba55f2f5SFrançois Tigeot  */
37605718399fSFrançois Tigeot int drm_mode_getproperty_ioctl(struct drm_device *dev,
37615718399fSFrançois Tigeot 			       void *data, struct drm_file *file_priv)
37625718399fSFrançois Tigeot {
37635718399fSFrançois Tigeot 	struct drm_mode_get_property *out_resp = data;
37645718399fSFrançois Tigeot 	struct drm_property *property;
37655718399fSFrançois Tigeot 	int enum_count = 0;
37665718399fSFrançois Tigeot 	int blob_count = 0;
37675718399fSFrançois Tigeot 	int value_count = 0;
37685718399fSFrançois Tigeot 	int ret = 0, i;
37695718399fSFrançois Tigeot 	int copied;
37705718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum;
37715718399fSFrançois Tigeot 	struct drm_mode_property_enum __user *enum_ptr;
37725718399fSFrançois Tigeot 	struct drm_property_blob *prop_blob;
3773b5162e19SFrançois Tigeot 	uint32_t __user *blob_id_ptr;
3774b5162e19SFrançois Tigeot 	uint64_t __user *values_ptr;
3775b5162e19SFrançois Tigeot 	uint32_t __user *blob_length_ptr;
37765718399fSFrançois Tigeot 
37775718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
37785718399fSFrançois Tigeot 		return -EINVAL;
37795718399fSFrançois Tigeot 
37804dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
3781ba55f2f5SFrançois Tigeot 	property = drm_property_find(dev, out_resp->prop_id);
3782ba55f2f5SFrançois Tigeot 	if (!property) {
37839edbd4a0SFrançois Tigeot 		ret = -ENOENT;
37845718399fSFrançois Tigeot 		goto done;
37855718399fSFrançois Tigeot 	}
37865718399fSFrançois Tigeot 
3787ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
3788ba55f2f5SFrançois Tigeot 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
37895718399fSFrançois Tigeot 		list_for_each_entry(prop_enum, &property->enum_blob_list, head)
37905718399fSFrançois Tigeot 			enum_count++;
3791ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
37925718399fSFrançois Tigeot 		list_for_each_entry(prop_blob, &property->enum_blob_list, head)
37935718399fSFrançois Tigeot 			blob_count++;
37945718399fSFrançois Tigeot 	}
37955718399fSFrançois Tigeot 
37965718399fSFrançois Tigeot 	value_count = property->num_values;
37975718399fSFrançois Tigeot 
37985718399fSFrançois Tigeot 	strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
37995718399fSFrançois Tigeot 	out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
38005718399fSFrançois Tigeot 	out_resp->flags = property->flags;
38015718399fSFrançois Tigeot 
38025718399fSFrançois Tigeot 	if ((out_resp->count_values >= value_count) && value_count) {
3803b5162e19SFrançois Tigeot 		values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
38045718399fSFrançois Tigeot 		for (i = 0; i < value_count; i++) {
3805b5162e19SFrançois Tigeot 			if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
38065718399fSFrançois Tigeot 				ret = -EFAULT;
38075718399fSFrançois Tigeot 				goto done;
38085718399fSFrançois Tigeot 			}
38095718399fSFrançois Tigeot 		}
38105718399fSFrançois Tigeot 	}
38115718399fSFrançois Tigeot 	out_resp->count_values = value_count;
38125718399fSFrançois Tigeot 
3813ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
3814ba55f2f5SFrançois Tigeot 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
38155718399fSFrançois Tigeot 		if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
38165718399fSFrançois Tigeot 			copied = 0;
3817b5162e19SFrançois Tigeot 			enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
38185718399fSFrançois Tigeot 			list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
38195718399fSFrançois Tigeot 
3820b5162e19SFrançois Tigeot 				if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
38215718399fSFrançois Tigeot 					ret = -EFAULT;
38225718399fSFrançois Tigeot 					goto done;
38235718399fSFrançois Tigeot 				}
38245718399fSFrançois Tigeot 
3825b5162e19SFrançois Tigeot 				if (copy_to_user(&enum_ptr[copied].name,
3826b5162e19SFrançois Tigeot 						 &prop_enum->name, DRM_PROP_NAME_LEN)) {
38275718399fSFrançois Tigeot 					ret = -EFAULT;
38285718399fSFrançois Tigeot 					goto done;
38295718399fSFrançois Tigeot 				}
38305718399fSFrançois Tigeot 				copied++;
38315718399fSFrançois Tigeot 			}
38325718399fSFrançois Tigeot 		}
38335718399fSFrançois Tigeot 		out_resp->count_enum_blobs = enum_count;
38345718399fSFrançois Tigeot 	}
38355718399fSFrançois Tigeot 
3836ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
38375718399fSFrançois Tigeot 		if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
38385718399fSFrançois Tigeot 			copied = 0;
3839b5162e19SFrançois Tigeot 			blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
3840b5162e19SFrançois Tigeot 			blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr;
38415718399fSFrançois Tigeot 
38425718399fSFrançois Tigeot 			list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
3843b5162e19SFrançois Tigeot 				if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
38445718399fSFrançois Tigeot 					ret = -EFAULT;
38455718399fSFrançois Tigeot 					goto done;
38465718399fSFrançois Tigeot 				}
38475718399fSFrançois Tigeot 
3848b5162e19SFrançois Tigeot 				if (put_user(prop_blob->length, blob_length_ptr + copied)) {
38495718399fSFrançois Tigeot 					ret = -EFAULT;
38505718399fSFrançois Tigeot 					goto done;
38515718399fSFrançois Tigeot 				}
38525718399fSFrançois Tigeot 
38535718399fSFrançois Tigeot 				copied++;
38545718399fSFrançois Tigeot 			}
38555718399fSFrançois Tigeot 		}
38565718399fSFrançois Tigeot 		out_resp->count_enum_blobs = blob_count;
38575718399fSFrançois Tigeot 	}
38585718399fSFrançois Tigeot done:
38594dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
38605718399fSFrançois Tigeot 	return ret;
38615718399fSFrançois Tigeot }
38625718399fSFrançois Tigeot 
38635718399fSFrançois Tigeot static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length,
38645718399fSFrançois Tigeot 							  void *data)
38655718399fSFrançois Tigeot {
38665718399fSFrançois Tigeot 	struct drm_property_blob *blob;
38675718399fSFrançois Tigeot 	int ret;
38685718399fSFrançois Tigeot 
38695718399fSFrançois Tigeot 	if (!length || !data)
38705718399fSFrançois Tigeot 		return NULL;
38715718399fSFrançois Tigeot 
38724dbb207bSFrançois Tigeot 	blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
3873b5162e19SFrançois Tigeot 	if (!blob)
3874b5162e19SFrançois Tigeot 		return NULL;
38755718399fSFrançois Tigeot 
38765718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
38775718399fSFrançois Tigeot 	if (ret) {
38784dbb207bSFrançois Tigeot 		kfree(blob);
3879b5162e19SFrançois Tigeot 		return NULL;
38805718399fSFrançois Tigeot 	}
38815718399fSFrançois Tigeot 
38825718399fSFrançois Tigeot 	blob->length = length;
38835718399fSFrançois Tigeot 
38845718399fSFrançois Tigeot 	memcpy(blob->data, data, length);
38855718399fSFrançois Tigeot 
38865718399fSFrançois Tigeot 	list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
38875718399fSFrançois Tigeot 	return blob;
38885718399fSFrançois Tigeot }
38895718399fSFrançois Tigeot 
38905718399fSFrançois Tigeot static void drm_property_destroy_blob(struct drm_device *dev,
38915718399fSFrançois Tigeot 			       struct drm_property_blob *blob)
38925718399fSFrançois Tigeot {
38935718399fSFrançois Tigeot 	drm_mode_object_put(dev, &blob->base);
38945718399fSFrançois Tigeot 	list_del(&blob->head);
38954dbb207bSFrançois Tigeot 	kfree(blob);
38965718399fSFrançois Tigeot }
38975718399fSFrançois Tigeot 
3898ba55f2f5SFrançois Tigeot /**
3899ba55f2f5SFrançois Tigeot  * drm_mode_getblob_ioctl - get the contents of a blob property value
3900ba55f2f5SFrançois Tigeot  * @dev: DRM device
3901ba55f2f5SFrançois Tigeot  * @data: ioctl data
3902ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
3903ba55f2f5SFrançois Tigeot  *
3904ba55f2f5SFrançois Tigeot  * This function retrieves the contents of a blob property. The value stored in
3905ba55f2f5SFrançois Tigeot  * an object's blob property is just a normal modeset object id.
3906ba55f2f5SFrançois Tigeot  *
3907ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
3908ba55f2f5SFrançois Tigeot  *
3909ba55f2f5SFrançois Tigeot  * Returns:
3910ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
3911ba55f2f5SFrançois Tigeot  */
39125718399fSFrançois Tigeot int drm_mode_getblob_ioctl(struct drm_device *dev,
39135718399fSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
39145718399fSFrançois Tigeot {
39155718399fSFrançois Tigeot 	struct drm_mode_get_blob *out_resp = data;
39165718399fSFrançois Tigeot 	struct drm_property_blob *blob;
39175718399fSFrançois Tigeot 	int ret = 0;
3918b5162e19SFrançois Tigeot 	void __user *blob_ptr;
39195718399fSFrançois Tigeot 
39205718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
39215718399fSFrançois Tigeot 		return -EINVAL;
39225718399fSFrançois Tigeot 
39234dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
3924ba55f2f5SFrançois Tigeot 	blob = drm_property_blob_find(dev, out_resp->blob_id);
3925ba55f2f5SFrançois Tigeot 	if (!blob) {
39269edbd4a0SFrançois Tigeot 		ret = -ENOENT;
39275718399fSFrançois Tigeot 		goto done;
39285718399fSFrançois Tigeot 	}
39295718399fSFrançois Tigeot 
39305718399fSFrançois Tigeot 	if (out_resp->length == blob->length) {
3931b5162e19SFrançois Tigeot 		blob_ptr = (void __user *)(unsigned long)out_resp->data;
3932b5162e19SFrançois Tigeot 		if (copy_to_user(blob_ptr, blob->data, blob->length)){
39335718399fSFrançois Tigeot 			ret = -EFAULT;
39345718399fSFrançois Tigeot 			goto done;
39355718399fSFrançois Tigeot 		}
39365718399fSFrançois Tigeot 	}
39375718399fSFrançois Tigeot 	out_resp->length = blob->length;
39385718399fSFrançois Tigeot 
39395718399fSFrançois Tigeot done:
39404dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
39415718399fSFrançois Tigeot 	return ret;
39425718399fSFrançois Tigeot }
39435718399fSFrançois Tigeot 
3944*24edb884SFrançois Tigeot int drm_mode_connector_set_path_property(struct drm_connector *connector,
3945*24edb884SFrançois Tigeot 					 char *path)
3946*24edb884SFrançois Tigeot {
3947*24edb884SFrançois Tigeot 	struct drm_device *dev = connector->dev;
3948*24edb884SFrançois Tigeot 	int ret, size;
3949*24edb884SFrançois Tigeot 	size = strlen(path) + 1;
3950*24edb884SFrançois Tigeot 
3951*24edb884SFrançois Tigeot 	connector->path_blob_ptr = drm_property_create_blob(connector->dev,
3952*24edb884SFrançois Tigeot 							    size, path);
3953*24edb884SFrançois Tigeot 	if (!connector->path_blob_ptr)
3954*24edb884SFrançois Tigeot 		return -EINVAL;
3955*24edb884SFrançois Tigeot 
3956*24edb884SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base,
3957*24edb884SFrançois Tigeot 					    dev->mode_config.path_property,
3958*24edb884SFrançois Tigeot 					    connector->path_blob_ptr->base.id);
3959*24edb884SFrançois Tigeot 	return ret;
3960*24edb884SFrançois Tigeot }
3961*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_path_property);
3962*24edb884SFrançois Tigeot 
3963ba55f2f5SFrançois Tigeot /**
3964ba55f2f5SFrançois Tigeot  * drm_mode_connector_update_edid_property - update the edid property of a connector
3965ba55f2f5SFrançois Tigeot  * @connector: drm connector
3966ba55f2f5SFrançois Tigeot  * @edid: new value of the edid property
3967ba55f2f5SFrançois Tigeot  *
3968ba55f2f5SFrançois Tigeot  * This function creates a new blob modeset object and assigns its id to the
3969ba55f2f5SFrançois Tigeot  * connector's edid property.
3970ba55f2f5SFrançois Tigeot  *
3971ba55f2f5SFrançois Tigeot  * Returns:
3972ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
3973ba55f2f5SFrançois Tigeot  */
39745718399fSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector,
39755718399fSFrançois Tigeot 					    struct edid *edid)
39765718399fSFrançois Tigeot {
39775718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
3978b5162e19SFrançois Tigeot 	int ret, size;
39795718399fSFrançois Tigeot 
3980*24edb884SFrançois Tigeot 	/* ignore requests to set edid when overridden */
3981*24edb884SFrançois Tigeot 	if (connector->override_edid)
3982*24edb884SFrançois Tigeot 		return 0;
3983*24edb884SFrançois Tigeot 
39845718399fSFrançois Tigeot 	if (connector->edid_blob_ptr)
39855718399fSFrançois Tigeot 		drm_property_destroy_blob(dev, connector->edid_blob_ptr);
39865718399fSFrançois Tigeot 
39875718399fSFrançois Tigeot 	/* Delete edid, when there is none. */
39885718399fSFrançois Tigeot 	if (!edid) {
39895718399fSFrançois Tigeot 		connector->edid_blob_ptr = NULL;
3990b5162e19SFrançois Tigeot 		ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0);
39915718399fSFrançois Tigeot 		return ret;
39925718399fSFrançois Tigeot 	}
39935718399fSFrançois Tigeot 
39945718399fSFrançois Tigeot 	size = EDID_LENGTH * (1 + edid->extensions);
39955718399fSFrançois Tigeot 	connector->edid_blob_ptr = drm_property_create_blob(connector->dev,
39965718399fSFrançois Tigeot 							    size, edid);
3997b5162e19SFrançois Tigeot 	if (!connector->edid_blob_ptr)
3998b5162e19SFrançois Tigeot 		return -EINVAL;
39995718399fSFrançois Tigeot 
4000b5162e19SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base,
40015718399fSFrançois Tigeot 					       dev->mode_config.edid_property,
40025718399fSFrançois Tigeot 					       connector->edid_blob_ptr->base.id);
40035718399fSFrançois Tigeot 
40045718399fSFrançois Tigeot 	return ret;
40055718399fSFrançois Tigeot }
4006b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
4007b5162e19SFrançois Tigeot 
4008b5162e19SFrançois Tigeot static bool drm_property_change_is_valid(struct drm_property *property,
4009b5162e19SFrançois Tigeot 					 uint64_t value)
4010b5162e19SFrançois Tigeot {
4011b5162e19SFrançois Tigeot 	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
4012b5162e19SFrançois Tigeot 		return false;
4013ba55f2f5SFrançois Tigeot 
4014ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
4015b5162e19SFrançois Tigeot 		if (value < property->values[0] || value > property->values[1])
4016b5162e19SFrançois Tigeot 			return false;
4017b5162e19SFrançois Tigeot 		return true;
4018ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
4019ba55f2f5SFrançois Tigeot 		int64_t svalue = U642I64(value);
4020ba55f2f5SFrançois Tigeot 		if (svalue < U642I64(property->values[0]) ||
4021ba55f2f5SFrançois Tigeot 				svalue > U642I64(property->values[1]))
4022ba55f2f5SFrançois Tigeot 			return false;
4023ba55f2f5SFrançois Tigeot 		return true;
4024ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
4025b5162e19SFrançois Tigeot 		int i;
4026b5162e19SFrançois Tigeot 		uint64_t valid_mask = 0;
4027b5162e19SFrançois Tigeot 		for (i = 0; i < property->num_values; i++)
4028b5162e19SFrançois Tigeot 			valid_mask |= (1ULL << property->values[i]);
4029b5162e19SFrançois Tigeot 		return !(value & ~valid_mask);
4030ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
4031b5162e19SFrançois Tigeot 		/* Only the driver knows */
4032b5162e19SFrançois Tigeot 		return true;
4033ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
4034ba55f2f5SFrançois Tigeot 		struct drm_mode_object *obj;
4035ba55f2f5SFrançois Tigeot 		/* a zero value for an object property translates to null: */
4036ba55f2f5SFrançois Tigeot 		if (value == 0)
4037ba55f2f5SFrançois Tigeot 			return true;
4038ba55f2f5SFrançois Tigeot 		/*
4039ba55f2f5SFrançois Tigeot 		 * NOTE: use _object_find() directly to bypass restriction on
4040ba55f2f5SFrançois Tigeot 		 * looking up refcnt'd objects (ie. fb's).  For a refcnt'd
4041ba55f2f5SFrançois Tigeot 		 * object this could race against object finalization, so it
4042ba55f2f5SFrançois Tigeot 		 * simply tells us that the object *was* valid.  Which is good
4043ba55f2f5SFrançois Tigeot 		 * enough.
4044ba55f2f5SFrançois Tigeot 		 */
4045ba55f2f5SFrançois Tigeot 		obj = _object_find(property->dev, value, property->values[0]);
4046ba55f2f5SFrançois Tigeot 		return obj != NULL;
4047b5162e19SFrançois Tigeot 	} else {
4048b5162e19SFrançois Tigeot 		int i;
4049b5162e19SFrançois Tigeot 		for (i = 0; i < property->num_values; i++)
4050b5162e19SFrançois Tigeot 			if (property->values[i] == value)
4051b5162e19SFrançois Tigeot 				return true;
4052b5162e19SFrançois Tigeot 		return false;
4053b5162e19SFrançois Tigeot 	}
4054b5162e19SFrançois Tigeot }
40555718399fSFrançois Tigeot 
4056ba55f2f5SFrançois Tigeot /**
4057ba55f2f5SFrançois Tigeot  * drm_mode_connector_property_set_ioctl - set the current value of a connector property
4058ba55f2f5SFrançois Tigeot  * @dev: DRM device
4059ba55f2f5SFrançois Tigeot  * @data: ioctl data
4060ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4061ba55f2f5SFrançois Tigeot  *
4062ba55f2f5SFrançois Tigeot  * This function sets the current value for a connectors's property. It also
4063ba55f2f5SFrançois Tigeot  * calls into a driver's ->set_property callback to update the hardware state
4064ba55f2f5SFrançois Tigeot  *
4065ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4066ba55f2f5SFrançois Tigeot  *
4067ba55f2f5SFrançois Tigeot  * Returns:
4068ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4069ba55f2f5SFrançois Tigeot  */
40705718399fSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
40715718399fSFrançois Tigeot 				       void *data, struct drm_file *file_priv)
40725718399fSFrançois Tigeot {
4073b5162e19SFrançois Tigeot 	struct drm_mode_connector_set_property *conn_set_prop = data;
4074b5162e19SFrançois Tigeot 	struct drm_mode_obj_set_property obj_set_prop = {
4075b5162e19SFrançois Tigeot 		.value = conn_set_prop->value,
4076b5162e19SFrançois Tigeot 		.prop_id = conn_set_prop->prop_id,
4077b5162e19SFrançois Tigeot 		.obj_id = conn_set_prop->connector_id,
4078b5162e19SFrançois Tigeot 		.obj_type = DRM_MODE_OBJECT_CONNECTOR
4079b5162e19SFrançois Tigeot 	};
4080b5162e19SFrançois Tigeot 
4081b5162e19SFrançois Tigeot 	/* It does all the locking and checking we need */
4082b5162e19SFrançois Tigeot 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
4083b5162e19SFrançois Tigeot }
4084b5162e19SFrançois Tigeot 
4085b5162e19SFrançois Tigeot static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
4086b5162e19SFrançois Tigeot 					   struct drm_property *property,
4087b5162e19SFrançois Tigeot 					   uint64_t value)
4088b5162e19SFrançois Tigeot {
4089b5162e19SFrançois Tigeot 	int ret = -EINVAL;
4090b5162e19SFrançois Tigeot 	struct drm_connector *connector = obj_to_connector(obj);
4091b5162e19SFrançois Tigeot 
4092b5162e19SFrançois Tigeot 	/* Do DPMS ourselves */
4093b5162e19SFrançois Tigeot 	if (property == connector->dev->mode_config.dpms_property) {
4094b5162e19SFrançois Tigeot 		if (connector->funcs->dpms)
4095b5162e19SFrançois Tigeot 			(*connector->funcs->dpms)(connector, (int)value);
4096b5162e19SFrançois Tigeot 		ret = 0;
4097b5162e19SFrançois Tigeot 	} else if (connector->funcs->set_property)
4098b5162e19SFrançois Tigeot 		ret = connector->funcs->set_property(connector, property, value);
4099b5162e19SFrançois Tigeot 
4100b5162e19SFrançois Tigeot 	/* store the property value if successful */
4101b5162e19SFrançois Tigeot 	if (!ret)
4102b5162e19SFrançois Tigeot 		drm_object_property_set_value(&connector->base, property, value);
4103b5162e19SFrançois Tigeot 	return ret;
4104b5162e19SFrançois Tigeot }
4105b5162e19SFrançois Tigeot 
4106b5162e19SFrançois Tigeot static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
4107b5162e19SFrançois Tigeot 				      struct drm_property *property,
4108b5162e19SFrançois Tigeot 				      uint64_t value)
4109b5162e19SFrançois Tigeot {
4110b5162e19SFrançois Tigeot 	int ret = -EINVAL;
4111b5162e19SFrançois Tigeot 	struct drm_crtc *crtc = obj_to_crtc(obj);
4112b5162e19SFrançois Tigeot 
4113b5162e19SFrançois Tigeot 	if (crtc->funcs->set_property)
4114b5162e19SFrançois Tigeot 		ret = crtc->funcs->set_property(crtc, property, value);
4115b5162e19SFrançois Tigeot 	if (!ret)
4116b5162e19SFrançois Tigeot 		drm_object_property_set_value(obj, property, value);
4117b5162e19SFrançois Tigeot 
4118b5162e19SFrançois Tigeot 	return ret;
4119b5162e19SFrançois Tigeot }
4120b5162e19SFrançois Tigeot 
4121b5162e19SFrançois Tigeot static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
4122b5162e19SFrançois Tigeot 				      struct drm_property *property,
4123b5162e19SFrançois Tigeot 				      uint64_t value)
4124b5162e19SFrançois Tigeot {
4125b5162e19SFrançois Tigeot 	int ret = -EINVAL;
4126b5162e19SFrançois Tigeot 	struct drm_plane *plane = obj_to_plane(obj);
4127b5162e19SFrançois Tigeot 
4128b5162e19SFrançois Tigeot 	if (plane->funcs->set_property)
4129b5162e19SFrançois Tigeot 		ret = plane->funcs->set_property(plane, property, value);
4130b5162e19SFrançois Tigeot 	if (!ret)
4131b5162e19SFrançois Tigeot 		drm_object_property_set_value(obj, property, value);
4132b5162e19SFrançois Tigeot 
4133b5162e19SFrançois Tigeot 	return ret;
4134b5162e19SFrançois Tigeot }
4135b5162e19SFrançois Tigeot 
4136ba55f2f5SFrançois Tigeot /**
4137ba55f2f5SFrançois Tigeot  * drm_mode_getproperty_ioctl - get the current value of a object's property
4138ba55f2f5SFrançois Tigeot  * @dev: DRM device
4139ba55f2f5SFrançois Tigeot  * @data: ioctl data
4140ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4141ba55f2f5SFrançois Tigeot  *
4142ba55f2f5SFrançois Tigeot  * This function retrieves the current value for an object's property. Compared
4143ba55f2f5SFrançois Tigeot  * to the connector specific ioctl this one is extended to also work on crtc and
4144ba55f2f5SFrançois Tigeot  * plane objects.
4145ba55f2f5SFrançois Tigeot  *
4146ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4147ba55f2f5SFrançois Tigeot  *
4148ba55f2f5SFrançois Tigeot  * Returns:
4149ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4150ba55f2f5SFrançois Tigeot  */
4151b5162e19SFrançois Tigeot int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
4152b5162e19SFrançois Tigeot 				      struct drm_file *file_priv)
4153b5162e19SFrançois Tigeot {
4154b5162e19SFrançois Tigeot 	struct drm_mode_obj_get_properties *arg = data;
41555718399fSFrançois Tigeot 	struct drm_mode_object *obj;
4156b5162e19SFrançois Tigeot 	int ret = 0;
4157b5162e19SFrançois Tigeot 	int i;
4158b5162e19SFrançois Tigeot 	int copied = 0;
4159b5162e19SFrançois Tigeot 	int props_count = 0;
4160b5162e19SFrançois Tigeot 	uint32_t __user *props_ptr;
4161b5162e19SFrançois Tigeot 	uint64_t __user *prop_values_ptr;
4162b5162e19SFrançois Tigeot 
4163b5162e19SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
4164b5162e19SFrançois Tigeot 		return -EINVAL;
4165b5162e19SFrançois Tigeot 
41664dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
4167b5162e19SFrançois Tigeot 
4168b5162e19SFrançois Tigeot 	obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
4169b5162e19SFrançois Tigeot 	if (!obj) {
41709edbd4a0SFrançois Tigeot 		ret = -ENOENT;
4171b5162e19SFrançois Tigeot 		goto out;
4172b5162e19SFrançois Tigeot 	}
4173b5162e19SFrançois Tigeot 	if (!obj->properties) {
4174b5162e19SFrançois Tigeot 		ret = -EINVAL;
4175b5162e19SFrançois Tigeot 		goto out;
4176b5162e19SFrançois Tigeot 	}
4177b5162e19SFrançois Tigeot 
4178b5162e19SFrançois Tigeot 	props_count = obj->properties->count;
4179b5162e19SFrançois Tigeot 
4180b5162e19SFrançois Tigeot 	/* This ioctl is called twice, once to determine how much space is
4181b5162e19SFrançois Tigeot 	 * needed, and the 2nd time to fill it. */
4182b5162e19SFrançois Tigeot 	if ((arg->count_props >= props_count) && props_count) {
4183b5162e19SFrançois Tigeot 		copied = 0;
4184b5162e19SFrançois Tigeot 		props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr);
4185b5162e19SFrançois Tigeot 		prop_values_ptr = (uint64_t __user *)(unsigned long)
4186b5162e19SFrançois Tigeot 				  (arg->prop_values_ptr);
4187b5162e19SFrançois Tigeot 		for (i = 0; i < props_count; i++) {
4188b5162e19SFrançois Tigeot 			if (put_user(obj->properties->ids[i],
4189b5162e19SFrançois Tigeot 				     props_ptr + copied)) {
4190b5162e19SFrançois Tigeot 				ret = -EFAULT;
4191b5162e19SFrançois Tigeot 				goto out;
4192b5162e19SFrançois Tigeot 			}
4193b5162e19SFrançois Tigeot 			if (put_user(obj->properties->values[i],
4194b5162e19SFrançois Tigeot 				     prop_values_ptr + copied)) {
4195b5162e19SFrançois Tigeot 				ret = -EFAULT;
4196b5162e19SFrançois Tigeot 				goto out;
4197b5162e19SFrançois Tigeot 			}
4198b5162e19SFrançois Tigeot 			copied++;
4199b5162e19SFrançois Tigeot 		}
4200b5162e19SFrançois Tigeot 	}
4201b5162e19SFrançois Tigeot 	arg->count_props = props_count;
4202b5162e19SFrançois Tigeot out:
42034dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
4204b5162e19SFrançois Tigeot 	return ret;
4205b5162e19SFrançois Tigeot }
4206b5162e19SFrançois Tigeot 
4207ba55f2f5SFrançois Tigeot /**
4208ba55f2f5SFrançois Tigeot  * drm_mode_obj_set_property_ioctl - set the current value of an object's property
4209ba55f2f5SFrançois Tigeot  * @dev: DRM device
4210ba55f2f5SFrançois Tigeot  * @data: ioctl data
4211ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4212ba55f2f5SFrançois Tigeot  *
4213ba55f2f5SFrançois Tigeot  * This function sets the current value for an object's property. It also calls
4214ba55f2f5SFrançois Tigeot  * into a driver's ->set_property callback to update the hardware state.
4215ba55f2f5SFrançois Tigeot  * Compared to the connector specific ioctl this one is extended to also work on
4216ba55f2f5SFrançois Tigeot  * crtc and plane objects.
4217ba55f2f5SFrançois Tigeot  *
4218ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4219ba55f2f5SFrançois Tigeot  *
4220ba55f2f5SFrançois Tigeot  * Returns:
4221ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4222ba55f2f5SFrançois Tigeot  */
4223b5162e19SFrançois Tigeot int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
4224b5162e19SFrançois Tigeot 				    struct drm_file *file_priv)
4225b5162e19SFrançois Tigeot {
4226b5162e19SFrançois Tigeot 	struct drm_mode_obj_set_property *arg = data;
4227b5162e19SFrançois Tigeot 	struct drm_mode_object *arg_obj;
4228b5162e19SFrançois Tigeot 	struct drm_mode_object *prop_obj;
42295718399fSFrançois Tigeot 	struct drm_property *property;
42305718399fSFrançois Tigeot 	int ret = -EINVAL;
42315718399fSFrançois Tigeot 	int i;
42325718399fSFrançois Tigeot 
42335718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
42345718399fSFrançois Tigeot 		return -EINVAL;
42355718399fSFrançois Tigeot 
42364dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
42375718399fSFrançois Tigeot 
4238b5162e19SFrançois Tigeot 	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
42399edbd4a0SFrançois Tigeot 	if (!arg_obj) {
42409edbd4a0SFrançois Tigeot 		ret = -ENOENT;
42415718399fSFrançois Tigeot 		goto out;
42429edbd4a0SFrançois Tigeot 	}
4243b5162e19SFrançois Tigeot 	if (!arg_obj->properties)
4244b5162e19SFrançois Tigeot 		goto out;
42455718399fSFrançois Tigeot 
4246b5162e19SFrançois Tigeot 	for (i = 0; i < arg_obj->properties->count; i++)
4247b5162e19SFrançois Tigeot 		if (arg_obj->properties->ids[i] == arg->prop_id)
4248b5162e19SFrançois Tigeot 			break;
4249b5162e19SFrançois Tigeot 
4250b5162e19SFrançois Tigeot 	if (i == arg_obj->properties->count)
4251b5162e19SFrançois Tigeot 		goto out;
4252b5162e19SFrançois Tigeot 
4253b5162e19SFrançois Tigeot 	prop_obj = drm_mode_object_find(dev, arg->prop_id,
4254b5162e19SFrançois Tigeot 					DRM_MODE_OBJECT_PROPERTY);
42559edbd4a0SFrançois Tigeot 	if (!prop_obj) {
42569edbd4a0SFrançois Tigeot 		ret = -ENOENT;
4257b5162e19SFrançois Tigeot 		goto out;
42589edbd4a0SFrançois Tigeot 	}
4259b5162e19SFrançois Tigeot 	property = obj_to_property(prop_obj);
4260b5162e19SFrançois Tigeot 
4261b5162e19SFrançois Tigeot 	if (!drm_property_change_is_valid(property, arg->value))
4262b5162e19SFrançois Tigeot 		goto out;
4263b5162e19SFrançois Tigeot 
4264b5162e19SFrançois Tigeot 	switch (arg_obj->type) {
4265b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_CONNECTOR:
4266b5162e19SFrançois Tigeot 		ret = drm_mode_connector_set_obj_prop(arg_obj, property,
4267b5162e19SFrançois Tigeot 						      arg->value);
4268b5162e19SFrançois Tigeot 		break;
4269b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_CRTC:
4270b5162e19SFrançois Tigeot 		ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
4271b5162e19SFrançois Tigeot 		break;
4272b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_PLANE:
4273b5162e19SFrançois Tigeot 		ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value);
42745718399fSFrançois Tigeot 		break;
42755718399fSFrançois Tigeot 	}
42765718399fSFrançois Tigeot 
42775718399fSFrançois Tigeot out:
42784dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
42795718399fSFrançois Tigeot 	return ret;
42805718399fSFrançois Tigeot }
42815718399fSFrançois Tigeot 
4282ba55f2f5SFrançois Tigeot /**
4283ba55f2f5SFrançois Tigeot  * drm_mode_connector_attach_encoder - attach a connector to an encoder
4284ba55f2f5SFrançois Tigeot  * @connector: connector to attach
4285ba55f2f5SFrançois Tigeot  * @encoder: encoder to attach @connector to
4286ba55f2f5SFrançois Tigeot  *
4287ba55f2f5SFrançois Tigeot  * This function links up a connector to an encoder. Note that the routing
4288ba55f2f5SFrançois Tigeot  * restrictions between encoders and crtcs are exposed to userspace through the
4289ba55f2f5SFrançois Tigeot  * possible_clones and possible_crtcs bitmasks.
4290ba55f2f5SFrançois Tigeot  *
4291ba55f2f5SFrançois Tigeot  * Returns:
4292ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4293ba55f2f5SFrançois Tigeot  */
42945718399fSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector,
42955718399fSFrançois Tigeot 				      struct drm_encoder *encoder)
42965718399fSFrançois Tigeot {
42975718399fSFrançois Tigeot 	int i;
42985718399fSFrançois Tigeot 
42995718399fSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
43005718399fSFrançois Tigeot 		if (connector->encoder_ids[i] == 0) {
43015718399fSFrançois Tigeot 			connector->encoder_ids[i] = encoder->base.id;
43025718399fSFrançois Tigeot 			return 0;
43035718399fSFrançois Tigeot 		}
43045718399fSFrançois Tigeot 	}
43055718399fSFrançois Tigeot 	return -ENOMEM;
43065718399fSFrançois Tigeot }
4307b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
43085718399fSFrançois Tigeot 
4309ba55f2f5SFrançois Tigeot /**
4310ba55f2f5SFrançois Tigeot  * drm_mode_crtc_set_gamma_size - set the gamma table size
4311ba55f2f5SFrançois Tigeot  * @crtc: CRTC to set the gamma table size for
4312ba55f2f5SFrançois Tigeot  * @gamma_size: size of the gamma table
4313ba55f2f5SFrançois Tigeot  *
4314ba55f2f5SFrançois Tigeot  * Drivers which support gamma tables should set this to the supported gamma
4315ba55f2f5SFrançois Tigeot  * table size when initializing the CRTC. Currently the drm core only supports a
4316ba55f2f5SFrançois Tigeot  * fixed gamma table size.
4317ba55f2f5SFrançois Tigeot  *
4318ba55f2f5SFrançois Tigeot  * Returns:
4319ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4320ba55f2f5SFrançois Tigeot  */
43215718399fSFrançois Tigeot int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
43225718399fSFrançois Tigeot 				 int gamma_size)
43235718399fSFrançois Tigeot {
43245718399fSFrançois Tigeot 	crtc->gamma_size = gamma_size;
43255718399fSFrançois Tigeot 
43264dbb207bSFrançois Tigeot 	crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
4327b5162e19SFrançois Tigeot 	if (!crtc->gamma_store) {
4328b5162e19SFrançois Tigeot 		crtc->gamma_size = 0;
4329b5162e19SFrançois Tigeot 		return -ENOMEM;
4330b5162e19SFrançois Tigeot 	}
43315718399fSFrançois Tigeot 
43325718399fSFrançois Tigeot 	return 0;
43335718399fSFrançois Tigeot }
4334b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
43355718399fSFrançois Tigeot 
4336ba55f2f5SFrançois Tigeot /**
4337ba55f2f5SFrançois Tigeot  * drm_mode_gamma_set_ioctl - set the gamma table
4338ba55f2f5SFrançois Tigeot  * @dev: DRM device
4339ba55f2f5SFrançois Tigeot  * @data: ioctl data
4340ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4341ba55f2f5SFrançois Tigeot  *
4342ba55f2f5SFrançois Tigeot  * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
4343ba55f2f5SFrançois Tigeot  * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
4344ba55f2f5SFrançois Tigeot  *
4345ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4346ba55f2f5SFrançois Tigeot  *
4347ba55f2f5SFrançois Tigeot  * Returns:
4348ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4349ba55f2f5SFrançois Tigeot  */
43505718399fSFrançois Tigeot int drm_mode_gamma_set_ioctl(struct drm_device *dev,
43515718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
43525718399fSFrançois Tigeot {
43535718399fSFrançois Tigeot 	struct drm_mode_crtc_lut *crtc_lut = data;
43545718399fSFrançois Tigeot 	struct drm_crtc *crtc;
43555718399fSFrançois Tigeot 	void *r_base, *g_base, *b_base;
43565718399fSFrançois Tigeot 	int size;
43575718399fSFrançois Tigeot 	int ret = 0;
43585718399fSFrançois Tigeot 
43595718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
43605718399fSFrançois Tigeot 		return -EINVAL;
43615718399fSFrançois Tigeot 
43624dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
4363ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
4364ba55f2f5SFrançois Tigeot 	if (!crtc) {
43659edbd4a0SFrançois Tigeot 		ret = -ENOENT;
43665718399fSFrançois Tigeot 		goto out;
43675718399fSFrançois Tigeot 	}
43685718399fSFrançois Tigeot 
4369b5162e19SFrançois Tigeot 	if (crtc->funcs->gamma_set == NULL) {
4370b5162e19SFrançois Tigeot 		ret = -ENOSYS;
4371b5162e19SFrançois Tigeot 		goto out;
4372b5162e19SFrançois Tigeot 	}
4373b5162e19SFrançois Tigeot 
43745718399fSFrançois Tigeot 	/* memcpy into gamma store */
43755718399fSFrançois Tigeot 	if (crtc_lut->gamma_size != crtc->gamma_size) {
43765718399fSFrançois Tigeot 		ret = -EINVAL;
43775718399fSFrançois Tigeot 		goto out;
43785718399fSFrançois Tigeot 	}
43795718399fSFrançois Tigeot 
43805718399fSFrançois Tigeot 	size = crtc_lut->gamma_size * (sizeof(uint16_t));
43815718399fSFrançois Tigeot 	r_base = crtc->gamma_store;
4382b5162e19SFrançois Tigeot 	if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
43835718399fSFrançois Tigeot 		ret = -EFAULT;
43845718399fSFrançois Tigeot 		goto out;
43855718399fSFrançois Tigeot 	}
43865718399fSFrançois Tigeot 
43875718399fSFrançois Tigeot 	g_base = (char *)r_base + size;
4388b5162e19SFrançois Tigeot 	if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
43895718399fSFrançois Tigeot 		ret = -EFAULT;
43905718399fSFrançois Tigeot 		goto out;
43915718399fSFrançois Tigeot 	}
43925718399fSFrançois Tigeot 
439306fede5aSFrançois Tigeot 	b_base = (char *)g_base + size;
4394b5162e19SFrançois Tigeot 	if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
43955718399fSFrançois Tigeot 		ret = -EFAULT;
43965718399fSFrançois Tigeot 		goto out;
43975718399fSFrançois Tigeot 	}
43985718399fSFrançois Tigeot 
43995718399fSFrançois Tigeot 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
44005718399fSFrançois Tigeot 
44015718399fSFrançois Tigeot out:
44024dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
44035718399fSFrançois Tigeot 	return ret;
44045718399fSFrançois Tigeot 
44055718399fSFrançois Tigeot }
44065718399fSFrançois Tigeot 
4407ba55f2f5SFrançois Tigeot /**
4408ba55f2f5SFrançois Tigeot  * drm_mode_gamma_get_ioctl - get the gamma table
4409ba55f2f5SFrançois Tigeot  * @dev: DRM device
4410ba55f2f5SFrançois Tigeot  * @data: ioctl data
4411ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4412ba55f2f5SFrançois Tigeot  *
4413ba55f2f5SFrançois Tigeot  * Copy the current gamma table into the storage provided. This also provides
4414ba55f2f5SFrançois Tigeot  * the gamma table size the driver expects, which can be used to size the
4415ba55f2f5SFrançois Tigeot  * allocated storage.
4416ba55f2f5SFrançois Tigeot  *
4417ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4418ba55f2f5SFrançois Tigeot  *
4419ba55f2f5SFrançois Tigeot  * Returns:
4420ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4421ba55f2f5SFrançois Tigeot  */
44225718399fSFrançois Tigeot int drm_mode_gamma_get_ioctl(struct drm_device *dev,
44235718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
44245718399fSFrançois Tigeot {
44255718399fSFrançois Tigeot 	struct drm_mode_crtc_lut *crtc_lut = data;
44265718399fSFrançois Tigeot 	struct drm_crtc *crtc;
44275718399fSFrançois Tigeot 	void *r_base, *g_base, *b_base;
44285718399fSFrançois Tigeot 	int size;
44295718399fSFrançois Tigeot 	int ret = 0;
44305718399fSFrançois Tigeot 
44315718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
44325718399fSFrançois Tigeot 		return -EINVAL;
44335718399fSFrançois Tigeot 
44344dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
4435ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
4436ba55f2f5SFrançois Tigeot 	if (!crtc) {
44379edbd4a0SFrançois Tigeot 		ret = -ENOENT;
44385718399fSFrançois Tigeot 		goto out;
44395718399fSFrançois Tigeot 	}
44405718399fSFrançois Tigeot 
44415718399fSFrançois Tigeot 	/* memcpy into gamma store */
44425718399fSFrançois Tigeot 	if (crtc_lut->gamma_size != crtc->gamma_size) {
44435718399fSFrançois Tigeot 		ret = -EINVAL;
44445718399fSFrançois Tigeot 		goto out;
44455718399fSFrançois Tigeot 	}
44465718399fSFrançois Tigeot 
44475718399fSFrançois Tigeot 	size = crtc_lut->gamma_size * (sizeof(uint16_t));
44485718399fSFrançois Tigeot 	r_base = crtc->gamma_store;
4449b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
44505718399fSFrançois Tigeot 		ret = -EFAULT;
44515718399fSFrançois Tigeot 		goto out;
44525718399fSFrançois Tigeot 	}
44535718399fSFrançois Tigeot 
44545718399fSFrançois Tigeot 	g_base = (char *)r_base + size;
4455b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
44565718399fSFrançois Tigeot 		ret = -EFAULT;
44575718399fSFrançois Tigeot 		goto out;
44585718399fSFrançois Tigeot 	}
44595718399fSFrançois Tigeot 
44605718399fSFrançois Tigeot 	b_base = (char *)g_base + size;
4461b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
44625718399fSFrançois Tigeot 		ret = -EFAULT;
44635718399fSFrançois Tigeot 		goto out;
44645718399fSFrançois Tigeot 	}
44655718399fSFrançois Tigeot out:
44664dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
44675718399fSFrançois Tigeot 	return ret;
44685718399fSFrançois Tigeot }
44695718399fSFrançois Tigeot 
44700554239fSFrançois Tigeot /*
44710554239fSFrançois Tigeot  * The Linux version of kfree() is a macro and can't be called
44720554239fSFrançois Tigeot  * directly via a function pointer
44730554239fSFrançois Tigeot  */
44740554239fSFrançois Tigeot static void drm_kms_free(void *arg)
44750554239fSFrançois Tigeot {
44760554239fSFrançois Tigeot 	kfree(arg);
44770554239fSFrançois Tigeot }
44780554239fSFrançois Tigeot 
4479ba55f2f5SFrançois Tigeot /**
4480ba55f2f5SFrançois Tigeot  * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
4481ba55f2f5SFrançois Tigeot  * @dev: DRM device
4482ba55f2f5SFrançois Tigeot  * @data: ioctl data
4483ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4484ba55f2f5SFrançois Tigeot  *
4485ba55f2f5SFrançois Tigeot  * This schedules an asynchronous update on a given CRTC, called page flip.
4486ba55f2f5SFrançois Tigeot  * Optionally a drm event is generated to signal the completion of the event.
4487ba55f2f5SFrançois Tigeot  * Generic drivers cannot assume that a pageflip with changed framebuffer
4488ba55f2f5SFrançois Tigeot  * properties (including driver specific metadata like tiling layout) will work,
4489ba55f2f5SFrançois Tigeot  * but some drivers support e.g. pixel format changes through the pageflip
4490ba55f2f5SFrançois Tigeot  * ioctl.
4491ba55f2f5SFrançois Tigeot  *
4492ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4493ba55f2f5SFrançois Tigeot  *
4494ba55f2f5SFrançois Tigeot  * Returns:
4495ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4496a5f72378SFrançois Tigeot  */
4497b5162e19SFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev,
4498b5162e19SFrançois Tigeot 			     void *data, struct drm_file *file_priv)
44995718399fSFrançois Tigeot {
45005718399fSFrançois Tigeot 	struct drm_mode_crtc_page_flip *page_flip = data;
45015718399fSFrançois Tigeot 	struct drm_crtc *crtc;
45024dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
45035718399fSFrançois Tigeot 	struct drm_pending_vblank_event *e = NULL;
4504b5162e19SFrançois Tigeot 	int ret = -EINVAL;
45055718399fSFrançois Tigeot 
45065718399fSFrançois Tigeot 	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
45075718399fSFrançois Tigeot 	    page_flip->reserved != 0)
4508b5162e19SFrançois Tigeot 		return -EINVAL;
45095718399fSFrançois Tigeot 
45109edbd4a0SFrançois Tigeot 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
45119edbd4a0SFrançois Tigeot 		return -EINVAL;
45129edbd4a0SFrançois Tigeot 
4513ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, page_flip->crtc_id);
4514ba55f2f5SFrançois Tigeot 	if (!crtc)
45159edbd4a0SFrançois Tigeot 		return -ENOENT;
45165718399fSFrançois Tigeot 
4517ba55f2f5SFrançois Tigeot 	drm_modeset_lock(&crtc->mutex, NULL);
4518ba55f2f5SFrançois Tigeot 	if (crtc->primary->fb == NULL) {
45195718399fSFrançois Tigeot 		/* The framebuffer is currently unbound, presumably
45205718399fSFrançois Tigeot 		 * due to a hotplug event, that userspace has not
45215718399fSFrançois Tigeot 		 * yet discovered.
45225718399fSFrançois Tigeot 		 */
4523b5162e19SFrançois Tigeot 		ret = -EBUSY;
45245718399fSFrançois Tigeot 		goto out;
45255718399fSFrançois Tigeot 	}
45265718399fSFrançois Tigeot 
45275718399fSFrançois Tigeot 	if (crtc->funcs->page_flip == NULL)
45285718399fSFrançois Tigeot 		goto out;
45295718399fSFrançois Tigeot 
45304dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
45319edbd4a0SFrançois Tigeot 	if (!fb) {
45329edbd4a0SFrançois Tigeot 		ret = -ENOENT;
45335718399fSFrançois Tigeot 		goto out;
45345718399fSFrançois Tigeot 	}
45355718399fSFrançois Tigeot 
45369edbd4a0SFrançois Tigeot 	ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
45379edbd4a0SFrançois Tigeot 	if (ret)
45389edbd4a0SFrançois Tigeot 		goto out;
45399edbd4a0SFrançois Tigeot 
4540ba55f2f5SFrançois Tigeot 	if (crtc->primary->fb->pixel_format != fb->pixel_format) {
45414dbb207bSFrançois Tigeot 		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
45424dbb207bSFrançois Tigeot 		ret = -EINVAL;
45434dbb207bSFrançois Tigeot 		goto out;
45444dbb207bSFrançois Tigeot 	}
45454dbb207bSFrançois Tigeot 
45465718399fSFrançois Tigeot 	if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
4547b5162e19SFrançois Tigeot 		ret = -ENOMEM;
45485718399fSFrançois Tigeot 		lockmgr(&dev->event_lock, LK_EXCLUSIVE);
45495718399fSFrançois Tigeot 		if (file_priv->event_space < sizeof e->event) {
45505718399fSFrançois Tigeot 			lockmgr(&dev->event_lock, LK_RELEASE);
45515718399fSFrançois Tigeot 			goto out;
45525718399fSFrançois Tigeot 		}
45535718399fSFrançois Tigeot 		file_priv->event_space -= sizeof e->event;
45545718399fSFrançois Tigeot 		lockmgr(&dev->event_lock, LK_RELEASE);
45555718399fSFrançois Tigeot 
45564dbb207bSFrançois Tigeot 		e = kzalloc(sizeof *e, GFP_KERNEL);
4557b5162e19SFrançois Tigeot 		if (e == NULL) {
4558b5162e19SFrançois Tigeot 			lockmgr(&dev->event_lock, LK_EXCLUSIVE);
4559b5162e19SFrançois Tigeot 			file_priv->event_space += sizeof e->event;
4560b5162e19SFrançois Tigeot 			lockmgr(&dev->event_lock, LK_RELEASE);
4561b5162e19SFrançois Tigeot 			goto out;
4562b5162e19SFrançois Tigeot 		}
45635718399fSFrançois Tigeot 
45645718399fSFrançois Tigeot 		e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
45655718399fSFrançois Tigeot 		e->event.base.length = sizeof e->event;
45665718399fSFrançois Tigeot 		e->event.user_data = page_flip->user_data;
45675718399fSFrançois Tigeot 		e->base.event = &e->event.base;
45685718399fSFrançois Tigeot 		e->base.file_priv = file_priv;
45690554239fSFrançois Tigeot 		/* XXX: DragonFly-specific */
45705718399fSFrançois Tigeot 		e->base.destroy =
45710554239fSFrançois Tigeot 			(void (*) (struct drm_pending_event *)) drm_kms_free;
45725718399fSFrançois Tigeot 	}
45735718399fSFrançois Tigeot 
4574ba55f2f5SFrançois Tigeot 	old_fb = crtc->primary->fb;
45759edbd4a0SFrançois Tigeot 	ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
4576b5162e19SFrançois Tigeot 	if (ret) {
45775718399fSFrançois Tigeot 		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
45785718399fSFrançois Tigeot 			lockmgr(&dev->event_lock, LK_EXCLUSIVE);
45795718399fSFrançois Tigeot 			file_priv->event_space += sizeof e->event;
45805718399fSFrançois Tigeot 			lockmgr(&dev->event_lock, LK_RELEASE);
45814dbb207bSFrançois Tigeot 			kfree(e);
45825718399fSFrançois Tigeot 		}
45834dbb207bSFrançois Tigeot 		/* Keep the old fb, don't unref it. */
45844dbb207bSFrançois Tigeot 		old_fb = NULL;
45854dbb207bSFrançois Tigeot 	} else {
45864dbb207bSFrançois Tigeot 		/*
45874dbb207bSFrançois Tigeot 		 * Warn if the driver hasn't properly updated the crtc->fb
45884dbb207bSFrançois Tigeot 		 * field to reflect that the new framebuffer is now used.
45894dbb207bSFrançois Tigeot 		 * Failing to do so will screw with the reference counting
45904dbb207bSFrançois Tigeot 		 * on framebuffers.
45914dbb207bSFrançois Tigeot 		 */
4592ba55f2f5SFrançois Tigeot 		WARN_ON(crtc->primary->fb != fb);
45934dbb207bSFrançois Tigeot 		/* Unref only the old framebuffer. */
45944dbb207bSFrançois Tigeot 		fb = NULL;
45955718399fSFrançois Tigeot 	}
45965718399fSFrançois Tigeot 
45975718399fSFrançois Tigeot out:
45984dbb207bSFrançois Tigeot 	if (fb)
45994dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
46004dbb207bSFrançois Tigeot 	if (old_fb)
46014dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(old_fb);
4602ba55f2f5SFrançois Tigeot 	drm_modeset_unlock(&crtc->mutex);
46034dbb207bSFrançois Tigeot 
4604b5162e19SFrançois Tigeot 	return ret;
46055718399fSFrançois Tigeot }
46065718399fSFrançois Tigeot 
4607ba55f2f5SFrançois Tigeot /**
4608ba55f2f5SFrançois Tigeot  * drm_mode_config_reset - call ->reset callbacks
4609ba55f2f5SFrançois Tigeot  * @dev: drm device
4610ba55f2f5SFrançois Tigeot  *
4611ba55f2f5SFrançois Tigeot  * This functions calls all the crtc's, encoder's and connector's ->reset
4612ba55f2f5SFrançois Tigeot  * callback. Drivers can use this in e.g. their driver load or resume code to
4613ba55f2f5SFrançois Tigeot  * reset hardware and software state.
4614ba55f2f5SFrançois Tigeot  */
46155718399fSFrançois Tigeot void drm_mode_config_reset(struct drm_device *dev)
46165718399fSFrançois Tigeot {
46175718399fSFrançois Tigeot 	struct drm_crtc *crtc;
46185718399fSFrançois Tigeot 	struct drm_encoder *encoder;
46195718399fSFrançois Tigeot 	struct drm_connector *connector;
46205718399fSFrançois Tigeot 
46215718399fSFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
46225718399fSFrançois Tigeot 		if (crtc->funcs->reset)
46235718399fSFrançois Tigeot 			crtc->funcs->reset(crtc);
46245718399fSFrançois Tigeot 
46255718399fSFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
46265718399fSFrançois Tigeot 		if (encoder->funcs->reset)
46275718399fSFrançois Tigeot 			encoder->funcs->reset(encoder);
46285718399fSFrançois Tigeot 
4629b5162e19SFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
4630b5162e19SFrançois Tigeot 		connector->status = connector_status_unknown;
4631b5162e19SFrançois Tigeot 
46325718399fSFrançois Tigeot 		if (connector->funcs->reset)
46335718399fSFrançois Tigeot 			connector->funcs->reset(connector);
46345718399fSFrançois Tigeot 	}
4635b5162e19SFrançois Tigeot }
4636b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_reset);
46375718399fSFrançois Tigeot 
4638ba55f2f5SFrançois Tigeot /**
4639ba55f2f5SFrançois Tigeot  * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer
4640ba55f2f5SFrançois Tigeot  * @dev: DRM device
4641ba55f2f5SFrançois Tigeot  * @data: ioctl data
4642ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4643ba55f2f5SFrançois Tigeot  *
4644ba55f2f5SFrançois Tigeot  * This creates a new dumb buffer in the driver's backing storage manager (GEM,
4645ba55f2f5SFrançois Tigeot  * TTM or something else entirely) and returns the resulting buffer handle. This
4646ba55f2f5SFrançois Tigeot  * handle can then be wrapped up into a framebuffer modeset object.
4647ba55f2f5SFrançois Tigeot  *
4648ba55f2f5SFrançois Tigeot  * Note that userspace is not allowed to use such objects for render
4649ba55f2f5SFrançois Tigeot  * acceleration - drivers must create their own private ioctls for such a use
4650ba55f2f5SFrançois Tigeot  * case.
4651ba55f2f5SFrançois Tigeot  *
4652ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4653ba55f2f5SFrançois Tigeot  *
4654ba55f2f5SFrançois Tigeot  * Returns:
4655ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4656ba55f2f5SFrançois Tigeot  */
46575718399fSFrançois Tigeot int drm_mode_create_dumb_ioctl(struct drm_device *dev,
46585718399fSFrançois Tigeot 			       void *data, struct drm_file *file_priv)
46595718399fSFrançois Tigeot {
46605718399fSFrançois Tigeot 	struct drm_mode_create_dumb *args = data;
4661ba55f2f5SFrançois Tigeot 	u32 cpp, stride, size;
46625718399fSFrançois Tigeot 
46635718399fSFrançois Tigeot 	if (!dev->driver->dumb_create)
4664b5162e19SFrançois Tigeot 		return -ENOSYS;
4665ba55f2f5SFrançois Tigeot 	if (!args->width || !args->height || !args->bpp)
4666ba55f2f5SFrançois Tigeot 		return -EINVAL;
4667ba55f2f5SFrançois Tigeot 
4668ba55f2f5SFrançois Tigeot 	/* overflow checks for 32bit size calculations */
4669*24edb884SFrançois Tigeot 	/* NOTE: DIV_ROUND_UP() can overflow */
4670ba55f2f5SFrançois Tigeot 	cpp = DIV_ROUND_UP(args->bpp, 8);
4671*24edb884SFrançois Tigeot 	if (!cpp || cpp > 0xffffffffU / args->width)
4672ba55f2f5SFrançois Tigeot 		return -EINVAL;
4673ba55f2f5SFrançois Tigeot 	stride = cpp * args->width;
4674ba55f2f5SFrançois Tigeot 	if (args->height > 0xffffffffU / stride)
4675ba55f2f5SFrançois Tigeot 		return -EINVAL;
4676ba55f2f5SFrançois Tigeot 
4677ba55f2f5SFrançois Tigeot 	/* test for wrap-around */
4678ba55f2f5SFrançois Tigeot 	size = args->height * stride;
4679ba55f2f5SFrançois Tigeot 	if (PAGE_ALIGN(size) == 0)
4680ba55f2f5SFrançois Tigeot 		return -EINVAL;
4681ba55f2f5SFrançois Tigeot 
46825718399fSFrançois Tigeot 	return dev->driver->dumb_create(file_priv, dev, args);
46835718399fSFrançois Tigeot }
46845718399fSFrançois Tigeot 
4685ba55f2f5SFrançois Tigeot /**
4686ba55f2f5SFrançois Tigeot  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
4687ba55f2f5SFrançois Tigeot  * @dev: DRM device
4688ba55f2f5SFrançois Tigeot  * @data: ioctl data
4689ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4690ba55f2f5SFrançois Tigeot  *
4691ba55f2f5SFrançois Tigeot  * Allocate an offset in the drm device node's address space to be able to
4692ba55f2f5SFrançois Tigeot  * memory map a dumb buffer.
4693ba55f2f5SFrançois Tigeot  *
4694ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4695ba55f2f5SFrançois Tigeot  *
4696ba55f2f5SFrançois Tigeot  * Returns:
4697ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4698ba55f2f5SFrançois Tigeot  */
46995718399fSFrançois Tigeot int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
47005718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
47015718399fSFrançois Tigeot {
47025718399fSFrançois Tigeot 	struct drm_mode_map_dumb *args = data;
47035718399fSFrançois Tigeot 
47045718399fSFrançois Tigeot 	/* call driver ioctl to get mmap offset */
47055718399fSFrançois Tigeot 	if (!dev->driver->dumb_map_offset)
4706b5162e19SFrançois Tigeot 		return -ENOSYS;
47075718399fSFrançois Tigeot 
47085718399fSFrançois Tigeot 	return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset);
47095718399fSFrançois Tigeot }
47105718399fSFrançois Tigeot 
4711ba55f2f5SFrançois Tigeot /**
4712ba55f2f5SFrançois Tigeot  * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer
4713ba55f2f5SFrançois Tigeot  * @dev: DRM device
4714ba55f2f5SFrançois Tigeot  * @data: ioctl data
4715ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4716ba55f2f5SFrançois Tigeot  *
4717ba55f2f5SFrançois Tigeot  * This destroys the userspace handle for the given dumb backing storage buffer.
4718ba55f2f5SFrançois Tigeot  * Since buffer objects must be reference counted in the kernel a buffer object
4719ba55f2f5SFrançois Tigeot  * won't be immediately freed if a framebuffer modeset object still uses it.
4720ba55f2f5SFrançois Tigeot  *
4721ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4722ba55f2f5SFrançois Tigeot  *
4723ba55f2f5SFrançois Tigeot  * Returns:
4724ba55f2f5SFrançois Tigeot  * Zero on success, errno on failure.
4725ba55f2f5SFrançois Tigeot  */
47265718399fSFrançois Tigeot int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
47275718399fSFrançois Tigeot 				void *data, struct drm_file *file_priv)
47285718399fSFrançois Tigeot {
47295718399fSFrançois Tigeot 	struct drm_mode_destroy_dumb *args = data;
47305718399fSFrançois Tigeot 
47315718399fSFrançois Tigeot 	if (!dev->driver->dumb_destroy)
4732b5162e19SFrançois Tigeot 		return -ENOSYS;
47335718399fSFrançois Tigeot 
47345718399fSFrançois Tigeot 	return dev->driver->dumb_destroy(file_priv, dev, args->handle);
47355718399fSFrançois Tigeot }
47365718399fSFrançois Tigeot 
4737ba55f2f5SFrançois Tigeot /**
4738ba55f2f5SFrançois Tigeot  * drm_fb_get_bpp_depth - get the bpp/depth values for format
4739ba55f2f5SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
4740ba55f2f5SFrançois Tigeot  * @depth: storage for the depth value
4741ba55f2f5SFrançois Tigeot  * @bpp: storage for the bpp value
4742ba55f2f5SFrançois Tigeot  *
4743ba55f2f5SFrançois Tigeot  * This only supports RGB formats here for compat with code that doesn't use
4744ba55f2f5SFrançois Tigeot  * pixel formats directly yet.
47455718399fSFrançois Tigeot  */
47465718399fSFrançois Tigeot void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
47475718399fSFrançois Tigeot 			  int *bpp)
47485718399fSFrançois Tigeot {
47495718399fSFrançois Tigeot 	switch (format) {
47504dbb207bSFrançois Tigeot 	case DRM_FORMAT_C8:
47515718399fSFrançois Tigeot 	case DRM_FORMAT_RGB332:
47525718399fSFrançois Tigeot 	case DRM_FORMAT_BGR233:
47535718399fSFrançois Tigeot 		*depth = 8;
47545718399fSFrançois Tigeot 		*bpp = 8;
47555718399fSFrançois Tigeot 		break;
47565718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB1555:
47575718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR1555:
47585718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX5551:
47595718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX5551:
47605718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB1555:
47615718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR1555:
47625718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA5551:
47635718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA5551:
47645718399fSFrançois Tigeot 		*depth = 15;
47655718399fSFrançois Tigeot 		*bpp = 16;
47665718399fSFrançois Tigeot 		break;
47675718399fSFrançois Tigeot 	case DRM_FORMAT_RGB565:
47685718399fSFrançois Tigeot 	case DRM_FORMAT_BGR565:
47695718399fSFrançois Tigeot 		*depth = 16;
47705718399fSFrançois Tigeot 		*bpp = 16;
47715718399fSFrançois Tigeot 		break;
47725718399fSFrançois Tigeot 	case DRM_FORMAT_RGB888:
47735718399fSFrançois Tigeot 	case DRM_FORMAT_BGR888:
47745718399fSFrançois Tigeot 		*depth = 24;
47755718399fSFrançois Tigeot 		*bpp = 24;
47765718399fSFrançois Tigeot 		break;
47775718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB8888:
47785718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR8888:
47795718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX8888:
47805718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX8888:
47815718399fSFrançois Tigeot 		*depth = 24;
47825718399fSFrançois Tigeot 		*bpp = 32;
47835718399fSFrançois Tigeot 		break;
47845718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB2101010:
47855718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR2101010:
47865718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX1010102:
47875718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX1010102:
47885718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB2101010:
47895718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR2101010:
47905718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA1010102:
47915718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA1010102:
47925718399fSFrançois Tigeot 		*depth = 30;
47935718399fSFrançois Tigeot 		*bpp = 32;
47945718399fSFrançois Tigeot 		break;
47955718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB8888:
47965718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR8888:
47975718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA8888:
47985718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA8888:
47995718399fSFrançois Tigeot 		*depth = 32;
48005718399fSFrançois Tigeot 		*bpp = 32;
48015718399fSFrançois Tigeot 		break;
48025718399fSFrançois Tigeot 	default:
48039edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("unsupported pixel format %s\n",
48049edbd4a0SFrançois Tigeot 			      drm_get_format_name(format));
48055718399fSFrançois Tigeot 		*depth = 0;
48065718399fSFrançois Tigeot 		*bpp = 0;
48075718399fSFrançois Tigeot 		break;
48085718399fSFrançois Tigeot 	}
48095718399fSFrançois Tigeot }
4810b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_fb_get_bpp_depth);
4811b5162e19SFrançois Tigeot 
4812b5162e19SFrançois Tigeot /**
4813b5162e19SFrançois Tigeot  * drm_format_num_planes - get the number of planes for format
4814b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
4815b5162e19SFrançois Tigeot  *
4816ba55f2f5SFrançois Tigeot  * Returns:
4817b5162e19SFrançois Tigeot  * The number of planes used by the specified pixel format.
4818b5162e19SFrançois Tigeot  */
4819b5162e19SFrançois Tigeot int drm_format_num_planes(uint32_t format)
4820b5162e19SFrançois Tigeot {
4821b5162e19SFrançois Tigeot 	switch (format) {
4822b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
4823b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
4824b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
4825b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
4826b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
4827b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
4828b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
4829b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
4830b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV444:
4831b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU444:
4832b5162e19SFrançois Tigeot 		return 3;
4833b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
4834b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
4835b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
4836b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
4837b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
4838b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
4839b5162e19SFrançois Tigeot 		return 2;
4840b5162e19SFrançois Tigeot 	default:
4841b5162e19SFrançois Tigeot 		return 1;
4842b5162e19SFrançois Tigeot 	}
4843b5162e19SFrançois Tigeot }
4844b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_num_planes);
4845b5162e19SFrançois Tigeot 
4846b5162e19SFrançois Tigeot /**
4847b5162e19SFrançois Tigeot  * drm_format_plane_cpp - determine the bytes per pixel value
4848b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
4849b5162e19SFrançois Tigeot  * @plane: plane index
4850b5162e19SFrançois Tigeot  *
4851ba55f2f5SFrançois Tigeot  * Returns:
4852b5162e19SFrançois Tigeot  * The bytes per pixel value for the specified plane.
4853b5162e19SFrançois Tigeot  */
4854b5162e19SFrançois Tigeot int drm_format_plane_cpp(uint32_t format, int plane)
4855b5162e19SFrançois Tigeot {
4856b5162e19SFrançois Tigeot 	unsigned int depth;
4857b5162e19SFrançois Tigeot 	int bpp;
4858b5162e19SFrançois Tigeot 
4859b5162e19SFrançois Tigeot 	if (plane >= drm_format_num_planes(format))
4860b5162e19SFrançois Tigeot 		return 0;
4861b5162e19SFrançois Tigeot 
4862b5162e19SFrançois Tigeot 	switch (format) {
4863b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUYV:
4864b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVYU:
4865b5162e19SFrançois Tigeot 	case DRM_FORMAT_UYVY:
4866b5162e19SFrançois Tigeot 	case DRM_FORMAT_VYUY:
4867b5162e19SFrançois Tigeot 		return 2;
4868b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
4869b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
4870b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
4871b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
4872b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
4873b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
4874b5162e19SFrançois Tigeot 		return plane ? 2 : 1;
4875b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
4876b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
4877b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
4878b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
4879b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
4880b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
4881b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
4882b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
4883b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV444:
4884b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU444:
4885b5162e19SFrançois Tigeot 		return 1;
4886b5162e19SFrançois Tigeot 	default:
4887b5162e19SFrançois Tigeot 		drm_fb_get_bpp_depth(format, &depth, &bpp);
4888b5162e19SFrançois Tigeot 		return bpp >> 3;
4889b5162e19SFrançois Tigeot 	}
4890b5162e19SFrançois Tigeot }
4891b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_cpp);
4892b5162e19SFrançois Tigeot 
4893b5162e19SFrançois Tigeot /**
4894b5162e19SFrançois Tigeot  * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor
4895b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
4896b5162e19SFrançois Tigeot  *
4897ba55f2f5SFrançois Tigeot  * Returns:
4898b5162e19SFrançois Tigeot  * The horizontal chroma subsampling factor for the
4899b5162e19SFrançois Tigeot  * specified pixel format.
4900b5162e19SFrançois Tigeot  */
4901b5162e19SFrançois Tigeot int drm_format_horz_chroma_subsampling(uint32_t format)
4902b5162e19SFrançois Tigeot {
4903b5162e19SFrançois Tigeot 	switch (format) {
4904b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
4905b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
4906b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
4907b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
4908b5162e19SFrançois Tigeot 		return 4;
4909b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUYV:
4910b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVYU:
4911b5162e19SFrançois Tigeot 	case DRM_FORMAT_UYVY:
4912b5162e19SFrançois Tigeot 	case DRM_FORMAT_VYUY:
4913b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
4914b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
4915b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
4916b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
4917b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
4918b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
4919b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
4920b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
4921b5162e19SFrançois Tigeot 		return 2;
4922b5162e19SFrançois Tigeot 	default:
4923b5162e19SFrançois Tigeot 		return 1;
4924b5162e19SFrançois Tigeot 	}
4925b5162e19SFrançois Tigeot }
4926b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_horz_chroma_subsampling);
4927b5162e19SFrançois Tigeot 
4928b5162e19SFrançois Tigeot /**
4929b5162e19SFrançois Tigeot  * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor
4930b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
4931b5162e19SFrançois Tigeot  *
4932ba55f2f5SFrançois Tigeot  * Returns:
4933b5162e19SFrançois Tigeot  * The vertical chroma subsampling factor for the
4934b5162e19SFrançois Tigeot  * specified pixel format.
4935b5162e19SFrançois Tigeot  */
4936b5162e19SFrançois Tigeot int drm_format_vert_chroma_subsampling(uint32_t format)
4937b5162e19SFrançois Tigeot {
4938b5162e19SFrançois Tigeot 	switch (format) {
4939b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
4940b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
4941b5162e19SFrançois Tigeot 		return 4;
4942b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
4943b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
4944b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
4945b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
4946b5162e19SFrançois Tigeot 		return 2;
4947b5162e19SFrançois Tigeot 	default:
4948b5162e19SFrançois Tigeot 		return 1;
4949b5162e19SFrançois Tigeot 	}
4950b5162e19SFrançois Tigeot }
4951b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
49524dbb207bSFrançois Tigeot 
49534dbb207bSFrançois Tigeot /**
49544dbb207bSFrançois Tigeot  * drm_mode_config_init - initialize DRM mode_configuration structure
49554dbb207bSFrançois Tigeot  * @dev: DRM device
49564dbb207bSFrançois Tigeot  *
49574dbb207bSFrançois Tigeot  * Initialize @dev's mode_config structure, used for tracking the graphics
49584dbb207bSFrançois Tigeot  * configuration of @dev.
49594dbb207bSFrançois Tigeot  *
49604dbb207bSFrançois Tigeot  * Since this initializes the modeset locks, no locking is possible. Which is no
49614dbb207bSFrançois Tigeot  * problem, since this should happen single threaded at init time. It is the
49624dbb207bSFrançois Tigeot  * driver's problem to ensure this guarantee.
49634dbb207bSFrançois Tigeot  *
49644dbb207bSFrançois Tigeot  */
49654dbb207bSFrançois Tigeot void drm_mode_config_init(struct drm_device *dev)
49664dbb207bSFrançois Tigeot {
49674dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE);
4968ba55f2f5SFrançois Tigeot 	drm_modeset_lock_init(&dev->mode_config.connection_mutex);
49694dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE);
49704dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE);
49714dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
49724dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.crtc_list);
49734dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.connector_list);
4974ba55f2f5SFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.bridge_list);
49754dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.encoder_list);
49764dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.property_list);
49774dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
49784dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.plane_list);
49794dbb207bSFrançois Tigeot 	idr_init(&dev->mode_config.crtc_idr);
49804dbb207bSFrançois Tigeot 
49814dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
49824dbb207bSFrançois Tigeot 	drm_mode_create_standard_connector_properties(dev);
4983ba55f2f5SFrançois Tigeot 	drm_mode_create_standard_plane_properties(dev);
49844dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
49854dbb207bSFrançois Tigeot 
49864dbb207bSFrançois Tigeot 	/* Just to be sure */
49874dbb207bSFrançois Tigeot 	dev->mode_config.num_fb = 0;
49884dbb207bSFrançois Tigeot 	dev->mode_config.num_connector = 0;
49894dbb207bSFrançois Tigeot 	dev->mode_config.num_crtc = 0;
49904dbb207bSFrançois Tigeot 	dev->mode_config.num_encoder = 0;
4991ba55f2f5SFrançois Tigeot 	dev->mode_config.num_overlay_plane = 0;
4992ba55f2f5SFrançois Tigeot 	dev->mode_config.num_total_plane = 0;
49934dbb207bSFrançois Tigeot }
49944dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_init);
49954dbb207bSFrançois Tigeot 
49964dbb207bSFrançois Tigeot /**
49974dbb207bSFrançois Tigeot  * drm_mode_config_cleanup - free up DRM mode_config info
49984dbb207bSFrançois Tigeot  * @dev: DRM device
49994dbb207bSFrançois Tigeot  *
50004dbb207bSFrançois Tigeot  * Free up all the connectors and CRTCs associated with this DRM device, then
50014dbb207bSFrançois Tigeot  * free up the framebuffers and associated buffer objects.
50024dbb207bSFrançois Tigeot  *
50034dbb207bSFrançois Tigeot  * Note that since this /should/ happen single-threaded at driver/device
50044dbb207bSFrançois Tigeot  * teardown time, no locking is required. It's the driver's job to ensure that
50054dbb207bSFrançois Tigeot  * this guarantee actually holds true.
50064dbb207bSFrançois Tigeot  *
50074dbb207bSFrançois Tigeot  * FIXME: cleanup any dangling user buffer objects too
50084dbb207bSFrançois Tigeot  */
50094dbb207bSFrançois Tigeot void drm_mode_config_cleanup(struct drm_device *dev)
50104dbb207bSFrançois Tigeot {
50114dbb207bSFrançois Tigeot 	struct drm_connector *connector, *ot;
50124dbb207bSFrançois Tigeot 	struct drm_crtc *crtc, *ct;
50134dbb207bSFrançois Tigeot 	struct drm_encoder *encoder, *enct;
5014ba55f2f5SFrançois Tigeot 	struct drm_bridge *bridge, *brt;
50154dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb, *fbt;
50164dbb207bSFrançois Tigeot 	struct drm_property *property, *pt;
50174dbb207bSFrançois Tigeot 	struct drm_property_blob *blob, *bt;
50184dbb207bSFrançois Tigeot 	struct drm_plane *plane, *plt;
50194dbb207bSFrançois Tigeot 
50204dbb207bSFrançois Tigeot 	list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
50214dbb207bSFrançois Tigeot 				 head) {
50224dbb207bSFrançois Tigeot 		encoder->funcs->destroy(encoder);
50234dbb207bSFrançois Tigeot 	}
50244dbb207bSFrançois Tigeot 
5025ba55f2f5SFrançois Tigeot 	list_for_each_entry_safe(bridge, brt,
5026ba55f2f5SFrançois Tigeot 				 &dev->mode_config.bridge_list, head) {
5027ba55f2f5SFrançois Tigeot 		bridge->funcs->destroy(bridge);
5028ba55f2f5SFrançois Tigeot 	}
5029ba55f2f5SFrançois Tigeot 
50304dbb207bSFrançois Tigeot 	list_for_each_entry_safe(connector, ot,
50314dbb207bSFrançois Tigeot 				 &dev->mode_config.connector_list, head) {
50324dbb207bSFrançois Tigeot 		connector->funcs->destroy(connector);
50334dbb207bSFrançois Tigeot 	}
50344dbb207bSFrançois Tigeot 
50354dbb207bSFrançois Tigeot 	list_for_each_entry_safe(property, pt, &dev->mode_config.property_list,
50364dbb207bSFrançois Tigeot 				 head) {
50374dbb207bSFrançois Tigeot 		drm_property_destroy(dev, property);
50384dbb207bSFrançois Tigeot 	}
50394dbb207bSFrançois Tigeot 
50404dbb207bSFrançois Tigeot 	list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list,
50414dbb207bSFrançois Tigeot 				 head) {
50424dbb207bSFrançois Tigeot 		drm_property_destroy_blob(dev, blob);
50434dbb207bSFrançois Tigeot 	}
50444dbb207bSFrançois Tigeot 
50454dbb207bSFrançois Tigeot 	/*
50464dbb207bSFrançois Tigeot 	 * Single-threaded teardown context, so it's not required to grab the
50474dbb207bSFrançois Tigeot 	 * fb_lock to protect against concurrent fb_list access. Contrary, it
50484dbb207bSFrançois Tigeot 	 * would actually deadlock with the drm_framebuffer_cleanup function.
50494dbb207bSFrançois Tigeot 	 *
50504dbb207bSFrançois Tigeot 	 * Also, if there are any framebuffers left, that's a driver leak now,
50514dbb207bSFrançois Tigeot 	 * so politely WARN about this.
50524dbb207bSFrançois Tigeot 	 */
50534dbb207bSFrançois Tigeot 	WARN_ON(!list_empty(&dev->mode_config.fb_list));
50544dbb207bSFrançois Tigeot 	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
50554dbb207bSFrançois Tigeot 		drm_framebuffer_remove(fb);
50564dbb207bSFrançois Tigeot 	}
50574dbb207bSFrançois Tigeot 
50584dbb207bSFrançois Tigeot 	list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
50594dbb207bSFrançois Tigeot 				 head) {
50604dbb207bSFrançois Tigeot 		plane->funcs->destroy(plane);
50614dbb207bSFrançois Tigeot 	}
50624dbb207bSFrançois Tigeot 
50634dbb207bSFrançois Tigeot 	list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
50644dbb207bSFrançois Tigeot 		crtc->funcs->destroy(crtc);
50654dbb207bSFrançois Tigeot 	}
50664dbb207bSFrançois Tigeot 
50674dbb207bSFrançois Tigeot 	idr_destroy(&dev->mode_config.crtc_idr);
5068ba55f2f5SFrançois Tigeot 	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
50694dbb207bSFrançois Tigeot }
50704dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_cleanup);
5071