xref: /dflybsd-src/sys/dev/drm/drm_crtc.c (revision 83b4b9b96d578e400ab5890b64f60d69d4429e75)
15718399fSFrançois Tigeot /*
25718399fSFrançois Tigeot  * Copyright (c) 2006-2008 Intel Corporation
35718399fSFrançois Tigeot  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
45718399fSFrançois Tigeot  * Copyright (c) 2008 Red Hat Inc.
55718399fSFrançois Tigeot  *
65718399fSFrançois Tigeot  * DRM core CRTC related functions
75718399fSFrançois Tigeot  *
85718399fSFrançois Tigeot  * Permission to use, copy, modify, distribute, and sell this software and its
95718399fSFrançois Tigeot  * documentation for any purpose is hereby granted without fee, provided that
105718399fSFrançois Tigeot  * the above copyright notice appear in all copies and that both that copyright
115718399fSFrançois Tigeot  * notice and this permission notice appear in supporting documentation, and
125718399fSFrançois Tigeot  * that the name of the copyright holders not be used in advertising or
135718399fSFrançois Tigeot  * publicity pertaining to distribution of the software without specific,
145718399fSFrançois Tigeot  * written prior permission.  The copyright holders make no representations
155718399fSFrançois Tigeot  * about the suitability of this software for any purpose.  It is provided "as
165718399fSFrançois Tigeot  * is" without express or implied warranty.
175718399fSFrançois Tigeot  *
185718399fSFrançois Tigeot  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
195718399fSFrançois Tigeot  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
205718399fSFrançois Tigeot  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
215718399fSFrançois Tigeot  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
225718399fSFrançois Tigeot  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
235718399fSFrançois Tigeot  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
245718399fSFrançois Tigeot  * OF THIS SOFTWARE.
255718399fSFrançois Tigeot  *
265718399fSFrançois Tigeot  * Authors:
275718399fSFrançois Tigeot  *      Keith Packard
285718399fSFrançois Tigeot  *	Eric Anholt <eric@anholt.net>
295718399fSFrançois Tigeot  *      Dave Airlie <airlied@linux.ie>
305718399fSFrançois Tigeot  *      Jesse Barnes <jesse.barnes@intel.com>
315718399fSFrançois Tigeot  */
3206fede5aSFrançois Tigeot #include <linux/ctype.h>
33b5162e19SFrançois Tigeot #include <linux/list.h>
34b5162e19SFrançois Tigeot #include <linux/export.h>
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3718e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
38*83b4b9b9SFrançois Tigeot #include <drm/drm_fourcc.h>
39ba55f2f5SFrançois Tigeot #include <linux/slab.h>
40ba55f2f5SFrançois Tigeot #include <drm/drm_modeset_lock.h>
412c9916cdSFrançois Tigeot #include <drm/drm_atomic.h>
42ba55f2f5SFrançois Tigeot 
43ba55f2f5SFrançois Tigeot #include "drm_crtc_internal.h"
442c9916cdSFrançois Tigeot #include "drm_internal.h"
455718399fSFrançois Tigeot 
462c9916cdSFrançois Tigeot static struct drm_framebuffer *
472c9916cdSFrançois Tigeot internal_framebuffer_create(struct drm_device *dev,
48aee94f86SFrançois Tigeot 			    const struct drm_mode_fb_cmd2 *r,
4924edb884SFrançois Tigeot 			    struct drm_file *file_priv);
5024edb884SFrançois Tigeot 
515718399fSFrançois Tigeot /* Avoid boilerplate.  I'm tired of typing. */
525718399fSFrançois Tigeot #define DRM_ENUM_NAME_FN(fnname, list)				\
5306fede5aSFrançois Tigeot 	const char *fnname(int val)				\
545718399fSFrançois Tigeot 	{							\
555718399fSFrançois Tigeot 		int i;						\
56b5162e19SFrançois Tigeot 		for (i = 0; i < ARRAY_SIZE(list); i++) {	\
575718399fSFrançois Tigeot 			if (list[i].type == val)		\
585718399fSFrançois Tigeot 				return list[i].name;		\
595718399fSFrançois Tigeot 		}						\
605718399fSFrançois Tigeot 		return "(unknown)";				\
615718399fSFrançois Tigeot 	}
625718399fSFrançois Tigeot 
635718399fSFrançois Tigeot /*
645718399fSFrançois Tigeot  * Global properties
655718399fSFrançois Tigeot  */
662c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] = {
672c9916cdSFrançois Tigeot 	{ DRM_MODE_DPMS_ON, "On" },
685718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_STANDBY, "Standby" },
695718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_SUSPEND, "Suspend" },
705718399fSFrançois Tigeot 	{ DRM_MODE_DPMS_OFF, "Off" }
715718399fSFrançois Tigeot };
725718399fSFrançois Tigeot 
735718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
745718399fSFrançois Tigeot 
752c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_plane_type_enum_list[] = {
76ba55f2f5SFrançois Tigeot 	{ DRM_PLANE_TYPE_OVERLAY, "Overlay" },
77ba55f2f5SFrançois Tigeot 	{ DRM_PLANE_TYPE_PRIMARY, "Primary" },
78ba55f2f5SFrançois Tigeot 	{ DRM_PLANE_TYPE_CURSOR, "Cursor" },
79ba55f2f5SFrançois Tigeot };
80ba55f2f5SFrançois Tigeot 
815718399fSFrançois Tigeot /*
825718399fSFrançois Tigeot  * Optional properties
835718399fSFrançois Tigeot  */
842c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = {
855718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_NONE, "None" },
865718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_FULLSCREEN, "Full" },
875718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_CENTER, "Center" },
885718399fSFrançois Tigeot 	{ DRM_MODE_SCALE_ASPECT, "Full aspect" },
895718399fSFrançois Tigeot };
905718399fSFrançois Tigeot 
9124edb884SFrançois Tigeot static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
9224edb884SFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
9324edb884SFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
9424edb884SFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
9524edb884SFrançois Tigeot };
9624edb884SFrançois Tigeot 
975718399fSFrançois Tigeot /*
985718399fSFrançois Tigeot  * Non-global properties, but "required" for certain connectors.
995718399fSFrançois Tigeot  */
1002c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
1015718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
1025718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
1035718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
1045718399fSFrançois Tigeot };
1055718399fSFrançois Tigeot 
1065718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
1075718399fSFrançois Tigeot 
1082c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
1095718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
1105718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
1115718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
1125718399fSFrançois Tigeot };
1135718399fSFrançois Tigeot 
1145718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
1155718399fSFrançois Tigeot 		 drm_dvi_i_subconnector_enum_list)
1165718399fSFrançois Tigeot 
1172c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
1185718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
1195718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
1205718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
1215718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
1225718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
1235718399fSFrançois Tigeot };
1245718399fSFrançois Tigeot 
1255718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
1265718399fSFrançois Tigeot 
1272c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
1285718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
1295718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
1305718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
1315718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
1325718399fSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
1335718399fSFrançois Tigeot };
1345718399fSFrançois Tigeot 
1355718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
1365718399fSFrançois Tigeot 		 drm_tv_subconnector_enum_list)
1375718399fSFrançois Tigeot 
13806fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
1395718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_OFF,      "Off"      },
1405718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_ON,       "On"       },
1415718399fSFrançois Tigeot 	{ DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
1425718399fSFrançois Tigeot };
1435718399fSFrançois Tigeot 
1445718399fSFrançois Tigeot struct drm_conn_prop_enum_list {
1455718399fSFrançois Tigeot 	int type;
14606fede5aSFrançois Tigeot 	const char *name;
147917ec290SFrançois Tigeot 	struct ida ida;
148c7dca6a7SFrançois Tigeot 	int count;
1495718399fSFrançois Tigeot };
1505718399fSFrançois Tigeot 
1515718399fSFrançois Tigeot /*
1525718399fSFrançois Tigeot  * Connector and encoder types.
1535718399fSFrançois Tigeot  */
1542c9916cdSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
1552c9916cdSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
1569edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
1579edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
1589edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
1599edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
1609edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Composite, "Composite" },
1619edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
1629edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
1639edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Component, "Component" },
1649edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
1659edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
1669edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
1679edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
1689edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_TV, "TV" },
1699edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
1709edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
1719edbd4a0SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
1728621f407SFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DPI, "DPI" },
1735718399fSFrançois Tigeot };
1745718399fSFrançois Tigeot 
1752c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
1762c9916cdSFrançois Tigeot 	{ DRM_MODE_ENCODER_NONE, "None" },
1775718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_DAC, "DAC" },
1785718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_TMDS, "TMDS" },
1795718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_LVDS, "LVDS" },
1805718399fSFrançois Tigeot 	{ DRM_MODE_ENCODER_TVDAC, "TV" },
181b5162e19SFrançois Tigeot 	{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
1829edbd4a0SFrançois Tigeot 	{ DRM_MODE_ENCODER_DSI, "DSI" },
183ba55f2f5SFrançois Tigeot 	{ DRM_MODE_ENCODER_DPMST, "DP MST" },
1848621f407SFrançois Tigeot 	{ DRM_MODE_ENCODER_DPI, "DPI" },
1855718399fSFrançois Tigeot };
1865718399fSFrançois Tigeot 
1872c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
188ba55f2f5SFrançois Tigeot 	{ SubPixelUnknown, "Unknown" },
189ba55f2f5SFrançois Tigeot 	{ SubPixelHorizontalRGB, "Horizontal RGB" },
190ba55f2f5SFrançois Tigeot 	{ SubPixelHorizontalBGR, "Horizontal BGR" },
191ba55f2f5SFrançois Tigeot 	{ SubPixelVerticalRGB, "Vertical RGB" },
192ba55f2f5SFrançois Tigeot 	{ SubPixelVerticalBGR, "Vertical BGR" },
193ba55f2f5SFrançois Tigeot 	{ SubPixelNone, "None" },
194ba55f2f5SFrançois Tigeot };
1955718399fSFrançois Tigeot 
196ba55f2f5SFrançois Tigeot void drm_connector_ida_init(void)
1975718399fSFrançois Tigeot {
198ba55f2f5SFrançois Tigeot 	int i;
1995718399fSFrançois Tigeot 
200ba55f2f5SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
201ba55f2f5SFrançois Tigeot 		ida_init(&drm_connector_enum_list[i].ida);
2025718399fSFrançois Tigeot }
2035718399fSFrançois Tigeot 
204ba55f2f5SFrançois Tigeot void drm_connector_ida_destroy(void)
205ba55f2f5SFrançois Tigeot {
206ba55f2f5SFrançois Tigeot 	int i;
207ba55f2f5SFrançois Tigeot 
208ba55f2f5SFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
209ba55f2f5SFrançois Tigeot 		ida_destroy(&drm_connector_enum_list[i].ida);
210ba55f2f5SFrançois Tigeot }
211ba55f2f5SFrançois Tigeot 
212ba55f2f5SFrançois Tigeot /**
213ba55f2f5SFrançois Tigeot  * drm_get_connector_status_name - return a string for connector status
214ba55f2f5SFrançois Tigeot  * @status: connector status to compute name of
215ba55f2f5SFrançois Tigeot  *
216ba55f2f5SFrançois Tigeot  * In contrast to the other drm_get_*_name functions this one here returns a
217ba55f2f5SFrançois Tigeot  * const pointer and hence is threadsafe.
218ba55f2f5SFrançois Tigeot  */
21906fede5aSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status)
2205718399fSFrançois Tigeot {
2215718399fSFrançois Tigeot 	if (status == connector_status_connected)
2225718399fSFrançois Tigeot 		return "connected";
2235718399fSFrançois Tigeot 	else if (status == connector_status_disconnected)
2245718399fSFrançois Tigeot 		return "disconnected";
2255718399fSFrançois Tigeot 	else
2265718399fSFrançois Tigeot 		return "unknown";
2275718399fSFrançois Tigeot }
2284dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name);
2295718399fSFrançois Tigeot 
230ba55f2f5SFrançois Tigeot /**
231ba55f2f5SFrançois Tigeot  * drm_get_subpixel_order_name - return a string for a given subpixel enum
232ba55f2f5SFrançois Tigeot  * @order: enum of subpixel_order
233ba55f2f5SFrançois Tigeot  *
234ba55f2f5SFrançois Tigeot  * Note you could abuse this and return something out of bounds, but that
235ba55f2f5SFrançois Tigeot  * would be a caller error.  No unscrubbed user data should make it here.
236ba55f2f5SFrançois Tigeot  */
237ba55f2f5SFrançois Tigeot const char *drm_get_subpixel_order_name(enum subpixel_order order)
238ba55f2f5SFrançois Tigeot {
239ba55f2f5SFrançois Tigeot 	return drm_subpixel_enum_list[order].name;
240ba55f2f5SFrançois Tigeot }
241ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_subpixel_order_name);
242ba55f2f5SFrançois Tigeot 
24306fede5aSFrançois Tigeot static char printable_char(int c)
24406fede5aSFrançois Tigeot {
24506fede5aSFrançois Tigeot 	return isascii(c) && isprint(c) ? c : '?';
24606fede5aSFrançois Tigeot }
24706fede5aSFrançois Tigeot 
248ba55f2f5SFrançois Tigeot /**
249ba55f2f5SFrançois Tigeot  * drm_get_format_name - return a string for drm fourcc format
250ba55f2f5SFrançois Tigeot  * @format: format to compute name of
251ba55f2f5SFrançois Tigeot  *
252ba55f2f5SFrançois Tigeot  * Note that the buffer used by this function is globally shared and owned by
253ba55f2f5SFrançois Tigeot  * the function itself.
254ba55f2f5SFrançois Tigeot  *
255ba55f2f5SFrançois Tigeot  * FIXME: This isn't really multithreading safe.
256ba55f2f5SFrançois Tigeot  */
25706fede5aSFrançois Tigeot const char *drm_get_format_name(uint32_t format)
25806fede5aSFrançois Tigeot {
25906fede5aSFrançois Tigeot 	static char buf[32];
26006fede5aSFrançois Tigeot 
26106fede5aSFrançois Tigeot 	ksnprintf(buf, sizeof(buf),
26206fede5aSFrançois Tigeot 		 "%c%c%c%c %s-endian (0x%08x)",
26306fede5aSFrançois Tigeot 		 printable_char(format & 0xff),
26406fede5aSFrançois Tigeot 		 printable_char((format >> 8) & 0xff),
26506fede5aSFrançois Tigeot 		 printable_char((format >> 16) & 0xff),
26606fede5aSFrançois Tigeot 		 printable_char((format >> 24) & 0x7f),
26706fede5aSFrançois Tigeot 		 format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little",
26806fede5aSFrançois Tigeot 		 format);
26906fede5aSFrançois Tigeot 
27006fede5aSFrançois Tigeot 	return buf;
27106fede5aSFrançois Tigeot }
27206fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_get_format_name);
27306fede5aSFrançois Tigeot 
2742c9916cdSFrançois Tigeot /*
2752c9916cdSFrançois Tigeot  * Internal function to assign a slot in the object idr and optionally
2762c9916cdSFrançois Tigeot  * register the object into the idr.
2772c9916cdSFrançois Tigeot  */
2782c9916cdSFrançois Tigeot static int drm_mode_object_get_reg(struct drm_device *dev,
2792c9916cdSFrançois Tigeot 				   struct drm_mode_object *obj,
2802c9916cdSFrançois Tigeot 				   uint32_t obj_type,
2818621f407SFrançois Tigeot 				   bool register_obj,
2828621f407SFrançois Tigeot 				   void (*obj_free_cb)(struct kref *kref))
2832c9916cdSFrançois Tigeot {
2842c9916cdSFrançois Tigeot 	int ret;
2852c9916cdSFrançois Tigeot 
2862c9916cdSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
2872c9916cdSFrançois Tigeot 	ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
2882c9916cdSFrançois Tigeot 	if (ret >= 0) {
2892c9916cdSFrançois Tigeot 		/*
2902c9916cdSFrançois Tigeot 		 * Set up the object linking under the protection of the idr
2912c9916cdSFrançois Tigeot 		 * lock so that other users can't see inconsistent state.
2922c9916cdSFrançois Tigeot 		 */
2932c9916cdSFrançois Tigeot 		obj->id = ret;
2942c9916cdSFrançois Tigeot 		obj->type = obj_type;
2958621f407SFrançois Tigeot 		if (obj_free_cb) {
2968621f407SFrançois Tigeot 			obj->free_cb = obj_free_cb;
2978621f407SFrançois Tigeot 			kref_init(&obj->refcount);
2988621f407SFrançois Tigeot 		}
2992c9916cdSFrançois Tigeot 	}
3002c9916cdSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
3012c9916cdSFrançois Tigeot 
3022c9916cdSFrançois Tigeot 	return ret < 0 ? ret : 0;
3032c9916cdSFrançois Tigeot }
3042c9916cdSFrançois Tigeot 
3055718399fSFrançois Tigeot /**
3064dbb207bSFrançois Tigeot  * drm_mode_object_get - allocate a new modeset identifier
3075718399fSFrançois Tigeot  * @dev: DRM device
3084dbb207bSFrançois Tigeot  * @obj: object pointer, used to generate unique ID
3094dbb207bSFrançois Tigeot  * @obj_type: object type
3105718399fSFrançois Tigeot  *
3115718399fSFrançois Tigeot  * Create a unique identifier based on @ptr in @dev's identifier space.  Used
312ba55f2f5SFrançois Tigeot  * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
313ba55f2f5SFrançois Tigeot  * modeset identifiers are _not_ reference counted. Hence don't use this for
314ba55f2f5SFrançois Tigeot  * reference counted modeset objects like framebuffers.
3155718399fSFrançois Tigeot  *
316ba55f2f5SFrançois Tigeot  * Returns:
317352ff8bdSFrançois Tigeot  * Zero on success, error code on failure.
3185718399fSFrançois Tigeot  */
319ba55f2f5SFrançois Tigeot int drm_mode_object_get(struct drm_device *dev,
3205718399fSFrançois Tigeot 			struct drm_mode_object *obj, uint32_t obj_type)
3215718399fSFrançois Tigeot {
3228621f407SFrançois Tigeot 	return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL);
3239f0f5970SFrançois Tigeot }
3249f0f5970SFrançois Tigeot 
3252c9916cdSFrançois Tigeot static void drm_mode_object_register(struct drm_device *dev,
3262c9916cdSFrançois Tigeot 				     struct drm_mode_object *obj)
3272c9916cdSFrançois Tigeot {
3282c9916cdSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
3292c9916cdSFrançois Tigeot 	idr_replace(&dev->mode_config.crtc_idr, obj, obj->id);
3302c9916cdSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
3315718399fSFrançois Tigeot }
3325718399fSFrançois Tigeot 
3335718399fSFrançois Tigeot /**
3348621f407SFrançois Tigeot  * drm_mode_object_unregister - free a modeset identifer
3355718399fSFrançois Tigeot  * @dev: DRM device
3364dbb207bSFrançois Tigeot  * @object: object to free
3375718399fSFrançois Tigeot  *
3388621f407SFrançois Tigeot  * Free @id from @dev's unique identifier pool.
3398621f407SFrançois Tigeot  * This function can be called multiple times, and guards against
3408621f407SFrançois Tigeot  * multiple removals.
3418621f407SFrançois Tigeot  * These modeset identifiers are _not_ reference counted. Hence don't use this
342ba55f2f5SFrançois Tigeot  * for reference counted modeset objects like framebuffers.
3435718399fSFrançois Tigeot  */
3448621f407SFrançois Tigeot void drm_mode_object_unregister(struct drm_device *dev,
3455718399fSFrançois Tigeot 			 struct drm_mode_object *object)
3465718399fSFrançois Tigeot {
3474dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
3488621f407SFrançois Tigeot 	if (object->id) {
349aea8bdbdSFrançois Tigeot 		idr_remove(&dev->mode_config.crtc_idr, object->id);
3508621f407SFrançois Tigeot 		object->id = 0;
3518621f407SFrançois Tigeot 	}
3524dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
3535718399fSFrançois Tigeot }
3545718399fSFrançois Tigeot 
355ba55f2f5SFrançois Tigeot static struct drm_mode_object *_object_find(struct drm_device *dev,
356ba55f2f5SFrançois Tigeot 		uint32_t id, uint32_t type)
357ba55f2f5SFrançois Tigeot {
358ba55f2f5SFrançois Tigeot 	struct drm_mode_object *obj = NULL;
359ba55f2f5SFrançois Tigeot 
360ba55f2f5SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
361ba55f2f5SFrançois Tigeot 	obj = idr_find(&dev->mode_config.crtc_idr, id);
36224edb884SFrançois Tigeot 	if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
36324edb884SFrançois Tigeot 		obj = NULL;
36424edb884SFrançois Tigeot 	if (obj && obj->id != id)
36524edb884SFrançois Tigeot 		obj = NULL;
3668621f407SFrançois Tigeot 
3678621f407SFrançois Tigeot 	if (obj && obj->free_cb) {
3688621f407SFrançois Tigeot 		if (!kref_get_unless_zero(&obj->refcount))
369ba55f2f5SFrançois Tigeot 			obj = NULL;
3708621f407SFrançois Tigeot 	}
371ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
372ba55f2f5SFrançois Tigeot 
373ba55f2f5SFrançois Tigeot 	return obj;
374ba55f2f5SFrançois Tigeot }
375ba55f2f5SFrançois Tigeot 
3764dbb207bSFrançois Tigeot /**
3774dbb207bSFrançois Tigeot  * drm_mode_object_find - look up a drm object with static lifetime
3784dbb207bSFrançois Tigeot  * @dev: drm device
3794dbb207bSFrançois Tigeot  * @id: id of the mode object
3804dbb207bSFrançois Tigeot  * @type: type of the mode object
3814dbb207bSFrançois Tigeot  *
3828621f407SFrançois Tigeot  * This function is used to look up a modeset object. It will acquire a
3838621f407SFrançois Tigeot  * reference for reference counted objects. This reference must be dropped again
3848621f407SFrançois Tigeot  * by callind drm_mode_object_unreference().
3854dbb207bSFrançois Tigeot  */
3865718399fSFrançois Tigeot struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
3875718399fSFrançois Tigeot 		uint32_t id, uint32_t type)
3885718399fSFrançois Tigeot {
389aea8bdbdSFrançois Tigeot 	struct drm_mode_object *obj = NULL;
3905718399fSFrançois Tigeot 
391ba55f2f5SFrançois Tigeot 	obj = _object_find(dev, id, type);
3925718399fSFrançois Tigeot 	return obj;
3935718399fSFrançois Tigeot }
394b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_find);
3955718399fSFrançois Tigeot 
3965718399fSFrançois Tigeot /**
3978621f407SFrançois Tigeot  * drm_mode_object_unreference - decr the object refcnt
3988621f407SFrançois Tigeot  * @obj: mode_object
3998621f407SFrançois Tigeot  *
4008621f407SFrançois Tigeot  * This functions decrements the object's refcount if it is a refcounted modeset
4018621f407SFrançois Tigeot  * object. It is a no-op on any other object. This is used to drop references
4028621f407SFrançois Tigeot  * acquired with drm_mode_object_reference().
4038621f407SFrançois Tigeot  */
4048621f407SFrançois Tigeot void drm_mode_object_unreference(struct drm_mode_object *obj)
4058621f407SFrançois Tigeot {
4068621f407SFrançois Tigeot 	if (obj->free_cb) {
4078621f407SFrançois Tigeot 		DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
4088621f407SFrançois Tigeot 		kref_put(&obj->refcount, obj->free_cb);
4098621f407SFrançois Tigeot 	}
4108621f407SFrançois Tigeot }
4118621f407SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_unreference);
4128621f407SFrançois Tigeot 
4138621f407SFrançois Tigeot /**
4148621f407SFrançois Tigeot  * drm_mode_object_reference - incr the object refcnt
4158621f407SFrançois Tigeot  * @obj: mode_object
4168621f407SFrançois Tigeot  *
4178621f407SFrançois Tigeot  * This functions increments the object's refcount if it is a refcounted modeset
4188621f407SFrançois Tigeot  * object. It is a no-op on any other object. References should be dropped again
4198621f407SFrançois Tigeot  * by calling drm_mode_object_unreference().
4208621f407SFrançois Tigeot  */
4218621f407SFrançois Tigeot void drm_mode_object_reference(struct drm_mode_object *obj)
4228621f407SFrançois Tigeot {
4238621f407SFrançois Tigeot 	if (obj->free_cb) {
4248621f407SFrançois Tigeot 		DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
4258621f407SFrançois Tigeot 		kref_get(&obj->refcount);
4268621f407SFrançois Tigeot 	}
4278621f407SFrançois Tigeot }
4288621f407SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_reference);
4298621f407SFrançois Tigeot 
4308621f407SFrançois Tigeot static void drm_framebuffer_free(struct kref *kref)
4318621f407SFrançois Tigeot {
4328621f407SFrançois Tigeot 	struct drm_framebuffer *fb =
4338621f407SFrançois Tigeot 			container_of(kref, struct drm_framebuffer, base.refcount);
4348621f407SFrançois Tigeot 	struct drm_device *dev = fb->dev;
4358621f407SFrançois Tigeot 
4368621f407SFrançois Tigeot 	/*
4378621f407SFrançois Tigeot 	 * The lookup idr holds a weak reference, which has not necessarily been
4388621f407SFrançois Tigeot 	 * removed at this point. Check for that.
4398621f407SFrançois Tigeot 	 */
4408621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &fb->base);
4418621f407SFrançois Tigeot 
4428621f407SFrançois Tigeot 	fb->funcs->destroy(fb);
4438621f407SFrançois Tigeot }
4448621f407SFrançois Tigeot 
4458621f407SFrançois Tigeot /**
4465718399fSFrançois Tigeot  * drm_framebuffer_init - initialize a framebuffer
4475718399fSFrançois Tigeot  * @dev: DRM device
4484dbb207bSFrançois Tigeot  * @fb: framebuffer to be initialized
4494dbb207bSFrançois Tigeot  * @funcs: ... with these functions
4505718399fSFrançois Tigeot  *
4515718399fSFrançois Tigeot  * Allocates an ID for the framebuffer's parent mode object, sets its mode
4525718399fSFrançois Tigeot  * functions & device file and adds it to the master fd list.
4535718399fSFrançois Tigeot  *
4544dbb207bSFrançois Tigeot  * IMPORTANT:
4554dbb207bSFrançois Tigeot  * This functions publishes the fb and makes it available for concurrent access
4564dbb207bSFrançois Tigeot  * by other users. Which means by this point the fb _must_ be fully set up -
4574dbb207bSFrançois Tigeot  * since all the fb attributes are invariant over its lifetime, no further
4584dbb207bSFrançois Tigeot  * locking but only correct reference counting is required.
4594dbb207bSFrançois Tigeot  *
460ba55f2f5SFrançois Tigeot  * Returns:
4615718399fSFrançois Tigeot  * Zero on success, error code on failure.
4625718399fSFrançois Tigeot  */
4635718399fSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
4645718399fSFrançois Tigeot 			 const struct drm_framebuffer_funcs *funcs)
4655718399fSFrançois Tigeot {
4665718399fSFrançois Tigeot 	int ret;
4675718399fSFrançois Tigeot 
4684dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&fb->filp_head);
4694dbb207bSFrançois Tigeot 	fb->dev = dev;
4704dbb207bSFrançois Tigeot 	fb->funcs = funcs;
471b5162e19SFrançois Tigeot 
4728621f407SFrançois Tigeot 	ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB,
4738621f407SFrançois Tigeot 				      false, drm_framebuffer_free);
4745718399fSFrançois Tigeot 	if (ret)
4754dbb207bSFrançois Tigeot 		goto out;
4765718399fSFrançois Tigeot 
4778621f407SFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
4785718399fSFrançois Tigeot 	dev->mode_config.num_fb++;
4795718399fSFrançois Tigeot 	list_add(&fb->head, &dev->mode_config.fb_list);
4804dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
4815718399fSFrançois Tigeot 
4828621f407SFrançois Tigeot 	drm_mode_object_register(dev, &fb->base);
4838621f407SFrançois Tigeot out:
484352ff8bdSFrançois Tigeot 	return ret;
4855718399fSFrançois Tigeot }
486b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init);
487b5162e19SFrançois Tigeot 
4884dbb207bSFrançois Tigeot /**
4894dbb207bSFrançois Tigeot  * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
4904dbb207bSFrançois Tigeot  * @dev: drm device
4914dbb207bSFrançois Tigeot  * @id: id of the fb object
4924dbb207bSFrançois Tigeot  *
4934dbb207bSFrançois Tigeot  * If successful, this grabs an additional reference to the framebuffer -
4944dbb207bSFrançois Tigeot  * callers need to make sure to eventually unreference the returned framebuffer
495ba55f2f5SFrançois Tigeot  * again, using @drm_framebuffer_unreference.
4964dbb207bSFrançois Tigeot  */
4974dbb207bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
4984dbb207bSFrançois Tigeot 					       uint32_t id)
4994dbb207bSFrançois Tigeot {
5008621f407SFrançois Tigeot 	struct drm_mode_object *obj;
5018621f407SFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
5024dbb207bSFrançois Tigeot 
5038621f407SFrançois Tigeot 	obj = _object_find(dev, id, DRM_MODE_OBJECT_FB);
5048621f407SFrançois Tigeot 	if (obj)
5058621f407SFrançois Tigeot 		fb = obj_to_fb(obj);
5064dbb207bSFrançois Tigeot 	return fb;
5074dbb207bSFrançois Tigeot }
5084dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup);
5094dbb207bSFrançois Tigeot 
510b5162e19SFrançois Tigeot /**
511a2fdbec6SFrançois Tigeot  * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
512a2fdbec6SFrançois Tigeot  * @fb: fb to unregister
513a2fdbec6SFrançois Tigeot  *
514a2fdbec6SFrançois Tigeot  * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
515a2fdbec6SFrançois Tigeot  * those used for fbdev. Note that the caller must hold a reference of it's own,
516a2fdbec6SFrançois Tigeot  * i.e. the object may not be destroyed through this call (since it'll lead to a
517a2fdbec6SFrançois Tigeot  * locking inversion).
518a2fdbec6SFrançois Tigeot  */
519a2fdbec6SFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
520a2fdbec6SFrançois Tigeot {
521352ff8bdSFrançois Tigeot 	struct drm_device *dev;
522352ff8bdSFrançois Tigeot 
523352ff8bdSFrançois Tigeot 	if (!fb)
524352ff8bdSFrançois Tigeot 		return;
525352ff8bdSFrançois Tigeot 
526352ff8bdSFrançois Tigeot 	dev = fb->dev;
527a2fdbec6SFrançois Tigeot 
528a2fdbec6SFrançois Tigeot 	/* Mark fb as reaped and drop idr ref. */
5298621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &fb->base);
530a2fdbec6SFrançois Tigeot }
531a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private);
532a2fdbec6SFrançois Tigeot 
5335718399fSFrançois Tigeot /**
5345718399fSFrançois Tigeot  * drm_framebuffer_cleanup - remove a framebuffer object
5355718399fSFrançois Tigeot  * @fb: framebuffer to remove
5365718399fSFrançois Tigeot  *
537ba55f2f5SFrançois Tigeot  * Cleanup framebuffer. This function is intended to be used from the drivers
538ba55f2f5SFrançois Tigeot  * ->destroy callback. It can also be used to clean up driver private
539ba55f2f5SFrançois Tigeot  *  framebuffers embedded into a larger structure.
5405718399fSFrançois Tigeot  *
5414dbb207bSFrançois Tigeot  * Note that this function does not remove the fb from active usuage - if it is
5424dbb207bSFrançois Tigeot  * still used anywhere, hilarity can ensue since userspace could call getfb on
5434dbb207bSFrançois Tigeot  * the id and get back -EINVAL. Obviously no concern at driver unload time.
5444dbb207bSFrançois Tigeot  *
5454dbb207bSFrançois Tigeot  * Also, the framebuffer will not be removed from the lookup idr - for
5464dbb207bSFrançois Tigeot  * user-created framebuffers this will happen in in the rmfb ioctl. For
5474dbb207bSFrançois Tigeot  * driver-private objects (e.g. for fbdev) drivers need to explicitly call
5484dbb207bSFrançois Tigeot  * drm_framebuffer_unregister_private.
5495718399fSFrançois Tigeot  */
5505718399fSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
5515718399fSFrançois Tigeot {
5525718399fSFrançois Tigeot 	struct drm_device *dev = fb->dev;
5534dbb207bSFrançois Tigeot 
5544dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.fb_lock);
555b5162e19SFrançois Tigeot 	list_del(&fb->head);
556b5162e19SFrançois Tigeot 	dev->mode_config.num_fb--;
5574dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.fb_lock);
558b5162e19SFrançois Tigeot }
559b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup);
560b5162e19SFrançois Tigeot 
561b5162e19SFrançois Tigeot /**
562b5162e19SFrançois Tigeot  * drm_framebuffer_remove - remove and unreference a framebuffer object
563b5162e19SFrançois Tigeot  * @fb: framebuffer to remove
564b5162e19SFrançois Tigeot  *
565b5162e19SFrançois Tigeot  * Scans all the CRTCs and planes in @dev's mode_config.  If they're
5664dbb207bSFrançois Tigeot  * using @fb, removes it, setting it to NULL. Then drops the reference to the
5674dbb207bSFrançois Tigeot  * passed-in framebuffer. Might take the modeset locks.
5684dbb207bSFrançois Tigeot  *
5694dbb207bSFrançois Tigeot  * Note that this function optimizes the cleanup away if the caller holds the
5704dbb207bSFrançois Tigeot  * last reference to the framebuffer. It is also guaranteed to not take the
5714dbb207bSFrançois Tigeot  * modeset locks in this case.
572b5162e19SFrançois Tigeot  */
573b5162e19SFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb)
574b5162e19SFrançois Tigeot {
575352ff8bdSFrançois Tigeot 	struct drm_device *dev;
5765718399fSFrançois Tigeot 	struct drm_crtc *crtc;
5775718399fSFrançois Tigeot 	struct drm_plane *plane;
5785718399fSFrançois Tigeot 	struct drm_mode_set set;
5795718399fSFrançois Tigeot 	int ret;
5805718399fSFrançois Tigeot 
581352ff8bdSFrançois Tigeot 	if (!fb)
582352ff8bdSFrançois Tigeot 		return;
583352ff8bdSFrançois Tigeot 
584352ff8bdSFrançois Tigeot 	dev = fb->dev;
585352ff8bdSFrançois Tigeot 
5864dbb207bSFrançois Tigeot 	WARN_ON(!list_empty(&fb->filp_head));
5874dbb207bSFrançois Tigeot 
5884dbb207bSFrançois Tigeot 	/*
5894dbb207bSFrançois Tigeot 	 * drm ABI mandates that we remove any deleted framebuffers from active
5904dbb207bSFrançois Tigeot 	 * useage. But since most sane clients only remove framebuffers they no
5914dbb207bSFrançois Tigeot 	 * longer need, try to optimize this away.
5924dbb207bSFrançois Tigeot 	 *
5934dbb207bSFrançois Tigeot 	 * Since we're holding a reference ourselves, observing a refcount of 1
5944dbb207bSFrançois Tigeot 	 * means that we're the last holder and can skip it. Also, the refcount
5954dbb207bSFrançois Tigeot 	 * can never increase from 1 again, so we don't need any barriers or
5964dbb207bSFrançois Tigeot 	 * locks.
5974dbb207bSFrançois Tigeot 	 *
5984dbb207bSFrançois Tigeot 	 * Note that userspace could try to race with use and instate a new
5994dbb207bSFrançois Tigeot 	 * usage _after_ we've cleared all current ones. End result will be an
6004dbb207bSFrançois Tigeot 	 * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
6014dbb207bSFrançois Tigeot 	 * in this manner.
6024dbb207bSFrançois Tigeot 	 */
6038621f407SFrançois Tigeot 	if (drm_framebuffer_read_refcount(fb) > 1) {
6044dbb207bSFrançois Tigeot 		drm_modeset_lock_all(dev);
6055718399fSFrançois Tigeot 		/* remove from any CRTC */
606a05eeebfSFrançois Tigeot 		drm_for_each_crtc(crtc, dev) {
607ba55f2f5SFrançois Tigeot 			if (crtc->primary->fb == fb) {
6085718399fSFrançois Tigeot 				/* should turn off the crtc */
6095718399fSFrançois Tigeot 				memset(&set, 0, sizeof(struct drm_mode_set));
6105718399fSFrançois Tigeot 				set.crtc = crtc;
6115718399fSFrançois Tigeot 				set.fb = NULL;
6124dbb207bSFrançois Tigeot 				ret = drm_mode_set_config_internal(&set);
6135718399fSFrançois Tigeot 				if (ret)
6145718399fSFrançois Tigeot 					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
6155718399fSFrançois Tigeot 			}
6165718399fSFrançois Tigeot 		}
6175718399fSFrançois Tigeot 
618a05eeebfSFrançois Tigeot 		drm_for_each_plane(plane, dev) {
61906fede5aSFrançois Tigeot 			if (plane->fb == fb)
62006fede5aSFrançois Tigeot 				drm_plane_force_disable(plane);
6215718399fSFrançois Tigeot 		}
6224dbb207bSFrançois Tigeot 		drm_modeset_unlock_all(dev);
6234dbb207bSFrançois Tigeot 	}
624b5162e19SFrançois Tigeot 
625b5162e19SFrançois Tigeot 	drm_framebuffer_unreference(fb);
6265718399fSFrançois Tigeot }
627b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove);
6285718399fSFrançois Tigeot 
629ba55f2f5SFrançois Tigeot DEFINE_WW_CLASS(crtc_ww_class);
630ba55f2f5SFrançois Tigeot 
631aee94f86SFrançois Tigeot static unsigned int drm_num_crtcs(struct drm_device *dev)
632aee94f86SFrançois Tigeot {
633aee94f86SFrançois Tigeot 	unsigned int num = 0;
634aee94f86SFrançois Tigeot 	struct drm_crtc *tmp;
635aee94f86SFrançois Tigeot 
636aee94f86SFrançois Tigeot 	drm_for_each_crtc(tmp, dev) {
637aee94f86SFrançois Tigeot 		num++;
638aee94f86SFrançois Tigeot 	}
639aee94f86SFrançois Tigeot 
640aee94f86SFrançois Tigeot 	return num;
641aee94f86SFrançois Tigeot }
642aee94f86SFrançois Tigeot 
6435718399fSFrançois Tigeot /**
644ba55f2f5SFrançois Tigeot  * drm_crtc_init_with_planes - Initialise a new CRTC object with
645ba55f2f5SFrançois Tigeot  *    specified primary and cursor planes.
6465718399fSFrançois Tigeot  * @dev: DRM device
6475718399fSFrançois Tigeot  * @crtc: CRTC object to init
648ba55f2f5SFrançois Tigeot  * @primary: Primary plane for CRTC
649ba55f2f5SFrançois Tigeot  * @cursor: Cursor plane for CRTC
6505718399fSFrançois Tigeot  * @funcs: callbacks for the new CRTC
651aee94f86SFrançois Tigeot  * @name: printf style format string for the CRTC name, or NULL for default name
6525718399fSFrançois Tigeot  *
65306fede5aSFrançois Tigeot  * Inits a new object created as base part of a driver crtc object.
6545718399fSFrançois Tigeot  *
655ba55f2f5SFrançois Tigeot  * Returns:
6565718399fSFrançois Tigeot  * Zero on success, error code on failure.
6575718399fSFrançois Tigeot  */
658ba55f2f5SFrançois Tigeot int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
659ba55f2f5SFrançois Tigeot 			      struct drm_plane *primary,
66024edb884SFrançois Tigeot 			      struct drm_plane *cursor,
661aee94f86SFrançois Tigeot 			      const struct drm_crtc_funcs *funcs,
662aee94f86SFrançois Tigeot 			      const char *name, ...)
6635718399fSFrançois Tigeot {
664ba55f2f5SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
6655718399fSFrançois Tigeot 	int ret;
6665718399fSFrançois Tigeot 
667477eb7f9SFrançois Tigeot 	WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY);
668477eb7f9SFrançois Tigeot 	WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR);
669477eb7f9SFrançois Tigeot 
6705718399fSFrançois Tigeot 	crtc->dev = dev;
6715718399fSFrançois Tigeot 	crtc->funcs = funcs;
6725718399fSFrançois Tigeot 
673ba55f2f5SFrançois Tigeot 	drm_modeset_lock_init(&crtc->mutex);
6745718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
6755718399fSFrançois Tigeot 	if (ret)
67619c468b4SFrançois Tigeot 		return ret;
6775718399fSFrançois Tigeot 
678aee94f86SFrançois Tigeot 	if (name) {
679aee94f86SFrançois Tigeot 		__va_list ap;
680aee94f86SFrançois Tigeot 
681aee94f86SFrançois Tigeot 		__va_start(ap, name);
682aee94f86SFrançois Tigeot 		crtc->name = kvasprintf(GFP_KERNEL, name, ap);
683aee94f86SFrançois Tigeot 		__va_end(ap);
684aee94f86SFrançois Tigeot 	} else {
685aee94f86SFrançois Tigeot 		crtc->name = kasprintf(GFP_KERNEL, "crtc-%d",
686aee94f86SFrançois Tigeot 				       drm_num_crtcs(dev));
687aee94f86SFrançois Tigeot 	}
688aee94f86SFrançois Tigeot 	if (!crtc->name) {
6898621f407SFrançois Tigeot 		drm_mode_object_unregister(dev, &crtc->base);
690aee94f86SFrançois Tigeot 		return -ENOMEM;
691aee94f86SFrançois Tigeot 	}
692aee94f86SFrançois Tigeot 
693b5162e19SFrançois Tigeot 	crtc->base.properties = &crtc->properties;
694b5162e19SFrançois Tigeot 
695ba55f2f5SFrançois Tigeot 	list_add_tail(&crtc->head, &config->crtc_list);
696ba55f2f5SFrançois Tigeot 	config->num_crtc++;
697ba55f2f5SFrançois Tigeot 
698ba55f2f5SFrançois Tigeot 	crtc->primary = primary;
69924edb884SFrançois Tigeot 	crtc->cursor = cursor;
700ba55f2f5SFrançois Tigeot 	if (primary)
701ba55f2f5SFrançois Tigeot 		primary->possible_crtcs = 1 << drm_crtc_index(crtc);
70224edb884SFrançois Tigeot 	if (cursor)
70324edb884SFrançois Tigeot 		cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
704b5162e19SFrançois Tigeot 
70519c468b4SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
70619c468b4SFrançois Tigeot 		drm_object_attach_property(&crtc->base, config->prop_active, 0);
70719c468b4SFrançois Tigeot 		drm_object_attach_property(&crtc->base, config->prop_mode_id, 0);
70819c468b4SFrançois Tigeot 	}
7095718399fSFrançois Tigeot 
71019c468b4SFrançois Tigeot 	return 0;
7115718399fSFrançois Tigeot }
712ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init_with_planes);
7135718399fSFrançois Tigeot 
7145718399fSFrançois Tigeot /**
71506fede5aSFrançois Tigeot  * drm_crtc_cleanup - Clean up the core crtc usage
7165718399fSFrançois Tigeot  * @crtc: CRTC to cleanup
7175718399fSFrançois Tigeot  *
71806fede5aSFrançois Tigeot  * This function cleans up @crtc and removes it from the DRM mode setting
71906fede5aSFrançois Tigeot  * core. Note that the function does *not* free the crtc structure itself,
72006fede5aSFrançois Tigeot  * this is the responsibility of the caller.
7215718399fSFrançois Tigeot  */
7225718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc)
7235718399fSFrançois Tigeot {
7245718399fSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
7255718399fSFrançois Tigeot 
7264dbb207bSFrançois Tigeot 	kfree(crtc->gamma_store);
7275718399fSFrançois Tigeot 	crtc->gamma_store = NULL;
7285718399fSFrançois Tigeot 
729ba55f2f5SFrançois Tigeot 	drm_modeset_lock_fini(&crtc->mutex);
730ba55f2f5SFrançois Tigeot 
7318621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &crtc->base);
7325718399fSFrançois Tigeot 	list_del(&crtc->head);
7335718399fSFrançois Tigeot 	dev->mode_config.num_crtc--;
7342c9916cdSFrançois Tigeot 
7352c9916cdSFrançois Tigeot 	WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state);
7362c9916cdSFrançois Tigeot 	if (crtc->state && crtc->funcs->atomic_destroy_state)
7372c9916cdSFrançois Tigeot 		crtc->funcs->atomic_destroy_state(crtc, crtc->state);
7382c9916cdSFrançois Tigeot 
739aee94f86SFrançois Tigeot 	kfree(crtc->name);
740aee94f86SFrançois Tigeot 
7412c9916cdSFrançois Tigeot 	memset(crtc, 0, sizeof(*crtc));
7425718399fSFrançois Tigeot }
743b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup);
7445718399fSFrançois Tigeot 
7455718399fSFrançois Tigeot /**
7469edbd4a0SFrançois Tigeot  * drm_crtc_index - find the index of a registered CRTC
7479edbd4a0SFrançois Tigeot  * @crtc: CRTC to find index for
7489edbd4a0SFrançois Tigeot  *
7499edbd4a0SFrançois Tigeot  * Given a registered CRTC, return the index of that CRTC within a DRM
7509edbd4a0SFrançois Tigeot  * device's list of CRTCs.
7519edbd4a0SFrançois Tigeot  */
7529edbd4a0SFrançois Tigeot unsigned int drm_crtc_index(struct drm_crtc *crtc)
7539edbd4a0SFrançois Tigeot {
7549edbd4a0SFrançois Tigeot 	unsigned int index = 0;
7559edbd4a0SFrançois Tigeot 	struct drm_crtc *tmp;
7569edbd4a0SFrançois Tigeot 
757a05eeebfSFrançois Tigeot 	drm_for_each_crtc(tmp, crtc->dev) {
7589edbd4a0SFrançois Tigeot 		if (tmp == crtc)
7599edbd4a0SFrançois Tigeot 			return index;
7609edbd4a0SFrançois Tigeot 
7619edbd4a0SFrançois Tigeot 		index++;
7629edbd4a0SFrançois Tigeot 	}
7639edbd4a0SFrançois Tigeot 
7649edbd4a0SFrançois Tigeot 	BUG();
7659edbd4a0SFrançois Tigeot }
7669edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_index);
7679edbd4a0SFrançois Tigeot 
7689edbd4a0SFrançois Tigeot /*
7695718399fSFrançois Tigeot  * drm_mode_remove - remove and free a mode
7705718399fSFrançois Tigeot  * @connector: connector list to modify
7715718399fSFrançois Tigeot  * @mode: mode to remove
7725718399fSFrançois Tigeot  *
7735718399fSFrançois Tigeot  * Remove @mode from @connector's mode list, then free it.
7745718399fSFrançois Tigeot  */
7759edbd4a0SFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector,
7765718399fSFrançois Tigeot 			    struct drm_display_mode *mode)
7775718399fSFrançois Tigeot {
7785718399fSFrançois Tigeot 	list_del(&mode->head);
7795718399fSFrançois Tigeot 	drm_mode_destroy(connector->dev, mode);
7805718399fSFrançois Tigeot }
7815718399fSFrançois Tigeot 
7825718399fSFrançois Tigeot /**
7832c9916cdSFrançois Tigeot  * drm_display_info_set_bus_formats - set the supported bus formats
7842c9916cdSFrançois Tigeot  * @info: display info to store bus formats in
785917ec290SFrançois Tigeot  * @formats: array containing the supported bus formats
786917ec290SFrançois Tigeot  * @num_formats: the number of entries in the fmts array
7872c9916cdSFrançois Tigeot  *
7882c9916cdSFrançois Tigeot  * Store the supported bus formats in display info structure.
7892c9916cdSFrançois Tigeot  * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
7902c9916cdSFrançois Tigeot  * a full list of available formats.
7912c9916cdSFrançois Tigeot  */
7922c9916cdSFrançois Tigeot int drm_display_info_set_bus_formats(struct drm_display_info *info,
7932c9916cdSFrançois Tigeot 				     const u32 *formats,
7942c9916cdSFrançois Tigeot 				     unsigned int num_formats)
7952c9916cdSFrançois Tigeot {
7962c9916cdSFrançois Tigeot 	u32 *fmts = NULL;
7972c9916cdSFrançois Tigeot 
7982c9916cdSFrançois Tigeot 	if (!formats && num_formats)
7992c9916cdSFrançois Tigeot 		return -EINVAL;
8002c9916cdSFrançois Tigeot 
8012c9916cdSFrançois Tigeot 	if (formats && num_formats) {
8022c9916cdSFrançois Tigeot 		fmts = kmemdup(formats, sizeof(*formats) * num_formats,
8032c9916cdSFrançois Tigeot 			       GFP_KERNEL);
8042c9916cdSFrançois Tigeot 		if (!fmts)
8052c9916cdSFrançois Tigeot 			return -ENOMEM;
8062c9916cdSFrançois Tigeot 	}
8072c9916cdSFrançois Tigeot 
8082c9916cdSFrançois Tigeot 	kfree(info->bus_formats);
8092c9916cdSFrançois Tigeot 	info->bus_formats = fmts;
8102c9916cdSFrançois Tigeot 	info->num_bus_formats = num_formats;
8112c9916cdSFrançois Tigeot 
8122c9916cdSFrançois Tigeot 	return 0;
8132c9916cdSFrançois Tigeot }
8142c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_display_info_set_bus_formats);
8152c9916cdSFrançois Tigeot 
8162c9916cdSFrançois Tigeot /**
8171b13d190SFrançois Tigeot  * drm_connector_get_cmdline_mode - reads the user's cmdline mode
8181b13d190SFrançois Tigeot  * @connector: connector to quwery
8191b13d190SFrançois Tigeot  *
8201b13d190SFrançois Tigeot  * The kernel supports per-connector configration of its consoles through
8211b13d190SFrançois Tigeot  * use of the video= parameter. This function parses that option and
8221b13d190SFrançois Tigeot  * extracts the user's specified mode (or enable/disable status) for a
8231b13d190SFrançois Tigeot  * particular connector. This is typically only used during the early fbdev
8241b13d190SFrançois Tigeot  * setup.
8251b13d190SFrançois Tigeot  */
8261b13d190SFrançois Tigeot static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
8271b13d190SFrançois Tigeot {
8281b13d190SFrançois Tigeot 	struct drm_cmdline_mode *mode = &connector->cmdline_mode;
8291b13d190SFrançois Tigeot 	char *option = NULL;
8301b13d190SFrançois Tigeot 
8311b13d190SFrançois Tigeot 	if (fb_get_options(connector->name, &option))
8321b13d190SFrançois Tigeot 		return;
8331b13d190SFrançois Tigeot 
8341b13d190SFrançois Tigeot 	if (!drm_mode_parse_command_line_for_connector(option,
8351b13d190SFrançois Tigeot 						       connector,
8361b13d190SFrançois Tigeot 						       mode))
8371b13d190SFrançois Tigeot 		return;
8381b13d190SFrançois Tigeot 
8391b13d190SFrançois Tigeot 	if (mode->force) {
8401b13d190SFrançois Tigeot 		const char *s;
8411b13d190SFrançois Tigeot 
8421b13d190SFrançois Tigeot 		switch (mode->force) {
8431b13d190SFrançois Tigeot 		case DRM_FORCE_OFF:
8441b13d190SFrançois Tigeot 			s = "OFF";
8451b13d190SFrançois Tigeot 			break;
8461b13d190SFrançois Tigeot 		case DRM_FORCE_ON_DIGITAL:
8471b13d190SFrançois Tigeot 			s = "ON - dig";
8481b13d190SFrançois Tigeot 			break;
8491b13d190SFrançois Tigeot 		default:
8501b13d190SFrançois Tigeot 		case DRM_FORCE_ON:
8511b13d190SFrançois Tigeot 			s = "ON";
8521b13d190SFrançois Tigeot 			break;
8531b13d190SFrançois Tigeot 		}
8541b13d190SFrançois Tigeot 
8551b13d190SFrançois Tigeot 		DRM_INFO("forcing %s connector %s\n", connector->name, s);
8561b13d190SFrançois Tigeot 		connector->force = mode->force;
8571b13d190SFrançois Tigeot 	}
8581b13d190SFrançois Tigeot 
8591b13d190SFrançois Tigeot 	DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
8601b13d190SFrançois Tigeot 		      connector->name,
8611b13d190SFrançois Tigeot 		      mode->xres, mode->yres,
8621b13d190SFrançois Tigeot 		      mode->refresh_specified ? mode->refresh : 60,
8631b13d190SFrançois Tigeot 		      mode->rb ? " reduced blanking" : "",
8641b13d190SFrançois Tigeot 		      mode->margins ? " with margins" : "",
8651b13d190SFrançois Tigeot 		      mode->interlace ?  " interlaced" : "");
8661b13d190SFrançois Tigeot }
8671b13d190SFrançois Tigeot 
8688621f407SFrançois Tigeot static void drm_connector_free(struct kref *kref)
8698621f407SFrançois Tigeot {
8708621f407SFrançois Tigeot 	struct drm_connector *connector =
8718621f407SFrançois Tigeot 		container_of(kref, struct drm_connector, base.refcount);
8728621f407SFrançois Tigeot 	struct drm_device *dev = connector->dev;
8738621f407SFrançois Tigeot 
8748621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &connector->base);
8758621f407SFrançois Tigeot 	connector->funcs->destroy(connector);
8768621f407SFrançois Tigeot }
8778621f407SFrançois Tigeot 
8781b13d190SFrançois Tigeot /**
8795718399fSFrançois Tigeot  * drm_connector_init - Init a preallocated connector
8805718399fSFrançois Tigeot  * @dev: DRM device
8815718399fSFrançois Tigeot  * @connector: the connector to init
8825718399fSFrançois Tigeot  * @funcs: callbacks for this connector
883a2fdbec6SFrançois Tigeot  * @connector_type: user visible type of the connector
8845718399fSFrançois Tigeot  *
8855718399fSFrançois Tigeot  * Initialises a preallocated connector. Connectors should be
8865718399fSFrançois Tigeot  * subclassed as part of driver connector objects.
8875718399fSFrançois Tigeot  *
888ba55f2f5SFrançois Tigeot  * Returns:
8895718399fSFrançois Tigeot  * Zero on success, error code on failure.
8905718399fSFrançois Tigeot  */
8915718399fSFrançois Tigeot int drm_connector_init(struct drm_device *dev,
8925718399fSFrançois Tigeot 		       struct drm_connector *connector,
8935718399fSFrançois Tigeot 		       const struct drm_connector_funcs *funcs,
8945718399fSFrançois Tigeot 		       int connector_type)
8955718399fSFrançois Tigeot {
8962c9916cdSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
8975718399fSFrançois Tigeot 	int ret;
8985718399fSFrançois Tigeot 
8994dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
9005718399fSFrançois Tigeot 
9018621f407SFrançois Tigeot 	ret = drm_mode_object_get_reg(dev, &connector->base,
9028621f407SFrançois Tigeot 				      DRM_MODE_OBJECT_CONNECTOR,
9038621f407SFrançois Tigeot 				      true, drm_connector_free);
9045718399fSFrançois Tigeot 	if (ret)
905ba55f2f5SFrançois Tigeot 		goto out_unlock;
9065718399fSFrançois Tigeot 
907b5162e19SFrançois Tigeot 	connector->base.properties = &connector->properties;
9085718399fSFrançois Tigeot 	connector->dev = dev;
9095718399fSFrançois Tigeot 	connector->funcs = funcs;
9108621f407SFrançois Tigeot 
9115718399fSFrançois Tigeot 	connector->connector_type = connector_type;
9125718399fSFrançois Tigeot 	connector->connector_type_id =
913c7dca6a7SFrançois Tigeot 		++drm_connector_enum_list[connector_type].count; /* TODO */
9149edbd4a0SFrançois Tigeot 	if (connector->connector_type_id < 0) {
9159edbd4a0SFrançois Tigeot 		ret = connector->connector_type_id;
916c7dca6a7SFrançois Tigeot 		goto out_put;
9179edbd4a0SFrançois Tigeot 	}
918b4efbf42Szrj 	connector->name =
919c0e85e96SFrançois Tigeot 		kasprintf(GFP_KERNEL, "%s-%d",
920b4efbf42Szrj 			  drm_connector_enum_list[connector_type].name,
921b4efbf42Szrj 			  connector->connector_type_id);
922b4efbf42Szrj 	if (!connector->name) {
923b4efbf42Szrj 		ret = -ENOMEM;
924c7dca6a7SFrançois Tigeot 		goto out_put;
925b4efbf42Szrj 	}
926ba55f2f5SFrançois Tigeot 
9275718399fSFrançois Tigeot 	INIT_LIST_HEAD(&connector->probed_modes);
9285718399fSFrançois Tigeot 	INIT_LIST_HEAD(&connector->modes);
9295718399fSFrançois Tigeot 	connector->edid_blob_ptr = NULL;
930b5162e19SFrançois Tigeot 	connector->status = connector_status_unknown;
9315718399fSFrançois Tigeot 
9321b13d190SFrançois Tigeot 	drm_connector_get_cmdline_mode(connector);
9331b13d190SFrançois Tigeot 
9342c9916cdSFrançois Tigeot 	/* We should add connectors at the end to avoid upsetting the connector
9352c9916cdSFrançois Tigeot 	 * index too much. */
9362c9916cdSFrançois Tigeot 	list_add_tail(&connector->head, &config->connector_list);
9372c9916cdSFrançois Tigeot 	config->num_connector++;
9385718399fSFrançois Tigeot 
939b5162e19SFrançois Tigeot 	if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
940b5162e19SFrançois Tigeot 		drm_object_attach_property(&connector->base,
9412c9916cdSFrançois Tigeot 					      config->edid_property,
942b5162e19SFrançois Tigeot 					      0);
9435718399fSFrançois Tigeot 
944b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
9452c9916cdSFrançois Tigeot 				      config->dpms_property, 0);
9462c9916cdSFrançois Tigeot 
9472c9916cdSFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
9482c9916cdSFrançois Tigeot 		drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
9492c9916cdSFrançois Tigeot 	}
9505718399fSFrançois Tigeot 
95124edb884SFrançois Tigeot 	connector->debugfs_entry = NULL;
952c7dca6a7SFrançois Tigeot 
953ba55f2f5SFrançois Tigeot out_put:
954ba55f2f5SFrançois Tigeot 	if (ret)
9558621f407SFrançois Tigeot 		drm_mode_object_unregister(dev, &connector->base);
956ba55f2f5SFrançois Tigeot 
957ba55f2f5SFrançois Tigeot out_unlock:
9584dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
9595718399fSFrançois Tigeot 
9605718399fSFrançois Tigeot 	return ret;
9615718399fSFrançois Tigeot }
962b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_init);
9635718399fSFrançois Tigeot 
9645718399fSFrançois Tigeot /**
9655718399fSFrançois Tigeot  * drm_connector_cleanup - cleans up an initialised connector
9665718399fSFrançois Tigeot  * @connector: connector to cleanup
9675718399fSFrançois Tigeot  *
9685718399fSFrançois Tigeot  * Cleans up the connector but doesn't free the object.
9695718399fSFrançois Tigeot  */
9705718399fSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector)
9715718399fSFrançois Tigeot {
9725718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
9735718399fSFrançois Tigeot 	struct drm_display_mode *mode, *t;
9745718399fSFrançois Tigeot 
9755718399fSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
9765718399fSFrançois Tigeot 		drm_mode_remove(connector, mode);
9775718399fSFrançois Tigeot 
9785718399fSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->modes, head)
9795718399fSFrançois Tigeot 		drm_mode_remove(connector, mode);
9805718399fSFrançois Tigeot 
981917ec290SFrançois Tigeot 	ida_remove(&drm_connector_enum_list[connector->connector_type].ida,
982917ec290SFrançois Tigeot 		   connector->connector_type_id);
983917ec290SFrançois Tigeot 
984917ec290SFrançois Tigeot 	ida_remove(&dev->mode_config.connector_ida,
985917ec290SFrançois Tigeot 		   connector->connector_id);
986917ec290SFrançois Tigeot 
9872c9916cdSFrançois Tigeot 	kfree(connector->display_info.bus_formats);
9888621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &connector->base);
989b4efbf42Szrj 	kfree(connector->name);
990b4efbf42Szrj 	connector->name = NULL;
9915718399fSFrançois Tigeot 	list_del(&connector->head);
9925718399fSFrançois Tigeot 	dev->mode_config.num_connector--;
9932c9916cdSFrançois Tigeot 
9942c9916cdSFrançois Tigeot 	WARN_ON(connector->state && !connector->funcs->atomic_destroy_state);
9952c9916cdSFrançois Tigeot 	if (connector->state && connector->funcs->atomic_destroy_state)
9962c9916cdSFrançois Tigeot 		connector->funcs->atomic_destroy_state(connector,
9972c9916cdSFrançois Tigeot 						       connector->state);
9982c9916cdSFrançois Tigeot 
9992c9916cdSFrançois Tigeot 	memset(connector, 0, sizeof(*connector));
10005718399fSFrançois Tigeot }
1001b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup);
1002b5162e19SFrançois Tigeot 
1003ba55f2f5SFrançois Tigeot /**
1004c7dca6a7SFrançois Tigeot  * drm_connector_index - find the index of a registered connector
1005c7dca6a7SFrançois Tigeot  * @connector: connector to find index for
1006c7dca6a7SFrançois Tigeot  *
1007c7dca6a7SFrançois Tigeot  * Given a registered connector, return the index of that connector within a DRM
1008c7dca6a7SFrançois Tigeot  * device's list of connectors.
1009c7dca6a7SFrançois Tigeot  */
1010c7dca6a7SFrançois Tigeot unsigned int drm_connector_index(struct drm_connector *connector)
1011c7dca6a7SFrançois Tigeot {
1012c7dca6a7SFrançois Tigeot 	unsigned int index = 0;
1013c7dca6a7SFrançois Tigeot 	struct drm_connector *tmp;
1014c7dca6a7SFrançois Tigeot 	struct drm_mode_config *config = &connector->dev->mode_config;
1015c7dca6a7SFrançois Tigeot 
1016c7dca6a7SFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
1017c7dca6a7SFrançois Tigeot 
1018c7dca6a7SFrançois Tigeot 	drm_for_each_connector(tmp, connector->dev) {
1019c7dca6a7SFrançois Tigeot 		if (tmp == connector)
1020c7dca6a7SFrançois Tigeot 			return index;
1021c7dca6a7SFrançois Tigeot 
1022c7dca6a7SFrançois Tigeot 		index++;
1023c7dca6a7SFrançois Tigeot 	}
1024c7dca6a7SFrançois Tigeot 
1025c7dca6a7SFrançois Tigeot 	BUG();
1026c7dca6a7SFrançois Tigeot }
1027c7dca6a7SFrançois Tigeot EXPORT_SYMBOL(drm_connector_index);
1028c7dca6a7SFrançois Tigeot 
1029c7dca6a7SFrançois Tigeot /**
1030c6f73aabSFrançois Tigeot  * drm_connector_register - register a connector
1031c6f73aabSFrançois Tigeot  * @connector: the connector to register
1032c6f73aabSFrançois Tigeot  *
1033c6f73aabSFrançois Tigeot  * Register userspace interfaces for a connector
1034c6f73aabSFrançois Tigeot  *
1035c6f73aabSFrançois Tigeot  * Returns:
1036c6f73aabSFrançois Tigeot  * Zero on success, error code on failure.
1037c6f73aabSFrançois Tigeot  */
1038c6f73aabSFrançois Tigeot int drm_connector_register(struct drm_connector *connector)
1039c6f73aabSFrançois Tigeot {
104024edb884SFrançois Tigeot 	int ret;
104124edb884SFrançois Tigeot 
104224edb884SFrançois Tigeot 	ret = drm_sysfs_connector_add(connector);
104324edb884SFrançois Tigeot 	if (ret)
104424edb884SFrançois Tigeot 		return ret;
104524edb884SFrançois Tigeot 
104624edb884SFrançois Tigeot 	ret = drm_debugfs_connector_add(connector);
104724edb884SFrançois Tigeot 	if (ret) {
104824edb884SFrançois Tigeot 		drm_sysfs_connector_remove(connector);
104924edb884SFrançois Tigeot 		return ret;
105024edb884SFrançois Tigeot 	}
105124edb884SFrançois Tigeot 
10528621f407SFrançois Tigeot 	drm_mode_object_register(connector->dev, &connector->base);
10538621f407SFrançois Tigeot 
105424edb884SFrançois Tigeot 	return 0;
1055c6f73aabSFrançois Tigeot }
1056c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_register);
1057c6f73aabSFrançois Tigeot 
1058c6f73aabSFrançois Tigeot /**
1059c6f73aabSFrançois Tigeot  * drm_connector_unregister - unregister a connector
1060c6f73aabSFrançois Tigeot  * @connector: the connector to unregister
1061c6f73aabSFrançois Tigeot  *
1062c6f73aabSFrançois Tigeot  * Unregister userspace interfaces for a connector
1063c6f73aabSFrançois Tigeot  */
1064c6f73aabSFrançois Tigeot void drm_connector_unregister(struct drm_connector *connector)
1065c6f73aabSFrançois Tigeot {
1066c6f73aabSFrançois Tigeot 	drm_sysfs_connector_remove(connector);
106724edb884SFrançois Tigeot #if 0
106824edb884SFrançois Tigeot 	drm_debugfs_connector_remove(connector);
106924edb884SFrançois Tigeot #endif
1070c6f73aabSFrançois Tigeot }
1071c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister);
1072c6f73aabSFrançois Tigeot 
1073c6f73aabSFrançois Tigeot /**
10748621f407SFrançois Tigeot  * drm_connector_register_all - register all connectors
1075ba55f2f5SFrançois Tigeot  * @dev: drm device
1076ba55f2f5SFrançois Tigeot  *
10778621f407SFrançois Tigeot  * This function registers all connectors in sysfs and other places so that
10788621f407SFrançois Tigeot  * userspace can start to access them. Drivers can call it after calling
10798621f407SFrançois Tigeot  * drm_dev_register() to complete the device registration, if they don't call
10808621f407SFrançois Tigeot  * drm_connector_register() on each connector individually.
10818621f407SFrançois Tigeot  *
10828621f407SFrançois Tigeot  * When a device is unplugged and should be removed from userspace access,
10838621f407SFrançois Tigeot  * call drm_connector_unregister_all(), which is the inverse of this
10848621f407SFrançois Tigeot  * function.
10858621f407SFrançois Tigeot  *
10868621f407SFrançois Tigeot  * Returns:
10878621f407SFrançois Tigeot  * Zero on success, error code on failure.
1088ba55f2f5SFrançois Tigeot  */
10898621f407SFrançois Tigeot int drm_connector_register_all(struct drm_device *dev)
10908621f407SFrançois Tigeot {
10918621f407SFrançois Tigeot 	struct drm_connector *connector;
10928621f407SFrançois Tigeot 	int ret;
10938621f407SFrançois Tigeot 
10948621f407SFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
10958621f407SFrançois Tigeot 
10968621f407SFrançois Tigeot 	drm_for_each_connector(connector, dev) {
10978621f407SFrançois Tigeot 		ret = drm_connector_register(connector);
10988621f407SFrançois Tigeot 		if (ret)
10998621f407SFrançois Tigeot 			goto err;
11008621f407SFrançois Tigeot 	}
11018621f407SFrançois Tigeot 
11028621f407SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
11038621f407SFrançois Tigeot 
11048621f407SFrançois Tigeot 	return 0;
11058621f407SFrançois Tigeot 
11068621f407SFrançois Tigeot err:
11078621f407SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
11088621f407SFrançois Tigeot 	drm_connector_unregister_all(dev);
11098621f407SFrançois Tigeot 	return ret;
11108621f407SFrançois Tigeot }
11118621f407SFrançois Tigeot EXPORT_SYMBOL(drm_connector_register_all);
11128621f407SFrançois Tigeot 
11138621f407SFrançois Tigeot /**
11148621f407SFrançois Tigeot  * drm_connector_unregister_all - unregister connector userspace interfaces
11158621f407SFrançois Tigeot  * @dev: drm device
11168621f407SFrançois Tigeot  *
11178621f407SFrançois Tigeot  * This functions unregisters all connectors from sysfs and other places so
11188621f407SFrançois Tigeot  * that userspace can no longer access them. Drivers should call this as the
11198621f407SFrançois Tigeot  * first step tearing down the device instace, or when the underlying
11208621f407SFrançois Tigeot  * physical device disappeared (e.g. USB unplug), right before calling
11218621f407SFrançois Tigeot  * drm_dev_unregister().
11228621f407SFrançois Tigeot  */
11238621f407SFrançois Tigeot void drm_connector_unregister_all(struct drm_device *dev)
1124b5162e19SFrançois Tigeot {
1125b5162e19SFrançois Tigeot 	struct drm_connector *connector;
1126b5162e19SFrançois Tigeot 
1127a05eeebfSFrançois Tigeot 	/* FIXME: taking the mode config mutex ends up in a clash with sysfs */
1128b5162e19SFrançois Tigeot 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
1129c6f73aabSFrançois Tigeot 		drm_connector_unregister(connector);
1130b5162e19SFrançois Tigeot }
11318621f407SFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister_all);
11325718399fSFrançois Tigeot 
1133ba55f2f5SFrançois Tigeot /**
1134ba55f2f5SFrançois Tigeot  * drm_encoder_init - Init a preallocated encoder
1135ba55f2f5SFrançois Tigeot  * @dev: drm device
1136ba55f2f5SFrançois Tigeot  * @encoder: the encoder to init
1137ba55f2f5SFrançois Tigeot  * @funcs: callbacks for this encoder
1138ba55f2f5SFrançois Tigeot  * @encoder_type: user visible type of the encoder
1139aee94f86SFrançois Tigeot  * @name: printf style format string for the encoder name, or NULL for default name
1140ba55f2f5SFrançois Tigeot  *
1141ba55f2f5SFrançois Tigeot  * Initialises a preallocated encoder. Encoder should be
1142ba55f2f5SFrançois Tigeot  * subclassed as part of driver encoder objects.
1143ba55f2f5SFrançois Tigeot  *
1144ba55f2f5SFrançois Tigeot  * Returns:
1145ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
1146ba55f2f5SFrançois Tigeot  */
11475718399fSFrançois Tigeot int drm_encoder_init(struct drm_device *dev,
11485718399fSFrançois Tigeot 		      struct drm_encoder *encoder,
11495718399fSFrançois Tigeot 		      const struct drm_encoder_funcs *funcs,
1150aee94f86SFrançois Tigeot 		      int encoder_type, const char *name, ...)
11515718399fSFrançois Tigeot {
11525718399fSFrançois Tigeot 	int ret;
11535718399fSFrançois Tigeot 
11544dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
11555718399fSFrançois Tigeot 
11565718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
11575718399fSFrançois Tigeot 	if (ret)
1158ba55f2f5SFrançois Tigeot 		goto out_unlock;
11595718399fSFrançois Tigeot 
11605718399fSFrançois Tigeot 	encoder->dev = dev;
11615718399fSFrançois Tigeot 	encoder->encoder_type = encoder_type;
11625718399fSFrançois Tigeot 	encoder->funcs = funcs;
1163aee94f86SFrançois Tigeot 	if (name) {
1164aee94f86SFrançois Tigeot 		__va_list ap;
1165aee94f86SFrançois Tigeot 
1166aee94f86SFrançois Tigeot 		__va_start(ap, name);
1167aee94f86SFrançois Tigeot 		encoder->name = kvasprintf(GFP_KERNEL, name, ap);
1168aee94f86SFrançois Tigeot 		__va_end(ap);
1169aee94f86SFrançois Tigeot 	} else {
1170aee94f86SFrançois Tigeot 		encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
1171b4efbf42Szrj 					  drm_encoder_enum_list[encoder_type].name,
1172b4efbf42Szrj 					  encoder->base.id);
1173aee94f86SFrançois Tigeot 	}
1174b4efbf42Szrj 	if (!encoder->name) {
1175b4efbf42Szrj 		ret = -ENOMEM;
1176b4efbf42Szrj 		goto out_put;
1177b4efbf42Szrj 	}
11785718399fSFrançois Tigeot 
11795718399fSFrançois Tigeot 	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
11805718399fSFrançois Tigeot 	dev->mode_config.num_encoder++;
11815718399fSFrançois Tigeot 
1182b4efbf42Szrj out_put:
1183b4efbf42Szrj 	if (ret)
11848621f407SFrançois Tigeot 		drm_mode_object_unregister(dev, &encoder->base);
1185b4efbf42Szrj 
1186ba55f2f5SFrançois Tigeot out_unlock:
11874dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
11885718399fSFrançois Tigeot 
11895718399fSFrançois Tigeot 	return ret;
11905718399fSFrançois Tigeot }
1191b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_init);
11925718399fSFrançois Tigeot 
1193ba55f2f5SFrançois Tigeot /**
1194c0e85e96SFrançois Tigeot  * drm_encoder_index - find the index of a registered encoder
1195c0e85e96SFrançois Tigeot  * @encoder: encoder to find index for
1196c0e85e96SFrançois Tigeot  *
1197c0e85e96SFrançois Tigeot  * Given a registered encoder, return the index of that encoder within a DRM
1198c0e85e96SFrançois Tigeot  * device's list of encoders.
1199c0e85e96SFrançois Tigeot  */
1200c0e85e96SFrançois Tigeot unsigned int drm_encoder_index(struct drm_encoder *encoder)
1201c0e85e96SFrançois Tigeot {
1202c0e85e96SFrançois Tigeot 	unsigned int index = 0;
1203c0e85e96SFrançois Tigeot 	struct drm_encoder *tmp;
1204c0e85e96SFrançois Tigeot 
1205c0e85e96SFrançois Tigeot 	drm_for_each_encoder(tmp, encoder->dev) {
1206c0e85e96SFrançois Tigeot 		if (tmp == encoder)
1207c0e85e96SFrançois Tigeot 			return index;
1208c0e85e96SFrançois Tigeot 
1209c0e85e96SFrançois Tigeot 		index++;
1210c0e85e96SFrançois Tigeot 	}
1211c0e85e96SFrançois Tigeot 
1212c0e85e96SFrançois Tigeot 	BUG();
1213c0e85e96SFrançois Tigeot }
1214c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_index);
1215c0e85e96SFrançois Tigeot 
1216c0e85e96SFrançois Tigeot /**
1217ba55f2f5SFrançois Tigeot  * drm_encoder_cleanup - cleans up an initialised encoder
1218ba55f2f5SFrançois Tigeot  * @encoder: encoder to cleanup
1219ba55f2f5SFrançois Tigeot  *
1220ba55f2f5SFrançois Tigeot  * Cleans up the encoder but doesn't free the object.
1221ba55f2f5SFrançois Tigeot  */
12225718399fSFrançois Tigeot void drm_encoder_cleanup(struct drm_encoder *encoder)
12235718399fSFrançois Tigeot {
12245718399fSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
12252c9916cdSFrançois Tigeot 
12264dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
12278621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &encoder->base);
1228ba55f2f5SFrançois Tigeot 	kfree(encoder->name);
12295718399fSFrançois Tigeot 	list_del(&encoder->head);
12305718399fSFrançois Tigeot 	dev->mode_config.num_encoder--;
12314dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
12322c9916cdSFrançois Tigeot 
12332c9916cdSFrançois Tigeot 	memset(encoder, 0, sizeof(*encoder));
12345718399fSFrançois Tigeot }
1235b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_cleanup);
12365718399fSFrançois Tigeot 
1237917ec290SFrançois Tigeot static unsigned int drm_num_planes(struct drm_device *dev)
1238917ec290SFrançois Tigeot {
1239917ec290SFrançois Tigeot 	unsigned int num = 0;
1240917ec290SFrançois Tigeot 	struct drm_plane *tmp;
1241917ec290SFrançois Tigeot 
1242917ec290SFrançois Tigeot 	drm_for_each_plane(tmp, dev) {
1243917ec290SFrançois Tigeot 		num++;
1244917ec290SFrançois Tigeot 	}
1245917ec290SFrançois Tigeot 
1246917ec290SFrançois Tigeot 	return num;
1247917ec290SFrançois Tigeot }
1248917ec290SFrançois Tigeot 
124906fede5aSFrançois Tigeot /**
1250ba55f2f5SFrançois Tigeot  * drm_universal_plane_init - Initialize a new universal plane object
125106fede5aSFrançois Tigeot  * @dev: DRM device
125206fede5aSFrançois Tigeot  * @plane: plane object to init
125306fede5aSFrançois Tigeot  * @possible_crtcs: bitmask of possible CRTCs
125406fede5aSFrançois Tigeot  * @funcs: callbacks for the new plane
125506fede5aSFrançois Tigeot  * @formats: array of supported formats (%DRM_FORMAT_*)
125606fede5aSFrançois Tigeot  * @format_count: number of elements in @formats
1257ba55f2f5SFrançois Tigeot  * @type: type of plane (overlay, primary, cursor)
1258aee94f86SFrançois Tigeot  * @name: printf style format string for the plane name, or NULL for default name
125906fede5aSFrançois Tigeot  *
1260ba55f2f5SFrançois Tigeot  * Initializes a plane object of type @type.
126106fede5aSFrançois Tigeot  *
1262ba55f2f5SFrançois Tigeot  * Returns:
126306fede5aSFrançois Tigeot  * Zero on success, error code on failure.
126406fede5aSFrançois Tigeot  */
1265ba55f2f5SFrançois Tigeot int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
12665718399fSFrançois Tigeot 			     unsigned long possible_crtcs,
12675718399fSFrançois Tigeot 			     const struct drm_plane_funcs *funcs,
1268a05eeebfSFrançois Tigeot 			     const uint32_t *formats, unsigned int format_count,
1269aee94f86SFrançois Tigeot 			     enum drm_plane_type type,
1270aee94f86SFrançois Tigeot 			     const char *name, ...)
12715718399fSFrançois Tigeot {
12722c9916cdSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
12735718399fSFrançois Tigeot 	int ret;
12745718399fSFrançois Tigeot 
12755718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
12765718399fSFrançois Tigeot 	if (ret)
12772c9916cdSFrançois Tigeot 		return ret;
12782c9916cdSFrançois Tigeot 
12792c9916cdSFrançois Tigeot 	drm_modeset_lock_init(&plane->mutex);
12805718399fSFrançois Tigeot 
1281b5162e19SFrançois Tigeot 	plane->base.properties = &plane->properties;
12825718399fSFrançois Tigeot 	plane->dev = dev;
12835718399fSFrançois Tigeot 	plane->funcs = funcs;
1284917ec290SFrançois Tigeot 	plane->format_types = kmalloc(format_count * sizeof(uint32_t),
12855a3b77d5SFrançois Tigeot 				      M_DRM, M_WAITOK);
1286b5162e19SFrançois Tigeot 	if (!plane->format_types) {
1287b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("out of memory when allocating plane\n");
12888621f407SFrançois Tigeot 		drm_mode_object_unregister(dev, &plane->base);
12892c9916cdSFrançois Tigeot 		return -ENOMEM;
1290b5162e19SFrançois Tigeot 	}
12915718399fSFrançois Tigeot 
1292917ec290SFrançois Tigeot 	if (name) {
1293917ec290SFrançois Tigeot 		__va_list ap;
1294917ec290SFrançois Tigeot 
1295917ec290SFrançois Tigeot 		__va_start(ap, name);
1296917ec290SFrançois Tigeot 		plane->name = kvasprintf(GFP_KERNEL, name, ap);
1297917ec290SFrançois Tigeot 		__va_end(ap);
1298917ec290SFrançois Tigeot 	} else {
1299917ec290SFrançois Tigeot 		plane->name = kasprintf(GFP_KERNEL, "plane-%d",
1300917ec290SFrançois Tigeot 					drm_num_planes(dev));
1301917ec290SFrançois Tigeot 	}
1302917ec290SFrançois Tigeot 	if (!plane->name) {
1303917ec290SFrançois Tigeot 		kfree(plane->format_types);
13048621f407SFrançois Tigeot 		drm_mode_object_unregister(dev, &plane->base);
1305917ec290SFrançois Tigeot 		return -ENOMEM;
1306917ec290SFrançois Tigeot 	}
1307917ec290SFrançois Tigeot 
13085718399fSFrançois Tigeot 	memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
13095718399fSFrançois Tigeot 	plane->format_count = format_count;
13105718399fSFrançois Tigeot 	plane->possible_crtcs = possible_crtcs;
1311ba55f2f5SFrançois Tigeot 	plane->type = type;
13125718399fSFrançois Tigeot 
13132c9916cdSFrançois Tigeot 	list_add_tail(&plane->head, &config->plane_list);
13142c9916cdSFrançois Tigeot 	config->num_total_plane++;
1315ba55f2f5SFrançois Tigeot 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
13162c9916cdSFrançois Tigeot 		config->num_overlay_plane++;
1317ba55f2f5SFrançois Tigeot 
1318ba55f2f5SFrançois Tigeot 	drm_object_attach_property(&plane->base,
13192c9916cdSFrançois Tigeot 				   config->plane_type_property,
1320ba55f2f5SFrançois Tigeot 				   plane->type);
13215718399fSFrançois Tigeot 
13222c9916cdSFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
13232c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
13242c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
13252c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
13262c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
13272c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
13282c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
13292c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_src_x, 0);
13302c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_src_y, 0);
13312c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_src_w, 0);
13322c9916cdSFrançois Tigeot 		drm_object_attach_property(&plane->base, config->prop_src_h, 0);
13332c9916cdSFrançois Tigeot 	}
13345718399fSFrançois Tigeot 
13352c9916cdSFrançois Tigeot 	return 0;
13365718399fSFrançois Tigeot }
1337ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_universal_plane_init);
1338ba55f2f5SFrançois Tigeot 
1339ba55f2f5SFrançois Tigeot /**
1340ba55f2f5SFrançois Tigeot  * drm_plane_init - Initialize a legacy plane
1341ba55f2f5SFrançois Tigeot  * @dev: DRM device
1342ba55f2f5SFrançois Tigeot  * @plane: plane object to init
1343ba55f2f5SFrançois Tigeot  * @possible_crtcs: bitmask of possible CRTCs
1344ba55f2f5SFrançois Tigeot  * @funcs: callbacks for the new plane
1345ba55f2f5SFrançois Tigeot  * @formats: array of supported formats (%DRM_FORMAT_*)
1346ba55f2f5SFrançois Tigeot  * @format_count: number of elements in @formats
1347ba55f2f5SFrançois Tigeot  * @is_primary: plane type (primary vs overlay)
1348ba55f2f5SFrançois Tigeot  *
1349ba55f2f5SFrançois Tigeot  * Legacy API to initialize a DRM plane.
1350ba55f2f5SFrançois Tigeot  *
1351ba55f2f5SFrançois Tigeot  * New drivers should call drm_universal_plane_init() instead.
1352ba55f2f5SFrançois Tigeot  *
1353ba55f2f5SFrançois Tigeot  * Returns:
1354ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
1355ba55f2f5SFrançois Tigeot  */
1356ba55f2f5SFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
1357ba55f2f5SFrançois Tigeot 		   unsigned long possible_crtcs,
1358ba55f2f5SFrançois Tigeot 		   const struct drm_plane_funcs *funcs,
1359a05eeebfSFrançois Tigeot 		   const uint32_t *formats, unsigned int format_count,
1360ba55f2f5SFrançois Tigeot 		   bool is_primary)
1361ba55f2f5SFrançois Tigeot {
1362ba55f2f5SFrançois Tigeot 	enum drm_plane_type type;
1363ba55f2f5SFrançois Tigeot 
1364ba55f2f5SFrançois Tigeot 	type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
1365ba55f2f5SFrançois Tigeot 	return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
1366aee94f86SFrançois Tigeot 					formats, format_count, type, NULL);
1367ba55f2f5SFrançois Tigeot }
1368b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_init);
13695718399fSFrançois Tigeot 
137006fede5aSFrançois Tigeot /**
137106fede5aSFrançois Tigeot  * drm_plane_cleanup - Clean up the core plane usage
137206fede5aSFrançois Tigeot  * @plane: plane to cleanup
137306fede5aSFrançois Tigeot  *
137406fede5aSFrançois Tigeot  * This function cleans up @plane and removes it from the DRM mode setting
137506fede5aSFrançois Tigeot  * core. Note that the function does *not* free the plane structure itself,
137606fede5aSFrançois Tigeot  * this is the responsibility of the caller.
137706fede5aSFrançois Tigeot  */
13785718399fSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane)
13795718399fSFrançois Tigeot {
13805718399fSFrançois Tigeot 	struct drm_device *dev = plane->dev;
13815718399fSFrançois Tigeot 
13824dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
13834dbb207bSFrançois Tigeot 	kfree(plane->format_types);
13848621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &plane->base);
1385ba55f2f5SFrançois Tigeot 
1386ba55f2f5SFrançois Tigeot 	BUG_ON(list_empty(&plane->head));
1387ba55f2f5SFrançois Tigeot 
13885718399fSFrançois Tigeot 	list_del(&plane->head);
1389ba55f2f5SFrançois Tigeot 	dev->mode_config.num_total_plane--;
1390ba55f2f5SFrançois Tigeot 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
1391ba55f2f5SFrançois Tigeot 		dev->mode_config.num_overlay_plane--;
13924dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
13932c9916cdSFrançois Tigeot 
13942c9916cdSFrançois Tigeot 	WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
13952c9916cdSFrançois Tigeot 	if (plane->state && plane->funcs->atomic_destroy_state)
13962c9916cdSFrançois Tigeot 		plane->funcs->atomic_destroy_state(plane, plane->state);
13972c9916cdSFrançois Tigeot 
1398917ec290SFrançois Tigeot 	kfree(plane->name);
1399917ec290SFrançois Tigeot 
14002c9916cdSFrançois Tigeot 	memset(plane, 0, sizeof(*plane));
14015718399fSFrançois Tigeot }
1402b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup);
14035718399fSFrançois Tigeot 
14045718399fSFrançois Tigeot /**
14051b13d190SFrançois Tigeot  * drm_plane_index - find the index of a registered plane
14061b13d190SFrançois Tigeot  * @plane: plane to find index for
14071b13d190SFrançois Tigeot  *
14081b13d190SFrançois Tigeot  * Given a registered plane, return the index of that CRTC within a DRM
14091b13d190SFrançois Tigeot  * device's list of planes.
14101b13d190SFrançois Tigeot  */
14111b13d190SFrançois Tigeot unsigned int drm_plane_index(struct drm_plane *plane)
14121b13d190SFrançois Tigeot {
14131b13d190SFrançois Tigeot 	unsigned int index = 0;
14141b13d190SFrançois Tigeot 	struct drm_plane *tmp;
14151b13d190SFrançois Tigeot 
1416a05eeebfSFrançois Tigeot 	drm_for_each_plane(tmp, plane->dev) {
14171b13d190SFrançois Tigeot 		if (tmp == plane)
14181b13d190SFrançois Tigeot 			return index;
14191b13d190SFrançois Tigeot 
14201b13d190SFrançois Tigeot 		index++;
14211b13d190SFrançois Tigeot 	}
14221b13d190SFrançois Tigeot 
14231b13d190SFrançois Tigeot 	BUG();
14241b13d190SFrançois Tigeot }
14251b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_plane_index);
14261b13d190SFrançois Tigeot 
14271b13d190SFrançois Tigeot /**
142819c468b4SFrançois Tigeot  * drm_plane_from_index - find the registered plane at an index
142919c468b4SFrançois Tigeot  * @dev: DRM device
143019c468b4SFrançois Tigeot  * @idx: index of registered plane to find for
143119c468b4SFrançois Tigeot  *
143219c468b4SFrançois Tigeot  * Given a plane index, return the registered plane from DRM device's
143319c468b4SFrançois Tigeot  * list of planes with matching index.
143419c468b4SFrançois Tigeot  */
143519c468b4SFrançois Tigeot struct drm_plane *
143619c468b4SFrançois Tigeot drm_plane_from_index(struct drm_device *dev, int idx)
143719c468b4SFrançois Tigeot {
143819c468b4SFrançois Tigeot 	struct drm_plane *plane;
143919c468b4SFrançois Tigeot 	unsigned int i = 0;
144019c468b4SFrançois Tigeot 
1441a05eeebfSFrançois Tigeot 	drm_for_each_plane(plane, dev) {
144219c468b4SFrançois Tigeot 		if (i == idx)
144319c468b4SFrançois Tigeot 			return plane;
144419c468b4SFrançois Tigeot 		i++;
144519c468b4SFrançois Tigeot 	}
144619c468b4SFrançois Tigeot 	return NULL;
144719c468b4SFrançois Tigeot }
144819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_plane_from_index);
144919c468b4SFrançois Tigeot 
145019c468b4SFrançois Tigeot /**
145106fede5aSFrançois Tigeot  * drm_plane_force_disable - Forcibly disable a plane
145206fede5aSFrançois Tigeot  * @plane: plane to disable
145306fede5aSFrançois Tigeot  *
145406fede5aSFrançois Tigeot  * Forces the plane to be disabled.
145506fede5aSFrançois Tigeot  *
145606fede5aSFrançois Tigeot  * Used when the plane's current framebuffer is destroyed,
145706fede5aSFrançois Tigeot  * and when restoring fbdev mode.
145806fede5aSFrançois Tigeot  */
145906fede5aSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane)
146006fede5aSFrançois Tigeot {
146106fede5aSFrançois Tigeot 	int ret;
146206fede5aSFrançois Tigeot 
14631b13d190SFrançois Tigeot 	if (!plane->fb)
146406fede5aSFrançois Tigeot 		return;
146506fede5aSFrançois Tigeot 
14661b13d190SFrançois Tigeot 	plane->old_fb = plane->fb;
146706fede5aSFrançois Tigeot 	ret = plane->funcs->disable_plane(plane);
1468ba55f2f5SFrançois Tigeot 	if (ret) {
146906fede5aSFrançois Tigeot 		DRM_ERROR("failed to disable plane with busy fb\n");
14701b13d190SFrançois Tigeot 		plane->old_fb = NULL;
1471ba55f2f5SFrançois Tigeot 		return;
1472ba55f2f5SFrançois Tigeot 	}
147306fede5aSFrançois Tigeot 	/* disconnect the plane from the fb and crtc: */
14742c9916cdSFrançois Tigeot 	drm_framebuffer_unreference(plane->old_fb);
14751b13d190SFrançois Tigeot 	plane->old_fb = NULL;
147606fede5aSFrançois Tigeot 	plane->fb = NULL;
147706fede5aSFrançois Tigeot 	plane->crtc = NULL;
147806fede5aSFrançois Tigeot }
147906fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable);
148006fede5aSFrançois Tigeot 
14812c9916cdSFrançois Tigeot static int drm_mode_create_standard_properties(struct drm_device *dev)
14825718399fSFrançois Tigeot {
14832c9916cdSFrançois Tigeot 	struct drm_property *prop;
14845718399fSFrançois Tigeot 
14855718399fSFrançois Tigeot 	/*
14865718399fSFrançois Tigeot 	 * Standard properties (apply to all connectors)
14875718399fSFrançois Tigeot 	 */
14882c9916cdSFrançois Tigeot 	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
14895718399fSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
14905718399fSFrançois Tigeot 				   "EDID", 0);
14912c9916cdSFrançois Tigeot 	if (!prop)
14922c9916cdSFrançois Tigeot 		return -ENOMEM;
14932c9916cdSFrançois Tigeot 	dev->mode_config.edid_property = prop;
14945718399fSFrançois Tigeot 
14952c9916cdSFrançois Tigeot 	prop = drm_property_create_enum(dev, 0,
14965718399fSFrançois Tigeot 				   "DPMS", drm_dpms_enum_list,
1497b5162e19SFrançois Tigeot 				   ARRAY_SIZE(drm_dpms_enum_list));
14982c9916cdSFrançois Tigeot 	if (!prop)
14992c9916cdSFrançois Tigeot 		return -ENOMEM;
15002c9916cdSFrançois Tigeot 	dev->mode_config.dpms_property = prop;
15015718399fSFrançois Tigeot 
15022c9916cdSFrançois Tigeot 	prop = drm_property_create(dev,
150324edb884SFrançois Tigeot 				   DRM_MODE_PROP_BLOB |
150424edb884SFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
150524edb884SFrançois Tigeot 				   "PATH", 0);
15062c9916cdSFrançois Tigeot 	if (!prop)
15072c9916cdSFrançois Tigeot 		return -ENOMEM;
15082c9916cdSFrançois Tigeot 	dev->mode_config.path_property = prop;
150924edb884SFrançois Tigeot 
15102c9916cdSFrançois Tigeot 	prop = drm_property_create(dev,
15112c9916cdSFrançois Tigeot 				   DRM_MODE_PROP_BLOB |
15122c9916cdSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
15132c9916cdSFrançois Tigeot 				   "TILE", 0);
15142c9916cdSFrançois Tigeot 	if (!prop)
15152c9916cdSFrançois Tigeot 		return -ENOMEM;
15162c9916cdSFrançois Tigeot 	dev->mode_config.tile_property = prop;
15175718399fSFrançois Tigeot 
15182c9916cdSFrançois Tigeot 	prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
1519ba55f2f5SFrançois Tigeot 					"type", drm_plane_type_enum_list,
1520ba55f2f5SFrançois Tigeot 					ARRAY_SIZE(drm_plane_type_enum_list));
15212c9916cdSFrançois Tigeot 	if (!prop)
15222c9916cdSFrançois Tigeot 		return -ENOMEM;
15232c9916cdSFrançois Tigeot 	dev->mode_config.plane_type_property = prop;
15242c9916cdSFrançois Tigeot 
15252c9916cdSFrançois Tigeot 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
15262c9916cdSFrançois Tigeot 			"SRC_X", 0, UINT_MAX);
15272c9916cdSFrançois Tigeot 	if (!prop)
15282c9916cdSFrançois Tigeot 		return -ENOMEM;
15292c9916cdSFrançois Tigeot 	dev->mode_config.prop_src_x = prop;
15302c9916cdSFrançois Tigeot 
15312c9916cdSFrançois Tigeot 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
15322c9916cdSFrançois Tigeot 			"SRC_Y", 0, UINT_MAX);
15332c9916cdSFrançois Tigeot 	if (!prop)
15342c9916cdSFrançois Tigeot 		return -ENOMEM;
15352c9916cdSFrançois Tigeot 	dev->mode_config.prop_src_y = prop;
15362c9916cdSFrançois Tigeot 
15372c9916cdSFrançois Tigeot 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
15382c9916cdSFrançois Tigeot 			"SRC_W", 0, UINT_MAX);
15392c9916cdSFrançois Tigeot 	if (!prop)
15402c9916cdSFrançois Tigeot 		return -ENOMEM;
15412c9916cdSFrançois Tigeot 	dev->mode_config.prop_src_w = prop;
15422c9916cdSFrançois Tigeot 
15432c9916cdSFrançois Tigeot 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
15442c9916cdSFrançois Tigeot 			"SRC_H", 0, UINT_MAX);
15452c9916cdSFrançois Tigeot 	if (!prop)
15462c9916cdSFrançois Tigeot 		return -ENOMEM;
15472c9916cdSFrançois Tigeot 	dev->mode_config.prop_src_h = prop;
15482c9916cdSFrançois Tigeot 
15492c9916cdSFrançois Tigeot 	prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
15502c9916cdSFrançois Tigeot 			"CRTC_X", INT_MIN, INT_MAX);
15512c9916cdSFrançois Tigeot 	if (!prop)
15522c9916cdSFrançois Tigeot 		return -ENOMEM;
15532c9916cdSFrançois Tigeot 	dev->mode_config.prop_crtc_x = prop;
15542c9916cdSFrançois Tigeot 
15552c9916cdSFrançois Tigeot 	prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
15562c9916cdSFrançois Tigeot 			"CRTC_Y", INT_MIN, INT_MAX);
15572c9916cdSFrançois Tigeot 	if (!prop)
15582c9916cdSFrançois Tigeot 		return -ENOMEM;
15592c9916cdSFrançois Tigeot 	dev->mode_config.prop_crtc_y = prop;
15602c9916cdSFrançois Tigeot 
15612c9916cdSFrançois Tigeot 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
15622c9916cdSFrançois Tigeot 			"CRTC_W", 0, INT_MAX);
15632c9916cdSFrançois Tigeot 	if (!prop)
15642c9916cdSFrançois Tigeot 		return -ENOMEM;
15652c9916cdSFrançois Tigeot 	dev->mode_config.prop_crtc_w = prop;
15662c9916cdSFrançois Tigeot 
15672c9916cdSFrançois Tigeot 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
15682c9916cdSFrançois Tigeot 			"CRTC_H", 0, INT_MAX);
15692c9916cdSFrançois Tigeot 	if (!prop)
15702c9916cdSFrançois Tigeot 		return -ENOMEM;
15712c9916cdSFrançois Tigeot 	dev->mode_config.prop_crtc_h = prop;
15722c9916cdSFrançois Tigeot 
15732c9916cdSFrançois Tigeot 	prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
15742c9916cdSFrançois Tigeot 			"FB_ID", DRM_MODE_OBJECT_FB);
15752c9916cdSFrançois Tigeot 	if (!prop)
15762c9916cdSFrançois Tigeot 		return -ENOMEM;
15772c9916cdSFrançois Tigeot 	dev->mode_config.prop_fb_id = prop;
15782c9916cdSFrançois Tigeot 
15792c9916cdSFrançois Tigeot 	prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
15802c9916cdSFrançois Tigeot 			"CRTC_ID", DRM_MODE_OBJECT_CRTC);
15812c9916cdSFrançois Tigeot 	if (!prop)
15822c9916cdSFrançois Tigeot 		return -ENOMEM;
15832c9916cdSFrançois Tigeot 	dev->mode_config.prop_crtc_id = prop;
15842c9916cdSFrançois Tigeot 
15852c9916cdSFrançois Tigeot 	prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
15862c9916cdSFrançois Tigeot 			"ACTIVE");
15872c9916cdSFrançois Tigeot 	if (!prop)
15882c9916cdSFrançois Tigeot 		return -ENOMEM;
15892c9916cdSFrançois Tigeot 	dev->mode_config.prop_active = prop;
1590ba55f2f5SFrançois Tigeot 
159119c468b4SFrançois Tigeot 	prop = drm_property_create(dev,
159219c468b4SFrançois Tigeot 			DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
159319c468b4SFrançois Tigeot 			"MODE_ID", 0);
159419c468b4SFrançois Tigeot 	if (!prop)
159519c468b4SFrançois Tigeot 		return -ENOMEM;
159619c468b4SFrançois Tigeot 	dev->mode_config.prop_mode_id = prop;
159719c468b4SFrançois Tigeot 
1598c0e85e96SFrançois Tigeot 	prop = drm_property_create(dev,
1599c0e85e96SFrançois Tigeot 			DRM_MODE_PROP_BLOB,
1600c0e85e96SFrançois Tigeot 			"DEGAMMA_LUT", 0);
1601c0e85e96SFrançois Tigeot 	if (!prop)
1602c0e85e96SFrançois Tigeot 		return -ENOMEM;
1603c0e85e96SFrançois Tigeot 	dev->mode_config.degamma_lut_property = prop;
1604c0e85e96SFrançois Tigeot 
1605c0e85e96SFrançois Tigeot 	prop = drm_property_create_range(dev,
1606c0e85e96SFrançois Tigeot 			DRM_MODE_PROP_IMMUTABLE,
1607c0e85e96SFrançois Tigeot 			"DEGAMMA_LUT_SIZE", 0, UINT_MAX);
1608c0e85e96SFrançois Tigeot 	if (!prop)
1609c0e85e96SFrançois Tigeot 		return -ENOMEM;
1610c0e85e96SFrançois Tigeot 	dev->mode_config.degamma_lut_size_property = prop;
1611c0e85e96SFrançois Tigeot 
1612c0e85e96SFrançois Tigeot 	prop = drm_property_create(dev,
1613c0e85e96SFrançois Tigeot 			DRM_MODE_PROP_BLOB,
1614c0e85e96SFrançois Tigeot 			"CTM", 0);
1615c0e85e96SFrançois Tigeot 	if (!prop)
1616c0e85e96SFrançois Tigeot 		return -ENOMEM;
1617c0e85e96SFrançois Tigeot 	dev->mode_config.ctm_property = prop;
1618c0e85e96SFrançois Tigeot 
1619c0e85e96SFrançois Tigeot 	prop = drm_property_create(dev,
1620c0e85e96SFrançois Tigeot 			DRM_MODE_PROP_BLOB,
1621c0e85e96SFrançois Tigeot 			"GAMMA_LUT", 0);
1622c0e85e96SFrançois Tigeot 	if (!prop)
1623c0e85e96SFrançois Tigeot 		return -ENOMEM;
1624c0e85e96SFrançois Tigeot 	dev->mode_config.gamma_lut_property = prop;
1625c0e85e96SFrançois Tigeot 
1626c0e85e96SFrançois Tigeot 	prop = drm_property_create_range(dev,
1627c0e85e96SFrançois Tigeot 			DRM_MODE_PROP_IMMUTABLE,
1628c0e85e96SFrançois Tigeot 			"GAMMA_LUT_SIZE", 0, UINT_MAX);
1629c0e85e96SFrançois Tigeot 	if (!prop)
1630c0e85e96SFrançois Tigeot 		return -ENOMEM;
1631c0e85e96SFrançois Tigeot 	dev->mode_config.gamma_lut_size_property = prop;
1632c0e85e96SFrançois Tigeot 
1633ba55f2f5SFrançois Tigeot 	return 0;
1634ba55f2f5SFrançois Tigeot }
1635ba55f2f5SFrançois Tigeot 
16365718399fSFrançois Tigeot /**
16375718399fSFrançois Tigeot  * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
16385718399fSFrançois Tigeot  * @dev: DRM device
16395718399fSFrançois Tigeot  *
16405718399fSFrançois Tigeot  * Called by a driver the first time a DVI-I connector is made.
16415718399fSFrançois Tigeot  */
16425718399fSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev)
16435718399fSFrançois Tigeot {
16445718399fSFrançois Tigeot 	struct drm_property *dvi_i_selector;
16455718399fSFrançois Tigeot 	struct drm_property *dvi_i_subconnector;
16465718399fSFrançois Tigeot 
16475718399fSFrançois Tigeot 	if (dev->mode_config.dvi_i_select_subconnector_property)
16485718399fSFrançois Tigeot 		return 0;
16495718399fSFrançois Tigeot 
16505718399fSFrançois Tigeot 	dvi_i_selector =
16515718399fSFrançois Tigeot 		drm_property_create_enum(dev, 0,
16525718399fSFrançois Tigeot 				    "select subconnector",
16535718399fSFrançois Tigeot 				    drm_dvi_i_select_enum_list,
1654b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_select_enum_list));
16555718399fSFrançois Tigeot 	dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
16565718399fSFrançois Tigeot 
16575718399fSFrançois Tigeot 	dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
16585718399fSFrançois Tigeot 				    "subconnector",
16595718399fSFrançois Tigeot 				    drm_dvi_i_subconnector_enum_list,
1660b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
16615718399fSFrançois Tigeot 	dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
16625718399fSFrançois Tigeot 
16635718399fSFrançois Tigeot 	return 0;
16645718399fSFrançois Tigeot }
1665b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
16665718399fSFrançois Tigeot 
16675718399fSFrançois Tigeot /**
16685718399fSFrançois Tigeot  * drm_create_tv_properties - create TV specific connector properties
16695718399fSFrançois Tigeot  * @dev: DRM device
16705718399fSFrançois Tigeot  * @num_modes: number of different TV formats (modes) supported
16715718399fSFrançois Tigeot  * @modes: array of pointers to strings containing name of each format
16725718399fSFrançois Tigeot  *
16735718399fSFrançois Tigeot  * Called by a driver's TV initialization routine, this function creates
16745718399fSFrançois Tigeot  * the TV specific connector properties for a given device.  Caller is
16755718399fSFrançois Tigeot  * responsible for allocating a list of format names and passing them to
16765718399fSFrançois Tigeot  * this routine.
16775718399fSFrançois Tigeot  */
16782c9916cdSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev,
16792c9916cdSFrançois Tigeot 				  unsigned int num_modes,
1680352ff8bdSFrançois Tigeot 				  const char * const modes[])
16815718399fSFrançois Tigeot {
16825718399fSFrançois Tigeot 	struct drm_property *tv_selector;
16835718399fSFrançois Tigeot 	struct drm_property *tv_subconnector;
16842c9916cdSFrançois Tigeot 	unsigned int i;
16855718399fSFrançois Tigeot 
16865718399fSFrançois Tigeot 	if (dev->mode_config.tv_select_subconnector_property)
16875718399fSFrançois Tigeot 		return 0;
16885718399fSFrançois Tigeot 
16895718399fSFrançois Tigeot 	/*
16905718399fSFrançois Tigeot 	 * Basic connector properties
16915718399fSFrançois Tigeot 	 */
16925718399fSFrançois Tigeot 	tv_selector = drm_property_create_enum(dev, 0,
16935718399fSFrançois Tigeot 					  "select subconnector",
16945718399fSFrançois Tigeot 					  drm_tv_select_enum_list,
1695b5162e19SFrançois Tigeot 					  ARRAY_SIZE(drm_tv_select_enum_list));
1696352ff8bdSFrançois Tigeot 	if (!tv_selector)
1697352ff8bdSFrançois Tigeot 		goto nomem;
1698352ff8bdSFrançois Tigeot 
16995718399fSFrançois Tigeot 	dev->mode_config.tv_select_subconnector_property = tv_selector;
17005718399fSFrançois Tigeot 
17015718399fSFrançois Tigeot 	tv_subconnector =
17025718399fSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
17035718399fSFrançois Tigeot 				    "subconnector",
17045718399fSFrançois Tigeot 				    drm_tv_subconnector_enum_list,
1705b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_tv_subconnector_enum_list));
1706352ff8bdSFrançois Tigeot 	if (!tv_subconnector)
1707352ff8bdSFrançois Tigeot 		goto nomem;
17085718399fSFrançois Tigeot 	dev->mode_config.tv_subconnector_property = tv_subconnector;
17095718399fSFrançois Tigeot 
17105718399fSFrançois Tigeot 	/*
17115718399fSFrançois Tigeot 	 * Other, TV specific properties: margins & TV modes.
17125718399fSFrançois Tigeot 	 */
17135718399fSFrançois Tigeot 	dev->mode_config.tv_left_margin_property =
17145718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "left margin", 0, 100);
1715352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_left_margin_property)
1716352ff8bdSFrançois Tigeot 		goto nomem;
17175718399fSFrançois Tigeot 
17185718399fSFrançois Tigeot 	dev->mode_config.tv_right_margin_property =
17195718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "right margin", 0, 100);
1720352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_right_margin_property)
1721352ff8bdSFrançois Tigeot 		goto nomem;
17225718399fSFrançois Tigeot 
17235718399fSFrançois Tigeot 	dev->mode_config.tv_top_margin_property =
17245718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "top margin", 0, 100);
1725352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_top_margin_property)
1726352ff8bdSFrançois Tigeot 		goto nomem;
17275718399fSFrançois Tigeot 
17285718399fSFrançois Tigeot 	dev->mode_config.tv_bottom_margin_property =
17295718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "bottom margin", 0, 100);
1730352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_bottom_margin_property)
1731352ff8bdSFrançois Tigeot 		goto nomem;
17325718399fSFrançois Tigeot 
17335718399fSFrançois Tigeot 	dev->mode_config.tv_mode_property =
17345718399fSFrançois Tigeot 		drm_property_create(dev, DRM_MODE_PROP_ENUM,
17355718399fSFrançois Tigeot 				    "mode", num_modes);
1736352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_mode_property)
1737352ff8bdSFrançois Tigeot 		goto nomem;
1738352ff8bdSFrançois Tigeot 
17395718399fSFrançois Tigeot 	for (i = 0; i < num_modes; i++)
17405718399fSFrançois Tigeot 		drm_property_add_enum(dev->mode_config.tv_mode_property, i,
17415718399fSFrançois Tigeot 				      i, modes[i]);
17425718399fSFrançois Tigeot 
17435718399fSFrançois Tigeot 	dev->mode_config.tv_brightness_property =
17445718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "brightness", 0, 100);
1745352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_brightness_property)
1746352ff8bdSFrançois Tigeot 		goto nomem;
17475718399fSFrançois Tigeot 
17485718399fSFrançois Tigeot 	dev->mode_config.tv_contrast_property =
17495718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "contrast", 0, 100);
1750352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_contrast_property)
1751352ff8bdSFrançois Tigeot 		goto nomem;
17525718399fSFrançois Tigeot 
17535718399fSFrançois Tigeot 	dev->mode_config.tv_flicker_reduction_property =
17545718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
1755352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_flicker_reduction_property)
1756352ff8bdSFrançois Tigeot 		goto nomem;
17575718399fSFrançois Tigeot 
17585718399fSFrançois Tigeot 	dev->mode_config.tv_overscan_property =
17595718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "overscan", 0, 100);
1760352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_overscan_property)
1761352ff8bdSFrançois Tigeot 		goto nomem;
17625718399fSFrançois Tigeot 
17635718399fSFrançois Tigeot 	dev->mode_config.tv_saturation_property =
17645718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "saturation", 0, 100);
1765352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_saturation_property)
1766352ff8bdSFrançois Tigeot 		goto nomem;
17675718399fSFrançois Tigeot 
17685718399fSFrançois Tigeot 	dev->mode_config.tv_hue_property =
17695718399fSFrançois Tigeot 		drm_property_create_range(dev, 0, "hue", 0, 100);
1770352ff8bdSFrançois Tigeot 	if (!dev->mode_config.tv_hue_property)
1771352ff8bdSFrançois Tigeot 		goto nomem;
17725718399fSFrançois Tigeot 
17735718399fSFrançois Tigeot 	return 0;
1774352ff8bdSFrançois Tigeot nomem:
1775352ff8bdSFrançois Tigeot 	return -ENOMEM;
17765718399fSFrançois Tigeot }
1777b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties);
17785718399fSFrançois Tigeot 
17795718399fSFrançois Tigeot /**
17805718399fSFrançois Tigeot  * drm_mode_create_scaling_mode_property - create scaling mode property
17815718399fSFrançois Tigeot  * @dev: DRM device
17825718399fSFrançois Tigeot  *
17835718399fSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
17845718399fSFrançois Tigeot  * connectors.
17855718399fSFrançois Tigeot  */
17865718399fSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev)
17875718399fSFrançois Tigeot {
17885718399fSFrançois Tigeot 	struct drm_property *scaling_mode;
17895718399fSFrançois Tigeot 
17905718399fSFrançois Tigeot 	if (dev->mode_config.scaling_mode_property)
17915718399fSFrançois Tigeot 		return 0;
17925718399fSFrançois Tigeot 
17935718399fSFrançois Tigeot 	scaling_mode =
17945718399fSFrançois Tigeot 		drm_property_create_enum(dev, 0, "scaling mode",
17955718399fSFrançois Tigeot 				drm_scaling_mode_enum_list,
1796b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_scaling_mode_enum_list));
17975718399fSFrançois Tigeot 
17985718399fSFrançois Tigeot 	dev->mode_config.scaling_mode_property = scaling_mode;
17995718399fSFrançois Tigeot 
18005718399fSFrançois Tigeot 	return 0;
18015718399fSFrançois Tigeot }
1802b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
18035718399fSFrançois Tigeot 
18045718399fSFrançois Tigeot /**
180524edb884SFrançois Tigeot  * drm_mode_create_aspect_ratio_property - create aspect ratio property
180624edb884SFrançois Tigeot  * @dev: DRM device
180724edb884SFrançois Tigeot  *
180824edb884SFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
180924edb884SFrançois Tigeot  * connectors.
181024edb884SFrançois Tigeot  *
181124edb884SFrançois Tigeot  * Returns:
18122c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
181324edb884SFrançois Tigeot  */
181424edb884SFrançois Tigeot int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
181524edb884SFrançois Tigeot {
181624edb884SFrançois Tigeot 	if (dev->mode_config.aspect_ratio_property)
181724edb884SFrançois Tigeot 		return 0;
181824edb884SFrançois Tigeot 
181924edb884SFrançois Tigeot 	dev->mode_config.aspect_ratio_property =
182024edb884SFrançois Tigeot 		drm_property_create_enum(dev, 0, "aspect ratio",
182124edb884SFrançois Tigeot 				drm_aspect_ratio_enum_list,
182224edb884SFrançois Tigeot 				ARRAY_SIZE(drm_aspect_ratio_enum_list));
182324edb884SFrançois Tigeot 
182424edb884SFrançois Tigeot 	if (dev->mode_config.aspect_ratio_property == NULL)
182524edb884SFrançois Tigeot 		return -ENOMEM;
182624edb884SFrançois Tigeot 
182724edb884SFrançois Tigeot 	return 0;
182824edb884SFrançois Tigeot }
182924edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
183024edb884SFrançois Tigeot 
183124edb884SFrançois Tigeot /**
18325718399fSFrançois Tigeot  * drm_mode_create_dirty_property - create dirty property
18335718399fSFrançois Tigeot  * @dev: DRM device
18345718399fSFrançois Tigeot  *
18355718399fSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
18365718399fSFrançois Tigeot  * connectors.
18375718399fSFrançois Tigeot  */
18385718399fSFrançois Tigeot int drm_mode_create_dirty_info_property(struct drm_device *dev)
18395718399fSFrançois Tigeot {
18405718399fSFrançois Tigeot 	struct drm_property *dirty_info;
18415718399fSFrançois Tigeot 
18425718399fSFrançois Tigeot 	if (dev->mode_config.dirty_info_property)
18435718399fSFrançois Tigeot 		return 0;
18445718399fSFrançois Tigeot 
18455718399fSFrançois Tigeot 	dirty_info =
18465718399fSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
18475718399fSFrançois Tigeot 				    "dirty",
18485718399fSFrançois Tigeot 				    drm_dirty_info_enum_list,
1849b5162e19SFrançois Tigeot 				    ARRAY_SIZE(drm_dirty_info_enum_list));
18505718399fSFrançois Tigeot 	dev->mode_config.dirty_info_property = dirty_info;
18515718399fSFrançois Tigeot 
18525718399fSFrançois Tigeot 	return 0;
18535718399fSFrançois Tigeot }
1854b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
18555718399fSFrançois Tigeot 
18562c9916cdSFrançois Tigeot /**
18572c9916cdSFrançois Tigeot  * drm_mode_create_suggested_offset_properties - create suggests offset properties
18582c9916cdSFrançois Tigeot  * @dev: DRM device
18592c9916cdSFrançois Tigeot  *
18602c9916cdSFrançois Tigeot  * Create the the suggested x/y offset property for connectors.
18612c9916cdSFrançois Tigeot  */
18622c9916cdSFrançois Tigeot int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
18632c9916cdSFrançois Tigeot {
18642c9916cdSFrançois Tigeot 	if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property)
18652c9916cdSFrançois Tigeot 		return 0;
18662c9916cdSFrançois Tigeot 
18672c9916cdSFrançois Tigeot 	dev->mode_config.suggested_x_property =
18682c9916cdSFrançois Tigeot 		drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff);
18692c9916cdSFrançois Tigeot 
18702c9916cdSFrançois Tigeot 	dev->mode_config.suggested_y_property =
18712c9916cdSFrançois Tigeot 		drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff);
18722c9916cdSFrançois Tigeot 
18732c9916cdSFrançois Tigeot 	if (dev->mode_config.suggested_x_property == NULL ||
18742c9916cdSFrançois Tigeot 	    dev->mode_config.suggested_y_property == NULL)
18752c9916cdSFrançois Tigeot 		return -ENOMEM;
18762c9916cdSFrançois Tigeot 	return 0;
18772c9916cdSFrançois Tigeot }
18782c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties);
18792c9916cdSFrançois Tigeot 
18805718399fSFrançois Tigeot /**
18815718399fSFrançois Tigeot  * drm_mode_getresources - get graphics configuration
18824dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
18834dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
18844dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
18855718399fSFrançois Tigeot  *
18865718399fSFrançois Tigeot  * Construct a set of configuration description structures and return
18875718399fSFrançois Tigeot  * them to the user, including CRTC, connector and framebuffer configuration.
18885718399fSFrançois Tigeot  *
18895718399fSFrançois Tigeot  * Called by the user via ioctl.
18905718399fSFrançois Tigeot  *
1891ba55f2f5SFrançois Tigeot  * Returns:
18922c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
18935718399fSFrançois Tigeot  */
18945718399fSFrançois Tigeot int drm_mode_getresources(struct drm_device *dev, void *data,
18955718399fSFrançois Tigeot 			  struct drm_file *file_priv)
18965718399fSFrançois Tigeot {
18975718399fSFrançois Tigeot 	struct drm_mode_card_res *card_res = data;
18985718399fSFrançois Tigeot 	struct list_head *lh;
18995718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
19005718399fSFrançois Tigeot 	struct drm_connector *connector;
19015718399fSFrançois Tigeot 	struct drm_crtc *crtc;
19025718399fSFrançois Tigeot 	struct drm_encoder *encoder;
19035718399fSFrançois Tigeot 	int ret = 0;
19045718399fSFrançois Tigeot 	int connector_count = 0;
19055718399fSFrançois Tigeot 	int crtc_count = 0;
19065718399fSFrançois Tigeot 	int fb_count = 0;
19075718399fSFrançois Tigeot 	int encoder_count = 0;
1908a05eeebfSFrançois Tigeot 	int copied = 0;
19095718399fSFrançois Tigeot 	uint32_t __user *fb_id;
19105718399fSFrançois Tigeot 	uint32_t __user *crtc_id;
19115718399fSFrançois Tigeot 	uint32_t __user *connector_id;
19125718399fSFrançois Tigeot 	uint32_t __user *encoder_id;
19135718399fSFrançois Tigeot 
19145718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
1915b5162e19SFrançois Tigeot 		return -EINVAL;
19165718399fSFrançois Tigeot 
19175718399fSFrançois Tigeot 
19184dbb207bSFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
19195718399fSFrançois Tigeot 	/*
19205718399fSFrançois Tigeot 	 * For the non-control nodes we need to limit the list of resources
19215718399fSFrançois Tigeot 	 * by IDs in the group list for this node
19225718399fSFrançois Tigeot 	 */
19235718399fSFrançois Tigeot 	list_for_each(lh, &file_priv->fbs)
19245718399fSFrançois Tigeot 		fb_count++;
19255718399fSFrançois Tigeot 
19264dbb207bSFrançois Tigeot 	/* handle this in 4 parts */
19274dbb207bSFrançois Tigeot 	/* FBs */
19284dbb207bSFrançois Tigeot 	if (card_res->count_fbs >= fb_count) {
19294dbb207bSFrançois Tigeot 		copied = 0;
19304dbb207bSFrançois Tigeot 		fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
19314dbb207bSFrançois Tigeot 		list_for_each_entry(fb, &file_priv->fbs, filp_head) {
19324dbb207bSFrançois Tigeot 			if (put_user(fb->base.id, fb_id + copied)) {
19334dbb207bSFrançois Tigeot 				mutex_unlock(&file_priv->fbs_lock);
19344dbb207bSFrançois Tigeot 				return -EFAULT;
19354dbb207bSFrançois Tigeot 			}
19364dbb207bSFrançois Tigeot 			copied++;
19374dbb207bSFrançois Tigeot 		}
19384dbb207bSFrançois Tigeot 	}
19394dbb207bSFrançois Tigeot 	card_res->count_fbs = fb_count;
19404dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
19414dbb207bSFrançois Tigeot 
19422c9916cdSFrançois Tigeot 	/* mode_config.mutex protects the connector list against e.g. DP MST
19432c9916cdSFrançois Tigeot 	 * connector hot-adding. CRTC/Plane lists are invariant. */
19442c9916cdSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
1945a05eeebfSFrançois Tigeot 	drm_for_each_crtc(crtc, dev)
19465718399fSFrançois Tigeot 		crtc_count++;
19475718399fSFrançois Tigeot 
1948a05eeebfSFrançois Tigeot 	drm_for_each_connector(connector, dev)
19495718399fSFrançois Tigeot 		connector_count++;
19505718399fSFrançois Tigeot 
1951a05eeebfSFrançois Tigeot 	drm_for_each_encoder(encoder, dev)
19525718399fSFrançois Tigeot 		encoder_count++;
19535718399fSFrançois Tigeot 
19545718399fSFrançois Tigeot 	card_res->max_height = dev->mode_config.max_height;
19555718399fSFrançois Tigeot 	card_res->min_height = dev->mode_config.min_height;
19565718399fSFrançois Tigeot 	card_res->max_width = dev->mode_config.max_width;
19575718399fSFrançois Tigeot 	card_res->min_width = dev->mode_config.min_width;
19585718399fSFrançois Tigeot 
19595718399fSFrançois Tigeot 	/* CRTCs */
19605718399fSFrançois Tigeot 	if (card_res->count_crtcs >= crtc_count) {
19615718399fSFrançois Tigeot 		copied = 0;
1962b5162e19SFrançois Tigeot 		crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
1963a05eeebfSFrançois Tigeot 		drm_for_each_crtc(crtc, dev) {
1964b5162e19SFrançois Tigeot 			if (put_user(crtc->base.id, crtc_id + copied)) {
1965b5162e19SFrançois Tigeot 				ret = -EFAULT;
19665718399fSFrançois Tigeot 				goto out;
19675718399fSFrançois Tigeot 			}
19685718399fSFrançois Tigeot 			copied++;
19695718399fSFrançois Tigeot 		}
19705718399fSFrançois Tigeot 	}
19715718399fSFrançois Tigeot 	card_res->count_crtcs = crtc_count;
19725718399fSFrançois Tigeot 
19735718399fSFrançois Tigeot 	/* Encoders */
19745718399fSFrançois Tigeot 	if (card_res->count_encoders >= encoder_count) {
19755718399fSFrançois Tigeot 		copied = 0;
1976b5162e19SFrançois Tigeot 		encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
1977a05eeebfSFrançois Tigeot 		drm_for_each_encoder(encoder, dev) {
1978b5162e19SFrançois Tigeot 			if (put_user(encoder->base.id, encoder_id +
1979b5162e19SFrançois Tigeot 				     copied)) {
1980b5162e19SFrançois Tigeot 				ret = -EFAULT;
19815718399fSFrançois Tigeot 				goto out;
19825718399fSFrançois Tigeot 			}
19835718399fSFrançois Tigeot 			copied++;
19845718399fSFrançois Tigeot 		}
19855718399fSFrançois Tigeot 	}
19865718399fSFrançois Tigeot 	card_res->count_encoders = encoder_count;
19875718399fSFrançois Tigeot 
19885718399fSFrançois Tigeot 	/* Connectors */
19895718399fSFrançois Tigeot 	if (card_res->count_connectors >= connector_count) {
19905718399fSFrançois Tigeot 		copied = 0;
1991b5162e19SFrançois Tigeot 		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
1992a05eeebfSFrançois Tigeot 		drm_for_each_connector(connector, dev) {
1993b5162e19SFrançois Tigeot 			if (put_user(connector->base.id,
1994b5162e19SFrançois Tigeot 				     connector_id + copied)) {
1995b5162e19SFrançois Tigeot 				ret = -EFAULT;
19965718399fSFrançois Tigeot 				goto out;
19975718399fSFrançois Tigeot 			}
19985718399fSFrançois Tigeot 			copied++;
19995718399fSFrançois Tigeot 		}
20005718399fSFrançois Tigeot 	}
20015718399fSFrançois Tigeot 	card_res->count_connectors = connector_count;
20025718399fSFrançois Tigeot 
20035718399fSFrançois Tigeot out:
20042c9916cdSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
20055718399fSFrançois Tigeot 	return ret;
20065718399fSFrançois Tigeot }
20075718399fSFrançois Tigeot 
20085718399fSFrançois Tigeot /**
20095718399fSFrançois Tigeot  * drm_mode_getcrtc - get CRTC configuration
20104dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
20114dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
20124dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
20135718399fSFrançois Tigeot  *
20145718399fSFrançois Tigeot  * Construct a CRTC configuration structure to return to the user.
20155718399fSFrançois Tigeot  *
20165718399fSFrançois Tigeot  * Called by the user via ioctl.
20175718399fSFrançois Tigeot  *
2018ba55f2f5SFrançois Tigeot  * Returns:
20192c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
20205718399fSFrançois Tigeot  */
20215718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev,
20225718399fSFrançois Tigeot 		     void *data, struct drm_file *file_priv)
20235718399fSFrançois Tigeot {
20245718399fSFrançois Tigeot 	struct drm_mode_crtc *crtc_resp = data;
20255718399fSFrançois Tigeot 	struct drm_crtc *crtc;
20265718399fSFrançois Tigeot 
20275718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2028b5162e19SFrançois Tigeot 		return -EINVAL;
20295718399fSFrançois Tigeot 
2030ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
20312c9916cdSFrançois Tigeot 	if (!crtc)
20322c9916cdSFrançois Tigeot 		return -ENOENT;
20335718399fSFrançois Tigeot 
20342c9916cdSFrançois Tigeot 	drm_modeset_lock_crtc(crtc, crtc->primary);
20355718399fSFrançois Tigeot 	crtc_resp->gamma_size = crtc->gamma_size;
2036ba55f2f5SFrançois Tigeot 	if (crtc->primary->fb)
2037ba55f2f5SFrançois Tigeot 		crtc_resp->fb_id = crtc->primary->fb->base.id;
20385718399fSFrançois Tigeot 	else
20395718399fSFrançois Tigeot 		crtc_resp->fb_id = 0;
20405718399fSFrançois Tigeot 
2041477eb7f9SFrançois Tigeot 	if (crtc->state) {
2042477eb7f9SFrançois Tigeot 		crtc_resp->x = crtc->primary->state->src_x >> 16;
2043477eb7f9SFrançois Tigeot 		crtc_resp->y = crtc->primary->state->src_y >> 16;
2044477eb7f9SFrançois Tigeot 		if (crtc->state->enable) {
204519c468b4SFrançois Tigeot 			drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
2046477eb7f9SFrançois Tigeot 			crtc_resp->mode_valid = 1;
20475718399fSFrançois Tigeot 
2048477eb7f9SFrançois Tigeot 		} else {
2049477eb7f9SFrançois Tigeot 			crtc_resp->mode_valid = 0;
2050477eb7f9SFrançois Tigeot 		}
2051477eb7f9SFrançois Tigeot 	} else {
2052477eb7f9SFrançois Tigeot 		crtc_resp->x = crtc->x;
2053477eb7f9SFrançois Tigeot 		crtc_resp->y = crtc->y;
2054477eb7f9SFrançois Tigeot 		if (crtc->enabled) {
205519c468b4SFrançois Tigeot 			drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
20565718399fSFrançois Tigeot 			crtc_resp->mode_valid = 1;
20575718399fSFrançois Tigeot 
20585718399fSFrançois Tigeot 		} else {
20595718399fSFrançois Tigeot 			crtc_resp->mode_valid = 0;
20605718399fSFrançois Tigeot 		}
2061477eb7f9SFrançois Tigeot 	}
20622c9916cdSFrançois Tigeot 	drm_modeset_unlock_crtc(crtc);
20635718399fSFrançois Tigeot 
20642c9916cdSFrançois Tigeot 	return 0;
20655718399fSFrançois Tigeot }
20665718399fSFrançois Tigeot 
20679edbd4a0SFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
20689edbd4a0SFrançois Tigeot 					 const struct drm_file *file_priv)
20699edbd4a0SFrançois Tigeot {
20709edbd4a0SFrançois Tigeot 	/*
20719edbd4a0SFrançois Tigeot 	 * If user-space hasn't configured the driver to expose the stereo 3D
20729edbd4a0SFrançois Tigeot 	 * modes, don't expose them.
20739edbd4a0SFrançois Tigeot 	 */
20749edbd4a0SFrançois Tigeot 	if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
20759edbd4a0SFrançois Tigeot 		return false;
20769edbd4a0SFrançois Tigeot 
20779edbd4a0SFrançois Tigeot 	return true;
20789edbd4a0SFrançois Tigeot }
20799edbd4a0SFrançois Tigeot 
20802c9916cdSFrançois Tigeot static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
20812c9916cdSFrançois Tigeot {
20822c9916cdSFrançois Tigeot 	/* For atomic drivers only state objects are synchronously updated and
20832c9916cdSFrançois Tigeot 	 * protected by modeset locks, so check those first. */
20842c9916cdSFrançois Tigeot 	if (connector->state)
20852c9916cdSFrançois Tigeot 		return connector->state->best_encoder;
20862c9916cdSFrançois Tigeot 	return connector->encoder;
20872c9916cdSFrançois Tigeot }
20882c9916cdSFrançois Tigeot 
20892c9916cdSFrançois Tigeot /* helper for getconnector and getproperties ioctls */
20902c9916cdSFrançois Tigeot static int get_properties(struct drm_mode_object *obj, bool atomic,
20912c9916cdSFrançois Tigeot 		uint32_t __user *prop_ptr, uint64_t __user *prop_values,
20922c9916cdSFrançois Tigeot 		uint32_t *arg_count_props)
20932c9916cdSFrançois Tigeot {
20942c9916cdSFrançois Tigeot 	int props_count;
20952c9916cdSFrançois Tigeot 	int i, ret, copied;
20962c9916cdSFrançois Tigeot 
20972c9916cdSFrançois Tigeot 	props_count = obj->properties->count;
20982c9916cdSFrançois Tigeot 	if (!atomic)
20992c9916cdSFrançois Tigeot 		props_count -= obj->properties->atomic_count;
21002c9916cdSFrançois Tigeot 
21012c9916cdSFrançois Tigeot 	if ((*arg_count_props >= props_count) && props_count) {
21022c9916cdSFrançois Tigeot 		for (i = 0, copied = 0; copied < props_count; i++) {
21032c9916cdSFrançois Tigeot 			struct drm_property *prop = obj->properties->properties[i];
21042c9916cdSFrançois Tigeot 			uint64_t val;
21052c9916cdSFrançois Tigeot 
21062c9916cdSFrançois Tigeot 			if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
21072c9916cdSFrançois Tigeot 				continue;
21082c9916cdSFrançois Tigeot 
21092c9916cdSFrançois Tigeot 			ret = drm_object_property_get_value(obj, prop, &val);
21102c9916cdSFrançois Tigeot 			if (ret)
21112c9916cdSFrançois Tigeot 				return ret;
21122c9916cdSFrançois Tigeot 
21132c9916cdSFrançois Tigeot 			if (put_user(prop->base.id, prop_ptr + copied))
21142c9916cdSFrançois Tigeot 				return -EFAULT;
21152c9916cdSFrançois Tigeot 
21162c9916cdSFrançois Tigeot 			if (put_user(val, prop_values + copied))
21172c9916cdSFrançois Tigeot 				return -EFAULT;
21182c9916cdSFrançois Tigeot 
21192c9916cdSFrançois Tigeot 			copied++;
21202c9916cdSFrançois Tigeot 		}
21212c9916cdSFrançois Tigeot 	}
21222c9916cdSFrançois Tigeot 	*arg_count_props = props_count;
21232c9916cdSFrançois Tigeot 
21242c9916cdSFrançois Tigeot 	return 0;
21252c9916cdSFrançois Tigeot }
21262c9916cdSFrançois Tigeot 
21275718399fSFrançois Tigeot /**
21285718399fSFrançois Tigeot  * drm_mode_getconnector - get connector configuration
21294dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
21304dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
21314dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
21325718399fSFrançois Tigeot  *
21335718399fSFrançois Tigeot  * Construct a connector configuration structure to return to the user.
21345718399fSFrançois Tigeot  *
21355718399fSFrançois Tigeot  * Called by the user via ioctl.
21365718399fSFrançois Tigeot  *
2137ba55f2f5SFrançois Tigeot  * Returns:
21382c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
21395718399fSFrançois Tigeot  */
21405718399fSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data,
21415718399fSFrançois Tigeot 			  struct drm_file *file_priv)
21425718399fSFrançois Tigeot {
21435718399fSFrançois Tigeot 	struct drm_mode_get_connector *out_resp = data;
21445718399fSFrançois Tigeot 	struct drm_connector *connector;
21452c9916cdSFrançois Tigeot 	struct drm_encoder *encoder;
21465718399fSFrançois Tigeot 	struct drm_display_mode *mode;
21475718399fSFrançois Tigeot 	int mode_count = 0;
21485718399fSFrançois Tigeot 	int encoders_count = 0;
21495718399fSFrançois Tigeot 	int ret = 0;
21505718399fSFrançois Tigeot 	int copied = 0;
21515718399fSFrançois Tigeot 	int i;
21525718399fSFrançois Tigeot 	struct drm_mode_modeinfo u_mode;
21535718399fSFrançois Tigeot 	struct drm_mode_modeinfo __user *mode_ptr;
2154b5162e19SFrançois Tigeot 	uint32_t __user *encoder_ptr;
21555718399fSFrançois Tigeot 
21565718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2157b5162e19SFrançois Tigeot 		return -EINVAL;
21585718399fSFrançois Tigeot 
21595718399fSFrançois Tigeot 	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
21605718399fSFrançois Tigeot 
21614dbb207bSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
21625718399fSFrançois Tigeot 
21638621f407SFrançois Tigeot 	connector = drm_connector_lookup(dev, out_resp->connector_id);
2164ba55f2f5SFrançois Tigeot 	if (!connector) {
21659edbd4a0SFrançois Tigeot 		ret = -ENOENT;
21662c9916cdSFrançois Tigeot 		goto out_unlock;
21675718399fSFrançois Tigeot 	}
21685718399fSFrançois Tigeot 
21692c9916cdSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
21702c9916cdSFrançois Tigeot 		if (connector->encoder_ids[i] != 0)
21715718399fSFrançois Tigeot 			encoders_count++;
21725718399fSFrançois Tigeot 
21735718399fSFrançois Tigeot 	if (out_resp->count_modes == 0) {
21745718399fSFrançois Tigeot 		connector->funcs->fill_modes(connector,
21755718399fSFrançois Tigeot 					     dev->mode_config.max_width,
21765718399fSFrançois Tigeot 					     dev->mode_config.max_height);
21775718399fSFrançois Tigeot 	}
21785718399fSFrançois Tigeot 
21795718399fSFrançois Tigeot 	/* delayed so we get modes regardless of pre-fill_modes state */
21805718399fSFrançois Tigeot 	list_for_each_entry(mode, &connector->modes, head)
21819edbd4a0SFrançois Tigeot 		if (drm_mode_expose_to_userspace(mode, file_priv))
21825718399fSFrançois Tigeot 			mode_count++;
21835718399fSFrançois Tigeot 
21845718399fSFrançois Tigeot 	out_resp->connector_id = connector->base.id;
21855718399fSFrançois Tigeot 	out_resp->connector_type = connector->connector_type;
21865718399fSFrançois Tigeot 	out_resp->connector_type_id = connector->connector_type_id;
21875718399fSFrançois Tigeot 	out_resp->mm_width = connector->display_info.width_mm;
21885718399fSFrançois Tigeot 	out_resp->mm_height = connector->display_info.height_mm;
21895718399fSFrançois Tigeot 	out_resp->subpixel = connector->display_info.subpixel_order;
21905718399fSFrançois Tigeot 	out_resp->connection = connector->status;
21912c9916cdSFrançois Tigeot 
21922c9916cdSFrançois Tigeot 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
21932c9916cdSFrançois Tigeot 	encoder = drm_connector_get_encoder(connector);
21942c9916cdSFrançois Tigeot 	if (encoder)
21952c9916cdSFrançois Tigeot 		out_resp->encoder_id = encoder->base.id;
21965718399fSFrançois Tigeot 	else
21975718399fSFrançois Tigeot 		out_resp->encoder_id = 0;
21985718399fSFrançois Tigeot 
21995718399fSFrançois Tigeot 	/*
22005718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
22015718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
22025718399fSFrançois Tigeot 	 */
22035718399fSFrançois Tigeot 	if ((out_resp->count_modes >= mode_count) && mode_count) {
22045718399fSFrançois Tigeot 		copied = 0;
2205b5162e19SFrançois Tigeot 		mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
22065718399fSFrançois Tigeot 		list_for_each_entry(mode, &connector->modes, head) {
22079edbd4a0SFrançois Tigeot 			if (!drm_mode_expose_to_userspace(mode, file_priv))
22089edbd4a0SFrançois Tigeot 				continue;
22099edbd4a0SFrançois Tigeot 
221019c468b4SFrançois Tigeot 			drm_mode_convert_to_umode(&u_mode, mode);
2211b5162e19SFrançois Tigeot 			if (copy_to_user(mode_ptr + copied,
2212b5162e19SFrançois Tigeot 					 &u_mode, sizeof(u_mode))) {
2213b5162e19SFrançois Tigeot 				ret = -EFAULT;
22145718399fSFrançois Tigeot 				goto out;
22155718399fSFrançois Tigeot 			}
22165718399fSFrançois Tigeot 			copied++;
22175718399fSFrançois Tigeot 		}
22185718399fSFrançois Tigeot 	}
22195718399fSFrançois Tigeot 	out_resp->count_modes = mode_count;
22205718399fSFrançois Tigeot 
22212c9916cdSFrançois Tigeot 	ret = get_properties(&connector->base, file_priv->atomic,
22222c9916cdSFrançois Tigeot 			(uint32_t __user *)(unsigned long)(out_resp->props_ptr),
22232c9916cdSFrançois Tigeot 			(uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
22242c9916cdSFrançois Tigeot 			&out_resp->count_props);
22252c9916cdSFrançois Tigeot 	if (ret)
22265718399fSFrançois Tigeot 		goto out;
22275718399fSFrançois Tigeot 
22285718399fSFrançois Tigeot 	if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
22295718399fSFrançois Tigeot 		copied = 0;
2230b5162e19SFrançois Tigeot 		encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
22315718399fSFrançois Tigeot 		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
22325718399fSFrançois Tigeot 			if (connector->encoder_ids[i] != 0) {
2233b5162e19SFrançois Tigeot 				if (put_user(connector->encoder_ids[i],
2234b5162e19SFrançois Tigeot 					     encoder_ptr + copied)) {
2235b5162e19SFrançois Tigeot 					ret = -EFAULT;
22365718399fSFrançois Tigeot 					goto out;
22375718399fSFrançois Tigeot 				}
22385718399fSFrançois Tigeot 				copied++;
22395718399fSFrançois Tigeot 			}
22405718399fSFrançois Tigeot 		}
22415718399fSFrançois Tigeot 	}
22425718399fSFrançois Tigeot 	out_resp->count_encoders = encoders_count;
22435718399fSFrançois Tigeot 
22445718399fSFrançois Tigeot out:
2245ba55f2f5SFrançois Tigeot 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
22462c9916cdSFrançois Tigeot 
22478621f407SFrançois Tigeot 	drm_connector_unreference(connector);
22482c9916cdSFrançois Tigeot out_unlock:
22494dbb207bSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
22504dbb207bSFrançois Tigeot 
22515718399fSFrançois Tigeot 	return ret;
22525718399fSFrançois Tigeot }
22535718399fSFrançois Tigeot 
22542c9916cdSFrançois Tigeot static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder)
22552c9916cdSFrançois Tigeot {
22562c9916cdSFrançois Tigeot 	struct drm_connector *connector;
22572c9916cdSFrançois Tigeot 	struct drm_device *dev = encoder->dev;
22582c9916cdSFrançois Tigeot 	bool uses_atomic = false;
22592c9916cdSFrançois Tigeot 
22602c9916cdSFrançois Tigeot 	/* For atomic drivers only state objects are synchronously updated and
22612c9916cdSFrançois Tigeot 	 * protected by modeset locks, so check those first. */
2262a05eeebfSFrançois Tigeot 	drm_for_each_connector(connector, dev) {
22632c9916cdSFrançois Tigeot 		if (!connector->state)
22642c9916cdSFrançois Tigeot 			continue;
22652c9916cdSFrançois Tigeot 
22662c9916cdSFrançois Tigeot 		uses_atomic = true;
22672c9916cdSFrançois Tigeot 
22682c9916cdSFrançois Tigeot 		if (connector->state->best_encoder != encoder)
22692c9916cdSFrançois Tigeot 			continue;
22702c9916cdSFrançois Tigeot 
22712c9916cdSFrançois Tigeot 		return connector->state->crtc;
22722c9916cdSFrançois Tigeot 	}
22732c9916cdSFrançois Tigeot 
22742c9916cdSFrançois Tigeot 	/* Don't return stale data (e.g. pending async disable). */
22752c9916cdSFrançois Tigeot 	if (uses_atomic)
22762c9916cdSFrançois Tigeot 		return NULL;
22772c9916cdSFrançois Tigeot 
22782c9916cdSFrançois Tigeot 	return encoder->crtc;
22792c9916cdSFrançois Tigeot }
22802c9916cdSFrançois Tigeot 
2281ba55f2f5SFrançois Tigeot /**
2282ba55f2f5SFrançois Tigeot  * drm_mode_getencoder - get encoder configuration
2283ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
2284ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
2285ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
2286ba55f2f5SFrançois Tigeot  *
2287ba55f2f5SFrançois Tigeot  * Construct a encoder configuration structure to return to the user.
2288ba55f2f5SFrançois Tigeot  *
2289ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2290ba55f2f5SFrançois Tigeot  *
2291ba55f2f5SFrançois Tigeot  * Returns:
22922c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
2293ba55f2f5SFrançois Tigeot  */
22945718399fSFrançois Tigeot int drm_mode_getencoder(struct drm_device *dev, void *data,
22955718399fSFrançois Tigeot 			struct drm_file *file_priv)
22965718399fSFrançois Tigeot {
22975718399fSFrançois Tigeot 	struct drm_mode_get_encoder *enc_resp = data;
22985718399fSFrançois Tigeot 	struct drm_encoder *encoder;
22992c9916cdSFrançois Tigeot 	struct drm_crtc *crtc;
23005718399fSFrançois Tigeot 
23015718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2302b5162e19SFrançois Tigeot 		return -EINVAL;
23035718399fSFrançois Tigeot 
2304ba55f2f5SFrançois Tigeot 	encoder = drm_encoder_find(dev, enc_resp->encoder_id);
23052c9916cdSFrançois Tigeot 	if (!encoder)
23062c9916cdSFrançois Tigeot 		return -ENOENT;
23075718399fSFrançois Tigeot 
23082c9916cdSFrançois Tigeot 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
23092c9916cdSFrançois Tigeot 	crtc = drm_encoder_get_crtc(encoder);
23102c9916cdSFrançois Tigeot 	if (crtc)
23112c9916cdSFrançois Tigeot 		enc_resp->crtc_id = crtc->base.id;
23125718399fSFrançois Tigeot 	else
23135718399fSFrançois Tigeot 		enc_resp->crtc_id = 0;
23142c9916cdSFrançois Tigeot 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
23152c9916cdSFrançois Tigeot 
23165718399fSFrançois Tigeot 	enc_resp->encoder_type = encoder->encoder_type;
23175718399fSFrançois Tigeot 	enc_resp->encoder_id = encoder->base.id;
23185718399fSFrançois Tigeot 	enc_resp->possible_crtcs = encoder->possible_crtcs;
23195718399fSFrançois Tigeot 	enc_resp->possible_clones = encoder->possible_clones;
23205718399fSFrançois Tigeot 
23212c9916cdSFrançois Tigeot 	return 0;
23225718399fSFrançois Tigeot }
23235718399fSFrançois Tigeot 
23245718399fSFrançois Tigeot /**
2325ba55f2f5SFrançois Tigeot  * drm_mode_getplane_res - enumerate all plane resources
23265718399fSFrançois Tigeot  * @dev: DRM device
23275718399fSFrançois Tigeot  * @data: ioctl data
23285718399fSFrançois Tigeot  * @file_priv: DRM file info
23295718399fSFrançois Tigeot  *
2330ba55f2f5SFrançois Tigeot  * Construct a list of plane ids to return to the user.
2331ba55f2f5SFrançois Tigeot  *
2332ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2333ba55f2f5SFrançois Tigeot  *
2334ba55f2f5SFrançois Tigeot  * Returns:
23352c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
23365718399fSFrançois Tigeot  */
23375718399fSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data,
23385718399fSFrançois Tigeot 			  struct drm_file *file_priv)
23395718399fSFrançois Tigeot {
23405718399fSFrançois Tigeot 	struct drm_mode_get_plane_res *plane_resp = data;
23415718399fSFrançois Tigeot 	struct drm_mode_config *config;
23425718399fSFrançois Tigeot 	struct drm_plane *plane;
2343b5162e19SFrançois Tigeot 	uint32_t __user *plane_ptr;
23442c9916cdSFrançois Tigeot 	int copied = 0;
2345ba55f2f5SFrançois Tigeot 	unsigned num_planes;
23465718399fSFrançois Tigeot 
23475718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2348b5162e19SFrançois Tigeot 		return -EINVAL;
23495718399fSFrançois Tigeot 
23505718399fSFrançois Tigeot 	config = &dev->mode_config;
23515718399fSFrançois Tigeot 
2352ba55f2f5SFrançois Tigeot 	if (file_priv->universal_planes)
2353ba55f2f5SFrançois Tigeot 		num_planes = config->num_total_plane;
2354ba55f2f5SFrançois Tigeot 	else
2355ba55f2f5SFrançois Tigeot 		num_planes = config->num_overlay_plane;
2356ba55f2f5SFrançois Tigeot 
23575718399fSFrançois Tigeot 	/*
23585718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
23595718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
23605718399fSFrançois Tigeot 	 */
2361ba55f2f5SFrançois Tigeot 	if (num_planes &&
2362ba55f2f5SFrançois Tigeot 	    (plane_resp->count_planes >= num_planes)) {
2363b5162e19SFrançois Tigeot 		plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
23645718399fSFrançois Tigeot 
23652c9916cdSFrançois Tigeot 		/* Plane lists are invariant, no locking needed. */
2366a05eeebfSFrançois Tigeot 		drm_for_each_plane(plane, dev) {
2367ba55f2f5SFrançois Tigeot 			/*
2368ba55f2f5SFrançois Tigeot 			 * Unless userspace set the 'universal planes'
2369ba55f2f5SFrançois Tigeot 			 * capability bit, only advertise overlays.
2370ba55f2f5SFrançois Tigeot 			 */
2371ba55f2f5SFrançois Tigeot 			if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
2372ba55f2f5SFrançois Tigeot 			    !file_priv->universal_planes)
2373ba55f2f5SFrançois Tigeot 				continue;
2374ba55f2f5SFrançois Tigeot 
23752c9916cdSFrançois Tigeot 			if (put_user(plane->base.id, plane_ptr + copied))
23762c9916cdSFrançois Tigeot 				return -EFAULT;
23775718399fSFrançois Tigeot 			copied++;
23785718399fSFrançois Tigeot 		}
23795718399fSFrançois Tigeot 	}
2380ba55f2f5SFrançois Tigeot 	plane_resp->count_planes = num_planes;
23815718399fSFrançois Tigeot 
23822c9916cdSFrançois Tigeot 	return 0;
23835718399fSFrançois Tigeot }
23845718399fSFrançois Tigeot 
23855718399fSFrançois Tigeot /**
2386ba55f2f5SFrançois Tigeot  * drm_mode_getplane - get plane configuration
23875718399fSFrançois Tigeot  * @dev: DRM device
23885718399fSFrançois Tigeot  * @data: ioctl data
23895718399fSFrançois Tigeot  * @file_priv: DRM file info
23905718399fSFrançois Tigeot  *
2391ba55f2f5SFrançois Tigeot  * Construct a plane configuration structure to return to the user.
2392ba55f2f5SFrançois Tigeot  *
2393ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
2394ba55f2f5SFrançois Tigeot  *
2395ba55f2f5SFrançois Tigeot  * Returns:
23962c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
23975718399fSFrançois Tigeot  */
23985718399fSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data,
23995718399fSFrançois Tigeot 		      struct drm_file *file_priv)
24005718399fSFrançois Tigeot {
24015718399fSFrançois Tigeot 	struct drm_mode_get_plane *plane_resp = data;
24025718399fSFrançois Tigeot 	struct drm_plane *plane;
2403b5162e19SFrançois Tigeot 	uint32_t __user *format_ptr;
24045718399fSFrançois Tigeot 
24055718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2406b5162e19SFrançois Tigeot 		return -EINVAL;
24075718399fSFrançois Tigeot 
2408ba55f2f5SFrançois Tigeot 	plane = drm_plane_find(dev, plane_resp->plane_id);
24092c9916cdSFrançois Tigeot 	if (!plane)
24102c9916cdSFrançois Tigeot 		return -ENOENT;
24115718399fSFrançois Tigeot 
24122c9916cdSFrançois Tigeot 	drm_modeset_lock(&plane->mutex, NULL);
24135718399fSFrançois Tigeot 	if (plane->crtc)
24145718399fSFrançois Tigeot 		plane_resp->crtc_id = plane->crtc->base.id;
24155718399fSFrançois Tigeot 	else
24165718399fSFrançois Tigeot 		plane_resp->crtc_id = 0;
24175718399fSFrançois Tigeot 
24185718399fSFrançois Tigeot 	if (plane->fb)
24195718399fSFrançois Tigeot 		plane_resp->fb_id = plane->fb->base.id;
24205718399fSFrançois Tigeot 	else
24215718399fSFrançois Tigeot 		plane_resp->fb_id = 0;
24222c9916cdSFrançois Tigeot 	drm_modeset_unlock(&plane->mutex);
24235718399fSFrançois Tigeot 
24245718399fSFrançois Tigeot 	plane_resp->plane_id = plane->base.id;
24255718399fSFrançois Tigeot 	plane_resp->possible_crtcs = plane->possible_crtcs;
242606fede5aSFrançois Tigeot 	plane_resp->gamma_size = 0;
24275718399fSFrançois Tigeot 
24285718399fSFrançois Tigeot 	/*
24295718399fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
24305718399fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
24315718399fSFrançois Tigeot 	 */
24325718399fSFrançois Tigeot 	if (plane->format_count &&
24335718399fSFrançois Tigeot 	    (plane_resp->count_format_types >= plane->format_count)) {
2434b5162e19SFrançois Tigeot 		format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
2435b5162e19SFrançois Tigeot 		if (copy_to_user(format_ptr,
24365718399fSFrançois Tigeot 				 plane->format_types,
24375718399fSFrançois Tigeot 				 sizeof(uint32_t) * plane->format_count)) {
24382c9916cdSFrançois Tigeot 			return -EFAULT;
24395718399fSFrançois Tigeot 		}
24405718399fSFrançois Tigeot 	}
24415718399fSFrançois Tigeot 	plane_resp->count_format_types = plane->format_count;
24425718399fSFrançois Tigeot 
24432c9916cdSFrançois Tigeot 	return 0;
24445718399fSFrançois Tigeot }
24455718399fSFrançois Tigeot 
2446477eb7f9SFrançois Tigeot /**
2447477eb7f9SFrançois Tigeot  * drm_plane_check_pixel_format - Check if the plane supports the pixel format
2448477eb7f9SFrançois Tigeot  * @plane: plane to check for format support
2449477eb7f9SFrançois Tigeot  * @format: the pixel format
2450477eb7f9SFrançois Tigeot  *
2451477eb7f9SFrançois Tigeot  * Returns:
2452477eb7f9SFrançois Tigeot  * Zero of @plane has @format in its list of supported pixel formats, -EINVAL
2453477eb7f9SFrançois Tigeot  * otherwise.
2454477eb7f9SFrançois Tigeot  */
2455477eb7f9SFrançois Tigeot int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
2456477eb7f9SFrançois Tigeot {
2457477eb7f9SFrançois Tigeot 	unsigned int i;
2458477eb7f9SFrançois Tigeot 
2459477eb7f9SFrançois Tigeot 	for (i = 0; i < plane->format_count; i++) {
2460477eb7f9SFrançois Tigeot 		if (format == plane->format_types[i])
2461477eb7f9SFrançois Tigeot 			return 0;
2462477eb7f9SFrançois Tigeot 	}
2463477eb7f9SFrançois Tigeot 
2464477eb7f9SFrançois Tigeot 	return -EINVAL;
2465477eb7f9SFrançois Tigeot }
2466477eb7f9SFrançois Tigeot 
2467352ff8bdSFrançois Tigeot static int check_src_coords(uint32_t src_x, uint32_t src_y,
2468352ff8bdSFrançois Tigeot 			    uint32_t src_w, uint32_t src_h,
2469352ff8bdSFrançois Tigeot 			    const struct drm_framebuffer *fb)
2470352ff8bdSFrançois Tigeot {
2471352ff8bdSFrançois Tigeot 	unsigned int fb_width, fb_height;
2472352ff8bdSFrançois Tigeot 
2473352ff8bdSFrançois Tigeot 	fb_width = fb->width << 16;
2474352ff8bdSFrançois Tigeot 	fb_height = fb->height << 16;
2475352ff8bdSFrançois Tigeot 
2476352ff8bdSFrançois Tigeot 	/* Make sure source coordinates are inside the fb. */
2477352ff8bdSFrançois Tigeot 	if (src_w > fb_width ||
2478352ff8bdSFrançois Tigeot 	    src_x > fb_width - src_w ||
2479352ff8bdSFrançois Tigeot 	    src_h > fb_height ||
2480352ff8bdSFrançois Tigeot 	    src_y > fb_height - src_h) {
2481352ff8bdSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid source coordinates "
2482352ff8bdSFrançois Tigeot 			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
2483352ff8bdSFrançois Tigeot 			      src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
2484352ff8bdSFrançois Tigeot 			      src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
2485352ff8bdSFrançois Tigeot 			      src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
2486352ff8bdSFrançois Tigeot 			      src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
2487352ff8bdSFrançois Tigeot 		return -ENOSPC;
2488352ff8bdSFrançois Tigeot 	}
2489352ff8bdSFrançois Tigeot 
2490352ff8bdSFrançois Tigeot 	return 0;
2491352ff8bdSFrançois Tigeot }
2492352ff8bdSFrançois Tigeot 
249324edb884SFrançois Tigeot /*
249424edb884SFrançois Tigeot  * setplane_internal - setplane handler for internal callers
24955718399fSFrançois Tigeot  *
249624edb884SFrançois Tigeot  * Note that we assume an extra reference has already been taken on fb.  If the
249724edb884SFrançois Tigeot  * update fails, this reference will be dropped before return; if it succeeds,
249824edb884SFrançois Tigeot  * the previous framebuffer (if any) will be unreferenced instead.
2499ba55f2f5SFrançois Tigeot  *
250024edb884SFrançois Tigeot  * src_{x,y,w,h} are provided in 16.16 fixed point format
25015718399fSFrançois Tigeot  */
25021b13d190SFrançois Tigeot static int __setplane_internal(struct drm_plane *plane,
250324edb884SFrançois Tigeot 			       struct drm_crtc *crtc,
250424edb884SFrançois Tigeot 			       struct drm_framebuffer *fb,
250524edb884SFrançois Tigeot 			       int32_t crtc_x, int32_t crtc_y,
250624edb884SFrançois Tigeot 			       uint32_t crtc_w, uint32_t crtc_h,
250724edb884SFrançois Tigeot 			       /* src_{x,y,w,h} values are 16.16 fixed point */
250824edb884SFrançois Tigeot 			       uint32_t src_x, uint32_t src_y,
250924edb884SFrançois Tigeot 			       uint32_t src_w, uint32_t src_h)
25105718399fSFrançois Tigeot {
25115718399fSFrançois Tigeot 	int ret = 0;
25125718399fSFrançois Tigeot 
25135718399fSFrançois Tigeot 	/* No fb means shut it down */
251424edb884SFrançois Tigeot 	if (!fb) {
25151b13d190SFrançois Tigeot 		plane->old_fb = plane->fb;
2516ba55f2f5SFrançois Tigeot 		ret = plane->funcs->disable_plane(plane);
2517ba55f2f5SFrançois Tigeot 		if (!ret) {
25185718399fSFrançois Tigeot 			plane->crtc = NULL;
25195718399fSFrançois Tigeot 			plane->fb = NULL;
2520ba55f2f5SFrançois Tigeot 		} else {
25211b13d190SFrançois Tigeot 			plane->old_fb = NULL;
2522ba55f2f5SFrançois Tigeot 		}
25235718399fSFrançois Tigeot 		goto out;
25245718399fSFrançois Tigeot 	}
25255718399fSFrançois Tigeot 
252624edb884SFrançois Tigeot 	/* Check whether this plane is usable on this CRTC */
252724edb884SFrançois Tigeot 	if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
252824edb884SFrançois Tigeot 		DRM_DEBUG_KMS("Invalid crtc for plane\n");
252924edb884SFrançois Tigeot 		ret = -EINVAL;
25305718399fSFrançois Tigeot 		goto out;
25315718399fSFrançois Tigeot 	}
25325718399fSFrançois Tigeot 
25335718399fSFrançois Tigeot 	/* Check whether this plane supports the fb pixel format. */
2534477eb7f9SFrançois Tigeot 	ret = drm_plane_check_pixel_format(plane, fb->pixel_format);
2535477eb7f9SFrançois Tigeot 	if (ret) {
253606fede5aSFrançois Tigeot 		DRM_DEBUG_KMS("Invalid pixel format %s\n",
253706fede5aSFrançois Tigeot 			      drm_get_format_name(fb->pixel_format));
25385718399fSFrançois Tigeot 		goto out;
25395718399fSFrançois Tigeot 	}
25405718399fSFrançois Tigeot 
2541477eb7f9SFrançois Tigeot 	/* Give drivers some help against integer overflows */
2542477eb7f9SFrançois Tigeot 	if (crtc_w > INT_MAX ||
2543477eb7f9SFrançois Tigeot 	    crtc_x > INT_MAX - (int32_t) crtc_w ||
2544477eb7f9SFrançois Tigeot 	    crtc_h > INT_MAX ||
2545477eb7f9SFrançois Tigeot 	    crtc_y > INT_MAX - (int32_t) crtc_h) {
2546477eb7f9SFrançois Tigeot 		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
2547477eb7f9SFrançois Tigeot 			      crtc_w, crtc_h, crtc_x, crtc_y);
2548352ff8bdSFrançois Tigeot 		ret = -ERANGE;
25495718399fSFrançois Tigeot 		goto out;
25505718399fSFrançois Tigeot 	}
25515718399fSFrançois Tigeot 
2552352ff8bdSFrançois Tigeot 	ret = check_src_coords(src_x, src_y, src_w, src_h, fb);
2553352ff8bdSFrançois Tigeot 	if (ret)
2554352ff8bdSFrançois Tigeot 		goto out;
2555352ff8bdSFrançois Tigeot 
25561b13d190SFrançois Tigeot 	plane->old_fb = plane->fb;
2557b5162e19SFrançois Tigeot 	ret = plane->funcs->update_plane(plane, crtc, fb,
255824edb884SFrançois Tigeot 					 crtc_x, crtc_y, crtc_w, crtc_h,
255924edb884SFrançois Tigeot 					 src_x, src_y, src_w, src_h);
25605718399fSFrançois Tigeot 	if (!ret) {
25615718399fSFrançois Tigeot 		plane->crtc = crtc;
25625718399fSFrançois Tigeot 		plane->fb = fb;
25634dbb207bSFrançois Tigeot 		fb = NULL;
2564ba55f2f5SFrançois Tigeot 	} else {
25651b13d190SFrançois Tigeot 		plane->old_fb = NULL;
25665718399fSFrançois Tigeot 	}
25675718399fSFrançois Tigeot 
25685718399fSFrançois Tigeot out:
25694dbb207bSFrançois Tigeot 	if (fb)
25704dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
25711b13d190SFrançois Tigeot 	if (plane->old_fb)
25721b13d190SFrançois Tigeot 		drm_framebuffer_unreference(plane->old_fb);
25731b13d190SFrançois Tigeot 	plane->old_fb = NULL;
25745718399fSFrançois Tigeot 
25755718399fSFrançois Tigeot 	return ret;
25761b13d190SFrançois Tigeot }
257724edb884SFrançois Tigeot 
25781b13d190SFrançois Tigeot static int setplane_internal(struct drm_plane *plane,
25791b13d190SFrançois Tigeot 			     struct drm_crtc *crtc,
25801b13d190SFrançois Tigeot 			     struct drm_framebuffer *fb,
25811b13d190SFrançois Tigeot 			     int32_t crtc_x, int32_t crtc_y,
25821b13d190SFrançois Tigeot 			     uint32_t crtc_w, uint32_t crtc_h,
25831b13d190SFrançois Tigeot 			     /* src_{x,y,w,h} values are 16.16 fixed point */
25841b13d190SFrançois Tigeot 			     uint32_t src_x, uint32_t src_y,
25851b13d190SFrançois Tigeot 			     uint32_t src_w, uint32_t src_h)
25861b13d190SFrançois Tigeot {
25871b13d190SFrançois Tigeot 	int ret;
25881b13d190SFrançois Tigeot 
25891b13d190SFrançois Tigeot 	drm_modeset_lock_all(plane->dev);
25901b13d190SFrançois Tigeot 	ret = __setplane_internal(plane, crtc, fb,
25911b13d190SFrançois Tigeot 				  crtc_x, crtc_y, crtc_w, crtc_h,
25921b13d190SFrançois Tigeot 				  src_x, src_y, src_w, src_h);
25931b13d190SFrançois Tigeot 	drm_modeset_unlock_all(plane->dev);
25941b13d190SFrançois Tigeot 
25951b13d190SFrançois Tigeot 	return ret;
259624edb884SFrançois Tigeot }
259724edb884SFrançois Tigeot 
259824edb884SFrançois Tigeot /**
259924edb884SFrançois Tigeot  * drm_mode_setplane - configure a plane's configuration
260024edb884SFrançois Tigeot  * @dev: DRM device
260124edb884SFrançois Tigeot  * @data: ioctl data*
260224edb884SFrançois Tigeot  * @file_priv: DRM file info
260324edb884SFrançois Tigeot  *
260424edb884SFrançois Tigeot  * Set plane configuration, including placement, fb, scaling, and other factors.
260524edb884SFrançois Tigeot  * Or pass a NULL fb to disable (planes may be disabled without providing a
260624edb884SFrançois Tigeot  * valid crtc).
260724edb884SFrançois Tigeot  *
260824edb884SFrançois Tigeot  * Returns:
26092c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
261024edb884SFrançois Tigeot  */
261124edb884SFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data,
261224edb884SFrançois Tigeot 		      struct drm_file *file_priv)
261324edb884SFrançois Tigeot {
261424edb884SFrançois Tigeot 	struct drm_mode_set_plane *plane_req = data;
261524edb884SFrançois Tigeot 	struct drm_plane *plane;
261624edb884SFrançois Tigeot 	struct drm_crtc *crtc = NULL;
261724edb884SFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
261824edb884SFrançois Tigeot 
261924edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
262024edb884SFrançois Tigeot 		return -EINVAL;
262124edb884SFrançois Tigeot 
262224edb884SFrançois Tigeot 	/*
262324edb884SFrançois Tigeot 	 * First, find the plane, crtc, and fb objects.  If not available,
262424edb884SFrançois Tigeot 	 * we don't bother to call the driver.
262524edb884SFrançois Tigeot 	 */
26262c9916cdSFrançois Tigeot 	plane = drm_plane_find(dev, plane_req->plane_id);
26272c9916cdSFrançois Tigeot 	if (!plane) {
262824edb884SFrançois Tigeot 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
262924edb884SFrançois Tigeot 			      plane_req->plane_id);
263024edb884SFrançois Tigeot 		return -ENOENT;
263124edb884SFrançois Tigeot 	}
263224edb884SFrançois Tigeot 
263324edb884SFrançois Tigeot 	if (plane_req->fb_id) {
263424edb884SFrançois Tigeot 		fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
263524edb884SFrançois Tigeot 		if (!fb) {
263624edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
263724edb884SFrançois Tigeot 				      plane_req->fb_id);
263824edb884SFrançois Tigeot 			return -ENOENT;
263924edb884SFrançois Tigeot 		}
264024edb884SFrançois Tigeot 
26412c9916cdSFrançois Tigeot 		crtc = drm_crtc_find(dev, plane_req->crtc_id);
26422c9916cdSFrançois Tigeot 		if (!crtc) {
264324edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Unknown crtc ID %d\n",
264424edb884SFrançois Tigeot 				      plane_req->crtc_id);
264524edb884SFrançois Tigeot 			return -ENOENT;
264624edb884SFrançois Tigeot 		}
264724edb884SFrançois Tigeot 	}
264824edb884SFrançois Tigeot 
264924edb884SFrançois Tigeot 	/*
265024edb884SFrançois Tigeot 	 * setplane_internal will take care of deref'ing either the old or new
265124edb884SFrançois Tigeot 	 * framebuffer depending on success.
265224edb884SFrançois Tigeot 	 */
265324edb884SFrançois Tigeot 	return setplane_internal(plane, crtc, fb,
265424edb884SFrançois Tigeot 				 plane_req->crtc_x, plane_req->crtc_y,
265524edb884SFrançois Tigeot 				 plane_req->crtc_w, plane_req->crtc_h,
265624edb884SFrançois Tigeot 				 plane_req->src_x, plane_req->src_y,
265724edb884SFrançois Tigeot 				 plane_req->src_w, plane_req->src_h);
26585718399fSFrançois Tigeot }
26595718399fSFrançois Tigeot 
26605718399fSFrançois Tigeot /**
26614dbb207bSFrançois Tigeot  * drm_mode_set_config_internal - helper to call ->set_config
26624dbb207bSFrançois Tigeot  * @set: modeset config to set
26635718399fSFrançois Tigeot  *
26644dbb207bSFrançois Tigeot  * This is a little helper to wrap internal calls to the ->set_config driver
26654dbb207bSFrançois Tigeot  * interface. The only thing it adds is correct refcounting dance.
2666ba55f2f5SFrançois Tigeot  *
2667ba55f2f5SFrançois Tigeot  * Returns:
26682c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
26694dbb207bSFrançois Tigeot  */
26704dbb207bSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set)
26714dbb207bSFrançois Tigeot {
26724dbb207bSFrançois Tigeot 	struct drm_crtc *crtc = set->crtc;
267306fede5aSFrançois Tigeot 	struct drm_framebuffer *fb;
267406fede5aSFrançois Tigeot 	struct drm_crtc *tmp;
26754dbb207bSFrançois Tigeot 	int ret;
26764dbb207bSFrançois Tigeot 
267706fede5aSFrançois Tigeot 	/*
267806fede5aSFrançois Tigeot 	 * NOTE: ->set_config can also disable other crtcs (if we steal all
267906fede5aSFrançois Tigeot 	 * connectors from it), hence we need to refcount the fbs across all
268006fede5aSFrançois Tigeot 	 * crtcs. Atomic modeset will have saner semantics ...
268106fede5aSFrançois Tigeot 	 */
2682a05eeebfSFrançois Tigeot 	drm_for_each_crtc(tmp, crtc->dev)
26831b13d190SFrançois Tigeot 		tmp->primary->old_fb = tmp->primary->fb;
268406fede5aSFrançois Tigeot 
26854dbb207bSFrançois Tigeot 	fb = set->fb;
26864dbb207bSFrançois Tigeot 
26874dbb207bSFrançois Tigeot 	ret = crtc->funcs->set_config(set);
26884dbb207bSFrançois Tigeot 	if (ret == 0) {
2689ba55f2f5SFrançois Tigeot 		crtc->primary->crtc = crtc;
2690ba55f2f5SFrançois Tigeot 		crtc->primary->fb = fb;
269106fede5aSFrançois Tigeot 	}
269206fede5aSFrançois Tigeot 
2693a05eeebfSFrançois Tigeot 	drm_for_each_crtc(tmp, crtc->dev) {
2694ba55f2f5SFrançois Tigeot 		if (tmp->primary->fb)
2695ba55f2f5SFrançois Tigeot 			drm_framebuffer_reference(tmp->primary->fb);
26961b13d190SFrançois Tigeot 		if (tmp->primary->old_fb)
26971b13d190SFrançois Tigeot 			drm_framebuffer_unreference(tmp->primary->old_fb);
26981b13d190SFrançois Tigeot 		tmp->primary->old_fb = NULL;
26994dbb207bSFrançois Tigeot 	}
27004dbb207bSFrançois Tigeot 
27014dbb207bSFrançois Tigeot 	return ret;
27024dbb207bSFrançois Tigeot }
27034dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal);
27044dbb207bSFrançois Tigeot 
2705ba55f2f5SFrançois Tigeot /**
27062c9916cdSFrançois Tigeot  * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode
27072c9916cdSFrançois Tigeot  * @mode: mode to query
27082c9916cdSFrançois Tigeot  * @hdisplay: hdisplay value to fill in
27092c9916cdSFrançois Tigeot  * @vdisplay: vdisplay value to fill in
27102c9916cdSFrançois Tigeot  *
27112c9916cdSFrançois Tigeot  * The vdisplay value will be doubled if the specified mode is a stereo mode of
27122c9916cdSFrançois Tigeot  * the appropriate layout.
27132c9916cdSFrançois Tigeot  */
27142c9916cdSFrançois Tigeot void drm_crtc_get_hv_timing(const struct drm_display_mode *mode,
27152c9916cdSFrançois Tigeot 			    int *hdisplay, int *vdisplay)
27162c9916cdSFrançois Tigeot {
27172c9916cdSFrançois Tigeot 	struct drm_display_mode adjusted;
27182c9916cdSFrançois Tigeot 
27192c9916cdSFrançois Tigeot 	drm_mode_copy(&adjusted, mode);
27202c9916cdSFrançois Tigeot 	drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
27212c9916cdSFrançois Tigeot 	*hdisplay = adjusted.crtc_hdisplay;
27222c9916cdSFrançois Tigeot 	*vdisplay = adjusted.crtc_vdisplay;
27232c9916cdSFrançois Tigeot }
27242c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_crtc_get_hv_timing);
27252c9916cdSFrançois Tigeot 
27262c9916cdSFrançois Tigeot /**
2727ba55f2f5SFrançois Tigeot  * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
2728ba55f2f5SFrançois Tigeot  *     CRTC viewport
2729ba55f2f5SFrançois Tigeot  * @crtc: CRTC that framebuffer will be displayed on
2730ba55f2f5SFrançois Tigeot  * @x: x panning
2731ba55f2f5SFrançois Tigeot  * @y: y panning
2732ba55f2f5SFrançois Tigeot  * @mode: mode that framebuffer will be displayed under
2733ba55f2f5SFrançois Tigeot  * @fb: framebuffer to check size of
27349edbd4a0SFrançois Tigeot  */
2735ba55f2f5SFrançois Tigeot int drm_crtc_check_viewport(const struct drm_crtc *crtc,
27369edbd4a0SFrançois Tigeot 			    int x, int y,
27379edbd4a0SFrançois Tigeot 			    const struct drm_display_mode *mode,
27389edbd4a0SFrançois Tigeot 			    const struct drm_framebuffer *fb)
27399edbd4a0SFrançois Tigeot 
27409edbd4a0SFrançois Tigeot {
27419edbd4a0SFrançois Tigeot 	int hdisplay, vdisplay;
27429edbd4a0SFrançois Tigeot 
27432c9916cdSFrançois Tigeot 	drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
27449edbd4a0SFrançois Tigeot 
2745352ff8bdSFrançois Tigeot 	if (crtc->state &&
2746352ff8bdSFrançois Tigeot 	    crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) |
2747352ff8bdSFrançois Tigeot 					      BIT(DRM_ROTATE_270)))
27489edbd4a0SFrançois Tigeot 		swap(hdisplay, vdisplay);
27499edbd4a0SFrançois Tigeot 
2750352ff8bdSFrançois Tigeot 	return check_src_coords(x << 16, y << 16,
2751352ff8bdSFrançois Tigeot 				hdisplay << 16, vdisplay << 16, fb);
27529edbd4a0SFrançois Tigeot }
2753ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_check_viewport);
27549edbd4a0SFrançois Tigeot 
27554dbb207bSFrançois Tigeot /**
27564dbb207bSFrançois Tigeot  * drm_mode_setcrtc - set CRTC configuration
27574dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
27584dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
27594dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
27605718399fSFrançois Tigeot  *
27615718399fSFrançois Tigeot  * Build a new CRTC configuration based on user request.
27625718399fSFrançois Tigeot  *
27635718399fSFrançois Tigeot  * Called by the user via ioctl.
27645718399fSFrançois Tigeot  *
2765ba55f2f5SFrançois Tigeot  * Returns:
27662c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
27675718399fSFrançois Tigeot  */
27685718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data,
27695718399fSFrançois Tigeot 		     struct drm_file *file_priv)
27705718399fSFrançois Tigeot {
27715718399fSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
27725718399fSFrançois Tigeot 	struct drm_mode_crtc *crtc_req = data;
27735718399fSFrançois Tigeot 	struct drm_crtc *crtc;
27745718399fSFrançois Tigeot 	struct drm_connector **connector_set = NULL, *connector;
27755718399fSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
27765718399fSFrançois Tigeot 	struct drm_display_mode *mode = NULL;
27775718399fSFrançois Tigeot 	struct drm_mode_set set;
2778b5162e19SFrançois Tigeot 	uint32_t __user *set_connectors_ptr;
2779b5162e19SFrançois Tigeot 	int ret;
27805718399fSFrançois Tigeot 	int i;
27815718399fSFrançois Tigeot 
27825718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
2783b5162e19SFrançois Tigeot 		return -EINVAL;
27845718399fSFrançois Tigeot 
278519c468b4SFrançois Tigeot 	/*
278619c468b4SFrançois Tigeot 	 * Universal plane src offsets are only 16.16, prevent havoc for
278719c468b4SFrançois Tigeot 	 * drivers using universal plane code internally.
278819c468b4SFrançois Tigeot 	 */
278919c468b4SFrançois Tigeot 	if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
2790b5162e19SFrançois Tigeot 		return -ERANGE;
27915718399fSFrançois Tigeot 
27924dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
2793ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
2794ba55f2f5SFrançois Tigeot 	if (!crtc) {
27955718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
27969edbd4a0SFrançois Tigeot 		ret = -ENOENT;
27975718399fSFrançois Tigeot 		goto out;
27985718399fSFrançois Tigeot 	}
2799aee94f86SFrançois Tigeot 	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
28005718399fSFrançois Tigeot 
28015718399fSFrançois Tigeot 	if (crtc_req->mode_valid) {
28025718399fSFrançois Tigeot 		/* If we have a mode we need a framebuffer. */
28035718399fSFrançois Tigeot 		/* If we pass -1, set the mode with the currently bound fb */
28045718399fSFrançois Tigeot 		if (crtc_req->fb_id == -1) {
2805ba55f2f5SFrançois Tigeot 			if (!crtc->primary->fb) {
28065718399fSFrançois Tigeot 				DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
28075718399fSFrançois Tigeot 				ret = -EINVAL;
28085718399fSFrançois Tigeot 				goto out;
28095718399fSFrançois Tigeot 			}
2810ba55f2f5SFrançois Tigeot 			fb = crtc->primary->fb;
28114dbb207bSFrançois Tigeot 			/* Make refcounting symmetric with the lookup path. */
28124dbb207bSFrançois Tigeot 			drm_framebuffer_reference(fb);
28135718399fSFrançois Tigeot 		} else {
28144dbb207bSFrançois Tigeot 			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
28154dbb207bSFrançois Tigeot 			if (!fb) {
28165718399fSFrançois Tigeot 				DRM_DEBUG_KMS("Unknown FB ID%d\n",
28175718399fSFrançois Tigeot 						crtc_req->fb_id);
28189edbd4a0SFrançois Tigeot 				ret = -ENOENT;
28195718399fSFrançois Tigeot 				goto out;
28205718399fSFrançois Tigeot 			}
28215718399fSFrançois Tigeot 		}
28225718399fSFrançois Tigeot 
28235718399fSFrançois Tigeot 		mode = drm_mode_create(dev);
28245718399fSFrançois Tigeot 		if (!mode) {
2825b5162e19SFrançois Tigeot 			ret = -ENOMEM;
28265718399fSFrançois Tigeot 			goto out;
28275718399fSFrançois Tigeot 		}
28285718399fSFrançois Tigeot 
282919c468b4SFrançois Tigeot 		ret = drm_mode_convert_umode(mode, &crtc_req->mode);
28305718399fSFrançois Tigeot 		if (ret) {
28315718399fSFrançois Tigeot 			DRM_DEBUG_KMS("Invalid mode\n");
28325718399fSFrançois Tigeot 			goto out;
28335718399fSFrançois Tigeot 		}
28345718399fSFrançois Tigeot 
2835477eb7f9SFrançois Tigeot 		/*
2836477eb7f9SFrançois Tigeot 		 * Check whether the primary plane supports the fb pixel format.
2837477eb7f9SFrançois Tigeot 		 * Drivers not implementing the universal planes API use a
2838477eb7f9SFrançois Tigeot 		 * default formats list provided by the DRM core which doesn't
2839477eb7f9SFrançois Tigeot 		 * match real hardware capabilities. Skip the check in that
2840477eb7f9SFrançois Tigeot 		 * case.
2841477eb7f9SFrançois Tigeot 		 */
2842477eb7f9SFrançois Tigeot 		if (!crtc->primary->format_default) {
2843477eb7f9SFrançois Tigeot 			ret = drm_plane_check_pixel_format(crtc->primary,
2844477eb7f9SFrançois Tigeot 							   fb->pixel_format);
2845477eb7f9SFrançois Tigeot 			if (ret) {
2846477eb7f9SFrançois Tigeot 				DRM_DEBUG_KMS("Invalid pixel format %s\n",
2847477eb7f9SFrançois Tigeot 					drm_get_format_name(fb->pixel_format));
2848477eb7f9SFrançois Tigeot 				goto out;
2849477eb7f9SFrançois Tigeot 			}
2850477eb7f9SFrançois Tigeot 		}
2851477eb7f9SFrançois Tigeot 
28529edbd4a0SFrançois Tigeot 		ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
28539edbd4a0SFrançois Tigeot 					      mode, fb);
28549edbd4a0SFrançois Tigeot 		if (ret)
28555718399fSFrançois Tigeot 			goto out;
28569edbd4a0SFrançois Tigeot 
28575718399fSFrançois Tigeot 	}
28585718399fSFrançois Tigeot 
28595718399fSFrançois Tigeot 	if (crtc_req->count_connectors == 0 && mode) {
28605718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
2861b5162e19SFrançois Tigeot 		ret = -EINVAL;
28625718399fSFrançois Tigeot 		goto out;
28635718399fSFrançois Tigeot 	}
28645718399fSFrançois Tigeot 
28655718399fSFrançois Tigeot 	if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
28665718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
28675718399fSFrançois Tigeot 			  crtc_req->count_connectors);
2868b5162e19SFrançois Tigeot 		ret = -EINVAL;
28695718399fSFrançois Tigeot 		goto out;
28705718399fSFrançois Tigeot 	}
28715718399fSFrançois Tigeot 
28725718399fSFrançois Tigeot 	if (crtc_req->count_connectors > 0) {
28735718399fSFrançois Tigeot 		u32 out_id;
28745718399fSFrançois Tigeot 
28755718399fSFrançois Tigeot 		/* Avoid unbounded kernel memory allocation */
28765718399fSFrançois Tigeot 		if (crtc_req->count_connectors > config->num_connector) {
2877b5162e19SFrançois Tigeot 			ret = -EINVAL;
28785718399fSFrançois Tigeot 			goto out;
28795718399fSFrançois Tigeot 		}
28805718399fSFrançois Tigeot 
28815718399fSFrançois Tigeot 		connector_set = kmalloc(crtc_req->count_connectors *
28824dbb207bSFrançois Tigeot 					sizeof(struct drm_connector *),
28834dbb207bSFrançois Tigeot 					M_DRM, M_WAITOK);
2884b5162e19SFrançois Tigeot 		if (!connector_set) {
2885b5162e19SFrançois Tigeot 			ret = -ENOMEM;
2886b5162e19SFrançois Tigeot 			goto out;
2887b5162e19SFrançois Tigeot 		}
28885718399fSFrançois Tigeot 
28895718399fSFrançois Tigeot 		for (i = 0; i < crtc_req->count_connectors; i++) {
28908621f407SFrançois Tigeot 			connector_set[i] = NULL;
2891b5162e19SFrançois Tigeot 			set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
2892b5162e19SFrançois Tigeot 			if (get_user(out_id, &set_connectors_ptr[i])) {
2893b5162e19SFrançois Tigeot 				ret = -EFAULT;
28945718399fSFrançois Tigeot 				goto out;
28955718399fSFrançois Tigeot 			}
28965718399fSFrançois Tigeot 
28978621f407SFrançois Tigeot 			connector = drm_connector_lookup(dev, out_id);
2898ba55f2f5SFrançois Tigeot 			if (!connector) {
28995718399fSFrançois Tigeot 				DRM_DEBUG_KMS("Connector id %d unknown\n",
29005718399fSFrançois Tigeot 						out_id);
29019edbd4a0SFrançois Tigeot 				ret = -ENOENT;
29025718399fSFrançois Tigeot 				goto out;
29035718399fSFrançois Tigeot 			}
29045718399fSFrançois Tigeot 			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
29055718399fSFrançois Tigeot 					connector->base.id,
2906ba55f2f5SFrançois Tigeot 					connector->name);
29075718399fSFrançois Tigeot 
29085718399fSFrançois Tigeot 			connector_set[i] = connector;
29095718399fSFrançois Tigeot 		}
29105718399fSFrançois Tigeot 	}
29115718399fSFrançois Tigeot 
29125718399fSFrançois Tigeot 	set.crtc = crtc;
29135718399fSFrançois Tigeot 	set.x = crtc_req->x;
29145718399fSFrançois Tigeot 	set.y = crtc_req->y;
29155718399fSFrançois Tigeot 	set.mode = mode;
29165718399fSFrançois Tigeot 	set.connectors = connector_set;
29175718399fSFrançois Tigeot 	set.num_connectors = crtc_req->count_connectors;
29185718399fSFrançois Tigeot 	set.fb = fb;
29194dbb207bSFrançois Tigeot 	ret = drm_mode_set_config_internal(&set);
29205718399fSFrançois Tigeot 
29215718399fSFrançois Tigeot out:
29224dbb207bSFrançois Tigeot 	if (fb)
29234dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
29244dbb207bSFrançois Tigeot 
29258621f407SFrançois Tigeot 	if (connector_set) {
29268621f407SFrançois Tigeot 		for (i = 0; i < crtc_req->count_connectors; i++) {
29278621f407SFrançois Tigeot 			if (connector_set[i])
29288621f407SFrançois Tigeot 				drm_connector_unreference(connector_set[i]);
29298621f407SFrançois Tigeot 		}
29308621f407SFrançois Tigeot 	}
29314dbb207bSFrançois Tigeot 	kfree(connector_set);
29325718399fSFrançois Tigeot 	drm_mode_destroy(dev, mode);
29334dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
29345718399fSFrançois Tigeot 	return ret;
29355718399fSFrançois Tigeot }
29365718399fSFrançois Tigeot 
293724edb884SFrançois Tigeot /**
293824edb884SFrançois Tigeot  * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
293924edb884SFrançois Tigeot  *     universal plane handler call
294024edb884SFrançois Tigeot  * @crtc: crtc to update cursor for
294124edb884SFrançois Tigeot  * @req: data pointer for the ioctl
294224edb884SFrançois Tigeot  * @file_priv: drm file for the ioctl call
294324edb884SFrançois Tigeot  *
294424edb884SFrançois Tigeot  * Legacy cursor ioctl's work directly with driver buffer handles.  To
294524edb884SFrançois Tigeot  * translate legacy ioctl calls into universal plane handler calls, we need to
294624edb884SFrançois Tigeot  * wrap the native buffer handle in a drm_framebuffer.
294724edb884SFrançois Tigeot  *
294824edb884SFrançois Tigeot  * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
294924edb884SFrançois Tigeot  * buffer with a pitch of 4*width; the universal plane interface should be used
295024edb884SFrançois Tigeot  * directly in cases where the hardware can support other buffer settings and
295124edb884SFrançois Tigeot  * userspace wants to make use of these capabilities.
295224edb884SFrançois Tigeot  *
295324edb884SFrançois Tigeot  * Returns:
29542c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
295524edb884SFrançois Tigeot  */
295624edb884SFrançois Tigeot static int drm_mode_cursor_universal(struct drm_crtc *crtc,
295724edb884SFrançois Tigeot 				     struct drm_mode_cursor2 *req,
295824edb884SFrançois Tigeot 				     struct drm_file *file_priv)
295924edb884SFrançois Tigeot {
296024edb884SFrançois Tigeot 	struct drm_device *dev = crtc->dev;
296124edb884SFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
296224edb884SFrançois Tigeot 	struct drm_mode_fb_cmd2 fbreq = {
296324edb884SFrançois Tigeot 		.width = req->width,
296424edb884SFrançois Tigeot 		.height = req->height,
296524edb884SFrançois Tigeot 		.pixel_format = DRM_FORMAT_ARGB8888,
296624edb884SFrançois Tigeot 		.pitches = { req->width * 4 },
296724edb884SFrançois Tigeot 		.handles = { req->handle },
296824edb884SFrançois Tigeot 	};
296924edb884SFrançois Tigeot 	int32_t crtc_x, crtc_y;
297024edb884SFrançois Tigeot 	uint32_t crtc_w = 0, crtc_h = 0;
297124edb884SFrançois Tigeot 	uint32_t src_w = 0, src_h = 0;
297224edb884SFrançois Tigeot 	int ret = 0;
297324edb884SFrançois Tigeot 
297424edb884SFrançois Tigeot 	BUG_ON(!crtc->cursor);
29751b13d190SFrançois Tigeot 	WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL);
297624edb884SFrançois Tigeot 
297724edb884SFrançois Tigeot 	/*
297824edb884SFrançois Tigeot 	 * Obtain fb we'll be using (either new or existing) and take an extra
297924edb884SFrançois Tigeot 	 * reference to it if fb != null.  setplane will take care of dropping
298024edb884SFrançois Tigeot 	 * the reference if the plane update fails.
298124edb884SFrançois Tigeot 	 */
298224edb884SFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_BO) {
298324edb884SFrançois Tigeot 		if (req->handle) {
29842c9916cdSFrançois Tigeot 			fb = internal_framebuffer_create(dev, &fbreq, file_priv);
298524edb884SFrançois Tigeot 			if (IS_ERR(fb)) {
298624edb884SFrançois Tigeot 				DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
298724edb884SFrançois Tigeot 				return PTR_ERR(fb);
298824edb884SFrançois Tigeot 			}
298924edb884SFrançois Tigeot 		} else {
299024edb884SFrançois Tigeot 			fb = NULL;
299124edb884SFrançois Tigeot 		}
299224edb884SFrançois Tigeot 	} else {
299324edb884SFrançois Tigeot 		fb = crtc->cursor->fb;
299424edb884SFrançois Tigeot 		if (fb)
299524edb884SFrançois Tigeot 			drm_framebuffer_reference(fb);
299624edb884SFrançois Tigeot 	}
299724edb884SFrançois Tigeot 
299824edb884SFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_MOVE) {
299924edb884SFrançois Tigeot 		crtc_x = req->x;
300024edb884SFrançois Tigeot 		crtc_y = req->y;
300124edb884SFrançois Tigeot 	} else {
300224edb884SFrançois Tigeot 		crtc_x = crtc->cursor_x;
300324edb884SFrançois Tigeot 		crtc_y = crtc->cursor_y;
300424edb884SFrançois Tigeot 	}
300524edb884SFrançois Tigeot 
300624edb884SFrançois Tigeot 	if (fb) {
300724edb884SFrançois Tigeot 		crtc_w = fb->width;
300824edb884SFrançois Tigeot 		crtc_h = fb->height;
300924edb884SFrançois Tigeot 		src_w = fb->width << 16;
301024edb884SFrançois Tigeot 		src_h = fb->height << 16;
301124edb884SFrançois Tigeot 	}
301224edb884SFrançois Tigeot 
301324edb884SFrançois Tigeot 	/*
301424edb884SFrançois Tigeot 	 * setplane_internal will take care of deref'ing either the old or new
301524edb884SFrançois Tigeot 	 * framebuffer depending on success.
301624edb884SFrançois Tigeot 	 */
30171b13d190SFrançois Tigeot 	ret = __setplane_internal(crtc->cursor, crtc, fb,
301824edb884SFrançois Tigeot 				crtc_x, crtc_y, crtc_w, crtc_h,
301924edb884SFrançois Tigeot 				0, 0, src_w, src_h);
302024edb884SFrançois Tigeot 
302124edb884SFrançois Tigeot 	/* Update successful; save new cursor position, if necessary */
302224edb884SFrançois Tigeot 	if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
302324edb884SFrançois Tigeot 		crtc->cursor_x = req->x;
302424edb884SFrançois Tigeot 		crtc->cursor_y = req->y;
302524edb884SFrançois Tigeot 	}
302624edb884SFrançois Tigeot 
302724edb884SFrançois Tigeot 	return ret;
302824edb884SFrançois Tigeot }
302924edb884SFrançois Tigeot 
303006fede5aSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev,
303106fede5aSFrançois Tigeot 				  struct drm_mode_cursor2 *req,
303206fede5aSFrançois Tigeot 				  struct drm_file *file_priv)
30335718399fSFrançois Tigeot {
30345718399fSFrançois Tigeot 	struct drm_crtc *crtc;
30355718399fSFrançois Tigeot 	int ret = 0;
30365718399fSFrançois Tigeot 
30375718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3038b5162e19SFrançois Tigeot 		return -EINVAL;
30395718399fSFrançois Tigeot 
3040b5162e19SFrançois Tigeot 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
3041b5162e19SFrançois Tigeot 		return -EINVAL;
30425718399fSFrançois Tigeot 
3043ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, req->crtc_id);
3044ba55f2f5SFrançois Tigeot 	if (!crtc) {
30455718399fSFrançois Tigeot 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
30469edbd4a0SFrançois Tigeot 		return -ENOENT;
30475718399fSFrançois Tigeot 	}
30485718399fSFrançois Tigeot 
304924edb884SFrançois Tigeot 	/*
305024edb884SFrançois Tigeot 	 * If this crtc has a universal cursor plane, call that plane's update
305124edb884SFrançois Tigeot 	 * handler rather than using legacy cursor handlers.
305224edb884SFrançois Tigeot 	 */
30532c9916cdSFrançois Tigeot 	drm_modeset_lock_crtc(crtc, crtc->cursor);
30541b13d190SFrançois Tigeot 	if (crtc->cursor) {
30551b13d190SFrançois Tigeot 		ret = drm_mode_cursor_universal(crtc, req, file_priv);
30561b13d190SFrançois Tigeot 		goto out;
30571b13d190SFrançois Tigeot 	}
305824edb884SFrançois Tigeot 
30595718399fSFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_BO) {
306006fede5aSFrançois Tigeot 		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
3061b5162e19SFrançois Tigeot 			ret = -ENXIO;
30625718399fSFrançois Tigeot 			goto out;
30635718399fSFrançois Tigeot 		}
30645718399fSFrançois Tigeot 		/* Turns off the cursor if handle is 0 */
306506fede5aSFrançois Tigeot 		if (crtc->funcs->cursor_set2)
306606fede5aSFrançois Tigeot 			ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
306706fede5aSFrançois Tigeot 						      req->width, req->height, req->hot_x, req->hot_y);
306806fede5aSFrançois Tigeot 		else
3069b5162e19SFrançois Tigeot 			ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
30705718399fSFrançois Tigeot 						      req->width, req->height);
30715718399fSFrançois Tigeot 	}
30725718399fSFrançois Tigeot 
30735718399fSFrançois Tigeot 	if (req->flags & DRM_MODE_CURSOR_MOVE) {
30745718399fSFrançois Tigeot 		if (crtc->funcs->cursor_move) {
30755718399fSFrançois Tigeot 			ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
30765718399fSFrançois Tigeot 		} else {
3077b5162e19SFrançois Tigeot 			ret = -EFAULT;
30785718399fSFrançois Tigeot 			goto out;
30795718399fSFrançois Tigeot 		}
30805718399fSFrançois Tigeot 	}
30815718399fSFrançois Tigeot out:
30821b13d190SFrançois Tigeot 	drm_modeset_unlock_crtc(crtc);
30834dbb207bSFrançois Tigeot 
30845718399fSFrançois Tigeot 	return ret;
308506fede5aSFrançois Tigeot 
308606fede5aSFrançois Tigeot }
3087ba55f2f5SFrançois Tigeot 
3088ba55f2f5SFrançois Tigeot 
3089ba55f2f5SFrançois Tigeot /**
3090ba55f2f5SFrançois Tigeot  * drm_mode_cursor_ioctl - set CRTC's cursor configuration
3091ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
3092ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
3093ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
3094ba55f2f5SFrançois Tigeot  *
3095ba55f2f5SFrançois Tigeot  * Set the cursor configuration based on user request.
3096ba55f2f5SFrançois Tigeot  *
3097ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
3098ba55f2f5SFrançois Tigeot  *
3099ba55f2f5SFrançois Tigeot  * Returns:
31002c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
3101ba55f2f5SFrançois Tigeot  */
310206fede5aSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev,
310306fede5aSFrançois Tigeot 			  void *data, struct drm_file *file_priv)
310406fede5aSFrançois Tigeot {
310506fede5aSFrançois Tigeot 	struct drm_mode_cursor *req = data;
310606fede5aSFrançois Tigeot 	struct drm_mode_cursor2 new_req;
310706fede5aSFrançois Tigeot 
310806fede5aSFrançois Tigeot 	memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
310906fede5aSFrançois Tigeot 	new_req.hot_x = new_req.hot_y = 0;
311006fede5aSFrançois Tigeot 
311106fede5aSFrançois Tigeot 	return drm_mode_cursor_common(dev, &new_req, file_priv);
311206fede5aSFrançois Tigeot }
311306fede5aSFrançois Tigeot 
3114ba55f2f5SFrançois Tigeot /**
3115ba55f2f5SFrançois Tigeot  * drm_mode_cursor2_ioctl - set CRTC's cursor configuration
3116ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
3117ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
3118ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
3119ba55f2f5SFrançois Tigeot  *
3120ba55f2f5SFrançois Tigeot  * Set the cursor configuration based on user request. This implements the 2nd
3121ba55f2f5SFrançois Tigeot  * version of the cursor ioctl, which allows userspace to additionally specify
3122ba55f2f5SFrançois Tigeot  * the hotspot of the pointer.
3123ba55f2f5SFrançois Tigeot  *
3124ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
3125ba55f2f5SFrançois Tigeot  *
3126ba55f2f5SFrançois Tigeot  * Returns:
31272c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
3128ba55f2f5SFrançois Tigeot  */
312906fede5aSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev,
313006fede5aSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
313106fede5aSFrançois Tigeot {
313206fede5aSFrançois Tigeot 	struct drm_mode_cursor2 *req = data;
31332c9916cdSFrançois Tigeot 
313406fede5aSFrançois Tigeot 	return drm_mode_cursor_common(dev, req, file_priv);
31355718399fSFrançois Tigeot }
31365718399fSFrançois Tigeot 
3137ba55f2f5SFrançois Tigeot /**
3138ba55f2f5SFrançois Tigeot  * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
3139ba55f2f5SFrançois Tigeot  * @bpp: bits per pixels
3140ba55f2f5SFrançois Tigeot  * @depth: bit depth per pixel
3141ba55f2f5SFrançois Tigeot  *
3142ba55f2f5SFrançois Tigeot  * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
3143ba55f2f5SFrançois Tigeot  * Useful in fbdev emulation code, since that deals in those values.
3144ba55f2f5SFrançois Tigeot  */
31455718399fSFrançois Tigeot uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
31465718399fSFrançois Tigeot {
31475718399fSFrançois Tigeot 	uint32_t fmt;
31485718399fSFrançois Tigeot 
31495718399fSFrançois Tigeot 	switch (bpp) {
31505718399fSFrançois Tigeot 	case 8:
31514dbb207bSFrançois Tigeot 		fmt = DRM_FORMAT_C8;
31525718399fSFrançois Tigeot 		break;
31535718399fSFrançois Tigeot 	case 16:
31545718399fSFrançois Tigeot 		if (depth == 15)
31555718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB1555;
31565718399fSFrançois Tigeot 		else
31575718399fSFrançois Tigeot 			fmt = DRM_FORMAT_RGB565;
31585718399fSFrançois Tigeot 		break;
31595718399fSFrançois Tigeot 	case 24:
31605718399fSFrançois Tigeot 		fmt = DRM_FORMAT_RGB888;
31615718399fSFrançois Tigeot 		break;
31625718399fSFrançois Tigeot 	case 32:
31635718399fSFrançois Tigeot 		if (depth == 24)
31645718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB8888;
31655718399fSFrançois Tigeot 		else if (depth == 30)
31665718399fSFrançois Tigeot 			fmt = DRM_FORMAT_XRGB2101010;
31675718399fSFrançois Tigeot 		else
31685718399fSFrançois Tigeot 			fmt = DRM_FORMAT_ARGB8888;
31695718399fSFrançois Tigeot 		break;
31705718399fSFrançois Tigeot 	default:
3171b5162e19SFrançois Tigeot 		DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
31725718399fSFrançois Tigeot 		fmt = DRM_FORMAT_XRGB8888;
31735718399fSFrançois Tigeot 		break;
31745718399fSFrançois Tigeot 	}
31755718399fSFrançois Tigeot 
31765718399fSFrançois Tigeot 	return fmt;
31775718399fSFrançois Tigeot }
3178b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_legacy_fb_format);
31795718399fSFrançois Tigeot 
31805718399fSFrançois Tigeot /**
31815718399fSFrançois Tigeot  * drm_mode_addfb - add an FB to the graphics configuration
31824dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
31834dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
31844dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
31855718399fSFrançois Tigeot  *
3186ba55f2f5SFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request. This is the
31872c9916cdSFrançois Tigeot  * original addfb ioctl which only supported RGB formats.
31885718399fSFrançois Tigeot  *
31895718399fSFrançois Tigeot  * Called by the user via ioctl.
31905718399fSFrançois Tigeot  *
3191ba55f2f5SFrançois Tigeot  * Returns:
31922c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
31935718399fSFrançois Tigeot  */
31945718399fSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev,
31955718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
31965718399fSFrançois Tigeot {
31975718399fSFrançois Tigeot 	struct drm_mode_fb_cmd *or = data;
31985718399fSFrançois Tigeot 	struct drm_mode_fb_cmd2 r = {};
31992c9916cdSFrançois Tigeot 	int ret;
32005718399fSFrançois Tigeot 
32012c9916cdSFrançois Tigeot 	/* convert to new format and call new ioctl */
32025718399fSFrançois Tigeot 	r.fb_id = or->fb_id;
32035718399fSFrançois Tigeot 	r.width = or->width;
32045718399fSFrançois Tigeot 	r.height = or->height;
32055718399fSFrançois Tigeot 	r.pitches[0] = or->pitch;
32065718399fSFrançois Tigeot 	r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
32075718399fSFrançois Tigeot 	r.handles[0] = or->handle;
32085718399fSFrançois Tigeot 
32092c9916cdSFrançois Tigeot 	ret = drm_mode_addfb2(dev, &r, file_priv);
32102c9916cdSFrançois Tigeot 	if (ret)
32112c9916cdSFrançois Tigeot 		return ret;
32125718399fSFrançois Tigeot 
32132c9916cdSFrançois Tigeot 	or->fb_id = r.fb_id;
32145718399fSFrançois Tigeot 
3215917ec290SFrançois Tigeot 	return 0;
32165718399fSFrançois Tigeot }
32175718399fSFrançois Tigeot 
3218b5162e19SFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r)
32195718399fSFrançois Tigeot {
32205718399fSFrançois Tigeot 	uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
32215718399fSFrançois Tigeot 
32225718399fSFrançois Tigeot 	switch (format) {
32235718399fSFrançois Tigeot 	case DRM_FORMAT_C8:
32245718399fSFrançois Tigeot 	case DRM_FORMAT_RGB332:
32255718399fSFrançois Tigeot 	case DRM_FORMAT_BGR233:
32265718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB4444:
32275718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR4444:
32285718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX4444:
32295718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX4444:
32305718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB4444:
32315718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR4444:
32325718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA4444:
32335718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA4444:
32345718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB1555:
32355718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR1555:
32365718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX5551:
32375718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX5551:
32385718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB1555:
32395718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR1555:
32405718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA5551:
32415718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA5551:
32425718399fSFrançois Tigeot 	case DRM_FORMAT_RGB565:
32435718399fSFrançois Tigeot 	case DRM_FORMAT_BGR565:
32445718399fSFrançois Tigeot 	case DRM_FORMAT_RGB888:
32455718399fSFrançois Tigeot 	case DRM_FORMAT_BGR888:
32465718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB8888:
32475718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR8888:
32485718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX8888:
32495718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX8888:
32505718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB8888:
32515718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR8888:
32525718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA8888:
32535718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA8888:
32545718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB2101010:
32555718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR2101010:
32565718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX1010102:
32575718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX1010102:
32585718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB2101010:
32595718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR2101010:
32605718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA1010102:
32615718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA1010102:
32625718399fSFrançois Tigeot 	case DRM_FORMAT_YUYV:
32635718399fSFrançois Tigeot 	case DRM_FORMAT_YVYU:
32645718399fSFrançois Tigeot 	case DRM_FORMAT_UYVY:
32655718399fSFrançois Tigeot 	case DRM_FORMAT_VYUY:
32665718399fSFrançois Tigeot 	case DRM_FORMAT_AYUV:
32675718399fSFrançois Tigeot 	case DRM_FORMAT_NV12:
32685718399fSFrançois Tigeot 	case DRM_FORMAT_NV21:
32695718399fSFrançois Tigeot 	case DRM_FORMAT_NV16:
32705718399fSFrançois Tigeot 	case DRM_FORMAT_NV61:
3271b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
3272b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
32735718399fSFrançois Tigeot 	case DRM_FORMAT_YUV410:
32745718399fSFrançois Tigeot 	case DRM_FORMAT_YVU410:
32755718399fSFrançois Tigeot 	case DRM_FORMAT_YUV411:
32765718399fSFrançois Tigeot 	case DRM_FORMAT_YVU411:
32775718399fSFrançois Tigeot 	case DRM_FORMAT_YUV420:
32785718399fSFrançois Tigeot 	case DRM_FORMAT_YVU420:
32795718399fSFrançois Tigeot 	case DRM_FORMAT_YUV422:
32805718399fSFrançois Tigeot 	case DRM_FORMAT_YVU422:
32815718399fSFrançois Tigeot 	case DRM_FORMAT_YUV444:
32825718399fSFrançois Tigeot 	case DRM_FORMAT_YVU444:
32835718399fSFrançois Tigeot 		return 0;
32845718399fSFrançois Tigeot 	default:
32859edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("invalid pixel format %s\n",
32869edbd4a0SFrançois Tigeot 			      drm_get_format_name(r->pixel_format));
3287b5162e19SFrançois Tigeot 		return -EINVAL;
32885718399fSFrançois Tigeot 	}
32895718399fSFrançois Tigeot }
32905718399fSFrançois Tigeot 
3291b5162e19SFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
3292b5162e19SFrançois Tigeot {
3293b5162e19SFrançois Tigeot 	int ret, hsub, vsub, num_planes, i;
3294b5162e19SFrançois Tigeot 
3295b5162e19SFrançois Tigeot 	ret = format_check(r);
3296b5162e19SFrançois Tigeot 	if (ret) {
329706fede5aSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer format %s\n",
329806fede5aSFrançois Tigeot 			      drm_get_format_name(r->pixel_format));
3299b5162e19SFrançois Tigeot 		return ret;
3300b5162e19SFrançois Tigeot 	}
3301b5162e19SFrançois Tigeot 
3302b5162e19SFrançois Tigeot 	hsub = drm_format_horz_chroma_subsampling(r->pixel_format);
3303b5162e19SFrançois Tigeot 	vsub = drm_format_vert_chroma_subsampling(r->pixel_format);
3304b5162e19SFrançois Tigeot 	num_planes = drm_format_num_planes(r->pixel_format);
3305b5162e19SFrançois Tigeot 
3306b5162e19SFrançois Tigeot 	if (r->width == 0 || r->width % hsub) {
33072c9916cdSFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
3308b5162e19SFrançois Tigeot 		return -EINVAL;
3309b5162e19SFrançois Tigeot 	}
3310b5162e19SFrançois Tigeot 
3311b5162e19SFrançois Tigeot 	if (r->height == 0 || r->height % vsub) {
3312b5162e19SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
3313b5162e19SFrançois Tigeot 		return -EINVAL;
3314b5162e19SFrançois Tigeot 	}
3315b5162e19SFrançois Tigeot 
3316b5162e19SFrançois Tigeot 	for (i = 0; i < num_planes; i++) {
3317b5162e19SFrançois Tigeot 		unsigned int width = r->width / (i != 0 ? hsub : 1);
3318b5162e19SFrançois Tigeot 		unsigned int height = r->height / (i != 0 ? vsub : 1);
3319b5162e19SFrançois Tigeot 		unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
3320b5162e19SFrançois Tigeot 
3321b5162e19SFrançois Tigeot 		if (!r->handles[i]) {
3322b5162e19SFrançois Tigeot 			DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
3323b5162e19SFrançois Tigeot 			return -EINVAL;
3324b5162e19SFrançois Tigeot 		}
3325b5162e19SFrançois Tigeot 
3326b5162e19SFrançois Tigeot 		if ((uint64_t) width * cpp > UINT_MAX)
3327b5162e19SFrançois Tigeot 			return -ERANGE;
3328b5162e19SFrançois Tigeot 
3329b5162e19SFrançois Tigeot 		if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
3330b5162e19SFrançois Tigeot 			return -ERANGE;
3331b5162e19SFrançois Tigeot 
3332b5162e19SFrançois Tigeot 		if (r->pitches[i] < width * cpp) {
3333b5162e19SFrançois Tigeot 			DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
3334b5162e19SFrançois Tigeot 			return -EINVAL;
3335b5162e19SFrançois Tigeot 		}
3336477eb7f9SFrançois Tigeot 
3337477eb7f9SFrançois Tigeot 		if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
3338f77dbd6cSFrançois Tigeot 			DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
3339477eb7f9SFrançois Tigeot 				      r->modifier[i], i);
3340477eb7f9SFrançois Tigeot 			return -EINVAL;
3341477eb7f9SFrançois Tigeot 		}
334219c468b4SFrançois Tigeot 
334319c468b4SFrançois Tigeot 		/* modifier specific checks: */
334419c468b4SFrançois Tigeot 		switch (r->modifier[i]) {
334519c468b4SFrançois Tigeot 		case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
334619c468b4SFrançois Tigeot 			/* NOTE: the pitch restriction may be lifted later if it turns
334719c468b4SFrançois Tigeot 			 * out that no hw has this restriction:
334819c468b4SFrançois Tigeot 			 */
334919c468b4SFrançois Tigeot 			if (r->pixel_format != DRM_FORMAT_NV12 ||
335019c468b4SFrançois Tigeot 					width % 128 || height % 32 ||
335119c468b4SFrançois Tigeot 					r->pitches[i] % 128) {
335219c468b4SFrançois Tigeot 				DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
335319c468b4SFrançois Tigeot 				return -EINVAL;
335419c468b4SFrançois Tigeot 			}
335519c468b4SFrançois Tigeot 			break;
335619c468b4SFrançois Tigeot 
335719c468b4SFrançois Tigeot 		default:
335819c468b4SFrançois Tigeot 			break;
335919c468b4SFrançois Tigeot 		}
336019c468b4SFrançois Tigeot 	}
336119c468b4SFrançois Tigeot 
336219c468b4SFrançois Tigeot 	for (i = num_planes; i < 4; i++) {
336319c468b4SFrançois Tigeot 		if (r->modifier[i]) {
336419c468b4SFrançois Tigeot 			DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
336519c468b4SFrançois Tigeot 			return -EINVAL;
336619c468b4SFrançois Tigeot 		}
336719c468b4SFrançois Tigeot 
336819c468b4SFrançois Tigeot 		/* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
336919c468b4SFrançois Tigeot 		if (!(r->flags & DRM_MODE_FB_MODIFIERS))
337019c468b4SFrançois Tigeot 			continue;
337119c468b4SFrançois Tigeot 
337219c468b4SFrançois Tigeot 		if (r->handles[i]) {
337319c468b4SFrançois Tigeot 			DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
337419c468b4SFrançois Tigeot 			return -EINVAL;
337519c468b4SFrançois Tigeot 		}
337619c468b4SFrançois Tigeot 
337719c468b4SFrançois Tigeot 		if (r->pitches[i]) {
337819c468b4SFrançois Tigeot 			DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
337919c468b4SFrançois Tigeot 			return -EINVAL;
338019c468b4SFrançois Tigeot 		}
338119c468b4SFrançois Tigeot 
338219c468b4SFrançois Tigeot 		if (r->offsets[i]) {
338319c468b4SFrançois Tigeot 			DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
338419c468b4SFrançois Tigeot 			return -EINVAL;
338519c468b4SFrançois Tigeot 		}
3386b5162e19SFrançois Tigeot 	}
3387b5162e19SFrançois Tigeot 
3388b5162e19SFrançois Tigeot 	return 0;
3389b5162e19SFrançois Tigeot }
3390b5162e19SFrançois Tigeot 
33912c9916cdSFrançois Tigeot static struct drm_framebuffer *
33922c9916cdSFrançois Tigeot internal_framebuffer_create(struct drm_device *dev,
3393aee94f86SFrançois Tigeot 			    const struct drm_mode_fb_cmd2 *r,
339424edb884SFrançois Tigeot 			    struct drm_file *file_priv)
339524edb884SFrançois Tigeot {
339624edb884SFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
339724edb884SFrançois Tigeot 	struct drm_framebuffer *fb;
339824edb884SFrançois Tigeot 	int ret;
339924edb884SFrançois Tigeot 
3400477eb7f9SFrançois Tigeot 	if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
340124edb884SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
340224edb884SFrançois Tigeot 		return ERR_PTR(-EINVAL);
340324edb884SFrançois Tigeot 	}
340424edb884SFrançois Tigeot 
340524edb884SFrançois Tigeot 	if ((config->min_width > r->width) || (r->width > config->max_width)) {
340624edb884SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
340724edb884SFrançois Tigeot 			  r->width, config->min_width, config->max_width);
340824edb884SFrançois Tigeot 		return ERR_PTR(-EINVAL);
340924edb884SFrançois Tigeot 	}
341024edb884SFrançois Tigeot 	if ((config->min_height > r->height) || (r->height > config->max_height)) {
341124edb884SFrançois Tigeot 		DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
341224edb884SFrançois Tigeot 			  r->height, config->min_height, config->max_height);
341324edb884SFrançois Tigeot 		return ERR_PTR(-EINVAL);
341424edb884SFrançois Tigeot 	}
341524edb884SFrançois Tigeot 
3416477eb7f9SFrançois Tigeot 	if (r->flags & DRM_MODE_FB_MODIFIERS &&
3417477eb7f9SFrançois Tigeot 	    !dev->mode_config.allow_fb_modifiers) {
3418477eb7f9SFrançois Tigeot 		DRM_DEBUG_KMS("driver does not support fb modifiers\n");
3419477eb7f9SFrançois Tigeot 		return ERR_PTR(-EINVAL);
3420477eb7f9SFrançois Tigeot 	}
3421477eb7f9SFrançois Tigeot 
342224edb884SFrançois Tigeot 	ret = framebuffer_check(r);
342324edb884SFrançois Tigeot 	if (ret)
342424edb884SFrançois Tigeot 		return ERR_PTR(ret);
342524edb884SFrançois Tigeot 
342624edb884SFrançois Tigeot 	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
342724edb884SFrançois Tigeot 	if (IS_ERR(fb)) {
342824edb884SFrançois Tigeot 		DRM_DEBUG_KMS("could not create framebuffer\n");
342924edb884SFrançois Tigeot 		return fb;
343024edb884SFrançois Tigeot 	}
343124edb884SFrançois Tigeot 
343224edb884SFrançois Tigeot 	return fb;
343324edb884SFrançois Tigeot }
343424edb884SFrançois Tigeot 
34355718399fSFrançois Tigeot /**
34365718399fSFrançois Tigeot  * drm_mode_addfb2 - add an FB to the graphics configuration
34374dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
34384dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
34394dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
34405718399fSFrançois Tigeot  *
3441ba55f2f5SFrançois Tigeot  * Add a new FB to the specified CRTC, given a user request with format. This is
3442ba55f2f5SFrançois Tigeot  * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
3443ba55f2f5SFrançois Tigeot  * and uses fourcc codes as pixel format specifiers.
34445718399fSFrançois Tigeot  *
34455718399fSFrançois Tigeot  * Called by the user via ioctl.
34465718399fSFrançois Tigeot  *
3447ba55f2f5SFrançois Tigeot  * Returns:
34482c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
34495718399fSFrançois Tigeot  */
34505718399fSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev,
34515718399fSFrançois Tigeot 		    void *data, struct drm_file *file_priv)
34525718399fSFrançois Tigeot {
34532c9916cdSFrançois Tigeot 	struct drm_mode_fb_cmd2 *r = data;
34545718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
34555718399fSFrançois Tigeot 
34565718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3457b5162e19SFrançois Tigeot 		return -EINVAL;
3458b5162e19SFrançois Tigeot 
34592c9916cdSFrançois Tigeot 	fb = internal_framebuffer_create(dev, r, file_priv);
346024edb884SFrançois Tigeot 	if (IS_ERR(fb))
34614dbb207bSFrançois Tigeot 		return PTR_ERR(fb);
34625718399fSFrançois Tigeot 
34632c9916cdSFrançois Tigeot 	DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
34642c9916cdSFrançois Tigeot 	r->fb_id = fb->base.id;
34658621f407SFrançois Tigeot 
34668621f407SFrançois Tigeot 	/* Transfer ownership to the filp for reaping on close */
34678621f407SFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
34682c9916cdSFrançois Tigeot 	list_add(&fb->filp_head, &file_priv->fbs);
34692c9916cdSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
34702c9916cdSFrançois Tigeot 
347124edb884SFrançois Tigeot 	return 0;
34725718399fSFrançois Tigeot }
34735718399fSFrançois Tigeot 
34748621f407SFrançois Tigeot struct drm_mode_rmfb_work {
34758621f407SFrançois Tigeot 	struct work_struct work;
34768621f407SFrançois Tigeot 	struct list_head fbs;
34778621f407SFrançois Tigeot };
34788621f407SFrançois Tigeot 
34798621f407SFrançois Tigeot static void drm_mode_rmfb_work_fn(struct work_struct *w)
34808621f407SFrançois Tigeot {
34818621f407SFrançois Tigeot 	struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
34828621f407SFrançois Tigeot 
34838621f407SFrançois Tigeot 	while (!list_empty(&arg->fbs)) {
34848621f407SFrançois Tigeot 		struct drm_framebuffer *fb =
34858621f407SFrançois Tigeot 			list_first_entry(&arg->fbs, typeof(*fb), filp_head);
34868621f407SFrançois Tigeot 
34878621f407SFrançois Tigeot 		list_del_init(&fb->filp_head);
34888621f407SFrançois Tigeot 		drm_framebuffer_remove(fb);
34898621f407SFrançois Tigeot 	}
34908621f407SFrançois Tigeot }
34918621f407SFrançois Tigeot 
34925718399fSFrançois Tigeot /**
34935718399fSFrançois Tigeot  * drm_mode_rmfb - remove an FB from the configuration
34944dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
34954dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
34964dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
34975718399fSFrançois Tigeot  *
34985718399fSFrançois Tigeot  * Remove the FB specified by the user.
34995718399fSFrançois Tigeot  *
35005718399fSFrançois Tigeot  * Called by the user via ioctl.
35015718399fSFrançois Tigeot  *
3502ba55f2f5SFrançois Tigeot  * Returns:
35032c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
35045718399fSFrançois Tigeot  */
35055718399fSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev,
35065718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
35075718399fSFrançois Tigeot {
35085718399fSFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
35095718399fSFrançois Tigeot 	struct drm_framebuffer *fbl = NULL;
35105718399fSFrançois Tigeot 	uint32_t *id = data;
35115718399fSFrançois Tigeot 	int found = 0;
35125718399fSFrançois Tigeot 
35135718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3514b5162e19SFrançois Tigeot 		return -EINVAL;
35155718399fSFrançois Tigeot 
35168621f407SFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, *id);
35174dbb207bSFrançois Tigeot 	if (!fb)
35188621f407SFrançois Tigeot 		return -ENOENT;
35195718399fSFrançois Tigeot 
35208621f407SFrançois Tigeot 	mutex_lock(&file_priv->fbs_lock);
35215718399fSFrançois Tigeot 	list_for_each_entry(fbl, &file_priv->fbs, filp_head)
35225718399fSFrançois Tigeot 		if (fb == fbl)
35235718399fSFrançois Tigeot 			found = 1;
35248621f407SFrançois Tigeot 	if (!found) {
35258621f407SFrançois Tigeot 		mutex_unlock(&file_priv->fbs_lock);
35268621f407SFrançois Tigeot 		goto fail_unref;
35278621f407SFrançois Tigeot 	}
35285718399fSFrançois Tigeot 
35294dbb207bSFrançois Tigeot 	list_del_init(&fb->filp_head);
35304dbb207bSFrançois Tigeot 	mutex_unlock(&file_priv->fbs_lock);
35315718399fSFrançois Tigeot 
35328621f407SFrançois Tigeot 	/* drop the reference we picked up in framebuffer lookup */
35338621f407SFrançois Tigeot 	drm_framebuffer_unreference(fb);
35348621f407SFrançois Tigeot 
35358621f407SFrançois Tigeot 	/*
35368621f407SFrançois Tigeot 	 * we now own the reference that was stored in the fbs list
35378621f407SFrançois Tigeot 	 *
35388621f407SFrançois Tigeot 	 * drm_framebuffer_remove may fail with -EINTR on pending signals,
35398621f407SFrançois Tigeot 	 * so run this in a separate stack as there's no way to correctly
35408621f407SFrançois Tigeot 	 * handle this after the fb is already removed from the lookup table.
35418621f407SFrançois Tigeot 	 */
35428621f407SFrançois Tigeot 	if (drm_framebuffer_read_refcount(fb) > 1) {
35438621f407SFrançois Tigeot 		struct drm_mode_rmfb_work arg;
35448621f407SFrançois Tigeot 
35458621f407SFrançois Tigeot 		INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
35468621f407SFrançois Tigeot 		INIT_LIST_HEAD(&arg.fbs);
35478621f407SFrançois Tigeot 		list_add_tail(&fb->filp_head, &arg.fbs);
35488621f407SFrançois Tigeot 
35498621f407SFrançois Tigeot 		schedule_work(&arg.work);
35508621f407SFrançois Tigeot 		flush_work(&arg.work);
35518621f407SFrançois Tigeot 		destroy_work_on_stack(&arg.work);
35528621f407SFrançois Tigeot 	} else
3553352ff8bdSFrançois Tigeot 		drm_framebuffer_unreference(fb);
35545718399fSFrançois Tigeot 
35554dbb207bSFrançois Tigeot 	return 0;
35564dbb207bSFrançois Tigeot 
35578621f407SFrançois Tigeot fail_unref:
35588621f407SFrançois Tigeot 	drm_framebuffer_unreference(fb);
35599edbd4a0SFrançois Tigeot 	return -ENOENT;
35605718399fSFrançois Tigeot }
35615718399fSFrançois Tigeot 
35625718399fSFrançois Tigeot /**
35635718399fSFrançois Tigeot  * drm_mode_getfb - get FB info
35644dbb207bSFrançois Tigeot  * @dev: drm device for the ioctl
35654dbb207bSFrançois Tigeot  * @data: data pointer for the ioctl
35664dbb207bSFrançois Tigeot  * @file_priv: drm file for the ioctl call
35675718399fSFrançois Tigeot  *
35685718399fSFrançois Tigeot  * Lookup the FB given its ID and return info about it.
35695718399fSFrançois Tigeot  *
35705718399fSFrançois Tigeot  * Called by the user via ioctl.
35715718399fSFrançois Tigeot  *
3572ba55f2f5SFrançois Tigeot  * Returns:
35732c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
35745718399fSFrançois Tigeot  */
35755718399fSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev,
35765718399fSFrançois Tigeot 		   void *data, struct drm_file *file_priv)
35775718399fSFrançois Tigeot {
35785718399fSFrançois Tigeot 	struct drm_mode_fb_cmd *r = data;
35795718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
35804dbb207bSFrançois Tigeot 	int ret;
35815718399fSFrançois Tigeot 
35825718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3583b5162e19SFrançois Tigeot 		return -EINVAL;
35845718399fSFrançois Tigeot 
35854dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
35864dbb207bSFrançois Tigeot 	if (!fb)
35879edbd4a0SFrançois Tigeot 		return -ENOENT;
35885718399fSFrançois Tigeot 
35895718399fSFrançois Tigeot 	r->height = fb->height;
35905718399fSFrançois Tigeot 	r->width = fb->width;
35915718399fSFrançois Tigeot 	r->depth = fb->depth;
35925718399fSFrançois Tigeot 	r->bpp = fb->bits_per_pixel;
35935718399fSFrançois Tigeot 	r->pitch = fb->pitches[0];
3594ba55f2f5SFrançois Tigeot 	if (fb->funcs->create_handle) {
35954dbb207bSFrançois Tigeot 		ret = fb->funcs->create_handle(fb, file_priv, &r->handle);
3596ba55f2f5SFrançois Tigeot 	} else {
35974dbb207bSFrançois Tigeot 		ret = -ENODEV;
3598ba55f2f5SFrançois Tigeot 	}
35995718399fSFrançois Tigeot 
36004dbb207bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
36014dbb207bSFrançois Tigeot 
36025718399fSFrançois Tigeot 	return ret;
36035718399fSFrançois Tigeot }
36045718399fSFrançois Tigeot 
3605ba55f2f5SFrançois Tigeot /**
3606ba55f2f5SFrançois Tigeot  * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
3607ba55f2f5SFrançois Tigeot  * @dev: drm device for the ioctl
3608ba55f2f5SFrançois Tigeot  * @data: data pointer for the ioctl
3609ba55f2f5SFrançois Tigeot  * @file_priv: drm file for the ioctl call
3610ba55f2f5SFrançois Tigeot  *
3611ba55f2f5SFrançois Tigeot  * Lookup the FB and flush out the damaged area supplied by userspace as a clip
3612ba55f2f5SFrançois Tigeot  * rectangle list. Generic userspace which does frontbuffer rendering must call
3613ba55f2f5SFrançois Tigeot  * this ioctl to flush out the changes on manual-update display outputs, e.g.
3614ba55f2f5SFrançois Tigeot  * usb display-link, mipi manual update panels or edp panel self refresh modes.
3615ba55f2f5SFrançois Tigeot  *
3616ba55f2f5SFrançois Tigeot  * Modesetting drivers which always update the frontbuffer do not need to
3617ba55f2f5SFrançois Tigeot  * implement the corresponding ->dirty framebuffer callback.
3618ba55f2f5SFrançois Tigeot  *
3619ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
3620ba55f2f5SFrançois Tigeot  *
3621ba55f2f5SFrançois Tigeot  * Returns:
36222c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
3623ba55f2f5SFrançois Tigeot  */
36245718399fSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
36255718399fSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
36265718399fSFrançois Tigeot {
36275718399fSFrançois Tigeot 	struct drm_clip_rect __user *clips_ptr;
36285718399fSFrançois Tigeot 	struct drm_clip_rect *clips = NULL;
36295718399fSFrançois Tigeot 	struct drm_mode_fb_dirty_cmd *r = data;
36305718399fSFrançois Tigeot 	struct drm_framebuffer *fb;
36315718399fSFrançois Tigeot 	unsigned flags;
36325718399fSFrançois Tigeot 	int num_clips;
3633b5162e19SFrançois Tigeot 	int ret;
36345718399fSFrançois Tigeot 
36355718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
3636b5162e19SFrançois Tigeot 		return -EINVAL;
36375718399fSFrançois Tigeot 
36384dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, r->fb_id);
36394dbb207bSFrançois Tigeot 	if (!fb)
36409edbd4a0SFrançois Tigeot 		return -ENOENT;
36415718399fSFrançois Tigeot 
36425718399fSFrançois Tigeot 	num_clips = r->num_clips;
3643b5162e19SFrançois Tigeot 	clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
36445718399fSFrançois Tigeot 
36455718399fSFrançois Tigeot 	if (!num_clips != !clips_ptr) {
3646b5162e19SFrançois Tigeot 		ret = -EINVAL;
36475718399fSFrançois Tigeot 		goto out_err1;
36485718399fSFrançois Tigeot 	}
36495718399fSFrançois Tigeot 
36505718399fSFrançois Tigeot 	flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
36515718399fSFrançois Tigeot 
36525718399fSFrançois Tigeot 	/* If userspace annotates copy, clips must come in pairs */
36535718399fSFrançois Tigeot 	if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
3654b5162e19SFrançois Tigeot 		ret = -EINVAL;
36555718399fSFrançois Tigeot 		goto out_err1;
36565718399fSFrançois Tigeot 	}
36575718399fSFrançois Tigeot 
36585718399fSFrançois Tigeot 	if (num_clips && clips_ptr) {
36595718399fSFrançois Tigeot 		if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
3660b5162e19SFrançois Tigeot 			ret = -EINVAL;
36615718399fSFrançois Tigeot 			goto out_err1;
36625718399fSFrançois Tigeot 		}
36632c9916cdSFrançois Tigeot 		clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
3664b5162e19SFrançois Tigeot 		if (!clips) {
3665b5162e19SFrançois Tigeot 			ret = -ENOMEM;
3666b5162e19SFrançois Tigeot 			goto out_err1;
3667b5162e19SFrançois Tigeot 		}
36685718399fSFrançois Tigeot 
3669b5162e19SFrançois Tigeot 		ret = copy_from_user(clips, clips_ptr,
3670b5162e19SFrançois Tigeot 				     num_clips * sizeof(*clips));
3671b5162e19SFrançois Tigeot 		if (ret) {
3672b5162e19SFrançois Tigeot 			ret = -EFAULT;
36735718399fSFrançois Tigeot 			goto out_err2;
36745718399fSFrançois Tigeot 		}
3675b5162e19SFrançois Tigeot 	}
36765718399fSFrançois Tigeot 
36775718399fSFrançois Tigeot 	if (fb->funcs->dirty) {
3678b5162e19SFrançois Tigeot 		ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
36795718399fSFrançois Tigeot 				       clips, num_clips);
36805718399fSFrançois Tigeot 	} else {
3681b5162e19SFrançois Tigeot 		ret = -ENOSYS;
36825718399fSFrançois Tigeot 	}
36835718399fSFrançois Tigeot 
36845718399fSFrançois Tigeot out_err2:
36854dbb207bSFrançois Tigeot 	kfree(clips);
36865718399fSFrançois Tigeot out_err1:
36874dbb207bSFrançois Tigeot 	drm_framebuffer_unreference(fb);
36884dbb207bSFrançois Tigeot 
36895718399fSFrançois Tigeot 	return ret;
36905718399fSFrançois Tigeot }
36915718399fSFrançois Tigeot 
36925718399fSFrançois Tigeot /**
36935718399fSFrançois Tigeot  * drm_fb_release - remove and free the FBs on this file
36944dbb207bSFrançois Tigeot  * @priv: drm file for the ioctl
36955718399fSFrançois Tigeot  *
36965718399fSFrançois Tigeot  * Destroy all the FBs associated with @filp.
36975718399fSFrançois Tigeot  *
36985718399fSFrançois Tigeot  * Called by the user via ioctl.
36995718399fSFrançois Tigeot  *
3700ba55f2f5SFrançois Tigeot  * Returns:
37012c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
37025718399fSFrançois Tigeot  */
37035718399fSFrançois Tigeot void drm_fb_release(struct drm_file *priv)
37045718399fSFrançois Tigeot {
37055718399fSFrançois Tigeot 	struct drm_framebuffer *fb, *tfb;
37068621f407SFrançois Tigeot 	struct drm_mode_rmfb_work arg;
37078621f407SFrançois Tigeot 
37088621f407SFrançois Tigeot 	INIT_LIST_HEAD(&arg.fbs);
37095718399fSFrançois Tigeot 
37101b13d190SFrançois Tigeot 	/*
37111b13d190SFrançois Tigeot 	 * When the file gets released that means no one else can access the fb
37122c9916cdSFrançois Tigeot 	 * list any more, so no need to grab fpriv->fbs_lock. And we need to
37131b13d190SFrançois Tigeot 	 * avoid upsetting lockdep since the universal cursor code adds a
37141b13d190SFrançois Tigeot 	 * framebuffer while holding mutex locks.
37151b13d190SFrançois Tigeot 	 *
37161b13d190SFrançois Tigeot 	 * Note that a real deadlock between fpriv->fbs_lock and the modeset
37171b13d190SFrançois Tigeot 	 * locks is impossible here since no one else but this function can get
37181b13d190SFrançois Tigeot 	 * at it any more.
37191b13d190SFrançois Tigeot 	 */
37205718399fSFrançois Tigeot 	list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
37218621f407SFrançois Tigeot 		if (drm_framebuffer_read_refcount(fb) > 1) {
37228621f407SFrançois Tigeot 			list_move_tail(&fb->filp_head, &arg.fbs);
37238621f407SFrançois Tigeot 		} else {
37244dbb207bSFrançois Tigeot 			list_del_init(&fb->filp_head);
37254dbb207bSFrançois Tigeot 
3726352ff8bdSFrançois Tigeot 			/* This drops the fpriv->fbs reference. */
3727352ff8bdSFrançois Tigeot 			drm_framebuffer_unreference(fb);
37285718399fSFrançois Tigeot 		}
37295718399fSFrançois Tigeot 	}
37305718399fSFrançois Tigeot 
37318621f407SFrançois Tigeot 	if (!list_empty(&arg.fbs)) {
37328621f407SFrançois Tigeot 		INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
37338621f407SFrançois Tigeot 
37348621f407SFrançois Tigeot 		schedule_work(&arg.work);
37358621f407SFrançois Tigeot 		flush_work(&arg.work);
37368621f407SFrançois Tigeot 		destroy_work_on_stack(&arg.work);
37378621f407SFrançois Tigeot 	}
37388621f407SFrançois Tigeot }
37398621f407SFrançois Tigeot 
3740ba55f2f5SFrançois Tigeot /**
3741ba55f2f5SFrançois Tigeot  * drm_property_create - create a new property type
3742ba55f2f5SFrançois Tigeot  * @dev: drm device
3743ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3744ba55f2f5SFrançois Tigeot  * @name: name of the property
3745ba55f2f5SFrançois Tigeot  * @num_values: number of pre-defined values
3746ba55f2f5SFrançois Tigeot  *
3747ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3748ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3749ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3750ba55f2f5SFrançois Tigeot  *
37512c9916cdSFrançois Tigeot  * Note that the DRM core keeps a per-device list of properties and that, if
37522c9916cdSFrançois Tigeot  * drm_mode_config_cleanup() is called, it will destroy all properties created
37532c9916cdSFrançois Tigeot  * by the driver.
37542c9916cdSFrançois Tigeot  *
3755ba55f2f5SFrançois Tigeot  * Returns:
3756ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3757ba55f2f5SFrançois Tigeot  */
37585718399fSFrançois Tigeot struct drm_property *drm_property_create(struct drm_device *dev, int flags,
37595718399fSFrançois Tigeot 					 const char *name, int num_values)
37605718399fSFrançois Tigeot {
37615718399fSFrançois Tigeot 	struct drm_property *property = NULL;
37625718399fSFrançois Tigeot 	int ret;
37635718399fSFrançois Tigeot 
37644dbb207bSFrançois Tigeot 	property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
3765b5162e19SFrançois Tigeot 	if (!property)
3766b5162e19SFrançois Tigeot 		return NULL;
37675718399fSFrançois Tigeot 
3768ba55f2f5SFrançois Tigeot 	property->dev = dev;
3769ba55f2f5SFrançois Tigeot 
37705718399fSFrançois Tigeot 	if (num_values) {
37712c9916cdSFrançois Tigeot 		property->values = kcalloc(num_values, sizeof(uint64_t),
37722c9916cdSFrançois Tigeot 					   GFP_KERNEL);
3773b5162e19SFrançois Tigeot 		if (!property->values)
3774b5162e19SFrançois Tigeot 			goto fail;
37755718399fSFrançois Tigeot 	}
37765718399fSFrançois Tigeot 
37775718399fSFrançois Tigeot 	ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
37785718399fSFrançois Tigeot 	if (ret)
37795718399fSFrançois Tigeot 		goto fail;
3780b5162e19SFrançois Tigeot 
37815718399fSFrançois Tigeot 	property->flags = flags;
37825718399fSFrançois Tigeot 	property->num_values = num_values;
37832c9916cdSFrançois Tigeot 	INIT_LIST_HEAD(&property->enum_list);
37845718399fSFrançois Tigeot 
37855718399fSFrançois Tigeot 	if (name) {
37865718399fSFrançois Tigeot 		strncpy(property->name, name, DRM_PROP_NAME_LEN);
37875718399fSFrançois Tigeot 		property->name[DRM_PROP_NAME_LEN-1] = '\0';
37885718399fSFrançois Tigeot 	}
37895718399fSFrançois Tigeot 
37905718399fSFrançois Tigeot 	list_add_tail(&property->head, &dev->mode_config.property_list);
3791ba55f2f5SFrançois Tigeot 
3792ba55f2f5SFrançois Tigeot 	WARN_ON(!drm_property_type_valid(property));
3793ba55f2f5SFrançois Tigeot 
37945718399fSFrançois Tigeot 	return property;
37955718399fSFrançois Tigeot fail:
37964dbb207bSFrançois Tigeot 	kfree(property->values);
37974dbb207bSFrançois Tigeot 	kfree(property);
3798b5162e19SFrançois Tigeot 	return NULL;
37995718399fSFrançois Tigeot }
3800b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create);
38015718399fSFrançois Tigeot 
3802ba55f2f5SFrançois Tigeot /**
380324edb884SFrançois Tigeot  * drm_property_create_enum - create a new enumeration property type
3804ba55f2f5SFrançois Tigeot  * @dev: drm device
3805ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3806ba55f2f5SFrançois Tigeot  * @name: name of the property
3807ba55f2f5SFrançois Tigeot  * @props: enumeration lists with property values
3808ba55f2f5SFrançois Tigeot  * @num_values: number of pre-defined values
3809ba55f2f5SFrançois Tigeot  *
3810ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3811ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3812ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3813ba55f2f5SFrançois Tigeot  *
3814ba55f2f5SFrançois Tigeot  * Userspace is only allowed to set one of the predefined values for enumeration
3815ba55f2f5SFrançois Tigeot  * properties.
3816ba55f2f5SFrançois Tigeot  *
3817ba55f2f5SFrançois Tigeot  * Returns:
3818ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3819ba55f2f5SFrançois Tigeot  */
38205718399fSFrançois Tigeot struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
38215718399fSFrançois Tigeot 					 const char *name,
38225718399fSFrançois Tigeot 					 const struct drm_prop_enum_list *props,
38235718399fSFrançois Tigeot 					 int num_values)
38245718399fSFrançois Tigeot {
38255718399fSFrançois Tigeot 	struct drm_property *property;
38265718399fSFrançois Tigeot 	int i, ret;
38275718399fSFrançois Tigeot 
38285718399fSFrançois Tigeot 	flags |= DRM_MODE_PROP_ENUM;
38295718399fSFrançois Tigeot 
38305718399fSFrançois Tigeot 	property = drm_property_create(dev, flags, name, num_values);
38315718399fSFrançois Tigeot 	if (!property)
38325718399fSFrançois Tigeot 		return NULL;
38335718399fSFrançois Tigeot 
38345718399fSFrançois Tigeot 	for (i = 0; i < num_values; i++) {
38355718399fSFrançois Tigeot 		ret = drm_property_add_enum(property, i,
38365718399fSFrançois Tigeot 				      props[i].type,
38375718399fSFrançois Tigeot 				      props[i].name);
38385718399fSFrançois Tigeot 		if (ret) {
38395718399fSFrançois Tigeot 			drm_property_destroy(dev, property);
38405718399fSFrançois Tigeot 			return NULL;
38415718399fSFrançois Tigeot 		}
38425718399fSFrançois Tigeot 	}
38435718399fSFrançois Tigeot 
38445718399fSFrançois Tigeot 	return property;
38455718399fSFrançois Tigeot }
3846b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_enum);
3847b5162e19SFrançois Tigeot 
3848ba55f2f5SFrançois Tigeot /**
384924edb884SFrançois Tigeot  * drm_property_create_bitmask - create a new bitmask property type
3850ba55f2f5SFrançois Tigeot  * @dev: drm device
3851ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3852ba55f2f5SFrançois Tigeot  * @name: name of the property
3853ba55f2f5SFrançois Tigeot  * @props: enumeration lists with property bitflags
38541b13d190SFrançois Tigeot  * @num_props: size of the @props array
38551b13d190SFrançois Tigeot  * @supported_bits: bitmask of all supported enumeration values
3856ba55f2f5SFrançois Tigeot  *
38571b13d190SFrançois Tigeot  * This creates a new bitmask drm property which can then be attached to a drm
3858ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3859ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3860ba55f2f5SFrançois Tigeot  *
3861ba55f2f5SFrançois Tigeot  * Compared to plain enumeration properties userspace is allowed to set any
3862ba55f2f5SFrançois Tigeot  * or'ed together combination of the predefined property bitflag values
3863ba55f2f5SFrançois Tigeot  *
3864ba55f2f5SFrançois Tigeot  * Returns:
3865ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3866ba55f2f5SFrançois Tigeot  */
3867b5162e19SFrançois Tigeot struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
3868b5162e19SFrançois Tigeot 					 int flags, const char *name,
3869b5162e19SFrançois Tigeot 					 const struct drm_prop_enum_list *props,
38701b13d190SFrançois Tigeot 					 int num_props,
38711b13d190SFrançois Tigeot 					 uint64_t supported_bits)
3872b5162e19SFrançois Tigeot {
3873b5162e19SFrançois Tigeot 	struct drm_property *property;
38741b13d190SFrançois Tigeot 	int i, ret, index = 0;
38751b13d190SFrançois Tigeot 	int num_values = hweight64(supported_bits);
3876b5162e19SFrançois Tigeot 
3877b5162e19SFrançois Tigeot 	flags |= DRM_MODE_PROP_BITMASK;
3878b5162e19SFrançois Tigeot 
3879b5162e19SFrançois Tigeot 	property = drm_property_create(dev, flags, name, num_values);
3880b5162e19SFrançois Tigeot 	if (!property)
3881b5162e19SFrançois Tigeot 		return NULL;
38821b13d190SFrançois Tigeot 	for (i = 0; i < num_props; i++) {
38831b13d190SFrançois Tigeot 		if (!(supported_bits & (1ULL << props[i].type)))
38841b13d190SFrançois Tigeot 			continue;
3885b5162e19SFrançois Tigeot 
38861b13d190SFrançois Tigeot 		if (WARN_ON(index >= num_values)) {
38871b13d190SFrançois Tigeot 			drm_property_destroy(dev, property);
38881b13d190SFrançois Tigeot 			return NULL;
38891b13d190SFrançois Tigeot 		}
38901b13d190SFrançois Tigeot 
38911b13d190SFrançois Tigeot 		ret = drm_property_add_enum(property, index++,
3892b5162e19SFrançois Tigeot 				      props[i].type,
3893b5162e19SFrançois Tigeot 				      props[i].name);
3894b5162e19SFrançois Tigeot 		if (ret) {
3895b5162e19SFrançois Tigeot 			drm_property_destroy(dev, property);
3896b5162e19SFrançois Tigeot 			return NULL;
3897b5162e19SFrançois Tigeot 		}
3898b5162e19SFrançois Tigeot 	}
3899b5162e19SFrançois Tigeot 
3900b5162e19SFrançois Tigeot 	return property;
3901b5162e19SFrançois Tigeot }
3902b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bitmask);
39035718399fSFrançois Tigeot 
3904ba55f2f5SFrançois Tigeot static struct drm_property *property_create_range(struct drm_device *dev,
3905ba55f2f5SFrançois Tigeot 					 int flags, const char *name,
39065718399fSFrançois Tigeot 					 uint64_t min, uint64_t max)
39075718399fSFrançois Tigeot {
39085718399fSFrançois Tigeot 	struct drm_property *property;
39095718399fSFrançois Tigeot 
39105718399fSFrançois Tigeot 	property = drm_property_create(dev, flags, name, 2);
39115718399fSFrançois Tigeot 	if (!property)
39125718399fSFrançois Tigeot 		return NULL;
39135718399fSFrançois Tigeot 
39145718399fSFrançois Tigeot 	property->values[0] = min;
39155718399fSFrançois Tigeot 	property->values[1] = max;
39165718399fSFrançois Tigeot 
39175718399fSFrançois Tigeot 	return property;
39185718399fSFrançois Tigeot }
3919ba55f2f5SFrançois Tigeot 
3920ba55f2f5SFrançois Tigeot /**
39212c9916cdSFrançois Tigeot  * drm_property_create_range - create a new unsigned ranged property type
3922ba55f2f5SFrançois Tigeot  * @dev: drm device
3923ba55f2f5SFrançois Tigeot  * @flags: flags specifying the property type
3924ba55f2f5SFrançois Tigeot  * @name: name of the property
3925ba55f2f5SFrançois Tigeot  * @min: minimum value of the property
3926ba55f2f5SFrançois Tigeot  * @max: maximum value of the property
3927ba55f2f5SFrançois Tigeot  *
3928ba55f2f5SFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
3929ba55f2f5SFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
3930ba55f2f5SFrançois Tigeot  * freed with drm_property_destroy.
3931ba55f2f5SFrançois Tigeot  *
39322c9916cdSFrançois Tigeot  * Userspace is allowed to set any unsigned integer value in the (min, max)
39332c9916cdSFrançois Tigeot  * range inclusive.
3934ba55f2f5SFrançois Tigeot  *
3935ba55f2f5SFrançois Tigeot  * Returns:
3936ba55f2f5SFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
3937ba55f2f5SFrançois Tigeot  */
3938ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
3939ba55f2f5SFrançois Tigeot 					 const char *name,
3940ba55f2f5SFrançois Tigeot 					 uint64_t min, uint64_t max)
3941ba55f2f5SFrançois Tigeot {
3942ba55f2f5SFrançois Tigeot 	return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
3943ba55f2f5SFrançois Tigeot 			name, min, max);
3944ba55f2f5SFrançois Tigeot }
3945b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_range);
39465718399fSFrançois Tigeot 
39472c9916cdSFrançois Tigeot /**
39482c9916cdSFrançois Tigeot  * drm_property_create_signed_range - create a new signed ranged property type
39492c9916cdSFrançois Tigeot  * @dev: drm device
39502c9916cdSFrançois Tigeot  * @flags: flags specifying the property type
39512c9916cdSFrançois Tigeot  * @name: name of the property
39522c9916cdSFrançois Tigeot  * @min: minimum value of the property
39532c9916cdSFrançois Tigeot  * @max: maximum value of the property
39542c9916cdSFrançois Tigeot  *
39552c9916cdSFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
39562c9916cdSFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
39572c9916cdSFrançois Tigeot  * freed with drm_property_destroy.
39582c9916cdSFrançois Tigeot  *
39592c9916cdSFrançois Tigeot  * Userspace is allowed to set any signed integer value in the (min, max)
39602c9916cdSFrançois Tigeot  * range inclusive.
39612c9916cdSFrançois Tigeot  *
39622c9916cdSFrançois Tigeot  * Returns:
39632c9916cdSFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
39642c9916cdSFrançois Tigeot  */
3965ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
3966ba55f2f5SFrançois Tigeot 					 int flags, const char *name,
3967ba55f2f5SFrançois Tigeot 					 int64_t min, int64_t max)
3968ba55f2f5SFrançois Tigeot {
3969ba55f2f5SFrançois Tigeot 	return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
3970ba55f2f5SFrançois Tigeot 			name, I642U64(min), I642U64(max));
3971ba55f2f5SFrançois Tigeot }
3972ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_signed_range);
3973ba55f2f5SFrançois Tigeot 
39742c9916cdSFrançois Tigeot /**
39752c9916cdSFrançois Tigeot  * drm_property_create_object - create a new object property type
39762c9916cdSFrançois Tigeot  * @dev: drm device
39772c9916cdSFrançois Tigeot  * @flags: flags specifying the property type
39782c9916cdSFrançois Tigeot  * @name: name of the property
39792c9916cdSFrançois Tigeot  * @type: object type from DRM_MODE_OBJECT_* defines
39802c9916cdSFrançois Tigeot  *
39812c9916cdSFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
39822c9916cdSFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
39832c9916cdSFrançois Tigeot  * freed with drm_property_destroy.
39842c9916cdSFrançois Tigeot  *
39852c9916cdSFrançois Tigeot  * Userspace is only allowed to set this to any property value of the given
39862c9916cdSFrançois Tigeot  * @type. Only useful for atomic properties, which is enforced.
39872c9916cdSFrançois Tigeot  *
39882c9916cdSFrançois Tigeot  * Returns:
39892c9916cdSFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
39902c9916cdSFrançois Tigeot  */
3991ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_object(struct drm_device *dev,
3992ba55f2f5SFrançois Tigeot 					 int flags, const char *name, uint32_t type)
3993ba55f2f5SFrançois Tigeot {
3994ba55f2f5SFrançois Tigeot 	struct drm_property *property;
3995ba55f2f5SFrançois Tigeot 
3996ba55f2f5SFrançois Tigeot 	flags |= DRM_MODE_PROP_OBJECT;
3997ba55f2f5SFrançois Tigeot 
39982c9916cdSFrançois Tigeot 	if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC)))
39992c9916cdSFrançois Tigeot 		return NULL;
40002c9916cdSFrançois Tigeot 
4001ba55f2f5SFrançois Tigeot 	property = drm_property_create(dev, flags, name, 1);
4002ba55f2f5SFrançois Tigeot 	if (!property)
4003ba55f2f5SFrançois Tigeot 		return NULL;
4004ba55f2f5SFrançois Tigeot 
4005ba55f2f5SFrançois Tigeot 	property->values[0] = type;
4006ba55f2f5SFrançois Tigeot 
4007ba55f2f5SFrançois Tigeot 	return property;
4008ba55f2f5SFrançois Tigeot }
4009ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_object);
4010ba55f2f5SFrançois Tigeot 
4011ba55f2f5SFrançois Tigeot /**
40122c9916cdSFrançois Tigeot  * drm_property_create_bool - create a new boolean property type
40132c9916cdSFrançois Tigeot  * @dev: drm device
40142c9916cdSFrançois Tigeot  * @flags: flags specifying the property type
40152c9916cdSFrançois Tigeot  * @name: name of the property
40162c9916cdSFrançois Tigeot  *
40172c9916cdSFrançois Tigeot  * This creates a new generic drm property which can then be attached to a drm
40182c9916cdSFrançois Tigeot  * object with drm_object_attach_property. The returned property object must be
40192c9916cdSFrançois Tigeot  * freed with drm_property_destroy.
40202c9916cdSFrançois Tigeot  *
40212c9916cdSFrançois Tigeot  * This is implemented as a ranged property with only {0, 1} as valid values.
40222c9916cdSFrançois Tigeot  *
40232c9916cdSFrançois Tigeot  * Returns:
40242c9916cdSFrançois Tigeot  * A pointer to the newly created property on success, NULL on failure.
40252c9916cdSFrançois Tigeot  */
40262c9916cdSFrançois Tigeot struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
40272c9916cdSFrançois Tigeot 					 const char *name)
40282c9916cdSFrançois Tigeot {
40292c9916cdSFrançois Tigeot 	return drm_property_create_range(dev, flags, name, 0, 1);
40302c9916cdSFrançois Tigeot }
40312c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bool);
40322c9916cdSFrançois Tigeot 
40332c9916cdSFrançois Tigeot /**
4034ba55f2f5SFrançois Tigeot  * drm_property_add_enum - add a possible value to an enumeration property
4035ba55f2f5SFrançois Tigeot  * @property: enumeration property to change
4036ba55f2f5SFrançois Tigeot  * @index: index of the new enumeration
4037ba55f2f5SFrançois Tigeot  * @value: value of the new enumeration
4038ba55f2f5SFrançois Tigeot  * @name: symbolic name of the new enumeration
4039ba55f2f5SFrançois Tigeot  *
4040ba55f2f5SFrançois Tigeot  * This functions adds enumerations to a property.
4041ba55f2f5SFrançois Tigeot  *
4042ba55f2f5SFrançois Tigeot  * It's use is deprecated, drivers should use one of the more specific helpers
4043ba55f2f5SFrançois Tigeot  * to directly create the property with all enumerations already attached.
4044ba55f2f5SFrançois Tigeot  *
4045ba55f2f5SFrançois Tigeot  * Returns:
4046ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
4047ba55f2f5SFrançois Tigeot  */
40485718399fSFrançois Tigeot int drm_property_add_enum(struct drm_property *property, int index,
40495718399fSFrançois Tigeot 			  uint64_t value, const char *name)
40505718399fSFrançois Tigeot {
40515718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum;
40525718399fSFrançois Tigeot 
4053ba55f2f5SFrançois Tigeot 	if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
4054ba55f2f5SFrançois Tigeot 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
4055b5162e19SFrançois Tigeot 		return -EINVAL;
4056b5162e19SFrançois Tigeot 
4057b5162e19SFrançois Tigeot 	/*
4058b5162e19SFrançois Tigeot 	 * Bitmask enum properties have the additional constraint of values
4059b5162e19SFrançois Tigeot 	 * from 0 to 63
4060b5162e19SFrançois Tigeot 	 */
4061ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
4062ba55f2f5SFrançois Tigeot 			(value > 63))
40635718399fSFrançois Tigeot 		return -EINVAL;
40645718399fSFrançois Tigeot 
40652c9916cdSFrançois Tigeot 	if (!list_empty(&property->enum_list)) {
40662c9916cdSFrançois Tigeot 		list_for_each_entry(prop_enum, &property->enum_list, head) {
40675718399fSFrançois Tigeot 			if (prop_enum->value == value) {
40685718399fSFrançois Tigeot 				strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
40695718399fSFrançois Tigeot 				prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
40705718399fSFrançois Tigeot 				return 0;
40715718399fSFrançois Tigeot 			}
40725718399fSFrançois Tigeot 		}
40735718399fSFrançois Tigeot 	}
40745718399fSFrançois Tigeot 
40754dbb207bSFrançois Tigeot 	prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
4076b5162e19SFrançois Tigeot 	if (!prop_enum)
4077b5162e19SFrançois Tigeot 		return -ENOMEM;
40785718399fSFrançois Tigeot 
40795718399fSFrançois Tigeot 	strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
40805718399fSFrançois Tigeot 	prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
40815718399fSFrançois Tigeot 	prop_enum->value = value;
40825718399fSFrançois Tigeot 
40835718399fSFrançois Tigeot 	property->values[index] = value;
40842c9916cdSFrançois Tigeot 	list_add_tail(&prop_enum->head, &property->enum_list);
40855718399fSFrançois Tigeot 	return 0;
40865718399fSFrançois Tigeot }
4087b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_add_enum);
40885718399fSFrançois Tigeot 
4089ba55f2f5SFrançois Tigeot /**
4090ba55f2f5SFrançois Tigeot  * drm_property_destroy - destroy a drm property
4091ba55f2f5SFrançois Tigeot  * @dev: drm device
4092ba55f2f5SFrançois Tigeot  * @property: property to destry
4093ba55f2f5SFrançois Tigeot  *
4094ba55f2f5SFrançois Tigeot  * This function frees a property including any attached resources like
4095ba55f2f5SFrançois Tigeot  * enumeration values.
4096ba55f2f5SFrançois Tigeot  */
40975718399fSFrançois Tigeot void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
40985718399fSFrançois Tigeot {
40995718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum, *pt;
41005718399fSFrançois Tigeot 
41012c9916cdSFrançois Tigeot 	list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) {
41025718399fSFrançois Tigeot 		list_del(&prop_enum->head);
41034dbb207bSFrançois Tigeot 		kfree(prop_enum);
41045718399fSFrançois Tigeot 	}
41055718399fSFrançois Tigeot 
41065718399fSFrançois Tigeot 	if (property->num_values)
41074dbb207bSFrançois Tigeot 		kfree(property->values);
41088621f407SFrançois Tigeot 	drm_mode_object_unregister(dev, &property->base);
41095718399fSFrançois Tigeot 	list_del(&property->head);
41104dbb207bSFrançois Tigeot 	kfree(property);
41115718399fSFrançois Tigeot }
4112b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_destroy);
41135718399fSFrançois Tigeot 
4114ba55f2f5SFrançois Tigeot /**
4115ba55f2f5SFrançois Tigeot  * drm_object_attach_property - attach a property to a modeset object
4116ba55f2f5SFrançois Tigeot  * @obj: drm modeset object
4117ba55f2f5SFrançois Tigeot  * @property: property to attach
4118ba55f2f5SFrançois Tigeot  * @init_val: initial value of the property
4119ba55f2f5SFrançois Tigeot  *
4120ba55f2f5SFrançois Tigeot  * This attaches the given property to the modeset object with the given initial
4121ba55f2f5SFrançois Tigeot  * value. Currently this function cannot fail since the properties are stored in
4122ba55f2f5SFrançois Tigeot  * a statically sized array.
4123ba55f2f5SFrançois Tigeot  */
4124b5162e19SFrançois Tigeot void drm_object_attach_property(struct drm_mode_object *obj,
4125b5162e19SFrançois Tigeot 				struct drm_property *property,
4126b5162e19SFrançois Tigeot 				uint64_t init_val)
4127b5162e19SFrançois Tigeot {
4128b5162e19SFrançois Tigeot 	int count = obj->properties->count;
4129b5162e19SFrançois Tigeot 
4130b5162e19SFrançois Tigeot 	if (count == DRM_OBJECT_MAX_PROPERTY) {
4131b5162e19SFrançois Tigeot 		WARN(1, "Failed to attach object property (type: 0x%x). Please "
4132b5162e19SFrançois Tigeot 			"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
4133b5162e19SFrançois Tigeot 			"you see this message on the same object type.\n",
4134b5162e19SFrançois Tigeot 			obj->type);
4135b5162e19SFrançois Tigeot 		return;
4136b5162e19SFrançois Tigeot 	}
4137b5162e19SFrançois Tigeot 
41382c9916cdSFrançois Tigeot 	obj->properties->properties[count] = property;
4139b5162e19SFrançois Tigeot 	obj->properties->values[count] = init_val;
4140b5162e19SFrançois Tigeot 	obj->properties->count++;
41412c9916cdSFrançois Tigeot 	if (property->flags & DRM_MODE_PROP_ATOMIC)
41422c9916cdSFrançois Tigeot 		obj->properties->atomic_count++;
4143b5162e19SFrançois Tigeot }
4144b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_attach_property);
4145b5162e19SFrançois Tigeot 
4146ba55f2f5SFrançois Tigeot /**
4147ba55f2f5SFrançois Tigeot  * drm_object_property_set_value - set the value of a property
4148ba55f2f5SFrançois Tigeot  * @obj: drm mode object to set property value for
4149ba55f2f5SFrançois Tigeot  * @property: property to set
4150ba55f2f5SFrançois Tigeot  * @val: value the property should be set to
4151ba55f2f5SFrançois Tigeot  *
4152ba55f2f5SFrançois Tigeot  * This functions sets a given property on a given object. This function only
4153ba55f2f5SFrançois Tigeot  * changes the software state of the property, it does not call into the
4154ba55f2f5SFrançois Tigeot  * driver's ->set_property callback.
4155ba55f2f5SFrançois Tigeot  *
4156ba55f2f5SFrançois Tigeot  * Returns:
4157ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
4158ba55f2f5SFrançois Tigeot  */
4159b5162e19SFrançois Tigeot int drm_object_property_set_value(struct drm_mode_object *obj,
4160b5162e19SFrançois Tigeot 				  struct drm_property *property, uint64_t val)
41615718399fSFrançois Tigeot {
41625718399fSFrançois Tigeot 	int i;
41635718399fSFrançois Tigeot 
4164b5162e19SFrançois Tigeot 	for (i = 0; i < obj->properties->count; i++) {
41652c9916cdSFrançois Tigeot 		if (obj->properties->properties[i] == property) {
4166b5162e19SFrançois Tigeot 			obj->properties->values[i] = val;
41675718399fSFrançois Tigeot 			return 0;
41685718399fSFrançois Tigeot 		}
41695718399fSFrançois Tigeot 	}
41705718399fSFrançois Tigeot 
41715718399fSFrançois Tigeot 	return -EINVAL;
41725718399fSFrançois Tigeot }
4173b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_set_value);
41745718399fSFrançois Tigeot 
4175ba55f2f5SFrançois Tigeot /**
4176ba55f2f5SFrançois Tigeot  * drm_object_property_get_value - retrieve the value of a property
4177ba55f2f5SFrançois Tigeot  * @obj: drm mode object to get property value from
4178ba55f2f5SFrançois Tigeot  * @property: property to retrieve
4179ba55f2f5SFrançois Tigeot  * @val: storage for the property value
4180ba55f2f5SFrançois Tigeot  *
4181ba55f2f5SFrançois Tigeot  * This function retrieves the softare state of the given property for the given
4182ba55f2f5SFrançois Tigeot  * property. Since there is no driver callback to retrieve the current property
4183ba55f2f5SFrançois Tigeot  * value this might be out of sync with the hardware, depending upon the driver
4184ba55f2f5SFrançois Tigeot  * and property.
4185ba55f2f5SFrançois Tigeot  *
4186ba55f2f5SFrançois Tigeot  * Returns:
4187ba55f2f5SFrançois Tigeot  * Zero on success, error code on failure.
4188ba55f2f5SFrançois Tigeot  */
4189b5162e19SFrançois Tigeot int drm_object_property_get_value(struct drm_mode_object *obj,
41905718399fSFrançois Tigeot 				  struct drm_property *property, uint64_t *val)
41915718399fSFrançois Tigeot {
41925718399fSFrançois Tigeot 	int i;
41935718399fSFrançois Tigeot 
41942c9916cdSFrançois Tigeot 	/* read-only properties bypass atomic mechanism and still store
41952c9916cdSFrançois Tigeot 	 * their value in obj->properties->values[].. mostly to avoid
41962c9916cdSFrançois Tigeot 	 * having to deal w/ EDID and similar props in atomic paths:
41972c9916cdSFrançois Tigeot 	 */
41982c9916cdSFrançois Tigeot 	if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) &&
41992c9916cdSFrançois Tigeot 			!(property->flags & DRM_MODE_PROP_IMMUTABLE))
42002c9916cdSFrançois Tigeot 		return drm_atomic_get_property(obj, property, val);
42012c9916cdSFrançois Tigeot 
4202b5162e19SFrançois Tigeot 	for (i = 0; i < obj->properties->count; i++) {
42032c9916cdSFrançois Tigeot 		if (obj->properties->properties[i] == property) {
4204b5162e19SFrançois Tigeot 			*val = obj->properties->values[i];
4205b5162e19SFrançois Tigeot 			return 0;
42065718399fSFrançois Tigeot 		}
42075718399fSFrançois Tigeot 	}
42085718399fSFrançois Tigeot 
42095718399fSFrançois Tigeot 	return -EINVAL;
42105718399fSFrançois Tigeot }
4211b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_get_value);
42125718399fSFrançois Tigeot 
4213ba55f2f5SFrançois Tigeot /**
42142c9916cdSFrançois Tigeot  * drm_mode_getproperty_ioctl - get the property metadata
4215ba55f2f5SFrançois Tigeot  * @dev: DRM device
4216ba55f2f5SFrançois Tigeot  * @data: ioctl data
4217ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4218ba55f2f5SFrançois Tigeot  *
42192c9916cdSFrançois Tigeot  * This function retrieves the metadata for a given property, like the different
42202c9916cdSFrançois Tigeot  * possible values for an enum property or the limits for a range property.
42212c9916cdSFrançois Tigeot  *
42222c9916cdSFrançois Tigeot  * Blob properties are special
4223ba55f2f5SFrançois Tigeot  *
4224ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4225ba55f2f5SFrançois Tigeot  *
4226ba55f2f5SFrançois Tigeot  * Returns:
42272c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
4228ba55f2f5SFrançois Tigeot  */
42295718399fSFrançois Tigeot int drm_mode_getproperty_ioctl(struct drm_device *dev,
42305718399fSFrançois Tigeot 			       void *data, struct drm_file *file_priv)
42315718399fSFrançois Tigeot {
42325718399fSFrançois Tigeot 	struct drm_mode_get_property *out_resp = data;
42335718399fSFrançois Tigeot 	struct drm_property *property;
42345718399fSFrançois Tigeot 	int enum_count = 0;
42355718399fSFrançois Tigeot 	int value_count = 0;
42365718399fSFrançois Tigeot 	int ret = 0, i;
42375718399fSFrançois Tigeot 	int copied;
42385718399fSFrançois Tigeot 	struct drm_property_enum *prop_enum;
42395718399fSFrançois Tigeot 	struct drm_mode_property_enum __user *enum_ptr;
4240b5162e19SFrançois Tigeot 	uint64_t __user *values_ptr;
42415718399fSFrançois Tigeot 
42425718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
42435718399fSFrançois Tigeot 		return -EINVAL;
42445718399fSFrançois Tigeot 
42454dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
4246ba55f2f5SFrançois Tigeot 	property = drm_property_find(dev, out_resp->prop_id);
4247ba55f2f5SFrançois Tigeot 	if (!property) {
42489edbd4a0SFrançois Tigeot 		ret = -ENOENT;
42495718399fSFrançois Tigeot 		goto done;
42505718399fSFrançois Tigeot 	}
42515718399fSFrançois Tigeot 
4252ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
4253ba55f2f5SFrançois Tigeot 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
42542c9916cdSFrançois Tigeot 		list_for_each_entry(prop_enum, &property->enum_list, head)
42555718399fSFrançois Tigeot 			enum_count++;
42565718399fSFrançois Tigeot 	}
42575718399fSFrançois Tigeot 
42585718399fSFrançois Tigeot 	value_count = property->num_values;
42595718399fSFrançois Tigeot 
42605718399fSFrançois Tigeot 	strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
42615718399fSFrançois Tigeot 	out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
42625718399fSFrançois Tigeot 	out_resp->flags = property->flags;
42635718399fSFrançois Tigeot 
42645718399fSFrançois Tigeot 	if ((out_resp->count_values >= value_count) && value_count) {
4265b5162e19SFrançois Tigeot 		values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
42665718399fSFrançois Tigeot 		for (i = 0; i < value_count; i++) {
4267b5162e19SFrançois Tigeot 			if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
42685718399fSFrançois Tigeot 				ret = -EFAULT;
42695718399fSFrançois Tigeot 				goto done;
42705718399fSFrançois Tigeot 			}
42715718399fSFrançois Tigeot 		}
42725718399fSFrançois Tigeot 	}
42735718399fSFrançois Tigeot 	out_resp->count_values = value_count;
42745718399fSFrançois Tigeot 
4275ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
4276ba55f2f5SFrançois Tigeot 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
42775718399fSFrançois Tigeot 		if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
42785718399fSFrançois Tigeot 			copied = 0;
4279b5162e19SFrançois Tigeot 			enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
42802c9916cdSFrançois Tigeot 			list_for_each_entry(prop_enum, &property->enum_list, head) {
42815718399fSFrançois Tigeot 
4282b5162e19SFrançois Tigeot 				if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
42835718399fSFrançois Tigeot 					ret = -EFAULT;
42845718399fSFrançois Tigeot 					goto done;
42855718399fSFrançois Tigeot 				}
42865718399fSFrançois Tigeot 
4287b5162e19SFrançois Tigeot 				if (copy_to_user(&enum_ptr[copied].name,
4288b5162e19SFrançois Tigeot 						 &prop_enum->name, DRM_PROP_NAME_LEN)) {
42895718399fSFrançois Tigeot 					ret = -EFAULT;
42905718399fSFrançois Tigeot 					goto done;
42915718399fSFrançois Tigeot 				}
42925718399fSFrançois Tigeot 				copied++;
42935718399fSFrançois Tigeot 			}
42945718399fSFrançois Tigeot 		}
42955718399fSFrançois Tigeot 		out_resp->count_enum_blobs = enum_count;
42965718399fSFrançois Tigeot 	}
42975718399fSFrançois Tigeot 
42982c9916cdSFrançois Tigeot 	/*
42992c9916cdSFrançois Tigeot 	 * NOTE: The idea seems to have been to use this to read all the blob
43002c9916cdSFrançois Tigeot 	 * property values. But nothing ever added them to the corresponding
43012c9916cdSFrançois Tigeot 	 * list, userspace always used the special-purpose get_blob ioctl to
43022c9916cdSFrançois Tigeot 	 * read the value for a blob property. It also doesn't make a lot of
43032c9916cdSFrançois Tigeot 	 * sense to return values here when everything else is just metadata for
43042c9916cdSFrançois Tigeot 	 * the property itself.
43052c9916cdSFrançois Tigeot 	 */
43062c9916cdSFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
43072c9916cdSFrançois Tigeot 		out_resp->count_enum_blobs = 0;
43085718399fSFrançois Tigeot done:
43094dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
43105718399fSFrançois Tigeot 	return ret;
43115718399fSFrançois Tigeot }
43125718399fSFrançois Tigeot 
43138621f407SFrançois Tigeot static void drm_property_free_blob(struct kref *kref)
43148621f407SFrançois Tigeot {
43158621f407SFrançois Tigeot 	struct drm_property_blob *blob =
43168621f407SFrançois Tigeot 		container_of(kref, struct drm_property_blob, base.refcount);
43178621f407SFrançois Tigeot 
43188621f407SFrançois Tigeot 	mutex_lock(&blob->dev->mode_config.blob_lock);
43198621f407SFrançois Tigeot 	list_del(&blob->head_global);
43208621f407SFrançois Tigeot 	mutex_unlock(&blob->dev->mode_config.blob_lock);
43218621f407SFrançois Tigeot 
43228621f407SFrançois Tigeot 	drm_mode_object_unregister(blob->dev, &blob->base);
43238621f407SFrançois Tigeot 
43248621f407SFrançois Tigeot 	kfree(blob);
43258621f407SFrançois Tigeot }
43268621f407SFrançois Tigeot 
432719c468b4SFrançois Tigeot /**
432819c468b4SFrançois Tigeot  * drm_property_create_blob - Create new blob property
432919c468b4SFrançois Tigeot  *
433019c468b4SFrançois Tigeot  * Creates a new blob property for a specified DRM device, optionally
433119c468b4SFrançois Tigeot  * copying data.
433219c468b4SFrançois Tigeot  *
433319c468b4SFrançois Tigeot  * @dev: DRM device to create property for
433419c468b4SFrançois Tigeot  * @length: Length to allocate for blob data
433519c468b4SFrançois Tigeot  * @data: If specified, copies data into blob
433619c468b4SFrançois Tigeot  *
433719c468b4SFrançois Tigeot  * Returns:
433819c468b4SFrançois Tigeot  * New blob property with a single reference on success, or an ERR_PTR
433919c468b4SFrançois Tigeot  * value on failure.
434019c468b4SFrançois Tigeot  */
434119c468b4SFrançois Tigeot struct drm_property_blob *
43422c9916cdSFrançois Tigeot drm_property_create_blob(struct drm_device *dev, size_t length,
43432c9916cdSFrançois Tigeot 			 const void *data)
43445718399fSFrançois Tigeot {
43455718399fSFrançois Tigeot 	struct drm_property_blob *blob;
43465718399fSFrançois Tigeot 	int ret;
43475718399fSFrançois Tigeot 
4348a05eeebfSFrançois Tigeot 	if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
434919c468b4SFrançois Tigeot 		return ERR_PTR(-EINVAL);
43505718399fSFrançois Tigeot 
43514dbb207bSFrançois Tigeot 	blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
4352b5162e19SFrançois Tigeot 	if (!blob)
435319c468b4SFrançois Tigeot 		return ERR_PTR(-ENOMEM);
435419c468b4SFrançois Tigeot 
435519c468b4SFrançois Tigeot 	/* This must be explicitly initialised, so we can safely call list_del
435619c468b4SFrançois Tigeot 	 * on it in the removal handler, even if it isn't in a file list. */
435719c468b4SFrançois Tigeot 	INIT_LIST_HEAD(&blob->head_file);
435819c468b4SFrançois Tigeot 	blob->length = length;
435919c468b4SFrançois Tigeot 	blob->dev = dev;
436019c468b4SFrançois Tigeot 
436119c468b4SFrançois Tigeot 	if (data)
436219c468b4SFrançois Tigeot 		memcpy(blob->data, data, length);
436319c468b4SFrançois Tigeot 
43648621f407SFrançois Tigeot 	ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB,
43658621f407SFrançois Tigeot 				      true, drm_property_free_blob);
43665718399fSFrançois Tigeot 	if (ret) {
43674dbb207bSFrançois Tigeot 		kfree(blob);
436819c468b4SFrançois Tigeot 		return ERR_PTR(-EINVAL);
43695718399fSFrançois Tigeot 	}
43705718399fSFrançois Tigeot 
43718621f407SFrançois Tigeot 	mutex_lock(&dev->mode_config.blob_lock);
437219c468b4SFrançois Tigeot 	list_add_tail(&blob->head_global,
437319c468b4SFrançois Tigeot 	              &dev->mode_config.property_blob_list);
437419c468b4SFrançois Tigeot 	mutex_unlock(&dev->mode_config.blob_lock);
437519c468b4SFrançois Tigeot 
437619c468b4SFrançois Tigeot 	return blob;
437719c468b4SFrançois Tigeot }
437819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_blob);
437919c468b4SFrançois Tigeot 
438019c468b4SFrançois Tigeot /**
438119c468b4SFrançois Tigeot  * drm_property_unreference_blob - Unreference a blob property
438219c468b4SFrançois Tigeot  *
438319c468b4SFrançois Tigeot  * Drop a reference on a blob property. May free the object.
438419c468b4SFrançois Tigeot  *
438519c468b4SFrançois Tigeot  * @blob: Pointer to blob property
438619c468b4SFrançois Tigeot  */
438719c468b4SFrançois Tigeot void drm_property_unreference_blob(struct drm_property_blob *blob)
438819c468b4SFrançois Tigeot {
438919c468b4SFrançois Tigeot 	if (!blob)
439019c468b4SFrançois Tigeot 		return;
439119c468b4SFrançois Tigeot 
43928621f407SFrançois Tigeot 	drm_mode_object_unreference(&blob->base);
439319c468b4SFrançois Tigeot }
439419c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_unreference_blob);
439519c468b4SFrançois Tigeot 
439619c468b4SFrançois Tigeot /**
439719c468b4SFrançois Tigeot  * drm_property_destroy_user_blobs - destroy all blobs created by this client
439819c468b4SFrançois Tigeot  * @dev:       DRM device
439919c468b4SFrançois Tigeot  * @file_priv: destroy all blobs owned by this file handle
440019c468b4SFrançois Tigeot  */
440119c468b4SFrançois Tigeot void drm_property_destroy_user_blobs(struct drm_device *dev,
440219c468b4SFrançois Tigeot 				     struct drm_file *file_priv)
440319c468b4SFrançois Tigeot {
440419c468b4SFrançois Tigeot 	struct drm_property_blob *blob, *bt;
440519c468b4SFrançois Tigeot 
44068621f407SFrançois Tigeot 	/*
44078621f407SFrançois Tigeot 	 * When the file gets released that means no one else can access the
44088621f407SFrançois Tigeot 	 * blob list any more, so no need to grab dev->blob_lock.
44098621f407SFrançois Tigeot 	 */
441019c468b4SFrançois Tigeot 	list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) {
441119c468b4SFrançois Tigeot 		list_del_init(&blob->head_file);
44128621f407SFrançois Tigeot 		drm_property_unreference_blob(blob);
441319c468b4SFrançois Tigeot 	}
441419c468b4SFrançois Tigeot }
441519c468b4SFrançois Tigeot 
441619c468b4SFrançois Tigeot /**
441719c468b4SFrançois Tigeot  * drm_property_reference_blob - Take a reference on an existing property
441819c468b4SFrançois Tigeot  *
441919c468b4SFrançois Tigeot  * Take a new reference on an existing blob property.
442019c468b4SFrançois Tigeot  *
442119c468b4SFrançois Tigeot  * @blob: Pointer to blob property
442219c468b4SFrançois Tigeot  */
442319c468b4SFrançois Tigeot struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob)
442419c468b4SFrançois Tigeot {
44258621f407SFrançois Tigeot 	drm_mode_object_reference(&blob->base);
442619c468b4SFrançois Tigeot 	return blob;
442719c468b4SFrançois Tigeot }
442819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_reference_blob);
442919c468b4SFrançois Tigeot 
443019c468b4SFrançois Tigeot /**
443119c468b4SFrançois Tigeot  * drm_property_lookup_blob - look up a blob property and take a reference
443219c468b4SFrançois Tigeot  * @dev: drm device
443319c468b4SFrançois Tigeot  * @id: id of the blob property
443419c468b4SFrançois Tigeot  *
443519c468b4SFrançois Tigeot  * If successful, this takes an additional reference to the blob property.
443619c468b4SFrançois Tigeot  * callers need to make sure to eventually unreference the returned property
443719c468b4SFrançois Tigeot  * again, using @drm_property_unreference_blob.
443819c468b4SFrançois Tigeot  */
443919c468b4SFrançois Tigeot struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
444019c468b4SFrançois Tigeot 					           uint32_t id)
44415718399fSFrançois Tigeot {
44428621f407SFrançois Tigeot 	struct drm_mode_object *obj;
44438621f407SFrançois Tigeot 	struct drm_property_blob *blob = NULL;
444419c468b4SFrançois Tigeot 
44458621f407SFrançois Tigeot 	obj = _object_find(dev, id, DRM_MODE_OBJECT_BLOB);
44468621f407SFrançois Tigeot 	if (obj)
44478621f407SFrançois Tigeot 		blob = obj_to_blob(obj);
444819c468b4SFrançois Tigeot 	return blob;
444919c468b4SFrançois Tigeot }
445019c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_lookup_blob);
445119c468b4SFrançois Tigeot 
445219c468b4SFrançois Tigeot /**
445319c468b4SFrançois Tigeot  * drm_property_replace_global_blob - atomically replace existing blob property
445419c468b4SFrançois Tigeot  * @dev: drm device
445519c468b4SFrançois Tigeot  * @replace: location of blob property pointer to be replaced
445619c468b4SFrançois Tigeot  * @length: length of data for new blob, or 0 for no data
445719c468b4SFrançois Tigeot  * @data: content for new blob, or NULL for no data
445819c468b4SFrançois Tigeot  * @obj_holds_id: optional object for property holding blob ID
445919c468b4SFrançois Tigeot  * @prop_holds_id: optional property holding blob ID
446019c468b4SFrançois Tigeot  * @return 0 on success or error on failure
446119c468b4SFrançois Tigeot  *
446219c468b4SFrançois Tigeot  * This function will atomically replace a global property in the blob list,
446319c468b4SFrançois Tigeot  * optionally updating a property which holds the ID of that property. It is
446419c468b4SFrançois Tigeot  * guaranteed to be atomic: no caller will be allowed to see intermediate
446519c468b4SFrançois Tigeot  * results, and either the entire operation will succeed and clean up the
446619c468b4SFrançois Tigeot  * previous property, or it will fail and the state will be unchanged.
446719c468b4SFrançois Tigeot  *
446819c468b4SFrançois Tigeot  * If length is 0 or data is NULL, no new blob will be created, and the holding
446919c468b4SFrançois Tigeot  * property, if specified, will be set to 0.
447019c468b4SFrançois Tigeot  *
447119c468b4SFrançois Tigeot  * Access to the replace pointer is assumed to be protected by the caller, e.g.
447219c468b4SFrançois Tigeot  * by holding the relevant modesetting object lock for its parent.
447319c468b4SFrançois Tigeot  *
447419c468b4SFrançois Tigeot  * For example, a drm_connector has a 'PATH' property, which contains the ID
447519c468b4SFrançois Tigeot  * of a blob property with the value of the MST path information. Calling this
447619c468b4SFrançois Tigeot  * function with replace pointing to the connector's path_blob_ptr, length and
447719c468b4SFrançois Tigeot  * data set for the new path information, obj_holds_id set to the connector's
447819c468b4SFrançois Tigeot  * base object, and prop_holds_id set to the path property name, will perform
447919c468b4SFrançois Tigeot  * a completely atomic update. The access to path_blob_ptr is protected by the
448019c468b4SFrançois Tigeot  * caller holding a lock on the connector.
448119c468b4SFrançois Tigeot  */
448219c468b4SFrançois Tigeot static int drm_property_replace_global_blob(struct drm_device *dev,
448319c468b4SFrançois Tigeot                                             struct drm_property_blob **replace,
448419c468b4SFrançois Tigeot                                             size_t length,
448519c468b4SFrançois Tigeot                                             const void *data,
448619c468b4SFrançois Tigeot                                             struct drm_mode_object *obj_holds_id,
448719c468b4SFrançois Tigeot                                             struct drm_property *prop_holds_id)
448819c468b4SFrançois Tigeot {
448919c468b4SFrançois Tigeot 	struct drm_property_blob *new_blob = NULL;
449019c468b4SFrançois Tigeot 	struct drm_property_blob *old_blob = NULL;
449119c468b4SFrançois Tigeot 	int ret;
449219c468b4SFrançois Tigeot 
449319c468b4SFrançois Tigeot 	WARN_ON(replace == NULL);
449419c468b4SFrançois Tigeot 
449519c468b4SFrançois Tigeot 	old_blob = *replace;
449619c468b4SFrançois Tigeot 
449719c468b4SFrançois Tigeot 	if (length && data) {
449819c468b4SFrançois Tigeot 		new_blob = drm_property_create_blob(dev, length, data);
449919c468b4SFrançois Tigeot 		if (IS_ERR(new_blob))
450019c468b4SFrançois Tigeot 			return PTR_ERR(new_blob);
450119c468b4SFrançois Tigeot 	}
450219c468b4SFrançois Tigeot 
450319c468b4SFrançois Tigeot 	/* This does not need to be synchronised with blob_lock, as the
450419c468b4SFrançois Tigeot 	 * get_properties ioctl locks all modesetting objects, and
450519c468b4SFrançois Tigeot 	 * obj_holds_id must be locked before calling here, so we cannot
450619c468b4SFrançois Tigeot 	 * have its value out of sync with the list membership modified
450719c468b4SFrançois Tigeot 	 * below under blob_lock. */
450819c468b4SFrançois Tigeot 	if (obj_holds_id) {
450919c468b4SFrançois Tigeot 		ret = drm_object_property_set_value(obj_holds_id,
451019c468b4SFrançois Tigeot 						    prop_holds_id,
451119c468b4SFrançois Tigeot 						    new_blob ?
451219c468b4SFrançois Tigeot 						        new_blob->base.id : 0);
451319c468b4SFrançois Tigeot 		if (ret != 0)
451419c468b4SFrançois Tigeot 			goto err_created;
451519c468b4SFrançois Tigeot 	}
451619c468b4SFrançois Tigeot 
451719c468b4SFrançois Tigeot 	drm_property_unreference_blob(old_blob);
451819c468b4SFrançois Tigeot 	*replace = new_blob;
451919c468b4SFrançois Tigeot 
452019c468b4SFrançois Tigeot 	return 0;
452119c468b4SFrançois Tigeot 
452219c468b4SFrançois Tigeot err_created:
452319c468b4SFrançois Tigeot 	drm_property_unreference_blob(new_blob);
452419c468b4SFrançois Tigeot 	return ret;
45255718399fSFrançois Tigeot }
45265718399fSFrançois Tigeot 
4527ba55f2f5SFrançois Tigeot /**
4528ba55f2f5SFrançois Tigeot  * drm_mode_getblob_ioctl - get the contents of a blob property value
4529ba55f2f5SFrançois Tigeot  * @dev: DRM device
4530ba55f2f5SFrançois Tigeot  * @data: ioctl data
4531ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4532ba55f2f5SFrançois Tigeot  *
4533ba55f2f5SFrançois Tigeot  * This function retrieves the contents of a blob property. The value stored in
4534ba55f2f5SFrançois Tigeot  * an object's blob property is just a normal modeset object id.
4535ba55f2f5SFrançois Tigeot  *
4536ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4537ba55f2f5SFrançois Tigeot  *
4538ba55f2f5SFrançois Tigeot  * Returns:
45392c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
4540ba55f2f5SFrançois Tigeot  */
45415718399fSFrançois Tigeot int drm_mode_getblob_ioctl(struct drm_device *dev,
45425718399fSFrançois Tigeot 			   void *data, struct drm_file *file_priv)
45435718399fSFrançois Tigeot {
45445718399fSFrançois Tigeot 	struct drm_mode_get_blob *out_resp = data;
45455718399fSFrançois Tigeot 	struct drm_property_blob *blob;
45465718399fSFrançois Tigeot 	int ret = 0;
4547b5162e19SFrançois Tigeot 	void __user *blob_ptr;
45485718399fSFrançois Tigeot 
45495718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
45505718399fSFrançois Tigeot 		return -EINVAL;
45515718399fSFrançois Tigeot 
45528621f407SFrançois Tigeot 	blob = drm_property_lookup_blob(dev, out_resp->blob_id);
45538621f407SFrançois Tigeot 	if (!blob)
45548621f407SFrançois Tigeot 		return -ENOENT;
45555718399fSFrançois Tigeot 
45565718399fSFrançois Tigeot 	if (out_resp->length == blob->length) {
4557b5162e19SFrançois Tigeot 		blob_ptr = (void __user *)(unsigned long)out_resp->data;
4558b5162e19SFrançois Tigeot 		if (copy_to_user(blob_ptr, blob->data, blob->length)) {
45595718399fSFrançois Tigeot 			ret = -EFAULT;
45608621f407SFrançois Tigeot 			goto unref;
45615718399fSFrançois Tigeot 		}
45625718399fSFrançois Tigeot 	}
45635718399fSFrançois Tigeot 	out_resp->length = blob->length;
45648621f407SFrançois Tigeot unref:
45658621f407SFrançois Tigeot 	drm_property_unreference_blob(blob);
45665718399fSFrançois Tigeot 
45675718399fSFrançois Tigeot 	return ret;
45685718399fSFrançois Tigeot }
45695718399fSFrançois Tigeot 
45702c9916cdSFrançois Tigeot /**
457119c468b4SFrançois Tigeot  * drm_mode_createblob_ioctl - create a new blob property
457219c468b4SFrançois Tigeot  * @dev: DRM device
457319c468b4SFrançois Tigeot  * @data: ioctl data
457419c468b4SFrançois Tigeot  * @file_priv: DRM file info
457519c468b4SFrançois Tigeot  *
457619c468b4SFrançois Tigeot  * This function creates a new blob property with user-defined values. In order
457719c468b4SFrançois Tigeot  * to give us sensible validation and checking when creating, rather than at
457819c468b4SFrançois Tigeot  * every potential use, we also require a type to be provided upfront.
457919c468b4SFrançois Tigeot  *
458019c468b4SFrançois Tigeot  * Called by the user via ioctl.
458119c468b4SFrançois Tigeot  *
458219c468b4SFrançois Tigeot  * Returns:
458319c468b4SFrançois Tigeot  * Zero on success, negative errno on failure.
458419c468b4SFrançois Tigeot  */
458519c468b4SFrançois Tigeot int drm_mode_createblob_ioctl(struct drm_device *dev,
458619c468b4SFrançois Tigeot 			      void *data, struct drm_file *file_priv)
458719c468b4SFrançois Tigeot {
458819c468b4SFrançois Tigeot 	struct drm_mode_create_blob *out_resp = data;
458919c468b4SFrançois Tigeot 	struct drm_property_blob *blob;
459019c468b4SFrançois Tigeot 	void __user *blob_ptr;
459119c468b4SFrançois Tigeot 	int ret = 0;
459219c468b4SFrançois Tigeot 
459319c468b4SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
459419c468b4SFrançois Tigeot 		return -EINVAL;
459519c468b4SFrançois Tigeot 
459619c468b4SFrançois Tigeot 	blob = drm_property_create_blob(dev, out_resp->length, NULL);
459719c468b4SFrançois Tigeot 	if (IS_ERR(blob))
459819c468b4SFrançois Tigeot 		return PTR_ERR(blob);
459919c468b4SFrançois Tigeot 
460019c468b4SFrançois Tigeot 	blob_ptr = (void __user *)(unsigned long)out_resp->data;
460119c468b4SFrançois Tigeot 	if (copy_from_user(blob->data, blob_ptr, out_resp->length)) {
460219c468b4SFrançois Tigeot 		ret = -EFAULT;
460319c468b4SFrançois Tigeot 		goto out_blob;
460419c468b4SFrançois Tigeot 	}
460519c468b4SFrançois Tigeot 
460619c468b4SFrançois Tigeot 	/* Dropping the lock between create_blob and our access here is safe
460719c468b4SFrançois Tigeot 	 * as only the same file_priv can remove the blob; at this point, it is
460819c468b4SFrançois Tigeot 	 * not associated with any file_priv. */
460919c468b4SFrançois Tigeot 	mutex_lock(&dev->mode_config.blob_lock);
461019c468b4SFrançois Tigeot 	out_resp->blob_id = blob->base.id;
4611a05eeebfSFrançois Tigeot 	list_add_tail(&blob->head_file, &file_priv->blobs);
461219c468b4SFrançois Tigeot 	mutex_unlock(&dev->mode_config.blob_lock);
461319c468b4SFrançois Tigeot 
461419c468b4SFrançois Tigeot 	return 0;
461519c468b4SFrançois Tigeot 
461619c468b4SFrançois Tigeot out_blob:
461719c468b4SFrançois Tigeot 	drm_property_unreference_blob(blob);
461819c468b4SFrançois Tigeot 	return ret;
461919c468b4SFrançois Tigeot }
462019c468b4SFrançois Tigeot 
462119c468b4SFrançois Tigeot /**
462219c468b4SFrançois Tigeot  * drm_mode_destroyblob_ioctl - destroy a user blob property
462319c468b4SFrançois Tigeot  * @dev: DRM device
462419c468b4SFrançois Tigeot  * @data: ioctl data
462519c468b4SFrançois Tigeot  * @file_priv: DRM file info
462619c468b4SFrançois Tigeot  *
462719c468b4SFrançois Tigeot  * Destroy an existing user-defined blob property.
462819c468b4SFrançois Tigeot  *
462919c468b4SFrançois Tigeot  * Called by the user via ioctl.
463019c468b4SFrançois Tigeot  *
463119c468b4SFrançois Tigeot  * Returns:
463219c468b4SFrançois Tigeot  * Zero on success, negative errno on failure.
463319c468b4SFrançois Tigeot  */
463419c468b4SFrançois Tigeot int drm_mode_destroyblob_ioctl(struct drm_device *dev,
463519c468b4SFrançois Tigeot 			       void *data, struct drm_file *file_priv)
463619c468b4SFrançois Tigeot {
463719c468b4SFrançois Tigeot 	struct drm_mode_destroy_blob *out_resp = data;
463819c468b4SFrançois Tigeot 	struct drm_property_blob *blob = NULL, *bt;
463919c468b4SFrançois Tigeot 	bool found = false;
464019c468b4SFrançois Tigeot 	int ret = 0;
464119c468b4SFrançois Tigeot 
464219c468b4SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
464319c468b4SFrançois Tigeot 		return -EINVAL;
464419c468b4SFrançois Tigeot 
46458621f407SFrançois Tigeot 	blob = drm_property_lookup_blob(dev, out_resp->blob_id);
46468621f407SFrançois Tigeot 	if (!blob)
46478621f407SFrançois Tigeot 		return -ENOENT;
464819c468b4SFrançois Tigeot 
46498621f407SFrançois Tigeot 	mutex_lock(&dev->mode_config.blob_lock);
465019c468b4SFrançois Tigeot 	/* Ensure the property was actually created by this user. */
465119c468b4SFrançois Tigeot 	list_for_each_entry(bt, &file_priv->blobs, head_file) {
465219c468b4SFrançois Tigeot 		if (bt == blob) {
465319c468b4SFrançois Tigeot 			found = true;
465419c468b4SFrançois Tigeot 			break;
465519c468b4SFrançois Tigeot 		}
465619c468b4SFrançois Tigeot 	}
465719c468b4SFrançois Tigeot 
465819c468b4SFrançois Tigeot 	if (!found) {
465919c468b4SFrançois Tigeot 		ret = -EPERM;
466019c468b4SFrançois Tigeot 		goto err;
466119c468b4SFrançois Tigeot 	}
466219c468b4SFrançois Tigeot 
466319c468b4SFrançois Tigeot 	/* We must drop head_file here, because we may not be the last
466419c468b4SFrançois Tigeot 	 * reference on the blob. */
466519c468b4SFrançois Tigeot 	list_del_init(&blob->head_file);
466619c468b4SFrançois Tigeot 	mutex_unlock(&dev->mode_config.blob_lock);
466719c468b4SFrançois Tigeot 
46688621f407SFrançois Tigeot 	/* One reference from lookup, and one from the filp. */
46698621f407SFrançois Tigeot 	drm_property_unreference_blob(blob);
46708621f407SFrançois Tigeot 	drm_property_unreference_blob(blob);
46718621f407SFrançois Tigeot 
467219c468b4SFrançois Tigeot 	return 0;
467319c468b4SFrançois Tigeot 
467419c468b4SFrançois Tigeot err:
467519c468b4SFrançois Tigeot 	mutex_unlock(&dev->mode_config.blob_lock);
46768621f407SFrançois Tigeot 	drm_property_unreference_blob(blob);
46778621f407SFrançois Tigeot 
467819c468b4SFrançois Tigeot 	return ret;
467919c468b4SFrançois Tigeot }
468019c468b4SFrançois Tigeot 
468119c468b4SFrançois Tigeot /**
46822c9916cdSFrançois Tigeot  * drm_mode_connector_set_path_property - set tile property on connector
46832c9916cdSFrançois Tigeot  * @connector: connector to set property on.
468419c468b4SFrançois Tigeot  * @path: path to use for property; must not be NULL.
46852c9916cdSFrançois Tigeot  *
46862c9916cdSFrançois Tigeot  * This creates a property to expose to userspace to specify a
46872c9916cdSFrançois Tigeot  * connector path. This is mainly used for DisplayPort MST where
46882c9916cdSFrançois Tigeot  * connectors have a topology and we want to allow userspace to give
46892c9916cdSFrançois Tigeot  * them more meaningful names.
46902c9916cdSFrançois Tigeot  *
46912c9916cdSFrançois Tigeot  * Returns:
46922c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
46932c9916cdSFrançois Tigeot  */
469424edb884SFrançois Tigeot int drm_mode_connector_set_path_property(struct drm_connector *connector,
46952c9916cdSFrançois Tigeot 					 const char *path)
469624edb884SFrançois Tigeot {
469724edb884SFrançois Tigeot 	struct drm_device *dev = connector->dev;
46982c9916cdSFrançois Tigeot 	int ret;
469924edb884SFrançois Tigeot 
470019c468b4SFrançois Tigeot 	ret = drm_property_replace_global_blob(dev,
470119c468b4SFrançois Tigeot 	                                       &connector->path_blob_ptr,
470219c468b4SFrançois Tigeot 	                                       strlen(path) + 1,
470319c468b4SFrançois Tigeot 	                                       path,
470419c468b4SFrançois Tigeot 	                                       &connector->base,
470519c468b4SFrançois Tigeot 	                                       dev->mode_config.path_property);
470624edb884SFrançois Tigeot 	return ret;
470724edb884SFrançois Tigeot }
470824edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_path_property);
470924edb884SFrançois Tigeot 
4710ba55f2f5SFrançois Tigeot /**
47112c9916cdSFrançois Tigeot  * drm_mode_connector_set_tile_property - set tile property on connector
47122c9916cdSFrançois Tigeot  * @connector: connector to set property on.
47132c9916cdSFrançois Tigeot  *
47142c9916cdSFrançois Tigeot  * This looks up the tile information for a connector, and creates a
47152c9916cdSFrançois Tigeot  * property for userspace to parse if it exists. The property is of
47162c9916cdSFrançois Tigeot  * the form of 8 integers using ':' as a separator.
47172c9916cdSFrançois Tigeot  *
47182c9916cdSFrançois Tigeot  * Returns:
47192c9916cdSFrançois Tigeot  * Zero on success, errno on failure.
47202c9916cdSFrançois Tigeot  */
47212c9916cdSFrançois Tigeot int drm_mode_connector_set_tile_property(struct drm_connector *connector)
47222c9916cdSFrançois Tigeot {
47232c9916cdSFrançois Tigeot 	struct drm_device *dev = connector->dev;
47242c9916cdSFrançois Tigeot 	char tile[256];
472519c468b4SFrançois Tigeot 	int ret;
47262c9916cdSFrançois Tigeot 
47272c9916cdSFrançois Tigeot 	if (!connector->has_tile) {
472819c468b4SFrançois Tigeot 		ret  = drm_property_replace_global_blob(dev,
472919c468b4SFrançois Tigeot 		                                        &connector->tile_blob_ptr,
473019c468b4SFrançois Tigeot 		                                        0,
473119c468b4SFrançois Tigeot 		                                        NULL,
473219c468b4SFrançois Tigeot 		                                        &connector->base,
473319c468b4SFrançois Tigeot 		                                        dev->mode_config.tile_property);
47342c9916cdSFrançois Tigeot 		return ret;
47352c9916cdSFrançois Tigeot 	}
47362c9916cdSFrançois Tigeot 
47372c9916cdSFrançois Tigeot 	ksnprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
47382c9916cdSFrançois Tigeot 		 connector->tile_group->id, connector->tile_is_single_monitor,
47392c9916cdSFrançois Tigeot 		 connector->num_h_tile, connector->num_v_tile,
47402c9916cdSFrançois Tigeot 		 connector->tile_h_loc, connector->tile_v_loc,
47412c9916cdSFrançois Tigeot 		 connector->tile_h_size, connector->tile_v_size);
47422c9916cdSFrançois Tigeot 
474319c468b4SFrançois Tigeot 	ret = drm_property_replace_global_blob(dev,
474419c468b4SFrançois Tigeot 	                                       &connector->tile_blob_ptr,
474519c468b4SFrançois Tigeot 	                                       strlen(tile) + 1,
474619c468b4SFrançois Tigeot 	                                       tile,
474719c468b4SFrançois Tigeot 	                                       &connector->base,
474819c468b4SFrançois Tigeot 	                                       dev->mode_config.tile_property);
47492c9916cdSFrançois Tigeot 	return ret;
47502c9916cdSFrançois Tigeot }
47512c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_tile_property);
47522c9916cdSFrançois Tigeot 
47532c9916cdSFrançois Tigeot /**
4754ba55f2f5SFrançois Tigeot  * drm_mode_connector_update_edid_property - update the edid property of a connector
4755ba55f2f5SFrançois Tigeot  * @connector: drm connector
4756ba55f2f5SFrançois Tigeot  * @edid: new value of the edid property
4757ba55f2f5SFrançois Tigeot  *
4758ba55f2f5SFrançois Tigeot  * This function creates a new blob modeset object and assigns its id to the
4759ba55f2f5SFrançois Tigeot  * connector's edid property.
4760ba55f2f5SFrançois Tigeot  *
4761ba55f2f5SFrançois Tigeot  * Returns:
47622c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
4763ba55f2f5SFrançois Tigeot  */
47645718399fSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector,
47652c9916cdSFrançois Tigeot 					    const struct edid *edid)
47665718399fSFrançois Tigeot {
47675718399fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
476819c468b4SFrançois Tigeot 	size_t size = 0;
47692c9916cdSFrançois Tigeot 	int ret;
47705718399fSFrançois Tigeot 
477124edb884SFrançois Tigeot 	/* ignore requests to set edid when overridden */
477224edb884SFrançois Tigeot 	if (connector->override_edid)
477324edb884SFrançois Tigeot 		return 0;
477424edb884SFrançois Tigeot 
477519c468b4SFrançois Tigeot 	if (edid)
47765718399fSFrançois Tigeot 		size = EDID_LENGTH * (1 + edid->extensions);
47775718399fSFrançois Tigeot 
477819c468b4SFrançois Tigeot 	ret = drm_property_replace_global_blob(dev,
477919c468b4SFrançois Tigeot 					       &connector->edid_blob_ptr,
478019c468b4SFrançois Tigeot 	                                       size,
478119c468b4SFrançois Tigeot 	                                       edid,
478219c468b4SFrançois Tigeot 	                                       &connector->base,
478319c468b4SFrançois Tigeot 	                                       dev->mode_config.edid_property);
47845718399fSFrançois Tigeot 	return ret;
47855718399fSFrançois Tigeot }
4786b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
4787b5162e19SFrançois Tigeot 
47882c9916cdSFrançois Tigeot /* Some properties could refer to dynamic refcnt'd objects, or things that
47892c9916cdSFrançois Tigeot  * need special locking to handle lifetime issues (ie. to ensure the prop
47902c9916cdSFrançois Tigeot  * value doesn't become invalid part way through the property update due to
47912c9916cdSFrançois Tigeot  * race).  The value returned by reference via 'obj' should be passed back
47922c9916cdSFrançois Tigeot  * to drm_property_change_valid_put() after the property is set (and the
47932c9916cdSFrançois Tigeot  * object to which the property is attached has a chance to take it's own
47942c9916cdSFrançois Tigeot  * reference).
47952c9916cdSFrançois Tigeot  */
47962c9916cdSFrançois Tigeot bool drm_property_change_valid_get(struct drm_property *property,
47972c9916cdSFrançois Tigeot 					 uint64_t value, struct drm_mode_object **ref)
4798b5162e19SFrançois Tigeot {
47992c9916cdSFrançois Tigeot 	int i;
48002c9916cdSFrançois Tigeot 
4801b5162e19SFrançois Tigeot 	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
4802b5162e19SFrançois Tigeot 		return false;
4803ba55f2f5SFrançois Tigeot 
48042c9916cdSFrançois Tigeot 	*ref = NULL;
48052c9916cdSFrançois Tigeot 
4806ba55f2f5SFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
4807b5162e19SFrançois Tigeot 		if (value < property->values[0] || value > property->values[1])
4808b5162e19SFrançois Tigeot 			return false;
4809b5162e19SFrançois Tigeot 		return true;
4810ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
4811ba55f2f5SFrançois Tigeot 		int64_t svalue = U642I64(value);
48122c9916cdSFrançois Tigeot 
4813ba55f2f5SFrançois Tigeot 		if (svalue < U642I64(property->values[0]) ||
4814ba55f2f5SFrançois Tigeot 				svalue > U642I64(property->values[1]))
4815ba55f2f5SFrançois Tigeot 			return false;
4816ba55f2f5SFrançois Tigeot 		return true;
4817ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
4818b5162e19SFrançois Tigeot 		uint64_t valid_mask = 0;
48192c9916cdSFrançois Tigeot 
4820b5162e19SFrançois Tigeot 		for (i = 0; i < property->num_values; i++)
4821b5162e19SFrançois Tigeot 			valid_mask |= (1ULL << property->values[i]);
4822b5162e19SFrançois Tigeot 		return !(value & ~valid_mask);
4823ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
482419c468b4SFrançois Tigeot 		struct drm_property_blob *blob;
482519c468b4SFrançois Tigeot 
482619c468b4SFrançois Tigeot 		if (value == 0)
4827b5162e19SFrançois Tigeot 			return true;
482819c468b4SFrançois Tigeot 
482919c468b4SFrançois Tigeot 		blob = drm_property_lookup_blob(property->dev, value);
483019c468b4SFrançois Tigeot 		if (blob) {
483119c468b4SFrançois Tigeot 			*ref = &blob->base;
483219c468b4SFrançois Tigeot 			return true;
483319c468b4SFrançois Tigeot 		} else {
483419c468b4SFrançois Tigeot 			return false;
483519c468b4SFrançois Tigeot 		}
4836ba55f2f5SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
4837ba55f2f5SFrançois Tigeot 		/* a zero value for an object property translates to null: */
4838ba55f2f5SFrançois Tigeot 		if (value == 0)
4839ba55f2f5SFrançois Tigeot 			return true;
48402c9916cdSFrançois Tigeot 
48418621f407SFrançois Tigeot 		*ref = _object_find(property->dev, value, property->values[0]);
48428621f407SFrançois Tigeot 		return *ref != NULL;
48432c9916cdSFrançois Tigeot 	}
48442c9916cdSFrançois Tigeot 
4845b5162e19SFrançois Tigeot 	for (i = 0; i < property->num_values; i++)
4846b5162e19SFrançois Tigeot 		if (property->values[i] == value)
4847b5162e19SFrançois Tigeot 			return true;
4848b5162e19SFrançois Tigeot 	return false;
4849b5162e19SFrançois Tigeot }
48502c9916cdSFrançois Tigeot 
48512c9916cdSFrançois Tigeot void drm_property_change_valid_put(struct drm_property *property,
48522c9916cdSFrançois Tigeot 		struct drm_mode_object *ref)
48532c9916cdSFrançois Tigeot {
48542c9916cdSFrançois Tigeot 	if (!ref)
48552c9916cdSFrançois Tigeot 		return;
48562c9916cdSFrançois Tigeot 
48572c9916cdSFrançois Tigeot 	if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
48588621f407SFrançois Tigeot 		drm_mode_object_unreference(ref);
485919c468b4SFrançois Tigeot 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
486019c468b4SFrançois Tigeot 		drm_property_unreference_blob(obj_to_blob(ref));
4861b5162e19SFrançois Tigeot }
48625718399fSFrançois Tigeot 
4863ba55f2f5SFrançois Tigeot /**
4864ba55f2f5SFrançois Tigeot  * drm_mode_connector_property_set_ioctl - set the current value of a connector property
4865ba55f2f5SFrançois Tigeot  * @dev: DRM device
4866ba55f2f5SFrançois Tigeot  * @data: ioctl data
4867ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4868ba55f2f5SFrançois Tigeot  *
4869ba55f2f5SFrançois Tigeot  * This function sets the current value for a connectors's property. It also
4870ba55f2f5SFrançois Tigeot  * calls into a driver's ->set_property callback to update the hardware state
4871ba55f2f5SFrançois Tigeot  *
4872ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4873ba55f2f5SFrançois Tigeot  *
4874ba55f2f5SFrançois Tigeot  * Returns:
48752c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
4876ba55f2f5SFrançois Tigeot  */
48775718399fSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
48785718399fSFrançois Tigeot 				       void *data, struct drm_file *file_priv)
48795718399fSFrançois Tigeot {
4880b5162e19SFrançois Tigeot 	struct drm_mode_connector_set_property *conn_set_prop = data;
4881b5162e19SFrançois Tigeot 	struct drm_mode_obj_set_property obj_set_prop = {
4882b5162e19SFrançois Tigeot 		.value = conn_set_prop->value,
4883b5162e19SFrançois Tigeot 		.prop_id = conn_set_prop->prop_id,
4884b5162e19SFrançois Tigeot 		.obj_id = conn_set_prop->connector_id,
4885b5162e19SFrançois Tigeot 		.obj_type = DRM_MODE_OBJECT_CONNECTOR
4886b5162e19SFrançois Tigeot 	};
4887b5162e19SFrançois Tigeot 
4888b5162e19SFrançois Tigeot 	/* It does all the locking and checking we need */
4889b5162e19SFrançois Tigeot 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
4890b5162e19SFrançois Tigeot }
4891b5162e19SFrançois Tigeot 
4892b5162e19SFrançois Tigeot static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
4893b5162e19SFrançois Tigeot 					   struct drm_property *property,
4894b5162e19SFrançois Tigeot 					   uint64_t value)
4895b5162e19SFrançois Tigeot {
4896b5162e19SFrançois Tigeot 	int ret = -EINVAL;
4897b5162e19SFrançois Tigeot 	struct drm_connector *connector = obj_to_connector(obj);
4898b5162e19SFrançois Tigeot 
4899b5162e19SFrançois Tigeot 	/* Do DPMS ourselves */
4900b5162e19SFrançois Tigeot 	if (property == connector->dev->mode_config.dpms_property) {
4901a05eeebfSFrançois Tigeot 		ret = (*connector->funcs->dpms)(connector, (int)value);
4902b5162e19SFrançois Tigeot 	} else if (connector->funcs->set_property)
4903b5162e19SFrançois Tigeot 		ret = connector->funcs->set_property(connector, property, value);
4904b5162e19SFrançois Tigeot 
4905b5162e19SFrançois Tigeot 	/* store the property value if successful */
4906b5162e19SFrançois Tigeot 	if (!ret)
4907b5162e19SFrançois Tigeot 		drm_object_property_set_value(&connector->base, property, value);
4908b5162e19SFrançois Tigeot 	return ret;
4909b5162e19SFrançois Tigeot }
4910b5162e19SFrançois Tigeot 
4911b5162e19SFrançois Tigeot static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
4912b5162e19SFrançois Tigeot 				      struct drm_property *property,
4913b5162e19SFrançois Tigeot 				      uint64_t value)
4914b5162e19SFrançois Tigeot {
4915b5162e19SFrançois Tigeot 	int ret = -EINVAL;
4916b5162e19SFrançois Tigeot 	struct drm_crtc *crtc = obj_to_crtc(obj);
4917b5162e19SFrançois Tigeot 
4918b5162e19SFrançois Tigeot 	if (crtc->funcs->set_property)
4919b5162e19SFrançois Tigeot 		ret = crtc->funcs->set_property(crtc, property, value);
4920b5162e19SFrançois Tigeot 	if (!ret)
4921b5162e19SFrançois Tigeot 		drm_object_property_set_value(obj, property, value);
4922b5162e19SFrançois Tigeot 
4923b5162e19SFrançois Tigeot 	return ret;
4924b5162e19SFrançois Tigeot }
4925b5162e19SFrançois Tigeot 
49261b13d190SFrançois Tigeot /**
49271b13d190SFrançois Tigeot  * drm_mode_plane_set_obj_prop - set the value of a property
49281b13d190SFrançois Tigeot  * @plane: drm plane object to set property value for
49291b13d190SFrançois Tigeot  * @property: property to set
49301b13d190SFrançois Tigeot  * @value: value the property should be set to
49311b13d190SFrançois Tigeot  *
49321b13d190SFrançois Tigeot  * This functions sets a given property on a given plane object. This function
49331b13d190SFrançois Tigeot  * calls the driver's ->set_property callback and changes the software state of
49341b13d190SFrançois Tigeot  * the property if the callback succeeds.
49351b13d190SFrançois Tigeot  *
49361b13d190SFrançois Tigeot  * Returns:
49371b13d190SFrançois Tigeot  * Zero on success, error code on failure.
49381b13d190SFrançois Tigeot  */
49391b13d190SFrançois Tigeot int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
4940b5162e19SFrançois Tigeot 				struct drm_property *property,
4941b5162e19SFrançois Tigeot 				uint64_t value)
4942b5162e19SFrançois Tigeot {
4943b5162e19SFrançois Tigeot 	int ret = -EINVAL;
49441b13d190SFrançois Tigeot 	struct drm_mode_object *obj = &plane->base;
4945b5162e19SFrançois Tigeot 
4946b5162e19SFrançois Tigeot 	if (plane->funcs->set_property)
4947b5162e19SFrançois Tigeot 		ret = plane->funcs->set_property(plane, property, value);
4948b5162e19SFrançois Tigeot 	if (!ret)
4949b5162e19SFrançois Tigeot 		drm_object_property_set_value(obj, property, value);
4950b5162e19SFrançois Tigeot 
4951b5162e19SFrançois Tigeot 	return ret;
4952b5162e19SFrançois Tigeot }
49531b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
4954b5162e19SFrançois Tigeot 
4955ba55f2f5SFrançois Tigeot /**
49562c9916cdSFrançois Tigeot  * drm_mode_obj_get_properties_ioctl - get the current value of a object's property
4957ba55f2f5SFrançois Tigeot  * @dev: DRM device
4958ba55f2f5SFrançois Tigeot  * @data: ioctl data
4959ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
4960ba55f2f5SFrançois Tigeot  *
4961ba55f2f5SFrançois Tigeot  * This function retrieves the current value for an object's property. Compared
4962ba55f2f5SFrançois Tigeot  * to the connector specific ioctl this one is extended to also work on crtc and
4963ba55f2f5SFrançois Tigeot  * plane objects.
4964ba55f2f5SFrançois Tigeot  *
4965ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
4966ba55f2f5SFrançois Tigeot  *
4967ba55f2f5SFrançois Tigeot  * Returns:
49682c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
4969ba55f2f5SFrançois Tigeot  */
4970b5162e19SFrançois Tigeot int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
4971b5162e19SFrançois Tigeot 				      struct drm_file *file_priv)
4972b5162e19SFrançois Tigeot {
4973b5162e19SFrançois Tigeot 	struct drm_mode_obj_get_properties *arg = data;
49745718399fSFrançois Tigeot 	struct drm_mode_object *obj;
4975b5162e19SFrançois Tigeot 	int ret = 0;
4976b5162e19SFrançois Tigeot 
4977b5162e19SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
4978b5162e19SFrançois Tigeot 		return -EINVAL;
4979b5162e19SFrançois Tigeot 
49804dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
4981b5162e19SFrançois Tigeot 
4982b5162e19SFrançois Tigeot 	obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
4983b5162e19SFrançois Tigeot 	if (!obj) {
49849edbd4a0SFrançois Tigeot 		ret = -ENOENT;
4985b5162e19SFrançois Tigeot 		goto out;
4986b5162e19SFrançois Tigeot 	}
4987b5162e19SFrançois Tigeot 	if (!obj->properties) {
4988b5162e19SFrançois Tigeot 		ret = -EINVAL;
49898621f407SFrançois Tigeot 		goto out_unref;
4990b5162e19SFrançois Tigeot 	}
4991b5162e19SFrançois Tigeot 
49922c9916cdSFrançois Tigeot 	ret = get_properties(obj, file_priv->atomic,
49932c9916cdSFrançois Tigeot 			(uint32_t __user *)(unsigned long)(arg->props_ptr),
49942c9916cdSFrançois Tigeot 			(uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
49952c9916cdSFrançois Tigeot 			&arg->count_props);
4996b5162e19SFrançois Tigeot 
49978621f407SFrançois Tigeot out_unref:
49988621f407SFrançois Tigeot 	drm_mode_object_unreference(obj);
4999b5162e19SFrançois Tigeot out:
50004dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
5001b5162e19SFrançois Tigeot 	return ret;
5002b5162e19SFrançois Tigeot }
5003b5162e19SFrançois Tigeot 
5004ba55f2f5SFrançois Tigeot /**
5005ba55f2f5SFrançois Tigeot  * drm_mode_obj_set_property_ioctl - set the current value of an object's property
5006ba55f2f5SFrançois Tigeot  * @dev: DRM device
5007ba55f2f5SFrançois Tigeot  * @data: ioctl data
5008ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5009ba55f2f5SFrançois Tigeot  *
5010ba55f2f5SFrançois Tigeot  * This function sets the current value for an object's property. It also calls
5011ba55f2f5SFrançois Tigeot  * into a driver's ->set_property callback to update the hardware state.
5012ba55f2f5SFrançois Tigeot  * Compared to the connector specific ioctl this one is extended to also work on
5013ba55f2f5SFrançois Tigeot  * crtc and plane objects.
5014ba55f2f5SFrançois Tigeot  *
5015ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5016ba55f2f5SFrançois Tigeot  *
5017ba55f2f5SFrançois Tigeot  * Returns:
50182c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5019ba55f2f5SFrançois Tigeot  */
5020b5162e19SFrançois Tigeot int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
5021b5162e19SFrançois Tigeot 				    struct drm_file *file_priv)
5022b5162e19SFrançois Tigeot {
5023b5162e19SFrançois Tigeot 	struct drm_mode_obj_set_property *arg = data;
5024b5162e19SFrançois Tigeot 	struct drm_mode_object *arg_obj;
5025b5162e19SFrançois Tigeot 	struct drm_mode_object *prop_obj;
50265718399fSFrançois Tigeot 	struct drm_property *property;
50272c9916cdSFrançois Tigeot 	int i, ret = -EINVAL;
50282c9916cdSFrançois Tigeot 	struct drm_mode_object *ref;
50295718399fSFrançois Tigeot 
50305718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
50315718399fSFrançois Tigeot 		return -EINVAL;
50325718399fSFrançois Tigeot 
50334dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
50345718399fSFrançois Tigeot 
5035b5162e19SFrançois Tigeot 	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
50369edbd4a0SFrançois Tigeot 	if (!arg_obj) {
50379edbd4a0SFrançois Tigeot 		ret = -ENOENT;
50385718399fSFrançois Tigeot 		goto out;
50399edbd4a0SFrançois Tigeot 	}
5040b5162e19SFrançois Tigeot 	if (!arg_obj->properties)
50418621f407SFrançois Tigeot 		goto out_unref;
50425718399fSFrançois Tigeot 
5043b5162e19SFrançois Tigeot 	for (i = 0; i < arg_obj->properties->count; i++)
50442c9916cdSFrançois Tigeot 		if (arg_obj->properties->properties[i]->base.id == arg->prop_id)
5045b5162e19SFrançois Tigeot 			break;
5046b5162e19SFrançois Tigeot 
5047b5162e19SFrançois Tigeot 	if (i == arg_obj->properties->count)
50488621f407SFrançois Tigeot 		goto out_unref;
5049b5162e19SFrançois Tigeot 
5050b5162e19SFrançois Tigeot 	prop_obj = drm_mode_object_find(dev, arg->prop_id,
5051b5162e19SFrançois Tigeot 					DRM_MODE_OBJECT_PROPERTY);
50529edbd4a0SFrançois Tigeot 	if (!prop_obj) {
50539edbd4a0SFrançois Tigeot 		ret = -ENOENT;
50548621f407SFrançois Tigeot 		goto out_unref;
50559edbd4a0SFrançois Tigeot 	}
5056b5162e19SFrançois Tigeot 	property = obj_to_property(prop_obj);
5057b5162e19SFrançois Tigeot 
50582c9916cdSFrançois Tigeot 	if (!drm_property_change_valid_get(property, arg->value, &ref))
50598621f407SFrançois Tigeot 		goto out_unref;
5060b5162e19SFrançois Tigeot 
5061b5162e19SFrançois Tigeot 	switch (arg_obj->type) {
5062b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_CONNECTOR:
5063b5162e19SFrançois Tigeot 		ret = drm_mode_connector_set_obj_prop(arg_obj, property,
5064b5162e19SFrançois Tigeot 						      arg->value);
5065b5162e19SFrançois Tigeot 		break;
5066b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_CRTC:
5067b5162e19SFrançois Tigeot 		ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
5068b5162e19SFrançois Tigeot 		break;
5069b5162e19SFrançois Tigeot 	case DRM_MODE_OBJECT_PLANE:
50701b13d190SFrançois Tigeot 		ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj),
50711b13d190SFrançois Tigeot 						  property, arg->value);
50725718399fSFrançois Tigeot 		break;
50735718399fSFrançois Tigeot 	}
50745718399fSFrançois Tigeot 
50752c9916cdSFrançois Tigeot 	drm_property_change_valid_put(property, ref);
50762c9916cdSFrançois Tigeot 
50778621f407SFrançois Tigeot out_unref:
50788621f407SFrançois Tigeot 	drm_mode_object_unreference(arg_obj);
50795718399fSFrançois Tigeot out:
50804dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
50815718399fSFrançois Tigeot 	return ret;
50825718399fSFrançois Tigeot }
50835718399fSFrançois Tigeot 
5084ba55f2f5SFrançois Tigeot /**
5085ba55f2f5SFrançois Tigeot  * drm_mode_connector_attach_encoder - attach a connector to an encoder
5086ba55f2f5SFrançois Tigeot  * @connector: connector to attach
5087ba55f2f5SFrançois Tigeot  * @encoder: encoder to attach @connector to
5088ba55f2f5SFrançois Tigeot  *
5089ba55f2f5SFrançois Tigeot  * This function links up a connector to an encoder. Note that the routing
5090ba55f2f5SFrançois Tigeot  * restrictions between encoders and crtcs are exposed to userspace through the
5091ba55f2f5SFrançois Tigeot  * possible_clones and possible_crtcs bitmasks.
5092ba55f2f5SFrançois Tigeot  *
5093ba55f2f5SFrançois Tigeot  * Returns:
50942c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5095ba55f2f5SFrançois Tigeot  */
50965718399fSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector,
50975718399fSFrançois Tigeot 				      struct drm_encoder *encoder)
50985718399fSFrançois Tigeot {
50995718399fSFrançois Tigeot 	int i;
51005718399fSFrançois Tigeot 
5101aee94f86SFrançois Tigeot 	/*
5102aee94f86SFrançois Tigeot 	 * In the past, drivers have attempted to model the static association
5103aee94f86SFrançois Tigeot 	 * of connector to encoder in simple connector/encoder devices using a
5104aee94f86SFrançois Tigeot 	 * direct assignment of connector->encoder = encoder. This connection
5105aee94f86SFrançois Tigeot 	 * is a logical one and the responsibility of the core, so drivers are
5106aee94f86SFrançois Tigeot 	 * expected not to mess with this.
5107aee94f86SFrançois Tigeot 	 *
5108aee94f86SFrançois Tigeot 	 * Note that the error return should've been enough here, but a large
5109aee94f86SFrançois Tigeot 	 * majority of drivers ignores the return value, so add in a big WARN
5110aee94f86SFrançois Tigeot 	 * to get people's attention.
5111aee94f86SFrançois Tigeot 	 */
5112aee94f86SFrançois Tigeot 	if (WARN_ON(connector->encoder))
5113aee94f86SFrançois Tigeot 		return -EINVAL;
5114aee94f86SFrançois Tigeot 
51155718399fSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
51165718399fSFrançois Tigeot 		if (connector->encoder_ids[i] == 0) {
51175718399fSFrançois Tigeot 			connector->encoder_ids[i] = encoder->base.id;
51185718399fSFrançois Tigeot 			return 0;
51195718399fSFrançois Tigeot 		}
51205718399fSFrançois Tigeot 	}
51215718399fSFrançois Tigeot 	return -ENOMEM;
51225718399fSFrançois Tigeot }
5123b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
51245718399fSFrançois Tigeot 
5125ba55f2f5SFrançois Tigeot /**
5126ba55f2f5SFrançois Tigeot  * drm_mode_crtc_set_gamma_size - set the gamma table size
5127ba55f2f5SFrançois Tigeot  * @crtc: CRTC to set the gamma table size for
5128ba55f2f5SFrançois Tigeot  * @gamma_size: size of the gamma table
5129ba55f2f5SFrançois Tigeot  *
5130ba55f2f5SFrançois Tigeot  * Drivers which support gamma tables should set this to the supported gamma
5131ba55f2f5SFrançois Tigeot  * table size when initializing the CRTC. Currently the drm core only supports a
5132ba55f2f5SFrançois Tigeot  * fixed gamma table size.
5133ba55f2f5SFrançois Tigeot  *
5134ba55f2f5SFrançois Tigeot  * Returns:
51352c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5136ba55f2f5SFrançois Tigeot  */
51375718399fSFrançois Tigeot int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
51385718399fSFrançois Tigeot 				 int gamma_size)
51395718399fSFrançois Tigeot {
51405718399fSFrançois Tigeot 	crtc->gamma_size = gamma_size;
51415718399fSFrançois Tigeot 
51422c9916cdSFrançois Tigeot 	crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
51432c9916cdSFrançois Tigeot 				    GFP_KERNEL);
5144b5162e19SFrançois Tigeot 	if (!crtc->gamma_store) {
5145b5162e19SFrançois Tigeot 		crtc->gamma_size = 0;
5146b5162e19SFrançois Tigeot 		return -ENOMEM;
5147b5162e19SFrançois Tigeot 	}
51485718399fSFrançois Tigeot 
51495718399fSFrançois Tigeot 	return 0;
51505718399fSFrançois Tigeot }
5151b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
51525718399fSFrançois Tigeot 
5153ba55f2f5SFrançois Tigeot /**
5154ba55f2f5SFrançois Tigeot  * drm_mode_gamma_set_ioctl - set the gamma table
5155ba55f2f5SFrançois Tigeot  * @dev: DRM device
5156ba55f2f5SFrançois Tigeot  * @data: ioctl data
5157ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5158ba55f2f5SFrançois Tigeot  *
5159ba55f2f5SFrançois Tigeot  * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
5160ba55f2f5SFrançois Tigeot  * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
5161ba55f2f5SFrançois Tigeot  *
5162ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5163ba55f2f5SFrançois Tigeot  *
5164ba55f2f5SFrançois Tigeot  * Returns:
51652c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5166ba55f2f5SFrançois Tigeot  */
51675718399fSFrançois Tigeot int drm_mode_gamma_set_ioctl(struct drm_device *dev,
51685718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
51695718399fSFrançois Tigeot {
51705718399fSFrançois Tigeot 	struct drm_mode_crtc_lut *crtc_lut = data;
51715718399fSFrançois Tigeot 	struct drm_crtc *crtc;
51725718399fSFrançois Tigeot 	void *r_base, *g_base, *b_base;
51735718399fSFrançois Tigeot 	int size;
51745718399fSFrançois Tigeot 	int ret = 0;
51755718399fSFrançois Tigeot 
51765718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
51775718399fSFrançois Tigeot 		return -EINVAL;
51785718399fSFrançois Tigeot 
51794dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
5180ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
5181ba55f2f5SFrançois Tigeot 	if (!crtc) {
51829edbd4a0SFrançois Tigeot 		ret = -ENOENT;
51835718399fSFrançois Tigeot 		goto out;
51845718399fSFrançois Tigeot 	}
51855718399fSFrançois Tigeot 
5186b5162e19SFrançois Tigeot 	if (crtc->funcs->gamma_set == NULL) {
5187b5162e19SFrançois Tigeot 		ret = -ENOSYS;
5188b5162e19SFrançois Tigeot 		goto out;
5189b5162e19SFrançois Tigeot 	}
5190b5162e19SFrançois Tigeot 
51915718399fSFrançois Tigeot 	/* memcpy into gamma store */
51925718399fSFrançois Tigeot 	if (crtc_lut->gamma_size != crtc->gamma_size) {
51935718399fSFrançois Tigeot 		ret = -EINVAL;
51945718399fSFrançois Tigeot 		goto out;
51955718399fSFrançois Tigeot 	}
51965718399fSFrançois Tigeot 
51975718399fSFrançois Tigeot 	size = crtc_lut->gamma_size * (sizeof(uint16_t));
51985718399fSFrançois Tigeot 	r_base = crtc->gamma_store;
5199b5162e19SFrançois Tigeot 	if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
52005718399fSFrançois Tigeot 		ret = -EFAULT;
52015718399fSFrançois Tigeot 		goto out;
52025718399fSFrançois Tigeot 	}
52035718399fSFrançois Tigeot 
52045718399fSFrançois Tigeot 	g_base = (char *)r_base + size;
5205b5162e19SFrançois Tigeot 	if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
52065718399fSFrançois Tigeot 		ret = -EFAULT;
52075718399fSFrançois Tigeot 		goto out;
52085718399fSFrançois Tigeot 	}
52095718399fSFrançois Tigeot 
521006fede5aSFrançois Tigeot 	b_base = (char *)g_base + size;
5211b5162e19SFrançois Tigeot 	if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
52125718399fSFrançois Tigeot 		ret = -EFAULT;
52135718399fSFrançois Tigeot 		goto out;
52145718399fSFrançois Tigeot 	}
52155718399fSFrançois Tigeot 
52165718399fSFrançois Tigeot 	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
52175718399fSFrançois Tigeot 
52185718399fSFrançois Tigeot out:
52194dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
52205718399fSFrançois Tigeot 	return ret;
52215718399fSFrançois Tigeot 
52225718399fSFrançois Tigeot }
52235718399fSFrançois Tigeot 
5224ba55f2f5SFrançois Tigeot /**
5225ba55f2f5SFrançois Tigeot  * drm_mode_gamma_get_ioctl - get the gamma table
5226ba55f2f5SFrançois Tigeot  * @dev: DRM device
5227ba55f2f5SFrançois Tigeot  * @data: ioctl data
5228ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5229ba55f2f5SFrançois Tigeot  *
5230ba55f2f5SFrançois Tigeot  * Copy the current gamma table into the storage provided. This also provides
5231ba55f2f5SFrançois Tigeot  * the gamma table size the driver expects, which can be used to size the
5232ba55f2f5SFrançois Tigeot  * allocated storage.
5233ba55f2f5SFrançois Tigeot  *
5234ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5235ba55f2f5SFrançois Tigeot  *
5236ba55f2f5SFrançois Tigeot  * Returns:
52372c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5238ba55f2f5SFrançois Tigeot  */
52395718399fSFrançois Tigeot int drm_mode_gamma_get_ioctl(struct drm_device *dev,
52405718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
52415718399fSFrançois Tigeot {
52425718399fSFrançois Tigeot 	struct drm_mode_crtc_lut *crtc_lut = data;
52435718399fSFrançois Tigeot 	struct drm_crtc *crtc;
52445718399fSFrançois Tigeot 	void *r_base, *g_base, *b_base;
52455718399fSFrançois Tigeot 	int size;
52465718399fSFrançois Tigeot 	int ret = 0;
52475718399fSFrançois Tigeot 
52485718399fSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
52495718399fSFrançois Tigeot 		return -EINVAL;
52505718399fSFrançois Tigeot 
52514dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
5252ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
5253ba55f2f5SFrançois Tigeot 	if (!crtc) {
52549edbd4a0SFrançois Tigeot 		ret = -ENOENT;
52555718399fSFrançois Tigeot 		goto out;
52565718399fSFrançois Tigeot 	}
52575718399fSFrançois Tigeot 
52585718399fSFrançois Tigeot 	/* memcpy into gamma store */
52595718399fSFrançois Tigeot 	if (crtc_lut->gamma_size != crtc->gamma_size) {
52605718399fSFrançois Tigeot 		ret = -EINVAL;
52615718399fSFrançois Tigeot 		goto out;
52625718399fSFrançois Tigeot 	}
52635718399fSFrançois Tigeot 
52645718399fSFrançois Tigeot 	size = crtc_lut->gamma_size * (sizeof(uint16_t));
52655718399fSFrançois Tigeot 	r_base = crtc->gamma_store;
5266b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
52675718399fSFrançois Tigeot 		ret = -EFAULT;
52685718399fSFrançois Tigeot 		goto out;
52695718399fSFrançois Tigeot 	}
52705718399fSFrançois Tigeot 
52715718399fSFrançois Tigeot 	g_base = (char *)r_base + size;
5272b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
52735718399fSFrançois Tigeot 		ret = -EFAULT;
52745718399fSFrançois Tigeot 		goto out;
52755718399fSFrançois Tigeot 	}
52765718399fSFrançois Tigeot 
52775718399fSFrançois Tigeot 	b_base = (char *)g_base + size;
5278b5162e19SFrançois Tigeot 	if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
52795718399fSFrançois Tigeot 		ret = -EFAULT;
52805718399fSFrançois Tigeot 		goto out;
52815718399fSFrançois Tigeot 	}
52825718399fSFrançois Tigeot out:
52834dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
52845718399fSFrançois Tigeot 	return ret;
52855718399fSFrançois Tigeot }
52865718399fSFrançois Tigeot 
5287ba55f2f5SFrançois Tigeot /**
5288ba55f2f5SFrançois Tigeot  * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
5289ba55f2f5SFrançois Tigeot  * @dev: DRM device
5290ba55f2f5SFrançois Tigeot  * @data: ioctl data
5291ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5292ba55f2f5SFrançois Tigeot  *
5293ba55f2f5SFrançois Tigeot  * This schedules an asynchronous update on a given CRTC, called page flip.
5294ba55f2f5SFrançois Tigeot  * Optionally a drm event is generated to signal the completion of the event.
5295ba55f2f5SFrançois Tigeot  * Generic drivers cannot assume that a pageflip with changed framebuffer
5296ba55f2f5SFrançois Tigeot  * properties (including driver specific metadata like tiling layout) will work,
5297ba55f2f5SFrançois Tigeot  * but some drivers support e.g. pixel format changes through the pageflip
5298ba55f2f5SFrançois Tigeot  * ioctl.
5299ba55f2f5SFrançois Tigeot  *
5300ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5301ba55f2f5SFrançois Tigeot  *
5302ba55f2f5SFrançois Tigeot  * Returns:
53032c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5304a5f72378SFrançois Tigeot  */
5305b5162e19SFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev,
5306b5162e19SFrançois Tigeot 			     void *data, struct drm_file *file_priv)
53075718399fSFrançois Tigeot {
53085718399fSFrançois Tigeot 	struct drm_mode_crtc_page_flip *page_flip = data;
53095718399fSFrançois Tigeot 	struct drm_crtc *crtc;
53101b13d190SFrançois Tigeot 	struct drm_framebuffer *fb = NULL;
53115718399fSFrançois Tigeot 	struct drm_pending_vblank_event *e = NULL;
5312b5162e19SFrançois Tigeot 	int ret = -EINVAL;
53135718399fSFrançois Tigeot 
53148621f407SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
53158621f407SFrançois Tigeot 		return -EINVAL;
53168621f407SFrançois Tigeot 
53175718399fSFrançois Tigeot 	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
53185718399fSFrançois Tigeot 	    page_flip->reserved != 0)
5319b5162e19SFrançois Tigeot 		return -EINVAL;
53205718399fSFrançois Tigeot 
53219edbd4a0SFrançois Tigeot 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
53229edbd4a0SFrançois Tigeot 		return -EINVAL;
53239edbd4a0SFrançois Tigeot 
5324ba55f2f5SFrançois Tigeot 	crtc = drm_crtc_find(dev, page_flip->crtc_id);
5325ba55f2f5SFrançois Tigeot 	if (!crtc)
53269edbd4a0SFrançois Tigeot 		return -ENOENT;
53275718399fSFrançois Tigeot 
53282c9916cdSFrançois Tigeot 	drm_modeset_lock_crtc(crtc, crtc->primary);
5329ba55f2f5SFrançois Tigeot 	if (crtc->primary->fb == NULL) {
53305718399fSFrançois Tigeot 		/* The framebuffer is currently unbound, presumably
53315718399fSFrançois Tigeot 		 * due to a hotplug event, that userspace has not
53325718399fSFrançois Tigeot 		 * yet discovered.
53335718399fSFrançois Tigeot 		 */
5334b5162e19SFrançois Tigeot 		ret = -EBUSY;
53355718399fSFrançois Tigeot 		goto out;
53365718399fSFrançois Tigeot 	}
53375718399fSFrançois Tigeot 
53385718399fSFrançois Tigeot 	if (crtc->funcs->page_flip == NULL)
53395718399fSFrançois Tigeot 		goto out;
53405718399fSFrançois Tigeot 
53414dbb207bSFrançois Tigeot 	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
53429edbd4a0SFrançois Tigeot 	if (!fb) {
53439edbd4a0SFrançois Tigeot 		ret = -ENOENT;
53445718399fSFrançois Tigeot 		goto out;
53455718399fSFrançois Tigeot 	}
53465718399fSFrançois Tigeot 
5347352ff8bdSFrançois Tigeot 	if (crtc->state) {
5348352ff8bdSFrançois Tigeot 		const struct drm_plane_state *state = crtc->primary->state;
5349352ff8bdSFrançois Tigeot 
5350352ff8bdSFrançois Tigeot 		ret = check_src_coords(state->src_x, state->src_y,
5351352ff8bdSFrançois Tigeot 				       state->src_w, state->src_h, fb);
5352352ff8bdSFrançois Tigeot 	} else {
53539edbd4a0SFrançois Tigeot 		ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
5354352ff8bdSFrançois Tigeot 	}
53559edbd4a0SFrançois Tigeot 	if (ret)
53569edbd4a0SFrançois Tigeot 		goto out;
53579edbd4a0SFrançois Tigeot 
5358ba55f2f5SFrançois Tigeot 	if (crtc->primary->fb->pixel_format != fb->pixel_format) {
53594dbb207bSFrançois Tigeot 		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
53604dbb207bSFrançois Tigeot 		ret = -EINVAL;
53614dbb207bSFrançois Tigeot 		goto out;
53624dbb207bSFrançois Tigeot 	}
53634dbb207bSFrançois Tigeot 
53645718399fSFrançois Tigeot 	if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
5365c0e85e96SFrançois Tigeot 		e = kzalloc(sizeof *e, GFP_KERNEL);
5366c0e85e96SFrançois Tigeot 		if (!e) {
5367b5162e19SFrançois Tigeot 			ret = -ENOMEM;
53685718399fSFrançois Tigeot 			goto out;
53695718399fSFrançois Tigeot 		}
53705718399fSFrançois Tigeot 		e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
53712c9916cdSFrançois Tigeot 		e->event.base.length = sizeof(e->event);
53725718399fSFrançois Tigeot 		e->event.user_data = page_flip->user_data;
5373c0e85e96SFrançois Tigeot 		ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);
5374c0e85e96SFrançois Tigeot 		if (ret) {
5375c0e85e96SFrançois Tigeot 			kfree(e);
5376c0e85e96SFrançois Tigeot 			goto out;
5377c0e85e96SFrançois Tigeot 		}
53785718399fSFrançois Tigeot 	}
53795718399fSFrançois Tigeot 
53801b13d190SFrançois Tigeot 	crtc->primary->old_fb = crtc->primary->fb;
53819edbd4a0SFrançois Tigeot 	ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
5382b5162e19SFrançois Tigeot 	if (ret) {
5383c0e85e96SFrançois Tigeot 		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT)
5384c0e85e96SFrançois Tigeot 			drm_event_cancel_free(dev, &e->base);
53854dbb207bSFrançois Tigeot 		/* Keep the old fb, don't unref it. */
53861b13d190SFrançois Tigeot 		crtc->primary->old_fb = NULL;
53874dbb207bSFrançois Tigeot 	} else {
5388a05eeebfSFrançois Tigeot 		crtc->primary->fb = fb;
53894dbb207bSFrançois Tigeot 		/* Unref only the old framebuffer. */
53904dbb207bSFrançois Tigeot 		fb = NULL;
53915718399fSFrançois Tigeot 	}
53925718399fSFrançois Tigeot 
53935718399fSFrançois Tigeot out:
53944dbb207bSFrançois Tigeot 	if (fb)
53954dbb207bSFrançois Tigeot 		drm_framebuffer_unreference(fb);
53961b13d190SFrançois Tigeot 	if (crtc->primary->old_fb)
53971b13d190SFrançois Tigeot 		drm_framebuffer_unreference(crtc->primary->old_fb);
53981b13d190SFrançois Tigeot 	crtc->primary->old_fb = NULL;
53991b13d190SFrançois Tigeot 	drm_modeset_unlock_crtc(crtc);
54004dbb207bSFrançois Tigeot 
5401b5162e19SFrançois Tigeot 	return ret;
54025718399fSFrançois Tigeot }
54035718399fSFrançois Tigeot 
5404ba55f2f5SFrançois Tigeot /**
5405ba55f2f5SFrançois Tigeot  * drm_mode_config_reset - call ->reset callbacks
5406ba55f2f5SFrançois Tigeot  * @dev: drm device
5407ba55f2f5SFrançois Tigeot  *
5408ba55f2f5SFrançois Tigeot  * This functions calls all the crtc's, encoder's and connector's ->reset
5409ba55f2f5SFrançois Tigeot  * callback. Drivers can use this in e.g. their driver load or resume code to
5410ba55f2f5SFrançois Tigeot  * reset hardware and software state.
5411ba55f2f5SFrançois Tigeot  */
54125718399fSFrançois Tigeot void drm_mode_config_reset(struct drm_device *dev)
54135718399fSFrançois Tigeot {
54145718399fSFrançois Tigeot 	struct drm_crtc *crtc;
54151b13d190SFrançois Tigeot 	struct drm_plane *plane;
54165718399fSFrançois Tigeot 	struct drm_encoder *encoder;
54175718399fSFrançois Tigeot 	struct drm_connector *connector;
54185718399fSFrançois Tigeot 
5419a05eeebfSFrançois Tigeot 	drm_for_each_plane(plane, dev)
54201b13d190SFrançois Tigeot 		if (plane->funcs->reset)
54211b13d190SFrançois Tigeot 			plane->funcs->reset(plane);
54221b13d190SFrançois Tigeot 
5423a05eeebfSFrançois Tigeot 	drm_for_each_crtc(crtc, dev)
54245718399fSFrançois Tigeot 		if (crtc->funcs->reset)
54255718399fSFrançois Tigeot 			crtc->funcs->reset(crtc);
54265718399fSFrançois Tigeot 
5427a05eeebfSFrançois Tigeot 	drm_for_each_encoder(encoder, dev)
54285718399fSFrançois Tigeot 		if (encoder->funcs->reset)
54295718399fSFrançois Tigeot 			encoder->funcs->reset(encoder);
54305718399fSFrançois Tigeot 
5431a05eeebfSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
5432917ec290SFrançois Tigeot 	drm_for_each_connector(connector, dev)
54335718399fSFrançois Tigeot 		if (connector->funcs->reset)
54345718399fSFrançois Tigeot 			connector->funcs->reset(connector);
5435a05eeebfSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
5436a05eeebfSFrançois Tigeot }
5437b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_reset);
54385718399fSFrançois Tigeot 
5439ba55f2f5SFrançois Tigeot /**
5440ba55f2f5SFrançois Tigeot  * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer
5441ba55f2f5SFrançois Tigeot  * @dev: DRM device
5442ba55f2f5SFrançois Tigeot  * @data: ioctl data
5443ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5444ba55f2f5SFrançois Tigeot  *
5445ba55f2f5SFrançois Tigeot  * This creates a new dumb buffer in the driver's backing storage manager (GEM,
5446ba55f2f5SFrançois Tigeot  * TTM or something else entirely) and returns the resulting buffer handle. This
5447ba55f2f5SFrançois Tigeot  * handle can then be wrapped up into a framebuffer modeset object.
5448ba55f2f5SFrançois Tigeot  *
5449ba55f2f5SFrançois Tigeot  * Note that userspace is not allowed to use such objects for render
5450ba55f2f5SFrançois Tigeot  * acceleration - drivers must create their own private ioctls for such a use
5451ba55f2f5SFrançois Tigeot  * case.
5452ba55f2f5SFrançois Tigeot  *
5453ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5454ba55f2f5SFrançois Tigeot  *
5455ba55f2f5SFrançois Tigeot  * Returns:
54562c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5457ba55f2f5SFrançois Tigeot  */
54585718399fSFrançois Tigeot int drm_mode_create_dumb_ioctl(struct drm_device *dev,
54595718399fSFrançois Tigeot 			       void *data, struct drm_file *file_priv)
54605718399fSFrançois Tigeot {
54615718399fSFrançois Tigeot 	struct drm_mode_create_dumb *args = data;
5462ba55f2f5SFrançois Tigeot 	u32 cpp, stride, size;
54635718399fSFrançois Tigeot 
54645718399fSFrançois Tigeot 	if (!dev->driver->dumb_create)
5465b5162e19SFrançois Tigeot 		return -ENOSYS;
5466ba55f2f5SFrançois Tigeot 	if (!args->width || !args->height || !args->bpp)
5467ba55f2f5SFrançois Tigeot 		return -EINVAL;
5468ba55f2f5SFrançois Tigeot 
5469ba55f2f5SFrançois Tigeot 	/* overflow checks for 32bit size calculations */
547024edb884SFrançois Tigeot 	/* NOTE: DIV_ROUND_UP() can overflow */
5471ba55f2f5SFrançois Tigeot 	cpp = DIV_ROUND_UP(args->bpp, 8);
547224edb884SFrançois Tigeot 	if (!cpp || cpp > 0xffffffffU / args->width)
5473ba55f2f5SFrançois Tigeot 		return -EINVAL;
5474ba55f2f5SFrançois Tigeot 	stride = cpp * args->width;
5475ba55f2f5SFrançois Tigeot 	if (args->height > 0xffffffffU / stride)
5476ba55f2f5SFrançois Tigeot 		return -EINVAL;
5477ba55f2f5SFrançois Tigeot 
5478ba55f2f5SFrançois Tigeot 	/* test for wrap-around */
5479ba55f2f5SFrançois Tigeot 	size = args->height * stride;
5480ba55f2f5SFrançois Tigeot 	if (PAGE_ALIGN(size) == 0)
5481ba55f2f5SFrançois Tigeot 		return -EINVAL;
5482ba55f2f5SFrançois Tigeot 
54832c9916cdSFrançois Tigeot 	/*
54842c9916cdSFrançois Tigeot 	 * handle, pitch and size are output parameters. Zero them out to
54852c9916cdSFrançois Tigeot 	 * prevent drivers from accidentally using uninitialized data. Since
54862c9916cdSFrançois Tigeot 	 * not all existing userspace is clearing these fields properly we
54872c9916cdSFrançois Tigeot 	 * cannot reject IOCTL with garbage in them.
54882c9916cdSFrançois Tigeot 	 */
54892c9916cdSFrançois Tigeot 	args->handle = 0;
54902c9916cdSFrançois Tigeot 	args->pitch = 0;
54912c9916cdSFrançois Tigeot 	args->size = 0;
54922c9916cdSFrançois Tigeot 
54935718399fSFrançois Tigeot 	return dev->driver->dumb_create(file_priv, dev, args);
54945718399fSFrançois Tigeot }
54955718399fSFrançois Tigeot 
5496ba55f2f5SFrançois Tigeot /**
5497ba55f2f5SFrançois Tigeot  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
5498ba55f2f5SFrançois Tigeot  * @dev: DRM device
5499ba55f2f5SFrançois Tigeot  * @data: ioctl data
5500ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5501ba55f2f5SFrançois Tigeot  *
5502ba55f2f5SFrançois Tigeot  * Allocate an offset in the drm device node's address space to be able to
5503ba55f2f5SFrançois Tigeot  * memory map a dumb buffer.
5504ba55f2f5SFrançois Tigeot  *
5505ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5506ba55f2f5SFrançois Tigeot  *
5507ba55f2f5SFrançois Tigeot  * Returns:
55082c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5509ba55f2f5SFrançois Tigeot  */
55105718399fSFrançois Tigeot int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
55115718399fSFrançois Tigeot 			     void *data, struct drm_file *file_priv)
55125718399fSFrançois Tigeot {
55135718399fSFrançois Tigeot 	struct drm_mode_map_dumb *args = data;
55145718399fSFrançois Tigeot 
55155718399fSFrançois Tigeot 	/* call driver ioctl to get mmap offset */
55165718399fSFrançois Tigeot 	if (!dev->driver->dumb_map_offset)
5517b5162e19SFrançois Tigeot 		return -ENOSYS;
55185718399fSFrançois Tigeot 
5519f77dbd6cSFrançois Tigeot 	return dev->driver->dumb_map_offset(file_priv, dev, args->handle, (uint64_t *)&args->offset);
55205718399fSFrançois Tigeot }
55215718399fSFrançois Tigeot 
5522ba55f2f5SFrançois Tigeot /**
5523ba55f2f5SFrançois Tigeot  * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer
5524ba55f2f5SFrançois Tigeot  * @dev: DRM device
5525ba55f2f5SFrançois Tigeot  * @data: ioctl data
5526ba55f2f5SFrançois Tigeot  * @file_priv: DRM file info
5527ba55f2f5SFrançois Tigeot  *
5528ba55f2f5SFrançois Tigeot  * This destroys the userspace handle for the given dumb backing storage buffer.
5529ba55f2f5SFrançois Tigeot  * Since buffer objects must be reference counted in the kernel a buffer object
5530ba55f2f5SFrançois Tigeot  * won't be immediately freed if a framebuffer modeset object still uses it.
5531ba55f2f5SFrançois Tigeot  *
5532ba55f2f5SFrançois Tigeot  * Called by the user via ioctl.
5533ba55f2f5SFrançois Tigeot  *
5534ba55f2f5SFrançois Tigeot  * Returns:
55352c9916cdSFrançois Tigeot  * Zero on success, negative errno on failure.
5536ba55f2f5SFrançois Tigeot  */
55375718399fSFrançois Tigeot int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
55385718399fSFrançois Tigeot 				void *data, struct drm_file *file_priv)
55395718399fSFrançois Tigeot {
55405718399fSFrançois Tigeot 	struct drm_mode_destroy_dumb *args = data;
55415718399fSFrançois Tigeot 
55425718399fSFrançois Tigeot 	if (!dev->driver->dumb_destroy)
5543b5162e19SFrançois Tigeot 		return -ENOSYS;
55445718399fSFrançois Tigeot 
55455718399fSFrançois Tigeot 	return dev->driver->dumb_destroy(file_priv, dev, args->handle);
55465718399fSFrançois Tigeot }
55475718399fSFrançois Tigeot 
5548ba55f2f5SFrançois Tigeot /**
5549ba55f2f5SFrançois Tigeot  * drm_fb_get_bpp_depth - get the bpp/depth values for format
5550ba55f2f5SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
5551ba55f2f5SFrançois Tigeot  * @depth: storage for the depth value
5552ba55f2f5SFrançois Tigeot  * @bpp: storage for the bpp value
5553ba55f2f5SFrançois Tigeot  *
5554ba55f2f5SFrançois Tigeot  * This only supports RGB formats here for compat with code that doesn't use
5555ba55f2f5SFrançois Tigeot  * pixel formats directly yet.
55565718399fSFrançois Tigeot  */
55575718399fSFrançois Tigeot void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
55585718399fSFrançois Tigeot 			  int *bpp)
55595718399fSFrançois Tigeot {
55605718399fSFrançois Tigeot 	switch (format) {
55614dbb207bSFrançois Tigeot 	case DRM_FORMAT_C8:
55625718399fSFrançois Tigeot 	case DRM_FORMAT_RGB332:
55635718399fSFrançois Tigeot 	case DRM_FORMAT_BGR233:
55645718399fSFrançois Tigeot 		*depth = 8;
55655718399fSFrançois Tigeot 		*bpp = 8;
55665718399fSFrançois Tigeot 		break;
55675718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB1555:
55685718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR1555:
55695718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX5551:
55705718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX5551:
55715718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB1555:
55725718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR1555:
55735718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA5551:
55745718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA5551:
55755718399fSFrançois Tigeot 		*depth = 15;
55765718399fSFrançois Tigeot 		*bpp = 16;
55775718399fSFrançois Tigeot 		break;
55785718399fSFrançois Tigeot 	case DRM_FORMAT_RGB565:
55795718399fSFrançois Tigeot 	case DRM_FORMAT_BGR565:
55805718399fSFrançois Tigeot 		*depth = 16;
55815718399fSFrançois Tigeot 		*bpp = 16;
55825718399fSFrançois Tigeot 		break;
55835718399fSFrançois Tigeot 	case DRM_FORMAT_RGB888:
55845718399fSFrançois Tigeot 	case DRM_FORMAT_BGR888:
55855718399fSFrançois Tigeot 		*depth = 24;
55865718399fSFrançois Tigeot 		*bpp = 24;
55875718399fSFrançois Tigeot 		break;
55885718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB8888:
55895718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR8888:
55905718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX8888:
55915718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX8888:
55925718399fSFrançois Tigeot 		*depth = 24;
55935718399fSFrançois Tigeot 		*bpp = 32;
55945718399fSFrançois Tigeot 		break;
55955718399fSFrançois Tigeot 	case DRM_FORMAT_XRGB2101010:
55965718399fSFrançois Tigeot 	case DRM_FORMAT_XBGR2101010:
55975718399fSFrançois Tigeot 	case DRM_FORMAT_RGBX1010102:
55985718399fSFrançois Tigeot 	case DRM_FORMAT_BGRX1010102:
55995718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB2101010:
56005718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR2101010:
56015718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA1010102:
56025718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA1010102:
56035718399fSFrançois Tigeot 		*depth = 30;
56045718399fSFrançois Tigeot 		*bpp = 32;
56055718399fSFrançois Tigeot 		break;
56065718399fSFrançois Tigeot 	case DRM_FORMAT_ARGB8888:
56075718399fSFrançois Tigeot 	case DRM_FORMAT_ABGR8888:
56085718399fSFrançois Tigeot 	case DRM_FORMAT_RGBA8888:
56095718399fSFrançois Tigeot 	case DRM_FORMAT_BGRA8888:
56105718399fSFrançois Tigeot 		*depth = 32;
56115718399fSFrançois Tigeot 		*bpp = 32;
56125718399fSFrançois Tigeot 		break;
56135718399fSFrançois Tigeot 	default:
56149edbd4a0SFrançois Tigeot 		DRM_DEBUG_KMS("unsupported pixel format %s\n",
56159edbd4a0SFrançois Tigeot 			      drm_get_format_name(format));
56165718399fSFrançois Tigeot 		*depth = 0;
56175718399fSFrançois Tigeot 		*bpp = 0;
56185718399fSFrançois Tigeot 		break;
56195718399fSFrançois Tigeot 	}
56205718399fSFrançois Tigeot }
5621b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_fb_get_bpp_depth);
5622b5162e19SFrançois Tigeot 
5623b5162e19SFrançois Tigeot /**
5624b5162e19SFrançois Tigeot  * drm_format_num_planes - get the number of planes for format
5625b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
5626b5162e19SFrançois Tigeot  *
5627ba55f2f5SFrançois Tigeot  * Returns:
5628b5162e19SFrançois Tigeot  * The number of planes used by the specified pixel format.
5629b5162e19SFrançois Tigeot  */
5630b5162e19SFrançois Tigeot int drm_format_num_planes(uint32_t format)
5631b5162e19SFrançois Tigeot {
5632b5162e19SFrançois Tigeot 	switch (format) {
5633b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
5634b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
5635b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
5636b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
5637b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
5638b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
5639b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
5640b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
5641b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV444:
5642b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU444:
5643b5162e19SFrançois Tigeot 		return 3;
5644b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
5645b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
5646b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
5647b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
5648b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
5649b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
5650b5162e19SFrançois Tigeot 		return 2;
5651b5162e19SFrançois Tigeot 	default:
5652b5162e19SFrançois Tigeot 		return 1;
5653b5162e19SFrançois Tigeot 	}
5654b5162e19SFrançois Tigeot }
5655b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_num_planes);
5656b5162e19SFrançois Tigeot 
5657b5162e19SFrançois Tigeot /**
5658b5162e19SFrançois Tigeot  * drm_format_plane_cpp - determine the bytes per pixel value
5659b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
5660b5162e19SFrançois Tigeot  * @plane: plane index
5661b5162e19SFrançois Tigeot  *
5662ba55f2f5SFrançois Tigeot  * Returns:
5663b5162e19SFrançois Tigeot  * The bytes per pixel value for the specified plane.
5664b5162e19SFrançois Tigeot  */
5665b5162e19SFrançois Tigeot int drm_format_plane_cpp(uint32_t format, int plane)
5666b5162e19SFrançois Tigeot {
5667b5162e19SFrançois Tigeot 	unsigned int depth;
5668b5162e19SFrançois Tigeot 	int bpp;
5669b5162e19SFrançois Tigeot 
5670b5162e19SFrançois Tigeot 	if (plane >= drm_format_num_planes(format))
5671b5162e19SFrançois Tigeot 		return 0;
5672b5162e19SFrançois Tigeot 
5673b5162e19SFrançois Tigeot 	switch (format) {
5674b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUYV:
5675b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVYU:
5676b5162e19SFrançois Tigeot 	case DRM_FORMAT_UYVY:
5677b5162e19SFrançois Tigeot 	case DRM_FORMAT_VYUY:
5678b5162e19SFrançois Tigeot 		return 2;
5679b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
5680b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
5681b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
5682b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
5683b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV24:
5684b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV42:
5685b5162e19SFrançois Tigeot 		return plane ? 2 : 1;
5686b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
5687b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
5688b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
5689b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
5690b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
5691b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
5692b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
5693b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
5694b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV444:
5695b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU444:
5696b5162e19SFrançois Tigeot 		return 1;
5697b5162e19SFrançois Tigeot 	default:
5698b5162e19SFrançois Tigeot 		drm_fb_get_bpp_depth(format, &depth, &bpp);
5699b5162e19SFrançois Tigeot 		return bpp >> 3;
5700b5162e19SFrançois Tigeot 	}
5701b5162e19SFrançois Tigeot }
5702b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_cpp);
5703b5162e19SFrançois Tigeot 
5704b5162e19SFrançois Tigeot /**
5705b5162e19SFrançois Tigeot  * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor
5706b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
5707b5162e19SFrançois Tigeot  *
5708ba55f2f5SFrançois Tigeot  * Returns:
5709b5162e19SFrançois Tigeot  * The horizontal chroma subsampling factor for the
5710b5162e19SFrançois Tigeot  * specified pixel format.
5711b5162e19SFrançois Tigeot  */
5712b5162e19SFrançois Tigeot int drm_format_horz_chroma_subsampling(uint32_t format)
5713b5162e19SFrançois Tigeot {
5714b5162e19SFrançois Tigeot 	switch (format) {
5715b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV411:
5716b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU411:
5717b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
5718b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
5719b5162e19SFrançois Tigeot 		return 4;
5720b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUYV:
5721b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVYU:
5722b5162e19SFrançois Tigeot 	case DRM_FORMAT_UYVY:
5723b5162e19SFrançois Tigeot 	case DRM_FORMAT_VYUY:
5724b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
5725b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
5726b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV16:
5727b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV61:
5728b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV422:
5729b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU422:
5730b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
5731b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
5732b5162e19SFrançois Tigeot 		return 2;
5733b5162e19SFrançois Tigeot 	default:
5734b5162e19SFrançois Tigeot 		return 1;
5735b5162e19SFrançois Tigeot 	}
5736b5162e19SFrançois Tigeot }
5737b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_horz_chroma_subsampling);
5738b5162e19SFrançois Tigeot 
5739b5162e19SFrançois Tigeot /**
5740b5162e19SFrançois Tigeot  * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor
5741b5162e19SFrançois Tigeot  * @format: pixel format (DRM_FORMAT_*)
5742b5162e19SFrançois Tigeot  *
5743ba55f2f5SFrançois Tigeot  * Returns:
5744b5162e19SFrançois Tigeot  * The vertical chroma subsampling factor for the
5745b5162e19SFrançois Tigeot  * specified pixel format.
5746b5162e19SFrançois Tigeot  */
5747b5162e19SFrançois Tigeot int drm_format_vert_chroma_subsampling(uint32_t format)
5748b5162e19SFrançois Tigeot {
5749b5162e19SFrançois Tigeot 	switch (format) {
5750b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV410:
5751b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU410:
5752b5162e19SFrançois Tigeot 		return 4;
5753b5162e19SFrançois Tigeot 	case DRM_FORMAT_YUV420:
5754b5162e19SFrançois Tigeot 	case DRM_FORMAT_YVU420:
5755b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV12:
5756b5162e19SFrançois Tigeot 	case DRM_FORMAT_NV21:
5757b5162e19SFrançois Tigeot 		return 2;
5758b5162e19SFrançois Tigeot 	default:
5759b5162e19SFrançois Tigeot 		return 1;
5760b5162e19SFrançois Tigeot 	}
5761b5162e19SFrançois Tigeot }
5762b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
57634dbb207bSFrançois Tigeot 
57644dbb207bSFrançois Tigeot /**
5765c0e85e96SFrançois Tigeot  * drm_format_plane_width - width of the plane given the first plane
5766c0e85e96SFrançois Tigeot  * @width: width of the first plane
5767c0e85e96SFrançois Tigeot  * @format: pixel format
5768c0e85e96SFrançois Tigeot  * @plane: plane index
5769c0e85e96SFrançois Tigeot  *
5770c0e85e96SFrançois Tigeot  * Returns:
5771c0e85e96SFrançois Tigeot  * The width of @plane, given that the width of the first plane is @width.
5772c0e85e96SFrançois Tigeot  */
5773c0e85e96SFrançois Tigeot int drm_format_plane_width(int width, uint32_t format, int plane)
5774c0e85e96SFrançois Tigeot {
5775c0e85e96SFrançois Tigeot 	if (plane >= drm_format_num_planes(format))
5776c0e85e96SFrançois Tigeot 		return 0;
5777c0e85e96SFrançois Tigeot 
5778c0e85e96SFrançois Tigeot 	if (plane == 0)
5779c0e85e96SFrançois Tigeot 		return width;
5780c0e85e96SFrançois Tigeot 
5781c0e85e96SFrançois Tigeot 	return width / drm_format_horz_chroma_subsampling(format);
5782c0e85e96SFrançois Tigeot }
5783c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_width);
5784c0e85e96SFrançois Tigeot 
5785c0e85e96SFrançois Tigeot /**
5786c0e85e96SFrançois Tigeot  * drm_format_plane_height - height of the plane given the first plane
5787c0e85e96SFrançois Tigeot  * @height: height of the first plane
5788c0e85e96SFrançois Tigeot  * @format: pixel format
5789c0e85e96SFrançois Tigeot  * @plane: plane index
5790c0e85e96SFrançois Tigeot  *
5791c0e85e96SFrançois Tigeot  * Returns:
5792c0e85e96SFrançois Tigeot  * The height of @plane, given that the height of the first plane is @height.
5793c0e85e96SFrançois Tigeot  */
5794c0e85e96SFrançois Tigeot int drm_format_plane_height(int height, uint32_t format, int plane)
5795c0e85e96SFrançois Tigeot {
5796c0e85e96SFrançois Tigeot 	if (plane >= drm_format_num_planes(format))
5797c0e85e96SFrançois Tigeot 		return 0;
5798c0e85e96SFrançois Tigeot 
5799c0e85e96SFrançois Tigeot 	if (plane == 0)
5800c0e85e96SFrançois Tigeot 		return height;
5801c0e85e96SFrançois Tigeot 
5802c0e85e96SFrançois Tigeot 	return height / drm_format_vert_chroma_subsampling(format);
5803c0e85e96SFrançois Tigeot }
5804c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_height);
5805c0e85e96SFrançois Tigeot 
5806c0e85e96SFrançois Tigeot /**
58072c9916cdSFrançois Tigeot  * drm_rotation_simplify() - Try to simplify the rotation
58082c9916cdSFrançois Tigeot  * @rotation: Rotation to be simplified
58092c9916cdSFrançois Tigeot  * @supported_rotations: Supported rotations
58102c9916cdSFrançois Tigeot  *
58112c9916cdSFrançois Tigeot  * Attempt to simplify the rotation to a form that is supported.
58122c9916cdSFrançois Tigeot  * Eg. if the hardware supports everything except DRM_REFLECT_X
58132c9916cdSFrançois Tigeot  * one could call this function like this:
58142c9916cdSFrançois Tigeot  *
58152c9916cdSFrançois Tigeot  * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) |
58162c9916cdSFrançois Tigeot  *                       BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) |
58172c9916cdSFrançois Tigeot  *                       BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y));
58182c9916cdSFrançois Tigeot  *
58192c9916cdSFrançois Tigeot  * to eliminate the DRM_ROTATE_X flag. Depending on what kind of
58202c9916cdSFrançois Tigeot  * transforms the hardware supports, this function may not
58212c9916cdSFrançois Tigeot  * be able to produce a supported transform, so the caller should
58222c9916cdSFrançois Tigeot  * check the result afterwards.
58232c9916cdSFrançois Tigeot  */
58242c9916cdSFrançois Tigeot unsigned int drm_rotation_simplify(unsigned int rotation,
58252c9916cdSFrançois Tigeot 				   unsigned int supported_rotations)
58262c9916cdSFrançois Tigeot {
58272c9916cdSFrançois Tigeot 	if (rotation & ~supported_rotations) {
58282c9916cdSFrançois Tigeot 		rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y);
5829352ff8bdSFrançois Tigeot 		rotation = (rotation & DRM_REFLECT_MASK) |
5830352ff8bdSFrançois Tigeot 		           BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4);
58312c9916cdSFrançois Tigeot 	}
58322c9916cdSFrançois Tigeot 
58332c9916cdSFrançois Tigeot 	return rotation;
58342c9916cdSFrançois Tigeot }
58352c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_rotation_simplify);
58362c9916cdSFrançois Tigeot 
58372c9916cdSFrançois Tigeot /**
58384dbb207bSFrançois Tigeot  * drm_mode_config_init - initialize DRM mode_configuration structure
58394dbb207bSFrançois Tigeot  * @dev: DRM device
58404dbb207bSFrançois Tigeot  *
58414dbb207bSFrançois Tigeot  * Initialize @dev's mode_config structure, used for tracking the graphics
58424dbb207bSFrançois Tigeot  * configuration of @dev.
58434dbb207bSFrançois Tigeot  *
58444dbb207bSFrançois Tigeot  * Since this initializes the modeset locks, no locking is possible. Which is no
58454dbb207bSFrançois Tigeot  * problem, since this should happen single threaded at init time. It is the
58464dbb207bSFrançois Tigeot  * driver's problem to ensure this guarantee.
58474dbb207bSFrançois Tigeot  *
58484dbb207bSFrançois Tigeot  */
58494dbb207bSFrançois Tigeot void drm_mode_config_init(struct drm_device *dev)
58504dbb207bSFrançois Tigeot {
58514dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE);
5852ba55f2f5SFrançois Tigeot 	drm_modeset_lock_init(&dev->mode_config.connection_mutex);
58534dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE);
58544dbb207bSFrançois Tigeot 	lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE);
585519c468b4SFrançois Tigeot 	lockinit(&dev->mode_config.blob_lock, "drmcbl", 0, LK_CANRECURSE);
58564dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
58574dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.crtc_list);
58584dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.connector_list);
58594dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.encoder_list);
58604dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.property_list);
58614dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
58624dbb207bSFrançois Tigeot 	INIT_LIST_HEAD(&dev->mode_config.plane_list);
58634dbb207bSFrançois Tigeot 	idr_init(&dev->mode_config.crtc_idr);
58642c9916cdSFrançois Tigeot 	idr_init(&dev->mode_config.tile_idr);
5865917ec290SFrançois Tigeot 	ida_init(&dev->mode_config.connector_ida);
58664dbb207bSFrançois Tigeot 
58674dbb207bSFrançois Tigeot 	drm_modeset_lock_all(dev);
58682c9916cdSFrançois Tigeot 	drm_mode_create_standard_properties(dev);
58694dbb207bSFrançois Tigeot 	drm_modeset_unlock_all(dev);
58704dbb207bSFrançois Tigeot 
58714dbb207bSFrançois Tigeot 	/* Just to be sure */
58724dbb207bSFrançois Tigeot 	dev->mode_config.num_fb = 0;
58734dbb207bSFrançois Tigeot 	dev->mode_config.num_connector = 0;
58744dbb207bSFrançois Tigeot 	dev->mode_config.num_crtc = 0;
58754dbb207bSFrançois Tigeot 	dev->mode_config.num_encoder = 0;
5876ba55f2f5SFrançois Tigeot 	dev->mode_config.num_overlay_plane = 0;
5877ba55f2f5SFrançois Tigeot 	dev->mode_config.num_total_plane = 0;
58784dbb207bSFrançois Tigeot }
58794dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_init);
58804dbb207bSFrançois Tigeot 
58814dbb207bSFrançois Tigeot /**
58824dbb207bSFrançois Tigeot  * drm_mode_config_cleanup - free up DRM mode_config info
58834dbb207bSFrançois Tigeot  * @dev: DRM device
58844dbb207bSFrançois Tigeot  *
58854dbb207bSFrançois Tigeot  * Free up all the connectors and CRTCs associated with this DRM device, then
58864dbb207bSFrançois Tigeot  * free up the framebuffers and associated buffer objects.
58874dbb207bSFrançois Tigeot  *
58884dbb207bSFrançois Tigeot  * Note that since this /should/ happen single-threaded at driver/device
58894dbb207bSFrançois Tigeot  * teardown time, no locking is required. It's the driver's job to ensure that
58904dbb207bSFrançois Tigeot  * this guarantee actually holds true.
58914dbb207bSFrançois Tigeot  *
58924dbb207bSFrançois Tigeot  * FIXME: cleanup any dangling user buffer objects too
58934dbb207bSFrançois Tigeot  */
58944dbb207bSFrançois Tigeot void drm_mode_config_cleanup(struct drm_device *dev)
58954dbb207bSFrançois Tigeot {
58964dbb207bSFrançois Tigeot 	struct drm_connector *connector, *ot;
58974dbb207bSFrançois Tigeot 	struct drm_crtc *crtc, *ct;
58984dbb207bSFrançois Tigeot 	struct drm_encoder *encoder, *enct;
58994dbb207bSFrançois Tigeot 	struct drm_framebuffer *fb, *fbt;
59004dbb207bSFrançois Tigeot 	struct drm_property *property, *pt;
59014dbb207bSFrançois Tigeot 	struct drm_property_blob *blob, *bt;
59024dbb207bSFrançois Tigeot 	struct drm_plane *plane, *plt;
59034dbb207bSFrançois Tigeot 
59044dbb207bSFrançois Tigeot 	list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
59054dbb207bSFrançois Tigeot 				 head) {
59064dbb207bSFrançois Tigeot 		encoder->funcs->destroy(encoder);
59074dbb207bSFrançois Tigeot 	}
59084dbb207bSFrançois Tigeot 
59094dbb207bSFrançois Tigeot 	list_for_each_entry_safe(connector, ot,
59104dbb207bSFrançois Tigeot 				 &dev->mode_config.connector_list, head) {
59114dbb207bSFrançois Tigeot 		connector->funcs->destroy(connector);
59124dbb207bSFrançois Tigeot 	}
59134dbb207bSFrançois Tigeot 
59144dbb207bSFrançois Tigeot 	list_for_each_entry_safe(property, pt, &dev->mode_config.property_list,
59154dbb207bSFrançois Tigeot 				 head) {
59164dbb207bSFrançois Tigeot 		drm_property_destroy(dev, property);
59174dbb207bSFrançois Tigeot 	}
59184dbb207bSFrançois Tigeot 
59198621f407SFrançois Tigeot 	list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
59208621f407SFrançois Tigeot 				 head) {
59218621f407SFrançois Tigeot 		plane->funcs->destroy(plane);
59228621f407SFrançois Tigeot 	}
59238621f407SFrançois Tigeot 
59248621f407SFrançois Tigeot 	list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
59258621f407SFrançois Tigeot 		crtc->funcs->destroy(crtc);
59268621f407SFrançois Tigeot 	}
59278621f407SFrançois Tigeot 
59284dbb207bSFrançois Tigeot 	list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list,
592919c468b4SFrançois Tigeot 				 head_global) {
593019c468b4SFrançois Tigeot 		drm_property_unreference_blob(blob);
59314dbb207bSFrançois Tigeot 	}
59324dbb207bSFrançois Tigeot 
59334dbb207bSFrançois Tigeot 	/*
59344dbb207bSFrançois Tigeot 	 * Single-threaded teardown context, so it's not required to grab the
59354dbb207bSFrançois Tigeot 	 * fb_lock to protect against concurrent fb_list access. Contrary, it
59364dbb207bSFrançois Tigeot 	 * would actually deadlock with the drm_framebuffer_cleanup function.
59374dbb207bSFrançois Tigeot 	 *
59384dbb207bSFrançois Tigeot 	 * Also, if there are any framebuffers left, that's a driver leak now,
59394dbb207bSFrançois Tigeot 	 * so politely WARN about this.
59404dbb207bSFrançois Tigeot 	 */
59414dbb207bSFrançois Tigeot 	WARN_ON(!list_empty(&dev->mode_config.fb_list));
59424dbb207bSFrançois Tigeot 	list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
59438621f407SFrançois Tigeot 		drm_framebuffer_free(&fb->base.refcount);
59444dbb207bSFrançois Tigeot 	}
59454dbb207bSFrançois Tigeot 
5946917ec290SFrançois Tigeot 	ida_destroy(&dev->mode_config.connector_ida);
59472c9916cdSFrançois Tigeot 	idr_destroy(&dev->mode_config.tile_idr);
59484dbb207bSFrançois Tigeot 	idr_destroy(&dev->mode_config.crtc_idr);
5949ba55f2f5SFrançois Tigeot 	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
59504dbb207bSFrançois Tigeot }
59514dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_cleanup);
59521b13d190SFrançois Tigeot 
59531b13d190SFrançois Tigeot struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
59541b13d190SFrançois Tigeot 						       unsigned int supported_rotations)
59551b13d190SFrançois Tigeot {
59561b13d190SFrançois Tigeot 	static const struct drm_prop_enum_list props[] = {
59571b13d190SFrançois Tigeot 		{ DRM_ROTATE_0,   "rotate-0" },
59581b13d190SFrançois Tigeot 		{ DRM_ROTATE_90,  "rotate-90" },
59591b13d190SFrançois Tigeot 		{ DRM_ROTATE_180, "rotate-180" },
59601b13d190SFrançois Tigeot 		{ DRM_ROTATE_270, "rotate-270" },
59611b13d190SFrançois Tigeot 		{ DRM_REFLECT_X,  "reflect-x" },
59621b13d190SFrançois Tigeot 		{ DRM_REFLECT_Y,  "reflect-y" },
59631b13d190SFrançois Tigeot 	};
59641b13d190SFrançois Tigeot 
59651b13d190SFrançois Tigeot 	return drm_property_create_bitmask(dev, 0, "rotation",
59661b13d190SFrançois Tigeot 					   props, ARRAY_SIZE(props),
59671b13d190SFrançois Tigeot 					   supported_rotations);
59681b13d190SFrançois Tigeot }
59691b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_rotation_property);
59702c9916cdSFrançois Tigeot 
59712c9916cdSFrançois Tigeot /**
59722c9916cdSFrançois Tigeot  * DOC: Tile group
59732c9916cdSFrançois Tigeot  *
59742c9916cdSFrançois Tigeot  * Tile groups are used to represent tiled monitors with a unique
59752c9916cdSFrançois Tigeot  * integer identifier. Tiled monitors using DisplayID v1.3 have
59762c9916cdSFrançois Tigeot  * a unique 8-byte handle, we store this in a tile group, so we
59772c9916cdSFrançois Tigeot  * have a common identifier for all tiles in a monitor group.
59782c9916cdSFrançois Tigeot  */
59792c9916cdSFrançois Tigeot static void drm_tile_group_free(struct kref *kref)
59802c9916cdSFrançois Tigeot {
59812c9916cdSFrançois Tigeot 	struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount);
59822c9916cdSFrançois Tigeot 	struct drm_device *dev = tg->dev;
59832c9916cdSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
59842c9916cdSFrançois Tigeot 	idr_remove(&dev->mode_config.tile_idr, tg->id);
59852c9916cdSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
59862c9916cdSFrançois Tigeot 	kfree(tg);
59872c9916cdSFrançois Tigeot }
59882c9916cdSFrançois Tigeot 
59892c9916cdSFrançois Tigeot /**
59902c9916cdSFrançois Tigeot  * drm_mode_put_tile_group - drop a reference to a tile group.
59912c9916cdSFrançois Tigeot  * @dev: DRM device
59922c9916cdSFrançois Tigeot  * @tg: tile group to drop reference to.
59932c9916cdSFrançois Tigeot  *
59942c9916cdSFrançois Tigeot  * drop reference to tile group and free if 0.
59952c9916cdSFrançois Tigeot  */
59962c9916cdSFrançois Tigeot void drm_mode_put_tile_group(struct drm_device *dev,
59972c9916cdSFrançois Tigeot 			     struct drm_tile_group *tg)
59982c9916cdSFrançois Tigeot {
59992c9916cdSFrançois Tigeot 	kref_put(&tg->refcount, drm_tile_group_free);
60002c9916cdSFrançois Tigeot }
60012c9916cdSFrançois Tigeot 
60022c9916cdSFrançois Tigeot /**
60032c9916cdSFrançois Tigeot  * drm_mode_get_tile_group - get a reference to an existing tile group
60042c9916cdSFrançois Tigeot  * @dev: DRM device
60052c9916cdSFrançois Tigeot  * @topology: 8-bytes unique per monitor.
60062c9916cdSFrançois Tigeot  *
60072c9916cdSFrançois Tigeot  * Use the unique bytes to get a reference to an existing tile group.
60082c9916cdSFrançois Tigeot  *
60092c9916cdSFrançois Tigeot  * RETURNS:
60102c9916cdSFrançois Tigeot  * tile group or NULL if not found.
60112c9916cdSFrançois Tigeot  */
60122c9916cdSFrançois Tigeot struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
60132c9916cdSFrançois Tigeot 					       char topology[8])
60142c9916cdSFrançois Tigeot {
60152c9916cdSFrançois Tigeot #if 0
60162c9916cdSFrançois Tigeot 	struct drm_tile_group *tg;
60172c9916cdSFrançois Tigeot 	int id;
60182c9916cdSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
60192c9916cdSFrançois Tigeot 	idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) {
60202c9916cdSFrançois Tigeot 		if (!memcmp(tg->group_data, topology, 8)) {
60212c9916cdSFrançois Tigeot 			if (!kref_get_unless_zero(&tg->refcount))
60222c9916cdSFrançois Tigeot 				tg = NULL;
60232c9916cdSFrançois Tigeot 			mutex_unlock(&dev->mode_config.idr_mutex);
60242c9916cdSFrançois Tigeot 			return tg;
60252c9916cdSFrançois Tigeot 		}
60262c9916cdSFrançois Tigeot 	}
60272c9916cdSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
60282c9916cdSFrançois Tigeot #endif
60292c9916cdSFrançois Tigeot 	return NULL;
60302c9916cdSFrançois Tigeot }
6031477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_get_tile_group);
60322c9916cdSFrançois Tigeot 
60332c9916cdSFrançois Tigeot /**
60342c9916cdSFrançois Tigeot  * drm_mode_create_tile_group - create a tile group from a displayid description
60352c9916cdSFrançois Tigeot  * @dev: DRM device
60362c9916cdSFrançois Tigeot  * @topology: 8-bytes unique per monitor.
60372c9916cdSFrançois Tigeot  *
60382c9916cdSFrançois Tigeot  * Create a tile group for the unique monitor, and get a unique
60392c9916cdSFrançois Tigeot  * identifier for the tile group.
60402c9916cdSFrançois Tigeot  *
60412c9916cdSFrançois Tigeot  * RETURNS:
60422c9916cdSFrançois Tigeot  * new tile group or error.
60432c9916cdSFrançois Tigeot  */
60442c9916cdSFrançois Tigeot struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
60452c9916cdSFrançois Tigeot 						  char topology[8])
60462c9916cdSFrançois Tigeot {
60472c9916cdSFrançois Tigeot 	struct drm_tile_group *tg;
60482c9916cdSFrançois Tigeot 	int ret;
60492c9916cdSFrançois Tigeot 
60502c9916cdSFrançois Tigeot 	tg = kzalloc(sizeof(*tg), GFP_KERNEL);
60512c9916cdSFrançois Tigeot 	if (!tg)
60522c9916cdSFrançois Tigeot 		return ERR_PTR(-ENOMEM);
60532c9916cdSFrançois Tigeot 
60542c9916cdSFrançois Tigeot 	kref_init(&tg->refcount);
60552c9916cdSFrançois Tigeot 	memcpy(tg->group_data, topology, 8);
60562c9916cdSFrançois Tigeot 	tg->dev = dev;
60572c9916cdSFrançois Tigeot 
60582c9916cdSFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
60592c9916cdSFrançois Tigeot 	ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL);
60602c9916cdSFrançois Tigeot 	if (ret >= 0) {
60612c9916cdSFrançois Tigeot 		tg->id = ret;
60622c9916cdSFrançois Tigeot 	} else {
60632c9916cdSFrançois Tigeot 		kfree(tg);
60642c9916cdSFrançois Tigeot 		tg = ERR_PTR(ret);
60652c9916cdSFrançois Tigeot 	}
60662c9916cdSFrançois Tigeot 
60672c9916cdSFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
60682c9916cdSFrançois Tigeot 	return tg;
60692c9916cdSFrançois Tigeot }
6070477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tile_group);
6071