xref: /dflybsd-src/sys/dev/drm/drm_connector.c (revision 789731325bde747251c28a37e0a00ed4efb88c46)
11dedbd3bSFrançois Tigeot /*
21dedbd3bSFrançois Tigeot  * Copyright (c) 2016 Intel Corporation
31dedbd3bSFrançois Tigeot  *
41dedbd3bSFrançois Tigeot  * Permission to use, copy, modify, distribute, and sell this software and its
51dedbd3bSFrançois Tigeot  * documentation for any purpose is hereby granted without fee, provided that
61dedbd3bSFrançois Tigeot  * the above copyright notice appear in all copies and that both that copyright
71dedbd3bSFrançois Tigeot  * notice and this permission notice appear in supporting documentation, and
81dedbd3bSFrançois Tigeot  * that the name of the copyright holders not be used in advertising or
91dedbd3bSFrançois Tigeot  * publicity pertaining to distribution of the software without specific,
101dedbd3bSFrançois Tigeot  * written prior permission.  The copyright holders make no representations
111dedbd3bSFrançois Tigeot  * about the suitability of this software for any purpose.  It is provided "as
121dedbd3bSFrançois Tigeot  * is" without express or implied warranty.
131dedbd3bSFrançois Tigeot  *
141dedbd3bSFrançois Tigeot  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
151dedbd3bSFrançois Tigeot  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
161dedbd3bSFrançois Tigeot  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
171dedbd3bSFrançois Tigeot  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
181dedbd3bSFrançois Tigeot  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
191dedbd3bSFrançois Tigeot  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
201dedbd3bSFrançois Tigeot  * OF THIS SOFTWARE.
211dedbd3bSFrançois Tigeot  */
221dedbd3bSFrançois Tigeot 
231dedbd3bSFrançois Tigeot #include <drm/drmP.h>
241dedbd3bSFrançois Tigeot #include <drm/drm_connector.h>
251dedbd3bSFrançois Tigeot #include <drm/drm_edid.h>
26a85cb24fSFrançois Tigeot #include <drm/drm_encoder.h>
271dedbd3bSFrançois Tigeot 
281dedbd3bSFrançois Tigeot #include "drm_crtc_internal.h"
291dedbd3bSFrançois Tigeot #include "drm_internal.h"
301dedbd3bSFrançois Tigeot 
311dedbd3bSFrançois Tigeot /**
321dedbd3bSFrançois Tigeot  * DOC: overview
331dedbd3bSFrançois Tigeot  *
341dedbd3bSFrançois Tigeot  * In DRM connectors are the general abstraction for display sinks, and include
351dedbd3bSFrançois Tigeot  * als fixed panels or anything else that can display pixels in some form. As
361dedbd3bSFrançois Tigeot  * opposed to all other KMS objects representing hardware (like CRTC, encoder or
371dedbd3bSFrançois Tigeot  * plane abstractions) connectors can be hotplugged and unplugged at runtime.
38a85cb24fSFrançois Tigeot  * Hence they are reference-counted using drm_connector_get() and
39a85cb24fSFrançois Tigeot  * drm_connector_put().
401dedbd3bSFrançois Tigeot  *
41a85cb24fSFrançois Tigeot  * KMS driver must create, initialize, register and attach at a &struct
42a85cb24fSFrançois Tigeot  * drm_connector for each such sink. The instance is created as other KMS
43a85cb24fSFrançois Tigeot  * objects and initialized by setting the following fields. The connector is
44a85cb24fSFrançois Tigeot  * initialized with a call to drm_connector_init() with a pointer to the
45a85cb24fSFrançois Tigeot  * &struct drm_connector_funcs and a connector type, and then exposed to
46a85cb24fSFrançois Tigeot  * userspace with a call to drm_connector_register().
471dedbd3bSFrançois Tigeot  *
481dedbd3bSFrançois Tigeot  * Connectors must be attached to an encoder to be used. For devices that map
491dedbd3bSFrançois Tigeot  * connectors to encoders 1:1, the connector should be attached at
501dedbd3bSFrançois Tigeot  * initialization time with a call to drm_mode_connector_attach_encoder(). The
51a85cb24fSFrançois Tigeot  * driver must also set the &drm_connector.encoder field to point to the
521dedbd3bSFrançois Tigeot  * attached encoder.
531dedbd3bSFrançois Tigeot  *
541dedbd3bSFrançois Tigeot  * For connectors which are not fixed (like built-in panels) the driver needs to
551dedbd3bSFrançois Tigeot  * support hotplug notifications. The simplest way to do that is by using the
561dedbd3bSFrançois Tigeot  * probe helpers, see drm_kms_helper_poll_init() for connectors which don't have
571dedbd3bSFrançois Tigeot  * hardware support for hotplug interrupts. Connectors with hardware hotplug
581dedbd3bSFrançois Tigeot  * support can instead use e.g. drm_helper_hpd_irq_event().
591dedbd3bSFrançois Tigeot  */
601dedbd3bSFrançois Tigeot 
611dedbd3bSFrançois Tigeot struct drm_conn_prop_enum_list {
621dedbd3bSFrançois Tigeot 	int type;
631dedbd3bSFrançois Tigeot 	const char *name;
641dedbd3bSFrançois Tigeot 	struct ida ida;
651dedbd3bSFrançois Tigeot };
661dedbd3bSFrançois Tigeot 
671dedbd3bSFrançois Tigeot /*
681dedbd3bSFrançois Tigeot  * Connector and encoder types.
691dedbd3bSFrançois Tigeot  */
701dedbd3bSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
711dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
721dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VGA, "VGA" },
731dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },
741dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },
751dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
761dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Composite, "Composite" },
771dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
781dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },
791dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_Component, "Component" },
801dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
811dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },
821dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
831dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
841dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_TV, "TV" },
851dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_eDP, "eDP" },
861dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
871dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DSI, "DSI" },
881dedbd3bSFrançois Tigeot 	{ DRM_MODE_CONNECTOR_DPI, "DPI" },
891dedbd3bSFrançois Tigeot };
901dedbd3bSFrançois Tigeot 
drm_connector_ida_init(void)911dedbd3bSFrançois Tigeot void drm_connector_ida_init(void)
921dedbd3bSFrançois Tigeot {
931dedbd3bSFrançois Tigeot 	int i;
941dedbd3bSFrançois Tigeot 
951dedbd3bSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
961dedbd3bSFrançois Tigeot 		ida_init(&drm_connector_enum_list[i].ida);
971dedbd3bSFrançois Tigeot }
981dedbd3bSFrançois Tigeot 
drm_connector_ida_destroy(void)991dedbd3bSFrançois Tigeot void drm_connector_ida_destroy(void)
1001dedbd3bSFrançois Tigeot {
1011dedbd3bSFrançois Tigeot 	int i;
1021dedbd3bSFrançois Tigeot 
1031dedbd3bSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
1041dedbd3bSFrançois Tigeot 		ida_destroy(&drm_connector_enum_list[i].ida);
1051dedbd3bSFrançois Tigeot }
1061dedbd3bSFrançois Tigeot 
1071dedbd3bSFrançois Tigeot /**
1081dedbd3bSFrançois Tigeot  * drm_connector_get_cmdline_mode - reads the user's cmdline mode
1091dedbd3bSFrançois Tigeot  * @connector: connector to quwery
1101dedbd3bSFrançois Tigeot  *
1111dedbd3bSFrançois Tigeot  * The kernel supports per-connector configuration of its consoles through
1121dedbd3bSFrançois Tigeot  * use of the video= parameter. This function parses that option and
1131dedbd3bSFrançois Tigeot  * extracts the user's specified mode (or enable/disable status) for a
1141dedbd3bSFrançois Tigeot  * particular connector. This is typically only used during the early fbdev
1151dedbd3bSFrançois Tigeot  * setup.
1161dedbd3bSFrançois Tigeot  */
drm_connector_get_cmdline_mode(struct drm_connector * connector)1171dedbd3bSFrançois Tigeot static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
1181dedbd3bSFrançois Tigeot {
1191dedbd3bSFrançois Tigeot 	struct drm_cmdline_mode *mode = &connector->cmdline_mode;
1201dedbd3bSFrançois Tigeot 	char *option = NULL;
1211dedbd3bSFrançois Tigeot 
1221dedbd3bSFrançois Tigeot 	if (fb_get_options(connector->name, &option))
1231dedbd3bSFrançois Tigeot 		return;
1241dedbd3bSFrançois Tigeot 
1251dedbd3bSFrançois Tigeot 	if (!drm_mode_parse_command_line_for_connector(option,
1261dedbd3bSFrançois Tigeot 						       connector,
1271dedbd3bSFrançois Tigeot 						       mode))
1281dedbd3bSFrançois Tigeot 		return;
1291dedbd3bSFrançois Tigeot 
1301dedbd3bSFrançois Tigeot 	if (mode->force) {
131a85cb24fSFrançois Tigeot 		DRM_INFO("forcing %s connector %s\n", connector->name,
132a85cb24fSFrançois Tigeot 			 drm_get_connector_force_name(mode->force));
1331dedbd3bSFrançois Tigeot 		connector->force = mode->force;
1341dedbd3bSFrançois Tigeot 	}
1351dedbd3bSFrançois Tigeot 
1361dedbd3bSFrançois Tigeot 	DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
1371dedbd3bSFrançois Tigeot 		      connector->name,
1381dedbd3bSFrançois Tigeot 		      mode->xres, mode->yres,
1391dedbd3bSFrançois Tigeot 		      mode->refresh_specified ? mode->refresh : 60,
1401dedbd3bSFrançois Tigeot 		      mode->rb ? " reduced blanking" : "",
1411dedbd3bSFrançois Tigeot 		      mode->margins ? " with margins" : "",
1421dedbd3bSFrançois Tigeot 		      mode->interlace ?  " interlaced" : "");
1431dedbd3bSFrançois Tigeot }
1441dedbd3bSFrançois Tigeot 
drm_connector_free(struct kref * kref)1451dedbd3bSFrançois Tigeot static void drm_connector_free(struct kref *kref)
1461dedbd3bSFrançois Tigeot {
1471dedbd3bSFrançois Tigeot 	struct drm_connector *connector =
1481dedbd3bSFrançois Tigeot 		container_of(kref, struct drm_connector, base.refcount);
1491dedbd3bSFrançois Tigeot 	struct drm_device *dev = connector->dev;
1501dedbd3bSFrançois Tigeot 
1511dedbd3bSFrançois Tigeot 	drm_mode_object_unregister(dev, &connector->base);
1521dedbd3bSFrançois Tigeot 	connector->funcs->destroy(connector);
1531dedbd3bSFrançois Tigeot }
1541dedbd3bSFrançois Tigeot 
drm_connector_free_work_fn(struct work_struct * work)1553f2dd94aSFrançois Tigeot void drm_connector_free_work_fn(struct work_struct *work)
1563f2dd94aSFrançois Tigeot {
1573f2dd94aSFrançois Tigeot 	struct drm_connector *connector, *n;
1583f2dd94aSFrançois Tigeot 	struct drm_device *dev =
1593f2dd94aSFrançois Tigeot 		container_of(work, struct drm_device, mode_config.connector_free_work);
1603f2dd94aSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
1613f2dd94aSFrançois Tigeot 	unsigned long flags;
1623f2dd94aSFrançois Tigeot 	struct llist_node *freed;
1633f2dd94aSFrançois Tigeot 
1643f2dd94aSFrançois Tigeot 	spin_lock_irqsave(&config->connector_list_lock, flags);
1653f2dd94aSFrançois Tigeot 	freed = llist_del_all(&config->connector_free_list);
1663f2dd94aSFrançois Tigeot 	spin_unlock_irqrestore(&config->connector_list_lock, flags);
1673f2dd94aSFrançois Tigeot 
1683f2dd94aSFrançois Tigeot 	llist_for_each_entry_safe(connector, n, freed, free_node) {
1693f2dd94aSFrançois Tigeot 		drm_mode_object_unregister(dev, &connector->base);
1703f2dd94aSFrançois Tigeot 		connector->funcs->destroy(connector);
1713f2dd94aSFrançois Tigeot 	}
1723f2dd94aSFrançois Tigeot }
1733f2dd94aSFrançois Tigeot 
1741dedbd3bSFrançois Tigeot /**
1751dedbd3bSFrançois Tigeot  * drm_connector_init - Init a preallocated connector
1761dedbd3bSFrançois Tigeot  * @dev: DRM device
1771dedbd3bSFrançois Tigeot  * @connector: the connector to init
1781dedbd3bSFrançois Tigeot  * @funcs: callbacks for this connector
1791dedbd3bSFrançois Tigeot  * @connector_type: user visible type of the connector
1801dedbd3bSFrançois Tigeot  *
1811dedbd3bSFrançois Tigeot  * Initialises a preallocated connector. Connectors should be
1821dedbd3bSFrançois Tigeot  * subclassed as part of driver connector objects.
1831dedbd3bSFrançois Tigeot  *
1841dedbd3bSFrançois Tigeot  * Returns:
1851dedbd3bSFrançois Tigeot  * Zero on success, error code on failure.
1861dedbd3bSFrançois Tigeot  */
drm_connector_init(struct drm_device * dev,struct drm_connector * connector,const struct drm_connector_funcs * funcs,int connector_type)1871dedbd3bSFrançois Tigeot int drm_connector_init(struct drm_device *dev,
1881dedbd3bSFrançois Tigeot 		       struct drm_connector *connector,
1891dedbd3bSFrançois Tigeot 		       const struct drm_connector_funcs *funcs,
1901dedbd3bSFrançois Tigeot 		       int connector_type)
1911dedbd3bSFrançois Tigeot {
1921dedbd3bSFrançois Tigeot 	struct drm_mode_config *config = &dev->mode_config;
1931dedbd3bSFrançois Tigeot 	int ret;
1941dedbd3bSFrançois Tigeot 	struct ida *connector_ida =
1951dedbd3bSFrançois Tigeot 		&drm_connector_enum_list[connector_type].ida;
1961dedbd3bSFrançois Tigeot 
197a85cb24fSFrançois Tigeot 	ret = __drm_mode_object_add(dev, &connector->base,
1981dedbd3bSFrançois Tigeot 				    DRM_MODE_OBJECT_CONNECTOR,
1991dedbd3bSFrançois Tigeot 				    false, drm_connector_free);
2001dedbd3bSFrançois Tigeot 	if (ret)
201a85cb24fSFrançois Tigeot 		return ret;
2021dedbd3bSFrançois Tigeot 
2031dedbd3bSFrançois Tigeot 	connector->base.properties = &connector->properties;
2041dedbd3bSFrançois Tigeot 	connector->dev = dev;
2051dedbd3bSFrançois Tigeot 	connector->funcs = funcs;
2061dedbd3bSFrançois Tigeot 
2071dedbd3bSFrançois Tigeot 	ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL);
2081dedbd3bSFrançois Tigeot 	if (ret < 0)
2091dedbd3bSFrançois Tigeot 		goto out_put;
2101dedbd3bSFrançois Tigeot 	connector->index = ret;
2111dedbd3bSFrançois Tigeot 	ret = 0;
2121dedbd3bSFrançois Tigeot 
2131dedbd3bSFrançois Tigeot 	connector->connector_type = connector_type;
2141dedbd3bSFrançois Tigeot 	connector->connector_type_id =
2151dedbd3bSFrançois Tigeot 		ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
2161dedbd3bSFrançois Tigeot 	if (connector->connector_type_id < 0) {
2171dedbd3bSFrançois Tigeot 		ret = connector->connector_type_id;
2181dedbd3bSFrançois Tigeot 		goto out_put_id;
2191dedbd3bSFrançois Tigeot 	}
2201dedbd3bSFrançois Tigeot 	connector->name =
2211dedbd3bSFrançois Tigeot 		kasprintf(GFP_KERNEL, "%s-%d",
2221dedbd3bSFrançois Tigeot 			  drm_connector_enum_list[connector_type].name,
2231dedbd3bSFrançois Tigeot 			  connector->connector_type_id);
2241dedbd3bSFrançois Tigeot 	if (!connector->name) {
2251dedbd3bSFrançois Tigeot 		ret = -ENOMEM;
2261dedbd3bSFrançois Tigeot 		goto out_put_type_id;
2271dedbd3bSFrançois Tigeot 	}
2281dedbd3bSFrançois Tigeot 
2291dedbd3bSFrançois Tigeot 	INIT_LIST_HEAD(&connector->probed_modes);
2301dedbd3bSFrançois Tigeot 	INIT_LIST_HEAD(&connector->modes);
2314be47400SFrançois Tigeot 	lockinit(&connector->mutex, "drmcm", 0, LK_CANRECURSE);
2321dedbd3bSFrançois Tigeot 	connector->edid_blob_ptr = NULL;
2331dedbd3bSFrançois Tigeot 	connector->status = connector_status_unknown;
2341dedbd3bSFrançois Tigeot 
2351dedbd3bSFrançois Tigeot 	drm_connector_get_cmdline_mode(connector);
2361dedbd3bSFrançois Tigeot 
2371dedbd3bSFrançois Tigeot 	/* We should add connectors at the end to avoid upsetting the connector
2381dedbd3bSFrançois Tigeot 	 * index too much. */
239a85cb24fSFrançois Tigeot 	spin_lock_irq(&config->connector_list_lock);
2401dedbd3bSFrançois Tigeot 	list_add_tail(&connector->head, &config->connector_list);
2411dedbd3bSFrançois Tigeot 	config->num_connector++;
242a85cb24fSFrançois Tigeot 	spin_unlock_irq(&config->connector_list_lock);
2431dedbd3bSFrançois Tigeot 
2441dedbd3bSFrançois Tigeot 	if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
2451dedbd3bSFrançois Tigeot 		drm_object_attach_property(&connector->base,
2461dedbd3bSFrançois Tigeot 					      config->edid_property,
2471dedbd3bSFrançois Tigeot 					      0);
2481dedbd3bSFrançois Tigeot 
2491dedbd3bSFrançois Tigeot 	drm_object_attach_property(&connector->base,
2501dedbd3bSFrançois Tigeot 				      config->dpms_property, 0);
2511dedbd3bSFrançois Tigeot 
252a85cb24fSFrançois Tigeot 	drm_object_attach_property(&connector->base,
253a85cb24fSFrançois Tigeot 				   config->link_status_property,
254a85cb24fSFrançois Tigeot 				   0);
255a85cb24fSFrançois Tigeot 
2563f2dd94aSFrançois Tigeot 	drm_object_attach_property(&connector->base,
2573f2dd94aSFrançois Tigeot 				   config->non_desktop_property,
2583f2dd94aSFrançois Tigeot 				   0);
2593f2dd94aSFrançois Tigeot 
2601dedbd3bSFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
2611dedbd3bSFrançois Tigeot 		drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
2621dedbd3bSFrançois Tigeot 	}
2631dedbd3bSFrançois Tigeot 
2641dedbd3bSFrançois Tigeot 	connector->debugfs_entry = NULL;
2651dedbd3bSFrançois Tigeot out_put_type_id:
2661dedbd3bSFrançois Tigeot 	if (ret)
2671dedbd3bSFrançois Tigeot 		ida_simple_remove(connector_ida, connector->connector_type_id);
2681dedbd3bSFrançois Tigeot out_put_id:
2691dedbd3bSFrançois Tigeot 	if (ret)
2701dedbd3bSFrançois Tigeot 		ida_simple_remove(&config->connector_ida, connector->index);
2711dedbd3bSFrançois Tigeot out_put:
2721dedbd3bSFrançois Tigeot 	if (ret)
2731dedbd3bSFrançois Tigeot 		drm_mode_object_unregister(dev, &connector->base);
2741dedbd3bSFrançois Tigeot 
2751dedbd3bSFrançois Tigeot 	return ret;
2761dedbd3bSFrançois Tigeot }
2771dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_connector_init);
2781dedbd3bSFrançois Tigeot 
2791dedbd3bSFrançois Tigeot /**
2801dedbd3bSFrançois Tigeot  * drm_mode_connector_attach_encoder - attach a connector to an encoder
2811dedbd3bSFrançois Tigeot  * @connector: connector to attach
2821dedbd3bSFrançois Tigeot  * @encoder: encoder to attach @connector to
2831dedbd3bSFrançois Tigeot  *
2841dedbd3bSFrançois Tigeot  * This function links up a connector to an encoder. Note that the routing
2851dedbd3bSFrançois Tigeot  * restrictions between encoders and crtcs are exposed to userspace through the
2861dedbd3bSFrançois Tigeot  * possible_clones and possible_crtcs bitmasks.
2871dedbd3bSFrançois Tigeot  *
2881dedbd3bSFrançois Tigeot  * Returns:
2891dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
2901dedbd3bSFrançois Tigeot  */
drm_mode_connector_attach_encoder(struct drm_connector * connector,struct drm_encoder * encoder)2911dedbd3bSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector,
2921dedbd3bSFrançois Tigeot 				      struct drm_encoder *encoder)
2931dedbd3bSFrançois Tigeot {
2941dedbd3bSFrançois Tigeot 	int i;
2951dedbd3bSFrançois Tigeot 
2961dedbd3bSFrançois Tigeot 	/*
2971dedbd3bSFrançois Tigeot 	 * In the past, drivers have attempted to model the static association
2981dedbd3bSFrançois Tigeot 	 * of connector to encoder in simple connector/encoder devices using a
2991dedbd3bSFrançois Tigeot 	 * direct assignment of connector->encoder = encoder. This connection
3001dedbd3bSFrançois Tigeot 	 * is a logical one and the responsibility of the core, so drivers are
3011dedbd3bSFrançois Tigeot 	 * expected not to mess with this.
3021dedbd3bSFrançois Tigeot 	 *
3031dedbd3bSFrançois Tigeot 	 * Note that the error return should've been enough here, but a large
3041dedbd3bSFrançois Tigeot 	 * majority of drivers ignores the return value, so add in a big WARN
3051dedbd3bSFrançois Tigeot 	 * to get people's attention.
3061dedbd3bSFrançois Tigeot 	 */
3071dedbd3bSFrançois Tigeot 	if (WARN_ON(connector->encoder))
3081dedbd3bSFrançois Tigeot 		return -EINVAL;
3091dedbd3bSFrançois Tigeot 
3101dedbd3bSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
3111dedbd3bSFrançois Tigeot 		if (connector->encoder_ids[i] == 0) {
3121dedbd3bSFrançois Tigeot 			connector->encoder_ids[i] = encoder->base.id;
3131dedbd3bSFrançois Tigeot 			return 0;
3141dedbd3bSFrançois Tigeot 		}
3151dedbd3bSFrançois Tigeot 	}
3161dedbd3bSFrançois Tigeot 	return -ENOMEM;
3171dedbd3bSFrançois Tigeot }
3181dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
3191dedbd3bSFrançois Tigeot 
drm_mode_remove(struct drm_connector * connector,struct drm_display_mode * mode)3201dedbd3bSFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector,
3211dedbd3bSFrançois Tigeot 			    struct drm_display_mode *mode)
3221dedbd3bSFrançois Tigeot {
3231dedbd3bSFrançois Tigeot 	list_del(&mode->head);
3241dedbd3bSFrançois Tigeot 	drm_mode_destroy(connector->dev, mode);
3251dedbd3bSFrançois Tigeot }
3261dedbd3bSFrançois Tigeot 
3271dedbd3bSFrançois Tigeot /**
3281dedbd3bSFrançois Tigeot  * drm_connector_cleanup - cleans up an initialised connector
3291dedbd3bSFrançois Tigeot  * @connector: connector to cleanup
3301dedbd3bSFrançois Tigeot  *
3311dedbd3bSFrançois Tigeot  * Cleans up the connector but doesn't free the object.
3321dedbd3bSFrançois Tigeot  */
drm_connector_cleanup(struct drm_connector * connector)3331dedbd3bSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector)
3341dedbd3bSFrançois Tigeot {
3351dedbd3bSFrançois Tigeot 	struct drm_device *dev = connector->dev;
3361dedbd3bSFrançois Tigeot 	struct drm_display_mode *mode, *t;
3371dedbd3bSFrançois Tigeot 
3381dedbd3bSFrançois Tigeot 	/* The connector should have been removed from userspace long before
3391dedbd3bSFrançois Tigeot 	 * it is finally destroyed.
3401dedbd3bSFrançois Tigeot 	 */
3411dedbd3bSFrançois Tigeot 	if (WARN_ON(connector->registered))
3421dedbd3bSFrançois Tigeot 		drm_connector_unregister(connector);
3431dedbd3bSFrançois Tigeot 
3441dedbd3bSFrançois Tigeot 	if (connector->tile_group) {
3451dedbd3bSFrançois Tigeot 		drm_mode_put_tile_group(dev, connector->tile_group);
3461dedbd3bSFrançois Tigeot 		connector->tile_group = NULL;
3471dedbd3bSFrançois Tigeot 	}
3481dedbd3bSFrançois Tigeot 
3491dedbd3bSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
3501dedbd3bSFrançois Tigeot 		drm_mode_remove(connector, mode);
3511dedbd3bSFrançois Tigeot 
3521dedbd3bSFrançois Tigeot 	list_for_each_entry_safe(mode, t, &connector->modes, head)
3531dedbd3bSFrançois Tigeot 		drm_mode_remove(connector, mode);
3541dedbd3bSFrançois Tigeot 
3551dedbd3bSFrançois Tigeot 	ida_simple_remove(&drm_connector_enum_list[connector->connector_type].ida,
3561dedbd3bSFrançois Tigeot 			  connector->connector_type_id);
3571dedbd3bSFrançois Tigeot 
3581dedbd3bSFrançois Tigeot 	ida_simple_remove(&dev->mode_config.connector_ida,
3591dedbd3bSFrançois Tigeot 			  connector->index);
3601dedbd3bSFrançois Tigeot 
3611dedbd3bSFrançois Tigeot 	kfree(connector->display_info.bus_formats);
3621dedbd3bSFrançois Tigeot 	drm_mode_object_unregister(dev, &connector->base);
3631dedbd3bSFrançois Tigeot 	kfree(connector->name);
3641dedbd3bSFrançois Tigeot 	connector->name = NULL;
365a85cb24fSFrançois Tigeot 	spin_lock_irq(&dev->mode_config.connector_list_lock);
3661dedbd3bSFrançois Tigeot 	list_del(&connector->head);
3671dedbd3bSFrançois Tigeot 	dev->mode_config.num_connector--;
368a85cb24fSFrançois Tigeot 	spin_unlock_irq(&dev->mode_config.connector_list_lock);
3691dedbd3bSFrançois Tigeot 
3701dedbd3bSFrançois Tigeot 	WARN_ON(connector->state && !connector->funcs->atomic_destroy_state);
3711dedbd3bSFrançois Tigeot 	if (connector->state && connector->funcs->atomic_destroy_state)
3721dedbd3bSFrançois Tigeot 		connector->funcs->atomic_destroy_state(connector,
3731dedbd3bSFrançois Tigeot 						       connector->state);
3741dedbd3bSFrançois Tigeot 
3754be47400SFrançois Tigeot 	mutex_destroy(&connector->mutex);
3764be47400SFrançois Tigeot 
3771dedbd3bSFrançois Tigeot 	memset(connector, 0, sizeof(*connector));
3781dedbd3bSFrançois Tigeot }
3791dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup);
3801dedbd3bSFrançois Tigeot 
3811dedbd3bSFrançois Tigeot /**
3821dedbd3bSFrançois Tigeot  * drm_connector_register - register a connector
3831dedbd3bSFrançois Tigeot  * @connector: the connector to register
3841dedbd3bSFrançois Tigeot  *
3851dedbd3bSFrançois Tigeot  * Register userspace interfaces for a connector
3861dedbd3bSFrançois Tigeot  *
3871dedbd3bSFrançois Tigeot  * Returns:
3881dedbd3bSFrançois Tigeot  * Zero on success, error code on failure.
3891dedbd3bSFrançois Tigeot  */
drm_connector_register(struct drm_connector * connector)3901dedbd3bSFrançois Tigeot int drm_connector_register(struct drm_connector *connector)
3911dedbd3bSFrançois Tigeot {
3924be47400SFrançois Tigeot 	int ret = 0;
3931dedbd3bSFrançois Tigeot 
3944be47400SFrançois Tigeot 	if (!connector->dev->registered)
3951dedbd3bSFrançois Tigeot 		return 0;
3961dedbd3bSFrançois Tigeot 
3974be47400SFrançois Tigeot 	mutex_lock(&connector->mutex);
3984be47400SFrançois Tigeot 	if (connector->registered)
3994be47400SFrançois Tigeot 		goto unlock;
4004be47400SFrançois Tigeot 
4011dedbd3bSFrançois Tigeot 	ret = drm_sysfs_connector_add(connector);
4021dedbd3bSFrançois Tigeot 	if (ret)
4034be47400SFrançois Tigeot 		goto unlock;
4041dedbd3bSFrançois Tigeot 
4051dedbd3bSFrançois Tigeot 	ret = drm_debugfs_connector_add(connector);
4061dedbd3bSFrançois Tigeot 	if (ret) {
4071dedbd3bSFrançois Tigeot 		goto err_sysfs;
4081dedbd3bSFrançois Tigeot 	}
4091dedbd3bSFrançois Tigeot 
4101dedbd3bSFrançois Tigeot 	if (connector->funcs->late_register) {
4111dedbd3bSFrançois Tigeot 		ret = connector->funcs->late_register(connector);
4121dedbd3bSFrançois Tigeot 		if (ret)
4131dedbd3bSFrançois Tigeot 			goto err_debugfs;
4141dedbd3bSFrançois Tigeot 	}
4151dedbd3bSFrançois Tigeot 
4161dedbd3bSFrançois Tigeot 	drm_mode_object_register(connector->dev, &connector->base);
4171dedbd3bSFrançois Tigeot 
4181dedbd3bSFrançois Tigeot 	connector->registered = true;
4194be47400SFrançois Tigeot 	goto unlock;
4201dedbd3bSFrançois Tigeot 
4211dedbd3bSFrançois Tigeot err_debugfs:
4221dedbd3bSFrançois Tigeot 	drm_debugfs_connector_remove(connector);
4231dedbd3bSFrançois Tigeot err_sysfs:
4241dedbd3bSFrançois Tigeot 	drm_sysfs_connector_remove(connector);
4254be47400SFrançois Tigeot unlock:
4264be47400SFrançois Tigeot 	mutex_unlock(&connector->mutex);
4271dedbd3bSFrançois Tigeot 	return ret;
4281dedbd3bSFrançois Tigeot }
4291dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_connector_register);
4301dedbd3bSFrançois Tigeot 
4311dedbd3bSFrançois Tigeot /**
4321dedbd3bSFrançois Tigeot  * drm_connector_unregister - unregister a connector
4331dedbd3bSFrançois Tigeot  * @connector: the connector to unregister
4341dedbd3bSFrançois Tigeot  *
4351dedbd3bSFrançois Tigeot  * Unregister userspace interfaces for a connector
4361dedbd3bSFrançois Tigeot  */
drm_connector_unregister(struct drm_connector * connector)4371dedbd3bSFrançois Tigeot void drm_connector_unregister(struct drm_connector *connector)
4381dedbd3bSFrançois Tigeot {
4394be47400SFrançois Tigeot 	mutex_lock(&connector->mutex);
4404be47400SFrançois Tigeot 	if (!connector->registered) {
4414be47400SFrançois Tigeot 		mutex_unlock(&connector->mutex);
4421dedbd3bSFrançois Tigeot 		return;
4434be47400SFrançois Tigeot 	}
4441dedbd3bSFrançois Tigeot 
4451dedbd3bSFrançois Tigeot 	if (connector->funcs->early_unregister)
4461dedbd3bSFrançois Tigeot 		connector->funcs->early_unregister(connector);
4471dedbd3bSFrançois Tigeot 
4481dedbd3bSFrançois Tigeot 	drm_sysfs_connector_remove(connector);
4491dedbd3bSFrançois Tigeot 	drm_debugfs_connector_remove(connector);
4501dedbd3bSFrançois Tigeot 
4511dedbd3bSFrançois Tigeot 	connector->registered = false;
4524be47400SFrançois Tigeot 	mutex_unlock(&connector->mutex);
4531dedbd3bSFrançois Tigeot }
4541dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister);
4551dedbd3bSFrançois Tigeot 
drm_connector_unregister_all(struct drm_device * dev)4561dedbd3bSFrançois Tigeot void drm_connector_unregister_all(struct drm_device *dev)
4571dedbd3bSFrançois Tigeot {
4581dedbd3bSFrançois Tigeot 	struct drm_connector *connector;
459a85cb24fSFrançois Tigeot 	struct drm_connector_list_iter conn_iter;
4601dedbd3bSFrançois Tigeot 
461a85cb24fSFrançois Tigeot 	drm_connector_list_iter_begin(dev, &conn_iter);
462a85cb24fSFrançois Tigeot 	drm_for_each_connector_iter(connector, &conn_iter)
4631dedbd3bSFrançois Tigeot 		drm_connector_unregister(connector);
464a85cb24fSFrançois Tigeot 	drm_connector_list_iter_end(&conn_iter);
4651dedbd3bSFrançois Tigeot }
4661dedbd3bSFrançois Tigeot 
drm_connector_register_all(struct drm_device * dev)4671dedbd3bSFrançois Tigeot int drm_connector_register_all(struct drm_device *dev)
4681dedbd3bSFrançois Tigeot {
4691dedbd3bSFrançois Tigeot 	struct drm_connector *connector;
470a85cb24fSFrançois Tigeot 	struct drm_connector_list_iter conn_iter;
471a85cb24fSFrançois Tigeot 	int ret = 0;
4721dedbd3bSFrançois Tigeot 
473a85cb24fSFrançois Tigeot 	drm_connector_list_iter_begin(dev, &conn_iter);
474a85cb24fSFrançois Tigeot 	drm_for_each_connector_iter(connector, &conn_iter) {
4751dedbd3bSFrançois Tigeot 		ret = drm_connector_register(connector);
4761dedbd3bSFrançois Tigeot 		if (ret)
477a85cb24fSFrançois Tigeot 			break;
4781dedbd3bSFrançois Tigeot 	}
479a85cb24fSFrançois Tigeot 	drm_connector_list_iter_end(&conn_iter);
4801dedbd3bSFrançois Tigeot 
481a85cb24fSFrançois Tigeot 	if (ret)
4821dedbd3bSFrançois Tigeot 		drm_connector_unregister_all(dev);
4831dedbd3bSFrançois Tigeot 	return ret;
4841dedbd3bSFrançois Tigeot }
4851dedbd3bSFrançois Tigeot 
4861dedbd3bSFrançois Tigeot /**
4871dedbd3bSFrançois Tigeot  * drm_get_connector_status_name - return a string for connector status
4881dedbd3bSFrançois Tigeot  * @status: connector status to compute name of
4891dedbd3bSFrançois Tigeot  *
4901dedbd3bSFrançois Tigeot  * In contrast to the other drm_get_*_name functions this one here returns a
4911dedbd3bSFrançois Tigeot  * const pointer and hence is threadsafe.
4921dedbd3bSFrançois Tigeot  */
drm_get_connector_status_name(enum drm_connector_status status)4931dedbd3bSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status)
4941dedbd3bSFrançois Tigeot {
4951dedbd3bSFrançois Tigeot 	if (status == connector_status_connected)
4961dedbd3bSFrançois Tigeot 		return "connected";
4971dedbd3bSFrançois Tigeot 	else if (status == connector_status_disconnected)
4981dedbd3bSFrançois Tigeot 		return "disconnected";
4991dedbd3bSFrançois Tigeot 	else
5001dedbd3bSFrançois Tigeot 		return "unknown";
5011dedbd3bSFrançois Tigeot }
5021dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name);
5031dedbd3bSFrançois Tigeot 
504a85cb24fSFrançois Tigeot /**
505a85cb24fSFrançois Tigeot  * drm_get_connector_force_name - return a string for connector force
506a85cb24fSFrançois Tigeot  * @force: connector force to get name of
507a85cb24fSFrançois Tigeot  *
508a85cb24fSFrançois Tigeot  * Returns: const pointer to name.
509a85cb24fSFrançois Tigeot  */
drm_get_connector_force_name(enum drm_connector_force force)510a85cb24fSFrançois Tigeot const char *drm_get_connector_force_name(enum drm_connector_force force)
511a85cb24fSFrançois Tigeot {
512a85cb24fSFrançois Tigeot 	switch (force) {
513a85cb24fSFrançois Tigeot 	case DRM_FORCE_UNSPECIFIED:
514a85cb24fSFrançois Tigeot 		return "unspecified";
515a85cb24fSFrançois Tigeot 	case DRM_FORCE_OFF:
516a85cb24fSFrançois Tigeot 		return "off";
517a85cb24fSFrançois Tigeot 	case DRM_FORCE_ON:
518a85cb24fSFrançois Tigeot 		return "on";
519a85cb24fSFrançois Tigeot 	case DRM_FORCE_ON_DIGITAL:
520a85cb24fSFrançois Tigeot 		return "digital";
521a85cb24fSFrançois Tigeot 	default:
522a85cb24fSFrançois Tigeot 		return "unknown";
523a85cb24fSFrançois Tigeot 	}
524a85cb24fSFrançois Tigeot }
525a85cb24fSFrançois Tigeot 
526a85cb24fSFrançois Tigeot #ifdef CONFIG_LOCKDEP
527a85cb24fSFrançois Tigeot static struct lockdep_map connector_list_iter_dep_map = {
528a85cb24fSFrançois Tigeot 	.name = "drm_connector_list_iter"
529a85cb24fSFrançois Tigeot };
530a85cb24fSFrançois Tigeot #endif
531a85cb24fSFrançois Tigeot 
532a85cb24fSFrançois Tigeot /**
533a85cb24fSFrançois Tigeot  * drm_connector_list_iter_begin - initialize a connector_list iterator
534a85cb24fSFrançois Tigeot  * @dev: DRM device
535a85cb24fSFrançois Tigeot  * @iter: connector_list iterator
536a85cb24fSFrançois Tigeot  *
537a85cb24fSFrançois Tigeot  * Sets @iter up to walk the &drm_mode_config.connector_list of @dev. @iter
538a85cb24fSFrançois Tigeot  * must always be cleaned up again by calling drm_connector_list_iter_end().
539a85cb24fSFrançois Tigeot  * Iteration itself happens using drm_connector_list_iter_next() or
540a85cb24fSFrançois Tigeot  * drm_for_each_connector_iter().
541a85cb24fSFrançois Tigeot  */
drm_connector_list_iter_begin(struct drm_device * dev,struct drm_connector_list_iter * iter)542a85cb24fSFrançois Tigeot void drm_connector_list_iter_begin(struct drm_device *dev,
543a85cb24fSFrançois Tigeot 				   struct drm_connector_list_iter *iter)
544a85cb24fSFrançois Tigeot {
545a85cb24fSFrançois Tigeot 	iter->dev = dev;
546a85cb24fSFrançois Tigeot 	iter->conn = NULL;
547a85cb24fSFrançois Tigeot 	lock_acquire_shared_recursive(&connector_list_iter_dep_map, 0, 1, NULL, _RET_IP_);
548a85cb24fSFrançois Tigeot }
549a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_connector_list_iter_begin);
550a85cb24fSFrançois Tigeot 
5513f2dd94aSFrançois Tigeot /*
5523f2dd94aSFrançois Tigeot  * Extra-safe connector put function that works in any context. Should only be
5533f2dd94aSFrançois Tigeot  * used from the connector_iter functions, where we never really expect to
5543f2dd94aSFrançois Tigeot  * actually release the connector when dropping our final reference.
5553f2dd94aSFrançois Tigeot  */
5563f2dd94aSFrançois Tigeot static void
__drm_connector_put_safe(struct drm_connector * conn)5573f2dd94aSFrançois Tigeot __drm_connector_put_safe(struct drm_connector *conn)
5583f2dd94aSFrançois Tigeot {
5593f2dd94aSFrançois Tigeot 	struct drm_mode_config *config = &conn->dev->mode_config;
5603f2dd94aSFrançois Tigeot 
5613f2dd94aSFrançois Tigeot 	lockdep_assert_held(&config->connector_list_lock);
5623f2dd94aSFrançois Tigeot 
5633f2dd94aSFrançois Tigeot 	if (!refcount_dec_and_test(&conn->base.refcount.refcount))
5643f2dd94aSFrançois Tigeot 		return;
5653f2dd94aSFrançois Tigeot 
5663f2dd94aSFrançois Tigeot 	llist_add(&conn->free_node, &config->connector_free_list);
5673f2dd94aSFrançois Tigeot 	schedule_work(&config->connector_free_work);
5683f2dd94aSFrançois Tigeot }
5693f2dd94aSFrançois Tigeot 
570a85cb24fSFrançois Tigeot /**
571a85cb24fSFrançois Tigeot  * drm_connector_list_iter_next - return next connector
572a85cb24fSFrançois Tigeot  * @iter: connectr_list iterator
573a85cb24fSFrançois Tigeot  *
574a85cb24fSFrançois Tigeot  * Returns the next connector for @iter, or NULL when the list walk has
575a85cb24fSFrançois Tigeot  * completed.
576a85cb24fSFrançois Tigeot  */
577a85cb24fSFrançois Tigeot struct drm_connector *
drm_connector_list_iter_next(struct drm_connector_list_iter * iter)578a85cb24fSFrançois Tigeot drm_connector_list_iter_next(struct drm_connector_list_iter *iter)
579a85cb24fSFrançois Tigeot {
580a85cb24fSFrançois Tigeot 	struct drm_connector *old_conn = iter->conn;
581a85cb24fSFrançois Tigeot 	struct drm_mode_config *config = &iter->dev->mode_config;
582a85cb24fSFrançois Tigeot 	struct list_head *lhead;
583a85cb24fSFrançois Tigeot 	unsigned long flags;
584a85cb24fSFrançois Tigeot 
585a85cb24fSFrançois Tigeot 	spin_lock_irqsave(&config->connector_list_lock, flags);
586a85cb24fSFrançois Tigeot 	lhead = old_conn ? &old_conn->head : &config->connector_list;
587a85cb24fSFrançois Tigeot 
588a85cb24fSFrançois Tigeot 	do {
589a85cb24fSFrançois Tigeot 		if (lhead->next == &config->connector_list) {
590a85cb24fSFrançois Tigeot 			iter->conn = NULL;
591a85cb24fSFrançois Tigeot 			break;
592a85cb24fSFrançois Tigeot 		}
593a85cb24fSFrançois Tigeot 
594a85cb24fSFrançois Tigeot 		lhead = lhead->next;
595a85cb24fSFrançois Tigeot 		iter->conn = list_entry(lhead, struct drm_connector, head);
596a85cb24fSFrançois Tigeot 
597a85cb24fSFrançois Tigeot 		/* loop until it's not a zombie connector */
598a85cb24fSFrançois Tigeot 	} while (!kref_get_unless_zero(&iter->conn->base.refcount));
599a85cb24fSFrançois Tigeot 
600a85cb24fSFrançois Tigeot 	if (old_conn)
6013f2dd94aSFrançois Tigeot 		__drm_connector_put_safe(old_conn);
6023f2dd94aSFrançois Tigeot 	spin_unlock_irqrestore(&config->connector_list_lock, flags);
603a85cb24fSFrançois Tigeot 
604a85cb24fSFrançois Tigeot 	return iter->conn;
605a85cb24fSFrançois Tigeot }
606a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_connector_list_iter_next);
607a85cb24fSFrançois Tigeot 
608a85cb24fSFrançois Tigeot /**
609a85cb24fSFrançois Tigeot  * drm_connector_list_iter_end - tear down a connector_list iterator
610a85cb24fSFrançois Tigeot  * @iter: connector_list iterator
611a85cb24fSFrançois Tigeot  *
612a85cb24fSFrançois Tigeot  * Tears down @iter and releases any resources (like &drm_connector references)
613a85cb24fSFrançois Tigeot  * acquired while walking the list. This must always be called, both when the
614a85cb24fSFrançois Tigeot  * iteration completes fully or when it was aborted without walking the entire
615a85cb24fSFrançois Tigeot  * list.
616a85cb24fSFrançois Tigeot  */
drm_connector_list_iter_end(struct drm_connector_list_iter * iter)617a85cb24fSFrançois Tigeot void drm_connector_list_iter_end(struct drm_connector_list_iter *iter)
618a85cb24fSFrançois Tigeot {
6193f2dd94aSFrançois Tigeot 	struct drm_mode_config *config = &iter->dev->mode_config;
6203f2dd94aSFrançois Tigeot 	unsigned long flags;
6213f2dd94aSFrançois Tigeot 
622a85cb24fSFrançois Tigeot 	iter->dev = NULL;
6233f2dd94aSFrançois Tigeot 	if (iter->conn) {
6243f2dd94aSFrançois Tigeot 		spin_lock_irqsave(&config->connector_list_lock, flags);
6253f2dd94aSFrançois Tigeot 		__drm_connector_put_safe(iter->conn);
6263f2dd94aSFrançois Tigeot 		spin_unlock_irqrestore(&config->connector_list_lock, flags);
6273f2dd94aSFrançois Tigeot 	}
628a85cb24fSFrançois Tigeot 	lock_release(&connector_list_iter_dep_map, 0, _RET_IP_);
629a85cb24fSFrançois Tigeot }
630a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_connector_list_iter_end);
631a85cb24fSFrançois Tigeot 
6321dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
6331dedbd3bSFrançois Tigeot 	{ SubPixelUnknown, "Unknown" },
6341dedbd3bSFrançois Tigeot 	{ SubPixelHorizontalRGB, "Horizontal RGB" },
6351dedbd3bSFrançois Tigeot 	{ SubPixelHorizontalBGR, "Horizontal BGR" },
6361dedbd3bSFrançois Tigeot 	{ SubPixelVerticalRGB, "Vertical RGB" },
6371dedbd3bSFrançois Tigeot 	{ SubPixelVerticalBGR, "Vertical BGR" },
6381dedbd3bSFrançois Tigeot 	{ SubPixelNone, "None" },
6391dedbd3bSFrançois Tigeot };
6401dedbd3bSFrançois Tigeot 
6411dedbd3bSFrançois Tigeot /**
6421dedbd3bSFrançois Tigeot  * drm_get_subpixel_order_name - return a string for a given subpixel enum
6431dedbd3bSFrançois Tigeot  * @order: enum of subpixel_order
6441dedbd3bSFrançois Tigeot  *
6451dedbd3bSFrançois Tigeot  * Note you could abuse this and return something out of bounds, but that
6461dedbd3bSFrançois Tigeot  * would be a caller error.  No unscrubbed user data should make it here.
6471dedbd3bSFrançois Tigeot  */
drm_get_subpixel_order_name(enum subpixel_order order)6481dedbd3bSFrançois Tigeot const char *drm_get_subpixel_order_name(enum subpixel_order order)
6491dedbd3bSFrançois Tigeot {
6501dedbd3bSFrançois Tigeot 	return drm_subpixel_enum_list[order].name;
6511dedbd3bSFrançois Tigeot }
6521dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_get_subpixel_order_name);
6531dedbd3bSFrançois Tigeot 
6541dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] = {
6551dedbd3bSFrançois Tigeot 	{ DRM_MODE_DPMS_ON, "On" },
6561dedbd3bSFrançois Tigeot 	{ DRM_MODE_DPMS_STANDBY, "Standby" },
6571dedbd3bSFrançois Tigeot 	{ DRM_MODE_DPMS_SUSPEND, "Suspend" },
6581dedbd3bSFrançois Tigeot 	{ DRM_MODE_DPMS_OFF, "Off" }
6591dedbd3bSFrançois Tigeot };
6601dedbd3bSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
6611dedbd3bSFrançois Tigeot 
662a85cb24fSFrançois Tigeot static const struct drm_prop_enum_list drm_link_status_enum_list[] = {
663a85cb24fSFrançois Tigeot 	{ DRM_MODE_LINK_STATUS_GOOD, "Good" },
664a85cb24fSFrançois Tigeot 	{ DRM_MODE_LINK_STATUS_BAD, "Bad" },
665a85cb24fSFrançois Tigeot };
666a85cb24fSFrançois Tigeot 
6671dedbd3bSFrançois Tigeot /**
6681dedbd3bSFrançois Tigeot  * drm_display_info_set_bus_formats - set the supported bus formats
6691dedbd3bSFrançois Tigeot  * @info: display info to store bus formats in
6701dedbd3bSFrançois Tigeot  * @formats: array containing the supported bus formats
6711dedbd3bSFrançois Tigeot  * @num_formats: the number of entries in the fmts array
6721dedbd3bSFrançois Tigeot  *
6731dedbd3bSFrançois Tigeot  * Store the supported bus formats in display info structure.
6741dedbd3bSFrançois Tigeot  * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
6751dedbd3bSFrançois Tigeot  * a full list of available formats.
6761dedbd3bSFrançois Tigeot  */
drm_display_info_set_bus_formats(struct drm_display_info * info,const u32 * formats,unsigned int num_formats)6771dedbd3bSFrançois Tigeot int drm_display_info_set_bus_formats(struct drm_display_info *info,
6781dedbd3bSFrançois Tigeot 				     const u32 *formats,
6791dedbd3bSFrançois Tigeot 				     unsigned int num_formats)
6801dedbd3bSFrançois Tigeot {
6811dedbd3bSFrançois Tigeot 	u32 *fmts = NULL;
6821dedbd3bSFrançois Tigeot 
6831dedbd3bSFrançois Tigeot 	if (!formats && num_formats)
6841dedbd3bSFrançois Tigeot 		return -EINVAL;
6851dedbd3bSFrançois Tigeot 
6861dedbd3bSFrançois Tigeot 	if (formats && num_formats) {
6871dedbd3bSFrançois Tigeot 		fmts = kmemdup(formats, sizeof(*formats) * num_formats,
6881dedbd3bSFrançois Tigeot 			       GFP_KERNEL);
6891dedbd3bSFrançois Tigeot 		if (!fmts)
6901dedbd3bSFrançois Tigeot 			return -ENOMEM;
6911dedbd3bSFrançois Tigeot 	}
6921dedbd3bSFrançois Tigeot 
6931dedbd3bSFrançois Tigeot 	kfree(info->bus_formats);
6941dedbd3bSFrançois Tigeot 	info->bus_formats = fmts;
6951dedbd3bSFrançois Tigeot 	info->num_bus_formats = num_formats;
6961dedbd3bSFrançois Tigeot 
6971dedbd3bSFrançois Tigeot 	return 0;
6981dedbd3bSFrançois Tigeot }
6991dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_display_info_set_bus_formats);
7001dedbd3bSFrançois Tigeot 
7011dedbd3bSFrançois Tigeot /* Optional connector properties. */
7021dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = {
7031dedbd3bSFrançois Tigeot 	{ DRM_MODE_SCALE_NONE, "None" },
7041dedbd3bSFrançois Tigeot 	{ DRM_MODE_SCALE_FULLSCREEN, "Full" },
7051dedbd3bSFrançois Tigeot 	{ DRM_MODE_SCALE_CENTER, "Center" },
7061dedbd3bSFrançois Tigeot 	{ DRM_MODE_SCALE_ASPECT, "Full aspect" },
7071dedbd3bSFrançois Tigeot };
7081dedbd3bSFrançois Tigeot 
7091dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
7101dedbd3bSFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
7111dedbd3bSFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
7121dedbd3bSFrançois Tigeot 	{ DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
7131dedbd3bSFrançois Tigeot };
7141dedbd3bSFrançois Tigeot 
7151dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
7161dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
7171dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
7181dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
7191dedbd3bSFrançois Tigeot };
7201dedbd3bSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
7211dedbd3bSFrançois Tigeot 
7221dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
7231dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
7241dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
7251dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
7261dedbd3bSFrançois Tigeot };
7271dedbd3bSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
7281dedbd3bSFrançois Tigeot 		 drm_dvi_i_subconnector_enum_list)
7291dedbd3bSFrançois Tigeot 
7301dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
7311dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
7321dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
7331dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
7341dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
7351dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
7361dedbd3bSFrançois Tigeot };
7371dedbd3bSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
7381dedbd3bSFrançois Tigeot 
7391dedbd3bSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
7401dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
7411dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
7421dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
7431dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
7441dedbd3bSFrançois Tigeot 	{ DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
7451dedbd3bSFrançois Tigeot };
DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,drm_tv_subconnector_enum_list)7461dedbd3bSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
7471dedbd3bSFrançois Tigeot 		 drm_tv_subconnector_enum_list)
7481dedbd3bSFrançois Tigeot 
7494be47400SFrançois Tigeot /**
7504be47400SFrançois Tigeot  * DOC: standard connector properties
7514be47400SFrançois Tigeot  *
7524be47400SFrançois Tigeot  * DRM connectors have a few standardized properties:
7534be47400SFrançois Tigeot  *
7544be47400SFrançois Tigeot  * EDID:
7554be47400SFrançois Tigeot  * 	Blob property which contains the current EDID read from the sink. This
7564be47400SFrançois Tigeot  * 	is useful to parse sink identification information like vendor, model
7574be47400SFrançois Tigeot  * 	and serial. Drivers should update this property by calling
7584be47400SFrançois Tigeot  * 	drm_mode_connector_update_edid_property(), usually after having parsed
7594be47400SFrançois Tigeot  * 	the EDID using drm_add_edid_modes(). Userspace cannot change this
7604be47400SFrançois Tigeot  * 	property.
7614be47400SFrançois Tigeot  * DPMS:
7624be47400SFrançois Tigeot  * 	Legacy property for setting the power state of the connector. For atomic
7634be47400SFrançois Tigeot  * 	drivers this is only provided for backwards compatibility with existing
7644be47400SFrançois Tigeot  * 	drivers, it remaps to controlling the "ACTIVE" property on the CRTC the
7654be47400SFrançois Tigeot  * 	connector is linked to. Drivers should never set this property directly,
766a85cb24fSFrançois Tigeot  * 	it is handled by the DRM core by calling the &drm_connector_funcs.dpms
7673f2dd94aSFrançois Tigeot  * 	callback. For atomic drivers the remapping to the "ACTIVE" property is
7683f2dd94aSFrançois Tigeot  * 	implemented in the DRM core.  This is the only standard connector
7693f2dd94aSFrançois Tigeot  * 	property that userspace can change.
7703f2dd94aSFrançois Tigeot  *
7713f2dd94aSFrançois Tigeot  * 	Note that this property cannot be set through the MODE_ATOMIC ioctl,
7723f2dd94aSFrançois Tigeot  * 	userspace must use "ACTIVE" on the CRTC instead.
7733f2dd94aSFrançois Tigeot  *
7743f2dd94aSFrançois Tigeot  * 	WARNING:
7753f2dd94aSFrançois Tigeot  *
7763f2dd94aSFrançois Tigeot  * 	For userspace also running on legacy drivers the "DPMS" semantics are a
7773f2dd94aSFrançois Tigeot  * 	lot more complicated. First, userspace cannot rely on the "DPMS" value
7783f2dd94aSFrançois Tigeot  * 	returned by the GETCONNECTOR actually reflecting reality, because many
7793f2dd94aSFrançois Tigeot  * 	drivers fail to update it. For atomic drivers this is taken care of in
7803f2dd94aSFrançois Tigeot  * 	drm_atomic_helper_update_legacy_modeset_state().
7813f2dd94aSFrançois Tigeot  *
7823f2dd94aSFrançois Tigeot  * 	The second issue is that the DPMS state is only well-defined when the
7833f2dd94aSFrançois Tigeot  * 	connector is connected to a CRTC. In atomic the DRM core enforces that
7843f2dd94aSFrançois Tigeot  * 	"ACTIVE" is off in such a case, no such checks exists for "DPMS".
7853f2dd94aSFrançois Tigeot  *
7863f2dd94aSFrançois Tigeot  * 	Finally, when enabling an output using the legacy SETCONFIG ioctl then
7873f2dd94aSFrançois Tigeot  * 	"DPMS" is forced to ON. But see above, that might not be reflected in
7883f2dd94aSFrançois Tigeot  * 	the software value on legacy drivers.
7893f2dd94aSFrançois Tigeot  *
7903f2dd94aSFrançois Tigeot  * 	Summarizing: Only set "DPMS" when the connector is known to be enabled,
7913f2dd94aSFrançois Tigeot  * 	assume that a successful SETCONFIG call also sets "DPMS" to on, and
7923f2dd94aSFrançois Tigeot  * 	never read back the value of "DPMS" because it can be incorrect.
7934be47400SFrançois Tigeot  * PATH:
7944be47400SFrançois Tigeot  * 	Connector path property to identify how this sink is physically
7954be47400SFrançois Tigeot  * 	connected. Used by DP MST. This should be set by calling
7964be47400SFrançois Tigeot  * 	drm_mode_connector_set_path_property(), in the case of DP MST with the
7974be47400SFrançois Tigeot  * 	path property the MST manager created. Userspace cannot change this
7984be47400SFrançois Tigeot  * 	property.
7994be47400SFrançois Tigeot  * TILE:
8004be47400SFrançois Tigeot  * 	Connector tile group property to indicate how a set of DRM connector
8014be47400SFrançois Tigeot  * 	compose together into one logical screen. This is used by both high-res
8024be47400SFrançois Tigeot  * 	external screens (often only using a single cable, but exposing multiple
8034be47400SFrançois Tigeot  * 	DP MST sinks), or high-res integrated panels (like dual-link DSI) which
8044be47400SFrançois Tigeot  * 	are not gen-locked. Note that for tiled panels which are genlocked, like
8054be47400SFrançois Tigeot  * 	dual-link LVDS or dual-link DSI, the driver should try to not expose the
8064be47400SFrançois Tigeot  * 	tiling and virtualize both &drm_crtc and &drm_plane if needed. Drivers
8074be47400SFrançois Tigeot  * 	should update this value using drm_mode_connector_set_tile_property().
8084be47400SFrançois Tigeot  * 	Userspace cannot change this property.
809a85cb24fSFrançois Tigeot  * link-status:
810a85cb24fSFrançois Tigeot  *      Connector link-status property to indicate the status of link. The default
811a85cb24fSFrançois Tigeot  *      value of link-status is "GOOD". If something fails during or after modeset,
812a85cb24fSFrançois Tigeot  *      the kernel driver may set this to "BAD" and issue a hotplug uevent. Drivers
813a85cb24fSFrançois Tigeot  *      should update this value using drm_mode_connector_set_link_status_property().
8143f2dd94aSFrançois Tigeot  * non_desktop:
8153f2dd94aSFrançois Tigeot  * 	Indicates the output should be ignored for purposes of displaying a
8163f2dd94aSFrançois Tigeot  * 	standard desktop environment or console. This is most likely because
8173f2dd94aSFrançois Tigeot  * 	the output device is not rectilinear.
8184be47400SFrançois Tigeot  *
8194be47400SFrançois Tigeot  * Connectors also have one standardized atomic property:
8204be47400SFrançois Tigeot  *
8214be47400SFrançois Tigeot  * CRTC_ID:
8224be47400SFrançois Tigeot  * 	Mode object ID of the &drm_crtc this connector should be connected to.
8234be47400SFrançois Tigeot  */
8244be47400SFrançois Tigeot 
8251dedbd3bSFrançois Tigeot int drm_connector_create_standard_properties(struct drm_device *dev)
8261dedbd3bSFrançois Tigeot {
8271dedbd3bSFrançois Tigeot 	struct drm_property *prop;
8281dedbd3bSFrançois Tigeot 
8291dedbd3bSFrançois Tigeot 	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
8301dedbd3bSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
8311dedbd3bSFrançois Tigeot 				   "EDID", 0);
8321dedbd3bSFrançois Tigeot 	if (!prop)
8331dedbd3bSFrançois Tigeot 		return -ENOMEM;
8341dedbd3bSFrançois Tigeot 	dev->mode_config.edid_property = prop;
8351dedbd3bSFrançois Tigeot 
8361dedbd3bSFrançois Tigeot 	prop = drm_property_create_enum(dev, 0,
8371dedbd3bSFrançois Tigeot 				   "DPMS", drm_dpms_enum_list,
8381dedbd3bSFrançois Tigeot 				   ARRAY_SIZE(drm_dpms_enum_list));
8391dedbd3bSFrançois Tigeot 	if (!prop)
8401dedbd3bSFrançois Tigeot 		return -ENOMEM;
8411dedbd3bSFrançois Tigeot 	dev->mode_config.dpms_property = prop;
8421dedbd3bSFrançois Tigeot 
8431dedbd3bSFrançois Tigeot 	prop = drm_property_create(dev,
8441dedbd3bSFrançois Tigeot 				   DRM_MODE_PROP_BLOB |
8451dedbd3bSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
8461dedbd3bSFrançois Tigeot 				   "PATH", 0);
8471dedbd3bSFrançois Tigeot 	if (!prop)
8481dedbd3bSFrançois Tigeot 		return -ENOMEM;
8491dedbd3bSFrançois Tigeot 	dev->mode_config.path_property = prop;
8501dedbd3bSFrançois Tigeot 
8511dedbd3bSFrançois Tigeot 	prop = drm_property_create(dev,
8521dedbd3bSFrançois Tigeot 				   DRM_MODE_PROP_BLOB |
8531dedbd3bSFrançois Tigeot 				   DRM_MODE_PROP_IMMUTABLE,
8541dedbd3bSFrançois Tigeot 				   "TILE", 0);
8551dedbd3bSFrançois Tigeot 	if (!prop)
8561dedbd3bSFrançois Tigeot 		return -ENOMEM;
8571dedbd3bSFrançois Tigeot 	dev->mode_config.tile_property = prop;
8581dedbd3bSFrançois Tigeot 
859a85cb24fSFrançois Tigeot 	prop = drm_property_create_enum(dev, 0, "link-status",
860a85cb24fSFrançois Tigeot 					drm_link_status_enum_list,
861a85cb24fSFrançois Tigeot 					ARRAY_SIZE(drm_link_status_enum_list));
862a85cb24fSFrançois Tigeot 	if (!prop)
863a85cb24fSFrançois Tigeot 		return -ENOMEM;
864a85cb24fSFrançois Tigeot 	dev->mode_config.link_status_property = prop;
865a85cb24fSFrançois Tigeot 
8663f2dd94aSFrançois Tigeot 	prop = drm_property_create_bool(dev, DRM_MODE_PROP_IMMUTABLE, "non-desktop");
8673f2dd94aSFrançois Tigeot 	if (!prop)
8683f2dd94aSFrançois Tigeot 		return -ENOMEM;
8693f2dd94aSFrançois Tigeot 	dev->mode_config.non_desktop_property = prop;
8703f2dd94aSFrançois Tigeot 
8711dedbd3bSFrançois Tigeot 	return 0;
8721dedbd3bSFrançois Tigeot }
8731dedbd3bSFrançois Tigeot 
8741dedbd3bSFrançois Tigeot /**
8751dedbd3bSFrançois Tigeot  * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
8761dedbd3bSFrançois Tigeot  * @dev: DRM device
8771dedbd3bSFrançois Tigeot  *
8781dedbd3bSFrançois Tigeot  * Called by a driver the first time a DVI-I connector is made.
8791dedbd3bSFrançois Tigeot  */
drm_mode_create_dvi_i_properties(struct drm_device * dev)8801dedbd3bSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev)
8811dedbd3bSFrançois Tigeot {
8821dedbd3bSFrançois Tigeot 	struct drm_property *dvi_i_selector;
8831dedbd3bSFrançois Tigeot 	struct drm_property *dvi_i_subconnector;
8841dedbd3bSFrançois Tigeot 
8851dedbd3bSFrançois Tigeot 	if (dev->mode_config.dvi_i_select_subconnector_property)
8861dedbd3bSFrançois Tigeot 		return 0;
8871dedbd3bSFrançois Tigeot 
8881dedbd3bSFrançois Tigeot 	dvi_i_selector =
8891dedbd3bSFrançois Tigeot 		drm_property_create_enum(dev, 0,
8901dedbd3bSFrançois Tigeot 				    "select subconnector",
8911dedbd3bSFrançois Tigeot 				    drm_dvi_i_select_enum_list,
8921dedbd3bSFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_select_enum_list));
8931dedbd3bSFrançois Tigeot 	dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
8941dedbd3bSFrançois Tigeot 
8951dedbd3bSFrançois Tigeot 	dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
8961dedbd3bSFrançois Tigeot 				    "subconnector",
8971dedbd3bSFrançois Tigeot 				    drm_dvi_i_subconnector_enum_list,
8981dedbd3bSFrançois Tigeot 				    ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
8991dedbd3bSFrançois Tigeot 	dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
9001dedbd3bSFrançois Tigeot 
9011dedbd3bSFrançois Tigeot 	return 0;
9021dedbd3bSFrançois Tigeot }
9031dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
9041dedbd3bSFrançois Tigeot 
9051dedbd3bSFrançois Tigeot /**
9061dedbd3bSFrançois Tigeot  * drm_create_tv_properties - create TV specific connector properties
9071dedbd3bSFrançois Tigeot  * @dev: DRM device
9081dedbd3bSFrançois Tigeot  * @num_modes: number of different TV formats (modes) supported
9091dedbd3bSFrançois Tigeot  * @modes: array of pointers to strings containing name of each format
9101dedbd3bSFrançois Tigeot  *
9111dedbd3bSFrançois Tigeot  * Called by a driver's TV initialization routine, this function creates
9121dedbd3bSFrançois Tigeot  * the TV specific connector properties for a given device.  Caller is
9131dedbd3bSFrançois Tigeot  * responsible for allocating a list of format names and passing them to
9141dedbd3bSFrançois Tigeot  * this routine.
9151dedbd3bSFrançois Tigeot  */
drm_mode_create_tv_properties(struct drm_device * dev,unsigned int num_modes,const char * const modes[])9161dedbd3bSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev,
9171dedbd3bSFrançois Tigeot 				  unsigned int num_modes,
9181dedbd3bSFrançois Tigeot 				  const char * const modes[])
9191dedbd3bSFrançois Tigeot {
9201dedbd3bSFrançois Tigeot 	struct drm_property *tv_selector;
9211dedbd3bSFrançois Tigeot 	struct drm_property *tv_subconnector;
9221dedbd3bSFrançois Tigeot 	unsigned int i;
9231dedbd3bSFrançois Tigeot 
9241dedbd3bSFrançois Tigeot 	if (dev->mode_config.tv_select_subconnector_property)
9251dedbd3bSFrançois Tigeot 		return 0;
9261dedbd3bSFrançois Tigeot 
9271dedbd3bSFrançois Tigeot 	/*
9281dedbd3bSFrançois Tigeot 	 * Basic connector properties
9291dedbd3bSFrançois Tigeot 	 */
9301dedbd3bSFrançois Tigeot 	tv_selector = drm_property_create_enum(dev, 0,
9311dedbd3bSFrançois Tigeot 					  "select subconnector",
9321dedbd3bSFrançois Tigeot 					  drm_tv_select_enum_list,
9331dedbd3bSFrançois Tigeot 					  ARRAY_SIZE(drm_tv_select_enum_list));
9341dedbd3bSFrançois Tigeot 	if (!tv_selector)
9351dedbd3bSFrançois Tigeot 		goto nomem;
9361dedbd3bSFrançois Tigeot 
9371dedbd3bSFrançois Tigeot 	dev->mode_config.tv_select_subconnector_property = tv_selector;
9381dedbd3bSFrançois Tigeot 
9391dedbd3bSFrançois Tigeot 	tv_subconnector =
9401dedbd3bSFrançois Tigeot 		drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
9411dedbd3bSFrançois Tigeot 				    "subconnector",
9421dedbd3bSFrançois Tigeot 				    drm_tv_subconnector_enum_list,
9431dedbd3bSFrançois Tigeot 				    ARRAY_SIZE(drm_tv_subconnector_enum_list));
9441dedbd3bSFrançois Tigeot 	if (!tv_subconnector)
9451dedbd3bSFrançois Tigeot 		goto nomem;
9461dedbd3bSFrançois Tigeot 	dev->mode_config.tv_subconnector_property = tv_subconnector;
9471dedbd3bSFrançois Tigeot 
9481dedbd3bSFrançois Tigeot 	/*
9491dedbd3bSFrançois Tigeot 	 * Other, TV specific properties: margins & TV modes.
9501dedbd3bSFrançois Tigeot 	 */
9511dedbd3bSFrançois Tigeot 	dev->mode_config.tv_left_margin_property =
9521dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "left margin", 0, 100);
9531dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_left_margin_property)
9541dedbd3bSFrançois Tigeot 		goto nomem;
9551dedbd3bSFrançois Tigeot 
9561dedbd3bSFrançois Tigeot 	dev->mode_config.tv_right_margin_property =
9571dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "right margin", 0, 100);
9581dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_right_margin_property)
9591dedbd3bSFrançois Tigeot 		goto nomem;
9601dedbd3bSFrançois Tigeot 
9611dedbd3bSFrançois Tigeot 	dev->mode_config.tv_top_margin_property =
9621dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "top margin", 0, 100);
9631dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_top_margin_property)
9641dedbd3bSFrançois Tigeot 		goto nomem;
9651dedbd3bSFrançois Tigeot 
9661dedbd3bSFrançois Tigeot 	dev->mode_config.tv_bottom_margin_property =
9671dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "bottom margin", 0, 100);
9681dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_bottom_margin_property)
9691dedbd3bSFrançois Tigeot 		goto nomem;
9701dedbd3bSFrançois Tigeot 
9711dedbd3bSFrançois Tigeot 	dev->mode_config.tv_mode_property =
9721dedbd3bSFrançois Tigeot 		drm_property_create(dev, DRM_MODE_PROP_ENUM,
9731dedbd3bSFrançois Tigeot 				    "mode", num_modes);
9741dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_mode_property)
9751dedbd3bSFrançois Tigeot 		goto nomem;
9761dedbd3bSFrançois Tigeot 
9771dedbd3bSFrançois Tigeot 	for (i = 0; i < num_modes; i++)
9781dedbd3bSFrançois Tigeot 		drm_property_add_enum(dev->mode_config.tv_mode_property, i,
9791dedbd3bSFrançois Tigeot 				      i, modes[i]);
9801dedbd3bSFrançois Tigeot 
9811dedbd3bSFrançois Tigeot 	dev->mode_config.tv_brightness_property =
9821dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "brightness", 0, 100);
9831dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_brightness_property)
9841dedbd3bSFrançois Tigeot 		goto nomem;
9851dedbd3bSFrançois Tigeot 
9861dedbd3bSFrançois Tigeot 	dev->mode_config.tv_contrast_property =
9871dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "contrast", 0, 100);
9881dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_contrast_property)
9891dedbd3bSFrançois Tigeot 		goto nomem;
9901dedbd3bSFrançois Tigeot 
9911dedbd3bSFrançois Tigeot 	dev->mode_config.tv_flicker_reduction_property =
9921dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
9931dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_flicker_reduction_property)
9941dedbd3bSFrançois Tigeot 		goto nomem;
9951dedbd3bSFrançois Tigeot 
9961dedbd3bSFrançois Tigeot 	dev->mode_config.tv_overscan_property =
9971dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "overscan", 0, 100);
9981dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_overscan_property)
9991dedbd3bSFrançois Tigeot 		goto nomem;
10001dedbd3bSFrançois Tigeot 
10011dedbd3bSFrançois Tigeot 	dev->mode_config.tv_saturation_property =
10021dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "saturation", 0, 100);
10031dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_saturation_property)
10041dedbd3bSFrançois Tigeot 		goto nomem;
10051dedbd3bSFrançois Tigeot 
10061dedbd3bSFrançois Tigeot 	dev->mode_config.tv_hue_property =
10071dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, 0, "hue", 0, 100);
10081dedbd3bSFrançois Tigeot 	if (!dev->mode_config.tv_hue_property)
10091dedbd3bSFrançois Tigeot 		goto nomem;
10101dedbd3bSFrançois Tigeot 
10111dedbd3bSFrançois Tigeot 	return 0;
10121dedbd3bSFrançois Tigeot nomem:
10131dedbd3bSFrançois Tigeot 	return -ENOMEM;
10141dedbd3bSFrançois Tigeot }
10151dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties);
10161dedbd3bSFrançois Tigeot 
10171dedbd3bSFrançois Tigeot /**
10181dedbd3bSFrançois Tigeot  * drm_mode_create_scaling_mode_property - create scaling mode property
10191dedbd3bSFrançois Tigeot  * @dev: DRM device
10201dedbd3bSFrançois Tigeot  *
10211dedbd3bSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
10221dedbd3bSFrançois Tigeot  * connectors.
10233f2dd94aSFrançois Tigeot  *
10243f2dd94aSFrançois Tigeot  * Atomic drivers should use drm_connector_attach_scaling_mode_property()
10253f2dd94aSFrançois Tigeot  * instead to correctly assign &drm_connector_state.picture_aspect_ratio
10263f2dd94aSFrançois Tigeot  * in the atomic state.
10271dedbd3bSFrançois Tigeot  */
drm_mode_create_scaling_mode_property(struct drm_device * dev)10281dedbd3bSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev)
10291dedbd3bSFrançois Tigeot {
10301dedbd3bSFrançois Tigeot 	struct drm_property *scaling_mode;
10311dedbd3bSFrançois Tigeot 
10321dedbd3bSFrançois Tigeot 	if (dev->mode_config.scaling_mode_property)
10331dedbd3bSFrançois Tigeot 		return 0;
10341dedbd3bSFrançois Tigeot 
10351dedbd3bSFrançois Tigeot 	scaling_mode =
10361dedbd3bSFrançois Tigeot 		drm_property_create_enum(dev, 0, "scaling mode",
10371dedbd3bSFrançois Tigeot 				drm_scaling_mode_enum_list,
10381dedbd3bSFrançois Tigeot 				    ARRAY_SIZE(drm_scaling_mode_enum_list));
10391dedbd3bSFrançois Tigeot 
10401dedbd3bSFrançois Tigeot 	dev->mode_config.scaling_mode_property = scaling_mode;
10411dedbd3bSFrançois Tigeot 
10421dedbd3bSFrançois Tigeot 	return 0;
10431dedbd3bSFrançois Tigeot }
10441dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
10451dedbd3bSFrançois Tigeot 
10461dedbd3bSFrançois Tigeot /**
10473f2dd94aSFrançois Tigeot  * drm_connector_attach_scaling_mode_property - attach atomic scaling mode property
10483f2dd94aSFrançois Tigeot  * @connector: connector to attach scaling mode property on.
10493f2dd94aSFrançois Tigeot  * @scaling_mode_mask: or'ed mask of BIT(%DRM_MODE_SCALE_\*).
10503f2dd94aSFrançois Tigeot  *
10513f2dd94aSFrançois Tigeot  * This is used to add support for scaling mode to atomic drivers.
10523f2dd94aSFrançois Tigeot  * The scaling mode will be set to &drm_connector_state.picture_aspect_ratio
10533f2dd94aSFrançois Tigeot  * and can be used from &drm_connector_helper_funcs->atomic_check for validation.
10543f2dd94aSFrançois Tigeot  *
10553f2dd94aSFrançois Tigeot  * This is the atomic version of drm_mode_create_scaling_mode_property().
10563f2dd94aSFrançois Tigeot  *
10573f2dd94aSFrançois Tigeot  * Returns:
10583f2dd94aSFrançois Tigeot  * Zero on success, negative errno on failure.
10593f2dd94aSFrançois Tigeot  */
drm_connector_attach_scaling_mode_property(struct drm_connector * connector,u32 scaling_mode_mask)10603f2dd94aSFrançois Tigeot int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
10613f2dd94aSFrançois Tigeot 					       u32 scaling_mode_mask)
10623f2dd94aSFrançois Tigeot {
10633f2dd94aSFrançois Tigeot 	struct drm_device *dev = connector->dev;
10643f2dd94aSFrançois Tigeot 	struct drm_property *scaling_mode_property;
10653f2dd94aSFrançois Tigeot 	int i, j = 0;
10663f2dd94aSFrançois Tigeot 	const unsigned valid_scaling_mode_mask =
10673f2dd94aSFrançois Tigeot 		(1U << ARRAY_SIZE(drm_scaling_mode_enum_list)) - 1;
10683f2dd94aSFrançois Tigeot 
10693f2dd94aSFrançois Tigeot 	if (WARN_ON(hweight32(scaling_mode_mask) < 2 ||
10703f2dd94aSFrançois Tigeot 		    scaling_mode_mask & ~valid_scaling_mode_mask))
10713f2dd94aSFrançois Tigeot 		return -EINVAL;
10723f2dd94aSFrançois Tigeot 
10733f2dd94aSFrançois Tigeot 	scaling_mode_property =
10743f2dd94aSFrançois Tigeot 		drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
10753f2dd94aSFrançois Tigeot 				    hweight32(scaling_mode_mask));
10763f2dd94aSFrançois Tigeot 
10773f2dd94aSFrançois Tigeot 	if (!scaling_mode_property)
10783f2dd94aSFrançois Tigeot 		return -ENOMEM;
10793f2dd94aSFrançois Tigeot 
10803f2dd94aSFrançois Tigeot 	for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) {
10813f2dd94aSFrançois Tigeot 		int ret;
10823f2dd94aSFrançois Tigeot 
10833f2dd94aSFrançois Tigeot 		if (!(BIT(i) & scaling_mode_mask))
10843f2dd94aSFrançois Tigeot 			continue;
10853f2dd94aSFrançois Tigeot 
10863f2dd94aSFrançois Tigeot 		ret = drm_property_add_enum(scaling_mode_property, j++,
10873f2dd94aSFrançois Tigeot 					    drm_scaling_mode_enum_list[i].type,
10883f2dd94aSFrançois Tigeot 					    drm_scaling_mode_enum_list[i].name);
10893f2dd94aSFrançois Tigeot 
10903f2dd94aSFrançois Tigeot 		if (ret) {
10913f2dd94aSFrançois Tigeot 			drm_property_destroy(dev, scaling_mode_property);
10923f2dd94aSFrançois Tigeot 
10933f2dd94aSFrançois Tigeot 			return ret;
10943f2dd94aSFrançois Tigeot 		}
10953f2dd94aSFrançois Tigeot 	}
10963f2dd94aSFrançois Tigeot 
10973f2dd94aSFrançois Tigeot 	drm_object_attach_property(&connector->base,
10983f2dd94aSFrançois Tigeot 				   scaling_mode_property, 0);
10993f2dd94aSFrançois Tigeot 
11003f2dd94aSFrançois Tigeot 	connector->scaling_mode_property = scaling_mode_property;
11013f2dd94aSFrançois Tigeot 
11023f2dd94aSFrançois Tigeot 	return 0;
11033f2dd94aSFrançois Tigeot }
11043f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
11053f2dd94aSFrançois Tigeot 
11063f2dd94aSFrançois Tigeot /**
11071dedbd3bSFrançois Tigeot  * drm_mode_create_aspect_ratio_property - create aspect ratio property
11081dedbd3bSFrançois Tigeot  * @dev: DRM device
11091dedbd3bSFrançois Tigeot  *
11101dedbd3bSFrançois Tigeot  * Called by a driver the first time it's needed, must be attached to desired
11111dedbd3bSFrançois Tigeot  * connectors.
11121dedbd3bSFrançois Tigeot  *
11131dedbd3bSFrançois Tigeot  * Returns:
11141dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
11151dedbd3bSFrançois Tigeot  */
drm_mode_create_aspect_ratio_property(struct drm_device * dev)11161dedbd3bSFrançois Tigeot int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
11171dedbd3bSFrançois Tigeot {
11181dedbd3bSFrançois Tigeot 	if (dev->mode_config.aspect_ratio_property)
11191dedbd3bSFrançois Tigeot 		return 0;
11201dedbd3bSFrançois Tigeot 
11211dedbd3bSFrançois Tigeot 	dev->mode_config.aspect_ratio_property =
11221dedbd3bSFrançois Tigeot 		drm_property_create_enum(dev, 0, "aspect ratio",
11231dedbd3bSFrançois Tigeot 				drm_aspect_ratio_enum_list,
11241dedbd3bSFrançois Tigeot 				ARRAY_SIZE(drm_aspect_ratio_enum_list));
11251dedbd3bSFrançois Tigeot 
11261dedbd3bSFrançois Tigeot 	if (dev->mode_config.aspect_ratio_property == NULL)
11271dedbd3bSFrançois Tigeot 		return -ENOMEM;
11281dedbd3bSFrançois Tigeot 
11291dedbd3bSFrançois Tigeot 	return 0;
11301dedbd3bSFrançois Tigeot }
11311dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
11321dedbd3bSFrançois Tigeot 
11331dedbd3bSFrançois Tigeot /**
11341dedbd3bSFrançois Tigeot  * drm_mode_create_suggested_offset_properties - create suggests offset properties
11351dedbd3bSFrançois Tigeot  * @dev: DRM device
11361dedbd3bSFrançois Tigeot  *
11371dedbd3bSFrançois Tigeot  * Create the the suggested x/y offset property for connectors.
11381dedbd3bSFrançois Tigeot  */
drm_mode_create_suggested_offset_properties(struct drm_device * dev)11391dedbd3bSFrançois Tigeot int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
11401dedbd3bSFrançois Tigeot {
11411dedbd3bSFrançois Tigeot 	if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property)
11421dedbd3bSFrançois Tigeot 		return 0;
11431dedbd3bSFrançois Tigeot 
11441dedbd3bSFrançois Tigeot 	dev->mode_config.suggested_x_property =
11451dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff);
11461dedbd3bSFrançois Tigeot 
11471dedbd3bSFrançois Tigeot 	dev->mode_config.suggested_y_property =
11481dedbd3bSFrançois Tigeot 		drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff);
11491dedbd3bSFrançois Tigeot 
11501dedbd3bSFrançois Tigeot 	if (dev->mode_config.suggested_x_property == NULL ||
11511dedbd3bSFrançois Tigeot 	    dev->mode_config.suggested_y_property == NULL)
11521dedbd3bSFrançois Tigeot 		return -ENOMEM;
11531dedbd3bSFrançois Tigeot 	return 0;
11541dedbd3bSFrançois Tigeot }
11551dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties);
11561dedbd3bSFrançois Tigeot 
11571dedbd3bSFrançois Tigeot /**
11581dedbd3bSFrançois Tigeot  * drm_mode_connector_set_path_property - set tile property on connector
11591dedbd3bSFrançois Tigeot  * @connector: connector to set property on.
11601dedbd3bSFrançois Tigeot  * @path: path to use for property; must not be NULL.
11611dedbd3bSFrançois Tigeot  *
11621dedbd3bSFrançois Tigeot  * This creates a property to expose to userspace to specify a
11631dedbd3bSFrançois Tigeot  * connector path. This is mainly used for DisplayPort MST where
11641dedbd3bSFrançois Tigeot  * connectors have a topology and we want to allow userspace to give
11651dedbd3bSFrançois Tigeot  * them more meaningful names.
11661dedbd3bSFrançois Tigeot  *
11671dedbd3bSFrançois Tigeot  * Returns:
11681dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
11691dedbd3bSFrançois Tigeot  */
drm_mode_connector_set_path_property(struct drm_connector * connector,const char * path)11701dedbd3bSFrançois Tigeot int drm_mode_connector_set_path_property(struct drm_connector *connector,
11711dedbd3bSFrançois Tigeot 					 const char *path)
11721dedbd3bSFrançois Tigeot {
11731dedbd3bSFrançois Tigeot 	struct drm_device *dev = connector->dev;
11741dedbd3bSFrançois Tigeot 	int ret;
11751dedbd3bSFrançois Tigeot 
11761dedbd3bSFrançois Tigeot 	ret = drm_property_replace_global_blob(dev,
11771dedbd3bSFrançois Tigeot 	                                       &connector->path_blob_ptr,
11781dedbd3bSFrançois Tigeot 	                                       strlen(path) + 1,
11791dedbd3bSFrançois Tigeot 	                                       path,
11801dedbd3bSFrançois Tigeot 	                                       &connector->base,
11811dedbd3bSFrançois Tigeot 	                                       dev->mode_config.path_property);
11821dedbd3bSFrançois Tigeot 	return ret;
11831dedbd3bSFrançois Tigeot }
11841dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_path_property);
11851dedbd3bSFrançois Tigeot 
11861dedbd3bSFrançois Tigeot /**
11871dedbd3bSFrançois Tigeot  * drm_mode_connector_set_tile_property - set tile property on connector
11881dedbd3bSFrançois Tigeot  * @connector: connector to set property on.
11891dedbd3bSFrançois Tigeot  *
11901dedbd3bSFrançois Tigeot  * This looks up the tile information for a connector, and creates a
11911dedbd3bSFrançois Tigeot  * property for userspace to parse if it exists. The property is of
11921dedbd3bSFrançois Tigeot  * the form of 8 integers using ':' as a separator.
11931dedbd3bSFrançois Tigeot  *
11941dedbd3bSFrançois Tigeot  * Returns:
11951dedbd3bSFrançois Tigeot  * Zero on success, errno on failure.
11961dedbd3bSFrançois Tigeot  */
drm_mode_connector_set_tile_property(struct drm_connector * connector)11971dedbd3bSFrançois Tigeot int drm_mode_connector_set_tile_property(struct drm_connector *connector)
11981dedbd3bSFrançois Tigeot {
11991dedbd3bSFrançois Tigeot 	struct drm_device *dev = connector->dev;
12001dedbd3bSFrançois Tigeot 	char tile[256];
12011dedbd3bSFrançois Tigeot 	int ret;
12021dedbd3bSFrançois Tigeot 
12031dedbd3bSFrançois Tigeot 	if (!connector->has_tile) {
12041dedbd3bSFrançois Tigeot 		ret  = drm_property_replace_global_blob(dev,
12051dedbd3bSFrançois Tigeot 		                                        &connector->tile_blob_ptr,
12061dedbd3bSFrançois Tigeot 		                                        0,
12071dedbd3bSFrançois Tigeot 		                                        NULL,
12081dedbd3bSFrançois Tigeot 		                                        &connector->base,
12091dedbd3bSFrançois Tigeot 		                                        dev->mode_config.tile_property);
12101dedbd3bSFrançois Tigeot 		return ret;
12111dedbd3bSFrançois Tigeot 	}
12121dedbd3bSFrançois Tigeot 
12131dedbd3bSFrançois Tigeot 	snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
12141dedbd3bSFrançois Tigeot 		 connector->tile_group->id, connector->tile_is_single_monitor,
12151dedbd3bSFrançois Tigeot 		 connector->num_h_tile, connector->num_v_tile,
12161dedbd3bSFrançois Tigeot 		 connector->tile_h_loc, connector->tile_v_loc,
12171dedbd3bSFrançois Tigeot 		 connector->tile_h_size, connector->tile_v_size);
12181dedbd3bSFrançois Tigeot 
12191dedbd3bSFrançois Tigeot 	ret = drm_property_replace_global_blob(dev,
12201dedbd3bSFrançois Tigeot 	                                       &connector->tile_blob_ptr,
12211dedbd3bSFrançois Tigeot 	                                       strlen(tile) + 1,
12221dedbd3bSFrançois Tigeot 	                                       tile,
12231dedbd3bSFrançois Tigeot 	                                       &connector->base,
12241dedbd3bSFrançois Tigeot 	                                       dev->mode_config.tile_property);
12251dedbd3bSFrançois Tigeot 	return ret;
12261dedbd3bSFrançois Tigeot }
12271dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_tile_property);
12281dedbd3bSFrançois Tigeot 
12291dedbd3bSFrançois Tigeot /**
12301dedbd3bSFrançois Tigeot  * drm_mode_connector_update_edid_property - update the edid property of a connector
12311dedbd3bSFrançois Tigeot  * @connector: drm connector
12321dedbd3bSFrançois Tigeot  * @edid: new value of the edid property
12331dedbd3bSFrançois Tigeot  *
12341dedbd3bSFrançois Tigeot  * This function creates a new blob modeset object and assigns its id to the
12351dedbd3bSFrançois Tigeot  * connector's edid property.
12361dedbd3bSFrançois Tigeot  *
12371dedbd3bSFrançois Tigeot  * Returns:
12381dedbd3bSFrançois Tigeot  * Zero on success, negative errno on failure.
12391dedbd3bSFrançois Tigeot  */
drm_mode_connector_update_edid_property(struct drm_connector * connector,const struct edid * edid)12401dedbd3bSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector,
12411dedbd3bSFrançois Tigeot 					    const struct edid *edid)
12421dedbd3bSFrançois Tigeot {
12431dedbd3bSFrançois Tigeot 	struct drm_device *dev = connector->dev;
12441dedbd3bSFrançois Tigeot 	size_t size = 0;
12451dedbd3bSFrançois Tigeot 	int ret;
12461dedbd3bSFrançois Tigeot 
12471dedbd3bSFrançois Tigeot 	/* ignore requests to set edid when overridden */
12481dedbd3bSFrançois Tigeot 	if (connector->override_edid)
12491dedbd3bSFrançois Tigeot 		return 0;
12501dedbd3bSFrançois Tigeot 
12511dedbd3bSFrançois Tigeot 	if (edid)
12521dedbd3bSFrançois Tigeot 		size = EDID_LENGTH * (1 + edid->extensions);
12531dedbd3bSFrançois Tigeot 
12543f2dd94aSFrançois Tigeot 	/* Set the display info, using edid if available, otherwise
12553f2dd94aSFrançois Tigeot 	 * reseting the values to defaults. This duplicates the work
12563f2dd94aSFrançois Tigeot 	 * done in drm_add_edid_modes, but that function is not
12573f2dd94aSFrançois Tigeot 	 * consistently called before this one in all drivers and the
12583f2dd94aSFrançois Tigeot 	 * computation is cheap enough that it seems better to
12593f2dd94aSFrançois Tigeot 	 * duplicate it rather than attempt to ensure some arbitrary
12603f2dd94aSFrançois Tigeot 	 * ordering of calls.
12613f2dd94aSFrançois Tigeot 	 */
12623f2dd94aSFrançois Tigeot 	if (edid)
12633f2dd94aSFrançois Tigeot 		drm_add_display_info(connector, edid);
12643f2dd94aSFrançois Tigeot 	else
12653f2dd94aSFrançois Tigeot 		drm_reset_display_info(connector);
12663f2dd94aSFrançois Tigeot 
12673f2dd94aSFrançois Tigeot 	drm_object_property_set_value(&connector->base,
12683f2dd94aSFrançois Tigeot 				      dev->mode_config.non_desktop_property,
12693f2dd94aSFrançois Tigeot 				      connector->display_info.non_desktop);
12703f2dd94aSFrançois Tigeot 
12711dedbd3bSFrançois Tigeot 	ret = drm_property_replace_global_blob(dev,
12721dedbd3bSFrançois Tigeot 					       &connector->edid_blob_ptr,
12731dedbd3bSFrançois Tigeot 	                                       size,
12741dedbd3bSFrançois Tigeot 	                                       edid,
12751dedbd3bSFrançois Tigeot 	                                       &connector->base,
12761dedbd3bSFrançois Tigeot 	                                       dev->mode_config.edid_property);
12771dedbd3bSFrançois Tigeot 	return ret;
12781dedbd3bSFrançois Tigeot }
12791dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
12801dedbd3bSFrançois Tigeot 
1281a85cb24fSFrançois Tigeot /**
1282a85cb24fSFrançois Tigeot  * drm_mode_connector_set_link_status_property - Set link status property of a connector
1283a85cb24fSFrançois Tigeot  * @connector: drm connector
1284a85cb24fSFrançois Tigeot  * @link_status: new value of link status property (0: Good, 1: Bad)
1285a85cb24fSFrançois Tigeot  *
1286a85cb24fSFrançois Tigeot  * In usual working scenario, this link status property will always be set to
1287a85cb24fSFrançois Tigeot  * "GOOD". If something fails during or after a mode set, the kernel driver
1288a85cb24fSFrançois Tigeot  * may set this link status property to "BAD". The caller then needs to send a
1289a85cb24fSFrançois Tigeot  * hotplug uevent for userspace to re-check the valid modes through
1290a85cb24fSFrançois Tigeot  * GET_CONNECTOR_IOCTL and retry modeset.
1291a85cb24fSFrançois Tigeot  *
1292a85cb24fSFrançois Tigeot  * Note: Drivers cannot rely on userspace to support this property and
1293a85cb24fSFrançois Tigeot  * issue a modeset. As such, they may choose to handle issues (like
1294a85cb24fSFrançois Tigeot  * re-training a link) without userspace's intervention.
1295a85cb24fSFrançois Tigeot  *
1296a85cb24fSFrançois Tigeot  * The reason for adding this property is to handle link training failures, but
1297a85cb24fSFrançois Tigeot  * it is not limited to DP or link training. For example, if we implement
1298a85cb24fSFrançois Tigeot  * asynchronous setcrtc, this property can be used to report any failures in that.
1299a85cb24fSFrançois Tigeot  */
drm_mode_connector_set_link_status_property(struct drm_connector * connector,uint64_t link_status)1300a85cb24fSFrançois Tigeot void drm_mode_connector_set_link_status_property(struct drm_connector *connector,
1301a85cb24fSFrançois Tigeot 						 uint64_t link_status)
1302a85cb24fSFrançois Tigeot {
1303a85cb24fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
1304a85cb24fSFrançois Tigeot 
1305a85cb24fSFrançois Tigeot 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
1306a85cb24fSFrançois Tigeot 	connector->state->link_status = link_status;
1307a85cb24fSFrançois Tigeot 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
1308a85cb24fSFrançois Tigeot }
1309a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_link_status_property);
1310a85cb24fSFrançois Tigeot 
drm_mode_connector_set_obj_prop(struct drm_mode_object * obj,struct drm_property * property,uint64_t value)13111dedbd3bSFrançois Tigeot int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
13121dedbd3bSFrançois Tigeot 				    struct drm_property *property,
13131dedbd3bSFrançois Tigeot 				    uint64_t value)
13141dedbd3bSFrançois Tigeot {
13151dedbd3bSFrançois Tigeot 	int ret = -EINVAL;
13161dedbd3bSFrançois Tigeot 	struct drm_connector *connector = obj_to_connector(obj);
13171dedbd3bSFrançois Tigeot 
13181dedbd3bSFrançois Tigeot 	/* Do DPMS ourselves */
13191dedbd3bSFrançois Tigeot 	if (property == connector->dev->mode_config.dpms_property) {
13201dedbd3bSFrançois Tigeot 		ret = (*connector->funcs->dpms)(connector, (int)value);
13211dedbd3bSFrançois Tigeot 	} else if (connector->funcs->set_property)
13221dedbd3bSFrançois Tigeot 		ret = connector->funcs->set_property(connector, property, value);
13231dedbd3bSFrançois Tigeot 
13241dedbd3bSFrançois Tigeot 	if (!ret)
13251dedbd3bSFrançois Tigeot 		drm_object_property_set_value(&connector->base, property, value);
13261dedbd3bSFrançois Tigeot 	return ret;
13271dedbd3bSFrançois Tigeot }
13281dedbd3bSFrançois Tigeot 
drm_mode_connector_property_set_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)13291dedbd3bSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
13301dedbd3bSFrançois Tigeot 				       void *data, struct drm_file *file_priv)
13311dedbd3bSFrançois Tigeot {
13321dedbd3bSFrançois Tigeot 	struct drm_mode_connector_set_property *conn_set_prop = data;
13331dedbd3bSFrançois Tigeot 	struct drm_mode_obj_set_property obj_set_prop = {
13341dedbd3bSFrançois Tigeot 		.value = conn_set_prop->value,
13351dedbd3bSFrançois Tigeot 		.prop_id = conn_set_prop->prop_id,
13361dedbd3bSFrançois Tigeot 		.obj_id = conn_set_prop->connector_id,
13371dedbd3bSFrançois Tigeot 		.obj_type = DRM_MODE_OBJECT_CONNECTOR
13381dedbd3bSFrançois Tigeot 	};
13391dedbd3bSFrançois Tigeot 
13401dedbd3bSFrançois Tigeot 	/* It does all the locking and checking we need */
13411dedbd3bSFrançois Tigeot 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
13421dedbd3bSFrançois Tigeot }
13431dedbd3bSFrançois Tigeot 
drm_connector_get_encoder(struct drm_connector * connector)13441dedbd3bSFrançois Tigeot static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
13451dedbd3bSFrançois Tigeot {
13461dedbd3bSFrançois Tigeot 	/* For atomic drivers only state objects are synchronously updated and
13471dedbd3bSFrançois Tigeot 	 * protected by modeset locks, so check those first. */
13481dedbd3bSFrançois Tigeot 	if (connector->state)
13491dedbd3bSFrançois Tigeot 		return connector->state->best_encoder;
13501dedbd3bSFrançois Tigeot 	return connector->encoder;
13511dedbd3bSFrançois Tigeot }
13521dedbd3bSFrançois Tigeot 
drm_mode_expose_to_userspace(const struct drm_display_mode * mode,const struct drm_file * file_priv)13531dedbd3bSFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
13541dedbd3bSFrançois Tigeot 					 const struct drm_file *file_priv)
13551dedbd3bSFrançois Tigeot {
13561dedbd3bSFrançois Tigeot 	/*
13571dedbd3bSFrançois Tigeot 	 * If user-space hasn't configured the driver to expose the stereo 3D
13581dedbd3bSFrançois Tigeot 	 * modes, don't expose them.
13591dedbd3bSFrançois Tigeot 	 */
13601dedbd3bSFrançois Tigeot 	if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
13611dedbd3bSFrançois Tigeot 		return false;
13621dedbd3bSFrançois Tigeot 
13631dedbd3bSFrançois Tigeot 	return true;
13641dedbd3bSFrançois Tigeot }
13651dedbd3bSFrançois Tigeot 
drm_mode_getconnector(struct drm_device * dev,void * data,struct drm_file * file_priv)13661dedbd3bSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data,
13671dedbd3bSFrançois Tigeot 			  struct drm_file *file_priv)
13681dedbd3bSFrançois Tigeot {
13691dedbd3bSFrançois Tigeot 	struct drm_mode_get_connector *out_resp = data;
13701dedbd3bSFrançois Tigeot 	struct drm_connector *connector;
13711dedbd3bSFrançois Tigeot 	struct drm_encoder *encoder;
13721dedbd3bSFrançois Tigeot 	struct drm_display_mode *mode;
13731dedbd3bSFrançois Tigeot 	int mode_count = 0;
13741dedbd3bSFrançois Tigeot 	int encoders_count = 0;
13751dedbd3bSFrançois Tigeot 	int ret = 0;
13761dedbd3bSFrançois Tigeot 	int copied = 0;
13771dedbd3bSFrançois Tigeot 	int i;
13781dedbd3bSFrançois Tigeot 	struct drm_mode_modeinfo u_mode;
13791dedbd3bSFrançois Tigeot 	struct drm_mode_modeinfo __user *mode_ptr;
13801dedbd3bSFrançois Tigeot 	uint32_t __user *encoder_ptr;
13811dedbd3bSFrançois Tigeot 
13821dedbd3bSFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
13831dedbd3bSFrançois Tigeot 		return -EINVAL;
13841dedbd3bSFrançois Tigeot 
13851dedbd3bSFrançois Tigeot 	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
13861dedbd3bSFrançois Tigeot 
13873f2dd94aSFrançois Tigeot 	connector = drm_connector_lookup(dev, file_priv, out_resp->connector_id);
1388a85cb24fSFrançois Tigeot 	if (!connector)
1389a85cb24fSFrançois Tigeot 		return -ENOENT;
13901dedbd3bSFrançois Tigeot 
13911dedbd3bSFrançois Tigeot 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
13921dedbd3bSFrançois Tigeot 		if (connector->encoder_ids[i] != 0)
13931dedbd3bSFrançois Tigeot 			encoders_count++;
13941dedbd3bSFrançois Tigeot 
13951dedbd3bSFrançois Tigeot 	if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
13961dedbd3bSFrançois Tigeot 		copied = 0;
13971dedbd3bSFrançois Tigeot 		encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
13981dedbd3bSFrançois Tigeot 		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
13991dedbd3bSFrançois Tigeot 			if (connector->encoder_ids[i] != 0) {
14001dedbd3bSFrançois Tigeot 				if (put_user(connector->encoder_ids[i],
14011dedbd3bSFrançois Tigeot 					     encoder_ptr + copied)) {
14021dedbd3bSFrançois Tigeot 					ret = -EFAULT;
14031dedbd3bSFrançois Tigeot 					goto out;
14041dedbd3bSFrançois Tigeot 				}
14051dedbd3bSFrançois Tigeot 				copied++;
14061dedbd3bSFrançois Tigeot 			}
14071dedbd3bSFrançois Tigeot 		}
14081dedbd3bSFrançois Tigeot 	}
14091dedbd3bSFrançois Tigeot 	out_resp->count_encoders = encoders_count;
14101dedbd3bSFrançois Tigeot 
1411a85cb24fSFrançois Tigeot 	out_resp->connector_id = connector->base.id;
1412a85cb24fSFrançois Tigeot 	out_resp->connector_type = connector->connector_type;
1413a85cb24fSFrançois Tigeot 	out_resp->connector_type_id = connector->connector_type_id;
1414a85cb24fSFrançois Tigeot 
1415a85cb24fSFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
1416a85cb24fSFrançois Tigeot 	if (out_resp->count_modes == 0) {
1417a85cb24fSFrançois Tigeot 		connector->funcs->fill_modes(connector,
1418a85cb24fSFrançois Tigeot 					     dev->mode_config.max_width,
1419a85cb24fSFrançois Tigeot 					     dev->mode_config.max_height);
1420a85cb24fSFrançois Tigeot 	}
1421a85cb24fSFrançois Tigeot 
1422a85cb24fSFrançois Tigeot 	out_resp->mm_width = connector->display_info.width_mm;
1423a85cb24fSFrançois Tigeot 	out_resp->mm_height = connector->display_info.height_mm;
1424a85cb24fSFrançois Tigeot 	out_resp->subpixel = connector->display_info.subpixel_order;
1425a85cb24fSFrançois Tigeot 	out_resp->connection = connector->status;
1426a85cb24fSFrançois Tigeot 
1427a85cb24fSFrançois Tigeot 	/* delayed so we get modes regardless of pre-fill_modes state */
1428a85cb24fSFrançois Tigeot 	list_for_each_entry(mode, &connector->modes, head)
1429a85cb24fSFrançois Tigeot 		if (drm_mode_expose_to_userspace(mode, file_priv))
1430a85cb24fSFrançois Tigeot 			mode_count++;
1431a85cb24fSFrançois Tigeot 
1432a85cb24fSFrançois Tigeot 	/*
1433a85cb24fSFrançois Tigeot 	 * This ioctl is called twice, once to determine how much space is
1434a85cb24fSFrançois Tigeot 	 * needed, and the 2nd time to fill it.
1435a85cb24fSFrançois Tigeot 	 */
1436a85cb24fSFrançois Tigeot 	if ((out_resp->count_modes >= mode_count) && mode_count) {
1437a85cb24fSFrançois Tigeot 		copied = 0;
1438a85cb24fSFrançois Tigeot 		mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
1439a85cb24fSFrançois Tigeot 		list_for_each_entry(mode, &connector->modes, head) {
1440a85cb24fSFrançois Tigeot 			if (!drm_mode_expose_to_userspace(mode, file_priv))
1441a85cb24fSFrançois Tigeot 				continue;
1442a85cb24fSFrançois Tigeot 
1443a85cb24fSFrançois Tigeot 			drm_mode_convert_to_umode(&u_mode, mode);
1444a85cb24fSFrançois Tigeot 			if (copy_to_user(mode_ptr + copied,
1445a85cb24fSFrançois Tigeot 					 &u_mode, sizeof(u_mode))) {
1446a85cb24fSFrançois Tigeot 				ret = -EFAULT;
1447a85cb24fSFrançois Tigeot 				mutex_unlock(&dev->mode_config.mutex);
1448a85cb24fSFrançois Tigeot 
1449a85cb24fSFrançois Tigeot 				goto out;
1450a85cb24fSFrançois Tigeot 			}
1451a85cb24fSFrançois Tigeot 			copied++;
1452a85cb24fSFrançois Tigeot 		}
1453a85cb24fSFrançois Tigeot 	}
1454a85cb24fSFrançois Tigeot 	out_resp->count_modes = mode_count;
1455a85cb24fSFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
1456a85cb24fSFrançois Tigeot 
1457a85cb24fSFrançois Tigeot 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
1458a85cb24fSFrançois Tigeot 	encoder = drm_connector_get_encoder(connector);
1459a85cb24fSFrançois Tigeot 	if (encoder)
1460a85cb24fSFrançois Tigeot 		out_resp->encoder_id = encoder->base.id;
1461a85cb24fSFrançois Tigeot 	else
1462a85cb24fSFrançois Tigeot 		out_resp->encoder_id = 0;
1463a85cb24fSFrançois Tigeot 
1464a85cb24fSFrançois Tigeot 	/* Only grab properties after probing, to make sure EDID and other
1465a85cb24fSFrançois Tigeot 	 * properties reflect the latest status. */
1466a85cb24fSFrançois Tigeot 	ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,
1467a85cb24fSFrançois Tigeot 			(uint32_t __user *)(unsigned long)(out_resp->props_ptr),
1468a85cb24fSFrançois Tigeot 			(uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
1469a85cb24fSFrançois Tigeot 			&out_resp->count_props);
14701dedbd3bSFrançois Tigeot 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
14711dedbd3bSFrançois Tigeot 
1472a85cb24fSFrançois Tigeot out:
14733f2dd94aSFrançois Tigeot 	drm_connector_put(connector);
14741dedbd3bSFrançois Tigeot 
14751dedbd3bSFrançois Tigeot 	return ret;
14761dedbd3bSFrançois Tigeot }
14771dedbd3bSFrançois Tigeot 
14784be47400SFrançois Tigeot 
14794be47400SFrançois Tigeot /**
14804be47400SFrançois Tigeot  * DOC: Tile group
14814be47400SFrançois Tigeot  *
14824be47400SFrançois Tigeot  * Tile groups are used to represent tiled monitors with a unique integer
14834be47400SFrançois Tigeot  * identifier. Tiled monitors using DisplayID v1.3 have a unique 8-byte handle,
14844be47400SFrançois Tigeot  * we store this in a tile group, so we have a common identifier for all tiles
14854be47400SFrançois Tigeot  * in a monitor group. The property is called "TILE". Drivers can manage tile
14864be47400SFrançois Tigeot  * groups using drm_mode_create_tile_group(), drm_mode_put_tile_group() and
14874be47400SFrançois Tigeot  * drm_mode_get_tile_group(). But this is only needed for internal panels where
14884be47400SFrançois Tigeot  * the tile group information is exposed through a non-standard way.
14894be47400SFrançois Tigeot  */
14904be47400SFrançois Tigeot 
drm_tile_group_free(struct kref * kref)14914be47400SFrançois Tigeot static void drm_tile_group_free(struct kref *kref)
14924be47400SFrançois Tigeot {
14934be47400SFrançois Tigeot 	struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount);
14944be47400SFrançois Tigeot 	struct drm_device *dev = tg->dev;
14954be47400SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
14964be47400SFrançois Tigeot 	idr_remove(&dev->mode_config.tile_idr, tg->id);
14974be47400SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
14984be47400SFrançois Tigeot 	kfree(tg);
14994be47400SFrançois Tigeot }
15004be47400SFrançois Tigeot 
15014be47400SFrançois Tigeot /**
15024be47400SFrançois Tigeot  * drm_mode_put_tile_group - drop a reference to a tile group.
15034be47400SFrançois Tigeot  * @dev: DRM device
15044be47400SFrançois Tigeot  * @tg: tile group to drop reference to.
15054be47400SFrançois Tigeot  *
15064be47400SFrançois Tigeot  * drop reference to tile group and free if 0.
15074be47400SFrançois Tigeot  */
drm_mode_put_tile_group(struct drm_device * dev,struct drm_tile_group * tg)15084be47400SFrançois Tigeot void drm_mode_put_tile_group(struct drm_device *dev,
15094be47400SFrançois Tigeot 			     struct drm_tile_group *tg)
15104be47400SFrançois Tigeot {
15114be47400SFrançois Tigeot 	kref_put(&tg->refcount, drm_tile_group_free);
15124be47400SFrançois Tigeot }
15134be47400SFrançois Tigeot EXPORT_SYMBOL(drm_mode_put_tile_group);
15144be47400SFrançois Tigeot 
15154be47400SFrançois Tigeot /**
15164be47400SFrançois Tigeot  * drm_mode_get_tile_group - get a reference to an existing tile group
15174be47400SFrançois Tigeot  * @dev: DRM device
15184be47400SFrançois Tigeot  * @topology: 8-bytes unique per monitor.
15194be47400SFrançois Tigeot  *
15204be47400SFrançois Tigeot  * Use the unique bytes to get a reference to an existing tile group.
15214be47400SFrançois Tigeot  *
15224be47400SFrançois Tigeot  * RETURNS:
15234be47400SFrançois Tigeot  * tile group or NULL if not found.
15244be47400SFrançois Tigeot  */
drm_mode_get_tile_group(struct drm_device * dev,char topology[8])15254be47400SFrançois Tigeot struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
15264be47400SFrançois Tigeot 					       char topology[8])
15274be47400SFrançois Tigeot {
15284be47400SFrançois Tigeot 	struct drm_tile_group *tg;
15294be47400SFrançois Tigeot 	int id;
15304be47400SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
15314be47400SFrançois Tigeot 	idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) {
15324be47400SFrançois Tigeot 		if (!memcmp(tg->group_data, topology, 8)) {
15334be47400SFrançois Tigeot 			if (!kref_get_unless_zero(&tg->refcount))
15344be47400SFrançois Tigeot 				tg = NULL;
15354be47400SFrançois Tigeot 			mutex_unlock(&dev->mode_config.idr_mutex);
15364be47400SFrançois Tigeot 			return tg;
15374be47400SFrançois Tigeot 		}
15384be47400SFrançois Tigeot 	}
15394be47400SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
15404be47400SFrançois Tigeot 	return NULL;
15414be47400SFrançois Tigeot }
15424be47400SFrançois Tigeot EXPORT_SYMBOL(drm_mode_get_tile_group);
15434be47400SFrançois Tigeot 
15444be47400SFrançois Tigeot /**
15454be47400SFrançois Tigeot  * drm_mode_create_tile_group - create a tile group from a displayid description
15464be47400SFrançois Tigeot  * @dev: DRM device
15474be47400SFrançois Tigeot  * @topology: 8-bytes unique per monitor.
15484be47400SFrançois Tigeot  *
15494be47400SFrançois Tigeot  * Create a tile group for the unique monitor, and get a unique
15504be47400SFrançois Tigeot  * identifier for the tile group.
15514be47400SFrançois Tigeot  *
15524be47400SFrançois Tigeot  * RETURNS:
15534be47400SFrançois Tigeot  * new tile group or error.
15544be47400SFrançois Tigeot  */
drm_mode_create_tile_group(struct drm_device * dev,char topology[8])15554be47400SFrançois Tigeot struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
15564be47400SFrançois Tigeot 						  char topology[8])
15574be47400SFrançois Tigeot {
15584be47400SFrançois Tigeot 	struct drm_tile_group *tg;
15594be47400SFrançois Tigeot 	int ret;
15604be47400SFrançois Tigeot 
15614be47400SFrançois Tigeot 	tg = kzalloc(sizeof(*tg), GFP_KERNEL);
15624be47400SFrançois Tigeot 	if (!tg)
15634be47400SFrançois Tigeot 		return ERR_PTR(-ENOMEM);
15644be47400SFrançois Tigeot 
15654be47400SFrançois Tigeot 	kref_init(&tg->refcount);
15664be47400SFrançois Tigeot 	memcpy(tg->group_data, topology, 8);
15674be47400SFrançois Tigeot 	tg->dev = dev;
15684be47400SFrançois Tigeot 
15694be47400SFrançois Tigeot 	mutex_lock(&dev->mode_config.idr_mutex);
15704be47400SFrançois Tigeot 	ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL);
15714be47400SFrançois Tigeot 	if (ret >= 0) {
15724be47400SFrançois Tigeot 		tg->id = ret;
15734be47400SFrançois Tigeot 	} else {
15744be47400SFrançois Tigeot 		kfree(tg);
15754be47400SFrançois Tigeot 		tg = ERR_PTR(ret);
15764be47400SFrançois Tigeot 	}
15774be47400SFrançois Tigeot 
15784be47400SFrançois Tigeot 	mutex_unlock(&dev->mode_config.idr_mutex);
15794be47400SFrançois Tigeot 	return tg;
15804be47400SFrançois Tigeot }
15814be47400SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tile_group);
1582*78973132SSergey Zigachev 
1583*78973132SSergey Zigachev /**
1584*78973132SSergey Zigachev  * drm_connector_update_edid_property - update the edid property of a connector
1585*78973132SSergey Zigachev  * @connector: drm connector
1586*78973132SSergey Zigachev  * @edid: new value of the edid property
1587*78973132SSergey Zigachev  *
1588*78973132SSergey Zigachev  * This function creates a new blob modeset object and assigns its id to the
1589*78973132SSergey Zigachev  * connector's edid property.
1590*78973132SSergey Zigachev  *
1591*78973132SSergey Zigachev  * Returns:
1592*78973132SSergey Zigachev  * Zero on success, negative errno on failure.
1593*78973132SSergey Zigachev  */
drm_connector_update_edid_property(struct drm_connector * connector,const struct edid * edid)1594*78973132SSergey Zigachev int drm_connector_update_edid_property(struct drm_connector *connector,
1595*78973132SSergey Zigachev 				       const struct edid *edid)
1596*78973132SSergey Zigachev {
1597*78973132SSergey Zigachev 	struct drm_device *dev = connector->dev;
1598*78973132SSergey Zigachev 	size_t size = 0;
1599*78973132SSergey Zigachev 	int ret;
1600*78973132SSergey Zigachev 
1601*78973132SSergey Zigachev 	/* ignore requests to set edid when overridden */
1602*78973132SSergey Zigachev 	if (connector->override_edid)
1603*78973132SSergey Zigachev 		return 0;
1604*78973132SSergey Zigachev 
1605*78973132SSergey Zigachev 	if (edid)
1606*78973132SSergey Zigachev 		size = EDID_LENGTH * (1 + edid->extensions);
1607*78973132SSergey Zigachev 
1608*78973132SSergey Zigachev 	/* Set the display info, using edid if available, otherwise
1609*78973132SSergey Zigachev 	 * reseting the values to defaults. This duplicates the work
1610*78973132SSergey Zigachev 	 * done in drm_add_edid_modes, but that function is not
1611*78973132SSergey Zigachev 	 * consistently called before this one in all drivers and the
1612*78973132SSergey Zigachev 	 * computation is cheap enough that it seems better to
1613*78973132SSergey Zigachev 	 * duplicate it rather than attempt to ensure some arbitrary
1614*78973132SSergey Zigachev 	 * ordering of calls.
1615*78973132SSergey Zigachev 	 */
1616*78973132SSergey Zigachev 	if (edid)
1617*78973132SSergey Zigachev 		drm_add_display_info(connector, edid);
1618*78973132SSergey Zigachev 	else
1619*78973132SSergey Zigachev 		drm_reset_display_info(connector);
1620*78973132SSergey Zigachev 
1621*78973132SSergey Zigachev 	drm_object_property_set_value(&connector->base,
1622*78973132SSergey Zigachev 				      dev->mode_config.non_desktop_property,
1623*78973132SSergey Zigachev 				      connector->display_info.non_desktop);
1624*78973132SSergey Zigachev 
1625*78973132SSergey Zigachev 	ret = drm_property_replace_global_blob(dev,
1626*78973132SSergey Zigachev 					       &connector->edid_blob_ptr,
1627*78973132SSergey Zigachev 	                                       size,
1628*78973132SSergey Zigachev 	                                       edid,
1629*78973132SSergey Zigachev 	                                       &connector->base,
1630*78973132SSergey Zigachev 	                                       dev->mode_config.edid_property);
1631*78973132SSergey Zigachev 	return ret;
1632*78973132SSergey Zigachev }
1633*78973132SSergey Zigachev EXPORT_SYMBOL(drm_connector_update_edid_property);
1634