xref: /dflybsd-src/sys/dev/drm/drm_crtc.c (revision 9edbd4a07c3138f5c4f076f77de5d722fcc606cc)
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>
344dbb207bSFrançois Tigeot #include <linux/slab.h>
35b5162e19SFrançois Tigeot #include <linux/export.h>
3618e26a6dSFrançois Tigeot #include <drm/drmP.h>
3718e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3818e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
39b5162e19SFrançois Tigeot #include <uapi_drm/drm_fourcc.h>
405718399fSFrançois Tigeot 
41a2fdbec6SFrançois Tigeot /**
42a2fdbec6SFrançois Tigeot  * drm_modeset_lock_all - take all modeset locks
43a2fdbec6SFrançois Tigeot  * @dev: drm device
44a2fdbec6SFrançois Tigeot  *
45a2fdbec6SFrançois Tigeot  * This function takes all modeset locks, suitable where a more fine-grained
46a2fdbec6SFrançois Tigeot  * scheme isn't (yet) implemented.
47a2fdbec6SFrançois Tigeot  */
48a2fdbec6SFrançois Tigeot void drm_modeset_lock_all(struct drm_device *dev)
49a2fdbec6SFrançois Tigeot {
50a2fdbec6SFrançois Tigeot 	struct drm_crtc *crtc;
51a2fdbec6SFrançois Tigeot 
524dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
53a2fdbec6SFrançois Tigeot 
54a2fdbec6SFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
55a2fdbec6SFrançois Tigeot 		lockmgr(&crtc->mutex, LK_EXCLUSIVE);
56a2fdbec6SFrançois Tigeot }
57a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_all);
58a2fdbec6SFrançois Tigeot 
59a2fdbec6SFrançois Tigeot /**
60a2fdbec6SFrançois Tigeot  * drm_modeset_unlock_all - drop all modeset locks
61a2fdbec6SFrançois Tigeot  * @dev: device
62a2fdbec6SFrançois Tigeot  */
63a2fdbec6SFrançois Tigeot void drm_modeset_unlock_all(struct drm_device *dev)
64a2fdbec6SFrançois Tigeot {
65a2fdbec6SFrançois Tigeot 	struct drm_crtc *crtc;
66a2fdbec6SFrançois Tigeot 
67a2fdbec6SFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
684dbb207bSFrançois Tigeot 		mutex_unlock(&crtc->mutex);
69a2fdbec6SFrançois Tigeot 
704dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
71a2fdbec6SFrançois Tigeot }
72a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_unlock_all);
73a2fdbec6SFrançois Tigeot 
744dbb207bSFrançois Tigeot /**
754dbb207bSFrançois Tigeot  * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
764dbb207bSFrançois Tigeot  * @dev: device
774dbb207bSFrançois Tigeot  */
784dbb207bSFrançois Tigeot void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
794dbb207bSFrançois Tigeot {
804dbb207bSFrançois Tigeot 	struct drm_crtc *crtc;
814dbb207bSFrançois Tigeot 
824dbb207bSFrançois Tigeot 	/* Locking is currently fubar in the panic handler. */
83*9edbd4a0SFrançois Tigeot #if 0
844dbb207bSFrançois Tigeot 	if (oops_in_progress)
854dbb207bSFrançois Tigeot 		return;
864dbb207bSFrançois Tigeot #endif
874dbb207bSFrançois Tigeot 
884dbb207bSFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
894dbb207bSFrançois Tigeot 		WARN_ON(!mutex_is_locked(&crtc->mutex));
904dbb207bSFrançois Tigeot 
914dbb207bSFrançois Tigeot 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
924dbb207bSFrançois Tigeot }
934dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
944dbb207bSFrançois Tigeot 
955718399fSFrançois Tigeot /* Avoid boilerplate.  I'm tired of typing. */
965718399fSFrançois Tigeot #define DRM_ENUM_NAME_FN(fnname, list)				\
9706fede5aSFrançois Tigeot 	const char *fnname(int val)				\
985718399fSFrançois Tigeot 	{							\
995718399fSFrançois Tigeot 		int i;						\
100b5162e19SFrançois Tigeot 		for (i = 0; i < ARRAY_SIZE(list); i++) {	\
1015718399fSFrançois Tigeot 			if (list[i].type == val)		\
1025718399fSFrançois Tigeot 				return list[i].name;		\
1035718399fSFrançois Tigeot 		}						\
1045718399fSFrançois Tigeot 		return "(unknown)";				\
1055718399fSFrançois Tigeot 	}
1065718399fSFrançois Tigeot 
1075718399fSFrançois Tigeot /*
1085718399fSFrançois Tigeot  * Global properties
1095718399fSFrançois Tigeot  */
11006fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] =
1115718399fSFrançois Tigeot {	{ DRM_MODE_DPMS_ON, "On" },
1125718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_STANDBY, "Standby" },
1135718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_SUSPEND, "Suspend" },
1145718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_OFF, "Off" }
1155718399fSFrançois Tigeot };
1165718399fSFrançois Tigeot 
1175718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
1185718399fSFrançois Tigeot 
1195718399fSFrançois Tigeot /*
1205718399fSFrançois Tigeot  * Optional properties
1215718399fSFrançois Tigeot  */
12206fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
1235718399fSFrançois Tigeot {
1245718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_NONE, "None" },
1255718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_FULLSCREEN, "Full" },
1265718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_CENTER, "Center" },
1275718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_ASPECT, "Full aspect" },
1285718399fSFrançois Tigeot };
1295718399fSFrançois Tigeot 
1305718399fSFrançois Tigeot /*
1315718399fSFrançois Tigeot  * Non-global properties, but "required" for certain connectors.
1325718399fSFrançois Tigeot  */
13306fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
1345718399fSFrançois Tigeot {
1355718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
1365718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
1375718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
1385718399fSFrançois Tigeot };
1395718399fSFrançois Tigeot 
1405718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
1415718399fSFrançois Tigeot 
14206fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
1435718399fSFrançois Tigeot {
1445718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
1455718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
1465718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
1475718399fSFrançois Tigeot };
1485718399fSFrançois Tigeot 
1495718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
1505718399fSFrançois Tigeot 		 drm_dvi_i_subconnector_enum_list)
1515718399fSFrançois Tigeot 
15206fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] =
1535718399fSFrançois Tigeot {
1545718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
1555718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
1565718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
1575718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
1585718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
1595718399fSFrançois Tigeot };
1605718399fSFrançois Tigeot 
1615718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
1625718399fSFrançois Tigeot 
16306fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
1645718399fSFrançois Tigeot {
1655718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
1665718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
1675718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
1685718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
1695718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
1705718399fSFrançois Tigeot };
1715718399fSFrançois Tigeot 
1725718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
1735718399fSFrançois Tigeot 		 drm_tv_subconnector_enum_list)
1745718399fSFrançois Tigeot 
17506fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
1765718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_OFF,      "Off"      },
1775718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_ON,       "On"       },
1785718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
1795718399fSFrançois Tigeot };
1805718399fSFrançois Tigeot 
1815718399fSFrançois Tigeot struct drm_conn_prop_enum_list {
1825718399fSFrançois Tigeot 	int type;
18306fede5aSFrançois Tigeot 	const char *name;
1845718399fSFrançois Tigeot 	int count;
1855718399fSFrançois Tigeot };
1865718399fSFrançois Tigeot 
1875718399fSFrançois Tigeot /*
1885718399fSFrançois Tigeot  * Connector and encoder types.
1895718399fSFrançois Tigeot  */
1905718399fSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
191*9edbd4a0SFrançois Tigeot {	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
192*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
193*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
194*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
195*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
196*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Composite, "Composite" },
197*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
198*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
199*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Component, "Component" },
200*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
201*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
202*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
203*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
204*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_TV, "TV" },
205*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
206*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
207*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
2085718399fSFrançois Tigeot };
2095718399fSFrançois Tigeot 
21006fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_encoder_enum_list[] =
2115718399fSFrançois Tigeot {	{ DRM_MODE_ENCODER_NONE, "None" },
2125718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_DAC, "DAC" },
2135718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
2145718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
2155718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_TVDAC, "TV" },
216b5162e19SFrançois Tigeot 	{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
217*9edbd4a0SFrançois Tigeot 	{ DRM_MODE_ENCODER_DSI, "DSI" },
2185718399fSFrançois Tigeot };
2195718399fSFrançois Tigeot 
22006fede5aSFrançois Tigeot const char *drm_get_encoder_name(const struct drm_encoder *encoder)
2215718399fSFrançois Tigeot {
2225718399fSFrançois Tigeot 	static char buf[32];
2235718399fSFrançois Tigeot 
2245718399fSFrançois Tigeot 	ksnprintf(buf, 32, "%s-%d",
2255718399fSFrançois Tigeot 		 drm_encoder_enum_list[encoder->encoder_type].name,
2265718399fSFrançois Tigeot 		 encoder->base.id);
2275718399fSFrançois Tigeot 	return buf;
2285718399fSFrançois Tigeot }
229b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_get_encoder_name);
2305718399fSFrançois Tigeot 
23106fede5aSFrançois Tigeot const char *drm_get_connector_name(const struct drm_connector *connector)
2325718399fSFrançois Tigeot {
2335718399fSFrançois Tigeot 	static char buf[32];
2345718399fSFrançois Tigeot 
2355718399fSFrançois Tigeot 	ksnprintf(buf, 32, "%s-%d",
2365718399fSFrançois Tigeot 		 drm_connector_enum_list[connector->connector_type].name,
2375718399fSFrançois Tigeot 		 connector->connector_type_id);
2385718399fSFrançois Tigeot 	return buf;
2395718399fSFrançois Tigeot }
240b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_name);
2415718399fSFrançois Tigeot 
24206fede5aSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status)
2435718399fSFrançois Tigeot {
2445718399fSFrançois Tigeot 	if (status == connector_status_connected)
2455718399fSFrançois Tigeot 		return "connected";
2465718399fSFrançois Tigeot 	else if (status == connector_status_disconnected)
2475718399fSFrançois Tigeot 		return "disconnected";
2485718399fSFrançois Tigeot 	else
2495718399fSFrançois Tigeot 		return "unknown";
2505718399fSFrançois Tigeot }
2514dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name);
2525718399fSFrançois Tigeot 
25306fede5aSFrançois Tigeot static char printable_char(int c)
25406fede5aSFrançois Tigeot {
25506fede5aSFrançois Tigeot 	return isascii(c) && isprint(c) ? c : '?';
25606fede5aSFrançois Tigeot }
25706fede5aSFrançois Tigeot 
25806fede5aSFrançois Tigeot const char *drm_get_format_name(uint32_t format)
25906fede5aSFrançois Tigeot {
26006fede5aSFrançois Tigeot 	static char buf[32];
26106fede5aSFrançois Tigeot 
26206fede5aSFrançois Tigeot 	ksnprintf(buf, sizeof(buf),
26306fede5aSFrançois Tigeot 		 "%c%c%c%c %s-endian (0x%08x)",
26406fede5aSFrançois Tigeot 		 printable_char(format & 0xff),
26506fede5aSFrançois Tigeot 		 printable_char((format >> 8) & 0xff),
26606fede5aSFrançois Tigeot 		 printable_char((format >> 16) & 0xff),
26706fede5aSFrançois Tigeot 		 printable_char((format >> 24) & 0x7f),
26806fede5aSFrançois Tigeot 		 format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little",
26906fede5aSFrançois Tigeot 		 format);
27006fede5aSFrançois Tigeot 
27106fede5aSFrançois Tigeot 	return buf;
27206fede5aSFrançois Tigeot }
27306fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_get_format_name);
27406fede5aSFrançois Tigeot 
2755718399fSFrançois Tigeot /**
2764dbb207bSFrançois Tigeot  * drm_mode_object_get - allocate a new modeset identifier
2775718399fSFrançois Tigeot  * @dev: DRM device
2784dbb207bSFrançois Tigeot  * @obj: object pointer, used to generate unique ID
2794dbb207bSFrançois Tigeot  * @obj_type: object type
2805718399fSFrançois Tigeot  *
2815718399fSFrançois Tigeot  * Create a unique identifier based on @ptr in @dev's identifier space.  Used
2825718399fSFrançois Tigeot  * for tracking modes, CRTCs and connectors.
2835718399fSFrançois Tigeot  *
2845718399fSFrançois Tigeot  * RETURNS:
2855718399fSFrançois Tigeot  * New unique (relative to other objects in @dev) integer identifier for the
2865718399fSFrançois Tigeot  * object.
2875718399fSFrançois Tigeot  */
2885718399fSFrançois Tigeot static int drm_mode_object_get(struct drm_device *dev,
2895718399fSFrançois Tigeot 			       struct drm_mode_object *obj, uint32_t obj_type)
2905718399fSFrançois Tigeot {
2915718399fSFrançois Tigeot 	int ret;
2925718399fSFrançois Tigeot 
2939f0f5970SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
2949f0f5970SFrançois Tigeot 	ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL);
2959f0f5970SFrançois Tigeot 	if (ret >= 0) {
2964dbb207bSFrançois Tigeot 		/*
2974dbb207bSFrançois Tigeot 		 * Set up the object linking under the protection of the idr
2984dbb207bSFrançois Tigeot 		 * lock so that other users can't see inconsistent state.
2994dbb207bSFrançois Tigeot 		 */
3009f0f5970SFrançois Tigeot 		obj->id = ret;
3015718399fSFrançois Tigeot 		obj->type = obj_type;
3029f0f5970SFrançois Tigeot 	}
3039f0f5970SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
3049f0f5970SFrançois Tigeot 
3059f0f5970SFrançois Tigeot 	return ret < 0 ? ret : 0;
3065718399fSFrançois Tigeot }
3075718399fSFrançois Tigeot 
3085718399fSFrançois Tigeot /**
3094dbb207bSFrançois Tigeot  * drm_mode_object_put - free a modeset identifer
3105718399fSFrançois Tigeot  * @dev: DRM device
3114dbb207bSFrançois Tigeot  * @object: object to free
3125718399fSFrançois Tigeot  *
3135718399fSFrançois Tigeot  * Free @id from @dev's unique identifier pool.
3145718399fSFrançois Tigeot  */
3155718399fSFrançois Tigeot static void drm_mode_object_put(struct drm_device *dev,
3165718399fSFrançois Tigeot 				struct drm_mode_object *object)
3175718399fSFrançois Tigeot {
3184dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
319aea8bdbdSFrançois Tigeot 	idr_remove(&dev->mode_config.crtc_idr, object->id);
3204dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
3215718399fSFrançois Tigeot }
3225718399fSFrançois Tigeot 
3234dbb207bSFrançois Tigeot /**
3244dbb207bSFrançois Tigeot  * drm_mode_object_find - look up a drm object with static lifetime
3254dbb207bSFrançois Tigeot  * @dev: drm device
3264dbb207bSFrançois Tigeot  * @id: id of the mode object
3274dbb207bSFrançois Tigeot  * @type: type of the mode object
3284dbb207bSFrançois Tigeot  *
3294dbb207bSFrançois Tigeot  * Note that framebuffers cannot be looked up with this functions - since those
3304dbb207bSFrançois Tigeot  * are reference counted, they need special treatment.
3314dbb207bSFrançois Tigeot  */
3325718399fSFrançois Tigeot struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
3335718399fSFrançois Tigeot 		uint32_t id, uint32_t type)
3345718399fSFrançois Tigeot {
335aea8bdbdSFrançois Tigeot 	struct drm_mode_object *obj = NULL;
3365718399fSFrançois Tigeot 
3374dbb207bSFrançois Tigeot 	/* Framebuffers are reference counted and need their own lookup
3384dbb207bSFrançois Tigeot 	 * function.*/
3394dbb207bSFrançois Tigeot 	WARN_ON(type == DRM_MODE_OBJECT_FB);
3404dbb207bSFrançois Tigeot 
3414dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
342aea8bdbdSFrançois Tigeot 	obj = idr_find(&dev->mode_config.crtc_idr, id);
3435718399fSFrançois Tigeot 	if (!obj || (obj->type != type) || (obj->id != id))
3445718399fSFrançois Tigeot 		obj = NULL;
3454dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
3465718399fSFrançois Tigeot 
3475718399fSFrançois Tigeot 	return obj;
3485718399fSFrançois Tigeot }
349b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_find);
3505718399fSFrançois Tigeot 
3515718399fSFrançois Tigeot /**
3525718399fSFrançois Tigeot  * drm_framebuffer_init - initialize a framebuffer
3535718399fSFrançois Tigeot  * @dev: DRM device
3544dbb207bSFrançois Tigeot  * @fb: framebuffer to be initialized
3554dbb207bSFrançois Tigeot  * @funcs: ... with these functions
3565718399fSFrançois Tigeot  *
3575718399fSFrançois Tigeot  * Allocates an ID for the framebuffer's parent mode object, sets its mode
3585718399fSFrançois Tigeot  * functions & device file and adds it to the master fd list.
3595718399fSFrançois Tigeot  *
3604dbb207bSFrançois Tigeot  * IMPORTANT:
3614dbb207bSFrançois Tigeot  * This functions publishes the fb and makes it available for concurrent access
3624dbb207bSFrançois Tigeot  * by other users. Which means by this point the fb _must_ be fully set up -
3634dbb207bSFrançois Tigeot  * since all the fb attributes are invariant over its lifetime, no further
3644dbb207bSFrançois Tigeot  * locking but only correct reference counting is required.
3654dbb207bSFrançois Tigeot  *
3665718399fSFrançois Tigeot  * RETURNS:
3675718399fSFrançois Tigeot  * Zero on success, error code on failure.
3685718399fSFrançois Tigeot  */
3695718399fSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
3705718399fSFrançois Tigeot 			 const struct drm_framebuffer_funcs *funcs)
3715718399fSFrançois Tigeot {
3725718399fSFrançois Tigeot 	int ret;
3735718399fSFrançois Tigeot 
3744dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
375b5162e19SFrançois Tigeot 	kref_init(&fb->refcount);
3764dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&fb->filp_head);
3774dbb207bSFrançois Tigeot 	fb->dev = dev;
3784dbb207bSFrançois Tigeot 	fb->funcs = funcs;
379b5162e19SFrançois Tigeot 
3805718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
3815718399fSFrançois Tigeot 	if (ret)
3824dbb207bSFrançois Tigeot 		goto out;
3835718399fSFrançois Tigeot 
3844dbb207bSFrançois Tigeot 	/* Grab the idr reference. */
3854dbb207bSFrançois Tigeot 	drm_framebuffer_reference(fb);
3864dbb207bSFrançois Tigeot 
3875718399fSFrançois Tigeot 	dev->mode_config.num_fb++;
3885718399fSFrançois Tigeot 	list_add(&fb->head, &dev->mode_config.fb_list);
3894dbb207bSFrançois Tigeot out:
3904dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
3915718399fSFrançois Tigeot 
3925718399fSFrançois Tigeot 	return 0;
3935718399fSFrançois Tigeot }
394b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init);
395b5162e19SFrançois Tigeot 
396b5162e19SFrançois Tigeot static void drm_framebuffer_free(struct kref *kref)
397b5162e19SFrançois Tigeot {
398b5162e19SFrançois Tigeot 	struct drm_framebuffer *fb =
399b5162e19SFrançois Tigeot 			container_of(kref, struct drm_framebuffer, refcount);
400b5162e19SFrançois Tigeot 	fb->funcs->destroy(fb);
401b5162e19SFrançois Tigeot }
402b5162e19SFrançois Tigeot 
4034dbb207bSFrançois Tigeot static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev,
4044dbb207bSFrançois Tigeot 							uint32_t id)
4054dbb207bSFrançois Tigeot {
4064dbb207bSFrançois Tigeot 	struct drm_mode_object *obj = NULL;
4074dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb;
4084dbb207bSFrançois Tigeot 
4094dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
4104dbb207bSFrançois Tigeot 	obj = idr_find(&dev->mode_config.crtc_idr, id);
4114dbb207bSFrançois Tigeot 	if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id))
4124dbb207bSFrançois Tigeot 		fb = NULL;
4134dbb207bSFrançois Tigeot 	else
4144dbb207bSFrançois Tigeot 		fb = obj_to_fb(obj);
4154dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
4164dbb207bSFrançois Tigeot 
4174dbb207bSFrançois Tigeot 	return fb;
4184dbb207bSFrançois Tigeot }
4194dbb207bSFrançois Tigeot 
4204dbb207bSFrançois Tigeot /**
4214dbb207bSFrançois Tigeot  * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
4224dbb207bSFrançois Tigeot  * @dev: drm device
4234dbb207bSFrançois Tigeot  * @id: id of the fb object
4244dbb207bSFrançois Tigeot  *
4254dbb207bSFrançois Tigeot  * If successful, this grabs an additional reference to the framebuffer -
4264dbb207bSFrançois Tigeot  * callers need to make sure to eventually unreference the returned framebuffer
4274dbb207bSFrançois Tigeot  * again.
4284dbb207bSFrançois Tigeot  */
4294dbb207bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
4304dbb207bSFrançois Tigeot 					       uint32_t id)
4314dbb207bSFrançois Tigeot {
4324dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb;
4334dbb207bSFrançois Tigeot 
4344dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
4354dbb207bSFrançois Tigeot 	fb = __drm_framebuffer_lookup(dev, id);
4364dbb207bSFrançois Tigeot 	if (fb)
4374dbb207bSFrançois Tigeot 		drm_framebuffer_reference(fb);
4384dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
4394dbb207bSFrançois Tigeot 
4404dbb207bSFrançois Tigeot 	return fb;
4414dbb207bSFrançois Tigeot }
4424dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup);
4434dbb207bSFrançois Tigeot 
444b5162e19SFrançois Tigeot /**
445b5162e19SFrançois Tigeot  * drm_framebuffer_unreference - unref a framebuffer
4464dbb207bSFrançois Tigeot  * @fb: framebuffer to unref
447b5162e19SFrançois Tigeot  *
4484dbb207bSFrançois Tigeot  * This functions decrements the fb's refcount and frees it if it drops to zero.
449b5162e19SFrançois Tigeot  */
450b5162e19SFrançois Tigeot void drm_framebuffer_unreference(struct drm_framebuffer *fb)
451b5162e19SFrançois Tigeot {
452b5162e19SFrançois Tigeot 	DRM_DEBUG("FB ID: %d\n", fb->base.id);
453b5162e19SFrançois Tigeot 	kref_put(&fb->refcount, drm_framebuffer_free);
454b5162e19SFrançois Tigeot }
455b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unreference);
456b5162e19SFrançois Tigeot 
457b5162e19SFrançois Tigeot /**
458b5162e19SFrançois Tigeot  * drm_framebuffer_reference - incr the fb refcnt
4594dbb207bSFrançois Tigeot  * @fb: framebuffer
460b5162e19SFrançois Tigeot  */
461b5162e19SFrançois Tigeot void drm_framebuffer_reference(struct drm_framebuffer *fb)
462b5162e19SFrançois Tigeot {
463b5162e19SFrançois Tigeot 	DRM_DEBUG("FB ID: %d\n", fb->base.id);
464b5162e19SFrançois Tigeot 	kref_get(&fb->refcount);
465b5162e19SFrançois Tigeot }
466b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_reference);
4675718399fSFrançois Tigeot 
468a2fdbec6SFrançois Tigeot static void drm_framebuffer_free_bug(struct kref *kref)
469a2fdbec6SFrançois Tigeot {
470a2fdbec6SFrançois Tigeot 	BUG();
471a2fdbec6SFrançois Tigeot }
472a2fdbec6SFrançois Tigeot 
473a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
474a2fdbec6SFrançois Tigeot {
475a2fdbec6SFrançois Tigeot 	DRM_DEBUG("FB ID: %d\n", fb->base.id);
476a2fdbec6SFrançois Tigeot 	kref_put(&fb->refcount, drm_framebuffer_free_bug);
477a2fdbec6SFrançois Tigeot }
478a2fdbec6SFrançois Tigeot 
479a2fdbec6SFrançois Tigeot /* dev->mode_config.fb_lock must be held! */
480a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unregister(struct drm_device *dev,
481a2fdbec6SFrançois Tigeot 					 struct drm_framebuffer *fb)
482a2fdbec6SFrançois Tigeot {
483a2fdbec6SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
484a2fdbec6SFrançois Tigeot 	idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
485a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
486a2fdbec6SFrançois Tigeot 
487a2fdbec6SFrançois Tigeot 	fb->base.id = 0;
488a2fdbec6SFrançois Tigeot 
489a2fdbec6SFrançois Tigeot 	__drm_framebuffer_unreference(fb);
490a2fdbec6SFrançois Tigeot }
491a2fdbec6SFrançois Tigeot 
492a2fdbec6SFrançois Tigeot /**
493a2fdbec6SFrançois Tigeot  * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
494a2fdbec6SFrançois Tigeot  * @fb: fb to unregister
495a2fdbec6SFrançois Tigeot  *
496a2fdbec6SFrançois Tigeot  * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
497a2fdbec6SFrançois Tigeot  * those used for fbdev. Note that the caller must hold a reference of it's own,
498a2fdbec6SFrançois Tigeot  * i.e. the object may not be destroyed through this call (since it'll lead to a
499a2fdbec6SFrançois Tigeot  * locking inversion).
500a2fdbec6SFrançois Tigeot  */
501a2fdbec6SFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
502a2fdbec6SFrançois Tigeot {
503a2fdbec6SFrançois Tigeot 	struct drm_device *dev = fb->dev;
504a2fdbec6SFrançois Tigeot 
505a2fdbec6SFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
506a2fdbec6SFrançois Tigeot 	/* Mark fb as reaped and drop idr ref. */
507a2fdbec6SFrançois Tigeot 	__drm_framebuffer_unregister(dev, fb);
508a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
509a2fdbec6SFrançois Tigeot }
510a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private);
511a2fdbec6SFrançois Tigeot 
5125718399fSFrançois Tigeot /**
5135718399fSFrançois Tigeot  * drm_framebuffer_cleanup - remove a framebuffer object
5145718399fSFrançois Tigeot  * @fb: framebuffer to remove
5155718399fSFrançois Tigeot  *
5164dbb207bSFrançois Tigeot  * Cleanup references to a user-created framebuffer. This function is intended
5174dbb207bSFrançois Tigeot  * to be used from the drivers ->destroy callback.
5185718399fSFrançois Tigeot  *
5194dbb207bSFrançois Tigeot  * Note that this function does not remove the fb from active usuage - if it is
5204dbb207bSFrançois Tigeot  * still used anywhere, hilarity can ensue since userspace could call getfb on
5214dbb207bSFrançois Tigeot  * the id and get back -EINVAL. Obviously no concern at driver unload time.
5224dbb207bSFrançois Tigeot  *
5234dbb207bSFrançois Tigeot  * Also, the framebuffer will not be removed from the lookup idr - for
5244dbb207bSFrançois Tigeot  * user-created framebuffers this will happen in in the rmfb ioctl. For
5254dbb207bSFrançois Tigeot  * driver-private objects (e.g. for fbdev) drivers need to explicitly call
5264dbb207bSFrançois Tigeot  * drm_framebuffer_unregister_private.
5275718399fSFrançois Tigeot  */
5285718399fSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
5295718399fSFrançois Tigeot {
5305718399fSFrançois Tigeot 	struct drm_device *dev = fb->dev;
5314dbb207bSFrançois Tigeot 
5324dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
533b5162e19SFrançois Tigeot 	list_del(&fb->head);
534b5162e19SFrançois Tigeot 	dev->mode_config.num_fb--;
5354dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
536b5162e19SFrançois Tigeot }
537b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup);
538b5162e19SFrançois Tigeot 
539b5162e19SFrançois Tigeot /**
540b5162e19SFrançois Tigeot  * drm_framebuffer_remove - remove and unreference a framebuffer object
541b5162e19SFrançois Tigeot  * @fb: framebuffer to remove
542b5162e19SFrançois Tigeot  *
543b5162e19SFrançois Tigeot  * Scans all the CRTCs and planes in @dev's mode_config.  If they're
5444dbb207bSFrançois Tigeot  * using @fb, removes it, setting it to NULL. Then drops the reference to the
5454dbb207bSFrançois Tigeot  * passed-in framebuffer. Might take the modeset locks.
5464dbb207bSFrançois Tigeot  *
5474dbb207bSFrançois Tigeot  * Note that this function optimizes the cleanup away if the caller holds the
5484dbb207bSFrançois Tigeot  * last reference to the framebuffer. It is also guaranteed to not take the
5494dbb207bSFrançois Tigeot  * modeset locks in this case.
550b5162e19SFrançois Tigeot  */
551b5162e19SFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb)
552b5162e19SFrançois Tigeot {
553b5162e19SFrançois Tigeot 	struct drm_device *dev = fb->dev;
5545718399fSFrançois Tigeot 	struct drm_crtc *crtc;
5555718399fSFrançois Tigeot 	struct drm_plane *plane;
5565718399fSFrançois Tigeot 	struct drm_mode_set set;
5575718399fSFrançois Tigeot 	int ret;
5585718399fSFrançois Tigeot 
5594dbb207bSFrançois Tigeot 	WARN_ON(!list_empty(&fb->filp_head));
5604dbb207bSFrançois Tigeot 
5614dbb207bSFrançois Tigeot 	/*
5624dbb207bSFrançois Tigeot 	 * drm ABI mandates that we remove any deleted framebuffers from active
5634dbb207bSFrançois Tigeot 	 * useage. But since most sane clients only remove framebuffers they no
5644dbb207bSFrançois Tigeot 	 * longer need, try to optimize this away.
5654dbb207bSFrançois Tigeot 	 *
5664dbb207bSFrançois Tigeot 	 * Since we're holding a reference ourselves, observing a refcount of 1
5674dbb207bSFrançois Tigeot 	 * means that we're the last holder and can skip it. Also, the refcount
5684dbb207bSFrançois Tigeot 	 * can never increase from 1 again, so we don't need any barriers or
5694dbb207bSFrançois Tigeot 	 * locks.
5704dbb207bSFrançois Tigeot 	 *
5714dbb207bSFrançois Tigeot 	 * Note that userspace could try to race with use and instate a new
5724dbb207bSFrançois Tigeot 	 * usage _after_ we've cleared all current ones. End result will be an
5734dbb207bSFrançois Tigeot 	 * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
5744dbb207bSFrançois Tigeot 	 * in this manner.
5754dbb207bSFrançois Tigeot 	 */
5764dbb207bSFrançois Tigeot 	if (atomic_read(&fb->refcount.refcount) > 1) {
5774dbb207bSFrançois Tigeot 		drm_modeset_lock_all(dev);
5785718399fSFrançois Tigeot 		/* remove from any CRTC */
5795718399fSFrançois Tigeot 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
5805718399fSFrançois Tigeot 			if (crtc->fb == fb) {
5815718399fSFrançois Tigeot 				/* should turn off the crtc */
5825718399fSFrançois Tigeot 				memset(&set, 0, sizeof(struct drm_mode_set));
5835718399fSFrançois Tigeot 				set.crtc = crtc;
5845718399fSFrançois Tigeot 				set.fb = NULL;
5854dbb207bSFrançois Tigeot 				ret = drm_mode_set_config_internal(&set);
5865718399fSFrançois Tigeot 				if (ret)
5875718399fSFrançois Tigeot 					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
5885718399fSFrançois Tigeot 			}
5895718399fSFrançois Tigeot 		}
5905718399fSFrançois Tigeot 
5915718399fSFrançois Tigeot 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
59206fede5aSFrançois Tigeot 			if (plane->fb == fb)
59306fede5aSFrançois Tigeot 				drm_plane_force_disable(plane);
5945718399fSFrançois Tigeot 		}
5954dbb207bSFrançois Tigeot 		drm_modeset_unlock_all(dev);
5964dbb207bSFrançois Tigeot 	}
597b5162e19SFrançois Tigeot 
598b5162e19SFrançois Tigeot 	drm_framebuffer_unreference(fb);
5995718399fSFrançois Tigeot }
600b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove);
6015718399fSFrançois Tigeot 
6025718399fSFrançois Tigeot /**
6035718399fSFrançois Tigeot  * drm_crtc_init - Initialise a new CRTC object
6045718399fSFrançois Tigeot  * @dev: DRM device
6055718399fSFrançois Tigeot  * @crtc: CRTC object to init
6065718399fSFrançois Tigeot  * @funcs: callbacks for the new CRTC
6075718399fSFrançois Tigeot  *
60806fede5aSFrançois Tigeot  * Inits a new object created as base part of a driver crtc object.
6095718399fSFrançois Tigeot  *
6105718399fSFrançois Tigeot  * RETURNS:
6115718399fSFrançois Tigeot  * Zero on success, error code on failure.
6125718399fSFrançois Tigeot  */
6135718399fSFrançois Tigeot int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
6145718399fSFrançois Tigeot 		   const struct drm_crtc_funcs *funcs)
6155718399fSFrançois Tigeot {
6165718399fSFrançois Tigeot 	int ret;
6175718399fSFrançois Tigeot 
6185718399fSFrançois Tigeot 	crtc->dev = dev;
6195718399fSFrançois Tigeot 	crtc->funcs = funcs;
620b5162e19SFrançois Tigeot 	crtc->invert_dimensions = false;
6215718399fSFrançois Tigeot 
6224dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
6234dbb207bSFrançois Tigeot 	lockinit(&crtc->mutex, "drmcm", 0, LK_CANRECURSE);
6244dbb207bSFrançois Tigeot 	lockmgr(&crtc->mutex, LK_EXCLUSIVE);
625b5162e19SFrançois Tigeot 
6265718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
6275718399fSFrançois Tigeot 	if (ret)
6285718399fSFrançois Tigeot 		goto out;
6295718399fSFrançois Tigeot 
630b5162e19SFrançois Tigeot 	crtc->base.properties = &crtc->properties;
631b5162e19SFrançois Tigeot 
6325718399fSFrançois Tigeot 	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
6335718399fSFrançois Tigeot 	dev->mode_config.num_crtc++;
634b5162e19SFrançois Tigeot 
6355718399fSFrançois Tigeot  out:
6364dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
6375718399fSFrançois Tigeot 
6385718399fSFrançois Tigeot 	return ret;
6395718399fSFrançois Tigeot }
640b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init);
6415718399fSFrançois Tigeot 
6425718399fSFrançois Tigeot /**
64306fede5aSFrançois Tigeot  * drm_crtc_cleanup - Clean up the core crtc usage
6445718399fSFrançois Tigeot  * @crtc: CRTC to cleanup
6455718399fSFrançois Tigeot  *
64606fede5aSFrançois Tigeot  * This function cleans up @crtc and removes it from the DRM mode setting
64706fede5aSFrançois Tigeot  * core. Note that the function does *not* free the crtc structure itself,
64806fede5aSFrançois Tigeot  * this is the responsibility of the caller.
6495718399fSFrançois Tigeot  */
6505718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc)
6515718399fSFrançois Tigeot {
6525718399fSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
6535718399fSFrançois Tigeot 
6544dbb207bSFrançois Tigeot 	kfree(crtc->gamma_store);
6555718399fSFrançois Tigeot 	crtc->gamma_store = NULL;
6565718399fSFrançois Tigeot 
6575718399fSFrançois Tigeot 	drm_mode_object_put(dev, &crtc->base);
6585718399fSFrançois Tigeot 	list_del(&crtc->head);
6595718399fSFrançois Tigeot 	dev->mode_config.num_crtc--;
6605718399fSFrançois Tigeot }
661b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup);
6625718399fSFrançois Tigeot 
6635718399fSFrançois Tigeot /**
664*9edbd4a0SFrançois Tigeot  * drm_crtc_index - find the index of a registered CRTC
665*9edbd4a0SFrançois Tigeot  * @crtc: CRTC to find index for
666*9edbd4a0SFrançois Tigeot  *
667*9edbd4a0SFrançois Tigeot  * Given a registered CRTC, return the index of that CRTC within a DRM
668*9edbd4a0SFrançois Tigeot  * device's list of CRTCs.
669*9edbd4a0SFrançois Tigeot  */
670*9edbd4a0SFrançois Tigeot unsigned int drm_crtc_index(struct drm_crtc *crtc)
671*9edbd4a0SFrançois Tigeot {
672*9edbd4a0SFrançois Tigeot 	unsigned int index = 0;
673*9edbd4a0SFrançois Tigeot 	struct drm_crtc *tmp;
674*9edbd4a0SFrançois Tigeot 
675*9edbd4a0SFrançois Tigeot 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
676*9edbd4a0SFrançois Tigeot 		if (tmp == crtc)
677*9edbd4a0SFrançois Tigeot 			return index;
678*9edbd4a0SFrançois Tigeot 
679*9edbd4a0SFrançois Tigeot 		index++;
680*9edbd4a0SFrançois Tigeot 	}
681*9edbd4a0SFrançois Tigeot 
682*9edbd4a0SFrançois Tigeot 	BUG();
683*9edbd4a0SFrançois Tigeot }
684*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_index);
685*9edbd4a0SFrançois Tigeot 
686*9edbd4a0SFrançois Tigeot /**
6875718399fSFrançois Tigeot  * drm_mode_probed_add - add a mode to a connector's probed mode list
6885718399fSFrançois Tigeot  * @connector: connector the new mode
6895718399fSFrançois Tigeot  * @mode: mode data
6905718399fSFrançois Tigeot  *
6915718399fSFrançois Tigeot  * Add @mode to @connector's mode list for later use.
6925718399fSFrançois Tigeot  */
6935718399fSFrançois Tigeot void drm_mode_probed_add(struct drm_connector *connector,
6945718399fSFrançois Tigeot 			 struct drm_display_mode *mode)
6955718399fSFrançois Tigeot {
69606fede5aSFrançois Tigeot 	list_add_tail(&mode->head, &connector->probed_modes);
6975718399fSFrançois Tigeot }
698b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_probed_add);
6995718399fSFrançois Tigeot 
700*9edbd4a0SFrançois Tigeot /*
7015718399fSFrançois Tigeot  * drm_mode_remove - remove and free a mode
7025718399fSFrançois Tigeot  * @connector: connector list to modify
7035718399fSFrançois Tigeot  * @mode: mode to remove
7045718399fSFrançois Tigeot  *
7055718399fSFrançois Tigeot  * Remove @mode from @connector's mode list, then free it.
7065718399fSFrançois Tigeot  */
707*9edbd4a0SFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector,
7085718399fSFrançois Tigeot 			    struct drm_display_mode *mode)
7095718399fSFrançois Tigeot {
7105718399fSFrançois Tigeot 	list_del(&mode->head);
7115718399fSFrançois Tigeot 	drm_mode_destroy(connector->dev, mode);
7125718399fSFrançois Tigeot }
7135718399fSFrançois Tigeot 
7145718399fSFrançois Tigeot /**
7155718399fSFrançois Tigeot  * drm_connector_init - Init a preallocated connector
7165718399fSFrançois Tigeot  * @dev: DRM device
7175718399fSFrançois Tigeot  * @connector: the connector to init
7185718399fSFrançois Tigeot  * @funcs: callbacks for this connector
719a2fdbec6SFrançois Tigeot  * @connector_type: user visible type of the connector
7205718399fSFrançois Tigeot  *
7215718399fSFrançois Tigeot  * Initialises a preallocated connector. Connectors should be
7225718399fSFrançois Tigeot  * subclassed as part of driver connector objects.
7235718399fSFrançois Tigeot  *
7245718399fSFrançois Tigeot  * RETURNS:
7255718399fSFrançois Tigeot  * Zero on success, error code on failure.
7265718399fSFrançois Tigeot  */
7275718399fSFrançois Tigeot int drm_connector_init(struct drm_device *dev,
7285718399fSFrançois Tigeot 		       struct drm_connector *connector,
7295718399fSFrançois Tigeot 		       const struct drm_connector_funcs *funcs,
7305718399fSFrançois Tigeot 		       int connector_type)
7315718399fSFrançois Tigeot {
7325718399fSFrançois Tigeot 	int ret;
7335718399fSFrançois Tigeot 
7344dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
7355718399fSFrançois Tigeot 
7365718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
7375718399fSFrançois Tigeot 	if (ret)
7385718399fSFrançois Tigeot 		goto out;
7395718399fSFrançois Tigeot 
740b5162e19SFrançois Tigeot 	connector->base.properties = &connector->properties;
7415718399fSFrançois Tigeot 	connector->dev = dev;
7425718399fSFrançois Tigeot 	connector->funcs = funcs;
7435718399fSFrançois Tigeot 	connector->connector_type = connector_type;
7445718399fSFrançois Tigeot 	connector->connector_type_id =
7455718399fSFrançois Tigeot 		++drm_connector_enum_list[connector_type].count; /* TODO */
746*9edbd4a0SFrançois Tigeot 	if (connector->connector_type_id < 0) {
747*9edbd4a0SFrançois Tigeot 		ret = connector->connector_type_id;
748*9edbd4a0SFrançois Tigeot 		drm_mode_object_put(dev, &connector->base);
749*9edbd4a0SFrançois Tigeot 		goto out;
750*9edbd4a0SFrançois Tigeot 	}
7515718399fSFrançois Tigeot 	INIT_LIST_HEAD(&connector->probed_modes);
7525718399fSFrançois Tigeot 	INIT_LIST_HEAD(&connector->modes);
7535718399fSFrançois Tigeot 	connector->edid_blob_ptr = NULL;
754b5162e19SFrançois Tigeot 	connector->status = connector_status_unknown;
7555718399fSFrançois Tigeot 
7565718399fSFrançois Tigeot 	list_add_tail(&connector->head, &dev->mode_config.connector_list);
7575718399fSFrançois Tigeot 	dev->mode_config.num_connector++;
7585718399fSFrançois Tigeot 
759b5162e19SFrançois Tigeot 	if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
760b5162e19SFrançois Tigeot 		drm_object_attach_property(&connector->base,
761b5162e19SFrançois Tigeot 					      dev->mode_config.edid_property,
762b5162e19SFrançois Tigeot 					      0);
7635718399fSFrançois Tigeot 
764b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
7655718399fSFrançois Tigeot 				      dev->mode_config.dpms_property, 0);
7665718399fSFrançois Tigeot 
7675718399fSFrançois Tigeot  out:
7684dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
7695718399fSFrançois Tigeot 
7705718399fSFrançois Tigeot 	return ret;
7715718399fSFrançois Tigeot }
772b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_init);
7735718399fSFrançois Tigeot 
7745718399fSFrançois Tigeot /**
7755718399fSFrançois Tigeot  * drm_connector_cleanup - cleans up an initialised connector
7765718399fSFrançois Tigeot  * @connector: connector to cleanup
7775718399fSFrançois Tigeot  *
7785718399fSFrançois Tigeot  * Cleans up the connector but doesn't free the object.
7795718399fSFrançois Tigeot  */
7805718399fSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector)
7815718399fSFrançois Tigeot {
7825718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
7835718399fSFrançois Tigeot 	struct drm_display_mode *mode, *t;
7845718399fSFrançois Tigeot 
7855718399fSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
7865718399fSFrançois Tigeot 		drm_mode_remove(connector, mode);
7875718399fSFrançois Tigeot 
7885718399fSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->modes, head)
7895718399fSFrançois Tigeot 		drm_mode_remove(connector, mode);
7905718399fSFrançois Tigeot 
7915718399fSFrançois Tigeot 	drm_mode_object_put(dev, &connector->base);
7925718399fSFrançois Tigeot 	list_del(&connector->head);
7935718399fSFrançois Tigeot 	dev->mode_config.num_connector--;
7945718399fSFrançois Tigeot }
795b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup);
796b5162e19SFrançois Tigeot 
797b5162e19SFrançois Tigeot void drm_connector_unplug_all(struct drm_device *dev)
798b5162e19SFrançois Tigeot {
799b5162e19SFrançois Tigeot #if 0
800b5162e19SFrançois Tigeot 	struct drm_connector *connector;
801b5162e19SFrançois Tigeot 
802b5162e19SFrançois Tigeot 	/* taking the mode config mutex ends up in a clash with sysfs */
803b5162e19SFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
804b5162e19SFrançois Tigeot 		drm_sysfs_connector_remove(connector);
8054dbb207bSFrançois Tigeot 
806*9edbd4a0SFrançois Tigeot #endif
807b5162e19SFrançois Tigeot }
808b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_unplug_all);
8095718399fSFrançois Tigeot 
8105718399fSFrançois Tigeot int drm_encoder_init(struct drm_device *dev,
8115718399fSFrançois Tigeot 		      struct drm_encoder *encoder,
8125718399fSFrançois Tigeot 		      const struct drm_encoder_funcs *funcs,
8135718399fSFrançois Tigeot 		      int encoder_type)
8145718399fSFrançois Tigeot {
8155718399fSFrançois Tigeot 	int ret;
8165718399fSFrançois Tigeot 
8174dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
8185718399fSFrançois Tigeot 
8195718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
8205718399fSFrançois Tigeot 	if (ret)
8215718399fSFrançois Tigeot 		goto out;
8225718399fSFrançois Tigeot 
8235718399fSFrançois Tigeot 	encoder->dev = dev;
8245718399fSFrançois Tigeot 	encoder->encoder_type = encoder_type;
8255718399fSFrançois Tigeot 	encoder->funcs = funcs;
8265718399fSFrançois Tigeot 
8275718399fSFrançois Tigeot 	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
8285718399fSFrançois Tigeot 	dev->mode_config.num_encoder++;
8295718399fSFrançois Tigeot 
8305718399fSFrançois Tigeot  out:
8314dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
8325718399fSFrançois Tigeot 
8335718399fSFrançois Tigeot 	return ret;
8345718399fSFrançois Tigeot }
835b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_init);
8365718399fSFrançois Tigeot 
8375718399fSFrançois Tigeot void drm_encoder_cleanup(struct drm_encoder *encoder)
8385718399fSFrançois Tigeot {
8395718399fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
8404dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
8415718399fSFrançois Tigeot 	drm_mode_object_put(dev, &encoder->base);
8425718399fSFrançois Tigeot 	list_del(&encoder->head);
8435718399fSFrançois Tigeot 	dev->mode_config.num_encoder--;
8444dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
8455718399fSFrançois Tigeot }
846b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_cleanup);
8475718399fSFrançois Tigeot 
84806fede5aSFrançois Tigeot /**
84906fede5aSFrançois Tigeot  * drm_plane_init - Initialise a new plane object
85006fede5aSFrançois Tigeot  * @dev: DRM device
85106fede5aSFrançois Tigeot  * @plane: plane object to init
85206fede5aSFrançois Tigeot  * @possible_crtcs: bitmask of possible CRTCs
85306fede5aSFrançois Tigeot  * @funcs: callbacks for the new plane
85406fede5aSFrançois Tigeot  * @formats: array of supported formats (%DRM_FORMAT_*)
85506fede5aSFrançois Tigeot  * @format_count: number of elements in @formats
85606fede5aSFrançois Tigeot  * @priv: plane is private (hidden from userspace)?
85706fede5aSFrançois Tigeot  *
85806fede5aSFrançois Tigeot  * Inits a new object created as base part of a driver plane object.
85906fede5aSFrançois Tigeot  *
86006fede5aSFrançois Tigeot  * RETURNS:
86106fede5aSFrançois Tigeot  * Zero on success, error code on failure.
86206fede5aSFrançois Tigeot  */
8635718399fSFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
8645718399fSFrançois Tigeot 		   unsigned long possible_crtcs,
8655718399fSFrançois Tigeot 		   const struct drm_plane_funcs *funcs,
8665718399fSFrançois Tigeot 		   const uint32_t *formats, uint32_t format_count,
8675718399fSFrançois Tigeot 		   bool priv)
8685718399fSFrançois Tigeot {
8695718399fSFrançois Tigeot 	int ret;
8705718399fSFrançois Tigeot 
8714dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
8725718399fSFrançois Tigeot 
8735718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
8745718399fSFrançois Tigeot 	if (ret)
8755718399fSFrançois Tigeot 		goto out;
8765718399fSFrançois Tigeot 
877b5162e19SFrançois Tigeot 	plane->base.properties = &plane->properties;
8785718399fSFrançois Tigeot 	plane->dev = dev;
8795718399fSFrançois Tigeot 	plane->funcs = funcs;
8805718399fSFrançois Tigeot 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
8815a3b77d5SFrançois Tigeot 				      M_DRM, M_WAITOK);
882b5162e19SFrançois Tigeot 	if (!plane->format_types) {
883b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("out of memory when allocating plane\n");
884b5162e19SFrançois Tigeot 		drm_mode_object_put(dev, &plane->base);
885b5162e19SFrançois Tigeot 		ret = -ENOMEM;
886b5162e19SFrançois Tigeot 		goto out;
887b5162e19SFrançois Tigeot 	}
8885718399fSFrançois Tigeot 
8895718399fSFrançois Tigeot 	memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
8905718399fSFrançois Tigeot 	plane->format_count = format_count;
8915718399fSFrançois Tigeot 	plane->possible_crtcs = possible_crtcs;
8925718399fSFrançois Tigeot 
8935718399fSFrançois Tigeot 	/* private planes are not exposed to userspace, but depending on
8945718399fSFrançois Tigeot 	 * display hardware, might be convenient to allow sharing programming
8955718399fSFrançois Tigeot 	 * for the scanout engine with the crtc implementation.
8965718399fSFrançois Tigeot 	 */
8975718399fSFrançois Tigeot 	if (!priv) {
8985718399fSFrançois Tigeot 		list_add_tail(&plane->head, &dev->mode_config.plane_list);
8995718399fSFrançois Tigeot 		dev->mode_config.num_plane++;
9005718399fSFrançois Tigeot 	} else {
9015718399fSFrançois Tigeot 		INIT_LIST_HEAD(&plane->head);
9025718399fSFrançois Tigeot 	}
9035718399fSFrançois Tigeot 
9045718399fSFrançois Tigeot  out:
9054dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
9065718399fSFrançois Tigeot 
9075718399fSFrançois Tigeot 	return ret;
9085718399fSFrançois Tigeot }
909b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_init);
9105718399fSFrançois Tigeot 
91106fede5aSFrançois Tigeot /**
91206fede5aSFrançois Tigeot  * drm_plane_cleanup - Clean up the core plane usage
91306fede5aSFrançois Tigeot  * @plane: plane to cleanup
91406fede5aSFrançois Tigeot  *
91506fede5aSFrançois Tigeot  * This function cleans up @plane and removes it from the DRM mode setting
91606fede5aSFrançois Tigeot  * core. Note that the function does *not* free the plane structure itself,
91706fede5aSFrançois Tigeot  * this is the responsibility of the caller.
91806fede5aSFrançois Tigeot  */
9195718399fSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane)
9205718399fSFrançois Tigeot {
9215718399fSFrançois Tigeot 	struct drm_device *dev = plane->dev;
9225718399fSFrançois Tigeot 
9234dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
9244dbb207bSFrançois Tigeot 	kfree(plane->format_types);
9255718399fSFrançois Tigeot 	drm_mode_object_put(dev, &plane->base);
9265718399fSFrançois Tigeot 	/* if not added to a list, it must be a private plane */
9275718399fSFrançois Tigeot 	if (!list_empty(&plane->head)) {
9285718399fSFrançois Tigeot 		list_del(&plane->head);
9295718399fSFrançois Tigeot 		dev->mode_config.num_plane--;
9305718399fSFrançois Tigeot 	}
9314dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
9325718399fSFrançois Tigeot }
933b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup);
9345718399fSFrançois Tigeot 
9355718399fSFrançois Tigeot /**
93606fede5aSFrançois Tigeot  * drm_plane_force_disable - Forcibly disable a plane
93706fede5aSFrançois Tigeot  * @plane: plane to disable
93806fede5aSFrançois Tigeot  *
93906fede5aSFrançois Tigeot  * Forces the plane to be disabled.
94006fede5aSFrançois Tigeot  *
94106fede5aSFrançois Tigeot  * Used when the plane's current framebuffer is destroyed,
94206fede5aSFrançois Tigeot  * and when restoring fbdev mode.
94306fede5aSFrançois Tigeot  */
94406fede5aSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane)
94506fede5aSFrançois Tigeot {
94606fede5aSFrançois Tigeot 	int ret;
94706fede5aSFrançois Tigeot 
94806fede5aSFrançois Tigeot 	if (!plane->fb)
94906fede5aSFrançois Tigeot 		return;
95006fede5aSFrançois Tigeot 
95106fede5aSFrançois Tigeot 	ret = plane->funcs->disable_plane(plane);
95206fede5aSFrançois Tigeot 	if (ret)
95306fede5aSFrançois Tigeot 		DRM_ERROR("failed to disable plane with busy fb\n");
95406fede5aSFrançois Tigeot 	/* disconnect the plane from the fb and crtc: */
95506fede5aSFrançois Tigeot 	__drm_framebuffer_unreference(plane->fb);
95606fede5aSFrançois Tigeot 	plane->fb = NULL;
95706fede5aSFrançois Tigeot 	plane->crtc = NULL;
95806fede5aSFrançois Tigeot }
95906fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable);
96006fede5aSFrançois Tigeot 
96106fede5aSFrançois Tigeot /**
9625718399fSFrançois Tigeot  * drm_mode_create - create a new display mode
9635718399fSFrançois Tigeot  * @dev: DRM device
9645718399fSFrançois Tigeot  *
9655718399fSFrançois Tigeot  * Create a new drm_display_mode, give it an ID, and return it.
9665718399fSFrançois Tigeot  *
9675718399fSFrançois Tigeot  * RETURNS:
9685718399fSFrançois Tigeot  * Pointer to new mode on success, NULL on error.
9695718399fSFrançois Tigeot  */
9705718399fSFrançois Tigeot struct drm_display_mode *drm_mode_create(struct drm_device *dev)
9715718399fSFrançois Tigeot {
9725718399fSFrançois Tigeot 	struct drm_display_mode *nmode;
9735718399fSFrançois Tigeot 
9744dbb207bSFrançois Tigeot 	nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
975b5162e19SFrançois Tigeot 	if (!nmode)
976b5162e19SFrançois Tigeot 		return NULL;
9775718399fSFrançois Tigeot 
9785718399fSFrançois Tigeot 	if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
9794dbb207bSFrançois Tigeot 		kfree(nmode);
980b5162e19SFrançois Tigeot 		return NULL;
9815718399fSFrançois Tigeot 	}
982b5162e19SFrançois Tigeot 
9835718399fSFrançois Tigeot 	return nmode;
9845718399fSFrançois Tigeot }
985b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create);
9865718399fSFrançois Tigeot 
9875718399fSFrançois Tigeot /**
9885718399fSFrançois Tigeot  * drm_mode_destroy - remove a mode
9895718399fSFrançois Tigeot  * @dev: DRM device
9905718399fSFrançois Tigeot  * @mode: mode to remove
9915718399fSFrançois Tigeot  *
9925718399fSFrançois Tigeot  * Free @mode's unique identifier, then free it.
9935718399fSFrançois Tigeot  */
9945718399fSFrançois Tigeot void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
9955718399fSFrançois Tigeot {
9965718399fSFrançois Tigeot 	if (!mode)
9975718399fSFrançois Tigeot 		return;
9985718399fSFrançois Tigeot 
9995718399fSFrançois Tigeot 	drm_mode_object_put(dev, &mode->base);
10005718399fSFrançois Tigeot 
10014dbb207bSFrançois Tigeot 	kfree(mode);
10025718399fSFrançois Tigeot }
1003b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_destroy);
10045718399fSFrançois Tigeot 
10055718399fSFrançois Tigeot static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
10065718399fSFrançois Tigeot {
10075718399fSFrançois Tigeot 	struct drm_property *edid;
10085718399fSFrançois Tigeot 	struct drm_property *dpms;
10095718399fSFrançois Tigeot 
10105718399fSFrançois Tigeot 	/*
10115718399fSFrançois Tigeot 	 * Standard properties (apply to all connectors)
10125718399fSFrançois Tigeot 	 */
10135718399fSFrançois Tigeot 	edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
10145718399fSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
10155718399fSFrançois Tigeot 				   "EDID", 0);
10165718399fSFrançois Tigeot 	dev->mode_config.edid_property = edid;
10175718399fSFrançois Tigeot 
10185718399fSFrançois Tigeot 	dpms = drm_property_create_enum(dev, 0,
10195718399fSFrançois Tigeot 				   "DPMS", drm_dpms_enum_list,
1020b5162e19SFrançois Tigeot 				   ARRAY_SIZE(drm_dpms_enum_list));
10215718399fSFrançois Tigeot 	dev->mode_config.dpms_property = dpms;
10225718399fSFrançois Tigeot 
10235718399fSFrançois Tigeot 	return 0;
10245718399fSFrançois Tigeot }
10255718399fSFrançois Tigeot 
10265718399fSFrançois Tigeot /**
10275718399fSFrançois Tigeot  * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
10285718399fSFrançois Tigeot  * @dev: DRM device
10295718399fSFrançois Tigeot  *
10305718399fSFrançois Tigeot  * Called by a driver the first time a DVI-I connector is made.
10315718399fSFrançois Tigeot  */
10325718399fSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev)
10335718399fSFrançois Tigeot {
10345718399fSFrançois Tigeot 	struct drm_property *dvi_i_selector;
10355718399fSFrançois Tigeot 	struct drm_property *dvi_i_subconnector;
10365718399fSFrançois Tigeot 
10375718399fSFrançois Tigeot 	if (dev->mode_config.dvi_i_select_subconnector_property)
10385718399fSFrançois Tigeot 		return 0;
10395718399fSFrançois Tigeot 
10405718399fSFrançois Tigeot 	dvi_i_selector =
10415718399fSFrançois Tigeot 		drm_property_create_enum(dev, 0,
10425718399fSFrançois Tigeot 				    "select subconnector",
10435718399fSFrançois Tigeot 				    drm_dvi_i_select_enum_list,
1044b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_select_enum_list));
10455718399fSFrançois Tigeot 	dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
10465718399fSFrançois Tigeot 
10475718399fSFrançois Tigeot 	dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
10485718399fSFrançois Tigeot 				    "subconnector",
10495718399fSFrançois Tigeot 				    drm_dvi_i_subconnector_enum_list,
1050b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
10515718399fSFrançois Tigeot 	dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
10525718399fSFrançois Tigeot 
10535718399fSFrançois Tigeot 	return 0;
10545718399fSFrançois Tigeot }
1055b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
10565718399fSFrançois Tigeot 
10575718399fSFrançois Tigeot /**
10585718399fSFrançois Tigeot  * drm_create_tv_properties - create TV specific connector properties
10595718399fSFrançois Tigeot  * @dev: DRM device
10605718399fSFrançois Tigeot  * @num_modes: number of different TV formats (modes) supported
10615718399fSFrançois Tigeot  * @modes: array of pointers to strings containing name of each format
10625718399fSFrançois Tigeot  *
10635718399fSFrançois Tigeot  * Called by a driver's TV initialization routine, this function creates
10645718399fSFrançois Tigeot  * the TV specific connector properties for a given device.  Caller is
10655718399fSFrançois Tigeot  * responsible for allocating a list of format names and passing them to
10665718399fSFrançois Tigeot  * this routine.
10675718399fSFrançois Tigeot  */
10685718399fSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
10695718399fSFrançois Tigeot 				  char *modes[])
10705718399fSFrançois Tigeot {
10715718399fSFrançois Tigeot 	struct drm_property *tv_selector;
10725718399fSFrançois Tigeot 	struct drm_property *tv_subconnector;
10735718399fSFrançois Tigeot 	int i;
10745718399fSFrançois Tigeot 
10755718399fSFrançois Tigeot 	if (dev->mode_config.tv_select_subconnector_property)
10765718399fSFrançois Tigeot 		return 0;
10775718399fSFrançois Tigeot 
10785718399fSFrançois Tigeot 	/*
10795718399fSFrançois Tigeot 	 * Basic connector properties
10805718399fSFrançois Tigeot 	 */
10815718399fSFrançois Tigeot 	tv_selector = drm_property_create_enum(dev, 0,
10825718399fSFrançois Tigeot 					  "select subconnector",
10835718399fSFrançois Tigeot 					  drm_tv_select_enum_list,
1084b5162e19SFrançois Tigeot 					  ARRAY_SIZE(drm_tv_select_enum_list));
10855718399fSFrançois Tigeot 	dev->mode_config.tv_select_subconnector_property = tv_selector;
10865718399fSFrançois Tigeot 
10875718399fSFrançois Tigeot 	tv_subconnector =
10885718399fSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
10895718399fSFrançois Tigeot 				    "subconnector",
10905718399fSFrançois Tigeot 				    drm_tv_subconnector_enum_list,
1091b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_tv_subconnector_enum_list));
10925718399fSFrançois Tigeot 	dev->mode_config.tv_subconnector_property = tv_subconnector;
10935718399fSFrançois Tigeot 
10945718399fSFrançois Tigeot 	/*
10955718399fSFrançois Tigeot 	 * Other, TV specific properties: margins & TV modes.
10965718399fSFrançois Tigeot 	 */
10975718399fSFrançois Tigeot 	dev->mode_config.tv_left_margin_property =
10985718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "left margin", 0, 100);
10995718399fSFrançois Tigeot 
11005718399fSFrançois Tigeot 	dev->mode_config.tv_right_margin_property =
11015718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "right margin", 0, 100);
11025718399fSFrançois Tigeot 
11035718399fSFrançois Tigeot 	dev->mode_config.tv_top_margin_property =
11045718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "top margin", 0, 100);
11055718399fSFrançois Tigeot 
11065718399fSFrançois Tigeot 	dev->mode_config.tv_bottom_margin_property =
11075718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "bottom margin", 0, 100);
11085718399fSFrançois Tigeot 
11095718399fSFrançois Tigeot 	dev->mode_config.tv_mode_property =
11105718399fSFrançois Tigeot 		drm_property_create(dev, DRM_MODE_PROP_ENUM,
11115718399fSFrançois Tigeot 				    "mode", num_modes);
11125718399fSFrançois Tigeot 	for (i = 0; i < num_modes; i++)
11135718399fSFrançois Tigeot 		drm_property_add_enum(dev->mode_config.tv_mode_property, i,
11145718399fSFrançois Tigeot 				      i, modes[i]);
11155718399fSFrançois Tigeot 
11165718399fSFrançois Tigeot 	dev->mode_config.tv_brightness_property =
11175718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "brightness", 0, 100);
11185718399fSFrançois Tigeot 
11195718399fSFrançois Tigeot 	dev->mode_config.tv_contrast_property =
11205718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "contrast", 0, 100);
11215718399fSFrançois Tigeot 
11225718399fSFrançois Tigeot 	dev->mode_config.tv_flicker_reduction_property =
11235718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
11245718399fSFrançois Tigeot 
11255718399fSFrançois Tigeot 	dev->mode_config.tv_overscan_property =
11265718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "overscan", 0, 100);
11275718399fSFrançois Tigeot 
11285718399fSFrançois Tigeot 	dev->mode_config.tv_saturation_property =
11295718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "saturation", 0, 100);
11305718399fSFrançois Tigeot 
11315718399fSFrançois Tigeot 	dev->mode_config.tv_hue_property =
11325718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "hue", 0, 100);
11335718399fSFrançois Tigeot 
11345718399fSFrançois Tigeot 	return 0;
11355718399fSFrançois Tigeot }
1136b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties);
11375718399fSFrançois Tigeot 
11385718399fSFrançois Tigeot /**
11395718399fSFrançois Tigeot  * drm_mode_create_scaling_mode_property - create scaling mode property
11405718399fSFrançois Tigeot  * @dev: DRM device
11415718399fSFrançois Tigeot  *
11425718399fSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
11435718399fSFrançois Tigeot  * connectors.
11445718399fSFrançois Tigeot  */
11455718399fSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev)
11465718399fSFrançois Tigeot {
11475718399fSFrançois Tigeot 	struct drm_property *scaling_mode;
11485718399fSFrançois Tigeot 
11495718399fSFrançois Tigeot 	if (dev->mode_config.scaling_mode_property)
11505718399fSFrançois Tigeot 		return 0;
11515718399fSFrançois Tigeot 
11525718399fSFrançois Tigeot 	scaling_mode =
11535718399fSFrançois Tigeot 		drm_property_create_enum(dev, 0, "scaling mode",
11545718399fSFrançois Tigeot 				drm_scaling_mode_enum_list,
1155b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_scaling_mode_enum_list));
11565718399fSFrançois Tigeot 
11575718399fSFrançois Tigeot 	dev->mode_config.scaling_mode_property = scaling_mode;
11585718399fSFrançois Tigeot 
11595718399fSFrançois Tigeot 	return 0;
11605718399fSFrançois Tigeot }
1161b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
11625718399fSFrançois Tigeot 
11635718399fSFrançois Tigeot /**
11645718399fSFrançois Tigeot  * drm_mode_create_dirty_property - create dirty property
11655718399fSFrançois Tigeot  * @dev: DRM device
11665718399fSFrançois Tigeot  *
11675718399fSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
11685718399fSFrançois Tigeot  * connectors.
11695718399fSFrançois Tigeot  */
11705718399fSFrançois Tigeot int drm_mode_create_dirty_info_property(struct drm_device *dev)
11715718399fSFrançois Tigeot {
11725718399fSFrançois Tigeot 	struct drm_property *dirty_info;
11735718399fSFrançois Tigeot 
11745718399fSFrançois Tigeot 	if (dev->mode_config.dirty_info_property)
11755718399fSFrançois Tigeot 		return 0;
11765718399fSFrançois Tigeot 
11775718399fSFrançois Tigeot 	dirty_info =
11785718399fSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
11795718399fSFrançois Tigeot 				    "dirty",
11805718399fSFrançois Tigeot 				    drm_dirty_info_enum_list,
1181b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dirty_info_enum_list));
11825718399fSFrançois Tigeot 	dev->mode_config.dirty_info_property = dirty_info;
11835718399fSFrançois Tigeot 
11845718399fSFrançois Tigeot 	return 0;
11855718399fSFrançois Tigeot }
1186b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
11875718399fSFrançois Tigeot 
1188b5162e19SFrançois Tigeot static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
11895718399fSFrançois Tigeot {
11905718399fSFrançois Tigeot 	uint32_t total_objects = 0;
11915718399fSFrançois Tigeot 
11925718399fSFrançois Tigeot 	total_objects += dev->mode_config.num_crtc;
11935718399fSFrançois Tigeot 	total_objects += dev->mode_config.num_connector;
11945718399fSFrançois Tigeot 	total_objects += dev->mode_config.num_encoder;
11955718399fSFrançois Tigeot 
11964dbb207bSFrançois Tigeot 	group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
1197b5162e19SFrançois Tigeot 	if (!group->id_list)
1198b5162e19SFrançois Tigeot 		return -ENOMEM;
11995718399fSFrançois Tigeot 
12005718399fSFrançois Tigeot 	group->num_crtcs = 0;
12015718399fSFrançois Tigeot 	group->num_connectors = 0;
12025718399fSFrançois Tigeot 	group->num_encoders = 0;
12035718399fSFrançois Tigeot 	return 0;
12045718399fSFrançois Tigeot }
12055718399fSFrançois Tigeot 
12065718399fSFrançois Tigeot int drm_mode_group_init_legacy_group(struct drm_device *dev,
12075718399fSFrançois Tigeot 				     struct drm_mode_group *group)
12085718399fSFrançois Tigeot {
12095718399fSFrançois Tigeot 	struct drm_crtc *crtc;
12105718399fSFrançois Tigeot 	struct drm_encoder *encoder;
12115718399fSFrançois Tigeot 	struct drm_connector *connector;
12125718399fSFrançois Tigeot 	int ret;
12135718399fSFrançois Tigeot 
12145718399fSFrançois Tigeot 	if ((ret = drm_mode_group_init(dev, group)))
12155718399fSFrançois Tigeot 		return ret;
12165718399fSFrançois Tigeot 
12175718399fSFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
12185718399fSFrançois Tigeot 		group->id_list[group->num_crtcs++] = crtc->base.id;
12195718399fSFrançois Tigeot 
12205718399fSFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
12215718399fSFrançois Tigeot 		group->id_list[group->num_crtcs + group->num_encoders++] =
12225718399fSFrançois Tigeot 		encoder->base.id;
12235718399fSFrançois Tigeot 
12245718399fSFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
12255718399fSFrançois Tigeot 		group->id_list[group->num_crtcs + group->num_encoders +
12265718399fSFrançois Tigeot 			       group->num_connectors++] = connector->base.id;
12275718399fSFrançois Tigeot 
12285718399fSFrançois Tigeot 	return 0;
12295718399fSFrançois Tigeot }
1230b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
12315718399fSFrançois Tigeot 
12325718399fSFrançois Tigeot /**
12335718399fSFrançois Tigeot  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
12345718399fSFrançois Tigeot  * @out: drm_mode_modeinfo struct to return to the user
12355718399fSFrançois Tigeot  * @in: drm_display_mode to use
12365718399fSFrançois Tigeot  *
12375718399fSFrançois Tigeot  * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
12385718399fSFrançois Tigeot  * the user.
12395718399fSFrançois Tigeot  */
12405718399fSFrançois Tigeot static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
12415718399fSFrançois Tigeot 				      const struct drm_display_mode *in)
12425718399fSFrançois Tigeot {
1243b5162e19SFrançois Tigeot 	WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX ||
12445718399fSFrançois Tigeot 	     in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX ||
12455718399fSFrançois Tigeot 	     in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX ||
12465718399fSFrançois Tigeot 	     in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX ||
1247b5162e19SFrançois Tigeot 	     in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX,
1248b5162e19SFrançois Tigeot 	     "timing values too large for mode info\n");
12495718399fSFrançois Tigeot 
12505718399fSFrançois Tigeot 	out->clock = in->clock;
12515718399fSFrançois Tigeot 	out->hdisplay = in->hdisplay;
12525718399fSFrançois Tigeot 	out->hsync_start = in->hsync_start;
12535718399fSFrançois Tigeot 	out->hsync_end = in->hsync_end;
12545718399fSFrançois Tigeot 	out->htotal = in->htotal;
12555718399fSFrançois Tigeot 	out->hskew = in->hskew;
12565718399fSFrançois Tigeot 	out->vdisplay = in->vdisplay;
12575718399fSFrançois Tigeot 	out->vsync_start = in->vsync_start;
12585718399fSFrançois Tigeot 	out->vsync_end = in->vsync_end;
12595718399fSFrançois Tigeot 	out->vtotal = in->vtotal;
12605718399fSFrançois Tigeot 	out->vscan = in->vscan;
12615718399fSFrançois Tigeot 	out->vrefresh = in->vrefresh;
12625718399fSFrançois Tigeot 	out->flags = in->flags;
12635718399fSFrançois Tigeot 	out->type = in->type;
12645718399fSFrançois Tigeot 	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
12655718399fSFrançois Tigeot 	out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
12665718399fSFrançois Tigeot }
12675718399fSFrançois Tigeot 
12685718399fSFrançois Tigeot /**
1269*9edbd4a0SFrançois Tigeot  * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode
12705718399fSFrançois Tigeot  * @out: drm_display_mode to return to the user
12715718399fSFrançois Tigeot  * @in: drm_mode_modeinfo to use
12725718399fSFrançois Tigeot  *
12735718399fSFrançois Tigeot  * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
12745718399fSFrançois Tigeot  * the caller.
12755718399fSFrançois Tigeot  *
12765718399fSFrançois Tigeot  * RETURNS:
12775718399fSFrançois Tigeot  * Zero on success, errno on failure.
12785718399fSFrançois Tigeot  */
12795718399fSFrançois Tigeot static int drm_crtc_convert_umode(struct drm_display_mode *out,
12805718399fSFrançois Tigeot 				  const struct drm_mode_modeinfo *in)
12815718399fSFrançois Tigeot {
12825718399fSFrançois Tigeot 	if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
1283b5162e19SFrançois Tigeot 		return -ERANGE;
12845718399fSFrançois Tigeot 
1285*9edbd4a0SFrançois Tigeot 	if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX)
1286*9edbd4a0SFrançois Tigeot 		return -EINVAL;
1287*9edbd4a0SFrançois Tigeot 
12885718399fSFrançois Tigeot 	out->clock = in->clock;
12895718399fSFrançois Tigeot 	out->hdisplay = in->hdisplay;
12905718399fSFrançois Tigeot 	out->hsync_start = in->hsync_start;
12915718399fSFrançois Tigeot 	out->hsync_end = in->hsync_end;
12925718399fSFrançois Tigeot 	out->htotal = in->htotal;
12935718399fSFrançois Tigeot 	out->hskew = in->hskew;
12945718399fSFrançois Tigeot 	out->vdisplay = in->vdisplay;
12955718399fSFrançois Tigeot 	out->vsync_start = in->vsync_start;
12965718399fSFrançois Tigeot 	out->vsync_end = in->vsync_end;
12975718399fSFrançois Tigeot 	out->vtotal = in->vtotal;
12985718399fSFrançois Tigeot 	out->vscan = in->vscan;
12995718399fSFrançois Tigeot 	out->vrefresh = in->vrefresh;
13005718399fSFrançois Tigeot 	out->flags = in->flags;
13015718399fSFrançois Tigeot 	out->type = in->type;
13025718399fSFrançois Tigeot 	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
13035718399fSFrançois Tigeot 	out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
13045718399fSFrançois Tigeot 
13055718399fSFrançois Tigeot 	return 0;
13065718399fSFrançois Tigeot }
13075718399fSFrançois Tigeot 
13085718399fSFrançois Tigeot /**
13095718399fSFrançois Tigeot  * drm_mode_getresources - get graphics configuration
13104dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
13114dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
13124dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
13135718399fSFrançois Tigeot  *
13145718399fSFrançois Tigeot  * Construct a set of configuration description structures and return
13155718399fSFrançois Tigeot  * them to the user, including CRTC, connector and framebuffer configuration.
13165718399fSFrançois Tigeot  *
13175718399fSFrançois Tigeot  * Called by the user via ioctl.
13185718399fSFrançois Tigeot  *
13195718399fSFrançois Tigeot  * RETURNS:
13205718399fSFrançois Tigeot  * Zero on success, errno on failure.
13215718399fSFrançois Tigeot  */
13225718399fSFrançois Tigeot int drm_mode_getresources(struct drm_device *dev, void *data,
13235718399fSFrançois Tigeot 			  struct drm_file *file_priv)
13245718399fSFrançois Tigeot {
13255718399fSFrançois Tigeot 	struct drm_mode_card_res *card_res = data;
13265718399fSFrançois Tigeot 	struct list_head *lh;
13275718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
13285718399fSFrançois Tigeot 	struct drm_connector *connector;
13295718399fSFrançois Tigeot 	struct drm_crtc *crtc;
13305718399fSFrançois Tigeot 	struct drm_encoder *encoder;
13315718399fSFrançois Tigeot 	int ret = 0;
13325718399fSFrançois Tigeot 	int connector_count = 0;
13335718399fSFrançois Tigeot 	int crtc_count = 0;
13345718399fSFrançois Tigeot 	int fb_count = 0;
13355718399fSFrançois Tigeot 	int encoder_count = 0;
13365718399fSFrançois Tigeot 	int copied = 0, i;
13375718399fSFrançois Tigeot 	uint32_t __user *fb_id;
13385718399fSFrançois Tigeot 	uint32_t __user *crtc_id;
13395718399fSFrançois Tigeot 	uint32_t __user *connector_id;
13405718399fSFrançois Tigeot 	uint32_t __user *encoder_id;
13415718399fSFrançois Tigeot 	struct drm_mode_group *mode_group;
13425718399fSFrançois Tigeot 
13435718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1344b5162e19SFrançois Tigeot 		return -EINVAL;
13455718399fSFrançois Tigeot 
13465718399fSFrançois Tigeot 
13474dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
13485718399fSFrançois Tigeot 	/*
13495718399fSFrançois Tigeot 	 * For the non-control nodes we need to limit the list of resources
13505718399fSFrançois Tigeot 	 * by IDs in the group list for this node
13515718399fSFrançois Tigeot 	 */
13525718399fSFrançois Tigeot 	list_for_each(lh, &file_priv->fbs)
13535718399fSFrançois Tigeot 		fb_count++;
13545718399fSFrançois Tigeot 
13554dbb207bSFrançois Tigeot 	/* handle this in 4 parts */
13564dbb207bSFrançois Tigeot 	/* FBs */
13574dbb207bSFrançois Tigeot 	if (card_res->count_fbs >= fb_count) {
13584dbb207bSFrançois Tigeot 		copied = 0;
13594dbb207bSFrançois Tigeot 		fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
13604dbb207bSFrançois Tigeot 		list_for_each_entry(fb, &file_priv->fbs, filp_head) {
13614dbb207bSFrançois Tigeot 			if (put_user(fb->base.id, fb_id + copied)) {
13624dbb207bSFrançois Tigeot 				mutex_unlock(&file_priv->fbs_lock);
13634dbb207bSFrançois Tigeot 				return -EFAULT;
13644dbb207bSFrançois Tigeot 			}
13654dbb207bSFrançois Tigeot 			copied++;
13664dbb207bSFrançois Tigeot 		}
13674dbb207bSFrançois Tigeot 	}
13684dbb207bSFrançois Tigeot 	card_res->count_fbs = fb_count;
13694dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
13704dbb207bSFrançois Tigeot 
13714dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
13724dbb207bSFrançois Tigeot #if 0
13734dbb207bSFrançois Tigeot 	mode_group = &file_priv->master->minor->mode_group;
13744dbb207bSFrançois Tigeot 	if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
13754dbb207bSFrançois Tigeot #else
13765718399fSFrançois Tigeot 	mode_group = NULL; /* XXXKIB */
13775718399fSFrançois Tigeot 	if (1 || file_priv->master) {
13785718399fSFrançois Tigeot #endif
13795718399fSFrançois Tigeot 		list_for_each(lh, &dev->mode_config.crtc_list)
13805718399fSFrançois Tigeot 			crtc_count++;
13815718399fSFrançois Tigeot 
13825718399fSFrançois Tigeot 		list_for_each(lh, &dev->mode_config.connector_list)
13835718399fSFrançois Tigeot 			connector_count++;
13845718399fSFrançois Tigeot 
13855718399fSFrançois Tigeot 		list_for_each(lh, &dev->mode_config.encoder_list)
13865718399fSFrançois Tigeot 			encoder_count++;
13875718399fSFrançois Tigeot 	} else {
13885718399fSFrançois Tigeot 
13895718399fSFrançois Tigeot 		crtc_count = mode_group->num_crtcs;
13905718399fSFrançois Tigeot 		connector_count = mode_group->num_connectors;
13915718399fSFrançois Tigeot 		encoder_count = mode_group->num_encoders;
13925718399fSFrançois Tigeot 	}
13935718399fSFrançois Tigeot 
13945718399fSFrançois Tigeot 	card_res->max_height = dev->mode_config.max_height;
13955718399fSFrançois Tigeot 	card_res->min_height = dev->mode_config.min_height;
13965718399fSFrançois Tigeot 	card_res->max_width = dev->mode_config.max_width;
13975718399fSFrançois Tigeot 	card_res->min_width = dev->mode_config.min_width;
13985718399fSFrançois Tigeot 
13995718399fSFrançois Tigeot 	/* CRTCs */
14005718399fSFrançois Tigeot 	if (card_res->count_crtcs >= crtc_count) {
14015718399fSFrançois Tigeot 		copied = 0;
1402b5162e19SFrançois Tigeot 		crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
14034dbb207bSFrançois Tigeot 		if (1 || file_priv->master) {
14045718399fSFrançois Tigeot 			list_for_each_entry(crtc, &dev->mode_config.crtc_list,
14055718399fSFrançois Tigeot 					    head) {
14065718399fSFrançois Tigeot 				DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
1407b5162e19SFrançois Tigeot 				if (put_user(crtc->base.id, crtc_id + copied)) {
1408b5162e19SFrançois Tigeot 					ret = -EFAULT;
14095718399fSFrançois Tigeot 					goto out;
14105718399fSFrançois Tigeot 				}
14115718399fSFrançois Tigeot 				copied++;
14125718399fSFrançois Tigeot 			}
14135718399fSFrançois Tigeot 		} else {
14145718399fSFrançois Tigeot 			for (i = 0; i < mode_group->num_crtcs; i++) {
1415b5162e19SFrançois Tigeot 				if (put_user(mode_group->id_list[i],
1416b5162e19SFrançois Tigeot 					     crtc_id + copied)) {
1417b5162e19SFrançois Tigeot 					ret = -EFAULT;
14185718399fSFrançois Tigeot 					goto out;
14195718399fSFrançois Tigeot 				}
14205718399fSFrançois Tigeot 				copied++;
14215718399fSFrançois Tigeot 			}
14225718399fSFrançois Tigeot 		}
14235718399fSFrançois Tigeot 	}
14245718399fSFrançois Tigeot 	card_res->count_crtcs = crtc_count;
14255718399fSFrançois Tigeot 
14265718399fSFrançois Tigeot 	/* Encoders */
14275718399fSFrançois Tigeot 	if (card_res->count_encoders >= encoder_count) {
14285718399fSFrançois Tigeot 		copied = 0;
1429b5162e19SFrançois Tigeot 		encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
14304dbb207bSFrançois Tigeot 		if (file_priv->master) {
14315718399fSFrançois Tigeot 			list_for_each_entry(encoder,
14325718399fSFrançois Tigeot 					    &dev->mode_config.encoder_list,
14335718399fSFrançois Tigeot 					    head) {
14345718399fSFrançois Tigeot 				DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
14355718399fSFrançois Tigeot 						drm_get_encoder_name(encoder));
1436b5162e19SFrançois Tigeot 				if (put_user(encoder->base.id, encoder_id +
1437b5162e19SFrançois Tigeot 					     copied)) {
1438b5162e19SFrançois Tigeot 					ret = -EFAULT;
14395718399fSFrançois Tigeot 					goto out;
14405718399fSFrançois Tigeot 				}
14415718399fSFrançois Tigeot 				copied++;
14425718399fSFrançois Tigeot 			}
14435718399fSFrançois Tigeot 		} else {
1444b5162e19SFrançois Tigeot 			for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
1445b5162e19SFrançois Tigeot 				if (put_user(mode_group->id_list[i],
1446b5162e19SFrançois Tigeot 					     encoder_id + copied)) {
1447b5162e19SFrançois Tigeot 					ret = -EFAULT;
14485718399fSFrançois Tigeot 					goto out;
14495718399fSFrançois Tigeot 				}
14505718399fSFrançois Tigeot 				copied++;
14515718399fSFrançois Tigeot 			}
14525718399fSFrançois Tigeot 
14535718399fSFrançois Tigeot 		}
14545718399fSFrançois Tigeot 	}
14555718399fSFrançois Tigeot 	card_res->count_encoders = encoder_count;
14565718399fSFrançois Tigeot 
14575718399fSFrançois Tigeot 	/* Connectors */
14585718399fSFrançois Tigeot 	if (card_res->count_connectors >= connector_count) {
14595718399fSFrançois Tigeot 		copied = 0;
1460b5162e19SFrançois Tigeot 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
14614dbb207bSFrançois Tigeot 		if (file_priv->master) {
14625718399fSFrançois Tigeot 			list_for_each_entry(connector,
14635718399fSFrançois Tigeot 					    &dev->mode_config.connector_list,
14645718399fSFrançois Tigeot 					    head) {
14655718399fSFrançois Tigeot 				DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
14665718399fSFrançois Tigeot 					connector->base.id,
14675718399fSFrançois Tigeot 					drm_get_connector_name(connector));
1468b5162e19SFrançois Tigeot 				if (put_user(connector->base.id,
1469b5162e19SFrançois Tigeot 					     connector_id + copied)) {
1470b5162e19SFrançois Tigeot 					ret = -EFAULT;
14715718399fSFrançois Tigeot 					goto out;
14725718399fSFrançois Tigeot 				}
14735718399fSFrançois Tigeot 				copied++;
14745718399fSFrançois Tigeot 			}
14755718399fSFrançois Tigeot 		} else {
14765718399fSFrançois Tigeot 			int start = mode_group->num_crtcs +
14775718399fSFrançois Tigeot 				mode_group->num_encoders;
14785718399fSFrançois Tigeot 			for (i = start; i < start + mode_group->num_connectors; i++) {
1479b5162e19SFrançois Tigeot 				if (put_user(mode_group->id_list[i],
1480b5162e19SFrançois Tigeot 					     connector_id + copied)) {
1481b5162e19SFrançois Tigeot 					ret = -EFAULT;
14825718399fSFrançois Tigeot 					goto out;
14835718399fSFrançois Tigeot 				}
14845718399fSFrançois Tigeot 				copied++;
14855718399fSFrançois Tigeot 			}
14865718399fSFrançois Tigeot 		}
14875718399fSFrançois Tigeot 	}
14885718399fSFrançois Tigeot 	card_res->count_connectors = connector_count;
14895718399fSFrançois Tigeot 
14905718399fSFrançois Tigeot 	DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs,
14915718399fSFrançois Tigeot 		  card_res->count_connectors, card_res->count_encoders);
14925718399fSFrançois Tigeot 
14935718399fSFrançois Tigeot out:
14944dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
14955718399fSFrançois Tigeot 	return ret;
14965718399fSFrançois Tigeot }
14975718399fSFrançois Tigeot 
14985718399fSFrançois Tigeot /**
14995718399fSFrançois Tigeot  * drm_mode_getcrtc - get CRTC configuration
15004dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
15014dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
15024dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
15035718399fSFrançois Tigeot  *
15045718399fSFrançois Tigeot  * Construct a CRTC configuration structure to return to the user.
15055718399fSFrançois Tigeot  *
15065718399fSFrançois Tigeot  * Called by the user via ioctl.
15075718399fSFrançois Tigeot  *
15085718399fSFrançois Tigeot  * RETURNS:
15095718399fSFrançois Tigeot  * Zero on success, errno on failure.
15105718399fSFrançois Tigeot  */
15115718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev,
15125718399fSFrançois Tigeot 		     void *data, struct drm_file *file_priv)
15135718399fSFrançois Tigeot {
15145718399fSFrançois Tigeot 	struct drm_mode_crtc *crtc_resp = data;
15155718399fSFrançois Tigeot 	struct drm_crtc *crtc;
15165718399fSFrançois Tigeot 	struct drm_mode_object *obj;
15175718399fSFrançois Tigeot 	int ret = 0;
15185718399fSFrançois Tigeot 
15195718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1520b5162e19SFrançois Tigeot 		return -EINVAL;
15215718399fSFrançois Tigeot 
15224dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
15235718399fSFrançois Tigeot 
15245718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
15255718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_CRTC);
15265718399fSFrançois Tigeot 	if (!obj) {
1527*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
15285718399fSFrançois Tigeot 		goto out;
15295718399fSFrançois Tigeot 	}
15305718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
15315718399fSFrançois Tigeot 
15325718399fSFrançois Tigeot 	crtc_resp->x = crtc->x;
15335718399fSFrançois Tigeot 	crtc_resp->y = crtc->y;
15345718399fSFrançois Tigeot 	crtc_resp->gamma_size = crtc->gamma_size;
15355718399fSFrançois Tigeot 	if (crtc->fb)
15365718399fSFrançois Tigeot 		crtc_resp->fb_id = crtc->fb->base.id;
15375718399fSFrançois Tigeot 	else
15385718399fSFrançois Tigeot 		crtc_resp->fb_id = 0;
15395718399fSFrançois Tigeot 
15405718399fSFrançois Tigeot 	if (crtc->enabled) {
15415718399fSFrançois Tigeot 
15425718399fSFrançois Tigeot 		drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
15435718399fSFrançois Tigeot 		crtc_resp->mode_valid = 1;
15445718399fSFrançois Tigeot 
15455718399fSFrançois Tigeot 	} else {
15465718399fSFrançois Tigeot 		crtc_resp->mode_valid = 0;
15475718399fSFrançois Tigeot 	}
15485718399fSFrançois Tigeot 
15495718399fSFrançois Tigeot out:
15504dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
15515718399fSFrançois Tigeot 	return ret;
15525718399fSFrançois Tigeot }
15535718399fSFrançois Tigeot 
1554*9edbd4a0SFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
1555*9edbd4a0SFrançois Tigeot 					 const struct drm_file *file_priv)
1556*9edbd4a0SFrançois Tigeot {
1557*9edbd4a0SFrançois Tigeot 	/*
1558*9edbd4a0SFrançois Tigeot 	 * If user-space hasn't configured the driver to expose the stereo 3D
1559*9edbd4a0SFrançois Tigeot 	 * modes, don't expose them.
1560*9edbd4a0SFrançois Tigeot 	 */
1561*9edbd4a0SFrançois Tigeot 	if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
1562*9edbd4a0SFrançois Tigeot 		return false;
1563*9edbd4a0SFrançois Tigeot 
1564*9edbd4a0SFrançois Tigeot 	return true;
1565*9edbd4a0SFrançois Tigeot }
1566*9edbd4a0SFrançois Tigeot 
15675718399fSFrançois Tigeot /**
15685718399fSFrançois Tigeot  * drm_mode_getconnector - get connector configuration
15694dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
15704dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
15714dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
15725718399fSFrançois Tigeot  *
15735718399fSFrançois Tigeot  * Construct a connector configuration structure to return to the user.
15745718399fSFrançois Tigeot  *
15755718399fSFrançois Tigeot  * Called by the user via ioctl.
15765718399fSFrançois Tigeot  *
15775718399fSFrançois Tigeot  * RETURNS:
15785718399fSFrançois Tigeot  * Zero on success, errno on failure.
15795718399fSFrançois Tigeot  */
15805718399fSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data,
15815718399fSFrançois Tigeot 			  struct drm_file *file_priv)
15825718399fSFrançois Tigeot {
15835718399fSFrançois Tigeot 	struct drm_mode_get_connector *out_resp = data;
15845718399fSFrançois Tigeot 	struct drm_mode_object *obj;
15855718399fSFrançois Tigeot 	struct drm_connector *connector;
15865718399fSFrançois Tigeot 	struct drm_display_mode *mode;
15875718399fSFrançois Tigeot 	int mode_count = 0;
15885718399fSFrançois Tigeot 	int props_count = 0;
15895718399fSFrançois Tigeot 	int encoders_count = 0;
15905718399fSFrançois Tigeot 	int ret = 0;
15915718399fSFrançois Tigeot 	int copied = 0;
15925718399fSFrançois Tigeot 	int i;
15935718399fSFrançois Tigeot 	struct drm_mode_modeinfo u_mode;
15945718399fSFrançois Tigeot 	struct drm_mode_modeinfo __user *mode_ptr;
1595b5162e19SFrançois Tigeot 	uint32_t __user *prop_ptr;
1596b5162e19SFrançois Tigeot 	uint64_t __user *prop_values;
1597b5162e19SFrançois Tigeot 	uint32_t __user *encoder_ptr;
15985718399fSFrançois Tigeot 
15995718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1600b5162e19SFrançois Tigeot 		return -EINVAL;
16015718399fSFrançois Tigeot 
16025718399fSFrançois Tigeot 	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
16035718399fSFrançois Tigeot 
16045718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
16055718399fSFrançois Tigeot 
16064dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
16075718399fSFrançois Tigeot 
16085718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, out_resp->connector_id,
16095718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_CONNECTOR);
16105718399fSFrançois Tigeot 	if (!obj) {
1611*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
16125718399fSFrançois Tigeot 		goto out;
16135718399fSFrançois Tigeot 	}
16145718399fSFrançois Tigeot 	connector = obj_to_connector(obj);
16155718399fSFrançois Tigeot 
1616b5162e19SFrançois Tigeot 	props_count = connector->properties.count;
16175718399fSFrançois Tigeot 
16185718399fSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
16195718399fSFrançois Tigeot 		if (connector->encoder_ids[i] != 0) {
16205718399fSFrançois Tigeot 			encoders_count++;
16215718399fSFrançois Tigeot 		}
16225718399fSFrançois Tigeot 	}
16235718399fSFrançois Tigeot 
16245718399fSFrançois Tigeot 	if (out_resp->count_modes == 0) {
16255718399fSFrançois Tigeot 		connector->funcs->fill_modes(connector,
16265718399fSFrançois Tigeot 					     dev->mode_config.max_width,
16275718399fSFrançois Tigeot 					     dev->mode_config.max_height);
16285718399fSFrançois Tigeot 	}
16295718399fSFrançois Tigeot 
16305718399fSFrançois Tigeot 	/* delayed so we get modes regardless of pre-fill_modes state */
16315718399fSFrançois Tigeot 	list_for_each_entry(mode, &connector->modes, head)
1632*9edbd4a0SFrançois Tigeot 		if (drm_mode_expose_to_userspace(mode, file_priv))
16335718399fSFrançois Tigeot 			mode_count++;
16345718399fSFrançois Tigeot 
16355718399fSFrançois Tigeot 	out_resp->connector_id = connector->base.id;
16365718399fSFrançois Tigeot 	out_resp->connector_type = connector->connector_type;
16375718399fSFrançois Tigeot 	out_resp->connector_type_id = connector->connector_type_id;
16385718399fSFrançois Tigeot 	out_resp->mm_width = connector->display_info.width_mm;
16395718399fSFrançois Tigeot 	out_resp->mm_height = connector->display_info.height_mm;
16405718399fSFrançois Tigeot 	out_resp->subpixel = connector->display_info.subpixel_order;
16415718399fSFrançois Tigeot 	out_resp->connection = connector->status;
16425718399fSFrançois Tigeot 	if (connector->encoder)
16435718399fSFrançois Tigeot 		out_resp->encoder_id = connector->encoder->base.id;
16445718399fSFrançois Tigeot 	else
16455718399fSFrançois Tigeot 		out_resp->encoder_id = 0;
16465718399fSFrançois Tigeot 
16475718399fSFrançois Tigeot 	/*
16485718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
16495718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
16505718399fSFrançois Tigeot 	 */
16515718399fSFrançois Tigeot 	if ((out_resp->count_modes >= mode_count) && mode_count) {
16525718399fSFrançois Tigeot 		copied = 0;
1653b5162e19SFrançois Tigeot 		mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
16545718399fSFrançois Tigeot 		list_for_each_entry(mode, &connector->modes, head) {
1655*9edbd4a0SFrançois Tigeot 			if (!drm_mode_expose_to_userspace(mode, file_priv))
1656*9edbd4a0SFrançois Tigeot 				continue;
1657*9edbd4a0SFrançois Tigeot 
16585718399fSFrançois Tigeot 			drm_crtc_convert_to_umode(&u_mode, mode);
1659b5162e19SFrançois Tigeot 			if (copy_to_user(mode_ptr + copied,
1660b5162e19SFrançois Tigeot 					 &u_mode, sizeof(u_mode))) {
1661b5162e19SFrançois Tigeot 				ret = -EFAULT;
16625718399fSFrançois Tigeot 				goto out;
16635718399fSFrançois Tigeot 			}
16645718399fSFrançois Tigeot 			copied++;
16655718399fSFrançois Tigeot 		}
16665718399fSFrançois Tigeot 	}
16675718399fSFrançois Tigeot 	out_resp->count_modes = mode_count;
16685718399fSFrançois Tigeot 
16695718399fSFrançois Tigeot 	if ((out_resp->count_props >= props_count) && props_count) {
16705718399fSFrançois Tigeot 		copied = 0;
1671b5162e19SFrançois Tigeot 		prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr);
1672b5162e19SFrançois Tigeot 		prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr);
1673b5162e19SFrançois Tigeot 		for (i = 0; i < connector->properties.count; i++) {
1674b5162e19SFrançois Tigeot 			if (put_user(connector->properties.ids[i],
1675b5162e19SFrançois Tigeot 				     prop_ptr + copied)) {
1676b5162e19SFrançois Tigeot 				ret = -EFAULT;
16775718399fSFrançois Tigeot 				goto out;
16785718399fSFrançois Tigeot 			}
16795718399fSFrançois Tigeot 
1680b5162e19SFrançois Tigeot 			if (put_user(connector->properties.values[i],
1681b5162e19SFrançois Tigeot 				     prop_values + copied)) {
1682b5162e19SFrançois Tigeot 				ret = -EFAULT;
16835718399fSFrançois Tigeot 				goto out;
16845718399fSFrançois Tigeot 			}
16855718399fSFrançois Tigeot 			copied++;
16865718399fSFrançois Tigeot 		}
16875718399fSFrançois Tigeot 	}
16885718399fSFrançois Tigeot 	out_resp->count_props = props_count;
16895718399fSFrançois Tigeot 
16905718399fSFrançois Tigeot 	if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
16915718399fSFrançois Tigeot 		copied = 0;
1692b5162e19SFrançois Tigeot 		encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
16935718399fSFrançois Tigeot 		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
16945718399fSFrançois Tigeot 			if (connector->encoder_ids[i] != 0) {
1695b5162e19SFrançois Tigeot 				if (put_user(connector->encoder_ids[i],
1696b5162e19SFrançois Tigeot 					     encoder_ptr + copied)) {
1697b5162e19SFrançois Tigeot 					ret = -EFAULT;
16985718399fSFrançois Tigeot 					goto out;
16995718399fSFrançois Tigeot 				}
17005718399fSFrançois Tigeot 				copied++;
17015718399fSFrançois Tigeot 			}
17025718399fSFrançois Tigeot 		}
17035718399fSFrançois Tigeot 	}
17045718399fSFrançois Tigeot 	out_resp->count_encoders = encoders_count;
17055718399fSFrançois Tigeot 
17065718399fSFrançois Tigeot out:
17074dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
17084dbb207bSFrançois Tigeot 
17095718399fSFrançois Tigeot 	return ret;
17105718399fSFrançois Tigeot }
17115718399fSFrançois Tigeot 
17125718399fSFrançois Tigeot int drm_mode_getencoder(struct drm_device *dev, void *data,
17135718399fSFrançois Tigeot 			struct drm_file *file_priv)
17145718399fSFrançois Tigeot {
17155718399fSFrançois Tigeot 	struct drm_mode_get_encoder *enc_resp = data;
17165718399fSFrançois Tigeot 	struct drm_mode_object *obj;
17175718399fSFrançois Tigeot 	struct drm_encoder *encoder;
17185718399fSFrançois Tigeot 	int ret = 0;
17195718399fSFrançois Tigeot 
17205718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1721b5162e19SFrançois Tigeot 		return -EINVAL;
17225718399fSFrançois Tigeot 
17234dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
17245718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, enc_resp->encoder_id,
17255718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_ENCODER);
17265718399fSFrançois Tigeot 	if (!obj) {
1727*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
17285718399fSFrançois Tigeot 		goto out;
17295718399fSFrançois Tigeot 	}
17305718399fSFrançois Tigeot 	encoder = obj_to_encoder(obj);
17315718399fSFrançois Tigeot 
17325718399fSFrançois Tigeot 	if (encoder->crtc)
17335718399fSFrançois Tigeot 		enc_resp->crtc_id = encoder->crtc->base.id;
17345718399fSFrançois Tigeot 	else
17355718399fSFrançois Tigeot 		enc_resp->crtc_id = 0;
17365718399fSFrançois Tigeot 	enc_resp->encoder_type = encoder->encoder_type;
17375718399fSFrançois Tigeot 	enc_resp->encoder_id = encoder->base.id;
17385718399fSFrançois Tigeot 	enc_resp->possible_crtcs = encoder->possible_crtcs;
17395718399fSFrançois Tigeot 	enc_resp->possible_clones = encoder->possible_clones;
17405718399fSFrançois Tigeot 
17415718399fSFrançois Tigeot out:
17424dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
17435718399fSFrançois Tigeot 	return ret;
17445718399fSFrançois Tigeot }
17455718399fSFrançois Tigeot 
17465718399fSFrançois Tigeot /**
17475718399fSFrançois Tigeot  * drm_mode_getplane_res - get plane info
17485718399fSFrançois Tigeot  * @dev: DRM device
17495718399fSFrançois Tigeot  * @data: ioctl data
17505718399fSFrançois Tigeot  * @file_priv: DRM file info
17515718399fSFrançois Tigeot  *
17525718399fSFrançois Tigeot  * Return an plane count and set of IDs.
17535718399fSFrançois Tigeot  */
17545718399fSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data,
17555718399fSFrançois Tigeot 			    struct drm_file *file_priv)
17565718399fSFrançois Tigeot {
17575718399fSFrançois Tigeot 	struct drm_mode_get_plane_res *plane_resp = data;
17585718399fSFrançois Tigeot 	struct drm_mode_config *config;
17595718399fSFrançois Tigeot 	struct drm_plane *plane;
1760b5162e19SFrançois Tigeot 	uint32_t __user *plane_ptr;
17615718399fSFrançois Tigeot 	int copied = 0, ret = 0;
17625718399fSFrançois Tigeot 
17635718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1764b5162e19SFrançois Tigeot 		return -EINVAL;
17655718399fSFrançois Tigeot 
17664dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
17675718399fSFrançois Tigeot 	config = &dev->mode_config;
17685718399fSFrançois Tigeot 
17695718399fSFrançois Tigeot 	/*
17705718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
17715718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
17725718399fSFrançois Tigeot 	 */
17735718399fSFrançois Tigeot 	if (config->num_plane &&
17745718399fSFrançois Tigeot 	    (plane_resp->count_planes >= config->num_plane)) {
1775b5162e19SFrançois Tigeot 		plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
17765718399fSFrançois Tigeot 
17775718399fSFrançois Tigeot 		list_for_each_entry(plane, &config->plane_list, head) {
1778b5162e19SFrançois Tigeot 			if (put_user(plane->base.id, plane_ptr + copied)) {
1779b5162e19SFrançois Tigeot 				ret = -EFAULT;
17805718399fSFrançois Tigeot 				goto out;
17815718399fSFrançois Tigeot 			}
17825718399fSFrançois Tigeot 			copied++;
17835718399fSFrançois Tigeot 		}
17845718399fSFrançois Tigeot 	}
17855718399fSFrançois Tigeot 	plane_resp->count_planes = config->num_plane;
17865718399fSFrançois Tigeot 
17875718399fSFrançois Tigeot out:
17884dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
17895718399fSFrançois Tigeot 	return ret;
17905718399fSFrançois Tigeot }
17915718399fSFrançois Tigeot 
17925718399fSFrançois Tigeot /**
17935718399fSFrançois Tigeot  * drm_mode_getplane - get plane info
17945718399fSFrançois Tigeot  * @dev: DRM device
17955718399fSFrançois Tigeot  * @data: ioctl data
17965718399fSFrançois Tigeot  * @file_priv: DRM file info
17975718399fSFrançois Tigeot  *
17985718399fSFrançois Tigeot  * Return plane info, including formats supported, gamma size, any
17995718399fSFrançois Tigeot  * current fb, etc.
18005718399fSFrançois Tigeot  */
18015718399fSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data,
18025718399fSFrançois Tigeot 			struct drm_file *file_priv)
18035718399fSFrançois Tigeot {
18045718399fSFrançois Tigeot 	struct drm_mode_get_plane *plane_resp = data;
18055718399fSFrançois Tigeot 	struct drm_mode_object *obj;
18065718399fSFrançois Tigeot 	struct drm_plane *plane;
1807b5162e19SFrançois Tigeot 	uint32_t __user *format_ptr;
18085718399fSFrançois Tigeot 	int ret = 0;
18095718399fSFrançois Tigeot 
18105718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1811b5162e19SFrançois Tigeot 		return -EINVAL;
18125718399fSFrançois Tigeot 
18134dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
18145718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, plane_resp->plane_id,
18155718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_PLANE);
18165718399fSFrançois Tigeot 	if (!obj) {
1817b5162e19SFrançois Tigeot 		ret = -ENOENT;
18185718399fSFrançois Tigeot 		goto out;
18195718399fSFrançois Tigeot 	}
18205718399fSFrançois Tigeot 	plane = obj_to_plane(obj);
18215718399fSFrançois Tigeot 
18225718399fSFrançois Tigeot 	if (plane->crtc)
18235718399fSFrançois Tigeot 		plane_resp->crtc_id = plane->crtc->base.id;
18245718399fSFrançois Tigeot 	else
18255718399fSFrançois Tigeot 		plane_resp->crtc_id = 0;
18265718399fSFrançois Tigeot 
18275718399fSFrançois Tigeot 	if (plane->fb)
18285718399fSFrançois Tigeot 		plane_resp->fb_id = plane->fb->base.id;
18295718399fSFrançois Tigeot 	else
18305718399fSFrançois Tigeot 		plane_resp->fb_id = 0;
18315718399fSFrançois Tigeot 
18325718399fSFrançois Tigeot 	plane_resp->plane_id = plane->base.id;
18335718399fSFrançois Tigeot 	plane_resp->possible_crtcs = plane->possible_crtcs;
183406fede5aSFrançois Tigeot 	plane_resp->gamma_size = 0;
18355718399fSFrançois Tigeot 
18365718399fSFrançois Tigeot 	/*
18375718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
18385718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
18395718399fSFrançois Tigeot 	 */
18405718399fSFrançois Tigeot 	if (plane->format_count &&
18415718399fSFrançois Tigeot 	    (plane_resp->count_format_types >= plane->format_count)) {
1842b5162e19SFrançois Tigeot 		format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
1843b5162e19SFrançois Tigeot 		if (copy_to_user(format_ptr,
18445718399fSFrançois Tigeot 				 plane->format_types,
18455718399fSFrançois Tigeot 				 sizeof(uint32_t) * plane->format_count)) {
1846b5162e19SFrançois Tigeot 			ret = -EFAULT;
18475718399fSFrançois Tigeot 			goto out;
18485718399fSFrançois Tigeot 		}
18495718399fSFrançois Tigeot 	}
18505718399fSFrançois Tigeot 	plane_resp->count_format_types = plane->format_count;
18515718399fSFrançois Tigeot 
18525718399fSFrançois Tigeot out:
18534dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
18545718399fSFrançois Tigeot 	return ret;
18555718399fSFrançois Tigeot }
18565718399fSFrançois Tigeot 
18575718399fSFrançois Tigeot /**
18585718399fSFrançois Tigeot  * drm_mode_setplane - set up or tear down an plane
18595718399fSFrançois Tigeot  * @dev: DRM device
18605718399fSFrançois Tigeot  * @data: ioctl data*
18614dbb207bSFrançois Tigeot  * @file_priv: DRM file info
18625718399fSFrançois Tigeot  *
18635718399fSFrançois Tigeot  * Set plane info, including placement, fb, scaling, and other factors.
18645718399fSFrançois Tigeot  * Or pass a NULL fb to disable.
18655718399fSFrançois Tigeot  */
18665718399fSFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data,
18675718399fSFrançois Tigeot 			struct drm_file *file_priv)
18685718399fSFrançois Tigeot {
18695718399fSFrançois Tigeot 	struct drm_mode_set_plane *plane_req = data;
18705718399fSFrançois Tigeot 	struct drm_mode_object *obj;
18715718399fSFrançois Tigeot 	struct drm_plane *plane;
18725718399fSFrançois Tigeot 	struct drm_crtc *crtc;
18734dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
18745718399fSFrançois Tigeot 	int ret = 0;
18755718399fSFrançois Tigeot 	unsigned int fb_width, fb_height;
18765718399fSFrançois Tigeot 	int i;
18775718399fSFrançois Tigeot 
18785718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1879b5162e19SFrançois Tigeot 		return -EINVAL;
18805718399fSFrançois Tigeot 
18815718399fSFrançois Tigeot 	/*
18825718399fSFrançois Tigeot 	 * First, find the plane, crtc, and fb objects.  If not available,
18835718399fSFrançois Tigeot 	 * we don't bother to call the driver.
18845718399fSFrançois Tigeot 	 */
18855718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, plane_req->plane_id,
18865718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_PLANE);
18875718399fSFrançois Tigeot 	if (!obj) {
18885718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
18895718399fSFrançois Tigeot 			      plane_req->plane_id);
18904dbb207bSFrançois Tigeot 		return -ENOENT;
18915718399fSFrançois Tigeot 	}
18925718399fSFrançois Tigeot 	plane = obj_to_plane(obj);
18935718399fSFrançois Tigeot 
18945718399fSFrançois Tigeot 	/* No fb means shut it down */
18955718399fSFrançois Tigeot 	if (!plane_req->fb_id) {
18964dbb207bSFrançois Tigeot 		drm_modeset_lock_all(dev);
18974dbb207bSFrançois Tigeot 		old_fb = plane->fb;
18985718399fSFrançois Tigeot 		plane->funcs->disable_plane(plane);
18995718399fSFrançois Tigeot 		plane->crtc = NULL;
19005718399fSFrançois Tigeot 		plane->fb = NULL;
19014dbb207bSFrançois Tigeot 		drm_modeset_unlock_all(dev);
19025718399fSFrançois Tigeot 		goto out;
19035718399fSFrançois Tigeot 	}
19045718399fSFrançois Tigeot 
19055718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, plane_req->crtc_id,
19065718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_CRTC);
19075718399fSFrançois Tigeot 	if (!obj) {
19085718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown crtc ID %d\n",
19095718399fSFrançois Tigeot 			      plane_req->crtc_id);
1910b5162e19SFrançois Tigeot 		ret = -ENOENT;
19115718399fSFrançois Tigeot 		goto out;
19125718399fSFrançois Tigeot 	}
19135718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
19145718399fSFrançois Tigeot 
19154dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
19164dbb207bSFrançois Tigeot 	if (!fb) {
19175718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
19185718399fSFrançois Tigeot 			      plane_req->fb_id);
1919b5162e19SFrançois Tigeot 		ret = -ENOENT;
19205718399fSFrançois Tigeot 		goto out;
19215718399fSFrançois Tigeot 	}
19225718399fSFrançois Tigeot 
19235718399fSFrançois Tigeot 	/* Check whether this plane supports the fb pixel format. */
19245718399fSFrançois Tigeot 	for (i = 0; i < plane->format_count; i++)
19255718399fSFrançois Tigeot 		if (fb->pixel_format == plane->format_types[i])
19265718399fSFrançois Tigeot 			break;
19275718399fSFrançois Tigeot 	if (i == plane->format_count) {
192806fede5aSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid pixel format %s\n",
192906fede5aSFrançois Tigeot 			      drm_get_format_name(fb->pixel_format));
1930b5162e19SFrançois Tigeot 		ret = -EINVAL;
19315718399fSFrançois Tigeot 		goto out;
19325718399fSFrançois Tigeot 	}
19335718399fSFrançois Tigeot 
19345718399fSFrançois Tigeot 	fb_width = fb->width << 16;
19355718399fSFrançois Tigeot 	fb_height = fb->height << 16;
19365718399fSFrançois Tigeot 
19375718399fSFrançois Tigeot 	/* Make sure source coordinates are inside the fb. */
19385718399fSFrançois Tigeot 	if (plane_req->src_w > fb_width ||
19395718399fSFrançois Tigeot 	    plane_req->src_x > fb_width - plane_req->src_w ||
19405718399fSFrançois Tigeot 	    plane_req->src_h > fb_height ||
19415718399fSFrançois Tigeot 	    plane_req->src_y > fb_height - plane_req->src_h) {
19425718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid source coordinates "
19435718399fSFrançois Tigeot 			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
19445718399fSFrançois Tigeot 			      plane_req->src_w >> 16,
19455718399fSFrançois Tigeot 			      ((plane_req->src_w & 0xffff) * 15625) >> 10,
19465718399fSFrançois Tigeot 			      plane_req->src_h >> 16,
19475718399fSFrançois Tigeot 			      ((plane_req->src_h & 0xffff) * 15625) >> 10,
19485718399fSFrançois Tigeot 			      plane_req->src_x >> 16,
19495718399fSFrançois Tigeot 			      ((plane_req->src_x & 0xffff) * 15625) >> 10,
19505718399fSFrançois Tigeot 			      plane_req->src_y >> 16,
19515718399fSFrançois Tigeot 			      ((plane_req->src_y & 0xffff) * 15625) >> 10);
1952b5162e19SFrançois Tigeot 		ret = -ENOSPC;
19535718399fSFrançois Tigeot 		goto out;
19545718399fSFrançois Tigeot 	}
19555718399fSFrançois Tigeot 
19565718399fSFrançois Tigeot 	/* Give drivers some help against integer overflows */
19575718399fSFrançois Tigeot 	if (plane_req->crtc_w > INT_MAX ||
19585718399fSFrançois Tigeot 	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
19595718399fSFrançois Tigeot 	    plane_req->crtc_h > INT_MAX ||
19605718399fSFrançois Tigeot 	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
19615718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
19625718399fSFrançois Tigeot 			      plane_req->crtc_w, plane_req->crtc_h,
19635718399fSFrançois Tigeot 			      plane_req->crtc_x, plane_req->crtc_y);
1964b5162e19SFrançois Tigeot 		ret = -ERANGE;
19655718399fSFrançois Tigeot 		goto out;
19665718399fSFrançois Tigeot 	}
19675718399fSFrançois Tigeot 
19684dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
1969b5162e19SFrançois Tigeot 	ret = plane->funcs->update_plane(plane, crtc, fb,
19705718399fSFrançois Tigeot 					 plane_req->crtc_x, plane_req->crtc_y,
19715718399fSFrançois Tigeot 					 plane_req->crtc_w, plane_req->crtc_h,
19725718399fSFrançois Tigeot 					 plane_req->src_x, plane_req->src_y,
19735718399fSFrançois Tigeot 					 plane_req->src_w, plane_req->src_h);
19745718399fSFrançois Tigeot 	if (!ret) {
19754dbb207bSFrançois Tigeot 		old_fb = plane->fb;
19765718399fSFrançois Tigeot 		plane->crtc = crtc;
19775718399fSFrançois Tigeot 		plane->fb = fb;
19784dbb207bSFrançois Tigeot 		fb = NULL;
19795718399fSFrançois Tigeot 	}
19804dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
19815718399fSFrançois Tigeot 
19825718399fSFrançois Tigeot out:
19834dbb207bSFrançois Tigeot 	if (fb)
19844dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
19854dbb207bSFrançois Tigeot 	if (old_fb)
19864dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(old_fb);
19875718399fSFrançois Tigeot 
19885718399fSFrançois Tigeot 	return ret;
19895718399fSFrançois Tigeot }
19905718399fSFrançois Tigeot 
19915718399fSFrançois Tigeot /**
19924dbb207bSFrançois Tigeot  * drm_mode_set_config_internal - helper to call ->set_config
19934dbb207bSFrançois Tigeot  * @set: modeset config to set
19945718399fSFrançois Tigeot  *
19954dbb207bSFrançois Tigeot  * This is a little helper to wrap internal calls to the ->set_config driver
19964dbb207bSFrançois Tigeot  * interface. The only thing it adds is correct refcounting dance.
19974dbb207bSFrançois Tigeot  */
19984dbb207bSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set)
19994dbb207bSFrançois Tigeot {
20004dbb207bSFrançois Tigeot 	struct drm_crtc *crtc = set->crtc;
200106fede5aSFrançois Tigeot 	struct drm_framebuffer *fb;
200206fede5aSFrançois Tigeot 	struct drm_crtc *tmp;
20034dbb207bSFrançois Tigeot 	int ret;
20044dbb207bSFrançois Tigeot 
200506fede5aSFrançois Tigeot 	/*
200606fede5aSFrançois Tigeot 	 * NOTE: ->set_config can also disable other crtcs (if we steal all
200706fede5aSFrançois Tigeot 	 * connectors from it), hence we need to refcount the fbs across all
200806fede5aSFrançois Tigeot 	 * crtcs. Atomic modeset will have saner semantics ...
200906fede5aSFrançois Tigeot 	 */
201006fede5aSFrançois Tigeot 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
201106fede5aSFrançois Tigeot 		tmp->old_fb = tmp->fb;
201206fede5aSFrançois Tigeot 
20134dbb207bSFrançois Tigeot 	fb = set->fb;
20144dbb207bSFrançois Tigeot 
20154dbb207bSFrançois Tigeot 	ret = crtc->funcs->set_config(set);
20164dbb207bSFrançois Tigeot 	if (ret == 0) {
201706fede5aSFrançois Tigeot 		/* crtc->fb must be updated by ->set_config, enforces this. */
201806fede5aSFrançois Tigeot 		WARN_ON(fb != crtc->fb);
201906fede5aSFrançois Tigeot 	}
202006fede5aSFrançois Tigeot 
202106fede5aSFrançois Tigeot 	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
202206fede5aSFrançois Tigeot 		if (tmp->fb)
202306fede5aSFrançois Tigeot 			drm_framebuffer_reference(tmp->fb);
202406fede5aSFrançois Tigeot 		if (tmp->old_fb)
202506fede5aSFrançois Tigeot 			drm_framebuffer_unreference(tmp->old_fb);
20264dbb207bSFrançois Tigeot 	}
20274dbb207bSFrançois Tigeot 
20284dbb207bSFrançois Tigeot 	return ret;
20294dbb207bSFrançois Tigeot }
20304dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal);
20314dbb207bSFrançois Tigeot 
2032*9edbd4a0SFrançois Tigeot /*
2033*9edbd4a0SFrançois Tigeot  * Checks that the framebuffer is big enough for the CRTC viewport
2034*9edbd4a0SFrançois Tigeot  * (x, y, hdisplay, vdisplay)
2035*9edbd4a0SFrançois Tigeot  */
2036*9edbd4a0SFrançois Tigeot static int drm_crtc_check_viewport(const struct drm_crtc *crtc,
2037*9edbd4a0SFrançois Tigeot 				   int x, int y,
2038*9edbd4a0SFrançois Tigeot 				   const struct drm_display_mode *mode,
2039*9edbd4a0SFrançois Tigeot 				   const struct drm_framebuffer *fb)
2040*9edbd4a0SFrançois Tigeot 
2041*9edbd4a0SFrançois Tigeot {
2042*9edbd4a0SFrançois Tigeot 	int hdisplay, vdisplay;
2043*9edbd4a0SFrançois Tigeot 
2044*9edbd4a0SFrançois Tigeot 	hdisplay = mode->hdisplay;
2045*9edbd4a0SFrançois Tigeot 	vdisplay = mode->vdisplay;
2046*9edbd4a0SFrançois Tigeot 
2047*9edbd4a0SFrançois Tigeot 	if (drm_mode_is_stereo(mode)) {
2048*9edbd4a0SFrançois Tigeot 		struct drm_display_mode adjusted = *mode;
2049*9edbd4a0SFrançois Tigeot 
2050*9edbd4a0SFrançois Tigeot 		drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
2051*9edbd4a0SFrançois Tigeot 		hdisplay = adjusted.crtc_hdisplay;
2052*9edbd4a0SFrançois Tigeot 		vdisplay = adjusted.crtc_vdisplay;
2053*9edbd4a0SFrançois Tigeot 	}
2054*9edbd4a0SFrançois Tigeot 
2055*9edbd4a0SFrançois Tigeot 	if (crtc->invert_dimensions)
2056*9edbd4a0SFrançois Tigeot 		swap(hdisplay, vdisplay);
2057*9edbd4a0SFrançois Tigeot 
2058*9edbd4a0SFrançois Tigeot 	if (hdisplay > fb->width ||
2059*9edbd4a0SFrançois Tigeot 	    vdisplay > fb->height ||
2060*9edbd4a0SFrançois Tigeot 	    x > fb->width - hdisplay ||
2061*9edbd4a0SFrançois Tigeot 	    y > fb->height - vdisplay) {
2062*9edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
2063*9edbd4a0SFrançois Tigeot 			      fb->width, fb->height, hdisplay, vdisplay, x, y,
2064*9edbd4a0SFrançois Tigeot 			      crtc->invert_dimensions ? " (inverted)" : "");
2065*9edbd4a0SFrançois Tigeot 		return -ENOSPC;
2066*9edbd4a0SFrançois Tigeot 	}
2067*9edbd4a0SFrançois Tigeot 
2068*9edbd4a0SFrançois Tigeot 	return 0;
2069*9edbd4a0SFrançois Tigeot }
2070*9edbd4a0SFrançois Tigeot 
20714dbb207bSFrançois Tigeot /**
20724dbb207bSFrançois Tigeot  * drm_mode_setcrtc - set CRTC configuration
20734dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
20744dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
20754dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
20765718399fSFrançois Tigeot  *
20775718399fSFrançois Tigeot  * Build a new CRTC configuration based on user request.
20785718399fSFrançois Tigeot  *
20795718399fSFrançois Tigeot  * Called by the user via ioctl.
20805718399fSFrançois Tigeot  *
20815718399fSFrançois Tigeot  * RETURNS:
20825718399fSFrançois Tigeot  * Zero on success, errno on failure.
20835718399fSFrançois Tigeot  */
20845718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data,
20855718399fSFrançois Tigeot 		     struct drm_file *file_priv)
20865718399fSFrançois Tigeot {
20875718399fSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
20885718399fSFrançois Tigeot 	struct drm_mode_crtc *crtc_req = data;
20895718399fSFrançois Tigeot 	struct drm_mode_object *obj;
20905718399fSFrançois Tigeot 	struct drm_crtc *crtc;
20915718399fSFrançois Tigeot 	struct drm_connector **connector_set = NULL, *connector;
20925718399fSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
20935718399fSFrançois Tigeot 	struct drm_display_mode *mode = NULL;
20945718399fSFrançois Tigeot 	struct drm_mode_set set;
2095b5162e19SFrançois Tigeot 	uint32_t __user *set_connectors_ptr;
2096b5162e19SFrançois Tigeot 	int ret;
20975718399fSFrançois Tigeot 	int i;
20985718399fSFrançois Tigeot 
20995718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2100b5162e19SFrançois Tigeot 		return -EINVAL;
21015718399fSFrançois Tigeot 
21025718399fSFrançois Tigeot 	/* For some reason crtc x/y offsets are signed internally. */
21035718399fSFrançois Tigeot 	if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
2104b5162e19SFrançois Tigeot 		return -ERANGE;
21055718399fSFrançois Tigeot 
21064dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
21075718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, crtc_req->crtc_id,
21085718399fSFrançois Tigeot 				   DRM_MODE_OBJECT_CRTC);
21095718399fSFrançois Tigeot 	if (!obj) {
21105718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
2111*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
21125718399fSFrançois Tigeot 		goto out;
21135718399fSFrançois Tigeot 	}
21145718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
21155718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
21165718399fSFrançois Tigeot 
21175718399fSFrançois Tigeot 	if (crtc_req->mode_valid) {
21185718399fSFrançois Tigeot 		/* If we have a mode we need a framebuffer. */
21195718399fSFrançois Tigeot 		/* If we pass -1, set the mode with the currently bound fb */
21205718399fSFrançois Tigeot 		if (crtc_req->fb_id == -1) {
21215718399fSFrançois Tigeot 			if (!crtc->fb) {
21225718399fSFrançois Tigeot 				DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
21235718399fSFrançois Tigeot 				ret = -EINVAL;
21245718399fSFrançois Tigeot 				goto out;
21255718399fSFrançois Tigeot 			}
21265718399fSFrançois Tigeot 			fb = crtc->fb;
21274dbb207bSFrançois Tigeot 			/* Make refcounting symmetric with the lookup path. */
21284dbb207bSFrançois Tigeot 			drm_framebuffer_reference(fb);
21295718399fSFrançois Tigeot 		} else {
21304dbb207bSFrançois Tigeot 			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
21314dbb207bSFrançois Tigeot 			if (!fb) {
21325718399fSFrançois Tigeot 				DRM_DEBUG_KMS("Unknown FB ID%d\n",
21335718399fSFrançois Tigeot 						crtc_req->fb_id);
2134*9edbd4a0SFrançois Tigeot 				ret = -ENOENT;
21355718399fSFrançois Tigeot 				goto out;
21365718399fSFrançois Tigeot 			}
21375718399fSFrançois Tigeot 		}
21385718399fSFrançois Tigeot 
21395718399fSFrançois Tigeot 		mode = drm_mode_create(dev);
21405718399fSFrançois Tigeot 		if (!mode) {
2141b5162e19SFrançois Tigeot 			ret = -ENOMEM;
21425718399fSFrançois Tigeot 			goto out;
21435718399fSFrançois Tigeot 		}
21445718399fSFrançois Tigeot 
21455718399fSFrançois Tigeot 		ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
21465718399fSFrançois Tigeot 		if (ret) {
21475718399fSFrançois Tigeot 			DRM_DEBUG_KMS("Invalid mode\n");
21485718399fSFrançois Tigeot 			goto out;
21495718399fSFrançois Tigeot 		}
21505718399fSFrançois Tigeot 
21515718399fSFrançois Tigeot 		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
21525718399fSFrançois Tigeot 
2153*9edbd4a0SFrançois Tigeot 		ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
2154*9edbd4a0SFrançois Tigeot 					      mode, fb);
2155*9edbd4a0SFrançois Tigeot 		if (ret)
21565718399fSFrançois Tigeot 			goto out;
2157*9edbd4a0SFrançois Tigeot 
21585718399fSFrançois Tigeot 	}
21595718399fSFrançois Tigeot 
21605718399fSFrançois Tigeot 	if (crtc_req->count_connectors == 0 && mode) {
21615718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
2162b5162e19SFrançois Tigeot 		ret = -EINVAL;
21635718399fSFrançois Tigeot 		goto out;
21645718399fSFrançois Tigeot 	}
21655718399fSFrançois Tigeot 
21665718399fSFrançois Tigeot 	if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
21675718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
21685718399fSFrançois Tigeot 			  crtc_req->count_connectors);
2169b5162e19SFrançois Tigeot 		ret = -EINVAL;
21705718399fSFrançois Tigeot 		goto out;
21715718399fSFrançois Tigeot 	}
21725718399fSFrançois Tigeot 
21735718399fSFrançois Tigeot 	if (crtc_req->count_connectors > 0) {
21745718399fSFrançois Tigeot 		u32 out_id;
21755718399fSFrançois Tigeot 
21765718399fSFrançois Tigeot 		/* Avoid unbounded kernel memory allocation */
21775718399fSFrançois Tigeot 		if (crtc_req->count_connectors > config->num_connector) {
2178b5162e19SFrançois Tigeot 			ret = -EINVAL;
21795718399fSFrançois Tigeot 			goto out;
21805718399fSFrançois Tigeot 		}
21815718399fSFrançois Tigeot 
21825718399fSFrançois Tigeot 		connector_set = kmalloc(crtc_req->count_connectors *
21834dbb207bSFrançois Tigeot 					sizeof(struct drm_connector *),
21844dbb207bSFrançois Tigeot 					M_DRM, M_WAITOK);
2185b5162e19SFrançois Tigeot 		if (!connector_set) {
2186b5162e19SFrançois Tigeot 			ret = -ENOMEM;
2187b5162e19SFrançois Tigeot 			goto out;
2188b5162e19SFrançois Tigeot 		}
21895718399fSFrançois Tigeot 
21905718399fSFrançois Tigeot 		for (i = 0; i < crtc_req->count_connectors; i++) {
2191b5162e19SFrançois Tigeot 			set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
2192b5162e19SFrançois Tigeot 			if (get_user(out_id, &set_connectors_ptr[i])) {
2193b5162e19SFrançois Tigeot 				ret = -EFAULT;
21945718399fSFrançois Tigeot 				goto out;
21955718399fSFrançois Tigeot 			}
21965718399fSFrançois Tigeot 
21975718399fSFrançois Tigeot 			obj = drm_mode_object_find(dev, out_id,
21985718399fSFrançois Tigeot 						   DRM_MODE_OBJECT_CONNECTOR);
21995718399fSFrançois Tigeot 			if (!obj) {
22005718399fSFrançois Tigeot 				DRM_DEBUG_KMS("Connector id %d unknown\n",
22015718399fSFrançois Tigeot 						out_id);
2202*9edbd4a0SFrançois Tigeot 				ret = -ENOENT;
22035718399fSFrançois Tigeot 				goto out;
22045718399fSFrançois Tigeot 			}
22055718399fSFrançois Tigeot 			connector = obj_to_connector(obj);
22065718399fSFrançois Tigeot 			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
22075718399fSFrançois Tigeot 					connector->base.id,
22085718399fSFrançois Tigeot 					drm_get_connector_name(connector));
22095718399fSFrançois Tigeot 
22105718399fSFrançois Tigeot 			connector_set[i] = connector;
22115718399fSFrançois Tigeot 		}
22125718399fSFrançois Tigeot 	}
22135718399fSFrançois Tigeot 
22145718399fSFrançois Tigeot 	set.crtc = crtc;
22155718399fSFrançois Tigeot 	set.x = crtc_req->x;
22165718399fSFrançois Tigeot 	set.y = crtc_req->y;
22175718399fSFrançois Tigeot 	set.mode = mode;
22185718399fSFrançois Tigeot 	set.connectors = connector_set;
22195718399fSFrançois Tigeot 	set.num_connectors = crtc_req->count_connectors;
22205718399fSFrançois Tigeot 	set.fb = fb;
22214dbb207bSFrançois Tigeot 	ret = drm_mode_set_config_internal(&set);
22225718399fSFrançois Tigeot 
22235718399fSFrançois Tigeot out:
22244dbb207bSFrançois Tigeot 	if (fb)
22254dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
22264dbb207bSFrançois Tigeot 
22274dbb207bSFrançois Tigeot 	kfree(connector_set);
22285718399fSFrançois Tigeot 	drm_mode_destroy(dev, mode);
22294dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
22305718399fSFrançois Tigeot 	return ret;
22315718399fSFrançois Tigeot }
22325718399fSFrançois Tigeot 
223306fede5aSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev,
223406fede5aSFrançois Tigeot 				  struct drm_mode_cursor2 *req,
223506fede5aSFrançois Tigeot 				  struct drm_file *file_priv)
22365718399fSFrançois Tigeot {
22375718399fSFrançois Tigeot 	struct drm_mode_object *obj;
22385718399fSFrançois Tigeot 	struct drm_crtc *crtc;
22395718399fSFrançois Tigeot 	int ret = 0;
22405718399fSFrançois Tigeot 
22415718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2242b5162e19SFrançois Tigeot 		return -EINVAL;
22435718399fSFrançois Tigeot 
2244b5162e19SFrançois Tigeot 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
2245b5162e19SFrançois Tigeot 		return -EINVAL;
22465718399fSFrançois Tigeot 
22475718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
22485718399fSFrançois Tigeot 	if (!obj) {
22495718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
2250*9edbd4a0SFrançois Tigeot 		return -ENOENT;
22515718399fSFrançois Tigeot 	}
22525718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
22535718399fSFrançois Tigeot 
22544dbb207bSFrançois Tigeot 	mutex_lock(&crtc->mutex);
22555718399fSFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_BO) {
225606fede5aSFrançois Tigeot 		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
2257b5162e19SFrançois Tigeot 			ret = -ENXIO;
22585718399fSFrançois Tigeot 			goto out;
22595718399fSFrançois Tigeot 		}
22605718399fSFrançois Tigeot 		/* Turns off the cursor if handle is 0 */
226106fede5aSFrançois Tigeot 		if (crtc->funcs->cursor_set2)
226206fede5aSFrançois Tigeot 			ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
226306fede5aSFrançois Tigeot 						      req->width, req->height, req->hot_x, req->hot_y);
226406fede5aSFrançois Tigeot 		else
2265b5162e19SFrançois Tigeot 			ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
22665718399fSFrançois Tigeot 						      req->width, req->height);
22675718399fSFrançois Tigeot 	}
22685718399fSFrançois Tigeot 
22695718399fSFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_MOVE) {
22705718399fSFrançois Tigeot 		if (crtc->funcs->cursor_move) {
22715718399fSFrançois Tigeot 			ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
22725718399fSFrançois Tigeot 		} else {
2273b5162e19SFrançois Tigeot 			ret = -EFAULT;
22745718399fSFrançois Tigeot 			goto out;
22755718399fSFrançois Tigeot 		}
22765718399fSFrançois Tigeot 	}
22775718399fSFrançois Tigeot out:
22784dbb207bSFrançois Tigeot 	mutex_unlock(&crtc->mutex);
22794dbb207bSFrançois Tigeot 
22805718399fSFrançois Tigeot 	return ret;
228106fede5aSFrançois Tigeot 
228206fede5aSFrançois Tigeot }
228306fede5aSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev,
228406fede5aSFrançois Tigeot 			void *data, struct drm_file *file_priv)
228506fede5aSFrançois Tigeot {
228606fede5aSFrançois Tigeot 	struct drm_mode_cursor *req = data;
228706fede5aSFrançois Tigeot 	struct drm_mode_cursor2 new_req;
228806fede5aSFrançois Tigeot 
228906fede5aSFrançois Tigeot 	memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
229006fede5aSFrançois Tigeot 	new_req.hot_x = new_req.hot_y = 0;
229106fede5aSFrançois Tigeot 
229206fede5aSFrançois Tigeot 	return drm_mode_cursor_common(dev, &new_req, file_priv);
229306fede5aSFrançois Tigeot }
229406fede5aSFrançois Tigeot 
229506fede5aSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev,
229606fede5aSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
229706fede5aSFrançois Tigeot {
229806fede5aSFrançois Tigeot 	struct drm_mode_cursor2 *req = data;
229906fede5aSFrançois Tigeot 	return drm_mode_cursor_common(dev, req, file_priv);
23005718399fSFrançois Tigeot }
23015718399fSFrançois Tigeot 
23025718399fSFrançois Tigeot /* Original addfb only supported RGB formats, so figure out which one */
23035718399fSFrançois Tigeot uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
23045718399fSFrançois Tigeot {
23055718399fSFrançois Tigeot 	uint32_t fmt;
23065718399fSFrançois Tigeot 
23075718399fSFrançois Tigeot 	switch (bpp) {
23085718399fSFrançois Tigeot 	case 8:
23094dbb207bSFrançois Tigeot 		fmt = DRM_FORMAT_C8;
23105718399fSFrançois Tigeot 		break;
23115718399fSFrançois Tigeot 	case 16:
23125718399fSFrançois Tigeot 		if (depth == 15)
23135718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB1555;
23145718399fSFrançois Tigeot 		else
23155718399fSFrançois Tigeot 			fmt = DRM_FORMAT_RGB565;
23165718399fSFrançois Tigeot 		break;
23175718399fSFrançois Tigeot 	case 24:
23185718399fSFrançois Tigeot 		fmt = DRM_FORMAT_RGB888;
23195718399fSFrançois Tigeot 		break;
23205718399fSFrançois Tigeot 	case 32:
23215718399fSFrançois Tigeot 		if (depth == 24)
23225718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB8888;
23235718399fSFrançois Tigeot 		else if (depth == 30)
23245718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB2101010;
23255718399fSFrançois Tigeot 		else
23265718399fSFrançois Tigeot 			fmt = DRM_FORMAT_ARGB8888;
23275718399fSFrançois Tigeot 		break;
23285718399fSFrançois Tigeot 	default:
2329b5162e19SFrançois Tigeot 		DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
23305718399fSFrançois Tigeot 		fmt = DRM_FORMAT_XRGB8888;
23315718399fSFrançois Tigeot 		break;
23325718399fSFrançois Tigeot 	}
23335718399fSFrançois Tigeot 
23345718399fSFrançois Tigeot 	return fmt;
23355718399fSFrançois Tigeot }
2336b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_legacy_fb_format);
23375718399fSFrançois Tigeot 
23385718399fSFrançois Tigeot /**
23395718399fSFrançois Tigeot  * drm_mode_addfb - add an FB to the graphics configuration
23404dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
23414dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
23424dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
23435718399fSFrançois Tigeot  *
23445718399fSFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request.
23455718399fSFrançois Tigeot  *
23465718399fSFrançois Tigeot  * Called by the user via ioctl.
23475718399fSFrançois Tigeot  *
23485718399fSFrançois Tigeot  * RETURNS:
23495718399fSFrançois Tigeot  * Zero on success, errno on failure.
23505718399fSFrançois Tigeot  */
23515718399fSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev,
23525718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
23535718399fSFrançois Tigeot {
23545718399fSFrançois Tigeot 	struct drm_mode_fb_cmd *or = data;
23555718399fSFrançois Tigeot 	struct drm_mode_fb_cmd2 r = {};
23565718399fSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
23575718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
23585718399fSFrançois Tigeot 	int ret = 0;
23595718399fSFrançois Tigeot 
23605718399fSFrançois Tigeot 	/* Use new struct with format internally */
23615718399fSFrançois Tigeot 	r.fb_id = or->fb_id;
23625718399fSFrançois Tigeot 	r.width = or->width;
23635718399fSFrançois Tigeot 	r.height = or->height;
23645718399fSFrançois Tigeot 	r.pitches[0] = or->pitch;
23655718399fSFrançois Tigeot 	r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
23665718399fSFrançois Tigeot 	r.handles[0] = or->handle;
23675718399fSFrançois Tigeot 
23685718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2369b5162e19SFrançois Tigeot 		return -EINVAL;
23705718399fSFrançois Tigeot 
23715718399fSFrançois Tigeot 	if ((config->min_width > r.width) || (r.width > config->max_width))
2372b5162e19SFrançois Tigeot 		return -EINVAL;
2373b5162e19SFrançois Tigeot 
23745718399fSFrançois Tigeot 	if ((config->min_height > r.height) || (r.height > config->max_height))
2375b5162e19SFrançois Tigeot 		return -EINVAL;
23765718399fSFrançois Tigeot 
237760fc7eecSFrançois Tigeot 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);
237860fc7eecSFrançois Tigeot 	if (IS_ERR(fb)) {
237960fc7eecSFrançois Tigeot 		DRM_DEBUG_KMS("could not create framebuffer\n");
23804dbb207bSFrançois Tigeot 		return PTR_ERR(fb);
23815718399fSFrançois Tigeot 	}
23825718399fSFrançois Tigeot 
23834dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
23845718399fSFrançois Tigeot 	or->fb_id = fb->base.id;
23855718399fSFrançois Tigeot 	list_add(&fb->filp_head, &file_priv->fbs);
23865718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
23874dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
23885718399fSFrançois Tigeot 
23895718399fSFrançois Tigeot 	return ret;
23905718399fSFrançois Tigeot }
23915718399fSFrançois Tigeot 
2392b5162e19SFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r)
23935718399fSFrançois Tigeot {
23945718399fSFrançois Tigeot 	uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
23955718399fSFrançois Tigeot 
23965718399fSFrançois Tigeot 	switch (format) {
23975718399fSFrançois Tigeot 	case DRM_FORMAT_C8:
23985718399fSFrançois Tigeot 	case DRM_FORMAT_RGB332:
23995718399fSFrançois Tigeot 	case DRM_FORMAT_BGR233:
24005718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB4444:
24015718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR4444:
24025718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX4444:
24035718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX4444:
24045718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB4444:
24055718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR4444:
24065718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA4444:
24075718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA4444:
24085718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB1555:
24095718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR1555:
24105718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX5551:
24115718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX5551:
24125718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB1555:
24135718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR1555:
24145718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA5551:
24155718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA5551:
24165718399fSFrançois Tigeot 	case DRM_FORMAT_RGB565:
24175718399fSFrançois Tigeot 	case DRM_FORMAT_BGR565:
24185718399fSFrançois Tigeot 	case DRM_FORMAT_RGB888:
24195718399fSFrançois Tigeot 	case DRM_FORMAT_BGR888:
24205718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB8888:
24215718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR8888:
24225718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX8888:
24235718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX8888:
24245718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB8888:
24255718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR8888:
24265718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA8888:
24275718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA8888:
24285718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB2101010:
24295718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR2101010:
24305718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX1010102:
24315718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX1010102:
24325718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB2101010:
24335718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR2101010:
24345718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA1010102:
24355718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA1010102:
24365718399fSFrançois Tigeot 	case DRM_FORMAT_YUYV:
24375718399fSFrançois Tigeot 	case DRM_FORMAT_YVYU:
24385718399fSFrançois Tigeot 	case DRM_FORMAT_UYVY:
24395718399fSFrançois Tigeot 	case DRM_FORMAT_VYUY:
24405718399fSFrançois Tigeot 	case DRM_FORMAT_AYUV:
24415718399fSFrançois Tigeot 	case DRM_FORMAT_NV12:
24425718399fSFrançois Tigeot 	case DRM_FORMAT_NV21:
24435718399fSFrançois Tigeot 	case DRM_FORMAT_NV16:
24445718399fSFrançois Tigeot 	case DRM_FORMAT_NV61:
2445b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
2446b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
24475718399fSFrançois Tigeot 	case DRM_FORMAT_YUV410:
24485718399fSFrançois Tigeot 	case DRM_FORMAT_YVU410:
24495718399fSFrançois Tigeot 	case DRM_FORMAT_YUV411:
24505718399fSFrançois Tigeot 	case DRM_FORMAT_YVU411:
24515718399fSFrançois Tigeot 	case DRM_FORMAT_YUV420:
24525718399fSFrançois Tigeot 	case DRM_FORMAT_YVU420:
24535718399fSFrançois Tigeot 	case DRM_FORMAT_YUV422:
24545718399fSFrançois Tigeot 	case DRM_FORMAT_YVU422:
24555718399fSFrançois Tigeot 	case DRM_FORMAT_YUV444:
24565718399fSFrançois Tigeot 	case DRM_FORMAT_YVU444:
24575718399fSFrançois Tigeot 		return 0;
24585718399fSFrançois Tigeot 	default:
2459*9edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("invalid pixel format %s\n",
2460*9edbd4a0SFrançois Tigeot 			      drm_get_format_name(r->pixel_format));
2461b5162e19SFrançois Tigeot 		return -EINVAL;
24625718399fSFrançois Tigeot 	}
24635718399fSFrançois Tigeot }
24645718399fSFrançois Tigeot 
2465b5162e19SFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
2466b5162e19SFrançois Tigeot {
2467b5162e19SFrançois Tigeot 	int ret, hsub, vsub, num_planes, i;
2468b5162e19SFrançois Tigeot 
2469b5162e19SFrançois Tigeot 	ret = format_check(r);
2470b5162e19SFrançois Tigeot 	if (ret) {
247106fede5aSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer format %s\n",
247206fede5aSFrançois Tigeot 			      drm_get_format_name(r->pixel_format));
2473b5162e19SFrançois Tigeot 		return ret;
2474b5162e19SFrançois Tigeot 	}
2475b5162e19SFrançois Tigeot 
2476b5162e19SFrançois Tigeot 	hsub = drm_format_horz_chroma_subsampling(r->pixel_format);
2477b5162e19SFrançois Tigeot 	vsub = drm_format_vert_chroma_subsampling(r->pixel_format);
2478b5162e19SFrançois Tigeot 	num_planes = drm_format_num_planes(r->pixel_format);
2479b5162e19SFrançois Tigeot 
2480b5162e19SFrançois Tigeot 	if (r->width == 0 || r->width % hsub) {
2481b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height);
2482b5162e19SFrançois Tigeot 		return -EINVAL;
2483b5162e19SFrançois Tigeot 	}
2484b5162e19SFrançois Tigeot 
2485b5162e19SFrançois Tigeot 	if (r->height == 0 || r->height % vsub) {
2486b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
2487b5162e19SFrançois Tigeot 		return -EINVAL;
2488b5162e19SFrançois Tigeot 	}
2489b5162e19SFrançois Tigeot 
2490b5162e19SFrançois Tigeot 	for (i = 0; i < num_planes; i++) {
2491b5162e19SFrançois Tigeot 		unsigned int width = r->width / (i != 0 ? hsub : 1);
2492b5162e19SFrançois Tigeot 		unsigned int height = r->height / (i != 0 ? vsub : 1);
2493b5162e19SFrançois Tigeot 		unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
2494b5162e19SFrançois Tigeot 
2495b5162e19SFrançois Tigeot 		if (!r->handles[i]) {
2496b5162e19SFrançois Tigeot 			DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
2497b5162e19SFrançois Tigeot 			return -EINVAL;
2498b5162e19SFrançois Tigeot 		}
2499b5162e19SFrançois Tigeot 
2500b5162e19SFrançois Tigeot 		if ((uint64_t) width * cpp > UINT_MAX)
2501b5162e19SFrançois Tigeot 			return -ERANGE;
2502b5162e19SFrançois Tigeot 
2503b5162e19SFrançois Tigeot 		if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
2504b5162e19SFrançois Tigeot 			return -ERANGE;
2505b5162e19SFrançois Tigeot 
2506b5162e19SFrançois Tigeot 		if (r->pitches[i] < width * cpp) {
2507b5162e19SFrançois Tigeot 			DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
2508b5162e19SFrançois Tigeot 			return -EINVAL;
2509b5162e19SFrançois Tigeot 		}
2510b5162e19SFrançois Tigeot 	}
2511b5162e19SFrançois Tigeot 
2512b5162e19SFrançois Tigeot 	return 0;
2513b5162e19SFrançois Tigeot }
2514b5162e19SFrançois Tigeot 
25155718399fSFrançois Tigeot /**
25165718399fSFrançois Tigeot  * drm_mode_addfb2 - add an FB to the graphics configuration
25174dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
25184dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
25194dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
25205718399fSFrançois Tigeot  *
25215718399fSFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request with format.
25225718399fSFrançois Tigeot  *
25235718399fSFrançois Tigeot  * Called by the user via ioctl.
25245718399fSFrançois Tigeot  *
25255718399fSFrançois Tigeot  * RETURNS:
25265718399fSFrançois Tigeot  * Zero on success, errno on failure.
25275718399fSFrançois Tigeot  */
25285718399fSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev,
25295718399fSFrançois Tigeot 		    void *data, struct drm_file *file_priv)
25305718399fSFrançois Tigeot {
25315718399fSFrançois Tigeot 	struct drm_mode_fb_cmd2 *r = data;
25325718399fSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
25335718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
2534b5162e19SFrançois Tigeot 	int ret;
25355718399fSFrançois Tigeot 
25365718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2537b5162e19SFrançois Tigeot 		return -EINVAL;
2538b5162e19SFrançois Tigeot 
2539b5162e19SFrançois Tigeot 	if (r->flags & ~DRM_MODE_FB_INTERLACED) {
2540b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
2541b5162e19SFrançois Tigeot 		return -EINVAL;
2542b5162e19SFrançois Tigeot 	}
25435718399fSFrançois Tigeot 
25445718399fSFrançois Tigeot 	if ((config->min_width > r->width) || (r->width > config->max_width)) {
2545b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
25465718399fSFrançois Tigeot 			  r->width, config->min_width, config->max_width);
2547b5162e19SFrançois Tigeot 		return -EINVAL;
25485718399fSFrançois Tigeot 	}
25495718399fSFrançois Tigeot 	if ((config->min_height > r->height) || (r->height > config->max_height)) {
2550b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
25515718399fSFrançois Tigeot 			  r->height, config->min_height, config->max_height);
2552b5162e19SFrançois Tigeot 		return -EINVAL;
25535718399fSFrançois Tigeot 	}
25545718399fSFrançois Tigeot 
2555b5162e19SFrançois Tigeot 	ret = framebuffer_check(r);
2556b5162e19SFrançois Tigeot 	if (ret)
25575718399fSFrançois Tigeot 		return ret;
25585718399fSFrançois Tigeot 
255960fc7eecSFrançois Tigeot 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
256060fc7eecSFrançois Tigeot 	if (IS_ERR(fb)) {
256160fc7eecSFrançois Tigeot 		DRM_DEBUG_KMS("could not create framebuffer\n");
25624dbb207bSFrançois Tigeot 		return PTR_ERR(fb);
25635718399fSFrançois Tigeot 	}
25645718399fSFrançois Tigeot 
25654dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
25665718399fSFrançois Tigeot 	r->fb_id = fb->base.id;
25675718399fSFrançois Tigeot 	list_add(&fb->filp_head, &file_priv->fbs);
25685718399fSFrançois Tigeot 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
25694dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
25705718399fSFrançois Tigeot 
25714dbb207bSFrançois Tigeot 
2572b5162e19SFrançois Tigeot 	return ret;
25735718399fSFrançois Tigeot }
25745718399fSFrançois Tigeot 
25755718399fSFrançois Tigeot /**
25765718399fSFrançois Tigeot  * drm_mode_rmfb - remove an FB from the configuration
25774dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
25784dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
25794dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
25805718399fSFrançois Tigeot  *
25815718399fSFrançois Tigeot  * Remove the FB specified by the user.
25825718399fSFrançois Tigeot  *
25835718399fSFrançois Tigeot  * Called by the user via ioctl.
25845718399fSFrançois Tigeot  *
25855718399fSFrançois Tigeot  * RETURNS:
25865718399fSFrançois Tigeot  * Zero on success, errno on failure.
25875718399fSFrançois Tigeot  */
25885718399fSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev,
25895718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
25905718399fSFrançois Tigeot {
25915718399fSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
25925718399fSFrançois Tigeot 	struct drm_framebuffer *fbl = NULL;
25935718399fSFrançois Tigeot 	uint32_t *id = data;
25945718399fSFrançois Tigeot 	int found = 0;
25955718399fSFrançois Tigeot 
25965718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2597b5162e19SFrançois Tigeot 		return -EINVAL;
25985718399fSFrançois Tigeot 
25994dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
26004dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
26014dbb207bSFrançois Tigeot 	fb = __drm_framebuffer_lookup(dev, *id);
26024dbb207bSFrançois Tigeot 	if (!fb)
26034dbb207bSFrançois Tigeot 		goto fail_lookup;
26045718399fSFrançois Tigeot 
26055718399fSFrançois Tigeot 	list_for_each_entry(fbl, &file_priv->fbs, filp_head)
26065718399fSFrançois Tigeot 		if (fb == fbl)
26075718399fSFrançois Tigeot 			found = 1;
26084dbb207bSFrançois Tigeot 	if (!found)
26094dbb207bSFrançois Tigeot 		goto fail_lookup;
26105718399fSFrançois Tigeot 
26114dbb207bSFrançois Tigeot 	/* Mark fb as reaped, we still have a ref from fpriv->fbs. */
26124dbb207bSFrançois Tigeot 	__drm_framebuffer_unregister(dev, fb);
26134dbb207bSFrançois Tigeot 
26144dbb207bSFrançois Tigeot 	list_del_init(&fb->filp_head);
26154dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
26164dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
26175718399fSFrançois Tigeot 
2618b5162e19SFrançois Tigeot 	drm_framebuffer_remove(fb);
26195718399fSFrançois Tigeot 
26204dbb207bSFrançois Tigeot 	return 0;
26214dbb207bSFrançois Tigeot 
26224dbb207bSFrançois Tigeot fail_lookup:
26234dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
26244dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
26254dbb207bSFrançois Tigeot 
2626*9edbd4a0SFrançois Tigeot 	return -ENOENT;
26275718399fSFrançois Tigeot }
26285718399fSFrançois Tigeot 
26295718399fSFrançois Tigeot /**
26305718399fSFrançois Tigeot  * drm_mode_getfb - get FB info
26314dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
26324dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
26334dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
26345718399fSFrançois Tigeot  *
26355718399fSFrançois Tigeot  * Lookup the FB given its ID and return info about it.
26365718399fSFrançois Tigeot  *
26375718399fSFrançois Tigeot  * Called by the user via ioctl.
26385718399fSFrançois Tigeot  *
26395718399fSFrançois Tigeot  * RETURNS:
26405718399fSFrançois Tigeot  * Zero on success, errno on failure.
26415718399fSFrançois Tigeot  */
26425718399fSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev,
26435718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
26445718399fSFrançois Tigeot {
26455718399fSFrançois Tigeot 	struct drm_mode_fb_cmd *r = data;
26465718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
26474dbb207bSFrançois Tigeot 	int ret;
26485718399fSFrançois Tigeot 
26495718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2650b5162e19SFrançois Tigeot 		return -EINVAL;
26515718399fSFrançois Tigeot 
26524dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
26534dbb207bSFrançois Tigeot 	if (!fb)
2654*9edbd4a0SFrançois Tigeot 		return -ENOENT;
26555718399fSFrançois Tigeot 
26565718399fSFrançois Tigeot 	r->height = fb->height;
26575718399fSFrançois Tigeot 	r->width = fb->width;
26585718399fSFrançois Tigeot 	r->depth = fb->depth;
26595718399fSFrançois Tigeot 	r->bpp = fb->bits_per_pixel;
26605718399fSFrançois Tigeot 	r->pitch = fb->pitches[0];
26614dbb207bSFrançois Tigeot 	if (fb->funcs->create_handle)
26624dbb207bSFrançois Tigeot 		ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
26634dbb207bSFrançois Tigeot 	else
26644dbb207bSFrançois Tigeot 		ret = -ENODEV;
26655718399fSFrançois Tigeot 
26664dbb207bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
26674dbb207bSFrançois Tigeot 
26685718399fSFrançois Tigeot 	return ret;
26695718399fSFrançois Tigeot }
26705718399fSFrançois Tigeot 
26715718399fSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
26725718399fSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
26735718399fSFrançois Tigeot {
26745718399fSFrançois Tigeot 	struct drm_clip_rect __user *clips_ptr;
26755718399fSFrançois Tigeot 	struct drm_clip_rect *clips = NULL;
26765718399fSFrançois Tigeot 	struct drm_mode_fb_dirty_cmd *r = data;
26775718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
26785718399fSFrançois Tigeot 	unsigned flags;
26795718399fSFrançois Tigeot 	int num_clips;
2680b5162e19SFrançois Tigeot 	int ret;
26815718399fSFrançois Tigeot 
26825718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2683b5162e19SFrançois Tigeot 		return -EINVAL;
26845718399fSFrançois Tigeot 
26854dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
26864dbb207bSFrançois Tigeot 	if (!fb)
2687*9edbd4a0SFrançois Tigeot 		return -ENOENT;
26885718399fSFrançois Tigeot 
26895718399fSFrançois Tigeot 	num_clips = r->num_clips;
2690b5162e19SFrançois Tigeot 	clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
26915718399fSFrançois Tigeot 
26925718399fSFrançois Tigeot 	if (!num_clips != !clips_ptr) {
2693b5162e19SFrançois Tigeot 		ret = -EINVAL;
26945718399fSFrançois Tigeot 		goto out_err1;
26955718399fSFrançois Tigeot 	}
26965718399fSFrançois Tigeot 
26975718399fSFrançois Tigeot 	flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
26985718399fSFrançois Tigeot 
26995718399fSFrançois Tigeot 	/* If userspace annotates copy, clips must come in pairs */
27005718399fSFrançois Tigeot 	if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
2701b5162e19SFrançois Tigeot 		ret = -EINVAL;
27025718399fSFrançois Tigeot 		goto out_err1;
27035718399fSFrançois Tigeot 	}
27045718399fSFrançois Tigeot 
27055718399fSFrançois Tigeot 	if (num_clips && clips_ptr) {
27065718399fSFrançois Tigeot 		if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
2707b5162e19SFrançois Tigeot 			ret = -EINVAL;
27085718399fSFrançois Tigeot 			goto out_err1;
27095718399fSFrançois Tigeot 		}
27104dbb207bSFrançois Tigeot 		clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
2711b5162e19SFrançois Tigeot 		if (!clips) {
2712b5162e19SFrançois Tigeot 			ret = -ENOMEM;
2713b5162e19SFrançois Tigeot 			goto out_err1;
2714b5162e19SFrançois Tigeot 		}
27155718399fSFrançois Tigeot 
2716b5162e19SFrançois Tigeot 		ret = copy_from_user(clips, clips_ptr,
2717b5162e19SFrançois Tigeot 				     num_clips * sizeof(*clips));
2718b5162e19SFrançois Tigeot 		if (ret) {
2719b5162e19SFrançois Tigeot 			ret = -EFAULT;
27205718399fSFrançois Tigeot 			goto out_err2;
27215718399fSFrançois Tigeot 		}
2722b5162e19SFrançois Tigeot 	}
27235718399fSFrançois Tigeot 
27245718399fSFrançois Tigeot 	if (fb->funcs->dirty) {
2725b5162e19SFrançois Tigeot 		ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
27265718399fSFrançois Tigeot 				       clips, num_clips);
27275718399fSFrançois Tigeot 	} else {
2728b5162e19SFrançois Tigeot 		ret = -ENOSYS;
27295718399fSFrançois Tigeot 	}
27305718399fSFrançois Tigeot 
27315718399fSFrançois Tigeot out_err2:
27324dbb207bSFrançois Tigeot 	kfree(clips);
27335718399fSFrançois Tigeot out_err1:
27344dbb207bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
27354dbb207bSFrançois Tigeot 
27365718399fSFrançois Tigeot 	return ret;
27375718399fSFrançois Tigeot }
27385718399fSFrançois Tigeot 
27395718399fSFrançois Tigeot 
27405718399fSFrançois Tigeot /**
27415718399fSFrançois Tigeot  * drm_fb_release - remove and free the FBs on this file
27424dbb207bSFrançois Tigeot  * @priv: drm file for the ioctl
27435718399fSFrançois Tigeot  *
27445718399fSFrançois Tigeot  * Destroy all the FBs associated with @filp.
27455718399fSFrançois Tigeot  *
27465718399fSFrançois Tigeot  * Called by the user via ioctl.
27475718399fSFrançois Tigeot  *
27485718399fSFrançois Tigeot  * RETURNS:
27495718399fSFrançois Tigeot  * Zero on success, errno on failure.
27505718399fSFrançois Tigeot  */
27515718399fSFrançois Tigeot void drm_fb_release(struct drm_file *priv)
27525718399fSFrançois Tigeot {
27534dbb207bSFrançois Tigeot #if 0
27545718399fSFrançois Tigeot 	struct drm_device *dev = priv->minor->dev;
27554dbb207bSFrançois Tigeot #else
27564dbb207bSFrançois Tigeot 	struct drm_device *dev = priv->dev;
275779f713b0SFrançois Tigeot #endif
27585718399fSFrançois Tigeot 	struct drm_framebuffer *fb, *tfb;
27595718399fSFrançois Tigeot 
27604dbb207bSFrançois Tigeot 	mutex_lock(&priv->fbs_lock);
27615718399fSFrançois Tigeot 	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
27624dbb207bSFrançois Tigeot 
27634dbb207bSFrançois Tigeot 		mutex_lock(&dev->mode_config.fb_lock);
27644dbb207bSFrançois Tigeot 		/* Mark fb as reaped, we still have a ref from fpriv->fbs. */
27654dbb207bSFrançois Tigeot 		__drm_framebuffer_unregister(dev, fb);
27664dbb207bSFrançois Tigeot 		mutex_unlock(&dev->mode_config.fb_lock);
27674dbb207bSFrançois Tigeot 
27684dbb207bSFrançois Tigeot 		list_del_init(&fb->filp_head);
27694dbb207bSFrançois Tigeot 
27704dbb207bSFrançois Tigeot 		/* This will also drop the fpriv->fbs reference. */
2771b5162e19SFrançois Tigeot 		drm_framebuffer_remove(fb);
27725718399fSFrançois Tigeot 	}
27734dbb207bSFrançois Tigeot 	mutex_unlock(&priv->fbs_lock);
27745718399fSFrançois Tigeot }
27755718399fSFrançois Tigeot 
27765718399fSFrançois Tigeot struct drm_property *drm_property_create(struct drm_device *dev, int flags,
27775718399fSFrançois Tigeot 					 const char *name, int num_values)
27785718399fSFrançois Tigeot {
27795718399fSFrançois Tigeot 	struct drm_property *property = NULL;
27805718399fSFrançois Tigeot 	int ret;
27815718399fSFrançois Tigeot 
27824dbb207bSFrançois Tigeot 	property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
2783b5162e19SFrançois Tigeot 	if (!property)
2784b5162e19SFrançois Tigeot 		return NULL;
27855718399fSFrançois Tigeot 
27865718399fSFrançois Tigeot 	if (num_values) {
27874dbb207bSFrançois Tigeot 		property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
2788b5162e19SFrançois Tigeot 		if (!property->values)
2789b5162e19SFrançois Tigeot 			goto fail;
27905718399fSFrançois Tigeot 	}
27915718399fSFrançois Tigeot 
27925718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
27935718399fSFrançois Tigeot 	if (ret)
27945718399fSFrançois Tigeot 		goto fail;
2795b5162e19SFrançois Tigeot 
27965718399fSFrançois Tigeot 	property->flags = flags;
27975718399fSFrançois Tigeot 	property->num_values = num_values;
27985718399fSFrançois Tigeot 	INIT_LIST_HEAD(&property->enum_blob_list);
27995718399fSFrançois Tigeot 
28005718399fSFrançois Tigeot 	if (name) {
28015718399fSFrançois Tigeot 		strncpy(property->name, name, DRM_PROP_NAME_LEN);
28025718399fSFrançois Tigeot 		property->name[DRM_PROP_NAME_LEN-1] = '\0';
28035718399fSFrançois Tigeot 	}
28045718399fSFrançois Tigeot 
28055718399fSFrançois Tigeot 	list_add_tail(&property->head, &dev->mode_config.property_list);
28065718399fSFrançois Tigeot 	return property;
28075718399fSFrançois Tigeot fail:
28084dbb207bSFrançois Tigeot 	kfree(property->values);
28094dbb207bSFrançois Tigeot 	kfree(property);
2810b5162e19SFrançois Tigeot 	return NULL;
28115718399fSFrançois Tigeot }
2812b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create);
28135718399fSFrançois Tigeot 
28145718399fSFrançois Tigeot struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
28155718399fSFrançois Tigeot 					 const char *name,
28165718399fSFrançois Tigeot 					 const struct drm_prop_enum_list *props,
28175718399fSFrançois Tigeot 					 int num_values)
28185718399fSFrançois Tigeot {
28195718399fSFrançois Tigeot 	struct drm_property *property;
28205718399fSFrançois Tigeot 	int i, ret;
28215718399fSFrançois Tigeot 
28225718399fSFrançois Tigeot 	flags |= DRM_MODE_PROP_ENUM;
28235718399fSFrançois Tigeot 
28245718399fSFrançois Tigeot 	property = drm_property_create(dev, flags, name, num_values);
28255718399fSFrançois Tigeot 	if (!property)
28265718399fSFrançois Tigeot 		return NULL;
28275718399fSFrançois Tigeot 
28285718399fSFrançois Tigeot 	for (i = 0; i < num_values; i++) {
28295718399fSFrançois Tigeot 		ret = drm_property_add_enum(property, i,
28305718399fSFrançois Tigeot 				      props[i].type,
28315718399fSFrançois Tigeot 				      props[i].name);
28325718399fSFrançois Tigeot 		if (ret) {
28335718399fSFrançois Tigeot 			drm_property_destroy(dev, property);
28345718399fSFrançois Tigeot 			return NULL;
28355718399fSFrançois Tigeot 		}
28365718399fSFrançois Tigeot 	}
28375718399fSFrançois Tigeot 
28385718399fSFrançois Tigeot 	return property;
28395718399fSFrançois Tigeot }
2840b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_enum);
2841b5162e19SFrançois Tigeot 
2842b5162e19SFrançois Tigeot struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
2843b5162e19SFrançois Tigeot 					 int flags, const char *name,
2844b5162e19SFrançois Tigeot 					 const struct drm_prop_enum_list *props,
2845b5162e19SFrançois Tigeot 					 int num_values)
2846b5162e19SFrançois Tigeot {
2847b5162e19SFrançois Tigeot 	struct drm_property *property;
2848b5162e19SFrançois Tigeot 	int i, ret;
2849b5162e19SFrançois Tigeot 
2850b5162e19SFrançois Tigeot 	flags |= DRM_MODE_PROP_BITMASK;
2851b5162e19SFrançois Tigeot 
2852b5162e19SFrançois Tigeot 	property = drm_property_create(dev, flags, name, num_values);
2853b5162e19SFrançois Tigeot 	if (!property)
2854b5162e19SFrançois Tigeot 		return NULL;
2855b5162e19SFrançois Tigeot 
2856b5162e19SFrançois Tigeot 	for (i = 0; i < num_values; i++) {
2857b5162e19SFrançois Tigeot 		ret = drm_property_add_enum(property, i,
2858b5162e19SFrançois Tigeot 				      props[i].type,
2859b5162e19SFrançois Tigeot 				      props[i].name);
2860b5162e19SFrançois Tigeot 		if (ret) {
2861b5162e19SFrançois Tigeot 			drm_property_destroy(dev, property);
2862b5162e19SFrançois Tigeot 			return NULL;
2863b5162e19SFrançois Tigeot 		}
2864b5162e19SFrançois Tigeot 	}
2865b5162e19SFrançois Tigeot 
2866b5162e19SFrançois Tigeot 	return property;
2867b5162e19SFrançois Tigeot }
2868b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bitmask);
28695718399fSFrançois Tigeot 
28705718399fSFrançois Tigeot struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
28715718399fSFrançois Tigeot 					 const char *name,
28725718399fSFrançois Tigeot 					 uint64_t min, uint64_t max)
28735718399fSFrançois Tigeot {
28745718399fSFrançois Tigeot 	struct drm_property *property;
28755718399fSFrançois Tigeot 
28765718399fSFrançois Tigeot 	flags |= DRM_MODE_PROP_RANGE;
28775718399fSFrançois Tigeot 
28785718399fSFrançois Tigeot 	property = drm_property_create(dev, flags, name, 2);
28795718399fSFrançois Tigeot 	if (!property)
28805718399fSFrançois Tigeot 		return NULL;
28815718399fSFrançois Tigeot 
28825718399fSFrançois Tigeot 	property->values[0] = min;
28835718399fSFrançois Tigeot 	property->values[1] = max;
28845718399fSFrançois Tigeot 
28855718399fSFrançois Tigeot 	return property;
28865718399fSFrançois Tigeot }
2887b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_range);
28885718399fSFrançois Tigeot 
28895718399fSFrançois Tigeot int drm_property_add_enum(struct drm_property *property, int index,
28905718399fSFrançois Tigeot 			  uint64_t value, const char *name)
28915718399fSFrançois Tigeot {
28925718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum;
28935718399fSFrançois Tigeot 
2894b5162e19SFrançois Tigeot 	if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
2895b5162e19SFrançois Tigeot 		return -EINVAL;
2896b5162e19SFrançois Tigeot 
2897b5162e19SFrançois Tigeot 	/*
2898b5162e19SFrançois Tigeot 	 * Bitmask enum properties have the additional constraint of values
2899b5162e19SFrançois Tigeot 	 * from 0 to 63
2900b5162e19SFrançois Tigeot 	 */
2901b5162e19SFrançois Tigeot 	if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
29025718399fSFrançois Tigeot 		return -EINVAL;
29035718399fSFrançois Tigeot 
29045718399fSFrançois Tigeot 	if (!list_empty(&property->enum_blob_list)) {
29055718399fSFrançois Tigeot 		list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
29065718399fSFrançois Tigeot 			if (prop_enum->value == value) {
29075718399fSFrançois Tigeot 				strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
29085718399fSFrançois Tigeot 				prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
29095718399fSFrançois Tigeot 				return 0;
29105718399fSFrançois Tigeot 			}
29115718399fSFrançois Tigeot 		}
29125718399fSFrançois Tigeot 	}
29135718399fSFrançois Tigeot 
29144dbb207bSFrançois Tigeot 	prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
2915b5162e19SFrançois Tigeot 	if (!prop_enum)
2916b5162e19SFrançois Tigeot 		return -ENOMEM;
29175718399fSFrançois Tigeot 
29185718399fSFrançois Tigeot 	strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
29195718399fSFrançois Tigeot 	prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
29205718399fSFrançois Tigeot 	prop_enum->value = value;
29215718399fSFrançois Tigeot 
29225718399fSFrançois Tigeot 	property->values[index] = value;
29235718399fSFrançois Tigeot 	list_add_tail(&prop_enum->head, &property->enum_blob_list);
29245718399fSFrançois Tigeot 	return 0;
29255718399fSFrançois Tigeot }
2926b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_add_enum);
29275718399fSFrançois Tigeot 
29285718399fSFrançois Tigeot void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
29295718399fSFrançois Tigeot {
29305718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum, *pt;
29315718399fSFrançois Tigeot 
29325718399fSFrançois Tigeot 	list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) {
29335718399fSFrançois Tigeot 		list_del(&prop_enum->head);
29344dbb207bSFrançois Tigeot 		kfree(prop_enum);
29355718399fSFrançois Tigeot 	}
29365718399fSFrançois Tigeot 
29375718399fSFrançois Tigeot 	if (property->num_values)
29384dbb207bSFrançois Tigeot 		kfree(property->values);
29395718399fSFrançois Tigeot 	drm_mode_object_put(dev, &property->base);
29405718399fSFrançois Tigeot 	list_del(&property->head);
29414dbb207bSFrançois Tigeot 	kfree(property);
29425718399fSFrançois Tigeot }
2943b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_destroy);
29445718399fSFrançois Tigeot 
2945b5162e19SFrançois Tigeot void drm_object_attach_property(struct drm_mode_object *obj,
2946b5162e19SFrançois Tigeot 				struct drm_property *property,
2947b5162e19SFrançois Tigeot 				uint64_t init_val)
2948b5162e19SFrançois Tigeot {
2949b5162e19SFrançois Tigeot 	int count = obj->properties->count;
2950b5162e19SFrançois Tigeot 
2951b5162e19SFrançois Tigeot 	if (count == DRM_OBJECT_MAX_PROPERTY) {
2952b5162e19SFrançois Tigeot 		WARN(1, "Failed to attach object property (type: 0x%x). Please "
2953b5162e19SFrançois Tigeot 			"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
2954b5162e19SFrançois Tigeot 			"you see this message on the same object type.\n",
2955b5162e19SFrançois Tigeot 			obj->type);
2956b5162e19SFrançois Tigeot 		return;
2957b5162e19SFrançois Tigeot 	}
2958b5162e19SFrançois Tigeot 
2959b5162e19SFrançois Tigeot 	obj->properties->ids[count] = property->base.id;
2960b5162e19SFrançois Tigeot 	obj->properties->values[count] = init_val;
2961b5162e19SFrançois Tigeot 	obj->properties->count++;
2962b5162e19SFrançois Tigeot }
2963b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_attach_property);
2964b5162e19SFrançois Tigeot 
2965b5162e19SFrançois Tigeot int drm_object_property_set_value(struct drm_mode_object *obj,
2966b5162e19SFrançois Tigeot 				  struct drm_property *property, uint64_t val)
29675718399fSFrançois Tigeot {
29685718399fSFrançois Tigeot 	int i;
29695718399fSFrançois Tigeot 
2970b5162e19SFrançois Tigeot 	for (i = 0; i < obj->properties->count; i++) {
2971b5162e19SFrançois Tigeot 		if (obj->properties->ids[i] == property->base.id) {
2972b5162e19SFrançois Tigeot 			obj->properties->values[i] = val;
29735718399fSFrançois Tigeot 			return 0;
29745718399fSFrançois Tigeot 		}
29755718399fSFrançois Tigeot 	}
29765718399fSFrançois Tigeot 
29775718399fSFrançois Tigeot 	return -EINVAL;
29785718399fSFrançois Tigeot }
2979b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_set_value);
29805718399fSFrançois Tigeot 
2981b5162e19SFrançois Tigeot int drm_object_property_get_value(struct drm_mode_object *obj,
29825718399fSFrançois Tigeot 				  struct drm_property *property, uint64_t *val)
29835718399fSFrançois Tigeot {
29845718399fSFrançois Tigeot 	int i;
29855718399fSFrançois Tigeot 
2986b5162e19SFrançois Tigeot 	for (i = 0; i < obj->properties->count; i++) {
2987b5162e19SFrançois Tigeot 		if (obj->properties->ids[i] == property->base.id) {
2988b5162e19SFrançois Tigeot 			*val = obj->properties->values[i];
2989b5162e19SFrançois Tigeot 			return 0;
29905718399fSFrançois Tigeot 		}
29915718399fSFrançois Tigeot 	}
29925718399fSFrançois Tigeot 
29935718399fSFrançois Tigeot 	return -EINVAL;
29945718399fSFrançois Tigeot }
2995b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_get_value);
29965718399fSFrançois Tigeot 
29975718399fSFrançois Tigeot int drm_mode_getproperty_ioctl(struct drm_device *dev,
29985718399fSFrançois Tigeot 			       void *data, struct drm_file *file_priv)
29995718399fSFrançois Tigeot {
30005718399fSFrançois Tigeot 	struct drm_mode_object *obj;
30015718399fSFrançois Tigeot 	struct drm_mode_get_property *out_resp = data;
30025718399fSFrançois Tigeot 	struct drm_property *property;
30035718399fSFrançois Tigeot 	int enum_count = 0;
30045718399fSFrançois Tigeot 	int blob_count = 0;
30055718399fSFrançois Tigeot 	int value_count = 0;
30065718399fSFrançois Tigeot 	int ret = 0, i;
30075718399fSFrançois Tigeot 	int copied;
30085718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum;
30095718399fSFrançois Tigeot 	struct drm_mode_property_enum __user *enum_ptr;
30105718399fSFrançois Tigeot 	struct drm_property_blob *prop_blob;
3011b5162e19SFrançois Tigeot 	uint32_t __user *blob_id_ptr;
3012b5162e19SFrançois Tigeot 	uint64_t __user *values_ptr;
3013b5162e19SFrançois Tigeot 	uint32_t __user *blob_length_ptr;
30145718399fSFrançois Tigeot 
30155718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
30165718399fSFrançois Tigeot 		return -EINVAL;
30175718399fSFrançois Tigeot 
30184dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
30195718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
30205718399fSFrançois Tigeot 	if (!obj) {
3021*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
30225718399fSFrançois Tigeot 		goto done;
30235718399fSFrançois Tigeot 	}
30245718399fSFrançois Tigeot 	property = obj_to_property(obj);
30255718399fSFrançois Tigeot 
3026b5162e19SFrançois Tigeot 	if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
30275718399fSFrançois Tigeot 		list_for_each_entry(prop_enum, &property->enum_blob_list, head)
30285718399fSFrançois Tigeot 			enum_count++;
30295718399fSFrançois Tigeot 	} else if (property->flags & DRM_MODE_PROP_BLOB) {
30305718399fSFrançois Tigeot 		list_for_each_entry(prop_blob, &property->enum_blob_list, head)
30315718399fSFrançois Tigeot 			blob_count++;
30325718399fSFrançois Tigeot 	}
30335718399fSFrançois Tigeot 
30345718399fSFrançois Tigeot 	value_count = property->num_values;
30355718399fSFrançois Tigeot 
30365718399fSFrançois Tigeot 	strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
30375718399fSFrançois Tigeot 	out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
30385718399fSFrançois Tigeot 	out_resp->flags = property->flags;
30395718399fSFrançois Tigeot 
30405718399fSFrançois Tigeot 	if ((out_resp->count_values >= value_count) && value_count) {
3041b5162e19SFrançois Tigeot 		values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
30425718399fSFrançois Tigeot 		for (i = 0; i < value_count; i++) {
3043b5162e19SFrançois Tigeot 			if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
30445718399fSFrançois Tigeot 				ret = -EFAULT;
30455718399fSFrançois Tigeot 				goto done;
30465718399fSFrançois Tigeot 			}
30475718399fSFrançois Tigeot 		}
30485718399fSFrançois Tigeot 	}
30495718399fSFrançois Tigeot 	out_resp->count_values = value_count;
30505718399fSFrançois Tigeot 
3051b5162e19SFrançois Tigeot 	if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
30525718399fSFrançois Tigeot 		if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
30535718399fSFrançois Tigeot 			copied = 0;
3054b5162e19SFrançois Tigeot 			enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
30555718399fSFrançois Tigeot 			list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
30565718399fSFrançois Tigeot 
3057b5162e19SFrançois Tigeot 				if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
30585718399fSFrançois Tigeot 					ret = -EFAULT;
30595718399fSFrançois Tigeot 					goto done;
30605718399fSFrançois Tigeot 				}
30615718399fSFrançois Tigeot 
3062b5162e19SFrançois Tigeot 				if (copy_to_user(&enum_ptr[copied].name,
3063b5162e19SFrançois Tigeot 						 &prop_enum->name, DRM_PROP_NAME_LEN)) {
30645718399fSFrançois Tigeot 					ret = -EFAULT;
30655718399fSFrançois Tigeot 					goto done;
30665718399fSFrançois Tigeot 				}
30675718399fSFrançois Tigeot 				copied++;
30685718399fSFrançois Tigeot 			}
30695718399fSFrançois Tigeot 		}
30705718399fSFrançois Tigeot 		out_resp->count_enum_blobs = enum_count;
30715718399fSFrançois Tigeot 	}
30725718399fSFrançois Tigeot 
30735718399fSFrançois Tigeot 	if (property->flags & DRM_MODE_PROP_BLOB) {
30745718399fSFrançois Tigeot 		if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
30755718399fSFrançois Tigeot 			copied = 0;
3076b5162e19SFrançois Tigeot 			blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
3077b5162e19SFrançois Tigeot 			blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr;
30785718399fSFrançois Tigeot 
30795718399fSFrançois Tigeot 			list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
3080b5162e19SFrançois Tigeot 				if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
30815718399fSFrançois Tigeot 					ret = -EFAULT;
30825718399fSFrançois Tigeot 					goto done;
30835718399fSFrançois Tigeot 				}
30845718399fSFrançois Tigeot 
3085b5162e19SFrançois Tigeot 				if (put_user(prop_blob->length, blob_length_ptr + copied)) {
30865718399fSFrançois Tigeot 					ret = -EFAULT;
30875718399fSFrançois Tigeot 					goto done;
30885718399fSFrançois Tigeot 				}
30895718399fSFrançois Tigeot 
30905718399fSFrançois Tigeot 				copied++;
30915718399fSFrançois Tigeot 			}
30925718399fSFrançois Tigeot 		}
30935718399fSFrançois Tigeot 		out_resp->count_enum_blobs = blob_count;
30945718399fSFrançois Tigeot 	}
30955718399fSFrançois Tigeot done:
30964dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
30975718399fSFrançois Tigeot 	return ret;
30985718399fSFrançois Tigeot }
30995718399fSFrançois Tigeot 
31005718399fSFrançois Tigeot static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length,
31015718399fSFrançois Tigeot 							  void *data)
31025718399fSFrançois Tigeot {
31035718399fSFrançois Tigeot 	struct drm_property_blob *blob;
31045718399fSFrançois Tigeot 	int ret;
31055718399fSFrançois Tigeot 
31065718399fSFrançois Tigeot 	if (!length || !data)
31075718399fSFrançois Tigeot 		return NULL;
31085718399fSFrançois Tigeot 
31094dbb207bSFrançois Tigeot 	blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
3110b5162e19SFrançois Tigeot 	if (!blob)
3111b5162e19SFrançois Tigeot 		return NULL;
31125718399fSFrançois Tigeot 
31135718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
31145718399fSFrançois Tigeot 	if (ret) {
31154dbb207bSFrançois Tigeot 		kfree(blob);
3116b5162e19SFrançois Tigeot 		return NULL;
31175718399fSFrançois Tigeot 	}
31185718399fSFrançois Tigeot 
31195718399fSFrançois Tigeot 	blob->length = length;
31205718399fSFrançois Tigeot 
31215718399fSFrançois Tigeot 	memcpy(blob->data, data, length);
31225718399fSFrançois Tigeot 
31235718399fSFrançois Tigeot 	list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
31245718399fSFrançois Tigeot 	return blob;
31255718399fSFrançois Tigeot }
31265718399fSFrançois Tigeot 
31275718399fSFrançois Tigeot static void drm_property_destroy_blob(struct drm_device *dev,
31285718399fSFrançois Tigeot 			       struct drm_property_blob *blob)
31295718399fSFrançois Tigeot {
31305718399fSFrançois Tigeot 	drm_mode_object_put(dev, &blob->base);
31315718399fSFrançois Tigeot 	list_del(&blob->head);
31324dbb207bSFrançois Tigeot 	kfree(blob);
31335718399fSFrançois Tigeot }
31345718399fSFrançois Tigeot 
31355718399fSFrançois Tigeot int drm_mode_getblob_ioctl(struct drm_device *dev,
31365718399fSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
31375718399fSFrançois Tigeot {
31385718399fSFrançois Tigeot 	struct drm_mode_object *obj;
31395718399fSFrançois Tigeot 	struct drm_mode_get_blob *out_resp = data;
31405718399fSFrançois Tigeot 	struct drm_property_blob *blob;
31415718399fSFrançois Tigeot 	int ret = 0;
3142b5162e19SFrançois Tigeot 	void __user *blob_ptr;
31435718399fSFrançois Tigeot 
31445718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
31455718399fSFrançois Tigeot 		return -EINVAL;
31465718399fSFrançois Tigeot 
31474dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
31485718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
31495718399fSFrançois Tigeot 	if (!obj) {
3150*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
31515718399fSFrançois Tigeot 		goto done;
31525718399fSFrançois Tigeot 	}
31535718399fSFrançois Tigeot 	blob = obj_to_blob(obj);
31545718399fSFrançois Tigeot 
31555718399fSFrançois Tigeot 	if (out_resp->length == blob->length) {
3156b5162e19SFrançois Tigeot 		blob_ptr = (void __user *)(unsigned long)out_resp->data;
3157b5162e19SFrançois Tigeot 		if (copy_to_user(blob_ptr, blob->data, blob->length)){
31585718399fSFrançois Tigeot 			ret = -EFAULT;
31595718399fSFrançois Tigeot 			goto done;
31605718399fSFrançois Tigeot 		}
31615718399fSFrançois Tigeot 	}
31625718399fSFrançois Tigeot 	out_resp->length = blob->length;
31635718399fSFrançois Tigeot 
31645718399fSFrançois Tigeot done:
31654dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
31665718399fSFrançois Tigeot 	return ret;
31675718399fSFrançois Tigeot }
31685718399fSFrançois Tigeot 
31695718399fSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector,
31705718399fSFrançois Tigeot 					    struct edid *edid)
31715718399fSFrançois Tigeot {
31725718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
3173b5162e19SFrançois Tigeot 	int ret, size;
31745718399fSFrançois Tigeot 
31755718399fSFrançois Tigeot 	if (connector->edid_blob_ptr)
31765718399fSFrançois Tigeot 		drm_property_destroy_blob(dev, connector->edid_blob_ptr);
31775718399fSFrançois Tigeot 
31785718399fSFrançois Tigeot 	/* Delete edid, when there is none. */
31795718399fSFrançois Tigeot 	if (!edid) {
31805718399fSFrançois Tigeot 		connector->edid_blob_ptr = NULL;
3181b5162e19SFrançois Tigeot 		ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0);
31825718399fSFrançois Tigeot 		return ret;
31835718399fSFrançois Tigeot 	}
31845718399fSFrançois Tigeot 
31855718399fSFrançois Tigeot 	size = EDID_LENGTH * (1 + edid->extensions);
31865718399fSFrançois Tigeot 	connector->edid_blob_ptr = drm_property_create_blob(connector->dev,
31875718399fSFrançois Tigeot 							    size, edid);
3188b5162e19SFrançois Tigeot 	if (!connector->edid_blob_ptr)
3189b5162e19SFrançois Tigeot 		return -EINVAL;
31905718399fSFrançois Tigeot 
3191b5162e19SFrançois Tigeot 	ret = drm_object_property_set_value(&connector->base,
31925718399fSFrançois Tigeot 					       dev->mode_config.edid_property,
31935718399fSFrançois Tigeot 					       connector->edid_blob_ptr->base.id);
31945718399fSFrançois Tigeot 
31955718399fSFrançois Tigeot 	return ret;
31965718399fSFrançois Tigeot }
3197b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
3198b5162e19SFrançois Tigeot 
3199b5162e19SFrançois Tigeot static bool drm_property_change_is_valid(struct drm_property *property,
3200b5162e19SFrançois Tigeot 					 uint64_t value)
3201b5162e19SFrançois Tigeot {
3202b5162e19SFrançois Tigeot 	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
3203b5162e19SFrançois Tigeot 		return false;
3204b5162e19SFrançois Tigeot 	if (property->flags & DRM_MODE_PROP_RANGE) {
3205b5162e19SFrançois Tigeot 		if (value < property->values[0] || value > property->values[1])
3206b5162e19SFrançois Tigeot 			return false;
3207b5162e19SFrançois Tigeot 		return true;
3208b5162e19SFrançois Tigeot 	} else if (property->flags & DRM_MODE_PROP_BITMASK) {
3209b5162e19SFrançois Tigeot 		int i;
3210b5162e19SFrançois Tigeot 		uint64_t valid_mask = 0;
3211b5162e19SFrançois Tigeot 		for (i = 0; i < property->num_values; i++)
3212b5162e19SFrançois Tigeot 			valid_mask |= (1ULL << property->values[i]);
3213b5162e19SFrançois Tigeot 		return !(value & ~valid_mask);
3214b5162e19SFrançois Tigeot 	} else if (property->flags & DRM_MODE_PROP_BLOB) {
3215b5162e19SFrançois Tigeot 		/* Only the driver knows */
3216b5162e19SFrançois Tigeot 		return true;
3217b5162e19SFrançois Tigeot 	} else {
3218b5162e19SFrançois Tigeot 		int i;
3219b5162e19SFrançois Tigeot 		for (i = 0; i < property->num_values; i++)
3220b5162e19SFrançois Tigeot 			if (property->values[i] == value)
3221b5162e19SFrançois Tigeot 				return true;
3222b5162e19SFrançois Tigeot 		return false;
3223b5162e19SFrançois Tigeot 	}
3224b5162e19SFrançois Tigeot }
32255718399fSFrançois Tigeot 
32265718399fSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
32275718399fSFrançois Tigeot 				       void *data, struct drm_file *file_priv)
32285718399fSFrançois Tigeot {
3229b5162e19SFrançois Tigeot 	struct drm_mode_connector_set_property *conn_set_prop = data;
3230b5162e19SFrançois Tigeot 	struct drm_mode_obj_set_property obj_set_prop = {
3231b5162e19SFrançois Tigeot 		.value = conn_set_prop->value,
3232b5162e19SFrançois Tigeot 		.prop_id = conn_set_prop->prop_id,
3233b5162e19SFrançois Tigeot 		.obj_id = conn_set_prop->connector_id,
3234b5162e19SFrançois Tigeot 		.obj_type = DRM_MODE_OBJECT_CONNECTOR
3235b5162e19SFrançois Tigeot 	};
3236b5162e19SFrançois Tigeot 
3237b5162e19SFrançois Tigeot 	/* It does all the locking and checking we need */
3238b5162e19SFrançois Tigeot 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
3239b5162e19SFrançois Tigeot }
3240b5162e19SFrançois Tigeot 
3241b5162e19SFrançois Tigeot static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
3242b5162e19SFrançois Tigeot 					   struct drm_property *property,
3243b5162e19SFrançois Tigeot 					   uint64_t value)
3244b5162e19SFrançois Tigeot {
3245b5162e19SFrançois Tigeot 	int ret = -EINVAL;
3246b5162e19SFrançois Tigeot 	struct drm_connector *connector = obj_to_connector(obj);
3247b5162e19SFrançois Tigeot 
3248b5162e19SFrançois Tigeot 	/* Do DPMS ourselves */
3249b5162e19SFrançois Tigeot 	if (property == connector->dev->mode_config.dpms_property) {
3250b5162e19SFrançois Tigeot 		if (connector->funcs->dpms)
3251b5162e19SFrançois Tigeot 			(*connector->funcs->dpms)(connector, (int)value);
3252b5162e19SFrançois Tigeot 		ret = 0;
3253b5162e19SFrançois Tigeot 	} else if (connector->funcs->set_property)
3254b5162e19SFrançois Tigeot 		ret = connector->funcs->set_property(connector, property, value);
3255b5162e19SFrançois Tigeot 
3256b5162e19SFrançois Tigeot 	/* store the property value if successful */
3257b5162e19SFrançois Tigeot 	if (!ret)
3258b5162e19SFrançois Tigeot 		drm_object_property_set_value(&connector->base, property, value);
3259b5162e19SFrançois Tigeot 	return ret;
3260b5162e19SFrançois Tigeot }
3261b5162e19SFrançois Tigeot 
3262b5162e19SFrançois Tigeot static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
3263b5162e19SFrançois Tigeot 				      struct drm_property *property,
3264b5162e19SFrançois Tigeot 				      uint64_t value)
3265b5162e19SFrançois Tigeot {
3266b5162e19SFrançois Tigeot 	int ret = -EINVAL;
3267b5162e19SFrançois Tigeot 	struct drm_crtc *crtc = obj_to_crtc(obj);
3268b5162e19SFrançois Tigeot 
3269b5162e19SFrançois Tigeot 	if (crtc->funcs->set_property)
3270b5162e19SFrançois Tigeot 		ret = crtc->funcs->set_property(crtc, property, value);
3271b5162e19SFrançois Tigeot 	if (!ret)
3272b5162e19SFrançois Tigeot 		drm_object_property_set_value(obj, property, value);
3273b5162e19SFrançois Tigeot 
3274b5162e19SFrançois Tigeot 	return ret;
3275b5162e19SFrançois Tigeot }
3276b5162e19SFrançois Tigeot 
3277b5162e19SFrançois Tigeot static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
3278b5162e19SFrançois Tigeot 				      struct drm_property *property,
3279b5162e19SFrançois Tigeot 				      uint64_t value)
3280b5162e19SFrançois Tigeot {
3281b5162e19SFrançois Tigeot 	int ret = -EINVAL;
3282b5162e19SFrançois Tigeot 	struct drm_plane *plane = obj_to_plane(obj);
3283b5162e19SFrançois Tigeot 
3284b5162e19SFrançois Tigeot 	if (plane->funcs->set_property)
3285b5162e19SFrançois Tigeot 		ret = plane->funcs->set_property(plane, property, value);
3286b5162e19SFrançois Tigeot 	if (!ret)
3287b5162e19SFrançois Tigeot 		drm_object_property_set_value(obj, property, value);
3288b5162e19SFrançois Tigeot 
3289b5162e19SFrançois Tigeot 	return ret;
3290b5162e19SFrançois Tigeot }
3291b5162e19SFrançois Tigeot 
3292b5162e19SFrançois Tigeot int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
3293b5162e19SFrançois Tigeot 				      struct drm_file *file_priv)
3294b5162e19SFrançois Tigeot {
3295b5162e19SFrançois Tigeot 	struct drm_mode_obj_get_properties *arg = data;
32965718399fSFrançois Tigeot 	struct drm_mode_object *obj;
3297b5162e19SFrançois Tigeot 	int ret = 0;
3298b5162e19SFrançois Tigeot 	int i;
3299b5162e19SFrançois Tigeot 	int copied = 0;
3300b5162e19SFrançois Tigeot 	int props_count = 0;
3301b5162e19SFrançois Tigeot 	uint32_t __user *props_ptr;
3302b5162e19SFrançois Tigeot 	uint64_t __user *prop_values_ptr;
3303b5162e19SFrançois Tigeot 
3304b5162e19SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3305b5162e19SFrançois Tigeot 		return -EINVAL;
3306b5162e19SFrançois Tigeot 
33074dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
3308b5162e19SFrançois Tigeot 
3309b5162e19SFrançois Tigeot 	obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
3310b5162e19SFrançois Tigeot 	if (!obj) {
3311*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
3312b5162e19SFrançois Tigeot 		goto out;
3313b5162e19SFrançois Tigeot 	}
3314b5162e19SFrançois Tigeot 	if (!obj->properties) {
3315b5162e19SFrançois Tigeot 		ret = -EINVAL;
3316b5162e19SFrançois Tigeot 		goto out;
3317b5162e19SFrançois Tigeot 	}
3318b5162e19SFrançois Tigeot 
3319b5162e19SFrançois Tigeot 	props_count = obj->properties->count;
3320b5162e19SFrançois Tigeot 
3321b5162e19SFrançois Tigeot 	/* This ioctl is called twice, once to determine how much space is
3322b5162e19SFrançois Tigeot 	 * needed, and the 2nd time to fill it. */
3323b5162e19SFrançois Tigeot 	if ((arg->count_props >= props_count) && props_count) {
3324b5162e19SFrançois Tigeot 		copied = 0;
3325b5162e19SFrançois Tigeot 		props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr);
3326b5162e19SFrançois Tigeot 		prop_values_ptr = (uint64_t __user *)(unsigned long)
3327b5162e19SFrançois Tigeot 				  (arg->prop_values_ptr);
3328b5162e19SFrançois Tigeot 		for (i = 0; i < props_count; i++) {
3329b5162e19SFrançois Tigeot 			if (put_user(obj->properties->ids[i],
3330b5162e19SFrançois Tigeot 				     props_ptr + copied)) {
3331b5162e19SFrançois Tigeot 				ret = -EFAULT;
3332b5162e19SFrançois Tigeot 				goto out;
3333b5162e19SFrançois Tigeot 			}
3334b5162e19SFrançois Tigeot 			if (put_user(obj->properties->values[i],
3335b5162e19SFrançois Tigeot 				     prop_values_ptr + copied)) {
3336b5162e19SFrançois Tigeot 				ret = -EFAULT;
3337b5162e19SFrançois Tigeot 				goto out;
3338b5162e19SFrançois Tigeot 			}
3339b5162e19SFrançois Tigeot 			copied++;
3340b5162e19SFrançois Tigeot 		}
3341b5162e19SFrançois Tigeot 	}
3342b5162e19SFrançois Tigeot 	arg->count_props = props_count;
3343b5162e19SFrançois Tigeot out:
33444dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
3345b5162e19SFrançois Tigeot 	return ret;
3346b5162e19SFrançois Tigeot }
3347b5162e19SFrançois Tigeot 
3348b5162e19SFrançois Tigeot int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
3349b5162e19SFrançois Tigeot 				    struct drm_file *file_priv)
3350b5162e19SFrançois Tigeot {
3351b5162e19SFrançois Tigeot 	struct drm_mode_obj_set_property *arg = data;
3352b5162e19SFrançois Tigeot 	struct drm_mode_object *arg_obj;
3353b5162e19SFrançois Tigeot 	struct drm_mode_object *prop_obj;
33545718399fSFrançois Tigeot 	struct drm_property *property;
33555718399fSFrançois Tigeot 	int ret = -EINVAL;
33565718399fSFrançois Tigeot 	int i;
33575718399fSFrançois Tigeot 
33585718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
33595718399fSFrançois Tigeot 		return -EINVAL;
33605718399fSFrançois Tigeot 
33614dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
33625718399fSFrançois Tigeot 
3363b5162e19SFrançois Tigeot 	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
3364*9edbd4a0SFrançois Tigeot 	if (!arg_obj) {
3365*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
33665718399fSFrançois Tigeot 		goto out;
3367*9edbd4a0SFrançois Tigeot 	}
3368b5162e19SFrançois Tigeot 	if (!arg_obj->properties)
3369b5162e19SFrançois Tigeot 		goto out;
33705718399fSFrançois Tigeot 
3371b5162e19SFrançois Tigeot 	for (i = 0; i < arg_obj->properties->count; i++)
3372b5162e19SFrançois Tigeot 		if (arg_obj->properties->ids[i] == arg->prop_id)
3373b5162e19SFrançois Tigeot 			break;
3374b5162e19SFrançois Tigeot 
3375b5162e19SFrançois Tigeot 	if (i == arg_obj->properties->count)
3376b5162e19SFrançois Tigeot 		goto out;
3377b5162e19SFrançois Tigeot 
3378b5162e19SFrançois Tigeot 	prop_obj = drm_mode_object_find(dev, arg->prop_id,
3379b5162e19SFrançois Tigeot 					DRM_MODE_OBJECT_PROPERTY);
3380*9edbd4a0SFrançois Tigeot 	if (!prop_obj) {
3381*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
3382b5162e19SFrançois Tigeot 		goto out;
3383*9edbd4a0SFrançois Tigeot 	}
3384b5162e19SFrançois Tigeot 	property = obj_to_property(prop_obj);
3385b5162e19SFrançois Tigeot 
3386b5162e19SFrançois Tigeot 	if (!drm_property_change_is_valid(property, arg->value))
3387b5162e19SFrançois Tigeot 		goto out;
3388b5162e19SFrançois Tigeot 
3389b5162e19SFrançois Tigeot 	switch (arg_obj->type) {
3390b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_CONNECTOR:
3391b5162e19SFrançois Tigeot 		ret = drm_mode_connector_set_obj_prop(arg_obj, property,
3392b5162e19SFrançois Tigeot 						      arg->value);
3393b5162e19SFrançois Tigeot 		break;
3394b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_CRTC:
3395b5162e19SFrançois Tigeot 		ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
3396b5162e19SFrançois Tigeot 		break;
3397b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_PLANE:
3398b5162e19SFrançois Tigeot 		ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value);
33995718399fSFrançois Tigeot 		break;
34005718399fSFrançois Tigeot 	}
34015718399fSFrançois Tigeot 
34025718399fSFrançois Tigeot out:
34034dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
34045718399fSFrançois Tigeot 	return ret;
34055718399fSFrançois Tigeot }
34065718399fSFrançois Tigeot 
34075718399fSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector,
34085718399fSFrançois Tigeot 				      struct drm_encoder *encoder)
34095718399fSFrançois Tigeot {
34105718399fSFrançois Tigeot 	int i;
34115718399fSFrançois Tigeot 
34125718399fSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
34135718399fSFrançois Tigeot 		if (connector->encoder_ids[i] == 0) {
34145718399fSFrançois Tigeot 			connector->encoder_ids[i] = encoder->base.id;
34155718399fSFrançois Tigeot 			return 0;
34165718399fSFrançois Tigeot 		}
34175718399fSFrançois Tigeot 	}
34185718399fSFrançois Tigeot 	return -ENOMEM;
34195718399fSFrançois Tigeot }
3420b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
34215718399fSFrançois Tigeot 
34225718399fSFrançois Tigeot void drm_mode_connector_detach_encoder(struct drm_connector *connector,
34235718399fSFrançois Tigeot 				    struct drm_encoder *encoder)
34245718399fSFrançois Tigeot {
34255718399fSFrançois Tigeot 	int i;
34265718399fSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
34275718399fSFrançois Tigeot 		if (connector->encoder_ids[i] == encoder->base.id) {
34285718399fSFrançois Tigeot 			connector->encoder_ids[i] = 0;
34295718399fSFrançois Tigeot 			if (connector->encoder == encoder)
34305718399fSFrançois Tigeot 				connector->encoder = NULL;
34315718399fSFrançois Tigeot 			break;
34325718399fSFrançois Tigeot 		}
34335718399fSFrançois Tigeot 	}
34345718399fSFrançois Tigeot }
3435b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
34365718399fSFrançois Tigeot 
34375718399fSFrançois Tigeot int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
34385718399fSFrançois Tigeot 				  int gamma_size)
34395718399fSFrançois Tigeot {
34405718399fSFrançois Tigeot 	crtc->gamma_size = gamma_size;
34415718399fSFrançois Tigeot 
34424dbb207bSFrançois Tigeot 	crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
3443b5162e19SFrançois Tigeot 	if (!crtc->gamma_store) {
3444b5162e19SFrançois Tigeot 		crtc->gamma_size = 0;
3445b5162e19SFrançois Tigeot 		return -ENOMEM;
3446b5162e19SFrançois Tigeot 	}
34475718399fSFrançois Tigeot 
34485718399fSFrançois Tigeot 	return 0;
34495718399fSFrançois Tigeot }
3450b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
34515718399fSFrançois Tigeot 
34525718399fSFrançois Tigeot int drm_mode_gamma_set_ioctl(struct drm_device *dev,
34535718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
34545718399fSFrançois Tigeot {
34555718399fSFrançois Tigeot 	struct drm_mode_crtc_lut *crtc_lut = data;
34565718399fSFrançois Tigeot 	struct drm_mode_object *obj;
34575718399fSFrançois Tigeot 	struct drm_crtc *crtc;
34585718399fSFrançois Tigeot 	void *r_base, *g_base, *b_base;
34595718399fSFrançois Tigeot 	int size;
34605718399fSFrançois Tigeot 	int ret = 0;
34615718399fSFrançois Tigeot 
34625718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
34635718399fSFrançois Tigeot 		return -EINVAL;
34645718399fSFrançois Tigeot 
34654dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
34665718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
34675718399fSFrançois Tigeot 	if (!obj) {
3468*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
34695718399fSFrançois Tigeot 		goto out;
34705718399fSFrançois Tigeot 	}
34715718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
34725718399fSFrançois Tigeot 
3473b5162e19SFrançois Tigeot 	if (crtc->funcs->gamma_set == NULL) {
3474b5162e19SFrançois Tigeot 		ret = -ENOSYS;
3475b5162e19SFrançois Tigeot 		goto out;
3476b5162e19SFrançois Tigeot 	}
3477b5162e19SFrançois Tigeot 
34785718399fSFrançois Tigeot 	/* memcpy into gamma store */
34795718399fSFrançois Tigeot 	if (crtc_lut->gamma_size != crtc->gamma_size) {
34805718399fSFrançois Tigeot 		ret = -EINVAL;
34815718399fSFrançois Tigeot 		goto out;
34825718399fSFrançois Tigeot 	}
34835718399fSFrançois Tigeot 
34845718399fSFrançois Tigeot 	size = crtc_lut->gamma_size * (sizeof(uint16_t));
34855718399fSFrançois Tigeot 	r_base = crtc->gamma_store;
3486b5162e19SFrançois Tigeot 	if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
34875718399fSFrançois Tigeot 		ret = -EFAULT;
34885718399fSFrançois Tigeot 		goto out;
34895718399fSFrançois Tigeot 	}
34905718399fSFrançois Tigeot 
34915718399fSFrançois Tigeot 	g_base = (char *)r_base + size;
3492b5162e19SFrançois Tigeot 	if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
34935718399fSFrançois Tigeot 		ret = -EFAULT;
34945718399fSFrançois Tigeot 		goto out;
34955718399fSFrançois Tigeot 	}
34965718399fSFrançois Tigeot 
349706fede5aSFrançois Tigeot 	b_base = (char *)g_base + size;
3498b5162e19SFrançois Tigeot 	if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
34995718399fSFrançois Tigeot 		ret = -EFAULT;
35005718399fSFrançois Tigeot 		goto out;
35015718399fSFrançois Tigeot 	}
35025718399fSFrançois Tigeot 
35035718399fSFrançois Tigeot 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
35045718399fSFrançois Tigeot 
35055718399fSFrançois Tigeot out:
35064dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
35075718399fSFrançois Tigeot 	return ret;
35085718399fSFrançois Tigeot 
35095718399fSFrançois Tigeot }
35105718399fSFrançois Tigeot 
35115718399fSFrançois Tigeot int drm_mode_gamma_get_ioctl(struct drm_device *dev,
35125718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
35135718399fSFrançois Tigeot {
35145718399fSFrançois Tigeot 	struct drm_mode_crtc_lut *crtc_lut = data;
35155718399fSFrançois Tigeot 	struct drm_mode_object *obj;
35165718399fSFrançois Tigeot 	struct drm_crtc *crtc;
35175718399fSFrançois Tigeot 	void *r_base, *g_base, *b_base;
35185718399fSFrançois Tigeot 	int size;
35195718399fSFrançois Tigeot 	int ret = 0;
35205718399fSFrançois Tigeot 
35215718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
35225718399fSFrançois Tigeot 		return -EINVAL;
35235718399fSFrançois Tigeot 
35244dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
35255718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
35265718399fSFrançois Tigeot 	if (!obj) {
3527*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
35285718399fSFrançois Tigeot 		goto out;
35295718399fSFrançois Tigeot 	}
35305718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
35315718399fSFrançois Tigeot 
35325718399fSFrançois Tigeot 	/* memcpy into gamma store */
35335718399fSFrançois Tigeot 	if (crtc_lut->gamma_size != crtc->gamma_size) {
35345718399fSFrançois Tigeot 		ret = -EINVAL;
35355718399fSFrançois Tigeot 		goto out;
35365718399fSFrançois Tigeot 	}
35375718399fSFrançois Tigeot 
35385718399fSFrançois Tigeot 	size = crtc_lut->gamma_size * (sizeof(uint16_t));
35395718399fSFrançois Tigeot 	r_base = crtc->gamma_store;
3540b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
35415718399fSFrançois Tigeot 		ret = -EFAULT;
35425718399fSFrançois Tigeot 		goto out;
35435718399fSFrançois Tigeot 	}
35445718399fSFrançois Tigeot 
35455718399fSFrançois Tigeot 	g_base = (char *)r_base + size;
3546b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
35475718399fSFrançois Tigeot 		ret = -EFAULT;
35485718399fSFrançois Tigeot 		goto out;
35495718399fSFrançois Tigeot 	}
35505718399fSFrançois Tigeot 
35515718399fSFrançois Tigeot 	b_base = (char *)g_base + size;
3552b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
35535718399fSFrançois Tigeot 		ret = -EFAULT;
35545718399fSFrançois Tigeot 		goto out;
35555718399fSFrançois Tigeot 	}
35565718399fSFrançois Tigeot out:
35574dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
35585718399fSFrançois Tigeot 	return ret;
35595718399fSFrançois Tigeot }
35605718399fSFrançois Tigeot 
3561a5f72378SFrançois Tigeot /*
3562a5f72378SFrançois Tigeot  * The Linux version of kfree() is a macro and can't be called
3563a5f72378SFrançois Tigeot  * directly via a function pointer
3564a5f72378SFrançois Tigeot  */
3565a5f72378SFrançois Tigeot static void drm_kms_free(void *arg)
3566a5f72378SFrançois Tigeot {
3567a5f72378SFrançois Tigeot 	kfree(arg);
3568a5f72378SFrançois Tigeot }
3569a5f72378SFrançois Tigeot 
3570b5162e19SFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev,
3571b5162e19SFrançois Tigeot 			     void *data, struct drm_file *file_priv)
35725718399fSFrançois Tigeot {
35735718399fSFrançois Tigeot 	struct drm_mode_crtc_page_flip *page_flip = data;
35745718399fSFrançois Tigeot 	struct drm_mode_object *obj;
35755718399fSFrançois Tigeot 	struct drm_crtc *crtc;
35764dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
35775718399fSFrançois Tigeot 	struct drm_pending_vblank_event *e = NULL;
3578b5162e19SFrançois Tigeot 	int ret = -EINVAL;
35795718399fSFrançois Tigeot 
35805718399fSFrançois Tigeot 	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
35815718399fSFrançois Tigeot 	    page_flip->reserved != 0)
3582b5162e19SFrançois Tigeot 		return -EINVAL;
35835718399fSFrançois Tigeot 
3584*9edbd4a0SFrançois Tigeot 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
3585*9edbd4a0SFrançois Tigeot 		return -EINVAL;
3586*9edbd4a0SFrançois Tigeot 
35875718399fSFrançois Tigeot 	obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
35885718399fSFrançois Tigeot 	if (!obj)
3589*9edbd4a0SFrançois Tigeot 		return -ENOENT;
35905718399fSFrançois Tigeot 	crtc = obj_to_crtc(obj);
35915718399fSFrançois Tigeot 
35924dbb207bSFrançois Tigeot 	mutex_lock(&crtc->mutex);
35935718399fSFrançois Tigeot 	if (crtc->fb == NULL) {
35945718399fSFrançois Tigeot 		/* The framebuffer is currently unbound, presumably
35955718399fSFrançois Tigeot 		 * due to a hotplug event, that userspace has not
35965718399fSFrançois Tigeot 		 * yet discovered.
35975718399fSFrançois Tigeot 		 */
3598b5162e19SFrançois Tigeot 		ret = -EBUSY;
35995718399fSFrançois Tigeot 		goto out;
36005718399fSFrançois Tigeot 	}
36015718399fSFrançois Tigeot 
36025718399fSFrançois Tigeot 	if (crtc->funcs->page_flip == NULL)
36035718399fSFrançois Tigeot 		goto out;
36045718399fSFrançois Tigeot 
36054dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
3606*9edbd4a0SFrançois Tigeot 	if (!fb) {
3607*9edbd4a0SFrançois Tigeot 		ret = -ENOENT;
36085718399fSFrançois Tigeot 		goto out;
36095718399fSFrançois Tigeot 	}
36105718399fSFrançois Tigeot 
3611*9edbd4a0SFrançois Tigeot 	ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
3612*9edbd4a0SFrançois Tigeot 	if (ret)
3613*9edbd4a0SFrançois Tigeot 		goto out;
3614*9edbd4a0SFrançois Tigeot 
36154dbb207bSFrançois Tigeot 	if (crtc->fb->pixel_format != fb->pixel_format) {
36164dbb207bSFrançois Tigeot 		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
36174dbb207bSFrançois Tigeot 		ret = -EINVAL;
36184dbb207bSFrançois Tigeot 		goto out;
36194dbb207bSFrançois Tigeot 	}
36204dbb207bSFrançois Tigeot 
36215718399fSFrançois Tigeot 	if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
3622b5162e19SFrançois Tigeot 		ret = -ENOMEM;
36235718399fSFrançois Tigeot 		lockmgr(&dev->event_lock, LK_EXCLUSIVE);
36245718399fSFrançois Tigeot 		if (file_priv->event_space < sizeof e->event) {
36255718399fSFrançois Tigeot 			lockmgr(&dev->event_lock, LK_RELEASE);
36265718399fSFrançois Tigeot 			goto out;
36275718399fSFrançois Tigeot 		}
36285718399fSFrançois Tigeot 		file_priv->event_space -= sizeof e->event;
36295718399fSFrançois Tigeot 		lockmgr(&dev->event_lock, LK_RELEASE);
36305718399fSFrançois Tigeot 
36314dbb207bSFrançois Tigeot 		e = kzalloc(sizeof *e, GFP_KERNEL);
3632b5162e19SFrançois Tigeot 		if (e == NULL) {
3633b5162e19SFrançois Tigeot 			lockmgr(&dev->event_lock, LK_EXCLUSIVE);
3634b5162e19SFrançois Tigeot 			file_priv->event_space += sizeof e->event;
3635b5162e19SFrançois Tigeot 			lockmgr(&dev->event_lock, LK_RELEASE);
3636b5162e19SFrançois Tigeot 			goto out;
3637b5162e19SFrançois Tigeot 		}
36385718399fSFrançois Tigeot 
36395718399fSFrançois Tigeot 		e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
36405718399fSFrançois Tigeot 		e->event.base.length = sizeof e->event;
36415718399fSFrançois Tigeot 		e->event.user_data = page_flip->user_data;
36425718399fSFrançois Tigeot 		e->base.event = &e->event.base;
36435718399fSFrançois Tigeot 		e->base.file_priv = file_priv;
36445718399fSFrançois Tigeot 		e->base.destroy =
3645a5f72378SFrançois Tigeot 			(void (*) (struct drm_pending_event *)) drm_kms_free;
36465718399fSFrançois Tigeot 	}
36475718399fSFrançois Tigeot 
36484dbb207bSFrançois Tigeot 	old_fb = crtc->fb;
3649*9edbd4a0SFrançois Tigeot 	ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
3650b5162e19SFrançois Tigeot 	if (ret) {
36515718399fSFrançois Tigeot 		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
36525718399fSFrançois Tigeot 			lockmgr(&dev->event_lock, LK_EXCLUSIVE);
36535718399fSFrançois Tigeot 			file_priv->event_space += sizeof e->event;
36545718399fSFrançois Tigeot 			lockmgr(&dev->event_lock, LK_RELEASE);
36554dbb207bSFrançois Tigeot 			kfree(e);
36565718399fSFrançois Tigeot 		}
36574dbb207bSFrançois Tigeot 		/* Keep the old fb, don't unref it. */
36584dbb207bSFrançois Tigeot 		old_fb = NULL;
36594dbb207bSFrançois Tigeot 	} else {
36604dbb207bSFrançois Tigeot 		/*
36614dbb207bSFrançois Tigeot 		 * Warn if the driver hasn't properly updated the crtc->fb
36624dbb207bSFrançois Tigeot 		 * field to reflect that the new framebuffer is now used.
36634dbb207bSFrançois Tigeot 		 * Failing to do so will screw with the reference counting
36644dbb207bSFrançois Tigeot 		 * on framebuffers.
36654dbb207bSFrançois Tigeot 		 */
36664dbb207bSFrançois Tigeot 		WARN_ON(crtc->fb != fb);
36674dbb207bSFrançois Tigeot 		/* Unref only the old framebuffer. */
36684dbb207bSFrançois Tigeot 		fb = NULL;
36695718399fSFrançois Tigeot 	}
36705718399fSFrançois Tigeot 
36715718399fSFrançois Tigeot out:
36724dbb207bSFrançois Tigeot 	if (fb)
36734dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
36744dbb207bSFrançois Tigeot 	if (old_fb)
36754dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(old_fb);
36764dbb207bSFrançois Tigeot 	mutex_unlock(&crtc->mutex);
36774dbb207bSFrançois Tigeot 
3678b5162e19SFrançois Tigeot 	return ret;
36795718399fSFrançois Tigeot }
36805718399fSFrançois Tigeot 
36815718399fSFrançois Tigeot void drm_mode_config_reset(struct drm_device *dev)
36825718399fSFrançois Tigeot {
36835718399fSFrançois Tigeot 	struct drm_crtc *crtc;
36845718399fSFrançois Tigeot 	struct drm_encoder *encoder;
36855718399fSFrançois Tigeot 	struct drm_connector *connector;
36865718399fSFrançois Tigeot 
36875718399fSFrançois Tigeot 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
36885718399fSFrançois Tigeot 		if (crtc->funcs->reset)
36895718399fSFrançois Tigeot 			crtc->funcs->reset(crtc);
36905718399fSFrançois Tigeot 
36915718399fSFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
36925718399fSFrançois Tigeot 		if (encoder->funcs->reset)
36935718399fSFrançois Tigeot 			encoder->funcs->reset(encoder);
36945718399fSFrançois Tigeot 
3695b5162e19SFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
3696b5162e19SFrançois Tigeot 		connector->status = connector_status_unknown;
3697b5162e19SFrançois Tigeot 
36985718399fSFrançois Tigeot 		if (connector->funcs->reset)
36995718399fSFrançois Tigeot 			connector->funcs->reset(connector);
37005718399fSFrançois Tigeot 	}
3701b5162e19SFrançois Tigeot }
3702b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_reset);
37035718399fSFrançois Tigeot 
37045718399fSFrançois Tigeot int drm_mode_create_dumb_ioctl(struct drm_device *dev,
37055718399fSFrançois Tigeot 			       void *data, struct drm_file *file_priv)
37065718399fSFrançois Tigeot {
37075718399fSFrançois Tigeot 	struct drm_mode_create_dumb *args = data;
37085718399fSFrançois Tigeot 
37095718399fSFrançois Tigeot 	if (!dev->driver->dumb_create)
3710b5162e19SFrançois Tigeot 		return -ENOSYS;
37115718399fSFrançois Tigeot 	return dev->driver->dumb_create(file_priv, dev, args);
37125718399fSFrançois Tigeot }
37135718399fSFrançois Tigeot 
37145718399fSFrançois Tigeot int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
37155718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
37165718399fSFrançois Tigeot {
37175718399fSFrançois Tigeot 	struct drm_mode_map_dumb *args = data;
37185718399fSFrançois Tigeot 
37195718399fSFrançois Tigeot 	/* call driver ioctl to get mmap offset */
37205718399fSFrançois Tigeot 	if (!dev->driver->dumb_map_offset)
3721b5162e19SFrançois Tigeot 		return -ENOSYS;
37225718399fSFrançois Tigeot 
37235718399fSFrançois Tigeot 	return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset);
37245718399fSFrançois Tigeot }
37255718399fSFrançois Tigeot 
37265718399fSFrançois Tigeot int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
37275718399fSFrançois Tigeot 				void *data, struct drm_file *file_priv)
37285718399fSFrançois Tigeot {
37295718399fSFrançois Tigeot 	struct drm_mode_destroy_dumb *args = data;
37305718399fSFrançois Tigeot 
37315718399fSFrançois Tigeot 	if (!dev->driver->dumb_destroy)
3732b5162e19SFrançois Tigeot 		return -ENOSYS;
37335718399fSFrançois Tigeot 
37345718399fSFrançois Tigeot 	return dev->driver->dumb_destroy(file_priv, dev, args->handle);
37355718399fSFrançois Tigeot }
37365718399fSFrançois Tigeot 
37375718399fSFrançois Tigeot /*
37385718399fSFrançois Tigeot  * Just need to support RGB formats here for compat with code that doesn't
37395718399fSFrançois Tigeot  * use pixel formats directly yet.
37405718399fSFrançois Tigeot  */
37415718399fSFrançois Tigeot void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
37425718399fSFrançois Tigeot 			  int *bpp)
37435718399fSFrançois Tigeot {
37445718399fSFrançois Tigeot 	switch (format) {
37454dbb207bSFrançois Tigeot 	case DRM_FORMAT_C8:
37465718399fSFrançois Tigeot 	case DRM_FORMAT_RGB332:
37475718399fSFrançois Tigeot 	case DRM_FORMAT_BGR233:
37485718399fSFrançois Tigeot 		*depth = 8;
37495718399fSFrançois Tigeot 		*bpp = 8;
37505718399fSFrançois Tigeot 		break;
37515718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB1555:
37525718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR1555:
37535718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX5551:
37545718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX5551:
37555718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB1555:
37565718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR1555:
37575718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA5551:
37585718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA5551:
37595718399fSFrançois Tigeot 		*depth = 15;
37605718399fSFrançois Tigeot 		*bpp = 16;
37615718399fSFrançois Tigeot 		break;
37625718399fSFrançois Tigeot 	case DRM_FORMAT_RGB565:
37635718399fSFrançois Tigeot 	case DRM_FORMAT_BGR565:
37645718399fSFrançois Tigeot 		*depth = 16;
37655718399fSFrançois Tigeot 		*bpp = 16;
37665718399fSFrançois Tigeot 		break;
37675718399fSFrançois Tigeot 	case DRM_FORMAT_RGB888:
37685718399fSFrançois Tigeot 	case DRM_FORMAT_BGR888:
37695718399fSFrançois Tigeot 		*depth = 24;
37705718399fSFrançois Tigeot 		*bpp = 24;
37715718399fSFrançois Tigeot 		break;
37725718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB8888:
37735718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR8888:
37745718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX8888:
37755718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX8888:
37765718399fSFrançois Tigeot 		*depth = 24;
37775718399fSFrançois Tigeot 		*bpp = 32;
37785718399fSFrançois Tigeot 		break;
37795718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB2101010:
37805718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR2101010:
37815718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX1010102:
37825718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX1010102:
37835718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB2101010:
37845718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR2101010:
37855718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA1010102:
37865718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA1010102:
37875718399fSFrançois Tigeot 		*depth = 30;
37885718399fSFrançois Tigeot 		*bpp = 32;
37895718399fSFrançois Tigeot 		break;
37905718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB8888:
37915718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR8888:
37925718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA8888:
37935718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA8888:
37945718399fSFrançois Tigeot 		*depth = 32;
37955718399fSFrançois Tigeot 		*bpp = 32;
37965718399fSFrançois Tigeot 		break;
37975718399fSFrançois Tigeot 	default:
3798*9edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("unsupported pixel format %s\n",
3799*9edbd4a0SFrançois Tigeot 			      drm_get_format_name(format));
38005718399fSFrançois Tigeot 		*depth = 0;
38015718399fSFrançois Tigeot 		*bpp = 0;
38025718399fSFrançois Tigeot 		break;
38035718399fSFrançois Tigeot 	}
38045718399fSFrançois Tigeot }
3805b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_fb_get_bpp_depth);
3806b5162e19SFrançois Tigeot 
3807b5162e19SFrançois Tigeot /**
3808b5162e19SFrançois Tigeot  * drm_format_num_planes - get the number of planes for format
3809b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
3810b5162e19SFrançois Tigeot  *
3811b5162e19SFrançois Tigeot  * RETURNS:
3812b5162e19SFrançois Tigeot  * The number of planes used by the specified pixel format.
3813b5162e19SFrançois Tigeot  */
3814b5162e19SFrançois Tigeot int drm_format_num_planes(uint32_t format)
3815b5162e19SFrançois Tigeot {
3816b5162e19SFrançois Tigeot 	switch (format) {
3817b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
3818b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
3819b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
3820b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
3821b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
3822b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
3823b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
3824b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
3825b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV444:
3826b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU444:
3827b5162e19SFrançois Tigeot 		return 3;
3828b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
3829b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
3830b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
3831b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
3832b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
3833b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
3834b5162e19SFrançois Tigeot 		return 2;
3835b5162e19SFrançois Tigeot 	default:
3836b5162e19SFrançois Tigeot 		return 1;
3837b5162e19SFrançois Tigeot 	}
3838b5162e19SFrançois Tigeot }
3839b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_num_planes);
3840b5162e19SFrançois Tigeot 
3841b5162e19SFrançois Tigeot /**
3842b5162e19SFrançois Tigeot  * drm_format_plane_cpp - determine the bytes per pixel value
3843b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
3844b5162e19SFrançois Tigeot  * @plane: plane index
3845b5162e19SFrançois Tigeot  *
3846b5162e19SFrançois Tigeot  * RETURNS:
3847b5162e19SFrançois Tigeot  * The bytes per pixel value for the specified plane.
3848b5162e19SFrançois Tigeot  */
3849b5162e19SFrançois Tigeot int drm_format_plane_cpp(uint32_t format, int plane)
3850b5162e19SFrançois Tigeot {
3851b5162e19SFrançois Tigeot 	unsigned int depth;
3852b5162e19SFrançois Tigeot 	int bpp;
3853b5162e19SFrançois Tigeot 
3854b5162e19SFrançois Tigeot 	if (plane >= drm_format_num_planes(format))
3855b5162e19SFrançois Tigeot 		return 0;
3856b5162e19SFrançois Tigeot 
3857b5162e19SFrançois Tigeot 	switch (format) {
3858b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUYV:
3859b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVYU:
3860b5162e19SFrançois Tigeot 	case DRM_FORMAT_UYVY:
3861b5162e19SFrançois Tigeot 	case DRM_FORMAT_VYUY:
3862b5162e19SFrançois Tigeot 		return 2;
3863b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
3864b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
3865b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
3866b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
3867b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
3868b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
3869b5162e19SFrançois Tigeot 		return plane ? 2 : 1;
3870b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
3871b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
3872b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
3873b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
3874b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
3875b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
3876b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
3877b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
3878b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV444:
3879b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU444:
3880b5162e19SFrançois Tigeot 		return 1;
3881b5162e19SFrançois Tigeot 	default:
3882b5162e19SFrançois Tigeot 		drm_fb_get_bpp_depth(format, &depth, &bpp);
3883b5162e19SFrançois Tigeot 		return bpp >> 3;
3884b5162e19SFrançois Tigeot 	}
3885b5162e19SFrançois Tigeot }
3886b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_cpp);
3887b5162e19SFrançois Tigeot 
3888b5162e19SFrançois Tigeot /**
3889b5162e19SFrançois Tigeot  * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor
3890b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
3891b5162e19SFrançois Tigeot  *
3892b5162e19SFrançois Tigeot  * RETURNS:
3893b5162e19SFrançois Tigeot  * The horizontal chroma subsampling factor for the
3894b5162e19SFrançois Tigeot  * specified pixel format.
3895b5162e19SFrançois Tigeot  */
3896b5162e19SFrançois Tigeot int drm_format_horz_chroma_subsampling(uint32_t format)
3897b5162e19SFrançois Tigeot {
3898b5162e19SFrançois Tigeot 	switch (format) {
3899b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
3900b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
3901b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
3902b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
3903b5162e19SFrançois Tigeot 		return 4;
3904b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUYV:
3905b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVYU:
3906b5162e19SFrançois Tigeot 	case DRM_FORMAT_UYVY:
3907b5162e19SFrançois Tigeot 	case DRM_FORMAT_VYUY:
3908b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
3909b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
3910b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
3911b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
3912b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
3913b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
3914b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
3915b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
3916b5162e19SFrançois Tigeot 		return 2;
3917b5162e19SFrançois Tigeot 	default:
3918b5162e19SFrançois Tigeot 		return 1;
3919b5162e19SFrançois Tigeot 	}
3920b5162e19SFrançois Tigeot }
3921b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_horz_chroma_subsampling);
3922b5162e19SFrançois Tigeot 
3923b5162e19SFrançois Tigeot /**
3924b5162e19SFrançois Tigeot  * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor
3925b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
3926b5162e19SFrançois Tigeot  *
3927b5162e19SFrançois Tigeot  * RETURNS:
3928b5162e19SFrançois Tigeot  * The vertical chroma subsampling factor for the
3929b5162e19SFrançois Tigeot  * specified pixel format.
3930b5162e19SFrançois Tigeot  */
3931b5162e19SFrançois Tigeot int drm_format_vert_chroma_subsampling(uint32_t format)
3932b5162e19SFrançois Tigeot {
3933b5162e19SFrançois Tigeot 	switch (format) {
3934b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
3935b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
3936b5162e19SFrançois Tigeot 		return 4;
3937b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
3938b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
3939b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
3940b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
3941b5162e19SFrançois Tigeot 		return 2;
3942b5162e19SFrançois Tigeot 	default:
3943b5162e19SFrançois Tigeot 		return 1;
3944b5162e19SFrançois Tigeot 	}
3945b5162e19SFrançois Tigeot }
3946b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
39474dbb207bSFrançois Tigeot 
39484dbb207bSFrançois Tigeot /**
39494dbb207bSFrançois Tigeot  * drm_mode_config_init - initialize DRM mode_configuration structure
39504dbb207bSFrançois Tigeot  * @dev: DRM device
39514dbb207bSFrançois Tigeot  *
39524dbb207bSFrançois Tigeot  * Initialize @dev's mode_config structure, used for tracking the graphics
39534dbb207bSFrançois Tigeot  * configuration of @dev.
39544dbb207bSFrançois Tigeot  *
39554dbb207bSFrançois Tigeot  * Since this initializes the modeset locks, no locking is possible. Which is no
39564dbb207bSFrançois Tigeot  * problem, since this should happen single threaded at init time. It is the
39574dbb207bSFrançois Tigeot  * driver's problem to ensure this guarantee.
39584dbb207bSFrançois Tigeot  *
39594dbb207bSFrançois Tigeot  */
39604dbb207bSFrançois Tigeot void drm_mode_config_init(struct drm_device *dev)
39614dbb207bSFrançois Tigeot {
39624dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE);
39634dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE);
39644dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE);
39654dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
39664dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.crtc_list);
39674dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.connector_list);
39684dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.encoder_list);
39694dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.property_list);
39704dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
39714dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.plane_list);
39724dbb207bSFrançois Tigeot 	idr_init(&dev->mode_config.crtc_idr);
39734dbb207bSFrançois Tigeot 
39744dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
39754dbb207bSFrançois Tigeot 	drm_mode_create_standard_connector_properties(dev);
39764dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
39774dbb207bSFrançois Tigeot 
39784dbb207bSFrançois Tigeot 	/* Just to be sure */
39794dbb207bSFrançois Tigeot 	dev->mode_config.num_fb = 0;
39804dbb207bSFrançois Tigeot 	dev->mode_config.num_connector = 0;
39814dbb207bSFrançois Tigeot 	dev->mode_config.num_crtc = 0;
39824dbb207bSFrançois Tigeot 	dev->mode_config.num_encoder = 0;
39834dbb207bSFrançois Tigeot }
39844dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_init);
39854dbb207bSFrançois Tigeot 
39864dbb207bSFrançois Tigeot /**
39874dbb207bSFrançois Tigeot  * drm_mode_config_cleanup - free up DRM mode_config info
39884dbb207bSFrançois Tigeot  * @dev: DRM device
39894dbb207bSFrançois Tigeot  *
39904dbb207bSFrançois Tigeot  * Free up all the connectors and CRTCs associated with this DRM device, then
39914dbb207bSFrançois Tigeot  * free up the framebuffers and associated buffer objects.
39924dbb207bSFrançois Tigeot  *
39934dbb207bSFrançois Tigeot  * Note that since this /should/ happen single-threaded at driver/device
39944dbb207bSFrançois Tigeot  * teardown time, no locking is required. It's the driver's job to ensure that
39954dbb207bSFrançois Tigeot  * this guarantee actually holds true.
39964dbb207bSFrançois Tigeot  *
39974dbb207bSFrançois Tigeot  * FIXME: cleanup any dangling user buffer objects too
39984dbb207bSFrançois Tigeot  */
39994dbb207bSFrançois Tigeot void drm_mode_config_cleanup(struct drm_device *dev)
40004dbb207bSFrançois Tigeot {
40014dbb207bSFrançois Tigeot 	struct drm_connector *connector, *ot;
40024dbb207bSFrançois Tigeot 	struct drm_crtc *crtc, *ct;
40034dbb207bSFrançois Tigeot 	struct drm_encoder *encoder, *enct;
40044dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb, *fbt;
40054dbb207bSFrançois Tigeot 	struct drm_property *property, *pt;
40064dbb207bSFrançois Tigeot 	struct drm_property_blob *blob, *bt;
40074dbb207bSFrançois Tigeot 	struct drm_plane *plane, *plt;
40084dbb207bSFrançois Tigeot 
40094dbb207bSFrançois Tigeot 	list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
40104dbb207bSFrançois Tigeot 				 head) {
40114dbb207bSFrançois Tigeot 		encoder->funcs->destroy(encoder);
40124dbb207bSFrançois Tigeot 	}
40134dbb207bSFrançois Tigeot 
40144dbb207bSFrançois Tigeot 	list_for_each_entry_safe(connector, ot,
40154dbb207bSFrançois Tigeot 				 &dev->mode_config.connector_list, head) {
40164dbb207bSFrançois Tigeot 		connector->funcs->destroy(connector);
40174dbb207bSFrançois Tigeot 	}
40184dbb207bSFrançois Tigeot 
40194dbb207bSFrançois Tigeot 	list_for_each_entry_safe(property, pt, &dev->mode_config.property_list,
40204dbb207bSFrançois Tigeot 				 head) {
40214dbb207bSFrançois Tigeot 		drm_property_destroy(dev, property);
40224dbb207bSFrançois Tigeot 	}
40234dbb207bSFrançois Tigeot 
40244dbb207bSFrançois Tigeot 	list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list,
40254dbb207bSFrançois Tigeot 				 head) {
40264dbb207bSFrançois Tigeot 		drm_property_destroy_blob(dev, blob);
40274dbb207bSFrançois Tigeot 	}
40284dbb207bSFrançois Tigeot 
40294dbb207bSFrançois Tigeot 	/*
40304dbb207bSFrançois Tigeot 	 * Single-threaded teardown context, so it's not required to grab the
40314dbb207bSFrançois Tigeot 	 * fb_lock to protect against concurrent fb_list access. Contrary, it
40324dbb207bSFrançois Tigeot 	 * would actually deadlock with the drm_framebuffer_cleanup function.
40334dbb207bSFrançois Tigeot 	 *
40344dbb207bSFrançois Tigeot 	 * Also, if there are any framebuffers left, that's a driver leak now,
40354dbb207bSFrançois Tigeot 	 * so politely WARN about this.
40364dbb207bSFrançois Tigeot 	 */
40374dbb207bSFrançois Tigeot 	WARN_ON(!list_empty(&dev->mode_config.fb_list));
40384dbb207bSFrançois Tigeot 	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
40394dbb207bSFrançois Tigeot 		drm_framebuffer_remove(fb);
40404dbb207bSFrançois Tigeot 	}
40414dbb207bSFrançois Tigeot 
40424dbb207bSFrançois Tigeot 	list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
40434dbb207bSFrançois Tigeot 				 head) {
40444dbb207bSFrançois Tigeot 		plane->funcs->destroy(plane);
40454dbb207bSFrançois Tigeot 	}
40464dbb207bSFrançois Tigeot 
40474dbb207bSFrançois Tigeot 	list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
40484dbb207bSFrançois Tigeot 		crtc->funcs->destroy(crtc);
40494dbb207bSFrançois Tigeot 	}
40504dbb207bSFrançois Tigeot 
40514dbb207bSFrançois Tigeot 	idr_destroy(&dev->mode_config.crtc_idr);
40524dbb207bSFrançois Tigeot }
40534dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_cleanup);
4054